├── .gitignore ├── .gitlab-ci.yml ├── .gitlab ├── issue_templates │ └── Issue_template.md └── merge_request_templates │ └── Merge_request_template.md ├── COMMUNITY-HW.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── acpid ├── Cargo.toml └── src │ ├── acpi.rs │ ├── acpi │ └── dmar │ │ ├── drhd.rs │ │ └── mod.rs │ ├── aml_physmem.rs │ ├── main.rs │ └── scheme.rs ├── amlserde ├── Cargo.toml └── src │ └── lib.rs ├── audio ├── ac97d │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── device.rs │ │ └── main.rs ├── ihdad │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── hda │ │ ├── cmdbuff.rs │ │ ├── common.rs │ │ ├── device.rs │ │ ├── mod.rs │ │ ├── node.rs │ │ └── stream.rs │ │ └── main.rs └── sb16d │ ├── Cargo.toml │ └── src │ ├── device.rs │ └── main.rs ├── common ├── Cargo.toml └── src │ ├── dma.rs │ ├── io.rs │ ├── io │ ├── mmio.rs │ └── pio.rs │ ├── lib.rs │ ├── logger.rs │ └── sgl.rs ├── executor ├── Cargo.toml └── src │ └── lib.rs ├── fmt.sh ├── graphics ├── bgad │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── bga.rs │ │ ├── main.rs │ │ └── scheme.rs ├── console-draw │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── driver-graphics │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── fbbootlogd │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── scheme.rs ├── fbcond │ ├── Cargo.toml │ └── src │ │ ├── display.rs │ │ ├── main.rs │ │ ├── scheme.rs │ │ └── text.rs ├── graphics-ipc │ ├── Cargo.toml │ └── src │ │ ├── lib.rs │ │ └── v1.rs ├── vesad │ ├── Cargo.toml │ └── src │ │ ├── framebuffer.rs │ │ ├── main.rs │ │ └── scheme.rs └── virtio-gpud │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── scheme.rs ├── hwd ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── initfs.toml ├── input ├── ps2d │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── controller.rs │ │ ├── keymap.rs │ │ ├── main.rs │ │ ├── state.rs │ │ └── vm.rs └── usbhidd │ ├── .gitignore │ ├── Cargo.toml │ └── src │ ├── keymap.rs │ ├── main.rs │ └── reqs.rs ├── inputd ├── Cargo.toml └── src │ ├── lib.rs │ └── main.rs ├── net ├── alxd │ ├── Cargo.toml │ └── src │ │ ├── device │ │ ├── mod.rs │ │ └── regs.rs │ │ └── main.rs ├── driver-network │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── e1000d │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── device.rs │ │ └── main.rs ├── ixgbed │ ├── Cargo.toml │ ├── LICENSE │ ├── README.md │ ├── config.toml │ └── src │ │ ├── device.rs │ │ ├── ixgbe.rs │ │ └── main.rs ├── rtl8139d │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── device.rs │ │ └── main.rs ├── rtl8168d │ ├── Cargo.toml │ ├── config.toml │ └── src │ │ ├── device.rs │ │ └── main.rs └── virtio-netd │ ├── Cargo.toml │ ├── config.toml │ └── src │ ├── main.rs │ └── scheme.rs ├── pcid-spawner ├── Cargo.toml └── src │ └── main.rs ├── pcid ├── .gitignore ├── Cargo.toml └── src │ ├── cfg_access │ ├── fallback.rs │ └── mod.rs │ ├── driver_handler.rs │ ├── driver_interface │ ├── bar.rs │ ├── cap.rs │ ├── config.rs │ ├── id.rs │ ├── irq_helpers.rs │ ├── mod.rs │ └── msi.rs │ ├── lib.rs │ ├── main.rs │ └── scheme.rs ├── rtcd ├── Cargo.toml └── src │ ├── main.rs │ └── x86.rs ├── rust-toolchain.toml ├── storage ├── ahcid │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── ahci │ │ ├── disk_ata.rs │ │ ├── disk_atapi.rs │ │ ├── fis.rs │ │ ├── hba.rs │ │ └── mod.rs │ │ └── main.rs ├── bcm2835-sdhcid │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── sd │ │ └── mod.rs ├── driver-block │ ├── Cargo.toml │ └── src │ │ └── lib.rs ├── ided │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── ide.rs │ │ └── main.rs ├── lived │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── nvmed │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── nvme │ │ ├── cmd.rs │ │ ├── executor.rs │ │ ├── identify.rs │ │ ├── mod.rs │ │ └── queues.rs ├── partitionlib │ ├── Cargo.toml │ ├── resources │ │ ├── disk.img │ │ └── disk_mbr.img │ ├── src │ │ ├── lib.rs │ │ ├── mbr.rs │ │ └── partition.rs │ └── tests │ │ └── test.rs ├── usbscsid │ ├── .gitignore │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ ├── protocol │ │ ├── bot.rs │ │ └── mod.rs │ │ └── scsi │ │ ├── cmds.rs │ │ ├── mod.rs │ │ └── opcodes.rs └── virtio-blkd │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── scheme.rs ├── usbctl ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── usbhubd ├── .gitignore ├── Cargo.toml └── src │ └── main.rs ├── vboxd ├── Cargo.toml ├── config.toml └── src │ ├── bga.rs │ └── main.rs ├── virtio-core ├── Cargo.toml └── src │ ├── arch │ ├── aarch64.rs │ ├── riscv64.rs │ └── x86.rs │ ├── lib.rs │ ├── probe.rs │ ├── spec │ ├── mod.rs │ ├── reserved_features.rs │ ├── split_virtqueue.rs │ └── transport_pci.rs │ ├── transport.rs │ └── utils.rs └── xhcid ├── .gitignore ├── Cargo.toml ├── config.toml ├── drivers.toml └── src ├── driver_interface.rs ├── lib.rs ├── main.rs ├── usb ├── bos.rs ├── config.rs ├── device.rs ├── endpoint.rs ├── hub.rs ├── interface.rs ├── mod.rs └── setup.rs └── xhci ├── capability.rs ├── context.rs ├── device_enumerator.rs ├── doorbell.rs ├── event.rs ├── extended.rs ├── irq_reactor.rs ├── mod.rs ├── operational.rs ├── port.rs ├── ring.rs ├── runtime.rs ├── scheme.rs └── trb.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | # Local settings folder for Visual Studio Code 4 | .vscode/ 5 | # Local settings folder for Jetbrains products (RustRover, IntelliJ, CLion) 6 | .idea/ 7 | # Local settings folder for Visual Studio Professional 8 | .vs/ 9 | # Local settings folder for the devcontainer extension that most IDEs support. 10 | .devcontainer/ 11 | -------------------------------------------------------------------------------- /.gitlab-ci.yml: -------------------------------------------------------------------------------- 1 | image: "redoxos/redoxer:latest" 2 | 3 | stages: 4 | - build 5 | 6 | # TODO? 7 | # - test 8 | 9 | # TODO check if all drivers build 10 | 11 | fmt: 12 | stage: build 13 | needs: [] 14 | script: 15 | - rustup component add rustfmt-preview 16 | # TODO add more packages as they get formatted 17 | - CHECK_ONLY=1 ./fmt.sh 18 | 19 | # TODO: unit tests 20 | -------------------------------------------------------------------------------- /.gitlab/issue_templates/Issue_template.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | - [ ] I agree that I have searched opened and closed issues to prevent duplicates. 6 | 7 | -------------------- 8 | 9 | 10 | 11 | ## Description 12 | 13 | Replace me 14 | 15 | 16 | 17 | ## Environment info 18 | 19 | 20 | 21 | - Redox OS Release: 22 | 0.0.0 Remove me 23 | 24 | 25 | - Operating system: 26 | Replace me 27 | - `uname -a`: 28 | `Replace me` 29 | - `rustc -V`: 30 | `Replace me` 31 | - `git rev-parse HEAD`: 32 | `Replace me` 33 | 34 | - Replace me: 35 | Replace me 36 | 37 | 38 | 39 | ## Steps to reproduce 40 | 41 | 1. Replace me 42 | 2. Replace me 43 | 3. ... 44 | 45 | 46 | 47 | ## Behavior 48 | 49 | 50 | 51 | - **Expected behavior**: 52 | Replace me 53 | 54 | 55 | - **Actual behavior**: 56 | Replace me 57 | 58 | 59 | ``` 60 | Replace me 61 | ``` 62 | 63 | 64 | - **Proposed solution**: 65 | Replace me 66 | 67 | 68 | 69 | 70 | 71 | ## Optional references 72 | 73 | 74 | Related to: 75 | - #0000 Remove me 76 | - Replace me 77 | - ... 78 | 79 | Blocked by: 80 | - #0000 Remove me 81 | - ... 82 | 83 | 84 | 85 | ## Optional extras 86 | 87 | Replace me 88 | 89 | 90 | 91 | 92 | 93 | -------------------------------------------------------------------------------- /.gitlab/merge_request_templates/Merge_request_template.md: -------------------------------------------------------------------------------- 1 | **Problem**: [describe the problem you try to solve with this PR.] 2 | 3 | **Solution**: [describe carefully what you change by this PR.] 4 | 5 | **Changes introduced by this pull request**: 6 | 7 | - [...] 8 | - [...] 9 | - [...] 10 | 11 | **Drawbacks**: [if any, describe the drawbacks of this pull request.] 12 | 13 | **TODOs**: [what is not done yet.] 14 | 15 | **Fixes**: [what issues this fixes.] 16 | 17 | **State**: [the state of this PR, e.g. WIP, ready, etc.] 18 | 19 | **Blocking/related**: [issues or PRs blocking or being related to this issue.] 20 | 21 | **Other**: [optional: for other relevant information that should be known or cannot be described in the other fields.] 22 | 23 | ------ 24 | 25 | _The above template is not necessary for smaller PRs._ 26 | -------------------------------------------------------------------------------- /COMMUNITY-HW.md: -------------------------------------------------------------------------------- 1 | # Community Hardware 2 | 3 | This document covers the devices from the community that needs a driver. 4 | 5 | Unfortunately we can't know the most sold device models of the world to measure our device porting priority, thus we will use our community data to measure our device priorities, if you find a "device model users" survey (similar to [Debian Popularity Contest](https://popcon.debian.org/) and [Steam Hardware/Software Survey](https://store.steampowered.com/hwsurvey/Steam-Hardware-Software-Survey-Welcome-to-Steam)), please comment. 6 | 7 | If you want to contribute to this table, install [pciutils](https://mj.ucw.cz/sw/pciutils/) on your Linux distribution (it should have a package on your distribution), run `lspci -v` to see your hardware devices, their kernel drivers and give the results of these items on each device: 8 | 9 | - The first field (each device has an unique name for this item) 10 | - Kernel driver in use 11 | - Kernel modules 12 | 13 | If you are unsure of what to do, you can talk with us on the [chat](https://doc.redox-os.org/book/chat.html). 14 | 15 | ## Template 16 | 17 | You will use this template to insert your devices on the table. 18 | 19 | ``` 20 | | | | | No | 21 | ``` 22 | 23 | | **Device model** | **Kernel driver** | **Kernel module** | **There's a Redox driver?** | 24 | |------------------|-------------------|-------------------|-----------------------------| 25 | | Realtek RTL8821CE 802.11ac (Wi-Fi) | rtw_8821ce | rtw88_8821ce | No | 26 | | Intel Ice Lake-LP SPI Controller | intel-spi | spi_intel_pci | No | 27 | | Intel Ice Lake-LP SMBus Controller | i801_smbus | i2c_i801 | No | 28 | | Intel Ice Lake-LP Smart Sound Technology Audio Controller | snd_hda_intel | snd_hda_intel, snd_sof_pci_intel_icl | No | 29 | | Intel Ice Lake-LP Serial IO SPI Controller | intel-lpss | No | No | 30 | | Intel Ice Lake-LP Serial IO UART Controller | intel-lpss | No | No | 31 | | Intel Ice Lake-LP Serial IO I2C Controller | intel-lpss | No | No | 32 | | Ice Lake-LP USB 3.1 xHCI Host Controller | xhci_hcd | No | No | 33 | | Intel Processor Power and Thermal Controller | proc_thermal | processor_thermal_device_pci_legacy | No | 34 | | Intel Device 8a02 | icl_uncore | No | No | 35 | | Iris Plus Graphics G1 (Ice Lake) | i915 | i915 | No | 36 | | Intel Corporation Raptor Lake-P 6p+8e cores Host Bridge/DRAM Controller | No | No | No | 37 | | Intel Corporation Raptor Lake PCI Express 5.0 Graphics Port (PEG010) (prog-if 00 [Normal decode]) | pcieport | No | No | 38 | | Intel Corporation Raptor Lake-P [UHD Graphics] (rev 04) (prog-if 00 [VGA controller]) | i915 | i915 | No | 39 | | Intel Corporation Raptor Lake Dynamic Platform and Thermal Framework Processor Participant | proc_thermal_pci | processor_thermal_device_pci | No | 40 | | Intel Corporation Raptor Lake PCIe 4.0 Graphics Port (prog-if 00 [Normal decode]) | pcieport | No | No | 41 | | Intel Corporation Raptor Lake-P Thunderbolt 4 PCI Express Root Port #0 (prog-if 00 [Normal decode]) | pcieport | No | No | 42 | | Intel Corporation GNA Scoring Accelerator module | No | No | No | 43 | | Intel Corporation Raptor Lake-P Thunderbolt 4 USB Controller (prog-if 30 [XHCI]) | xhci_hcd | xhci_pci | No | 44 | | Intel Corporation Raptor Lake-P Thunderbolt 4 NHI #0 (prog-if 40 [USB4 Host Interface]) | thunderbolt | thunderbolt | No | 45 | | Intel Corporation Raptor Lake-P Thunderbolt 4 NHI #1 (prog-if 40 [USB4 Host Interface]) | thunderbolt | thunderbolt | No | 46 | | Intel Corporation Alder Lake PCH USB 3.2 xHCI Host Controller (rev 01) (prog-if 30 [XHCI]) | xhci_hcd | xhci_pci | No | 47 | | Intel Corporation Alder Lake PCH Shared SRAM (rev 01) | No | No | No | 48 | | Intel Corporation Raptor Lake PCH CNVi WiFi (rev 01) | iwlwifi | iwlwifi | No | 49 | | Intel Corporation Alder Lake PCH Serial IO I2C Controller #0 (rev 01) | intel-lpss | intel_lpss_pci | No | 50 | | Intel Corporation Alder Lake PCH HECI Controller (rev 01) | mei_me | mei_me | No | 51 | | Intel Corporation Device 51b8 (rev 01) (prog-if 00 [Normal decode]) | pcieport | No | No | 52 | | Intel Corporation Alder Lake-P PCH PCIe Root Port #6 (rev 01) (prog-if 00 [Normal decode]) | pcieport | No | No | 53 | | Intel Corporation Raptor Lake LPC/eSPI Controller (rev 01) | No | No | No | 54 | | Intel Corporation Raptor Lake-P/U/H cAVS (rev 01) (prog-if 80) | sof-audio-pci-intel-tgl | snd_hda_intel, snd_sof_pci_intel_tgl | No | 55 | | Intel Corporation Alder Lake PCH-P SMBus Host Controller | i801_smbus | i2c_i801 | No | 56 | | Intel Corporation Alder Lake-P PCH SPI Controller (rev 01) | intel-spi | spi_intel_pci | No | 57 | | NVIDIA Corporation GA107GLM [RTX A1000 6GB Laptop GPU] (rev a1) | nvidia | nouveau, nvidia_drm, nvidia | No | 58 | | SK hynix Platinum P41/PC801 NVMe Solid State Drive (prog-if 02 [NVM Express]) | nvme | nvme | No | 59 | | Realtek Semiconductor Co., Ltd. RTS5261 PCI Express Card Reader (rev 01) | rtsx_pci | rtsx_pci | No | 60 | 61 | 65 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "common", 4 | "executor", 5 | 6 | "acpid", 7 | "hwd", 8 | "pcid", 9 | "pcid-spawner", 10 | "rtcd", 11 | "vboxd", 12 | "xhcid", 13 | "usbctl", 14 | "usbhubd", 15 | "inputd", 16 | "virtio-core", 17 | 18 | "audio/ac97d", 19 | "audio/ihdad", 20 | "audio/sb16d", 21 | 22 | "graphics/bgad", 23 | "graphics/console-draw", 24 | "graphics/fbbootlogd", 25 | "graphics/driver-graphics", 26 | "graphics/fbcond", 27 | "graphics/graphics-ipc", 28 | "graphics/vesad", 29 | "graphics/virtio-gpud", 30 | 31 | "input/ps2d", 32 | "input/usbhidd", 33 | 34 | "net/alxd", 35 | "net/driver-network", 36 | "net/e1000d", 37 | "net/ixgbed", 38 | "net/rtl8139d", 39 | "net/rtl8168d", 40 | "net/virtio-netd", 41 | 42 | "storage/ahcid", 43 | "storage/bcm2835-sdhcid", 44 | "storage/driver-block", 45 | "storage/ided", 46 | "storage/lived", # TODO: not really a driver... 47 | "storage/nvmed", 48 | "storage/usbscsid", 49 | "storage/virtio-blkd", 50 | ] 51 | 52 | [profile.release] 53 | lto = "fat" 54 | 55 | [patch.crates-io] 56 | mio = { git = "https://gitlab.redox-os.org/redox-os/mio.git", branch = "redox-unix" } 57 | orbclient = { git = "https://gitlab.redox-os.org/redox-os/orbclient.git", version = "0.3.44" } 58 | redox-daemon = { git = "https://gitlab.redox-os.org/redox-os/redox-daemon.git" } 59 | redox_syscall = { git = "https://gitlab.redox-os.org/redox-os/syscall.git", branch = "master" } 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 Redox OS 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Drivers 2 | 3 | This document covers the driver details. 4 | 5 | ## Hardware Interfaces and Devices 6 | 7 | - ac97d - Realtek audio chipsets 8 | - acpid - ACPI interface 9 | - ahcid - SATA interface 10 | - alxd - Atheros ethernet 11 | - amlserde - a library to provide serialization/deserialization of the AML symbol table from ACPI 12 | - bgad - Bochs emulator and debugger 13 | - block-io-wrapper - Library used by other drivers 14 | - e1000d - Intel Gigabit ethernet 15 | - ided - IDE interface 16 | - ihdad - Intel HD Audio chipsets 17 | - inputd - Multiplexes input from multiple input drivers and provides that to Orbital 18 | - ixgbed - Intel 10 Gigabit ethernet 19 | - nvmed - NVMe interface 20 | - pcid - PCI interface with extensions for PCI Express 21 | - ps2d - PS/2 interface 22 | - rtl8139d - Realtek ethernet 23 | - rtl8168d - Realtek ethernet 24 | - sb16d - Sound Blaster audio 25 | - usbctl - USB control 26 | - usbhidd - USB HID 27 | - usbscsid - USB SCSI 28 | - vboxd - VirtualBox guest 29 | - vesad - VESA interface 30 | - virtio-blkd - VirtIO block device 31 | - virtio-core - VirtIO core 32 | - virtio-gpud - VirtIO GPU device 33 | - virtio-netd - VirtIO Network device 34 | - xhcid - xHCI USB controller 35 | 36 | Some drivers are work-in-progress and incomplete, read [this](https://gitlab.redox-os.org/redox-os/drivers/-/issues/41) tracking issue to verify. 37 | 38 | ## System Interfaces 39 | 40 | This section cover the interfaces used by Redox drivers. 41 | 42 | ### System Calls 43 | 44 | - `iopl` - syscall that sets the I/O privilege level. x86 has four privilege rings (0/1/2/3), of which the kernel runs in ring 0 and userspace in ring 3. IOPL can only be changed by the kernel, for obvious security reasons, and therefore the Redox kernel needs root to set it. It is unique for each process. Processes with IOPL=3 can access I/O ports, and the kernel can access them as well. 45 | 46 | ### Schemes 47 | 48 | - `/scheme/memory/physical` - allows mapping physical memory frames to driver-accessible virtual memory pages, with various available memory types: 49 | - `/scheme/memory/physical`: default memory type (currently writeback) 50 | - `/scheme/memory/physical@wb` writeback cached memory 51 | - `/scheme/memory/physical@uc`: uncacheable memory 52 | - `/scheme/memory/physical@wc`: write-combining memory 53 | - `/scheme/irq` - allows getting events from interrupts. It is used primarily by listening for its file descriptors using the `/scheme/event` scheme. 54 | 55 | ## Contribution Details 56 | 57 | ### Driver Design 58 | 59 | A device driver on Redox is an user-space daemon that use system calls and schemes to work. 60 | 61 | For operating systems with monolithic kernels, drivers use internal kernel APIs instead of common program APIs. 62 | 63 | If you want to port a driver from a monolithic OS to Redox you will need to rewrite the driver with reverse enginnering of the code logic, because the logic is adapted to internal kernel APIs (it's a hard task if the device is complex, datasheets are more easy). 64 | 65 | ### Write a Driver 66 | 67 | Datasheets are preferable, when they are freely available. Be aware that datasheets are often provided under a [Non-Disclosure Agreement](https://en.wikipedia.org/wiki/Non-disclosure_agreement) from hardware vendors, which can affect the ability to create an MIT-licensed driver. 68 | 69 | If you don't have datasheets, we recommend you to do reverse-engineering of available C code on BSD drivers. 70 | 71 | You can use the [example](https://gitlab.redox-os.org/redox-os/exampled) driver or read the code of other drivers with the same type of your device. 72 | 73 | Before testing your changes, be aware of [this](https://doc.redox-os.org/book/coding-and-building.html#a-note-about-drivers). 74 | 75 | ### Driver References 76 | 77 | If you want to reverse enginner the existing drivers, you can access the BSD code using these links: 78 | 79 | - [FreeBSD drivers](https://github.com/freebsd/freebsd-src/tree/main/sys/dev) 80 | - [NetBSD drivers](https://github.com/NetBSD/src/tree/trunk/sys/dev) 81 | - [OpenBSD drivers](https://github.com/openbsd/src/tree/master/sys/dev) 82 | 83 | ## How To Contribute 84 | 85 | To learn how to contribute to this system component you need to read the following document: 86 | 87 | - [CONTRIBUTING.md](https://gitlab.redox-os.org/redox-os/redox/-/blob/master/CONTRIBUTING.md) 88 | 89 | ## Development 90 | 91 | To learn how to do development with this system component inside the Redox build system you need to read the [Build System](https://doc.redox-os.org/book/build-system-reference.html) and [Coding and Building](https://doc.redox-os.org/book/coding-and-building.html) pages. 92 | 93 | ### How To Build 94 | 95 | To build this system component you need to download the Redox build system, you can learn how to do it on the [Building Redox](https://doc.redox-os.org/book/podman-build.html) page. 96 | 97 | This is necessary because they only work with cross-compilation to a Redox virtual machine, but you can do some testing from Linux. 98 | -------------------------------------------------------------------------------- /acpid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "acpid" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | aml = { git = "https://github.com/rw-vanc/acpi.git", branch = "cumulative" } 11 | log = "0.4" 12 | num-derive = "0.3" 13 | num-traits = "0.2" 14 | parking_lot = "0.12" 15 | plain = "0.2.3" 16 | redox-daemon = "0.1" 17 | redox_syscall = "0.5.6" 18 | redox_event = "0.4.1" 19 | rustc-hash = "1.1.0" 20 | thiserror = "1" 21 | ron = "0.8.1" 22 | 23 | amlserde = { path = "../amlserde" } 24 | common = { path = "../common" } 25 | libredox = "0.1.3" 26 | redox-scheme = "0.4" 27 | arrayvec = "0.7.6" 28 | -------------------------------------------------------------------------------- /acpid/src/acpi/dmar/drhd.rs: -------------------------------------------------------------------------------- 1 | use std::ops::{Deref, DerefMut}; 2 | 3 | use common::io::Mmio; 4 | 5 | // TODO: Only wrap with Mmio where there are hardware-registers. (Some of these structs seem to be 6 | // ring buffer entries, which are not to be treated the same way). 7 | 8 | pub struct DrhdPage { 9 | virt: *mut Drhd, 10 | } 11 | impl DrhdPage { 12 | pub fn map(base_phys: usize) -> syscall::Result { 13 | assert_eq!( 14 | base_phys % crate::acpi::PAGE_SIZE, 15 | 0, 16 | "DRHD registers must be page-aligned" 17 | ); 18 | 19 | // TODO: Uncachable? Can reads have side-effects? 20 | let virt = unsafe { 21 | common::physmap( 22 | base_phys, 23 | crate::acpi::PAGE_SIZE, 24 | common::Prot::RO, 25 | common::MemoryType::default(), 26 | )? 27 | } as *mut Drhd; 28 | 29 | Ok(Self { virt }) 30 | } 31 | } 32 | impl Deref for DrhdPage { 33 | type Target = Drhd; 34 | 35 | fn deref(&self) -> &Self::Target { 36 | unsafe { &*self.virt } 37 | } 38 | } 39 | impl DerefMut for DrhdPage { 40 | fn deref_mut(&mut self) -> &mut Self::Target { 41 | unsafe { &mut *self.virt } 42 | } 43 | } 44 | impl Drop for DrhdPage { 45 | fn drop(&mut self) { 46 | unsafe { 47 | let _ = libredox::call::munmap(self.virt.cast(), crate::acpi::PAGE_SIZE); 48 | } 49 | } 50 | } 51 | 52 | #[repr(C, packed)] 53 | pub struct DrhdFault { 54 | pub sts: Mmio, 55 | pub ctrl: Mmio, 56 | pub data: Mmio, 57 | pub addr: [Mmio; 2], 58 | _rsv: [Mmio; 2], 59 | pub log: Mmio, 60 | } 61 | 62 | #[repr(C, packed)] 63 | pub struct DrhdProtectedMemory { 64 | pub en: Mmio, 65 | pub low_base: Mmio, 66 | pub low_limit: Mmio, 67 | pub high_base: Mmio, 68 | pub high_limit: Mmio, 69 | } 70 | 71 | #[repr(C, packed)] 72 | pub struct DrhdInvalidation { 73 | pub queue_head: Mmio, 74 | pub queue_tail: Mmio, 75 | pub queue_addr: Mmio, 76 | _rsv: Mmio, 77 | pub cmpl_sts: Mmio, 78 | pub cmpl_ctrl: Mmio, 79 | pub cmpl_data: Mmio, 80 | pub cmpl_addr: [Mmio; 2], 81 | } 82 | 83 | #[repr(C, packed)] 84 | pub struct DrhdPageRequest { 85 | pub queue_head: Mmio, 86 | pub queue_tail: Mmio, 87 | pub queue_addr: Mmio, 88 | _rsv: Mmio, 89 | pub sts: Mmio, 90 | pub ctrl: Mmio, 91 | pub data: Mmio, 92 | pub addr: [Mmio; 2], 93 | } 94 | 95 | #[repr(C, packed)] 96 | pub struct DrhdMtrrVariable { 97 | pub base: Mmio, 98 | pub mask: Mmio, 99 | } 100 | 101 | #[repr(C, packed)] 102 | pub struct DrhdMtrr { 103 | pub cap: Mmio, 104 | pub def_type: Mmio, 105 | pub fixed: [Mmio; 11], 106 | pub variable: [DrhdMtrrVariable; 10], 107 | } 108 | 109 | #[repr(C, packed)] 110 | pub struct Drhd { 111 | pub version: Mmio, 112 | _rsv: Mmio, 113 | pub cap: Mmio, 114 | pub ext_cap: Mmio, 115 | pub gl_cmd: Mmio, 116 | pub gl_sts: Mmio, 117 | pub root_table: Mmio, 118 | pub ctx_cmd: Mmio, 119 | _rsv1: Mmio, 120 | pub fault: DrhdFault, 121 | _rsv2: Mmio, 122 | pub pm: DrhdProtectedMemory, 123 | pub invl: DrhdInvalidation, 124 | _rsv3: Mmio, 125 | pub intr_table: Mmio, 126 | pub page_req: DrhdPageRequest, 127 | pub mtrr: DrhdMtrr, 128 | } 129 | -------------------------------------------------------------------------------- /amlserde/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "amlserde" 3 | version = "0.0.1" 4 | authors = ["Ron Williams"] 5 | repository = "https://gitlab.redox-os.org/redox-os/drivers" 6 | description = "Library for serializing AML symbols" 7 | categories = ["hardware-support"] 8 | license = "MIT/Apache-2.0" 9 | edition = "2021" 10 | 11 | [dependencies] 12 | aml = { git = "https://github.com/rw-vanc/acpi.git", branch = "cumulative" } 13 | rustc-hash = "1.1.0" 14 | serde = { version = "1.0", features = ["derive"] } 15 | toml = "0.7.3" 16 | -------------------------------------------------------------------------------- /audio/ac97d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ac97d" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "1" 8 | common = { path = "../../common" } 9 | libredox = "0.1.3" 10 | log = "0.4" 11 | redox-daemon = "0.1" 12 | redox_event = "0.4.1" 13 | redox_syscall = "0.5" 14 | spin = "0.9" 15 | 16 | pcid = { path = "../../pcid" } 17 | -------------------------------------------------------------------------------- /audio/ac97d/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "AC97 Audio" 3 | class = 0x04 4 | subclass = 0x01 5 | command = ["ac97d"] 6 | -------------------------------------------------------------------------------- /audio/ihdad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ihdad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | libredox = "0.1.3" 9 | log = "0.4" 10 | redox-daemon = "0.1" 11 | redox_event = "0.4.1" 12 | redox_syscall = "0.5" 13 | spin = "0.9" 14 | 15 | common = { path = "../../common" } 16 | pcid = { path = "../../pcid" } 17 | -------------------------------------------------------------------------------- /audio/ihdad/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "Intel HD Audio" 3 | class = 0x04 4 | subclass = 0x03 5 | command = ["ihdad"] 6 | -------------------------------------------------------------------------------- /audio/ihdad/src/hda/mod.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | pub mod cmdbuff; 3 | pub mod common; 4 | pub mod device; 5 | pub mod node; 6 | pub mod stream; 7 | 8 | pub use self::node::*; 9 | pub use self::stream::*; 10 | 11 | pub use self::cmdbuff::*; 12 | pub use self::device::IntelHDA; 13 | pub use self::stream::BitsPerSample; 14 | pub use self::stream::BufferDescriptorListEntry; 15 | pub use self::stream::StreamBuffer; 16 | pub use self::stream::StreamDescriptorRegs; 17 | -------------------------------------------------------------------------------- /audio/ihdad/src/hda/node.rs: -------------------------------------------------------------------------------- 1 | use super::common::*; 2 | use std::{fmt, mem}; 3 | 4 | #[derive(Clone)] 5 | pub struct HDANode { 6 | pub addr: WidgetAddr, 7 | 8 | // 0x4 9 | pub subnode_count: u16, 10 | pub subnode_start: u16, 11 | 12 | // 0x5 13 | pub function_group_type: u8, 14 | 15 | // 0x9 16 | pub capabilities: u32, 17 | 18 | // 0xE 19 | pub conn_list_len: u8, 20 | 21 | pub connections: Vec, 22 | 23 | pub connection_default: u8, 24 | 25 | pub is_widget: bool, 26 | 27 | pub config_default: u32, 28 | } 29 | 30 | impl HDANode { 31 | pub fn new() -> HDANode { 32 | HDANode { 33 | addr: (0, 0), 34 | subnode_count: 0, 35 | subnode_start: 0, 36 | function_group_type: 0, 37 | capabilities: 0, 38 | conn_list_len: 0, 39 | 40 | config_default: 0, 41 | is_widget: false, 42 | connections: Vec::::new(), 43 | connection_default: 0, 44 | } 45 | } 46 | 47 | pub fn widget_type(&self) -> HDAWidgetType { 48 | unsafe { mem::transmute(((self.capabilities >> 20) & 0xF) as u8) } 49 | } 50 | 51 | pub fn device_default(&self) -> Option { 52 | if self.widget_type() != HDAWidgetType::PinComplex { 53 | None 54 | } else { 55 | Some(unsafe { mem::transmute(((self.config_default >> 20) & 0xF) as u8) }) 56 | } 57 | } 58 | 59 | pub fn configuration_default(&self) -> ConfigurationDefault { 60 | ConfigurationDefault::from_u32(self.config_default) 61 | } 62 | 63 | pub fn addr(&self) -> WidgetAddr { 64 | self.addr 65 | } 66 | } 67 | 68 | impl fmt::Display for HDANode { 69 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 70 | if self.addr == (0, 0) { 71 | write!( 72 | f, 73 | "Addr: {:02X}:{:02X}, Root Node.", 74 | self.addr.0, self.addr.1 75 | ) 76 | } else if self.is_widget { 77 | match self.widget_type() { 78 | HDAWidgetType::PinComplex => write!( 79 | f, 80 | "Addr: {:02X}:{:02X}, Type: {:?}: {:?}, Inputs: {}/{}: {:X?}.", 81 | self.addr.0, 82 | self.addr.1, 83 | self.widget_type(), 84 | self.device_default().unwrap(), 85 | self.connection_default, 86 | self.conn_list_len, 87 | self.connections 88 | ), 89 | _ => write!( 90 | f, 91 | "Addr: {:02X}:{:02X}, Type: {:?}, Inputs: {}/{}: {:X?}.", 92 | self.addr.0, 93 | self.addr.1, 94 | self.widget_type(), 95 | self.connection_default, 96 | self.conn_list_len, 97 | self.connections 98 | ), 99 | } 100 | } else { 101 | write!( 102 | f, 103 | "Addr: {:02X}:{:02X}, AFG: {}, Widget count {}.", 104 | self.addr.0, self.addr.1, self.function_group_type, self.subnode_count 105 | ) 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /audio/sb16d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sb16d" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | common = { path = "../../common" } 9 | libredox = "0.1.3" 10 | log = "0.4" 11 | redox-daemon = "0.1" 12 | redox_event = "0.4.1" 13 | redox_syscall = "0.5" 14 | spin = "0.9" 15 | -------------------------------------------------------------------------------- /common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | libredox = "0.1.3" 12 | log = "0.4" 13 | redox_syscall = { version = "0.5.10", features = ["std"] } 14 | redox-log = "0.1.2" 15 | -------------------------------------------------------------------------------- /common/src/io.rs: -------------------------------------------------------------------------------- 1 | use core::{ 2 | cmp::PartialEq, 3 | ops::{BitAnd, BitOr, Not}, 4 | }; 5 | 6 | mod mmio; 7 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 8 | mod pio; 9 | 10 | pub use mmio::*; 11 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 12 | pub use pio::*; 13 | 14 | /// IO abstraction 15 | pub trait Io { 16 | /// Value type for IO, usually some unsigned number 17 | type Value: Copy 18 | + PartialEq 19 | + BitAnd 20 | + BitOr 21 | + Not; 22 | 23 | /// Read the underlying valu2e 24 | fn read(&self) -> Self::Value; 25 | /// Write the underlying value 26 | fn write(&mut self, value: Self::Value); 27 | 28 | /// Check whether the underlying value contains bit flags 29 | #[inline(always)] 30 | fn readf(&self, flags: Self::Value) -> bool { 31 | (self.read() & flags) as Self::Value == flags 32 | } 33 | 34 | /// Enable or disable specific bit flags 35 | #[inline(always)] 36 | fn writef(&mut self, flags: Self::Value, value: bool) { 37 | let tmp: Self::Value = match value { 38 | true => self.read() | flags, 39 | false => self.read() & !flags, 40 | }; 41 | self.write(tmp); 42 | } 43 | } 44 | 45 | /// Read-only IO 46 | #[repr(transparent)] 47 | pub struct ReadOnly { 48 | inner: I, 49 | } 50 | 51 | impl ReadOnly { 52 | /// Wraps IO 53 | pub const fn new(inner: I) -> ReadOnly { 54 | ReadOnly { inner } 55 | } 56 | } 57 | 58 | impl ReadOnly { 59 | /// Calls [Io::read] 60 | #[inline(always)] 61 | pub fn read(&self) -> I::Value { 62 | self.inner.read() 63 | } 64 | 65 | /// Calls [Io::readf] 66 | #[inline(always)] 67 | pub fn readf(&self, flags: I::Value) -> bool { 68 | self.inner.readf(flags) 69 | } 70 | } 71 | 72 | #[repr(transparent)] 73 | /// Write-only IO 74 | pub struct WriteOnly { 75 | inner: I, 76 | } 77 | 78 | impl WriteOnly { 79 | /// Wraps IO 80 | pub const fn new(inner: I) -> WriteOnly { 81 | WriteOnly { inner } 82 | } 83 | } 84 | 85 | impl WriteOnly { 86 | /// Calls [Io::write] 87 | #[inline(always)] 88 | pub fn write(&mut self, value: I::Value) { 89 | self.inner.write(value) 90 | } 91 | 92 | #[inline(always)] 93 | /// Calls [Io::writef] 94 | pub fn writef(&mut self, flags: I::Value, value: bool) { 95 | self.inner.writef(flags, value) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /common/src/io/pio.rs: -------------------------------------------------------------------------------- 1 | use core::{arch::asm, marker::PhantomData}; 2 | 3 | use super::Io; 4 | 5 | /// Generic PIO 6 | #[derive(Copy, Clone)] 7 | pub struct Pio { 8 | port: u16, 9 | value: PhantomData, 10 | } 11 | 12 | impl Pio { 13 | /// Create a PIO from a given port 14 | pub const fn new(port: u16) -> Self { 15 | Pio:: { 16 | port, 17 | value: PhantomData, 18 | } 19 | } 20 | } 21 | 22 | /// Read/Write for byte PIO 23 | impl Io for Pio { 24 | type Value = u8; 25 | 26 | /// Read 27 | #[inline(always)] 28 | fn read(&self) -> u8 { 29 | let value: u8; 30 | unsafe { 31 | asm!("in al, dx", in("dx") self.port, out("al") value, options(nostack, nomem, preserves_flags)); 32 | } 33 | value 34 | } 35 | 36 | /// Write 37 | #[inline(always)] 38 | fn write(&mut self, value: u8) { 39 | unsafe { 40 | asm!("out dx, al", in("dx") self.port, in("al") value, options(nostack, nomem, preserves_flags)); 41 | } 42 | } 43 | } 44 | 45 | /// Read/Write for word PIO 46 | impl Io for Pio { 47 | type Value = u16; 48 | 49 | /// Read 50 | #[inline(always)] 51 | fn read(&self) -> u16 { 52 | let value: u16; 53 | unsafe { 54 | asm!("in ax, dx", in("dx") self.port, out("ax") value, options(nostack, nomem, preserves_flags)); 55 | } 56 | value 57 | } 58 | 59 | /// Write 60 | #[inline(always)] 61 | fn write(&mut self, value: u16) { 62 | unsafe { 63 | asm!("out dx, ax", in("dx") self.port, in("ax") value, options(nostack, nomem, preserves_flags)); 64 | } 65 | } 66 | } 67 | 68 | /// Read/Write for doubleword PIO 69 | impl Io for Pio { 70 | type Value = u32; 71 | 72 | /// Read 73 | #[inline(always)] 74 | fn read(&self) -> u32 { 75 | let value: u32; 76 | unsafe { 77 | asm!("in eax, dx", in("dx") self.port, out("eax") value, options(nostack, nomem, preserves_flags)); 78 | } 79 | value 80 | } 81 | 82 | /// Write 83 | #[inline(always)] 84 | fn write(&mut self, value: u32) { 85 | unsafe { 86 | asm!("out dx, eax", in("dx") self.port, in("eax") value, options(nostack, nomem, preserves_flags)); 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /common/src/logger.rs: -------------------------------------------------------------------------------- 1 | use redox_log::{OutputBuilder, RedoxLogger}; 2 | 3 | /// Configures logging for a single driver. 4 | #[cfg_attr(not(target_os = "redox"), allow(unused_variables, unused_mut))] 5 | pub fn setup_logging( 6 | category: &str, 7 | subcategory: &str, 8 | logfile_base: &str, 9 | output_level: log::LevelFilter, 10 | file_level: log::LevelFilter, 11 | ) { 12 | let mut logger = RedoxLogger::new().with_output( 13 | OutputBuilder::stderr() 14 | .with_filter(output_level) // limit global output to important info 15 | .with_ansi_escape_codes() 16 | .flush_on_newline(true) 17 | .build(), 18 | ); 19 | 20 | #[cfg(target_os = "redox")] 21 | match OutputBuilder::in_redox_logging_scheme( 22 | category, 23 | subcategory, 24 | format!("{logfile_base}.log"), 25 | ) { 26 | Ok(b) => { 27 | logger = logger.with_output(b.with_filter(file_level).flush_on_newline(true).build()) 28 | } 29 | Err(error) => eprintln!("Failed to create {logfile_base}.log: {}", error), 30 | } 31 | 32 | #[cfg(target_os = "redox")] 33 | match OutputBuilder::in_redox_logging_scheme( 34 | category, 35 | subcategory, 36 | format!("{logfile_base}.ansi.log"), 37 | ) { 38 | Ok(b) => { 39 | logger = logger.with_output( 40 | b.with_filter(file_level) 41 | .with_ansi_escape_codes() 42 | .flush_on_newline(true) 43 | .build(), 44 | ) 45 | } 46 | Err(error) => eprintln!("Failed to create {logfile_base}.ansi.log: {}", error), 47 | } 48 | 49 | logger.enable().expect("failed to set default logger"); 50 | } 51 | -------------------------------------------------------------------------------- /common/src/sgl.rs: -------------------------------------------------------------------------------- 1 | use std::num::NonZeroUsize; 2 | 3 | use libredox::call::MmapArgs; 4 | use libredox::errno::EINVAL; 5 | use libredox::error::{Error, Result}; 6 | use libredox::flag::{MAP_PRIVATE, PROT_READ, PROT_WRITE}; 7 | use syscall::{MAP_FIXED, PAGE_SIZE}; 8 | 9 | use crate::dma::phys_contiguous_fd; 10 | use crate::VirtaddrTranslationHandle; 11 | 12 | /// A Scatter-Gather List data structure 13 | /// 14 | /// See: 15 | #[derive(Debug)] 16 | pub struct Sgl { 17 | /// A raw pointer to the SGL in virtual memory 18 | virt: *mut u8, 19 | /// The length of the allocated memory, guaranteed to be a multiple of [PAGE_SIZE]. 20 | aligned_length: usize, 21 | /// The length of the allocated memory. This value is NOT guaranteed to be a multiple of [PAGE_SIZE] 22 | unaligned_length: NonZeroUsize, 23 | /// The vector of chunks tracked by this [Sgl] object. This is the sparsely-populated vector in the SGL algorithm. 24 | chunks: Vec, 25 | } 26 | 27 | /// A structure representing a chunk of memory in the sparsely-populated vector of the SGL 28 | #[derive(Debug)] 29 | pub struct Chunk { 30 | /// The offset of the chunk in the sparsely-populated vector. 31 | pub offset: usize, 32 | /// The physical address of the chunk 33 | pub phys: usize, 34 | /// A raw pointer to the chunk in virtual memory 35 | pub virt: *mut u8, 36 | /// The length of the chunk in bytes. 37 | pub length: usize, 38 | } 39 | 40 | impl Sgl { 41 | /// Constructor for the scatter/gather list. 42 | /// 43 | /// # Arguments 44 | /// 45 | /// 'unaligned_length: [usize]' - The length of the SGL, not necessarily aligned to the nearest 46 | /// page. 47 | pub fn new(unaligned_length: usize) -> Result { 48 | let unaligned_length = NonZeroUsize::new(unaligned_length).ok_or(Error::new(EINVAL))?; 49 | 50 | // TODO: Both PAGE_SIZE and MAX_ALLOC_SIZE should be dynamic. 51 | let aligned_length = unaligned_length.get().next_multiple_of(PAGE_SIZE); 52 | const MAX_ALLOC_SIZE: usize = 1 << 22; 53 | 54 | unsafe { 55 | let virt = libredox::call::mmap(MmapArgs { 56 | flags: MAP_PRIVATE, 57 | prot: PROT_READ | PROT_WRITE, 58 | length: aligned_length, 59 | 60 | offset: 0, 61 | fd: !0, 62 | addr: core::ptr::null_mut(), 63 | })? 64 | .cast::(); 65 | 66 | let mut this = Self { 67 | virt, 68 | aligned_length, 69 | unaligned_length, 70 | chunks: Vec::new(), 71 | }; 72 | 73 | // TODO: SglContext to avoid reopening these fds? 74 | let phys_contiguous_fd = phys_contiguous_fd()?; 75 | let virttophys_handle = VirtaddrTranslationHandle::new()?; 76 | 77 | let mut offset = 0; 78 | while offset < aligned_length { 79 | let preferred_chunk_length = (aligned_length - offset) 80 | .min(MAX_ALLOC_SIZE) 81 | .next_power_of_two(); 82 | let chunk_length = if preferred_chunk_length > aligned_length - offset { 83 | preferred_chunk_length / 2 84 | } else { 85 | preferred_chunk_length 86 | }; 87 | libredox::call::mmap(MmapArgs { 88 | addr: virt.add(offset).cast(), 89 | flags: MAP_PRIVATE | (MAP_FIXED.bits() as u32), 90 | prot: PROT_READ | PROT_WRITE, 91 | length: chunk_length, 92 | fd: phys_contiguous_fd.raw(), 93 | 94 | offset: 0, 95 | })?; 96 | let phys = virttophys_handle.translate(virt as usize + offset)?; 97 | this.chunks.push(Chunk { 98 | offset, 99 | phys, 100 | length: (unaligned_length.get() - offset).min(chunk_length), 101 | virt: virt.add(offset), 102 | }); 103 | offset += chunk_length; 104 | } 105 | 106 | Ok(this) 107 | } 108 | } 109 | /// Returns an immutable reference to the vector of chunks 110 | pub fn chunks(&self) -> &[Chunk] { 111 | &self.chunks 112 | } 113 | 114 | /// Returns a raw pointer to the vector of chunks in virtual memory 115 | pub fn as_ptr(&self) -> *mut u8 { 116 | self.virt 117 | } 118 | /// Returns the length of the scatter-gather list. 119 | pub fn len(&self) -> usize { 120 | self.unaligned_length.get() 121 | } 122 | } 123 | 124 | impl Drop for Sgl { 125 | fn drop(&mut self) { 126 | unsafe { 127 | let _ = libredox::call::munmap(self.virt.cast(), self.aligned_length); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /executor/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "executor" 3 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 4 | version = "0.1.0" 5 | edition = "2021" 6 | license = "MIT" 7 | description = "Async framework for queue-based HW interfaces" 8 | 9 | [dependencies] 10 | log = "0.4" 11 | redox_event = "0.4.1" 12 | slab = "0.4.9" 13 | -------------------------------------------------------------------------------- /fmt.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/bash 2 | 3 | set -eo pipefail 4 | 5 | printf "\e[1;32mFormatting\e[0m $dir\n" 6 | if [[ "$CHECK_ONLY" -eq "1" ]]; then 7 | cargo fmt --all --check 8 | else 9 | cargo fmt --all 10 | fi 11 | -------------------------------------------------------------------------------- /graphics/bgad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bgad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | orbclient = "0.3.47" 8 | redox-daemon = "0.1" 9 | redox-scheme = "0.4" 10 | redox_syscall = "0.5" 11 | 12 | common = { path = "../../common" } 13 | inputd = { path = "../../inputd" } 14 | pcid = { path = "../../pcid" } 15 | libredox = "0.1.3" 16 | -------------------------------------------------------------------------------- /graphics/bgad/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "QEMU Graphics Array" 3 | class = 0x03 4 | vendor = 0x1234 5 | device = 0x1111 6 | command = ["bgad"] 7 | 8 | [[drivers]] 9 | name = "VirtualBox Graphics Array" 10 | class = 0x03 11 | vendor = 0x80EE 12 | device = 0xBEEF 13 | command = ["bgad"] 14 | -------------------------------------------------------------------------------- /graphics/bgad/src/bga.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Pio}; 2 | 3 | const BGA_INDEX_XRES: u16 = 1; 4 | const BGA_INDEX_YRES: u16 = 2; 5 | const BGA_INDEX_BPP: u16 = 3; 6 | const BGA_INDEX_ENABLE: u16 = 4; 7 | 8 | pub struct Bga { 9 | index: Pio, 10 | data: Pio, 11 | } 12 | 13 | impl Bga { 14 | pub fn new() -> Bga { 15 | Bga { 16 | index: Pio::new(0x1CE), 17 | data: Pio::new(0x1CF), 18 | } 19 | } 20 | 21 | fn read(&mut self, index: u16) -> u16 { 22 | self.index.write(index); 23 | self.data.read() 24 | } 25 | 26 | fn write(&mut self, index: u16, data: u16) { 27 | self.index.write(index); 28 | self.data.write(data); 29 | } 30 | 31 | pub fn width(&mut self) -> u16 { 32 | self.read(BGA_INDEX_XRES) 33 | } 34 | 35 | pub fn height(&mut self) -> u16 { 36 | self.read(BGA_INDEX_YRES) 37 | } 38 | 39 | #[allow(dead_code)] 40 | pub fn set_size(&mut self, width: u16, height: u16) { 41 | self.write(BGA_INDEX_ENABLE, 0); 42 | self.write(BGA_INDEX_XRES, width); 43 | self.write(BGA_INDEX_YRES, height); 44 | self.write(BGA_INDEX_BPP, 32); 45 | self.write(BGA_INDEX_ENABLE, 0x41); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /graphics/bgad/src/main.rs: -------------------------------------------------------------------------------- 1 | use common::acquire_port_io_rights; 2 | use inputd::ProducerHandle; 3 | use pcid_interface::PciFunctionHandle; 4 | use redox_scheme::{RequestKind, SignalBehavior, Socket}; 5 | 6 | use crate::bga::Bga; 7 | use crate::scheme::BgaScheme; 8 | 9 | mod bga; 10 | mod scheme; 11 | 12 | fn main() { 13 | let pcid_handle = PciFunctionHandle::connect_default(); 14 | let pci_config = pcid_handle.config(); 15 | 16 | let mut name = pci_config.func.name(); 17 | name.push_str("_bga"); 18 | 19 | println!(" + BGA {}", pci_config.func.display()); 20 | 21 | redox_daemon::Daemon::new(move |daemon| { 22 | acquire_port_io_rights().expect("bgad: failed to get port IO permission"); 23 | 24 | let socket = Socket::create("bga").expect("bgad: failed to create bga scheme"); 25 | 26 | let mut bga = Bga::new(); 27 | println!(" - BGA {}x{}", bga.width(), bga.height()); 28 | 29 | let mut scheme = BgaScheme { 30 | bga, 31 | display: ProducerHandle::new().ok(), 32 | }; 33 | 34 | scheme.update_size(); 35 | 36 | libredox::call::setrens(0, 0).expect("bgad: failed to enter null namespace"); 37 | 38 | daemon.ready().expect("bgad: failed to notify parent"); 39 | 40 | loop { 41 | let Some(request) = socket 42 | .next_request(SignalBehavior::Restart) 43 | .expect("bgad: failed to get next scheme request") 44 | else { 45 | // Scheme likely got unmounted 46 | std::process::exit(0); 47 | }; 48 | match request.kind() { 49 | RequestKind::Call(call) => { 50 | let response = call.handle_scheme(&mut scheme); 51 | 52 | socket 53 | .write_responses(&[response], SignalBehavior::Restart) 54 | .expect("bgad: failed to write next scheme response"); 55 | } 56 | RequestKind::OnClose { id } => { 57 | scheme.on_close(id); 58 | } 59 | _ => (), 60 | } 61 | } 62 | }) 63 | .expect("bgad: failed to daemonize"); 64 | } 65 | -------------------------------------------------------------------------------- /graphics/bgad/src/scheme.rs: -------------------------------------------------------------------------------- 1 | use inputd::ProducerHandle; 2 | use redox_scheme::Scheme; 3 | use std::str; 4 | use syscall::data::Stat; 5 | use syscall::{Error, Result, EACCES, EINVAL, MODE_CHR}; 6 | 7 | use crate::bga::Bga; 8 | 9 | pub struct BgaScheme { 10 | pub bga: Bga, 11 | pub display: Option, 12 | } 13 | 14 | impl BgaScheme { 15 | pub fn update_size(&mut self) { 16 | if let Some(ref mut display) = self.display { 17 | let _ = display.write_event( 18 | orbclient::ResizeEvent { 19 | width: self.bga.width() as u32, 20 | height: self.bga.height() as u32, 21 | } 22 | .to_event(), 23 | ); 24 | } 25 | } 26 | } 27 | 28 | impl Scheme for BgaScheme { 29 | fn open(&mut self, _path: &str, _flags: usize, uid: u32, _gid: u32) -> Result { 30 | if uid == 0 { 31 | Ok(0) 32 | } else { 33 | Err(Error::new(EACCES)) 34 | } 35 | } 36 | 37 | fn read( 38 | &mut self, 39 | _id: usize, 40 | buf: &mut [u8], 41 | _offset: u64, 42 | _fcntl_flags: u32, 43 | ) -> Result { 44 | let mut i = 0; 45 | let data = format!("{},{}\n", self.bga.width(), self.bga.height()).into_bytes(); 46 | while i < buf.len() && i < data.len() { 47 | buf[i] = data[i]; 48 | i += 1; 49 | } 50 | Ok(i) 51 | } 52 | 53 | fn write(&mut self, _id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result { 54 | let string = str::from_utf8(buf).or(Err(Error::new(EINVAL)))?; 55 | let string = string.trim(); 56 | 57 | let mut parts = string.split(','); 58 | 59 | let width = if let Some(part) = parts.next() { 60 | part.parse::().or(Err(Error::new(EINVAL)))? 61 | } else { 62 | self.bga.width() 63 | }; 64 | 65 | let height = if let Some(part) = parts.next() { 66 | part.parse::().or(Err(Error::new(EINVAL)))? 67 | } else { 68 | self.bga.height() 69 | }; 70 | 71 | self.bga.set_size(width, height); 72 | 73 | self.update_size(); 74 | 75 | Ok(buf.len()) 76 | } 77 | 78 | fn fpath(&mut self, _file: usize, buf: &mut [u8]) -> Result { 79 | let mut i = 0; 80 | let scheme_path = b"bga"; 81 | while i < buf.len() && i < scheme_path.len() { 82 | buf[i] = scheme_path[i]; 83 | i += 1; 84 | } 85 | Ok(i) 86 | } 87 | 88 | fn fstat(&mut self, _id: usize, stat: &mut Stat) -> Result { 89 | *stat = Stat { 90 | st_mode: MODE_CHR | 0o666, 91 | ..Default::default() 92 | }; 93 | 94 | Ok(0) 95 | } 96 | 97 | fn fcntl(&mut self, _id: usize, _cmd: usize, _arg: usize) -> Result { 98 | Ok(0) 99 | } 100 | } 101 | 102 | impl BgaScheme { 103 | pub fn on_close(&mut self, _id: usize) {} 104 | } 105 | -------------------------------------------------------------------------------- /graphics/console-draw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "console-draw" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | orbclient = "0.3.27" 8 | ransid = "0.4" 9 | 10 | graphics-ipc = { path = "../graphics-ipc" } 11 | 12 | [features] 13 | default = [] 14 | -------------------------------------------------------------------------------- /graphics/driver-graphics/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "driver-graphics" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | redox-scheme = "0.4" 9 | redox_syscall = "0.5" 10 | libredox = "0.1.3" 11 | 12 | common = { path = "../../common" } 13 | graphics-ipc = { path = "../graphics-ipc" } 14 | inputd = { path = "../../inputd" } 15 | -------------------------------------------------------------------------------- /graphics/fbbootlogd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fbbootlogd" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | orbclient = "0.3.27" 8 | ransid = "0.4" 9 | redox_event = "0.4" 10 | redox_syscall = "0.5" 11 | redox-daemon = "0.1" 12 | redox-scheme = "0.4" 13 | 14 | console-draw = { path = "../console-draw" } 15 | graphics-ipc = { path = "../graphics-ipc" } 16 | inputd = { path = "../../inputd" } 17 | libredox = "0.1.3" 18 | 19 | [features] 20 | default = [] 21 | -------------------------------------------------------------------------------- /graphics/fbbootlogd/src/main.rs: -------------------------------------------------------------------------------- 1 | //! Fbbootlogd renders the boot log and presents it on VT1. 2 | //! 3 | //! While fbbootlogd is superficially similar to fbcond, the major difference is: 4 | //! 5 | //! * Fbbootlogd doesn't accept input coming from the keyboard. It only allows getting written to. 6 | //! 7 | //! In the future fbbootlogd may also pull from logd as opposed to have logd push logs to it. And it 8 | //! it could display a boot splash like plymouth instead of a boot log when booting in quiet mode. 9 | 10 | use std::io::Write; 11 | use std::os::fd::AsRawFd; 12 | 13 | use event::EventQueue; 14 | use inputd::ConsumerHandleEvent; 15 | use libredox::errno::EAGAIN; 16 | use orbclient::Event; 17 | use redox_scheme::{RequestKind, SignalBehavior, Socket}; 18 | 19 | use crate::scheme::FbbootlogScheme; 20 | 21 | mod scheme; 22 | 23 | fn main() { 24 | redox_daemon::Daemon::new(|daemon| inner(daemon)).expect("failed to create daemon"); 25 | } 26 | fn inner(daemon: redox_daemon::Daemon) -> ! { 27 | let event_queue = EventQueue::new().expect("fbbootlogd: failed to create event queue"); 28 | 29 | event::user_data! { 30 | enum Source { 31 | Scheme, 32 | Input, 33 | } 34 | } 35 | 36 | let socket = 37 | Socket::nonblock("fbbootlog").expect("fbbootlogd: failed to create fbbootlog scheme"); 38 | 39 | { 40 | // Add ourself as log sink 41 | let mut log_file = std::fs::OpenOptions::new() 42 | .write(true) 43 | .open("/scheme/log/add_sink") 44 | .unwrap(); 45 | log_file.write(b"/scheme/fbbootlog").unwrap(); 46 | } 47 | 48 | event_queue 49 | .subscribe( 50 | socket.inner().raw(), 51 | Source::Scheme, 52 | event::EventFlags::READ, 53 | ) 54 | .expect("fbcond: failed to subscribe to scheme events"); 55 | 56 | let mut scheme = FbbootlogScheme::new(); 57 | 58 | event_queue 59 | .subscribe( 60 | scheme.input_handle.event_handle().as_raw_fd() as usize, 61 | Source::Input, 62 | event::EventFlags::READ, 63 | ) 64 | .expect("fbbootlogd: failed to subscribe to scheme events"); 65 | 66 | // This is not possible for now as fbbootlogd needs to open new displays at runtime for graphics 67 | // driver handoff. In the future inputd may directly pass a handle to the display instead. 68 | //libredox::call::setrens(0, 0).expect("fbbootlogd: failed to enter null namespace"); 69 | 70 | daemon.ready().expect("failed to notify parent"); 71 | 72 | for event in event_queue { 73 | match event.expect("fbbootlogd: failed to get event").user_data { 74 | Source::Scheme => { 75 | loop { 76 | let request = match socket.next_request(SignalBehavior::Restart) { 77 | Ok(Some(request)) => request, 78 | Ok(None) => { 79 | // Scheme likely got unmounted 80 | std::process::exit(0); 81 | } 82 | Err(err) if err.errno == EAGAIN => break, 83 | Err(err) => panic!("fbbootlogd: failed to read display scheme: {err:?}"), 84 | }; 85 | 86 | match request.kind() { 87 | RequestKind::Call(call) => { 88 | let response = call.handle_scheme(&mut scheme); 89 | 90 | socket 91 | .write_responses(&[response], SignalBehavior::Restart) 92 | .expect("pcid: failed to write next scheme response"); 93 | } 94 | RequestKind::OnClose { id } => { 95 | scheme.on_close(id); 96 | } 97 | _ => (), 98 | } 99 | } 100 | } 101 | Source::Input => { 102 | let mut events = [Event::new(); 16]; 103 | loop { 104 | match scheme 105 | .input_handle 106 | .read_events(&mut events) 107 | .expect("fbbootlogd: error while reading events") 108 | { 109 | ConsumerHandleEvent::Events(&[]) => break, 110 | ConsumerHandleEvent::Events(_) => {} 111 | ConsumerHandleEvent::Handoff => { 112 | eprintln!("fbbootlogd: handoff requested"); 113 | scheme.handle_handoff(); 114 | } 115 | } 116 | } 117 | } 118 | } 119 | } 120 | 121 | std::process::exit(0); 122 | } 123 | -------------------------------------------------------------------------------- /graphics/fbbootlogd/src/scheme.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | 3 | use graphics_ipc::v1::V1GraphicsHandle; 4 | use inputd::ConsumerHandle; 5 | use redox_scheme::Scheme; 6 | use syscall::{Error, Result, EINVAL, ENOENT}; 7 | 8 | pub struct DisplayMap { 9 | display_handle: V1GraphicsHandle, 10 | inner: graphics_ipc::v1::DisplayMap, 11 | } 12 | 13 | pub struct FbbootlogScheme { 14 | pub input_handle: ConsumerHandle, 15 | display_map: Option, 16 | text_screen: console_draw::TextScreen, 17 | } 18 | 19 | impl FbbootlogScheme { 20 | pub fn new() -> FbbootlogScheme { 21 | let mut scheme = FbbootlogScheme { 22 | input_handle: ConsumerHandle::new_vt().expect("fbbootlogd: Failed to open vt"), 23 | display_map: None, 24 | text_screen: console_draw::TextScreen::new(), 25 | }; 26 | 27 | scheme.handle_handoff(); 28 | 29 | scheme 30 | } 31 | 32 | pub fn handle_handoff(&mut self) { 33 | let new_display_handle = match self.input_handle.open_display() { 34 | Ok(display) => V1GraphicsHandle::from_file(display).unwrap(), 35 | Err(err) => { 36 | eprintln!("fbbootlogd: No display present yet: {err}"); 37 | return; 38 | } 39 | }; 40 | 41 | match new_display_handle.map_display() { 42 | Ok(display_map) => { 43 | self.display_map = Some(DisplayMap { 44 | display_handle: new_display_handle, 45 | inner: display_map, 46 | }); 47 | 48 | eprintln!("fbbootlogd: mapped display"); 49 | } 50 | Err(err) => { 51 | eprintln!("fbbootlogd: failed to open display: {}", err); 52 | } 53 | } 54 | } 55 | } 56 | 57 | impl Scheme for FbbootlogScheme { 58 | fn open(&mut self, path_str: &str, _flags: usize, _uid: u32, _gid: u32) -> Result { 59 | if !path_str.is_empty() { 60 | return Err(Error::new(ENOENT)); 61 | } 62 | 63 | Ok(0) 64 | } 65 | 66 | fn fpath(&mut self, _id: usize, buf: &mut [u8]) -> Result { 67 | let path = b"fbbootlog:"; 68 | 69 | let mut i = 0; 70 | while i < buf.len() && i < path.len() { 71 | buf[i] = path[i]; 72 | i += 1; 73 | } 74 | 75 | Ok(i) 76 | } 77 | 78 | fn fsync(&mut self, _id: usize) -> Result { 79 | Ok(0) 80 | } 81 | 82 | fn read( 83 | &mut self, 84 | _id: usize, 85 | _buf: &mut [u8], 86 | _offset: u64, 87 | _fcntl_flags: u32, 88 | ) -> Result { 89 | Err(Error::new(EINVAL)) 90 | } 91 | 92 | fn write(&mut self, _id: usize, buf: &[u8], _offset: u64, _fcntl_flags: u32) -> Result { 93 | if let Some(map) = &mut self.display_map { 94 | let damage = self.text_screen.write( 95 | &mut console_draw::DisplayMap { 96 | offscreen: map.inner.ptr_mut(), 97 | width: map.inner.width(), 98 | height: map.inner.height(), 99 | }, 100 | buf, 101 | &mut VecDeque::new(), 102 | ); 103 | 104 | if let Some(map) = &self.display_map { 105 | map.display_handle.sync_rect(damage).unwrap(); 106 | } 107 | } 108 | 109 | Ok(buf.len()) 110 | } 111 | } 112 | 113 | impl FbbootlogScheme { 114 | pub fn on_close(&mut self, _id: usize) {} 115 | } 116 | -------------------------------------------------------------------------------- /graphics/fbcond/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fbcond" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | orbclient = "0.3.27" 8 | ransid = "0.4" 9 | redox_event = "0.4" 10 | redox_syscall = "0.5" 11 | redox-daemon = "0.1" 12 | redox-scheme = "0.4" 13 | 14 | console-draw = { path = "../console-draw" } 15 | graphics-ipc = { path = "../graphics-ipc" } 16 | inputd = { path = "../../inputd" } 17 | libredox = "0.1.3" 18 | 19 | [features] 20 | default = [] 21 | -------------------------------------------------------------------------------- /graphics/fbcond/src/display.rs: -------------------------------------------------------------------------------- 1 | use graphics_ipc::v1::{Damage, V1GraphicsHandle}; 2 | use inputd::ConsumerHandle; 3 | use std::io; 4 | 5 | pub struct Display { 6 | pub input_handle: ConsumerHandle, 7 | pub map: Option, 8 | } 9 | 10 | pub struct DisplayMap { 11 | display_handle: V1GraphicsHandle, 12 | pub inner: graphics_ipc::v1::DisplayMap, 13 | } 14 | 15 | impl Display { 16 | pub fn open_new_vt() -> io::Result { 17 | let mut display = Self { 18 | input_handle: ConsumerHandle::new_vt()?, 19 | map: None, 20 | }; 21 | 22 | display.reopen_for_handoff(); 23 | 24 | Ok(display) 25 | } 26 | 27 | /// Re-open the display after a handoff. 28 | pub fn reopen_for_handoff(&mut self) { 29 | let new_display_handle = Self::open_display(&self.input_handle).unwrap(); 30 | 31 | eprintln!("fbcond: Opened new display"); 32 | 33 | match new_display_handle.map_display() { 34 | Ok(map) => { 35 | eprintln!( 36 | "fbcond: Mapped new display with size {}x{}", 37 | map.width(), 38 | map.height() 39 | ); 40 | 41 | self.map = Some(DisplayMap { 42 | display_handle: new_display_handle, 43 | inner: map, 44 | }); 45 | } 46 | Err(err) => { 47 | eprintln!("failed to resize display: {}", err); 48 | } 49 | } 50 | } 51 | 52 | fn open_display(input_handle: &ConsumerHandle) -> io::Result { 53 | let display_file = input_handle.open_display()?; 54 | 55 | V1GraphicsHandle::from_file(display_file) 56 | } 57 | 58 | pub fn sync_rect(&mut self, sync_rect: Damage) { 59 | if let Some(map) = &self.map { 60 | map.display_handle.sync_rect(sync_rect).unwrap(); 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /graphics/graphics-ipc/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "graphics-ipc" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | libredox = "0.1.3" 9 | 10 | common = { path = "../../common" } 11 | -------------------------------------------------------------------------------- /graphics/graphics-ipc/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod v1; 2 | -------------------------------------------------------------------------------- /graphics/graphics-ipc/src/v1.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::os::unix::io::AsRawFd; 3 | use std::{cmp, io, mem, ptr, slice}; 4 | 5 | use libredox::flag; 6 | 7 | /// A graphics handle using the v1 graphics API. 8 | /// 9 | /// The v1 graphics API only allows a single framebuffer for each VT and supports neither page 10 | /// flipping nor cursor planes. 11 | pub struct V1GraphicsHandle { 12 | file: File, 13 | } 14 | 15 | impl V1GraphicsHandle { 16 | pub fn from_file(file: File) -> io::Result { 17 | Ok(V1GraphicsHandle { file }) 18 | } 19 | 20 | pub fn map_display(&self) -> io::Result { 21 | let mut buf: [u8; 4096] = [0; 4096]; 22 | let count = 23 | libredox::call::fpath(self.file.as_raw_fd() as usize, &mut buf).unwrap_or_else(|e| { 24 | panic!("Could not read display path with fpath(): {e}"); 25 | }); 26 | 27 | let url = 28 | String::from_utf8(Vec::from(&buf[..count])).expect("Could not create Utf8 Url String"); 29 | let path = url.split(':').nth(1).expect("Could not get path from url"); 30 | 31 | let mut path_parts = path.split('/').skip(1); 32 | let width = path_parts 33 | .next() 34 | .unwrap_or("") 35 | .parse::() 36 | .unwrap_or(0); 37 | let height = path_parts 38 | .next() 39 | .unwrap_or("") 40 | .parse::() 41 | .unwrap_or(0); 42 | 43 | let display_ptr = unsafe { 44 | libredox::call::mmap(libredox::call::MmapArgs { 45 | fd: self.file.as_raw_fd() as usize, 46 | offset: 0, 47 | length: (width * height * 4), 48 | prot: flag::PROT_READ | flag::PROT_WRITE, 49 | flags: flag::MAP_SHARED, 50 | addr: core::ptr::null_mut(), 51 | })? 52 | }; 53 | let offscreen = ptr::slice_from_raw_parts_mut(display_ptr as *mut u32, width * height); 54 | 55 | Ok(DisplayMap { 56 | offscreen, 57 | width, 58 | height, 59 | }) 60 | } 61 | 62 | pub fn sync_full_screen(&self) -> io::Result<()> { 63 | libredox::call::fsync(self.file.as_raw_fd() as usize)?; 64 | Ok(()) 65 | } 66 | 67 | pub fn sync_rect(&self, sync_rect: Damage) -> io::Result<()> { 68 | libredox::call::write(self.file.as_raw_fd() as usize, unsafe { 69 | slice::from_raw_parts( 70 | ptr::addr_of!(sync_rect).cast::(), 71 | mem::size_of::(), 72 | ) 73 | })?; 74 | Ok(()) 75 | } 76 | } 77 | 78 | pub struct DisplayMap { 79 | offscreen: *mut [u32], 80 | width: usize, 81 | height: usize, 82 | } 83 | 84 | impl DisplayMap { 85 | pub fn ptr(&self) -> *const [u32] { 86 | self.offscreen 87 | } 88 | 89 | pub fn ptr_mut(&mut self) -> *mut [u32] { 90 | self.offscreen 91 | } 92 | 93 | pub fn width(&self) -> usize { 94 | self.width 95 | } 96 | 97 | pub fn height(&self) -> usize { 98 | self.height 99 | } 100 | } 101 | 102 | unsafe impl Send for DisplayMap {} 103 | unsafe impl Sync for DisplayMap {} 104 | 105 | impl Drop for DisplayMap { 106 | fn drop(&mut self) { 107 | unsafe { 108 | let _ = libredox::call::munmap(self.offscreen as *mut (), self.offscreen.len()); 109 | } 110 | } 111 | } 112 | 113 | #[derive(Debug, Copy, Clone)] 114 | #[repr(C, packed)] 115 | pub struct CursorDamage { 116 | pub header: u32, 117 | pub x: i32, 118 | pub y: i32, 119 | pub hot_x: i32, 120 | pub hot_y: i32, 121 | pub width: i32, 122 | pub height: i32, 123 | pub cursor_img_bytes: [u32; 4096], 124 | } 125 | 126 | // Keep synced with orbital's SyncRect 127 | // Technically orbital uses i32 rather than u32, but values larger than i32::MAX 128 | // would be a bug anyway. 129 | #[derive(Debug, Copy, Clone)] 130 | #[repr(packed)] 131 | pub struct Damage { 132 | pub x: u32, 133 | pub y: u32, 134 | pub width: u32, 135 | pub height: u32, 136 | } 137 | 138 | impl Damage { 139 | #[must_use] 140 | pub fn clip(mut self, width: u32, height: u32) -> Self { 141 | // Clip damage 142 | let x2 = self.x + self.width; 143 | self.x = cmp::min(self.x, width); 144 | if x2 > width { 145 | self.width = width - self.x; 146 | } 147 | 148 | let y2 = self.y + self.height; 149 | self.y = cmp::min(self.y, height); 150 | if y2 > height { 151 | self.height = height - self.y; 152 | } 153 | self 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /graphics/vesad/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vesad" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | orbclient = "0.3.27" 8 | ransid = "0.4" 9 | redox_syscall = "0.5" 10 | redox-daemon = "0.1" 11 | redox_event = "0.4.1" 12 | 13 | common = { path = "../../common" } 14 | driver-graphics = { path = "../driver-graphics" } 15 | graphics-ipc = { path = "../graphics-ipc" } 16 | inputd = { path = "../../inputd" } 17 | libredox = "0.1.3" 18 | 19 | [features] 20 | default = [] 21 | -------------------------------------------------------------------------------- /graphics/vesad/src/framebuffer.rs: -------------------------------------------------------------------------------- 1 | use std::ptr; 2 | 3 | pub struct FrameBuffer { 4 | pub onscreen: *mut [u32], 5 | pub phys: usize, 6 | pub width: usize, 7 | pub height: usize, 8 | pub stride: usize, 9 | } 10 | 11 | impl FrameBuffer { 12 | pub unsafe fn new(phys: usize, width: usize, height: usize, stride: usize) -> Self { 13 | let size = stride * height; 14 | let virt = common::physmap( 15 | phys, 16 | size * 4, 17 | common::Prot { 18 | read: true, 19 | write: true, 20 | }, 21 | common::MemoryType::WriteCombining, 22 | ) 23 | .expect("vesad: failed to map framebuffer") as *mut u32; 24 | 25 | let onscreen = ptr::slice_from_raw_parts_mut(virt, size); 26 | 27 | Self { 28 | onscreen, 29 | phys, 30 | width, 31 | height, 32 | stride, 33 | } 34 | } 35 | 36 | pub unsafe fn parse(var: &str) -> Option { 37 | fn parse_number(part: &str) -> Option { 38 | let (start, radix) = if part.starts_with("0x") { 39 | (2, 16) 40 | } else { 41 | (0, 10) 42 | }; 43 | match usize::from_str_radix(&part[start..], radix) { 44 | Ok(ok) => Some(ok), 45 | Err(err) => { 46 | eprintln!("vesad: failed to parse '{}': {}", part, err); 47 | None 48 | } 49 | } 50 | } 51 | 52 | let mut parts = var.split(','); 53 | let phys = parse_number(parts.next()?)?; 54 | let width = parse_number(parts.next()?)?; 55 | let height = parse_number(parts.next()?)?; 56 | let stride = parse_number(parts.next()?)?; 57 | Some(Self::new(phys, width, height, stride)) 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /graphics/vesad/src/scheme.rs: -------------------------------------------------------------------------------- 1 | use std::alloc::{self, Layout}; 2 | use std::convert::TryInto; 3 | use std::ptr::{self, NonNull}; 4 | 5 | use driver_graphics::{CursorFramebuffer, CursorPlane, Framebuffer, GraphicsAdapter}; 6 | use graphics_ipc::v1::Damage; 7 | use syscall::PAGE_SIZE; 8 | 9 | use crate::framebuffer::FrameBuffer; 10 | 11 | pub struct FbAdapter { 12 | pub framebuffers: Vec, 13 | } 14 | 15 | pub enum VesadCursor {} 16 | 17 | impl CursorFramebuffer for VesadCursor {} 18 | 19 | impl GraphicsAdapter for FbAdapter { 20 | type Framebuffer = GraphicScreen; 21 | type Cursor = VesadCursor; 22 | 23 | fn displays(&self) -> Vec { 24 | (0..self.framebuffers.len()).collect() 25 | } 26 | 27 | fn display_size(&self, display_id: usize) -> (u32, u32) { 28 | ( 29 | self.framebuffers[display_id].width as u32, 30 | self.framebuffers[display_id].height as u32, 31 | ) 32 | } 33 | 34 | fn create_dumb_framebuffer(&mut self, width: u32, height: u32) -> Self::Framebuffer { 35 | GraphicScreen::new(width as usize, height as usize) 36 | } 37 | 38 | fn map_dumb_framebuffer(&mut self, framebuffer: &Self::Framebuffer) -> *mut u8 { 39 | framebuffer.ptr.as_ptr().cast::() 40 | } 41 | 42 | fn update_plane(&mut self, display_id: usize, framebuffer: &Self::Framebuffer, damage: Damage) { 43 | framebuffer.sync(&mut self.framebuffers[display_id], damage) 44 | } 45 | 46 | fn supports_hw_cursor(&self) -> bool { 47 | false 48 | } 49 | 50 | fn create_cursor_framebuffer(&mut self) -> VesadCursor { 51 | unimplemented!("Vesad does not support this function"); 52 | } 53 | 54 | fn map_cursor_framebuffer(&mut self, _cursor: &Self::Cursor) -> *mut u8 { 55 | unimplemented!("Vesad does not support this function"); 56 | } 57 | 58 | fn handle_cursor(&mut self, _cursor: &mut CursorPlane, _dirty_fb: bool) { 59 | unimplemented!("Vesad does not support this function"); 60 | } 61 | } 62 | 63 | pub struct GraphicScreen { 64 | width: usize, 65 | height: usize, 66 | ptr: NonNull<[u32]>, 67 | } 68 | 69 | impl GraphicScreen { 70 | fn new(width: usize, height: usize) -> GraphicScreen { 71 | let len = width * height; 72 | let layout = Self::layout(len); 73 | let ptr = unsafe { alloc::alloc_zeroed(layout) }; 74 | let ptr = ptr::slice_from_raw_parts_mut(ptr.cast(), len); 75 | let ptr = NonNull::new(ptr).unwrap_or_else(|| alloc::handle_alloc_error(layout)); 76 | 77 | GraphicScreen { width, height, ptr } 78 | } 79 | 80 | #[inline] 81 | fn layout(len: usize) -> Layout { 82 | // optimizes to an integer mul 83 | Layout::array::(len) 84 | .unwrap() 85 | .align_to(PAGE_SIZE) 86 | .unwrap() 87 | } 88 | } 89 | 90 | impl Drop for GraphicScreen { 91 | fn drop(&mut self) { 92 | let layout = Self::layout(self.ptr.len()); 93 | unsafe { alloc::dealloc(self.ptr.as_ptr().cast(), layout) }; 94 | } 95 | } 96 | 97 | impl Framebuffer for GraphicScreen { 98 | fn width(&self) -> u32 { 99 | self.width as u32 100 | } 101 | 102 | fn height(&self) -> u32 { 103 | self.height as u32 104 | } 105 | } 106 | 107 | impl GraphicScreen { 108 | fn sync(&self, framebuffer: &mut FrameBuffer, sync_rect: Damage) { 109 | let sync_rect = sync_rect.clip( 110 | self.width.try_into().unwrap(), 111 | self.height.try_into().unwrap(), 112 | ); 113 | 114 | let start_x: usize = sync_rect.x.try_into().unwrap(); 115 | let start_y: usize = sync_rect.y.try_into().unwrap(); 116 | let w: usize = sync_rect.width.try_into().unwrap(); 117 | let h: usize = sync_rect.height.try_into().unwrap(); 118 | 119 | let offscreen_ptr = self.ptr.as_ptr() as *mut u32; 120 | let onscreen_ptr = framebuffer.onscreen as *mut u32; // FIXME use as_mut_ptr once stable 121 | 122 | for row in start_y..start_y + h { 123 | unsafe { 124 | ptr::copy( 125 | offscreen_ptr.add(row * self.width + start_x), 126 | onscreen_ptr.add(row * framebuffer.stride + start_x), 127 | w, 128 | ); 129 | } 130 | } 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /graphics/virtio-gpud/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio-gpud" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Anhad Singh "] 6 | 7 | [dependencies] 8 | log = "0.4" 9 | static_assertions = "1.1.0" 10 | futures = { version = "0.3.28", features = ["executor"] } 11 | anyhow = "1.0.71" 12 | 13 | common = { path = "../../common" } 14 | driver-graphics = { path = "../driver-graphics" } 15 | graphics-ipc = { path = "../graphics-ipc" } 16 | virtio-core = { path = "../../virtio-core" } 17 | pcid = { path = "../../pcid" } 18 | inputd = { path = "../../inputd" } 19 | 20 | redox-daemon = "0.1" 21 | redox_event = "0.4.1" 22 | redox_syscall = "0.5" 23 | orbclient = "0.3.27" 24 | spin = "0.9.8" 25 | libredox = "0.1.3" 26 | -------------------------------------------------------------------------------- /hwd/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /hwd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hwd" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | ron = "0.8.1" 9 | 10 | amlserde = { path = "../amlserde" } 11 | common = { path = "../common" } 12 | -------------------------------------------------------------------------------- /hwd/src/main.rs: -------------------------------------------------------------------------------- 1 | use amlserde::{AmlSerde, AmlSerdeValue}; 2 | use std::{error::Error, fs}; 3 | 4 | fn acpi() -> Result<(), Box> { 5 | for entry_res in fs::read_dir("/scheme/acpi/symbols")? { 6 | let entry = entry_res?; 7 | if let Some(file_name) = entry.file_name().to_str() { 8 | if file_name.ends_with("_CID") || file_name.ends_with("_HID") { 9 | let ron = fs::read_to_string(entry.path())?; 10 | let AmlSerde { name, value } = ron::from_str(&ron)?; 11 | let id = match value { 12 | AmlSerdeValue::Integer(integer) => { 13 | let vendor = integer & 0xFFFF; 14 | let device = (integer >> 16) & 0xFFFF; 15 | let vendor_rev = ((vendor & 0xFF) << 8) | vendor >> 8; 16 | let vendor_1 = (((vendor_rev >> 10) & 0x1f) as u8 + 64) as char; 17 | let vendor_2 = (((vendor_rev >> 5) & 0x1f) as u8 + 64) as char; 18 | let vendor_3 = (((vendor_rev >> 0) & 0x1f) as u8 + 64) as char; 19 | format!("{}{}{}{:04X}", vendor_1, vendor_2, vendor_3, device) 20 | } 21 | AmlSerdeValue::String(string) => string, 22 | _ => { 23 | log::warn!("{}: unsupported value {:x?}", name, value); 24 | continue; 25 | } 26 | }; 27 | log::debug!("{}: {}", name, id); 28 | } 29 | } 30 | } 31 | Ok(()) 32 | } 33 | 34 | fn main() { 35 | common::setup_logging( 36 | "misc", 37 | "hwd", 38 | "hwd", 39 | log::LevelFilter::Info, 40 | log::LevelFilter::Info, 41 | ); 42 | 43 | //TODO: HWD is meant to locate PCI/XHCI/etc devices in ACPI and DeviceTree definitions and start their drivers 44 | acpi().unwrap(); 45 | } 46 | -------------------------------------------------------------------------------- /initfs.toml: -------------------------------------------------------------------------------- 1 | ## Drivers for InitFS ## 2 | 3 | # ahcid 4 | [[drivers]] 5 | name = "AHCI storage" 6 | class = 1 7 | subclass = 6 8 | command = ["/scheme/initfs/lib/drivers/ahcid"] 9 | 10 | # ided 11 | [[drivers]] 12 | name = "IDE storage" 13 | class = 1 14 | subclass = 1 15 | command = ["/scheme/initfs/lib/drivers/ided"] 16 | 17 | # nvmed 18 | [[drivers]] 19 | name = "NVME storage" 20 | class = 1 21 | subclass = 8 22 | command = ["/scheme/initfs/lib/drivers/nvmed"] 23 | 24 | [[drivers]] 25 | name = "virtio-blk" 26 | class = 1 27 | subclass = 0 28 | vendor = 0x1AF4 29 | device = 0x1001 30 | command = ["/scheme/initfs/lib/drivers/virtio-blkd"] 31 | 32 | [[drivers]] 33 | name = "virtio-gpu" 34 | class = 3 35 | vendor = 0x1AF4 36 | device = 0x1050 37 | command = ["/scheme/initfs/lib/drivers/virtio-gpud"] 38 | -------------------------------------------------------------------------------- /input/ps2d/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /input/ps2d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ps2d" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bitflags = "1" 8 | log = "0.4" 9 | orbclient = "0.3.27" 10 | redox_event = "0.4.1" 11 | redox_syscall = "0.5.10" 12 | redox-daemon = "0.1" 13 | libredox = "0.1.3" 14 | 15 | common = { path = "../../common" } 16 | inputd = { path = "../../inputd" } 17 | -------------------------------------------------------------------------------- /input/ps2d/src/main.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate bitflags; 3 | extern crate orbclient; 4 | extern crate syscall; 5 | 6 | use std::fs::OpenOptions; 7 | use std::io::Read; 8 | use std::os::unix::fs::OpenOptionsExt; 9 | use std::os::unix::io::AsRawFd; 10 | use std::{env, process}; 11 | 12 | use common::acquire_port_io_rights; 13 | use event::{user_data, EventQueue}; 14 | use inputd::ProducerHandle; 15 | use log::info; 16 | 17 | use crate::state::Ps2d; 18 | 19 | mod controller; 20 | mod keymap; 21 | mod state; 22 | mod vm; 23 | 24 | fn daemon(daemon: redox_daemon::Daemon) -> ! { 25 | common::setup_logging( 26 | "misc", 27 | "ps2", 28 | "ps2", 29 | log::LevelFilter::Info, 30 | log::LevelFilter::Info, 31 | ); 32 | 33 | acquire_port_io_rights().expect("ps2d: failed to get I/O permission"); 34 | 35 | let (keymap, keymap_name): (fn(u8, bool) -> char, &str) = match env::args().skip(1).next() { 36 | Some(k) => match k.to_lowercase().as_ref() { 37 | "dvorak" => (keymap::dvorak::get_char, "dvorak"), 38 | "us" => (keymap::us::get_char, "us"), 39 | "gb" => (keymap::gb::get_char, "gb"), 40 | "azerty" => (keymap::azerty::get_char, "azerty"), 41 | "bepo" => (keymap::bepo::get_char, "bepo"), 42 | "it" => (keymap::it::get_char, "it"), 43 | &_ => (keymap::us::get_char, "us"), 44 | }, 45 | None => (keymap::us::get_char, "us"), 46 | }; 47 | 48 | info!("ps2d: using keymap '{}'", keymap_name); 49 | 50 | let input = ProducerHandle::new().expect("ps2d: failed to open input producer"); 51 | 52 | user_data! { 53 | enum Source { 54 | Keyboard, 55 | Mouse, 56 | } 57 | } 58 | 59 | let event_queue: EventQueue = 60 | EventQueue::new().expect("ps2d: failed to create event queue"); 61 | 62 | let mut key_file = OpenOptions::new() 63 | .read(true) 64 | .write(true) 65 | .custom_flags(syscall::O_NONBLOCK as i32) 66 | .open("/scheme/serio/0") 67 | .expect("ps2d: failed to open /scheme/serio/0"); 68 | 69 | event_queue 70 | .subscribe( 71 | key_file.as_raw_fd() as usize, 72 | Source::Keyboard, 73 | event::EventFlags::READ, 74 | ) 75 | .unwrap(); 76 | 77 | let mut mouse_file = OpenOptions::new() 78 | .read(true) 79 | .write(true) 80 | .custom_flags(syscall::O_NONBLOCK as i32) 81 | .open("/scheme/serio/1") 82 | .expect("ps2d: failed to open /scheme/serio/1"); 83 | 84 | event_queue 85 | .subscribe( 86 | mouse_file.as_raw_fd() as usize, 87 | Source::Mouse, 88 | event::EventFlags::READ, 89 | ) 90 | .unwrap(); 91 | 92 | libredox::call::setrens(0, 0).expect("ps2d: failed to enter null namespace"); 93 | 94 | daemon 95 | .ready() 96 | .expect("ps2d: failed to mark daemon as ready"); 97 | 98 | let mut ps2d = Ps2d::new(input, keymap); 99 | 100 | let mut data = [0; 256]; 101 | for event in event_queue.map(|e| e.expect("ps2d: failed to get next event").user_data) { 102 | // There are some gotchas with ps/2 controllers that require this weird 103 | // way of doing things. You read key and mouse data from the same 104 | // place. There is a status register that may show you which the data 105 | // came from, but if it is even implemented it can have a race 106 | // condition causing keyboard data to be read as mouse data. 107 | // 108 | // Due to this, we have a kernel driver doing a small amount of work 109 | // to grab bytes and sort them based on the source 110 | 111 | let (file, keyboard) = match event { 112 | Source::Keyboard => (&mut key_file, true), 113 | Source::Mouse => (&mut mouse_file, false), 114 | }; 115 | 116 | loop { 117 | let count = match file.read(&mut data) { 118 | Ok(0) => break, 119 | Ok(count) => count, 120 | Err(_) => break, 121 | }; 122 | for i in 0..count { 123 | ps2d.handle(keyboard, data[i]); 124 | } 125 | } 126 | } 127 | 128 | process::exit(0); 129 | } 130 | 131 | fn main() { 132 | redox_daemon::Daemon::new(daemon).expect("ps2d: failed to create daemon"); 133 | } 134 | -------------------------------------------------------------------------------- /input/ps2d/src/vm.rs: -------------------------------------------------------------------------------- 1 | // This code is informed by the QEMU implementation found here: 2 | // https://github.com/qemu/qemu/blob/master/hw/input/vmmouse.c 3 | // 4 | // As well as the Linux implementation here: 5 | // http://elixir.free-electrons.com/linux/v4.1/source/drivers/input/mouse/vmmouse.c 6 | 7 | use core::arch::asm; 8 | 9 | use log::{error, info, trace}; 10 | 11 | const MAGIC: u32 = 0x564D5868; 12 | const PORT: u16 = 0x5658; 13 | 14 | pub const GETVERSION: u32 = 10; 15 | pub const ABSPOINTER_DATA: u32 = 39; 16 | pub const ABSPOINTER_STATUS: u32 = 40; 17 | pub const ABSPOINTER_COMMAND: u32 = 41; 18 | 19 | pub const CMD_ENABLE: u32 = 0x45414552; 20 | pub const CMD_DISABLE: u32 = 0x000000f5; 21 | pub const CMD_REQUEST_ABSOLUTE: u32 = 0x53424152; 22 | pub const CMD_REQUEST_RELATIVE: u32 = 0x4c455252; 23 | 24 | const VERSION: u32 = 0x3442554a; 25 | 26 | pub const RELATIVE_PACKET: u32 = 0x00010000; 27 | 28 | pub const LEFT_BUTTON: u32 = 0x20; 29 | pub const RIGHT_BUTTON: u32 = 0x10; 30 | pub const MIDDLE_BUTTON: u32 = 0x08; 31 | 32 | pub unsafe fn cmd(cmd: u32, arg: u32) -> (u32, u32, u32, u32) { 33 | let a: u32; 34 | let b: u32; 35 | let c: u32; 36 | let d: u32; 37 | 38 | // ebx can't be used as input or output constraint in rust as LLVM reserves it. 39 | // Use xchg to pass it through r9 instead while restoring the original value in 40 | // rbx when leaving the inline asm block. si and di are clobbered too. 41 | #[cfg(not(target_arch = "x86"))] 42 | asm!( 43 | "xchg r9, rbx; in eax, dx; xchg r9, rbx", 44 | inout("eax") MAGIC => a, 45 | inout("r9") arg => b, 46 | inout("ecx") cmd => c, 47 | inout("edx") PORT as u32 => d, 48 | out("rsi") _, 49 | out("rdi") _, 50 | ); 51 | 52 | // On x86 we don't have a spare register, so push ebx to the stack instead. 53 | #[cfg(target_arch = "x86")] 54 | asm!( 55 | "push ebx; mov ebx, edi; in eax, dx; mov edi, ebx; pop ebx", 56 | inout("eax") MAGIC => a, 57 | inout("edi") arg => b, 58 | inout("ecx") cmd => c, 59 | inout("edx") PORT as u32 => d, 60 | ); 61 | 62 | (a, b, c, d) 63 | } 64 | 65 | pub fn enable(relative: bool) -> bool { 66 | trace!("ps2d: Enable vmmouse"); 67 | 68 | unsafe { 69 | let (eax, ebx, _, _) = cmd(GETVERSION, 0); 70 | if ebx != MAGIC || eax == 0xFFFFFFFF { 71 | info!("ps2d: No vmmouse support"); 72 | return false; 73 | } 74 | 75 | let _ = cmd(ABSPOINTER_COMMAND, CMD_ENABLE); 76 | 77 | let (status, _, _, _) = cmd(ABSPOINTER_STATUS, 0); 78 | if (status & 0x0000ffff) == 0 { 79 | info!("ps2d: No vmmouse"); 80 | return false; 81 | } 82 | 83 | let (version, _, _, _) = cmd(ABSPOINTER_DATA, 1); 84 | if version != VERSION { 85 | error!( 86 | "ps2d: Invalid vmmouse version: {} instead of {}", 87 | version, VERSION 88 | ); 89 | let _ = cmd(ABSPOINTER_COMMAND, CMD_DISABLE); 90 | return false; 91 | } 92 | 93 | if relative { 94 | cmd(ABSPOINTER_COMMAND, CMD_REQUEST_RELATIVE); 95 | } else { 96 | cmd(ABSPOINTER_COMMAND, CMD_REQUEST_ABSOLUTE); 97 | } 98 | } 99 | 100 | return true; 101 | } 102 | -------------------------------------------------------------------------------- /input/usbhidd/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /input/usbhidd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "usbhidd" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2018" 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | bitflags = "2" 12 | log = "0.4" 13 | orbclient = "0.3.47" 14 | redox_syscall = "0.5" 15 | rehid = { git = "https://gitlab.redox-os.org/redox-os/rehid.git" } 16 | xhcid = { path = "../../xhcid" } 17 | 18 | common = { path = "../../common" } 19 | inputd = { path = "../../inputd" } 20 | -------------------------------------------------------------------------------- /input/usbhidd/src/reqs.rs: -------------------------------------------------------------------------------- 1 | use std::slice; 2 | 3 | use rehid::report_desc::ReportTy; 4 | use xhcid_interface::{ 5 | DeviceReqData, PortReqRecipient, PortReqTy, XhciClientHandle, XhciClientHandleError, 6 | }; 7 | 8 | const GET_REPORT_REQ: u8 = 0x1; 9 | const SET_REPORT_REQ: u8 = 0x9; 10 | const GET_IDLE_REQ: u8 = 0x2; 11 | const SET_IDLE_REQ: u8 = 0xA; 12 | const GET_PROTOCOL_REQ: u8 = 0x3; 13 | const SET_PROTOCOL_REQ: u8 = 0xB; 14 | 15 | fn concat(hi: u8, lo: u8) -> u16 { 16 | (u16::from(hi) << 8) | u16::from(lo) 17 | } 18 | 19 | pub fn get_report( 20 | handle: &XhciClientHandle, 21 | report_ty: ReportTy, 22 | report_id: u8, 23 | if_num: u16, 24 | buffer: &mut [u8], 25 | ) -> Result<(), XhciClientHandleError> { 26 | handle.device_request( 27 | PortReqTy::Class, 28 | PortReqRecipient::Interface, 29 | GET_REPORT_REQ, 30 | concat(report_ty as u8, report_id), 31 | if_num, 32 | DeviceReqData::In(buffer), 33 | ) 34 | } 35 | pub fn set_report( 36 | handle: &XhciClientHandle, 37 | report_ty: ReportTy, 38 | report_id: u8, 39 | if_num: u16, 40 | buffer: &[u8], 41 | ) -> Result<(), XhciClientHandleError> { 42 | handle.device_request( 43 | PortReqTy::Class, 44 | PortReqRecipient::Interface, 45 | SET_REPORT_REQ, 46 | concat(report_id, report_ty as u8), 47 | if_num, 48 | DeviceReqData::Out(buffer), 49 | ) 50 | } 51 | pub fn get_idle( 52 | handle: &XhciClientHandle, 53 | report_id: u8, 54 | if_num: u16, 55 | ) -> Result { 56 | let mut idle_rate = 0; 57 | let buffer = slice::from_mut(&mut idle_rate); 58 | handle.device_request( 59 | PortReqTy::Class, 60 | PortReqRecipient::Interface, 61 | GET_IDLE_REQ, 62 | u16::from(report_id), 63 | if_num, 64 | DeviceReqData::In(buffer), 65 | )?; 66 | Ok(idle_rate) 67 | } 68 | pub fn set_idle( 69 | handle: &XhciClientHandle, 70 | duration: u8, 71 | report_id: u8, 72 | if_num: u16, 73 | ) -> Result<(), XhciClientHandleError> { 74 | handle.device_request( 75 | PortReqTy::Class, 76 | PortReqRecipient::Interface, 77 | SET_IDLE_REQ, 78 | concat(duration, report_id), 79 | if_num, 80 | DeviceReqData::NoData, 81 | ) 82 | } 83 | pub fn get_protocol(handle: &XhciClientHandle, if_num: u16) -> Result { 84 | let mut protocol = 0; 85 | let buffer = slice::from_mut(&mut protocol); 86 | handle.device_request( 87 | PortReqTy::Class, 88 | PortReqRecipient::Interface, 89 | GET_PROTOCOL_REQ, 90 | 0, 91 | if_num, 92 | DeviceReqData::In(buffer), 93 | )?; 94 | Ok(protocol) 95 | } 96 | pub fn set_protocol( 97 | handle: &XhciClientHandle, 98 | protocol: u8, 99 | if_num: u16, 100 | ) -> Result<(), XhciClientHandleError> { 101 | handle.device_request( 102 | PortReqTy::Class, 103 | PortReqRecipient::Interface, 104 | SET_PROTOCOL_REQ, 105 | u16::from(protocol), 106 | if_num, 107 | DeviceReqData::NoData, 108 | ) 109 | } 110 | -------------------------------------------------------------------------------- /inputd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inputd" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Anhad Singh "] 6 | 7 | [dependencies] 8 | anyhow = "1.0.71" 9 | log = "0.4.19" 10 | redox-daemon = "0.1.2" 11 | redox_syscall = { version = "0.5", features = ["std"] } 12 | orbclient = "0.3.27" 13 | libredox = "0.1.3" 14 | 15 | common = { path = "../common" } 16 | redox-scheme = "0.4" 17 | -------------------------------------------------------------------------------- /net/alxd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "alxd" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | redox_event = "0.4.1" 9 | redox_syscall = "0.5" 10 | redox-daemon = "0.1" 11 | 12 | common = { path = "../../common" } 13 | libredox = "0.1.3" 14 | -------------------------------------------------------------------------------- /net/driver-network/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "driver-network" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | libredox = "0.1.3" 8 | redox-scheme = "0.4" 9 | redox_syscall = { version = "0.5", features = ["std"] } 10 | -------------------------------------------------------------------------------- /net/e1000d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "e1000d" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | redox-daemon = "0.1.2" 9 | libredox = "0.1.3" 10 | redox_event = "0.4.1" 11 | redox_syscall = "0.5" 12 | 13 | common = { path = "../../common" } 14 | driver-network = { path = "../driver-network" } 15 | pcid = { path = "../../pcid" } 16 | -------------------------------------------------------------------------------- /net/e1000d/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "E1000 NIC" 3 | class = 0x02 4 | ids = { 0x8086 = [0x1004, 0x100e, 0x100f, 0x1503] } 5 | command = ["e1000d"] 6 | -------------------------------------------------------------------------------- /net/e1000d/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::os::unix::io::AsRawFd; 3 | 4 | use driver_network::NetworkScheme; 5 | use event::{user_data, EventQueue}; 6 | use pcid_interface::PciFunctionHandle; 7 | 8 | pub mod device; 9 | 10 | fn main() { 11 | let mut pcid_handle = PciFunctionHandle::connect_default(); 12 | let pci_config = pcid_handle.config(); 13 | 14 | let mut name = pci_config.func.name(); 15 | name.push_str("_e1000"); 16 | 17 | let irq = pci_config 18 | .func 19 | .legacy_interrupt_line 20 | .expect("e1000d: no legacy interrupts supported"); 21 | 22 | eprintln!(" + E1000 {}", pci_config.func.display()); 23 | 24 | redox_daemon::Daemon::new(move |daemon| { 25 | let mut irq_file = irq.irq_handle("e1000d"); 26 | 27 | let address = unsafe { pcid_handle.map_bar(0) }.ptr.as_ptr() as usize; 28 | 29 | let device = 30 | unsafe { device::Intel8254x::new(address).expect("e1000d: failed to allocate device") }; 31 | 32 | let mut scheme = NetworkScheme::new(device, format!("network.{name}")); 33 | 34 | user_data! { 35 | enum Source { 36 | Irq, 37 | Scheme, 38 | } 39 | } 40 | 41 | let event_queue = 42 | EventQueue::::new().expect("e1000d: failed to create event queue"); 43 | 44 | event_queue 45 | .subscribe( 46 | irq_file.as_raw_fd() as usize, 47 | Source::Irq, 48 | event::EventFlags::READ, 49 | ) 50 | .expect("e1000d: failed to subscribe to IRQ fd"); 51 | event_queue 52 | .subscribe( 53 | scheme.event_handle().raw(), 54 | Source::Scheme, 55 | event::EventFlags::READ, 56 | ) 57 | .expect("e1000d: failed to subscribe to scheme fd"); 58 | 59 | libredox::call::setrens(0, 0).expect("e1000d: failed to enter null namespace"); 60 | 61 | daemon 62 | .ready() 63 | .expect("e1000d: failed to mark daemon as ready"); 64 | 65 | scheme.tick().unwrap(); 66 | 67 | for event in event_queue.map(|e| e.expect("e1000d: failed to get event")) { 68 | match event.user_data { 69 | Source::Irq => { 70 | let mut irq = [0; 8]; 71 | irq_file.read(&mut irq).unwrap(); 72 | if unsafe { scheme.adapter().irq() } { 73 | irq_file.write(&mut irq).unwrap(); 74 | 75 | scheme.tick().expect("e1000d: failed to handle IRQ") 76 | } 77 | } 78 | Source::Scheme => scheme.tick().expect("e1000d: failed to handle scheme op"), 79 | } 80 | } 81 | unreachable!() 82 | }) 83 | .expect("e1000d: failed to create daemon"); 84 | } 85 | -------------------------------------------------------------------------------- /net/ixgbed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ixgbed" 3 | version = "1.0.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | libredox = "0.1.3" 9 | redox_event = "0.4.1" 10 | redox_syscall = "0.5" 11 | redox-daemon = "0.1" 12 | 13 | common = { path = "../../common" } 14 | driver-network = { path = "../driver-network" } 15 | pcid = { path = "../../pcid" } 16 | -------------------------------------------------------------------------------- /net/ixgbed/README.md: -------------------------------------------------------------------------------- 1 | # ixgbed (a.k.a. ixy.rs on Redox) 2 | 3 | ixgbed is the Redox port of [ixy.rs](https://github.com/ixy-languages/ixy.rs), a Rust rewrite of the [ixy](https://github.com/emmericp/ixy) userspace network driver. 4 | It is designed to be readable, idiomatic Rust code. 5 | It supports Intel 82599 10GbE NICs (`ixgbe` family). 6 | 7 | ## Features 8 | 9 | * first 10 Gbit/s network driver on Redox 10 | * transmitting 250 times faster than e1000 / rtl8168 driver 11 | * MSI-X interrupts (not supported by Redox yet) 12 | * less than 1000 lines of code for the driver 13 | * documented code 14 | 15 | ## Build instructions 16 | 17 | See the [Redox README](https://gitlab.redox-os.org/redox-os/redox/blob/master/README.md) for build instructions. 18 | 19 | To run ixgbed on Redox (in case the driver is not shipped with Redox anymore) 20 | 21 | * clone this project into `cookbook/recipes/drivers/source/` 22 | * create an entry for ixgbed in `cookbook/recipes/drivers/source/Cargo.toml` 23 | * check if your ixgbe device is included in `config.toml` 24 | * touch `filesystem.toml` in Redox's root directory, build Redox and run it 25 | 26 | ## Usage 27 | 28 | To test the driver's transmit and forwarding capabilities, have a look at [rheinfall](https://github.com/ackxolotl/rheinfall), a simple packet generator / forwarder application. 29 | 30 | ## Docs 31 | 32 | ixgbed contains documentation that can be created and viewed by running 33 | 34 | ``` 35 | cargo doc --open 36 | ``` 37 | 38 | -------------------------------------------------------------------------------- /net/ixgbed/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "Intel 10G NIC" 3 | class = 0x02 4 | ids = { 0x8086 = [0x10F7, 0x1514, 0x1517, 0x151C, 0x10F9, 0x10FB, 0x152a, 0x1529, 0x1507, 0x154D, 0x1557, 0x10FC, 0x10F8, 0x154F, 0x1528, 0x154A, 0x1558, 0x1560, 0x1563, 0x15D1, 0x15AA, 0x15AB, 0x15AC, 0x15AD, 0x15AE, 0x15B0, 0x15C2, 0x15C3, 0x15C4, 0x15C6, 0x15C7, 0x15C8, 0x15CE, 0x15E4, 0x15E5, 0x10ED, 0x1515, 0x1565, 0x15A8, 0x15C5] } 5 | command = ["ixgbed"] 6 | -------------------------------------------------------------------------------- /net/ixgbed/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::io::{Read, Write}; 2 | use std::os::unix::io::AsRawFd; 3 | 4 | use driver_network::NetworkScheme; 5 | use event::{user_data, EventQueue}; 6 | use pcid_interface::PciFunctionHandle; 7 | 8 | pub mod device; 9 | #[rustfmt::skip] 10 | mod ixgbe; 11 | 12 | fn main() { 13 | let mut pcid_handle = PciFunctionHandle::connect_default(); 14 | let pci_config = pcid_handle.config(); 15 | 16 | let mut name = pci_config.func.name(); 17 | name.push_str("_ixgbe"); 18 | 19 | let irq = pci_config 20 | .func 21 | .legacy_interrupt_line 22 | .expect("ixgbed: no legacy interrupts supported"); 23 | 24 | println!(" + IXGBE {}", pci_config.func.display()); 25 | 26 | redox_daemon::Daemon::new(move |daemon| { 27 | let mut irq_file = irq.irq_handle("ixgbed"); 28 | 29 | let mapped_bar = unsafe { pcid_handle.map_bar(0) }; 30 | let address = mapped_bar.ptr.as_ptr(); 31 | let size = mapped_bar.bar_size; 32 | 33 | let device = device::Intel8259x::new(address as usize, size) 34 | .expect("ixgbed: failed to allocate device"); 35 | 36 | let mut scheme = NetworkScheme::new(device, format!("network.{name}")); 37 | 38 | user_data! { 39 | enum Source { 40 | Irq, 41 | Scheme, 42 | } 43 | } 44 | 45 | let event_queue = 46 | EventQueue::::new().expect("ixgbed: Could not create event queue."); 47 | event_queue 48 | .subscribe( 49 | irq_file.as_raw_fd() as usize, 50 | Source::Irq, 51 | event::EventFlags::READ, 52 | ) 53 | .unwrap(); 54 | event_queue 55 | .subscribe( 56 | scheme.event_handle().raw(), 57 | Source::Scheme, 58 | event::EventFlags::READ, 59 | ) 60 | .unwrap(); 61 | 62 | libredox::call::setrens(0, 0).expect("ixgbed: failed to enter null namespace"); 63 | 64 | daemon 65 | .ready() 66 | .expect("ixgbed: failed to mark daemon as ready"); 67 | 68 | scheme.tick().unwrap(); 69 | 70 | for event in event_queue.map(|e| e.expect("ixgbed: failed to get next event")) { 71 | match event.user_data { 72 | Source::Irq => { 73 | let mut irq = [0; 8]; 74 | irq_file.read(&mut irq).unwrap(); 75 | if scheme.adapter().irq() { 76 | irq_file.write(&mut irq).unwrap(); 77 | 78 | scheme.tick().unwrap(); 79 | } 80 | } 81 | Source::Scheme => { 82 | scheme.tick().unwrap(); 83 | } 84 | } 85 | } 86 | unreachable!() 87 | }) 88 | .expect("ixgbed: failed to create daemon"); 89 | } 90 | -------------------------------------------------------------------------------- /net/rtl8139d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtl8139d" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | libredox = "0.1.3" 9 | log = "0.4" 10 | redox_event = "0.4.1" 11 | redox_syscall = "0.5" 12 | redox-daemon = "0.1" 13 | 14 | common = { path = "../../common" } 15 | driver-network = { path = "../driver-network" } 16 | pcid = { path = "../../pcid" } 17 | -------------------------------------------------------------------------------- /net/rtl8139d/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "RTL8139 NIC" 3 | class = 0x02 4 | ids = { 0x10ec = [0x8139] } 5 | command = ["rtl8139d"] 6 | -------------------------------------------------------------------------------- /net/rtl8168d/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtl8168d" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | bitflags = "2" 8 | libredox = "0.1.3" 9 | log = "0.4" 10 | redox_event = "0.4.1" 11 | redox_syscall = "0.5" 12 | redox-daemon = "0.1" 13 | 14 | common = { path = "../../common" } 15 | driver-network = { path = "../driver-network" } 16 | pcid = { path = "../../pcid" } 17 | -------------------------------------------------------------------------------- /net/rtl8168d/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "RTL8168 NIC" 3 | class = 0x02 4 | ids = { 0x10ec = [0x8168, 0x8169] } 5 | command = ["rtl8168d"] 6 | -------------------------------------------------------------------------------- /net/virtio-netd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio-netd" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | log = "0.4" 8 | static_assertions = "1.1.0" 9 | futures = { version = "0.3.28", features = ["executor"] } 10 | 11 | virtio-core = { path = "../../virtio-core" } 12 | pcid = { path = "../../pcid" } 13 | common = { path = "../../common" } 14 | driver-network = { path = "../driver-network" } 15 | 16 | redox-daemon = "0.1" 17 | redox_syscall = "0.5" 18 | libredox = "0.1.3" 19 | -------------------------------------------------------------------------------- /net/virtio-netd/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "virtio-net" 3 | class = 0x02 4 | vendor = 0x1AF4 5 | device = 0x1000 6 | command = ["virtio-netd"] 7 | -------------------------------------------------------------------------------- /net/virtio-netd/src/main.rs: -------------------------------------------------------------------------------- 1 | mod scheme; 2 | 3 | use std::fs::File; 4 | use std::io::{Read, Write}; 5 | use std::mem; 6 | 7 | use driver_network::NetworkScheme; 8 | use pcid_interface::PciFunctionHandle; 9 | 10 | use scheme::VirtioNet; 11 | 12 | pub const VIRTIO_NET_F_MAC: u32 = 5; 13 | 14 | #[derive(Debug)] 15 | #[repr(C)] 16 | pub struct VirtHeader { 17 | pub flags: u8, 18 | pub gso_type: u8, 19 | pub hdr_len: u16, 20 | pub gso_size: u16, 21 | pub csum_start: u16, 22 | pub csum_offset: u16, 23 | pub num_buffers: u16, 24 | } 25 | 26 | static_assertions::const_assert_eq!(core::mem::size_of::(), 12); 27 | 28 | const MAX_BUFFER_LEN: usize = 65535; 29 | 30 | fn deamon(daemon: redox_daemon::Daemon) -> Result<(), Box> { 31 | let mut pcid_handle = PciFunctionHandle::connect_default(); 32 | 33 | // Double check that we have the right device. 34 | // 35 | // 0x1000 - virtio-net 36 | let pci_config = pcid_handle.config(); 37 | 38 | assert_eq!(pci_config.func.full_device_id.device_id, 0x1000); 39 | log::info!("virtio-net: initiating startup sequence :^)"); 40 | 41 | let device = virtio_core::probe_device(&mut pcid_handle)?; 42 | let device_space = device.device_space; 43 | 44 | // Negotiate device features: 45 | let mac_address = if device.transport.check_device_feature(VIRTIO_NET_F_MAC) { 46 | let mac = unsafe { 47 | [ 48 | core::ptr::read_volatile(device_space.add(0)), 49 | core::ptr::read_volatile(device_space.add(1)), 50 | core::ptr::read_volatile(device_space.add(2)), 51 | core::ptr::read_volatile(device_space.add(3)), 52 | core::ptr::read_volatile(device_space.add(4)), 53 | core::ptr::read_volatile(device_space.add(5)), 54 | ] 55 | }; 56 | 57 | log::info!( 58 | "virtio-net: device MAC is {:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}:{:>02X}", 59 | mac[0], 60 | mac[1], 61 | mac[2], 62 | mac[3], 63 | mac[4], 64 | mac[5] 65 | ); 66 | 67 | device.transport.ack_driver_feature(VIRTIO_NET_F_MAC); 68 | mac 69 | } else { 70 | unimplemented!() 71 | }; 72 | 73 | device.transport.finalize_features(); 74 | 75 | // Allocate the recieve and transmit queues: 76 | // 77 | // > Empty buffers are placed in one virtqueue for receiving 78 | // > packets, and outgoing packets are enqueued into another 79 | // > for transmission in that order. 80 | // 81 | // TODO(andypython): Should we use the same IRQ vector for both? 82 | let rx_queue = device 83 | .transport 84 | .setup_queue(virtio_core::MSIX_PRIMARY_VECTOR, &device.irq_handle)?; 85 | 86 | let tx_queue = device 87 | .transport 88 | .setup_queue(virtio_core::MSIX_PRIMARY_VECTOR, &device.irq_handle)?; 89 | 90 | device.transport.run_device(); 91 | 92 | let mut name = pci_config.func.name(); 93 | name.push_str("_virtio_net"); 94 | 95 | let device = VirtioNet::new(mac_address, rx_queue, tx_queue); 96 | let mut scheme = NetworkScheme::new(device, format!("network.{name}")); 97 | 98 | let mut event_queue = File::open("/scheme/event")?; 99 | event_queue.write(&syscall::Event { 100 | id: scheme.event_handle().raw(), 101 | flags: syscall::EVENT_READ, 102 | data: 0, 103 | })?; 104 | 105 | libredox::call::setrens(0, 0).expect("virtio-netd: failed to enter null namespace"); 106 | 107 | daemon.ready().expect("virtio-netd: failed to daemonize"); 108 | 109 | scheme.tick()?; 110 | 111 | loop { 112 | event_queue.read(&mut [0; mem::size_of::()])?; // Wait for event 113 | scheme.tick()?; 114 | } 115 | } 116 | 117 | fn daemon_runner(redox_daemon: redox_daemon::Daemon) -> ! { 118 | deamon(redox_daemon).unwrap(); 119 | unreachable!(); 120 | } 121 | 122 | pub fn main() { 123 | common::setup_logging( 124 | "net", 125 | "pcie", 126 | "virtio-netd", 127 | log::LevelFilter::Trace, 128 | log::LevelFilter::Trace, 129 | ); 130 | redox_daemon::Daemon::new(daemon_runner).expect("virtio-core: failed to daemonize"); 131 | } 132 | -------------------------------------------------------------------------------- /net/virtio-netd/src/scheme.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use driver_network::NetworkAdapter; 4 | 5 | use common::dma::Dma; 6 | 7 | use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags}; 8 | use virtio_core::transport::Queue; 9 | 10 | use crate::{VirtHeader, MAX_BUFFER_LEN}; 11 | 12 | pub struct VirtioNet<'a> { 13 | mac_address: [u8; 6], 14 | 15 | /// Reciever Queue. 16 | rx: Arc>, 17 | rx_buffers: Vec>, 18 | 19 | /// Transmiter Queue. 20 | tx: Arc>, 21 | 22 | recv_head: u16, 23 | } 24 | 25 | impl<'a> VirtioNet<'a> { 26 | pub fn new(mac_address: [u8; 6], rx: Arc>, tx: Arc>) -> Self { 27 | // Populate all of the `rx_queue` with buffers to maximize performence. 28 | let mut rx_buffers = vec![]; 29 | for i in 0..(rx.descriptor_len() as usize) { 30 | rx_buffers.push(unsafe { 31 | Dma::<[u8]>::zeroed_slice(MAX_BUFFER_LEN) 32 | .unwrap() 33 | .assume_init() 34 | }); 35 | 36 | let chain = ChainBuilder::new() 37 | .chain(Buffer::new_unsized(&rx_buffers[i]).flags(DescriptorFlags::WRITE_ONLY)) 38 | .build(); 39 | 40 | let _ = rx.send(chain); 41 | } 42 | 43 | Self { 44 | mac_address, 45 | 46 | rx, 47 | rx_buffers, 48 | tx, 49 | 50 | recv_head: 0, 51 | } 52 | } 53 | 54 | /// Returns the number of bytes read. Returns `0` if the operation would block. 55 | fn try_recv(&mut self, target: &mut [u8]) -> usize { 56 | let header_size = core::mem::size_of::(); 57 | 58 | if self.recv_head == self.rx.used.head_index() { 59 | // The read would block. 60 | return 0; 61 | } 62 | 63 | let idx = self.rx.used.head_index() as usize; 64 | let element = self.rx.used.get_element_at(idx - 1); 65 | 66 | let descriptor_idx = element.table_index.get(); 67 | let payload_size = element.written.get() as usize - header_size; 68 | 69 | // XXX: The header and packet are added as one output descriptor to the transmit queue, 70 | // and the device is notified of the new entry (see 5.1.5 Device Initialization). 71 | let buffer = &self.rx_buffers[descriptor_idx as usize]; 72 | // TODO: Check the header. 73 | let _header = unsafe { &*(buffer.as_ptr() as *const VirtHeader) }; 74 | let packet = &buffer[header_size..(header_size + payload_size)]; 75 | 76 | // Copy the packet into the buffer. 77 | target[..payload_size].copy_from_slice(&packet); 78 | 79 | self.recv_head = self.rx.used.head_index(); 80 | payload_size 81 | } 82 | } 83 | 84 | impl<'a> NetworkAdapter for VirtioNet<'a> { 85 | fn mac_address(&mut self) -> [u8; 6] { 86 | self.mac_address 87 | } 88 | 89 | fn available_for_read(&mut self) -> usize { 90 | (self.rx.used.head_index() - self.recv_head).into() 91 | } 92 | 93 | fn read_packet(&mut self, buf: &mut [u8]) -> syscall::Result> { 94 | let bytes = self.try_recv(buf); 95 | 96 | if bytes != 0 { 97 | // We read some bytes. 98 | Ok(Some(bytes)) 99 | } else { 100 | Ok(None) 101 | } 102 | } 103 | 104 | fn write_packet(&mut self, buffer: &[u8]) -> syscall::Result { 105 | let header = unsafe { Dma::::zeroed()?.assume_init() }; 106 | 107 | let mut payload = unsafe { Dma::<[u8]>::zeroed_slice(buffer.len())?.assume_init() }; 108 | payload.copy_from_slice(buffer); 109 | 110 | let chain = ChainBuilder::new() 111 | .chain(Buffer::new(&header)) 112 | .chain(Buffer::new_unsized(&payload)) 113 | .build(); 114 | 115 | futures::executor::block_on(self.tx.send(chain)); 116 | Ok(buffer.len()) 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /pcid-spawner/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pcid-spawner" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | anyhow = "1" 10 | log = "0.4" 11 | pico-args = "0.5" 12 | redox_syscall = "0.5.9" 13 | serde = { version = "1", features = ["derive"] } 14 | toml = "0.5" 15 | 16 | common = { path = "../common" } 17 | pcid = { path = "../pcid" } 18 | -------------------------------------------------------------------------------- /pcid-spawner/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::fs; 2 | use std::process::Command; 3 | 4 | use anyhow::{anyhow, Context, Result}; 5 | 6 | use pcid_interface::config::Config; 7 | use pcid_interface::PciFunctionHandle; 8 | 9 | fn main() -> Result<()> { 10 | let mut args = pico_args::Arguments::from_env(); 11 | let config_path = args 12 | .free_from_str::() 13 | .expect("failed to parse --config argument"); 14 | 15 | common::setup_logging( 16 | "bus", 17 | "pci", 18 | "pci-spawner.log", 19 | log::LevelFilter::Info, 20 | log::LevelFilter::Trace, 21 | ); 22 | 23 | let config_data = if fs::metadata(&config_path)?.is_file() { 24 | fs::read_to_string(&config_path)? 25 | } else { 26 | let mut config_data = String::new(); 27 | for path in fs::read_dir(&config_path)? { 28 | if let Ok(tmp) = fs::read_to_string(path.unwrap().path()) { 29 | config_data.push_str(&tmp); 30 | } 31 | } 32 | config_data 33 | }; 34 | let config: Config = toml::from_str(&config_data)?; 35 | 36 | for entry in fs::read_dir("/scheme/pci")? { 37 | let entry = entry.context("failed to get entry")?; 38 | let device_path = entry.path(); 39 | log::trace!("ENTRY: {}", device_path.to_string_lossy()); 40 | 41 | let mut handle = match PciFunctionHandle::connect_by_path(&device_path) { 42 | Ok(handle) => handle, 43 | Err(err) => { 44 | // Either the device is gone or it is already in-use by a driver. 45 | log::debug!( 46 | "pcid-spawner: {} already in use: {err}", 47 | device_path.display(), 48 | ); 49 | continue; 50 | } 51 | }; 52 | 53 | let full_device_id = handle.config().func.full_device_id; 54 | 55 | log::debug!( 56 | "pcid-spawner enumerated: PCI {} {}", 57 | handle.config().func.addr, 58 | full_device_id.display() 59 | ); 60 | 61 | let Some(driver) = config 62 | .drivers 63 | .iter() 64 | .find(|driver| driver.match_function(&full_device_id)) 65 | else { 66 | log::debug!("no driver for {}, continuing", handle.config().func.addr); 67 | continue; 68 | }; 69 | 70 | let mut args = driver.command.iter(); 71 | 72 | let program = args 73 | .next() 74 | .ok_or_else(|| anyhow!("driver configuration entry did not have any command!"))?; 75 | let program = if program.starts_with('/') { 76 | program.to_owned() 77 | } else { 78 | "/usr/lib/drivers/".to_owned() + program 79 | }; 80 | 81 | let mut command = Command::new(program); 82 | command.args(args); 83 | 84 | log::info!("pcid-spawner: spawn {:?}", command); 85 | 86 | handle.enable_device(); 87 | 88 | let channel_fd = handle.into_inner_fd(); 89 | command.env("PCID_CLIENT_CHANNEL", channel_fd.to_string()); 90 | 91 | match command.status() { 92 | Ok(status) if !status.success() => { 93 | log::error!("pcid-spawner: driver {command:?} failed with {status}"); 94 | } 95 | Ok(_) => {} 96 | Err(err) => log::error!("pcid-spawner: failed to execute {command:?}: {err}"), 97 | } 98 | syscall::close(channel_fd as usize).unwrap(); 99 | } 100 | 101 | Ok(()) 102 | } 103 | -------------------------------------------------------------------------------- /pcid/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /pcid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "pcid" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [[bin]] 7 | name = "pcid" 8 | path = "src/main.rs" 9 | 10 | [lib] 11 | name = "pcid_interface" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | bincode = "1.2" 16 | fdt = "0.1.5" 17 | libc = "0.2" 18 | log = "0.4" 19 | pci_types = "0.10" 20 | pico-args = { version = "0.5", features = ["combined-flags"] } 21 | plain = "0.2" 22 | redox-daemon = "0.1" 23 | redox-scheme = "0.4" 24 | redox_syscall = "0.5.9" 25 | serde = { version = "1", features = ["derive"] } 26 | 27 | common = { path = "../common" } 28 | libredox = "0.1.3" 29 | -------------------------------------------------------------------------------- /pcid/src/cfg_access/fallback.rs: -------------------------------------------------------------------------------- 1 | use std::cell::Cell; 2 | use std::convert::TryFrom; 3 | use std::sync::Mutex; 4 | 5 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 6 | use common::io::{Io as _, Pio}; 7 | 8 | use log::info; 9 | use pci_types::{ConfigRegionAccess, PciAddress}; 10 | 11 | pub(crate) struct Pci { 12 | lock: Mutex<()>, 13 | } 14 | 15 | impl Pci { 16 | pub(crate) fn new() -> Self { 17 | Self { 18 | lock: Mutex::new(()), 19 | } 20 | } 21 | 22 | fn set_iopl() { 23 | // The IO privilege level is per-thread, so we need to do the initialization on every thread. 24 | thread_local! { 25 | static IOPL_ONCE: Cell = Cell::new(false); 26 | } 27 | 28 | IOPL_ONCE.with(|iopl_once| { 29 | if !iopl_once.replace(true) { 30 | // make sure that pcid is not granted io port permission unless pcie memory-mapped 31 | // configuration space is not available. 32 | info!( 33 | "PCI: couldn't find or access PCIe extended configuration, \ 34 | and thus falling back to PCI 3.0 io ports" 35 | ); 36 | common::acquire_port_io_rights().expect("pcid: failed to get IO port rights"); 37 | } 38 | }); 39 | } 40 | 41 | fn address(address: PciAddress, offset: u8) -> u32 { 42 | assert_eq!( 43 | address.segment(), 44 | 0, 45 | "usage of multiple segments requires PCIe extended configuration" 46 | ); 47 | 48 | assert_eq!(offset & 0xFC, offset, "pci offset is not aligned"); 49 | 50 | 0x80000000 51 | | (u32::from(address.bus()) << 16) 52 | | (u32::from(address.device()) << 11) 53 | | (u32::from(address.function()) << 8) 54 | | u32::from(offset) 55 | } 56 | } 57 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 58 | impl ConfigRegionAccess for Pci { 59 | unsafe fn read(&self, address: PciAddress, offset: u16) -> u32 { 60 | let _guard = self.lock.lock().unwrap(); 61 | 62 | Self::set_iopl(); 63 | 64 | let offset = 65 | u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); 66 | let address = Self::address(address, offset); 67 | 68 | Pio::::new(0xCF8).write(address); 69 | Pio::::new(0xCFC).read() 70 | } 71 | 72 | unsafe fn write(&self, address: PciAddress, offset: u16, value: u32) { 73 | let _guard = self.lock.lock().unwrap(); 74 | 75 | Self::set_iopl(); 76 | 77 | let offset = 78 | u8::try_from(offset).expect("offset too large for PCI 3.0 configuration space"); 79 | let address = Self::address(address, offset); 80 | 81 | Pio::::new(0xCF8).write(address); 82 | Pio::::new(0xCFC).write(value); 83 | } 84 | } 85 | #[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))] 86 | impl ConfigRegionAccess for Pci { 87 | unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 { 88 | let _guard = self.lock.lock().unwrap(); 89 | todo!("Pci::CfgAccess::read on this architecture") 90 | } 91 | 92 | unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) { 93 | let _guard = self.lock.lock().unwrap(); 94 | todo!("Pci::CfgAccess::write on this architecture") 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /pcid/src/driver_interface/bar.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | // This type is used instead of [pci_types::Bar] in the driver interface as the 6 | // latter can't be serialized and is missing the convenience functions of [PciBar]. 7 | #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] 8 | pub enum PciBar { 9 | None, 10 | Memory32 { addr: u32, size: u32 }, 11 | Memory64 { addr: u64, size: u64 }, 12 | Port(u16), 13 | } 14 | 15 | impl PciBar { 16 | pub fn display(&self) -> String { 17 | match self { 18 | PciBar::None => format!(""), 19 | PciBar::Memory32 { addr, .. } => format!("{addr:08X}"), 20 | PciBar::Memory64 { addr, .. } => format!("{addr:016X}"), 21 | PciBar::Port(port) => format!("P{port:04X}"), 22 | } 23 | } 24 | 25 | pub fn is_none(&self) -> bool { 26 | match self { 27 | &PciBar::None => true, 28 | _ => false, 29 | } 30 | } 31 | 32 | pub fn expect_port(&self) -> u16 { 33 | match *self { 34 | PciBar::Port(port) => port, 35 | PciBar::Memory32 { .. } | PciBar::Memory64 { .. } => { 36 | panic!("expected port BAR, found memory BAR"); 37 | } 38 | PciBar::None => panic!("expected BAR to exist"), 39 | } 40 | } 41 | 42 | pub fn expect_mem(&self) -> (usize, usize) { 43 | match *self { 44 | PciBar::Memory32 { addr, size } => (addr as usize, size as usize), 45 | PciBar::Memory64 { addr, size } => ( 46 | addr.try_into() 47 | .expect("conversion from 64bit BAR to usize failed"), 48 | size.try_into() 49 | .expect("conversion from 64bit BAR size to usize failed"), 50 | ), 51 | PciBar::Port(_) => panic!("expected memory BAR, found port BAR"), 52 | PciBar::None => panic!("expected BAR to exist"), 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /pcid/src/driver_interface/cap.rs: -------------------------------------------------------------------------------- 1 | use pci_types::capability::PciCapabilityAddress; 2 | use pci_types::ConfigRegionAccess; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)] 6 | pub struct VendorSpecificCapability { 7 | pub data: Vec, 8 | } 9 | 10 | impl VendorSpecificCapability { 11 | pub unsafe fn parse(addr: PciCapabilityAddress, access: &dyn ConfigRegionAccess) -> Self { 12 | let dword = access.read(addr.address, addr.offset); 13 | let length = ((dword >> 16) & 0xFF) as u16; 14 | // let next = (dword >> 8) & 0xFF; 15 | // log::trace!( 16 | // "Vendor specific offset: {:#02x} next: {next:#02x} cap len: {length:#02x}", 17 | // addr.offset 18 | // ); 19 | let data = if length > 0 { 20 | assert!( 21 | length > 3 && length % 4 == 0, 22 | "invalid range length: {}", 23 | length 24 | ); 25 | let mut raw_data = { 26 | (addr.offset..addr.offset + length) 27 | .step_by(4) 28 | .flat_map(|offset| access.read(addr.address, offset).to_le_bytes()) 29 | .collect::>() 30 | }; 31 | raw_data.drain(3..).collect() 32 | } else { 33 | log::warn!("Vendor specific capability is invalid"); 34 | Vec::new() 35 | }; 36 | VendorSpecificCapability { data } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /pcid/src/driver_interface/config.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | use std::ops::Range; 3 | 4 | use serde::Deserialize; 5 | 6 | use crate::driver_interface::FullDeviceId; 7 | 8 | #[derive(Clone, Debug, Default, Deserialize)] 9 | pub struct Config { 10 | pub drivers: Vec, 11 | } 12 | 13 | #[derive(Clone, Debug, Default, Deserialize)] 14 | pub struct DriverConfig { 15 | pub name: Option, 16 | pub class: Option, 17 | pub subclass: Option, 18 | pub interface: Option, 19 | pub ids: Option>>, 20 | pub vendor: Option, 21 | pub device: Option, 22 | pub device_id_range: Option>, 23 | pub command: Vec, 24 | } 25 | 26 | impl DriverConfig { 27 | pub fn match_function(&self, id: &FullDeviceId) -> bool { 28 | if let Some(class) = self.class { 29 | if class != id.class { 30 | return false; 31 | } 32 | } 33 | 34 | if let Some(subclass) = self.subclass { 35 | if subclass != id.subclass { 36 | return false; 37 | } 38 | } 39 | 40 | if let Some(interface) = self.interface { 41 | if interface != id.interface { 42 | return false; 43 | } 44 | } 45 | 46 | if let Some(ref ids) = self.ids { 47 | let mut device_found = false; 48 | for (vendor, devices) in ids { 49 | let vendor_without_prefix = vendor.trim_start_matches("0x"); 50 | let vendor = i64::from_str_radix(vendor_without_prefix, 16).unwrap() as u16; 51 | 52 | if vendor != id.vendor_id { 53 | continue; 54 | } 55 | 56 | for device in devices { 57 | if *device == id.device_id { 58 | device_found = true; 59 | break; 60 | } 61 | } 62 | } 63 | if !device_found { 64 | return false; 65 | } 66 | } else { 67 | if let Some(vendor) = self.vendor { 68 | if vendor != id.vendor_id { 69 | return false; 70 | } 71 | } 72 | 73 | if let Some(device) = self.device { 74 | if device != id.device_id { 75 | return false; 76 | } 77 | } 78 | } 79 | 80 | if let Some(ref device_id_range) = self.device_id_range { 81 | if id.device_id < device_id_range.start || device_id_range.end <= id.device_id { 82 | return false; 83 | } 84 | } 85 | 86 | true 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /pcid/src/driver_interface/id.rs: -------------------------------------------------------------------------------- 1 | use pci_types::device_type::DeviceType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | /// All identifying information of a PCI function. 5 | #[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)] 6 | pub struct FullDeviceId { 7 | pub vendor_id: u16, 8 | pub device_id: u16, 9 | pub class: u8, 10 | pub subclass: u8, 11 | pub interface: u8, 12 | pub revision: u8, 13 | } 14 | 15 | impl FullDeviceId { 16 | pub fn display(&self) -> String { 17 | let mut string = format!( 18 | "{:>04X}:{:>04X} {:>02X}.{:>02X}.{:>02X}.{:>02X} {:?}", 19 | self.vendor_id, 20 | self.device_id, 21 | self.class, 22 | self.subclass, 23 | self.interface, 24 | self.revision, 25 | self.class, 26 | ); 27 | let device_type = DeviceType::from((self.class, self.subclass)); 28 | match device_type { 29 | DeviceType::LegacyVgaCompatible => string.push_str(" VGA CTL"), 30 | DeviceType::IdeController => string.push_str(" IDE"), 31 | DeviceType::SataController => match self.interface { 32 | 0 => string.push_str(" SATA VND"), 33 | 1 => string.push_str(" SATA AHCI"), 34 | _ => (), 35 | }, 36 | DeviceType::UsbController => match self.interface { 37 | 0x00 => string.push_str(" UHCI"), 38 | 0x10 => string.push_str(" OHCI"), 39 | 0x20 => string.push_str(" EHCI"), 40 | 0x30 => string.push_str(" XHCI"), 41 | _ => (), 42 | }, 43 | DeviceType::NvmeController => string.push_str(" NVME"), 44 | _ => (), 45 | } 46 | string 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /pcid/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Interface to `pcid`. 2 | 3 | mod driver_interface; 4 | pub use driver_interface::*; 5 | -------------------------------------------------------------------------------- /rtcd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rtcd" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2018" 6 | license = "MIT" 7 | 8 | 9 | [dependencies] 10 | anyhow = "1" 11 | 12 | common = { path = "../common" } 13 | -------------------------------------------------------------------------------- /rtcd/src/main.rs: -------------------------------------------------------------------------------- 1 | use anyhow::{Context, Result}; 2 | 3 | // TODO: Do not use target architecture to distinguish these. 4 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 5 | mod x86; 6 | 7 | /// The rtc driver runs only once, being perhaps the first of all processes that init starts (since 8 | /// early logging benefits from knowing the time, even though this can be adjusted later once the 9 | /// time is known). The sole job of `rtcd` is to read from the hardware real-time clock, and then 10 | /// write the offset to the kernel. 11 | 12 | fn main() -> Result<()> { 13 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 14 | { 15 | common::acquire_port_io_rights().context("failed to set iopl")?; 16 | 17 | let time_s = self::x86::get_time(); 18 | let time_ns = u128::from(time_s) * 1_000_000_000; 19 | 20 | std::fs::write("/scheme/sys/update_time_offset", &time_ns.to_ne_bytes()) 21 | .context("failed to write to time offset")?; 22 | } 23 | // TODO: aarch64 is currently handled in the kernel 24 | 25 | Ok(()) 26 | } 27 | -------------------------------------------------------------------------------- /rtcd/src/x86.rs: -------------------------------------------------------------------------------- 1 | // TODO: Get RTC information from acpid. 2 | use common::io::{Io, Pio}; 3 | 4 | pub fn get_time() -> u64 { 5 | Rtc::new().time() 6 | } 7 | 8 | fn cvt_bcd(value: usize) -> usize { 9 | (value & 0xF) + ((value / 16) * 10) 10 | } 11 | 12 | /// RTC 13 | pub struct Rtc { 14 | addr: Pio, 15 | data: Pio, 16 | nmi: bool, 17 | } 18 | 19 | impl Rtc { 20 | /// Create new empty RTC 21 | pub fn new() -> Self { 22 | Rtc { 23 | addr: Pio::::new(0x70), 24 | data: Pio::::new(0x71), 25 | nmi: false, 26 | } 27 | } 28 | 29 | /// Read 30 | unsafe fn read(&mut self, reg: u8) -> u8 { 31 | if self.nmi { 32 | self.addr.write(reg & 0x7F); 33 | } else { 34 | self.addr.write(reg | 0x80); 35 | } 36 | self.data.read() 37 | } 38 | 39 | /// Write 40 | #[allow(dead_code)] 41 | unsafe fn write(&mut self, reg: u8, value: u8) { 42 | if self.nmi { 43 | self.addr.write(reg & 0x7F); 44 | } else { 45 | self.addr.write(reg | 0x80); 46 | } 47 | self.data.write(value); 48 | } 49 | 50 | /// Wait for an update, can take one second if full is specified! 51 | unsafe fn wait(&mut self, full: bool) { 52 | if full { 53 | while self.read(0xA) & 0x80 != 0x80 {} 54 | } 55 | while self.read(0xA) & 0x80 == 0x80 {} 56 | } 57 | 58 | /// Get time without waiting 59 | pub unsafe fn time_no_wait(&mut self) -> u64 { 60 | /*let century_register = if let Some(ref fadt) = acpi::ACPI_TABLE.lock().fadt { 61 | Some(fadt.century) 62 | } else { 63 | None 64 | };*/ 65 | 66 | let mut second = self.read(0) as usize; 67 | let mut minute = self.read(2) as usize; 68 | let mut hour = self.read(4) as usize; 69 | let mut day = self.read(7) as usize; 70 | let mut month = self.read(8) as usize; 71 | let mut year = self.read(9) as usize; 72 | let mut century = /* TODO: Fix invalid value from VirtualBox 73 | if let Some(century_reg) = century_register { 74 | self.read(century_reg) as usize 75 | } else */ { 76 | 20 77 | }; 78 | let register_b = self.read(0xB); 79 | 80 | if register_b & 4 != 4 { 81 | second = cvt_bcd(second); 82 | minute = cvt_bcd(minute); 83 | hour = cvt_bcd(hour & 0x7F) | (hour & 0x80); 84 | day = cvt_bcd(day); 85 | month = cvt_bcd(month); 86 | year = cvt_bcd(year); 87 | century = /* TODO: Fix invalid value from VirtualBox 88 | if century_register.is_some() { 89 | cvt_bcd(century) 90 | } else */ { 91 | century 92 | }; 93 | } 94 | 95 | if register_b & 2 != 2 || hour & 0x80 == 0x80 { 96 | hour = ((hour & 0x7F) + 12) % 24; 97 | } 98 | 99 | year += century * 100; 100 | 101 | // Unix time from clock 102 | let mut secs: u64 = (year as u64 - 1970) * 31_536_000; 103 | 104 | let mut leap_days = (year as u64 - 1972) / 4 + 1; 105 | if year % 4 == 0 && month <= 2 { 106 | leap_days -= 1; 107 | } 108 | secs += leap_days * 86_400; 109 | 110 | match month { 111 | 2 => secs += 2_678_400, 112 | 3 => secs += 5_097_600, 113 | 4 => secs += 7_776_000, 114 | 5 => secs += 10_368_000, 115 | 6 => secs += 13_046_400, 116 | 7 => secs += 15_638_400, 117 | 8 => secs += 18_316_800, 118 | 9 => secs += 20_995_200, 119 | 10 => secs += 23_587_200, 120 | 11 => secs += 26_265_600, 121 | 12 => secs += 28_857_600, 122 | _ => (), 123 | } 124 | 125 | secs += (day as u64 - 1) * 86_400; 126 | secs += hour as u64 * 3600; 127 | secs += minute as u64 * 60; 128 | secs += second as u64; 129 | 130 | secs 131 | } 132 | 133 | /// Get time 134 | pub fn time(&mut self) -> u64 { 135 | loop { 136 | unsafe { 137 | self.wait(false); 138 | let time = self.time_no_wait(); 139 | self.wait(false); 140 | let next_time = self.time_no_wait(); 141 | if time == next_time { 142 | return time; 143 | } 144 | } 145 | } 146 | } 147 | } 148 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "nightly-2025-01-12" 3 | components = ["rust-src"] 4 | -------------------------------------------------------------------------------- /storage/ahcid/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /storage/ahcid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ahcid" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | bitflags = "1.2" 8 | byteorder = "1.2" 9 | log = "0.4" 10 | redox-daemon = "0.1" 11 | redox_syscall = { version = "0.5", features = ["std"] } 12 | 13 | common = { path = "../../common" } 14 | driver-block = { path = "../driver-block" } 15 | pcid = { path = "../../pcid" } 16 | libredox = "0.1.3" 17 | redox_event = "0.4" 18 | -------------------------------------------------------------------------------- /storage/ahcid/src/ahci/disk_atapi.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | use std::convert::TryInto; 4 | use std::ptr; 5 | 6 | use byteorder::{BigEndian, ByteOrder}; 7 | 8 | use syscall::error::{Error, Result, EBADF}; 9 | 10 | use common::dma::Dma; 11 | 12 | use super::hba::{HbaCmdHeader, HbaCmdTable, HbaPort}; 13 | use super::Disk; 14 | 15 | const SCSI_READ_CAPACITY: u8 = 0x25; 16 | const SCSI_READ10: u8 = 0x28; 17 | 18 | pub struct DiskATAPI { 19 | id: usize, 20 | port: &'static mut HbaPort, 21 | size: u64, 22 | clb: Dma<[HbaCmdHeader; 32]>, 23 | ctbas: [Dma; 32], 24 | _fb: Dma<[u8; 256]>, 25 | // Just using the same buffer size as DiskATA 26 | // Although the sector size is different (and varies) 27 | buf: Dma<[u8; 256 * 512]>, 28 | blk_count: u32, 29 | blk_size: u32, 30 | } 31 | 32 | impl DiskATAPI { 33 | pub fn new(id: usize, port: &'static mut HbaPort) -> Result { 34 | let mut clb = unsafe { Dma::zeroed()?.assume_init() }; 35 | 36 | let mut ctbas: [_; 32] = (0..32) 37 | .map(|_| Ok(unsafe { Dma::zeroed()?.assume_init() })) 38 | .collect::>>()? 39 | .try_into() 40 | .unwrap_or_else(|_| unreachable!()); 41 | 42 | let mut fb = unsafe { Dma::zeroed()?.assume_init() }; 43 | let mut buf = unsafe { Dma::zeroed()?.assume_init() }; 44 | 45 | port.init(&mut clb, &mut ctbas, &mut fb); 46 | 47 | let size = unsafe { port.identify_packet(&mut clb, &mut ctbas).unwrap_or(0) }; 48 | 49 | let mut cmd = [0; 16]; 50 | cmd[0] = SCSI_READ_CAPACITY; 51 | port.atapi_dma(&cmd, 8, &mut clb, &mut ctbas, &mut buf)?; 52 | 53 | // Instead of a count, contains number of last LBA, so add 1 54 | let blk_count = BigEndian::read_u32(&buf[0..4]) + 1; 55 | let blk_size = BigEndian::read_u32(&buf[4..8]); 56 | 57 | Ok(DiskATAPI { 58 | id, 59 | port, 60 | size, 61 | clb, 62 | ctbas, 63 | _fb: fb, 64 | buf, 65 | blk_count, 66 | blk_size, 67 | }) 68 | } 69 | } 70 | 71 | impl Disk for DiskATAPI { 72 | fn block_size(&self) -> u32 { 73 | self.blk_size 74 | } 75 | 76 | fn size(&self) -> u64 { 77 | u64::from(self.blk_count) * u64::from(self.blk_size) 78 | } 79 | 80 | async fn read(&mut self, block: u64, buffer: &mut [u8]) -> Result { 81 | // TODO: Handle audio CDs, which use special READ CD command 82 | 83 | let blk_len = self.blk_size; 84 | let sectors = buffer.len() as u32 / blk_len; 85 | 86 | fn read10_cmd(block: u32, count: u16) -> [u8; 16] { 87 | let mut cmd = [0; 16]; 88 | cmd[0] = SCSI_READ10; 89 | BigEndian::write_u32(&mut cmd[2..6], block as u32); 90 | BigEndian::write_u16(&mut cmd[7..9], count as u16); 91 | cmd 92 | } 93 | 94 | let mut sector = 0; 95 | let buf_len = (256 * 512) / blk_len; 96 | let buf_size = buf_len * blk_len; 97 | while sectors - sector >= buf_len { 98 | let cmd = read10_cmd(block as u32 + sector, buf_len as u16); 99 | self.port.atapi_dma( 100 | &cmd, 101 | buf_size, 102 | &mut self.clb, 103 | &mut self.ctbas, 104 | &mut self.buf, 105 | )?; 106 | 107 | unsafe { 108 | ptr::copy( 109 | self.buf.as_ptr(), 110 | buffer 111 | .as_mut_ptr() 112 | .offset(sector as isize * blk_len as isize), 113 | buf_size as usize, 114 | ); 115 | } 116 | 117 | sector += blk_len; 118 | } 119 | if sector < sectors { 120 | let cmd = read10_cmd(block as u32 + sector, (sectors - sector) as u16); 121 | self.port.atapi_dma( 122 | &cmd, 123 | buf_size, 124 | &mut self.clb, 125 | &mut self.ctbas, 126 | &mut self.buf, 127 | )?; 128 | 129 | unsafe { 130 | ptr::copy( 131 | self.buf.as_ptr(), 132 | buffer 133 | .as_mut_ptr() 134 | .offset(sector as isize * blk_len as isize), 135 | ((sectors - sector) * blk_len) as usize, 136 | ); 137 | } 138 | 139 | sector += sectors - sector; 140 | } 141 | 142 | Ok((sector * blk_len) as usize) 143 | } 144 | 145 | async fn write(&mut self, _block: u64, _buffer: &[u8]) -> Result { 146 | Err(Error::new(EBADF)) // TODO: Implement writing 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /storage/ahcid/src/ahci/mod.rs: -------------------------------------------------------------------------------- 1 | use common::io::Io; 2 | use driver_block::Disk; 3 | use log::{error, info}; 4 | 5 | use self::disk_ata::DiskATA; 6 | use self::disk_atapi::DiskATAPI; 7 | use self::hba::{HbaMem, HbaPortType}; 8 | 9 | pub mod disk_ata; 10 | pub mod disk_atapi; 11 | pub mod fis; 12 | pub mod hba; 13 | 14 | pub enum AnyDisk { 15 | Ata(DiskATA), 16 | Atapi(DiskATAPI), 17 | } 18 | impl Disk for AnyDisk { 19 | fn block_size(&self) -> u32 { 20 | match self { 21 | Self::Ata(a) => a.block_size(), 22 | Self::Atapi(a) => a.block_size(), 23 | } 24 | } 25 | fn size(&self) -> u64 { 26 | match self { 27 | Self::Ata(a) => a.size(), 28 | Self::Atapi(a) => a.size(), 29 | } 30 | } 31 | async fn read(&mut self, base: u64, buffer: &mut [u8]) -> syscall::Result { 32 | match self { 33 | Self::Ata(a) => a.read(base, buffer).await, 34 | Self::Atapi(a) => a.read(base, buffer).await, 35 | } 36 | } 37 | async fn write(&mut self, base: u64, buffer: &[u8]) -> syscall::Result { 38 | match self { 39 | Self::Ata(a) => a.write(base, buffer).await, 40 | Self::Atapi(a) => a.write(base, buffer).await, 41 | } 42 | } 43 | } 44 | 45 | pub fn disks(base: usize, name: &str) -> (&'static mut HbaMem, Vec) { 46 | let hba_mem = unsafe { &mut *(base as *mut HbaMem) }; 47 | hba_mem.init(); 48 | let pi = hba_mem.pi.read(); 49 | let disks: Vec = (0..hba_mem.ports.len()) 50 | .filter(|&i| pi & 1 << i as i32 == 1 << i as i32) 51 | .filter_map(|i| { 52 | let port = unsafe { &mut *hba_mem.ports.as_mut_ptr().add(i) }; 53 | let port_type = port.probe(); 54 | info!("{}-{}: {:?}", name, i, port_type); 55 | 56 | let disk: Option = match port_type { 57 | HbaPortType::SATA => match DiskATA::new(i, port) { 58 | Ok(disk) => Some(AnyDisk::Ata(disk)), 59 | Err(err) => { 60 | error!("{}: {}", i, err); 61 | None 62 | } 63 | }, 64 | HbaPortType::SATAPI => match DiskATAPI::new(i, port) { 65 | Ok(disk) => Some(AnyDisk::Atapi(disk)), 66 | Err(err) => { 67 | error!("{}: {}", i, err); 68 | None 69 | } 70 | }, 71 | _ => None, 72 | }; 73 | 74 | disk 75 | }) 76 | .collect(); 77 | 78 | (hba_mem, disks) 79 | } 80 | -------------------------------------------------------------------------------- /storage/ahcid/src/main.rs: -------------------------------------------------------------------------------- 1 | #![cfg_attr(target_arch = "aarch64", feature(stdsimd))] // Required for yield instruction 2 | 3 | use std::io::{Read, Write}; 4 | use std::os::fd::AsRawFd; 5 | use std::usize; 6 | 7 | use common::io::Io; 8 | use driver_block::{DiskScheme, ExecutorTrait, FuturesExecutor}; 9 | use event::{EventFlags, RawEventQueue}; 10 | use pcid_interface::PciFunctionHandle; 11 | 12 | use log::{error, info}; 13 | 14 | pub mod ahci; 15 | 16 | fn main() { 17 | redox_daemon::Daemon::new(daemon).expect("ahcid: failed to daemonize"); 18 | } 19 | 20 | fn daemon(daemon: redox_daemon::Daemon) -> ! { 21 | let mut pcid_handle = PciFunctionHandle::connect_default(); 22 | let pci_config = pcid_handle.config(); 23 | 24 | let mut name = pci_config.func.name(); 25 | name.push_str("_ahci"); 26 | 27 | let irq = pci_config 28 | .func 29 | .legacy_interrupt_line 30 | .expect("ahcid: no legacy interrupts supported"); 31 | 32 | common::setup_logging( 33 | "disk", 34 | "pcie", 35 | &name, 36 | log::LevelFilter::Info, 37 | log::LevelFilter::Info, 38 | ); 39 | 40 | info!(" + AHCI {}", pci_config.func.display()); 41 | 42 | let address = unsafe { pcid_handle.map_bar(5) }.ptr.as_ptr() as usize; 43 | { 44 | let (hba_mem, disks) = ahci::disks(address as usize, &name); 45 | 46 | let scheme_name = format!("disk.{}", name); 47 | let mut scheme = DiskScheme::new( 48 | Some(daemon), 49 | scheme_name, 50 | disks 51 | .into_iter() 52 | .enumerate() 53 | .map(|(i, disk)| (i as u32, disk)) 54 | .collect(), 55 | &FuturesExecutor, 56 | ); 57 | 58 | let mut irq_file = irq.irq_handle("ahcid"); 59 | let irq_fd = irq_file.as_raw_fd() as usize; 60 | 61 | let event_queue = RawEventQueue::new().expect("ahcid: failed to create event queue"); 62 | 63 | libredox::call::setrens(0, 0).expect("ahcid: failed to enter null namespace"); 64 | 65 | event_queue 66 | .subscribe(scheme.event_handle().raw(), 1, EventFlags::READ) 67 | .expect("ahcid: failed to event scheme socket"); 68 | event_queue 69 | .subscribe(irq_fd, 1, EventFlags::READ) 70 | .expect("ahcid: failed to event irq scheme"); 71 | 72 | for event in event_queue { 73 | let event = event.unwrap(); 74 | if event.fd == scheme.event_handle().raw() { 75 | FuturesExecutor.block_on(scheme.tick()).unwrap(); 76 | } else if event.fd == irq_fd { 77 | let mut irq = [0; 8]; 78 | if irq_file 79 | .read(&mut irq) 80 | .expect("ahcid: failed to read irq file") 81 | >= irq.len() 82 | { 83 | let is = hba_mem.is.read(); 84 | if is > 0 { 85 | let pi = hba_mem.pi.read(); 86 | let pi_is = pi & is; 87 | for i in 0..hba_mem.ports.len() { 88 | if pi_is & 1 << i > 0 { 89 | let port = &mut hba_mem.ports[i]; 90 | let is = port.is.read(); 91 | port.is.write(is); 92 | } 93 | } 94 | hba_mem.is.write(is); 95 | 96 | irq_file 97 | .write(&irq) 98 | .expect("ahcid: failed to write irq file"); 99 | 100 | FuturesExecutor.block_on(scheme.tick()).unwrap(); 101 | } 102 | } 103 | } else { 104 | error!("Unknown event {}", event.fd); 105 | } 106 | } 107 | } 108 | 109 | std::process::exit(0); 110 | } 111 | -------------------------------------------------------------------------------- /storage/bcm2835-sdhcid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "bcm2835-sdhcid" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | fdt = { git = "https://github.com/repnop/fdt.git" } 10 | common = { path = "../../common" } 11 | driver-block = { path = "../driver-block" } 12 | 13 | redox-daemon = "0.1" 14 | libredox = "0.1.3" 15 | redox_syscall = { version = "0.5", features = ["std"] } 16 | redox_event = "0.4" 17 | -------------------------------------------------------------------------------- /storage/bcm2835-sdhcid/src/main.rs: -------------------------------------------------------------------------------- 1 | #![feature(let_chains)] 2 | 3 | use std::process; 4 | 5 | use driver_block::{Disk, DiskScheme, ExecutorTrait, TrivialExecutor}; 6 | use event::{EventFlags, RawEventQueue}; 7 | use fdt::Fdt; 8 | 9 | mod sd; 10 | 11 | #[cfg(target_os = "redox")] 12 | fn get_dtb() -> Vec { 13 | std::fs::read("kernel.dtb:").unwrap() 14 | } 15 | 16 | #[cfg(target_os = "linux")] 17 | fn get_dtb() -> Vec { 18 | use std::env; 19 | if let Some(arg1) = env::args().nth(1) { 20 | std::fs::read(arg1).unwrap() 21 | } else { 22 | Vec::new() 23 | } 24 | } 25 | 26 | fn main() { 27 | redox_daemon::Daemon::new(daemon).expect("mmc:failed to daemonize"); 28 | } 29 | 30 | fn daemon(daemon: redox_daemon::Daemon) -> ! { 31 | let dtb_data = get_dtb(); 32 | println!("read from OS, len = {}", dtb_data.len()); 33 | if dtb_data.len() == 0 { 34 | process::exit(0); 35 | } 36 | 37 | let fdt = Fdt::new(&dtb_data).unwrap(); 38 | println!("DTB model = {}", fdt.root().model()); 39 | let with = ["brcm,bcm2835-sdhci"]; 40 | let compat_node = fdt.find_compatible(&with).unwrap(); 41 | let reg = compat_node.reg().unwrap().next().unwrap(); 42 | let reg_size = reg.size.unwrap(); 43 | let mut reg_addr = reg.starting_address as usize; 44 | println!( 45 | "DeviceMemory start = 0x{:08x}, size = 0x{:08x}", 46 | reg_addr, reg_size 47 | ); 48 | if let Some(mut ranges) = fdt.find_node("/soc").and_then(|f| f.ranges()) { 49 | let range = ranges 50 | .find(|f| f.child_bus_address <= reg_addr && reg_addr - f.child_bus_address < f.size) 51 | .expect("Couldn't find device range in /soc/@ranges"); 52 | reg_addr = range.parent_bus_address + (reg_addr - range.child_bus_address); 53 | println!( 54 | "DeviceMemory remapped onto CPU address space: start = 0x{:08x}, size = 0x{:08x}", 55 | reg_addr, reg_size 56 | ); 57 | } 58 | 59 | let addr = unsafe { 60 | common::physmap( 61 | reg_addr, 62 | reg_size, 63 | common::Prot::RW, 64 | common::MemoryType::DeviceMemory, 65 | ) 66 | .expect("bcm2835-sdhcid: failed to map address") as usize 67 | }; 68 | println!( 69 | "ioremap 0x{:08x} to 0x{:08x} 2222", 70 | reg.starting_address as usize, addr 71 | ); 72 | let mut sdhci = sd::SdHostCtrl::new(addr); 73 | unsafe { 74 | sdhci.init(); 75 | /* 76 | let mut buf1 = [0u32; 512]; 77 | sdhci.sd_readblock(1, &mut buf1, 1); 78 | println!("readblock {:?}", buf1); 79 | buf1[0] = 0xdead_0000; 80 | buf1[1] = 0xdead_0000; 81 | buf1[2] = 0x0000_dead; 82 | buf1[3] = 0x0000_dead; 83 | sdhci.sd_writeblock(1, &buf1, 1); 84 | sdhci.sd_readblock(1, &mut buf1, 1); 85 | println!("readblock {:?}", buf1); 86 | */ 87 | /* 88 | let mut buf1 = [0u8; 512]; 89 | sdhci.read(1, &mut buf1); 90 | println!("readblock {:?}", buf1); 91 | buf1[0] = 0xde; 92 | buf1[1] = 0xad; 93 | buf1[2] = 0xde; 94 | buf1[3] = 0xad; 95 | sdhci.write(1, &buf1); 96 | sdhci.read(1, &mut buf1); 97 | println!("readblock {:?}", buf1); 98 | */ 99 | } 100 | 101 | let mut disks = Vec::new(); 102 | disks.push(sdhci); 103 | let mut scheme = DiskScheme::new( 104 | Some(daemon), 105 | "disk.mmc".to_string(), 106 | disks 107 | .into_iter() 108 | .enumerate() 109 | .map(|(i, disk)| (i as u32, disk)) 110 | .collect(), 111 | &TrivialExecutor, // TODO: real executor 112 | ); 113 | 114 | let event_queue = RawEventQueue::new().expect("mmcd: failed to open event file"); 115 | event_queue 116 | .subscribe(scheme.event_handle().raw(), 0, EventFlags::READ) 117 | .expect("mmcd: failed to event disk scheme"); 118 | 119 | libredox::call::setrens(0, 0).expect("mmcd: failed to enter null namespace"); 120 | 121 | for event in event_queue { 122 | let event = event.unwrap(); 123 | if event.fd == scheme.event_handle().raw() { 124 | TrivialExecutor.block_on(scheme.tick()).unwrap(); 125 | } else { 126 | println!("Unknown event {}", event.fd); 127 | } 128 | } 129 | process::exit(0); 130 | } 131 | -------------------------------------------------------------------------------- /storage/driver-block/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "driver-block" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | executor = { path = "../../executor" } 8 | partitionlib = { path = "../partitionlib" } 9 | 10 | libredox = "0.1.3" 11 | log = "0.4" 12 | 13 | # TODO: migrate virtio to our executor 14 | futures = { version = "0.3.28", features = ["executor"] } 15 | 16 | redox-daemon = "0.1" 17 | redox_syscall = { version = "0.5", features = ["std"] } 18 | redox-scheme = "0.5" 19 | -------------------------------------------------------------------------------- /storage/ided/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /storage/ided/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ided" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | common = { path = "../../common" } 8 | driver-block = { path = "../driver-block" } 9 | libredox = "0.1.3" 10 | log = "0.4" 11 | pcid = { path = "../../pcid" } 12 | redox-daemon = "0.1" 13 | redox_syscall = { version = "0.5", features = ["std"] } 14 | redox_event = "0.4" 15 | -------------------------------------------------------------------------------- /storage/lived/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "lived" 3 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 4 | version = "0.1.0" 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | anyhow = "1" 12 | libredox = "0.1.3" 13 | redox-daemon = "0.1" 14 | redox_syscall = { version = "0.5", features = ["std"] } 15 | redox_event = "0.4" 16 | driver-block = { path = "../driver-block" } 17 | -------------------------------------------------------------------------------- /storage/nvmed/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /storage/nvmed/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "nvmed" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | [dependencies] 7 | arrayvec = "0.7" 8 | bitflags = "2" 9 | futures = "0.3" 10 | libredox = "0.1.3" 11 | log = "0.4" 12 | parking_lot = "0.12.1" 13 | redox-daemon = "0.1" 14 | redox_event = "0.4.1" 15 | redox_syscall = { version = "0.5", features = ["std"] } 16 | smallvec = "1" 17 | 18 | executor = { path = "../../executor" } 19 | common = { path = "../../common" } 20 | driver-block = { path = "../driver-block" } 21 | partitionlib = { path = "../partitionlib" } 22 | pcid = { path = "../../pcid" } 23 | 24 | [features] 25 | default = [] 26 | -------------------------------------------------------------------------------- /storage/nvmed/src/nvme/cmd.rs: -------------------------------------------------------------------------------- 1 | use super::NvmeCmd; 2 | 3 | impl NvmeCmd { 4 | pub fn create_io_completion_queue( 5 | cid: u16, 6 | qid: u16, 7 | ptr: usize, 8 | size: u16, 9 | iv: Option, 10 | ) -> Self { 11 | const DW11_PHYSICALLY_CONTIGUOUS_BIT: u32 = 0x0000_0001; 12 | const DW11_ENABLE_INTERRUPTS_BIT: u32 = 0x0000_0002; 13 | const DW11_INTERRUPT_VECTOR_SHIFT: u8 = 16; 14 | 15 | Self { 16 | opcode: 5, 17 | flags: 0, 18 | cid, 19 | nsid: 0, 20 | _rsvd: 0, 21 | mptr: 0, 22 | dptr: [ptr as u64, 0], 23 | cdw10: ((size as u32) << 16) | (qid as u32), 24 | 25 | cdw11: DW11_PHYSICALLY_CONTIGUOUS_BIT 26 | | if let Some(iv) = iv { 27 | // enable interrupts if a vector is present 28 | DW11_ENABLE_INTERRUPTS_BIT | (u32::from(iv) << DW11_INTERRUPT_VECTOR_SHIFT) 29 | } else { 30 | 0 31 | }, 32 | 33 | cdw12: 0, 34 | cdw13: 0, 35 | cdw14: 0, 36 | cdw15: 0, 37 | } 38 | } 39 | 40 | pub fn create_io_submission_queue( 41 | cid: u16, 42 | qid: u16, 43 | ptr: usize, 44 | size: u16, 45 | cqid: u16, 46 | ) -> Self { 47 | Self { 48 | opcode: 1, 49 | flags: 0, 50 | cid, 51 | nsid: 0, 52 | _rsvd: 0, 53 | mptr: 0, 54 | dptr: [ptr as u64, 0], 55 | cdw10: ((size as u32) << 16) | (qid as u32), 56 | cdw11: ((cqid as u32) << 16) | 1, /* Physically Contiguous */ 57 | //TODO: QPRIO 58 | cdw12: 0, //TODO: NVMSETID 59 | cdw13: 0, 60 | cdw14: 0, 61 | cdw15: 0, 62 | } 63 | } 64 | 65 | pub fn identify_namespace(cid: u16, ptr: usize, nsid: u32) -> Self { 66 | Self { 67 | opcode: 6, 68 | flags: 0, 69 | cid, 70 | nsid, 71 | _rsvd: 0, 72 | mptr: 0, 73 | dptr: [ptr as u64, 0], 74 | cdw10: 0, 75 | cdw11: 0, 76 | cdw12: 0, 77 | cdw13: 0, 78 | cdw14: 0, 79 | cdw15: 0, 80 | } 81 | } 82 | 83 | pub fn identify_controller(cid: u16, ptr: usize) -> Self { 84 | Self { 85 | opcode: 6, 86 | flags: 0, 87 | cid, 88 | nsid: 0, 89 | _rsvd: 0, 90 | mptr: 0, 91 | dptr: [ptr as u64, 0], 92 | cdw10: 1, 93 | cdw11: 0, 94 | cdw12: 0, 95 | cdw13: 0, 96 | cdw14: 0, 97 | cdw15: 0, 98 | } 99 | } 100 | 101 | pub fn identify_namespace_list(cid: u16, ptr: usize, base: u32) -> Self { 102 | Self { 103 | opcode: 6, 104 | flags: 0, 105 | cid, 106 | nsid: base, 107 | _rsvd: 0, 108 | mptr: 0, 109 | dptr: [ptr as u64, 0], 110 | cdw10: 2, 111 | cdw11: 0, 112 | cdw12: 0, 113 | cdw13: 0, 114 | cdw14: 0, 115 | cdw15: 0, 116 | } 117 | } 118 | pub fn get_features(cid: u16, ptr: usize, fid: u8) -> Self { 119 | Self { 120 | opcode: 0xA, 121 | dptr: [ptr as u64, 0], 122 | cdw10: u32::from(fid), // TODO: SEL 123 | ..Default::default() 124 | } 125 | } 126 | 127 | pub fn io_read(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self { 128 | Self { 129 | opcode: 2, 130 | flags: 0, 131 | cid, 132 | nsid, 133 | _rsvd: 0, 134 | mptr: 0, 135 | dptr: [ptr0, ptr1], 136 | cdw10: lba as u32, 137 | cdw11: (lba >> 32) as u32, 138 | cdw12: blocks_1 as u32, 139 | cdw13: 0, 140 | cdw14: 0, 141 | cdw15: 0, 142 | } 143 | } 144 | 145 | pub fn io_write(cid: u16, nsid: u32, lba: u64, blocks_1: u16, ptr0: u64, ptr1: u64) -> Self { 146 | Self { 147 | opcode: 1, 148 | flags: 0, 149 | cid, 150 | nsid, 151 | _rsvd: 0, 152 | mptr: 0, 153 | dptr: [ptr0, ptr1], 154 | cdw10: lba as u32, 155 | cdw11: (lba >> 32) as u32, 156 | cdw12: blocks_1 as u32, 157 | cdw13: 0, 158 | cdw14: 0, 159 | cdw15: 0, 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /storage/nvmed/src/nvme/executor.rs: -------------------------------------------------------------------------------- 1 | use std::cell::RefCell; 2 | use std::fs::File; 3 | use std::rc::Rc; 4 | use std::sync::Arc; 5 | 6 | use executor::{Hardware, LocalExecutor}; 7 | 8 | use super::{CmdId, CqId, Nvme, NvmeCmd, NvmeComp, SqId}; 9 | 10 | pub struct NvmeHw; 11 | 12 | impl Hardware for NvmeHw { 13 | type Iv = u16; 14 | type Sqe = NvmeCmd; 15 | type Cqe = NvmeComp; 16 | type CmdId = CmdId; 17 | type CqId = CqId; 18 | type SqId = SqId; 19 | type GlobalCtxt = Arc; 20 | 21 | fn mask_vector(ctxt: &Arc, iv: Self::Iv) { 22 | ctxt.set_vector_masked(iv, true) 23 | } 24 | fn unmask_vector(ctxt: &Arc, iv: Self::Iv) { 25 | ctxt.set_vector_masked(iv, false) 26 | } 27 | fn set_sqe_cmdid(sqe: &mut NvmeCmd, id: CmdId) { 28 | sqe.cid = id; 29 | } 30 | fn get_cqe_cmdid(cqe: &Self::Cqe) -> Self::CmdId { 31 | cqe.cid 32 | } 33 | fn vtable() -> &'static std::task::RawWakerVTable { 34 | &VTABLE 35 | } 36 | fn current() -> std::rc::Rc> { 37 | THE_EXECUTOR.with(|exec| Rc::clone(exec.borrow().as_ref().unwrap())) 38 | } 39 | fn try_submit( 40 | nvme: &Arc, 41 | sq_id: Self::SqId, 42 | success: impl FnOnce(Self::CmdId) -> Self::Sqe, 43 | fail: impl FnOnce(), 44 | ) -> Option<(Self::CqId, Self::CmdId)> { 45 | let ctxt = nvme.cur_thread_ctxt(); 46 | let ctxt = ctxt.lock(); 47 | 48 | nvme.try_submit_raw(&*ctxt, sq_id, success, fail) 49 | } 50 | fn poll_cqes(nvme: &Arc, mut handle: impl FnMut(Self::CqId, Self::Cqe)) { 51 | let ctxt = nvme.cur_thread_ctxt(); 52 | let ctxt = ctxt.lock(); 53 | 54 | for (sq_cq_id, (sq, cq)) in ctxt.queues.borrow_mut().iter_mut() { 55 | while let Some((new_head, cqe)) = cq.complete() { 56 | unsafe { 57 | nvme.completion_queue_head(*sq_cq_id, new_head); 58 | } 59 | sq.head = cqe.sq_head; 60 | log::trace!("new head {new_head} cqe {cqe:?}"); 61 | handle(*sq_cq_id, cqe); 62 | } 63 | } 64 | } 65 | fn sq_cq(_ctxt: &Arc, id: Self::CqId) -> Self::SqId { 66 | id 67 | } 68 | } 69 | 70 | static VTABLE: std::task::RawWakerVTable = executor::vtable::(); 71 | 72 | thread_local! { 73 | static THE_EXECUTOR: RefCell>>> = RefCell::new(None); 74 | } 75 | 76 | pub type NvmeExecutor = LocalExecutor; 77 | 78 | pub fn init(nvme: Arc, iv: u16, intx: bool, irq_handle: File) -> Rc> { 79 | let this = Rc::new(executor::init_raw(nvme, iv, intx, irq_handle)); 80 | THE_EXECUTOR.with(|exec| *exec.borrow_mut() = Some(Rc::clone(&this))); 81 | this 82 | } 83 | -------------------------------------------------------------------------------- /storage/nvmed/src/nvme/queues.rs: -------------------------------------------------------------------------------- 1 | use std::cell::UnsafeCell; 2 | use std::ptr; 3 | use syscall::Result; 4 | 5 | use common::dma::Dma; 6 | 7 | /// A submission queue entry. 8 | #[derive(Clone, Copy, Debug, Default)] 9 | #[repr(C, packed)] 10 | pub struct NvmeCmd { 11 | /// Opcode 12 | pub opcode: u8, 13 | /// Flags 14 | pub flags: u8, 15 | /// Command ID 16 | pub cid: u16, 17 | /// Namespace identifier 18 | pub nsid: u32, 19 | /// Reserved 20 | pub _rsvd: u64, 21 | /// Metadata pointer 22 | pub mptr: u64, 23 | /// Data pointer 24 | pub dptr: [u64; 2], 25 | /// Command dword 10 26 | pub cdw10: u32, 27 | /// Command dword 11 28 | pub cdw11: u32, 29 | /// Command dword 12 30 | pub cdw12: u32, 31 | /// Command dword 13 32 | pub cdw13: u32, 33 | /// Command dword 14 34 | pub cdw14: u32, 35 | /// Command dword 15 36 | pub cdw15: u32, 37 | } 38 | 39 | /// A completion queue entry. 40 | #[derive(Clone, Copy, Debug)] 41 | #[repr(C, packed)] 42 | pub struct NvmeComp { 43 | pub command_specific: u32, 44 | pub _rsvd: u32, 45 | pub sq_head: u16, 46 | pub sq_id: u16, 47 | pub cid: u16, 48 | pub status: u16, 49 | } 50 | 51 | /// Completion queue 52 | pub struct NvmeCompQueue { 53 | pub data: Dma<[UnsafeCell]>, 54 | pub head: u16, 55 | pub phase: bool, 56 | } 57 | 58 | impl NvmeCompQueue { 59 | pub fn new() -> Result { 60 | Ok(Self { 61 | data: unsafe { Dma::zeroed_slice(256)?.assume_init() }, 62 | head: 0, 63 | phase: true, 64 | }) 65 | } 66 | 67 | /// Get a new completion queue entry, or return None if no entry is available yet. 68 | pub(crate) fn complete(&mut self) -> Option<(u16, NvmeComp)> { 69 | let entry = unsafe { ptr::read_volatile(self.data[usize::from(self.head)].get()) }; 70 | 71 | if ((entry.status & 1) == 1) == self.phase { 72 | self.head = (self.head + 1) % (self.data.len() as u16); 73 | if self.head == 0 { 74 | self.phase = !self.phase; 75 | } 76 | Some((self.head, entry)) 77 | } else { 78 | None 79 | } 80 | } 81 | 82 | /// Get a new CQ entry, busy waiting until an entry appears. 83 | pub fn complete_spin(&mut self) -> (u16, NvmeComp) { 84 | log::debug!("Waiting for new CQ entry"); 85 | loop { 86 | if let Some(some) = self.complete() { 87 | return some; 88 | } else { 89 | unsafe { 90 | super::pause(); 91 | } 92 | } 93 | } 94 | } 95 | } 96 | 97 | /// Submission queue 98 | pub struct NvmeCmdQueue { 99 | pub data: Dma<[UnsafeCell]>, 100 | pub tail: u16, 101 | pub head: u16, 102 | } 103 | 104 | impl NvmeCmdQueue { 105 | pub fn new() -> Result { 106 | Ok(Self { 107 | data: unsafe { Dma::zeroed_slice(64)?.assume_init() }, 108 | tail: 0, 109 | head: 0, 110 | }) 111 | } 112 | 113 | pub fn is_empty(&self) -> bool { 114 | self.head == self.tail 115 | } 116 | pub fn is_full(&self) -> bool { 117 | self.head == self.tail + 1 118 | } 119 | 120 | /// Add a new submission command entry to the queue. The caller must ensure that the queue have free 121 | /// entries; this can be checked using `is_full`. 122 | pub fn submit_unchecked(&mut self, entry: NvmeCmd) -> u16 { 123 | unsafe { ptr::write_volatile(self.data[usize::from(self.tail)].get(), entry) } 124 | self.tail = (self.tail + 1) % (self.data.len() as u16); 125 | self.tail 126 | } 127 | } 128 | 129 | #[derive(Debug)] 130 | pub enum Status { 131 | GenericCmdStatus(u8), 132 | CommandSpecificStatus(u8), 133 | IntegrityError(u8), 134 | PathRelatedStatus(u8), 135 | Rsvd(u8), 136 | Vendor(u8), 137 | } 138 | impl Status { 139 | pub fn parse(raw: u16) -> Self { 140 | let code = (raw >> 1) as u8; 141 | match (raw >> 9) & 0b111 { 142 | 0 => Self::GenericCmdStatus(code), 143 | 1 => Self::CommandSpecificStatus(code), 144 | 2 => Self::IntegrityError(code), 145 | 3 => Self::PathRelatedStatus(code), 146 | 4..=6 => Self::Rsvd(code), 147 | 7 => Self::Vendor(code), 148 | _ => unreachable!(), 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /storage/partitionlib/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "partitionlib" 3 | version = "0.1.0" 4 | authors = ["Deepak Sirone "] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | [dependencies] 9 | gpt = { version = "3.0.1" } 10 | scroll = { version = "0.10", features = ["derive"] } 11 | uuid = { version = "1.0", features = ["v4"] } 12 | -------------------------------------------------------------------------------- /storage/partitionlib/resources/disk.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/drivers/b6d6ef27f0ae34fc5341a18c19a61fd7a85e36a2/storage/partitionlib/resources/disk.img -------------------------------------------------------------------------------- /storage/partitionlib/resources/disk_mbr.img: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/redox-os/drivers/b6d6ef27f0ae34fc5341a18c19a61fd7a85e36a2/storage/partitionlib/resources/disk_mbr.img -------------------------------------------------------------------------------- /storage/partitionlib/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod mbr; 2 | mod partition; 3 | pub use self::partition::*; 4 | -------------------------------------------------------------------------------- /storage/partitionlib/src/mbr.rs: -------------------------------------------------------------------------------- 1 | use scroll::{Pread, Pwrite}; 2 | use std::io::{self, Read, Seek}; 3 | 4 | #[derive(Clone, Copy, Debug, Pread, Pwrite)] 5 | pub(crate) struct Entry { 6 | pub(crate) drive_attrs: u8, 7 | pub(crate) start_head: u8, 8 | pub(crate) start_cs: u16, 9 | pub(crate) sys_id: u8, 10 | pub(crate) end_head: u8, 11 | pub(crate) end_cs: u16, 12 | pub(crate) rel_sector: u32, 13 | pub(crate) len: u32, 14 | } 15 | 16 | #[derive(Pread, Pwrite)] 17 | pub(crate) struct Header { 18 | pub(crate) bootstrap: [u8; 446], 19 | pub(crate) first_entry: Entry, 20 | pub(crate) second_entry: Entry, 21 | pub(crate) third_entry: Entry, 22 | pub(crate) fourth_entry: Entry, 23 | pub(crate) last_signature: u16, // 0xAA55 24 | } 25 | 26 | pub(crate) fn read_header(device: &mut D) -> io::Result> { 27 | device.seek(io::SeekFrom::Start(0))?; 28 | 29 | let mut bytes = [0u8; 512]; 30 | device.read_exact(&mut bytes)?; 31 | 32 | let header: Header = bytes.pread_with(0, scroll::LE).unwrap(); 33 | 34 | if header.last_signature != 0xAA55 { 35 | return Ok(None); 36 | } 37 | 38 | Ok(Some(header)) 39 | } 40 | 41 | impl Header { 42 | pub(crate) fn partitions(&self) -> impl Iterator { 43 | [ 44 | self.first_entry, 45 | self.second_entry, 46 | self.third_entry, 47 | self.fourth_entry, 48 | ] 49 | .into_iter() 50 | .filter(Entry::is_valid) 51 | } 52 | } 53 | impl Entry { 54 | fn is_valid(&self) -> bool { 55 | (self.drive_attrs == 0 || self.drive_attrs == 0x80) && self.len != 0 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /storage/partitionlib/src/partition.rs: -------------------------------------------------------------------------------- 1 | pub use gpt::disk::LogicalBlockSize; 2 | use std::io::{self, Read, Seek}; 3 | use uuid::Uuid; 4 | 5 | /// A union of the MBR and GPT partition entry 6 | #[derive(Clone, Debug, Eq, Hash, PartialEq)] 7 | pub struct Partition { 8 | /// The starting logical block number 9 | pub start_lba: u64, 10 | /// The size of the partition in sectors 11 | pub size: u64, 12 | pub flags: Option, 13 | pub name: Option, 14 | pub uuid: Option, 15 | } 16 | 17 | #[derive(Clone, Copy, Debug, Eq, PartialEq)] 18 | pub enum PartitionTableKind { 19 | Mbr, 20 | Gpt, 21 | } 22 | 23 | #[derive(Clone, Debug, Eq, PartialEq)] 24 | pub struct PartitionTable { 25 | pub partitions: Vec, 26 | pub kind: PartitionTableKind, 27 | } 28 | 29 | fn get_gpt_partitions( 30 | device: &mut D, 31 | sector_size: LogicalBlockSize, 32 | ) -> io::Result { 33 | let header = gpt::header::read_header_from_arbitrary_device(device, sector_size)?; 34 | Ok(PartitionTable { 35 | partitions: gpt::partition::file_read_partitions(device, &header, sector_size).map( 36 | |btree| { 37 | btree 38 | .into_iter() 39 | .map(|(_, part)| Partition { 40 | flags: Some(part.flags), 41 | size: part.last_lba - part.first_lba + 1, 42 | name: Some(part.name.clone()), 43 | uuid: Some(part.part_guid), 44 | start_lba: part.first_lba, 45 | }) 46 | .collect() 47 | }, 48 | )?, 49 | kind: PartitionTableKind::Gpt, 50 | }) 51 | } 52 | fn get_mbr_partitions(device: &mut D) -> io::Result> { 53 | let Some(header) = crate::mbr::read_header(device)? else { 54 | return Ok(None); 55 | }; 56 | Ok(Some(PartitionTable { 57 | kind: PartitionTableKind::Mbr, 58 | partitions: header 59 | .partitions() 60 | .map(|partition: crate::mbr::Entry| Partition { 61 | name: None, 62 | uuid: None, // TODO: Some kind of one-way conversion should be possible 63 | flags: None, // TODO 64 | size: partition.len.into(), 65 | start_lba: partition.rel_sector.into(), 66 | }) 67 | .collect(), 68 | })) 69 | } 70 | pub fn get_partitions( 71 | device: &mut D, 72 | sector_size: LogicalBlockSize, 73 | ) -> io::Result> { 74 | get_gpt_partitions(device, sector_size) 75 | .map(Some) 76 | .or_else(|_| get_mbr_partitions(device)) 77 | } 78 | 79 | impl Partition { 80 | pub fn to_offset(&self, sector_size: LogicalBlockSize) -> u64 { 81 | let blksize: u64 = sector_size.into(); 82 | self.start_lba * blksize 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /storage/partitionlib/tests/test.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | 3 | use partitionlib::{ 4 | get_partitions, LogicalBlockSize, Partition, PartitionTable, PartitionTableKind, 5 | }; 6 | 7 | fn get_partitions_from_file(path: &str) -> PartitionTable { 8 | let mut file = File::open(path).unwrap(); 9 | get_partitions(&mut file, LogicalBlockSize::Lb512) 10 | .unwrap() 11 | .unwrap() 12 | } 13 | 14 | // NOTE: The following tests rely on outside resource files being correct. 15 | #[test] 16 | fn gpt() { 17 | let table = get_partitions_from_file("./resources/disk.img"); 18 | assert_eq!(table.kind, PartitionTableKind::Gpt); 19 | assert_eq!( 20 | &table.partitions, 21 | &[Partition { 22 | flags: Some(0), 23 | name: Some("bug".to_owned()), 24 | uuid: Some(uuid::Uuid::parse_str("b665fba9-74d5-4069-a6b9-5ba3a164fdfe").unwrap()), // Microsoft basic data 25 | size: 957, 26 | start_lba: 34, 27 | }] 28 | ); 29 | } 30 | 31 | #[test] 32 | fn mbr() { 33 | let table = get_partitions_from_file("./resources/disk_mbr.img"); 34 | assert_eq!(table.kind, PartitionTableKind::Mbr); 35 | assert_eq!( 36 | &table.partitions, 37 | &[Partition { 38 | flags: None, 39 | name: None, 40 | uuid: None, 41 | size: 3, 42 | start_lba: 1, 43 | }] 44 | ); 45 | } 46 | -------------------------------------------------------------------------------- /storage/usbscsid/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /storage/usbscsid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "usbscsid" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2021" 6 | license = "MIT" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [dependencies] 11 | base64 = "0.11" # Only for debugging 12 | libredox = "0.1.3" 13 | plain = "0.2" 14 | driver-block = { path = "../driver-block" } 15 | redox-daemon = "0.1" 16 | redox_event = "0.4" 17 | redox_syscall = { version = "0.5", features = ["std"] } 18 | thiserror = "1" 19 | xhcid = { path = "../../xhcid" } 20 | -------------------------------------------------------------------------------- /storage/usbscsid/src/protocol/mod.rs: -------------------------------------------------------------------------------- 1 | use std::io; 2 | use std::num::NonZeroU32; 3 | 4 | use thiserror::Error; 5 | use xhcid_interface::{ 6 | ConfDesc, DevDesc, DeviceReqData, IfDesc, XhciClientHandle, XhciClientHandleError, 7 | }; 8 | 9 | #[derive(Debug, Error)] 10 | pub enum ProtocolError { 11 | #[error("Too large command block ({0} > 16)")] 12 | TooLargeCommandBlock(usize), 13 | 14 | #[error("xhcid connection error: {0}")] 15 | XhciError(#[from] XhciClientHandleError), 16 | 17 | #[error("i/o error")] 18 | IoError(#[from] io::Error), 19 | 20 | #[error("attempted recovery failed")] 21 | RecoveryFailed, 22 | 23 | #[error("protocol error")] 24 | ProtocolError(&'static str), 25 | } 26 | 27 | #[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq)] 28 | pub struct SendCommandStatus { 29 | pub residue: Option, 30 | pub kind: SendCommandStatusKind, 31 | } 32 | 33 | impl SendCommandStatus { 34 | pub fn bytes_transferred(&self, transfer_len: u32) -> u32 { 35 | transfer_len - self.residue.map(u32::from).unwrap_or(0) 36 | } 37 | } 38 | 39 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 40 | pub enum SendCommandStatusKind { 41 | Success, 42 | Failed, 43 | } 44 | 45 | impl Default for SendCommandStatusKind { 46 | fn default() -> Self { 47 | Self::Success 48 | } 49 | } 50 | 51 | pub trait Protocol { 52 | fn send_command( 53 | &mut self, 54 | command: &[u8], 55 | data: DeviceReqData, 56 | ) -> Result; 57 | } 58 | 59 | /// Bulk-only transport 60 | pub mod bot; 61 | 62 | mod uas { 63 | // TODO 64 | } 65 | 66 | use bot::BulkOnlyTransport; 67 | 68 | pub fn setup<'a>( 69 | handle: &'a XhciClientHandle, 70 | protocol: u8, 71 | dev_desc: &DevDesc, 72 | conf_desc: &ConfDesc, 73 | if_desc: &IfDesc, 74 | ) -> Option> { 75 | match protocol { 76 | 0x50 => Some(Box::new( 77 | BulkOnlyTransport::init(handle, conf_desc, if_desc).unwrap(), 78 | )), 79 | _ => None, 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /storage/usbscsid/src/scsi/opcodes.rs: -------------------------------------------------------------------------------- 1 | #[repr(u8)] 2 | pub enum Opcode { 3 | TestUnitReady = 0x00, 4 | /// obsolete 5 | RezeroUnit = 0x01, 6 | RequestSense = 0x03, 7 | FormatUnit = 0x04, 8 | ReassignBlocks = 0x07, 9 | /// obsolete 10 | Read6 = 0x08, 11 | /// obsolete 12 | Write6 = 0x0A, 13 | /// obsolete 14 | Seek = 0x0B, 15 | Inquiry = 0x12, 16 | ModeSelect6 = 0x15, 17 | /// obsolete 18 | Reserve6 = 0x16, 19 | /// obsolete 20 | Release6 = 0x17, 21 | ModeSense6 = 0x1A, 22 | StartStopUnit = 0x1B, 23 | RecvDiagnosticRes = 0x1C, 24 | SendDiagnostic = 0x1D, 25 | ReadCapacity10 = 0x25, 26 | Read10 = 0x28, 27 | Write10 = 0x2A, 28 | /// obsolete 29 | SeekExt = 0x2B, 30 | WriteAndVerify10 = 0x2E, 31 | Verify10 = 0x2F, 32 | SyncCache10 = 0x35, 33 | ReadDefectData10 = 0x37, 34 | WriteBuf10 = 0x3B, 35 | ReadBuf10 = 0x3C, 36 | /// obsolete 37 | ReadLong10 = 0x3E, 38 | WriteLong10 = 0x3F, 39 | /// obsolete 40 | ChangeDef = 0x40, 41 | WriteSame10 = 0x41, 42 | Unmap = 0x42, 43 | Sanitize = 0x48, 44 | LogSelect = 0x4C, 45 | LogSense = 0x4D, 46 | ModeSelect10 = 0x55, 47 | /// obsolete 48 | Reserve10 = 0x56, 49 | /// obsolete 50 | Release10 = 0x57, 51 | ModeSense10 = 0x5A, 52 | PersistentResvIn = 0x5E, 53 | PersistentResvOut = 0x5F, 54 | ServiceAction7F = 0x7F, 55 | Read16 = 0x88, 56 | Write16 = 0x8A, 57 | WriteAndVerify16 = 0x8E, 58 | Verify16 = 0x8F, 59 | SyncCache16 = 0x91, 60 | WriteSame16 = 0x93, 61 | WriteStream16 = 0x9A, 62 | ReadBuf16 = 0x9B, 63 | WriteAtomic16 = 0x9C, 64 | ServiceAction9E = 0x9E, 65 | ServiceAction9F, 66 | ReportLuns = 0xA0, 67 | SecurityProtoIn = 0xA2, 68 | ServiceActionA3 = 0xA3, 69 | ServiceActionA4 = 0xA4, 70 | Read12 = 0xA8, 71 | Write12 = 0xAA, 72 | WriteAndVerify12 = 0xAE, 73 | Verify12 = 0xAF, 74 | SecurityProtoOut = 0xB5, 75 | ReadDefectData12 = 0xB7, 76 | } 77 | 78 | #[repr(u8)] 79 | pub enum ServiceAction7F { 80 | Read32 = 0x09, 81 | Verify32 = 0x0A, 82 | Write32 = 0x0B, 83 | WriteAndVerify32 = 0x0C, 84 | WriteSame32 = 0x0D, 85 | WriteAtomic32 = 0x18, 86 | } 87 | 88 | #[repr(u8)] 89 | pub enum ServiceAction9E { 90 | ReadCapacity16 = 0x10, 91 | ReadLong16 = 0x11, 92 | GetLbaStatus = 0x12, 93 | StreamControl = 0x14, 94 | BackgroundControl = 0x15, 95 | GetStreamStatus = 0x16, 96 | } 97 | #[repr(u8)] 98 | pub enum ServiceAction9F { 99 | WriteLong16 = 0x11, 100 | } 101 | #[repr(u8)] 102 | pub enum ServiceActionA3 { 103 | ReportIdentInfo = 0x05, 104 | ReportSuppOpcodes = 0x0C, 105 | ReportSuppTaskManFuncs = 0x0D, 106 | ReportTimestamp = 0x0F, 107 | } 108 | #[repr(u8)] 109 | pub enum ServiceActionA4 { 110 | SetIdentInfo = 0x06, 111 | SetTimestamp = 0x0F, 112 | } 113 | -------------------------------------------------------------------------------- /storage/virtio-blkd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio-blkd" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Anhad Singh "] 6 | 7 | [dependencies] 8 | anyhow = "1.0.71" 9 | log = "0.4" 10 | thiserror = "1.0.40" 11 | static_assertions = "1.1.0" 12 | futures = { version = "0.3.28", features = ["executor"] } 13 | spin = "*" 14 | 15 | redox-daemon = "0.1" 16 | redox_event = "0.4" 17 | redox_syscall = { version = "0.5", features = ["std"] } 18 | 19 | common = { path = "../../common" } 20 | driver-block = { path = "../driver-block" } 21 | pcid = { path = "../../pcid" } 22 | virtio-core = { path = "../../virtio-core" } 23 | libredox = "0.1.3" 24 | -------------------------------------------------------------------------------- /storage/virtio-blkd/src/scheme.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use common::dma::Dma; 4 | use virtio_core::spec::{Buffer, ChainBuilder, DescriptorFlags}; 5 | use virtio_core::transport::Queue; 6 | 7 | use crate::BlockDeviceConfig; 8 | use crate::BlockRequestTy; 9 | use crate::BlockVirtRequest; 10 | 11 | trait BlkExtension { 12 | async fn read(&self, block: u64, target: &mut [u8]) -> usize; 13 | async fn write(&self, block: u64, target: &[u8]) -> usize; 14 | } 15 | 16 | impl BlkExtension for Queue<'_> { 17 | async fn read(&self, block: u64, target: &mut [u8]) -> usize { 18 | let req = Dma::new(BlockVirtRequest { 19 | ty: BlockRequestTy::In, 20 | reserved: 0, 21 | sector: block, 22 | }) 23 | .unwrap(); 24 | 25 | let result = unsafe { 26 | Dma::<[u8]>::zeroed_slice(target.len()) 27 | .unwrap() 28 | .assume_init() 29 | }; 30 | let status = Dma::new(u8::MAX).unwrap(); 31 | 32 | let chain = ChainBuilder::new() 33 | .chain(Buffer::new(&req)) 34 | .chain(Buffer::new_unsized(&result).flags(DescriptorFlags::WRITE_ONLY)) 35 | .chain(Buffer::new(&status).flags(DescriptorFlags::WRITE_ONLY)) 36 | .build(); 37 | 38 | // XXX: Subtract 1 because the of status byte. 39 | let written = self.send(chain).await as usize - 1; 40 | assert_eq!(*status, 0); 41 | 42 | target[..written].copy_from_slice(&result); 43 | written 44 | } 45 | 46 | async fn write(&self, block: u64, target: &[u8]) -> usize { 47 | let req = Dma::new(BlockVirtRequest { 48 | ty: BlockRequestTy::Out, 49 | reserved: 0, 50 | sector: block, 51 | }) 52 | .unwrap(); 53 | 54 | let mut result = unsafe { 55 | Dma::<[u8]>::zeroed_slice(target.len()) 56 | .unwrap() 57 | .assume_init() 58 | }; 59 | result.copy_from_slice(target.as_ref()); 60 | 61 | let status = Dma::new(u8::MAX).unwrap(); 62 | 63 | let chain = ChainBuilder::new() 64 | .chain(Buffer::new(&req)) 65 | .chain(Buffer::new_sized(&result, target.len())) 66 | .chain(Buffer::new(&status).flags(DescriptorFlags::WRITE_ONLY)) 67 | .build(); 68 | 69 | self.send(chain).await as usize; 70 | assert_eq!(*status, 0); 71 | 72 | target.len() 73 | } 74 | } 75 | 76 | pub(crate) struct VirtioDisk<'a> { 77 | queue: Arc>, 78 | cfg: BlockDeviceConfig, 79 | } 80 | 81 | impl<'a> VirtioDisk<'a> { 82 | pub(crate) fn new(queue: Arc>, cfg: BlockDeviceConfig) -> Self { 83 | Self { queue, cfg } 84 | } 85 | } 86 | 87 | impl driver_block::Disk for VirtioDisk<'_> { 88 | fn block_size(&self) -> u32 { 89 | self.cfg.block_size() 90 | } 91 | 92 | fn size(&self) -> u64 { 93 | self.cfg.capacity() * u64::from(self.cfg.block_size()) 94 | } 95 | 96 | async fn read(&mut self, block: u64, buffer: &mut [u8]) -> syscall::Result { 97 | Ok(self.queue.read(block, buffer).await) 98 | } 99 | 100 | async fn write(&mut self, block: u64, buffer: &[u8]) -> syscall::Result { 101 | Ok(self.queue.write(block, buffer).await) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /usbctl/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /usbctl/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "usbctl" 3 | version = "0.1.0" 4 | authors = ["4lDO2 <4lDO2@protonmail.com>"] 5 | edition = "2018" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | clap = "2.33" 11 | xhcid = { path = "../xhcid" } 12 | -------------------------------------------------------------------------------- /usbctl/src/main.rs: -------------------------------------------------------------------------------- 1 | use clap::{App, Arg}; 2 | use xhcid_interface::{PortId, XhciClientHandle}; 3 | 4 | fn main() { 5 | let matches = App::new("usbctl") 6 | .arg( 7 | Arg::with_name("SCHEME") 8 | .takes_value(true) 9 | .required(true) 10 | .long("scheme") 11 | .short("s"), 12 | ) 13 | .subcommand( 14 | App::new("port") 15 | .arg(Arg::with_name("PORT").takes_value(true).required(true)) 16 | .subcommand(App::new("status")) 17 | .subcommand( 18 | App::new("endpoint") 19 | .arg( 20 | Arg::with_name("ENDPOINT_NUM") 21 | .takes_value(true) 22 | .required(true), 23 | ) 24 | .subcommand(App::new("status")), 25 | ), 26 | ) 27 | .get_matches(); 28 | 29 | let scheme = matches.value_of("SCHEME").expect("no scheme"); 30 | 31 | if let Some(port_scmd_matches) = matches.subcommand_matches("port") { 32 | let port = port_scmd_matches 33 | .value_of("PORT") 34 | .expect("invalid utf-8 for PORT argument") 35 | .parse::() 36 | .expect("expected PORT ID"); 37 | 38 | let handle = XhciClientHandle::new(scheme.to_owned(), port); 39 | 40 | if let Some(_status_scmd_matches) = port_scmd_matches.subcommand_matches("status") { 41 | let state = handle.port_state().expect("Failed to get port state"); 42 | println!("{}", state.as_str()); 43 | } else if let Some(endp_scmd_matches) = port_scmd_matches.subcommand_matches("endpoint") { 44 | let endp_num = endp_scmd_matches 45 | .value_of("ENDPOINT_NUM") 46 | .expect("no valid ENDPOINT_NUM") 47 | .parse::() 48 | .expect("expected ENDPOINT_NUM to be an 8-bit integer"); 49 | let mut endp_handle = handle 50 | .open_endpoint(endp_num) 51 | .expect("Failed to open endpoint"); 52 | let state = endp_handle.status().expect("Failed to get endpoint state"); 53 | println!("{}", state.as_str()); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /usbhubd/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /usbhubd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "usbhubd" 3 | version = "0.1.0" 4 | edition = "2018" 5 | license = "MIT" 6 | 7 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 8 | 9 | [dependencies] 10 | log = "0.4" 11 | redox_syscall = "0.5" 12 | xhcid = { path = "../xhcid" } 13 | 14 | common = { path = "../common" } 15 | -------------------------------------------------------------------------------- /vboxd/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "vboxd" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [dependencies] 7 | libredox = "0.1.3" 8 | orbclient = "0.3.47" 9 | redox_event = "0.4.1" 10 | redox_syscall = "0.5" 11 | redox-daemon = "0.1" 12 | 13 | common = { path = "../common" } 14 | pcid = { path = "../pcid" } 15 | -------------------------------------------------------------------------------- /vboxd/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "VirtualBox Guest Device" 3 | class = 0x08 4 | vendor = 0x80EE 5 | device = 0xCAFE 6 | command = ["vboxd"] 7 | -------------------------------------------------------------------------------- /vboxd/src/bga.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Pio}; 2 | 3 | const BGA_INDEX_XRES: u16 = 1; 4 | const BGA_INDEX_YRES: u16 = 2; 5 | const BGA_INDEX_BPP: u16 = 3; 6 | const BGA_INDEX_ENABLE: u16 = 4; 7 | 8 | pub struct Bga { 9 | index: Pio, 10 | data: Pio, 11 | } 12 | 13 | impl Bga { 14 | pub fn new() -> Bga { 15 | Bga { 16 | index: Pio::new(0x1CE), 17 | data: Pio::new(0x1CF), 18 | } 19 | } 20 | 21 | fn read(&mut self, index: u16) -> u16 { 22 | self.index.write(index); 23 | self.data.read() 24 | } 25 | 26 | fn write(&mut self, index: u16, data: u16) { 27 | self.index.write(index); 28 | self.data.write(data); 29 | } 30 | 31 | pub fn width(&mut self) -> u16 { 32 | self.read(BGA_INDEX_XRES) 33 | } 34 | 35 | pub fn height(&mut self) -> u16 { 36 | self.read(BGA_INDEX_YRES) 37 | } 38 | 39 | pub fn set_size(&mut self, width: u16, height: u16) { 40 | self.write(BGA_INDEX_ENABLE, 0); 41 | self.write(BGA_INDEX_XRES, width); 42 | self.write(BGA_INDEX_YRES, height); 43 | self.write(BGA_INDEX_BPP, 32); 44 | self.write(BGA_INDEX_ENABLE, 0x41); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /virtio-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "virtio-core" 3 | version = "0.1.0" 4 | edition = "2021" 5 | authors = ["Anhad Singh "] 6 | 7 | [dependencies] 8 | static_assertions = "1.1.0" 9 | bitflags = "2.3.2" 10 | redox_syscall = "0.5" 11 | libredox = "0.1.3" 12 | log = "0.4" 13 | thiserror = "1.0.40" 14 | futures = { version = "0.3.28", features = ["executor"] } 15 | crossbeam-queue = "0.3.8" 16 | 17 | redox_event = "0.4.1" 18 | 19 | common = { path = "../common" } 20 | pcid = { path = "../pcid" } 21 | -------------------------------------------------------------------------------- /virtio-core/src/arch/aarch64.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | 3 | use pcid_interface::*; 4 | 5 | use crate::{transport::Error, Device}; 6 | 7 | pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { 8 | unimplemented!("virtio_core: aarch64 enable_msix") 9 | } 10 | -------------------------------------------------------------------------------- /virtio-core/src/arch/riscv64.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | 3 | use pcid_interface::*; 4 | 5 | use crate::{transport::Error, Device}; 6 | 7 | pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { 8 | unimplemented!("virtio_core: enable_msix") 9 | } 10 | -------------------------------------------------------------------------------- /virtio-core/src/arch/x86.rs: -------------------------------------------------------------------------------- 1 | use crate::transport::Error; 2 | 3 | use pcid_interface::irq_helpers::{allocate_single_interrupt_vector_for_msi, read_bsp_apic_id}; 4 | use pcid_interface::msi::MsixTableEntry; 5 | use std::{fs::File, ptr::NonNull}; 6 | 7 | use crate::{probe::MappedMsixRegs, MSIX_PRIMARY_VECTOR}; 8 | 9 | use pcid_interface::*; 10 | 11 | pub fn enable_msix(pcid_handle: &mut PciFunctionHandle) -> Result { 12 | let pci_config = pcid_handle.config(); 13 | 14 | // Extended message signaled interrupts. 15 | let msix_info = match pcid_handle.feature_info(PciFeature::MsiX) { 16 | PciFeatureInfo::MsiX(capability) => capability, 17 | _ => unreachable!(), 18 | }; 19 | msix_info.validate(pci_config.func.bars); 20 | 21 | let bar_address = unsafe { pcid_handle.map_bar(msix_info.table_bar) } 22 | .ptr 23 | .as_ptr() as usize; 24 | let virt_table_base = (bar_address + msix_info.table_offset as usize) as *mut MsixTableEntry; 25 | 26 | let mut info = MappedMsixRegs { 27 | virt_table_base: NonNull::new(virt_table_base).unwrap(), 28 | info: msix_info, 29 | }; 30 | 31 | // Allocate the primary MSI vector. 32 | // FIXME allow the driver to register multiple MSI-X vectors 33 | // FIXME move this MSI-X registering code into pcid_interface or pcid itself 34 | let interrupt_handle = { 35 | let table_entry_pointer = info.table_entry_pointer(MSIX_PRIMARY_VECTOR as usize); 36 | 37 | let destination_id = read_bsp_apic_id().expect("virtio_core: `read_bsp_apic_id()` failed"); 38 | let (msg_addr_and_data, interrupt_handle) = 39 | allocate_single_interrupt_vector_for_msi(destination_id); 40 | table_entry_pointer.write_addr_and_data(msg_addr_and_data); 41 | table_entry_pointer.unmask(); 42 | 43 | interrupt_handle 44 | }; 45 | 46 | pcid_handle.enable_feature(PciFeature::MsiX); 47 | 48 | log::info!("virtio: using MSI-X (interrupt_handle={interrupt_handle:?})"); 49 | Ok(interrupt_handle) 50 | } 51 | -------------------------------------------------------------------------------- /virtio-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod spec; 2 | pub mod transport; 3 | pub mod utils; 4 | 5 | mod probe; 6 | 7 | #[cfg(target_arch = "aarch64")] 8 | #[path = "arch/aarch64.rs"] 9 | mod arch; 10 | 11 | #[cfg(any(target_arch = "x86", target_arch = "x86_64"))] 12 | #[path = "arch/x86.rs"] 13 | mod arch; 14 | 15 | #[cfg(target_arch = "riscv64")] 16 | #[path = "arch/riscv64.rs"] 17 | mod arch; 18 | 19 | pub use probe::{probe_device, reinit, Device, MSIX_PRIMARY_VECTOR}; 20 | -------------------------------------------------------------------------------- /virtio-core/src/spec/mod.rs: -------------------------------------------------------------------------------- 1 | //! https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html 2 | //! 3 | //! This file contains comments copied from the VirtIO specification which are 4 | //! licensed under the following conditions: 5 | //! 6 | //! Copyright © OASIS Open 2022. All Rights Reserved. 7 | //! 8 | //! All capitalized terms in the following text have the meanings assigned to them 9 | //! in the OASIS Intellectual Property Rights Policy (the "OASIS IPR Policy"). The 10 | //! full Policy may be found at the OASIS website. 11 | //! 12 | //! This document and translations of it may be copied and furnished to others, 13 | //! and derivative works that comment on or otherwise explain it or assist in its 14 | //! implementation may be prepared, copied, published, and distributed, in whole 15 | //! or in part, without restriction of any kind, provided that the above copyright 16 | //! notice and this section are included on all such copies and derivative works. 17 | //! However, this document itself may not be modified in any way, including by 18 | //! removing the copyright notice or references to OASIS, except as needed for the 19 | //! purpose of developing any document or deliverable produced by an OASIS Technical 20 | //! Committee (in which case the rules applicable to copyrights, as set forth in the 21 | //! OASIS IPR Policy, must be followed) or as required to translate it into languages 22 | //! other than English. 23 | 24 | bitflags::bitflags! { 25 | /// [2.1 Device Status Field](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-110001) 26 | #[derive(Debug, Copy, Clone, PartialEq)] 27 | #[repr(transparent)] 28 | pub struct DeviceStatusFlags: u8 { 29 | /// Indicates that the guest OS has found the device and recognized it as a 30 | /// valid device. 31 | const ACKNOWLEDGE = 1; 32 | /// Indicates that the guest OS knows how to drive the device. 33 | const DRIVER = 2; 34 | /// Indicates that something went wrong in the guest and it has given up on 35 | /// the device. 36 | const FAILED = 128; 37 | /// Indicates that the driver has acknowledged all the features it understands 38 | /// and feature negotiation is complete. 39 | const FEATURES_OK = 8; 40 | /// Indicates that the driver is set up and ready to drive the device. 41 | const DRIVER_OK = 4; 42 | /// Indicates that the device has experienced an error from which it can’t recover. 43 | const DEVICE_NEEDS_RESET = 64; 44 | } 45 | } 46 | 47 | mod split_virtqueue; 48 | pub use split_virtqueue::*; 49 | 50 | // FIXME add [2.8 Packed Virtqueues](https://docs.oasis-open.org/virtio/virtio/v1.2/cs01/virtio-v1.2-cs01.html#x1-720008) 51 | 52 | mod transport_pci; 53 | pub use transport_pci::*; 54 | 55 | mod reserved_features; 56 | pub use reserved_features::*; 57 | -------------------------------------------------------------------------------- /virtio-core/src/utils.rs: -------------------------------------------------------------------------------- 1 | use core::cell::UnsafeCell; 2 | use core::fmt::Debug; 3 | use core::marker::PhantomData; 4 | 5 | #[repr(C)] 6 | pub struct VolatileCell { 7 | value: UnsafeCell, 8 | } 9 | 10 | impl VolatileCell { 11 | #[inline] 12 | pub const fn new(value: T) -> Self { 13 | Self { 14 | value: UnsafeCell::new(value), 15 | } 16 | } 17 | 18 | /// Returns a copy of the contained value. 19 | #[inline] 20 | pub fn get(&self) -> T { 21 | unsafe { core::ptr::read_volatile(self.value.get()) } 22 | } 23 | 24 | /// Sets the contained value. 25 | #[inline] 26 | pub fn set(&mut self, value: T) { 27 | unsafe { core::ptr::write_volatile(self.value.get(), value) } 28 | } 29 | } 30 | 31 | impl Debug for VolatileCell 32 | where 33 | T: Debug + Copy, 34 | { 35 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 36 | f.debug_struct("VolatileCell") 37 | .field("value", &self.get()) 38 | .finish() 39 | } 40 | } 41 | 42 | unsafe impl Sync for VolatileCell {} 43 | 44 | #[repr(C)] 45 | pub struct IncompleteArrayField(PhantomData, [T; 0]); 46 | 47 | impl IncompleteArrayField { 48 | #[inline] 49 | pub const fn new() -> Self { 50 | IncompleteArrayField(PhantomData, []) 51 | } 52 | 53 | #[inline] 54 | pub unsafe fn as_slice(&self, len: usize) -> &[T] { 55 | core::slice::from_raw_parts(self.as_ptr(), len) 56 | } 57 | 58 | #[inline] 59 | pub unsafe fn as_mut_slice(&mut self, len: usize) -> &mut [T] { 60 | core::slice::from_raw_parts_mut(self.as_mut_ptr(), len) 61 | } 62 | 63 | #[inline] 64 | pub unsafe fn as_ptr(&self) -> *const T { 65 | self as *const _ as *const T 66 | } 67 | 68 | #[inline] 69 | pub unsafe fn as_mut_ptr(&mut self) -> *mut T { 70 | self as *mut _ as *mut T 71 | } 72 | } 73 | 74 | pub const fn align(val: usize, align: usize) -> usize { 75 | (val + align) & !align 76 | } 77 | 78 | pub const fn align_down(addr: usize) -> usize { 79 | addr & !(syscall::PAGE_SIZE - 1) 80 | } 81 | -------------------------------------------------------------------------------- /xhcid/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /xhcid/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xhcid" 3 | version = "0.1.0" 4 | edition = "2018" 5 | 6 | [[bin]] 7 | name = "xhcid" 8 | path = "src/main.rs" 9 | 10 | [lib] 11 | name = "xhcid_interface" 12 | path = "src/lib.rs" 13 | 14 | [dependencies] 15 | bitflags = "1" 16 | chashmap = "2.2.2" 17 | crossbeam-channel = "0.4" 18 | futures = "0.3" 19 | plain = "0.2" 20 | lazy_static = "1.4" 21 | log = "0.4" 22 | redox-daemon = "0.1" 23 | redox_event = "0.4.1" 24 | redox-scheme = "0.4" 25 | redox_syscall = "0.5" 26 | serde = { version = "1", features = ["derive"] } 27 | serde_json = "1" 28 | smallvec = { version = "1", features = ["serde"] } 29 | thiserror = "1" 30 | toml = "0.5" 31 | 32 | common = { path = "../common" } 33 | pcid = { path = "../pcid" } 34 | libredox = "0.1.3" 35 | regex = "1.10.6" 36 | -------------------------------------------------------------------------------- /xhcid/config.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "XHCI" 3 | class = 0x0C 4 | subclass = 0x03 5 | interface = 0x30 6 | command = ["xhcid"] 7 | use_channel = true 8 | -------------------------------------------------------------------------------- /xhcid/drivers.toml: -------------------------------------------------------------------------------- 1 | [[drivers]] 2 | name = "SCSI over USB" 3 | class = 8 # Mass Storage class 4 | subclass = 6 # SCSI transparent command set 5 | command = ["usbscsid", "$SCHEME", "$PORT", "$IF_PROTO"] 6 | 7 | [[drivers]] 8 | name = "USB HUB" 9 | class = 9 # HUB class 10 | subclass = -1 11 | command = ["usbhubd", "$SCHEME", "$PORT", "$IF_NUM"] 12 | 13 | [[drivers]] 14 | name = "USB HID" 15 | class = 3 # HID class 16 | subclass = -1 17 | command = ["usbhidd", "$SCHEME", "$PORT", "$IF_NUM"] 18 | -------------------------------------------------------------------------------- /xhcid/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! The eXtensible Host Controller Interface (XHCI) Daemon Interface 2 | //! 3 | //! This crate implements the driver interface for interacting with the Redox xhcid daemon from 4 | //! another userspace process. 5 | //! 6 | //! XHCI is a standard for the USB Host Controller interface specified by Intel that provides a 7 | //! common register interface for systems to use to interact with the Universal Serial Bus (USB) 8 | //! subsystem. 9 | //! 10 | //! USB consists of three types of devices: The Host Controller/Root Hub, USB Hubs, and Endpoints. 11 | //! Endpoints represent actual devices connected to the USB fabric. USB Hubs are intermediaries 12 | //! between the Host Controller and the endpoints that report when devices have been connected/disconnected. 13 | //! The Host Controller provides the interface to the USB subsystem that software running on the 14 | //! system's CPU can interact with. It's a tree-like structure, which the Host Controller enumerating 15 | //! and addressing all the hubs and endpoints in the tree. Data then flows through the fabric 16 | //! using the USB protocol (2.0 or 3.2) as packets. Hubs have multiple ports that endpoints can 17 | //! connect to, and they notify the Host Controller/Root Hub when devices are hot plugged or removed. 18 | //! 19 | //! This documentation will refer directly to the relevant standards, which are as follows: 20 | //! 21 | //! - XHCI - [eXtensible Host Controller Interface for Universal Serial Bus (xHCI) Requirements Specification](https://www.intel.com/content/dam/www/public/us/en/documents/technical-specifications/extensible-host-controler-interface-usb-xhci.pdf) 22 | //! - USB2 - [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification) 23 | //! - USB32 - [Universal Serial Bus 3.2 Specification Revision 1.1](https://usb.org/document-library/usb-32-revision-11-june-2022) 24 | //! 25 | pub extern crate plain; 26 | 27 | mod driver_interface; 28 | pub mod usb; 29 | 30 | pub use driver_interface::*; 31 | -------------------------------------------------------------------------------- /xhcid/src/usb/config.rs: -------------------------------------------------------------------------------- 1 | #[repr(C, packed)] 2 | #[derive(Clone, Copy, Debug, Default)] 3 | pub struct ConfigDescriptor { 4 | pub length: u8, 5 | pub kind: u8, 6 | pub total_length: u16, 7 | pub interfaces: u8, 8 | pub configuration_value: u8, 9 | pub configuration_str: u8, 10 | pub attributes: u8, 11 | pub max_power: u8, 12 | } 13 | 14 | unsafe impl plain::Plain for ConfigDescriptor {} 15 | 16 | #[repr(C, packed)] 17 | #[derive(Clone, Copy, Debug, Default)] 18 | pub struct OtherSpeedConfig { 19 | pub length: u8, 20 | pub kind: u8, 21 | pub total_length: u16, 22 | pub interfaces: u8, 23 | pub configuration_value: u8, 24 | pub configuration_str: u8, 25 | pub attributes: u8, 26 | pub max_power: u8, 27 | } 28 | -------------------------------------------------------------------------------- /xhcid/src/usb/endpoint.rs: -------------------------------------------------------------------------------- 1 | use plain::Plain; 2 | 3 | /// The descriptor for a USB Endpoint. 4 | /// 5 | /// Each endpoint for a particular interface has its own descriptor. The information in this 6 | /// structure is used by the host to determine the bandwidth requirements of the endpoint. 7 | /// 8 | /// This is returned automatically when you send a request for a ConfigurationDescriptor, 9 | /// and cannot be requested individually. 10 | /// 11 | /// See USB32 9.6.6 12 | /// 13 | /// The offsets for the fields in the packet are described in USB32 Table 9-26 14 | #[repr(C, packed)] 15 | #[derive(Clone, Copy, Debug, Default)] 16 | pub struct EndpointDescriptor { 17 | pub length: u8, 18 | pub kind: u8, 19 | pub address: u8, 20 | pub attributes: u8, 21 | pub max_packet_size: u16, 22 | pub interval: u8, 23 | } 24 | 25 | /// Mask that is ANDed to the [EndpointDescriptor].attributes buffer to get the endpoint type. 26 | pub const ENDP_ATTR_TY_MASK: u8 = 0x3; 27 | 28 | #[repr(u8)] 29 | #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)] 30 | pub enum EndpointTy { 31 | Ctrl = 0, 32 | Isoch = 1, 33 | Bulk = 2, 34 | Interrupt = 3, 35 | } 36 | 37 | impl EndpointDescriptor { 38 | fn ty(self) -> EndpointTy { 39 | match self.attributes & ENDP_ATTR_TY_MASK { 40 | 0 => EndpointTy::Ctrl, 41 | 1 => EndpointTy::Isoch, 42 | 2 => EndpointTy::Bulk, 43 | 3 => EndpointTy::Interrupt, 44 | _ => unreachable!(), 45 | } 46 | } 47 | } 48 | 49 | unsafe impl Plain for EndpointDescriptor {} 50 | 51 | #[repr(C, packed)] 52 | #[derive(Clone, Copy, Debug, Default)] 53 | pub struct SuperSpeedCompanionDescriptor { 54 | pub length: u8, 55 | pub kind: u8, 56 | pub max_burst: u8, 57 | pub attributes: u8, 58 | pub bytes_per_interval: u16, 59 | } 60 | unsafe impl Plain for SuperSpeedCompanionDescriptor {} 61 | 62 | #[repr(C, packed)] 63 | #[derive(Clone, Copy, Debug, Default)] 64 | pub struct SuperSpeedPlusIsochCmpDescriptor { 65 | pub length: u8, 66 | pub kind: u8, 67 | pub reserved: u16, 68 | pub bytes_per_interval: u32, 69 | } 70 | unsafe impl Plain for SuperSpeedPlusIsochCmpDescriptor {} 71 | 72 | #[repr(C, packed)] 73 | #[derive(Clone, Copy, Debug, Default)] 74 | pub struct HidDescriptor { 75 | pub length: u8, 76 | pub kind: u8, 77 | pub hid_spec_release: u16, 78 | pub country_code: u8, 79 | pub num_descriptors: u8, 80 | pub report_desc_ty: u8, 81 | pub report_desc_len: u16, 82 | pub optional_desc_ty: u8, 83 | pub optional_desc_len: u16, 84 | } 85 | 86 | unsafe impl Plain for HidDescriptor {} 87 | -------------------------------------------------------------------------------- /xhcid/src/usb/interface.rs: -------------------------------------------------------------------------------- 1 | use plain::Plain; 2 | 3 | /// 4 | #[repr(C, packed)] 5 | #[derive(Clone, Copy, Debug, Default)] 6 | pub struct InterfaceDescriptor { 7 | pub length: u8, 8 | pub kind: u8, 9 | pub number: u8, 10 | pub alternate_setting: u8, 11 | pub endpoints: u8, 12 | pub class: u8, 13 | pub sub_class: u8, 14 | pub protocol: u8, 15 | pub interface_str: u8, 16 | } 17 | 18 | unsafe impl Plain for InterfaceDescriptor {} 19 | -------------------------------------------------------------------------------- /xhcid/src/usb/mod.rs: -------------------------------------------------------------------------------- 1 | //! The Universal Serial Bus (USB) Module 2 | //! 3 | //! The implementations in this module are common to all USB interfaces (though individual elements 4 | //! may be specific to only 2.0 or 3.2), and are used by specialized driver components like [xhci] 5 | //! to implement the driver interface. 6 | //! 7 | //! The [Universal Serial Bus Specification](https://www.usb.org/document-library/usb-20-specification) and the [Universal Serial Bus 3.2 Specification](https://usb.org/document-library/usb-32-revision-11-june-2022) are 8 | //! the documents that inform this implementation. 9 | //! 10 | //! See the crate-level documentation for the acronyms used to refer to specific documents. 11 | pub use self::bos::{bos_capability_descs, BosAnyDevDesc, BosDescriptor, BosSuperSpeedDesc}; 12 | pub use self::config::ConfigDescriptor; 13 | pub use self::device::{DeviceDescriptor, DeviceDescriptor8Byte}; 14 | pub use self::endpoint::{ 15 | EndpointDescriptor, EndpointTy, HidDescriptor, SuperSpeedCompanionDescriptor, 16 | SuperSpeedPlusIsochCmpDescriptor, ENDP_ATTR_TY_MASK, 17 | }; 18 | pub use self::hub::*; 19 | pub use self::interface::InterfaceDescriptor; 20 | pub use self::setup::{Setup, SetupReq}; 21 | 22 | /// Enumerates the list of descriptor kinds that can be reported by a USB device to report its 23 | /// attributes to the system. (See USB32 Sections 9.5 and 9.6) 24 | #[derive(Clone, Copy, Debug)] 25 | #[repr(u8)] 26 | pub enum DescriptorKind { 27 | /// No Descriptor TODO: Determine why this state exists, and what it does in the code. 28 | None = 0, 29 | /// A Device Descriptor. See [DeviceDescriptor] 30 | Device = 1, 31 | /// A Configuration Descriptor. See [ConfigDescriptor] 32 | Configuration = 2, 33 | /// A String Descriptor. See (USB32 Section 9.6.9). 34 | String = 3, 35 | /// An Interface Descriptor. See [InterfaceDescriptor] 36 | Interface = 4, 37 | /// An Endpoint Descriptor. See [EndpointDescriptor] 38 | Endpoint = 5, 39 | /// A Device Qualifier. USB2-specific. See [DeviceQualifier] 40 | DeviceQualifier = 6, 41 | /// The "Other Speed Configuration" descriptor. USB2-specific. See (USB2 9.6.4] 42 | OtherSpeedConfiguration = 7, 43 | /// TODO: Determine the standard that specifies this 44 | InterfacePower = 8, 45 | /// TODO: Determine the standard that specifies this (Possibly USB-C?) 46 | OnTheGo = 9, 47 | /// A Binary Device Object Store Descriptor. See [BosDescriptor] 48 | BinaryObjectStorage = 15, 49 | /// TODO: Track down the HID standard for references 50 | Hid = 33, 51 | /// A USB Hub Device Descriptor. See [HubDescriptor] 52 | Hub = 41, 53 | /// A Super Speed Endpoint Companion Descriptor. See [SuperSpeedCompanionDescriptor] 54 | SuperSpeedCompanion = 48, 55 | } 56 | 57 | pub(crate) mod bos; 58 | pub(crate) mod config; 59 | pub(crate) mod device; 60 | pub(crate) mod endpoint; 61 | pub(crate) mod hub; 62 | pub(crate) mod interface; 63 | pub(crate) mod setup; 64 | -------------------------------------------------------------------------------- /xhcid/src/xhci/doorbell.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Mmio}; 2 | 3 | #[repr(C, packed)] 4 | pub struct Doorbell(Mmio); 5 | 6 | impl Doorbell { 7 | pub fn read(&self) -> u32 { 8 | self.0.read() 9 | } 10 | 11 | pub fn write(&mut self, data: u32) { 12 | self.0.write(data); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /xhcid/src/xhci/event.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Mmio}; 2 | use syscall::error::Result; 3 | 4 | use common::dma::Dma; 5 | 6 | use super::ring::Ring; 7 | use super::trb::Trb; 8 | use super::Xhci; 9 | 10 | #[repr(C, packed)] 11 | pub struct EventRingSte { 12 | pub address_low: Mmio, 13 | pub address_high: Mmio, 14 | pub size: Mmio, 15 | _rsvd: Mmio, 16 | _rsvd2: Mmio, 17 | } 18 | 19 | // TODO: Use atomic operations, and perhaps an occasional lock for reallocating. 20 | pub struct EventRing { 21 | pub ste: Dma<[EventRingSte]>, 22 | pub ring: Ring, 23 | } 24 | 25 | impl EventRing { 26 | pub fn new(ac64: bool) -> Result { 27 | let mut ring = EventRing { 28 | ste: unsafe { Xhci::alloc_dma_zeroed_unsized_raw(ac64, 1)? }, 29 | ring: Ring::new(ac64, 256, false)?, 30 | }; 31 | 32 | ring.ste[0] 33 | .address_low 34 | .write(ring.ring.trbs.physical() as u32); 35 | ring.ste[0] 36 | .address_high 37 | .write((ring.ring.trbs.physical() as u64 >> 32) as u32); 38 | ring.ste[0].size.write(ring.ring.trbs.len() as u16); 39 | 40 | Ok(ring) 41 | } 42 | 43 | pub fn next(&mut self) -> &mut Trb { 44 | self.ring.next().0 45 | } 46 | pub fn erdp(&self) -> u64 { 47 | self.ring.register() & 0xFFFF_FFFF_FFFF_FFF0 48 | } 49 | pub fn erstba(&self) -> u64 { 50 | self.ste.physical() as u64 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /xhcid/src/xhci/operational.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Mmio}; 2 | 3 | /// The XHCI Operational Registers 4 | /// 5 | /// These registers specify the operational state of the XHCI device, and are used to receive status 6 | /// messages and transmit commands. These registers are offset from the XHCI base address by the 7 | /// "length" field of the [CapabilityRegs] 8 | /// 9 | /// See XHCI section 5.4. Table 5-18 describes the offset of these registers in memory. 10 | #[repr(C, packed)] 11 | pub struct OperationalRegs { 12 | /// The USB Command Register (USBCMD) 13 | /// 14 | /// Describes the command to be executed by the XHCI. Writes to this register case a command 15 | /// to be executed. 16 | /// 17 | /// - Bit 0 is the Run/Stop bit (R/S). Writing a value of 1 stops the xHC from executing the schedule, 1 resumes. Latency is ~16ms at worst. (See XHCI Table 5-20) 18 | /// - Bit 1 is the Host Controller Reset Bit (HCRST). Used by software to reset the host controller (See XHCI Table 5-20) 19 | /// - Bit 2 is the Interrupter Enable Bit (INTE). Enables interrupting the host system. 20 | /// - Bit 3 is the Host System Error Enable Bit (HSEE). Enables out-of-band error signalling to the host. 21 | /// - Bits 4-6 are reserved. 22 | /// - Bit 7 is the Light Host Controller Reset Bit (LHCRST). Resets the driver without affecting the state of the ports. Affected by [CapabilityRegs] 23 | /// - Bit 8 is the Controller Save State Bit (CSS). See XHCI Table 5-20 24 | /// - Bit 9 is the Controller Restore State Bit (CRS). See XHCI Table 5-20 25 | /// - Bit 10 is the Enable Wrap Event Bit (EWE). See XHCI Table 5-20 26 | /// - Bit 11 is the Enable U3 MFINDEX Stop Bit (EU3S). See XHCI Table 5-20 27 | /// - Bit 12 is reserved. 28 | /// - Bit 13 is the CEM Enable Bit (CME). See XHCI Table 5-20 29 | /// - Bit 14 is the Extended TBC Enable Bit (ETE). See XHCI Table 5-20 30 | /// - Bit 15 is the Extended TBC TRB Status Enable Bit (TSC_En). See XHCI Table 5-20 31 | /// - Bit 16 is the VTIO Enable Bit (VTIOE). Controls the enable state of the VTIO capability. 32 | /// - Bits 17-31 are reserved. 33 | /// 34 | pub usb_cmd: Mmio, 35 | /// The USB Status Register (USBSTS) 36 | /// 37 | /// This register indicates pending interrupts and various states of the host controller. 38 | /// 39 | /// Software sets a bit to '0' in this register by writing a 1 to it. 40 | /// 41 | /// 42 | pub usb_sts: Mmio, 43 | /// The PAGESIZE Register (PAGESIZE) 44 | /// 45 | /// 46 | pub page_size: Mmio, 47 | /// Reserved bits (RsvdZ) 48 | _rsvd: [Mmio; 2], 49 | /// The Device Notification Control Register (DNCTRL) 50 | /// 51 | /// 52 | pub dn_ctrl: Mmio, 53 | /// The Command Ring Control Register Lower 32 bits (CRCR) 54 | /// 55 | /// 56 | pub crcr_low: Mmio, 57 | /// The Command Ring Control Register Upper 32 bits (CRCR) 58 | /// 59 | /// 60 | pub crcr_high: Mmio, 61 | /// Reserved bits (RsvdZ) 62 | _rsvd2: [Mmio; 4], 63 | /// Device Context Base Address Array Pointer Lower 32 bits (DCBAAP) 64 | /// 65 | /// 66 | pub dcbaap_low: Mmio, 67 | /// Device Context Base Address Array Pointer Upper 32 bits (DCBAAP) 68 | /// 69 | /// 70 | pub dcbaap_high: Mmio, 71 | /// The Configure Register (CONFIG) 72 | /// 73 | /// 74 | pub config: Mmio, 75 | // The standard has another set of reserved bits from 3C-3FFh here 76 | // The standard has 400-13FFh has a Port Register Set here (likely defined in port.rs). 77 | } 78 | 79 | /// The mask to get the CIE bit from the Config register. See [OperationalRegs] 80 | pub const OP_CONFIG_CIE_BIT: u32 = 1 << 9; 81 | 82 | impl OperationalRegs { 83 | pub fn cie(&self) -> bool { 84 | self.config.readf(OP_CONFIG_CIE_BIT) 85 | } 86 | pub fn set_cie(&mut self, value: bool) { 87 | self.config.writef(OP_CONFIG_CIE_BIT, value) 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /xhcid/src/xhci/port.rs: -------------------------------------------------------------------------------- 1 | use common::io::{Io, Mmio}; 2 | 3 | // RO - read-only 4 | // ROS - read-only sticky 5 | // RW - read/write 6 | // RWS - read/write sticky 7 | // RW1CS - read/write-1-to-clear sticky 8 | // RW1S - read/write-1-to-set 9 | // Sticky register values may preserve values through chip hardware reset 10 | 11 | bitflags! { 12 | pub struct PortFlags: u32 { 13 | const CCS = 1 << 0; // ROS 14 | const PED = 1 << 1; // RW1CS 15 | const RSVD_2 = 1 << 2; // RsvdZ 16 | const OCA = 1 << 3; // RO 17 | const PR = 1 << 4; // RW1S 18 | const PLS_0 = 1 << 5; // RWS 19 | const PLS_1 = 1 << 6; // RWS 20 | const PLS_2 = 1 << 7; // RWS 21 | const PLS_3 = 1 << 8; // RWS 22 | const PP = 1 << 9; // RWS 23 | const SPEED_0 = 1 << 10; // ROS 24 | const SPEED_1 = 1 << 11; // ROS 25 | const SPEED_2 = 1 << 12; // ROS 26 | const SPEED_3 = 1 << 13; // ROS 27 | const PIC_AMB = 1 << 14; // RWS 28 | const PIC_GRN = 1 << 15; // RWS 29 | const LWS = 1 << 16; // RW 30 | const CSC = 1 << 17; // RW1CS 31 | const PEC = 1 << 18; // RW1CS 32 | const WRC = 1 << 19; // RW1CS 33 | const OCC = 1 << 20; // RW1CS 34 | const PRC = 1 << 21; // RW1CS 35 | const PLC = 1 << 22; // RW1CS 36 | const CEC = 1 << 23; // RW1CS 37 | const CAS = 1 << 24; // RO 38 | const WCE = 1 << 25; // RWS 39 | const WDE = 1 << 26; // RWS 40 | const WOE = 1 << 27; // RWS 41 | const RSVD_28 = 1 << 28; // RsvdZ 42 | const RSVD_29 = 1 << 29; // RsvdZ 43 | const DR = 1 << 30; // RO 44 | const WPR = 1 << 31; // RW1S 45 | } 46 | } 47 | 48 | #[repr(C, packed)] 49 | pub struct Port { 50 | // This has write one to clear fields, do not expose it, handle writes carefully! 51 | portsc: Mmio, 52 | pub portpmsc: Mmio, 53 | pub portli: Mmio, 54 | pub porthlpmc: Mmio, 55 | } 56 | 57 | impl Port { 58 | pub fn read(&self) -> u32 { 59 | self.portsc.read() 60 | } 61 | 62 | pub fn clear_csc(&mut self) { 63 | self.portsc 64 | .write((self.flags_preserved() | PortFlags::CSC).bits()); 65 | } 66 | 67 | pub fn clear_prc(&mut self) { 68 | self.portsc 69 | .write((self.flags_preserved() | PortFlags::PRC).bits()); 70 | } 71 | 72 | pub fn set_pr(&mut self) { 73 | self.portsc 74 | .write((self.flags_preserved() | PortFlags::PR).bits()); 75 | } 76 | 77 | pub fn state(&self) -> u8 { 78 | ((self.read() & (0b1111 << 5)) >> 5) as u8 79 | } 80 | 81 | pub fn speed(&self) -> u8 { 82 | ((self.read() & (0b1111 << 10)) >> 10) as u8 83 | } 84 | 85 | pub fn flags(&self) -> PortFlags { 86 | PortFlags::from_bits_truncate(self.read()) 87 | } 88 | 89 | // Read only preserved flags 90 | pub fn flags_preserved(&self) -> PortFlags { 91 | // RO(S) and RW(S) bits should be preserved 92 | // RW1S and RW1CS bits should not 93 | let preserved = PortFlags::CCS 94 | | PortFlags::OCA 95 | | PortFlags::PLS_0 96 | | PortFlags::PLS_1 97 | | PortFlags::PLS_2 98 | | PortFlags::PLS_3 99 | | PortFlags::PP 100 | | PortFlags::SPEED_0 101 | | PortFlags::SPEED_1 102 | | PortFlags::SPEED_2 103 | | PortFlags::SPEED_3 104 | | PortFlags::PIC_AMB 105 | | PortFlags::PIC_GRN 106 | | PortFlags::WCE 107 | | PortFlags::WDE 108 | | PortFlags::WOE 109 | | PortFlags::DR; 110 | 111 | self.flags() & preserved 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /xhcid/src/xhci/runtime.rs: -------------------------------------------------------------------------------- 1 | use common::io::Mmio; 2 | 3 | #[repr(C, packed)] 4 | pub struct Interrupter { 5 | pub iman: Mmio, 6 | pub imod: Mmio, 7 | pub erstsz: Mmio, 8 | _rsvd: Mmio, 9 | pub erstba_low: Mmio, 10 | pub erstba_high: Mmio, 11 | pub erdp_low: Mmio, 12 | pub erdp_high: Mmio, 13 | } 14 | 15 | #[repr(C, packed)] 16 | pub struct RuntimeRegs { 17 | pub mfindex: Mmio, 18 | _rsvd: [Mmio; 7], 19 | pub ints: [Interrupter; 1024], 20 | } 21 | --------------------------------------------------------------------------------