Compare commits
12 Commits
975df985ac
...
82175f342e
Author | SHA1 | Date | |
---|---|---|---|
82175f342e | |||
f716c50988 | |||
99f5ad0987 | |||
41944890b6 | |||
70eb6cfaed | |||
5c1c980ccd | |||
99a099efad | |||
6253ab282e | |||
57d46ed070 | |||
f2cfc9136a | |||
24f1f41217 | |||
5d406feb07 |
151
Cargo.lock
generated
151
Cargo.lock
generated
@ -187,9 +187,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "async-trait"
|
||||
version = "0.1.85"
|
||||
version = "0.1.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056"
|
||||
checksum = "644dd749086bf3771a2fbc5f256fdb982d53f011c7d5d560304eafeecebce79d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -294,9 +294,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.16.0"
|
||||
version = "3.17.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
|
||||
checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
@ -326,9 +326,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.10"
|
||||
version = "1.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229"
|
||||
checksum = "c7777341816418c02e033934a09f20dc0ccaf65a5201ef8a450ae0105a573fda"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
"libc",
|
||||
@ -357,9 +357,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.27"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796"
|
||||
checksum = "8acebd8ad879283633b343856142139f2da2317c96b05b4dd6181c61e2480184"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
@ -367,9 +367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_builder"
|
||||
version = "4.5.27"
|
||||
version = "4.5.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7"
|
||||
checksum = "f6ba32cbda51c7e1dfd49acc1457ba1a7dec5b64fe360e828acb13ca8dc9c2f9"
|
||||
dependencies = [
|
||||
"anstream",
|
||||
"anstyle",
|
||||
@ -379,9 +379,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.24"
|
||||
version = "4.5.28"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c"
|
||||
checksum = "bf4ced95c6f4a675af3da73304b9ac4ed991640c36374e4b46795c49e17cf1ed"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
@ -409,9 +409,9 @@ checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.144"
|
||||
version = "0.1.146"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d18a7b7b5a56aa131e62314b4d862c9f6aa2860f615f3770094ec9064d7ec572"
|
||||
checksum = "a97117b1434b79833f39a5fabdf82f890bd98c1988334dea1cb67f7e627fa311"
|
||||
dependencies = [
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
@ -761,7 +761,19 @@ checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi 0.13.3+wasi-0.2.2",
|
||||
"windows-targets",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1040,9 +1052,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "indicatif"
|
||||
version = "0.17.9"
|
||||
version = "0.17.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbf675b85ed934d3c67b5c5469701eec7db22689d0a2139d856e0925fa28b281"
|
||||
checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235"
|
||||
dependencies = [
|
||||
"console",
|
||||
"number_prefix",
|
||||
@ -1133,6 +1145,7 @@ dependencies = [
|
||||
name = "kernel-arch-hosted"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"device-api",
|
||||
"kernel-arch-interface",
|
||||
"libk-mm-interface",
|
||||
"yggdrasil-abi",
|
||||
@ -1332,9 +1345,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libssh2-sys"
|
||||
version = "0.3.0"
|
||||
version = "0.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dc8a030b787e2119a731f1951d6a773e2280c660f8ec4b0f5e1505a386e71ee"
|
||||
checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@ -1457,9 +1470,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.3"
|
||||
version = "0.8.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924"
|
||||
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
]
|
||||
@ -1511,9 +1524,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.20.2"
|
||||
version = "1.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775"
|
||||
checksum = "945462a4b81e43c4e3ba96bd7b49d834c6f61198356aa858733bc4acf3cbe62e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
@ -1523,9 +1536,9 @@ checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
version = "0.9.104"
|
||||
version = "0.9.105"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741"
|
||||
checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@ -1696,7 +1709,7 @@ version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"getrandom 0.2.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1903,9 +1916,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.137"
|
||||
version = "1.0.138"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b"
|
||||
checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949"
|
||||
dependencies = [
|
||||
"itoa",
|
||||
"memchr",
|
||||
@ -2031,13 +2044,13 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tempfile"
|
||||
version = "3.15.0"
|
||||
version = "3.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704"
|
||||
checksum = "38c246215d7d24f48ae091a2902398798e05d978b24315d6efbc00ede9a8bb91"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"fastrand",
|
||||
"getrandom",
|
||||
"getrandom 0.3.1",
|
||||
"once_cell",
|
||||
"rustix",
|
||||
"windows-sys",
|
||||
@ -2135,9 +2148,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml"
|
||||
version = "0.8.19"
|
||||
version = "0.8.20"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e"
|
||||
checksum = "cd87a5cdd6ffab733b2f74bc4fd7ee5fff6634124999ac278c35fc78c6120148"
|
||||
dependencies = [
|
||||
"serde",
|
||||
"serde_spanned",
|
||||
@ -2156,9 +2169,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.22.22"
|
||||
version = "0.22.24"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||
checksum = "17b4795ff5edd201c7cd6dca065ae59972ce77d1b80fa0a84d94950ece7d1474"
|
||||
dependencies = [
|
||||
"indexmap",
|
||||
"serde",
|
||||
@ -2270,9 +2283,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||
|
||||
[[package]]
|
||||
name = "uuid"
|
||||
version = "1.12.1"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b"
|
||||
checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
@ -2307,9 +2320,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wait-timeout"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6"
|
||||
checksum = "09ac3b126d3914f9849036f826e054cbabdc8519970b8998ddaf3b5bd3c65f11"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
@ -2330,6 +2343,15 @@ version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.13.3+wasi-0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2"
|
||||
dependencies = [
|
||||
"wit-bindgen-rt",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.100"
|
||||
@ -2525,9 +2547,9 @@ checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.6.25"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ad699df48212c6cc6eb4435f35500ac6fd3b9913324f938aea302022ce19d310"
|
||||
checksum = "59690dea168f2198d1a3b0cac23b8063efcd11012f10ae4698f284808c8ef603"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@ -2538,6 +2560,15 @@ version = "0.0.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen-rt"
|
||||
version = "0.33.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "write16"
|
||||
version = "1.0.0"
|
||||
@ -2733,6 +2764,23 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_net_stmmac"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"device-tree",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_net_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_nvme"
|
||||
version = "0.1.0"
|
||||
@ -2820,12 +2868,30 @@ dependencies = [
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_blk"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
"log",
|
||||
"tock-registers",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_virtio_core",
|
||||
"yggdrasil-abi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ygg_driver_virtio_core"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"device-api",
|
||||
"kernel-arch-hosted",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
@ -2857,6 +2923,7 @@ dependencies = [
|
||||
"bitflags 2.8.0",
|
||||
"bytemuck",
|
||||
"device-api",
|
||||
"futures-util",
|
||||
"libk",
|
||||
"libk-mm",
|
||||
"libk-util",
|
||||
@ -2931,10 +2998,12 @@ dependencies = [
|
||||
"ygg_driver_net_igbe",
|
||||
"ygg_driver_net_loopback",
|
||||
"ygg_driver_net_rtl81xx",
|
||||
"ygg_driver_net_stmmac",
|
||||
"ygg_driver_nvme",
|
||||
"ygg_driver_pci",
|
||||
"ygg_driver_usb",
|
||||
"ygg_driver_usb_xhci",
|
||||
"ygg_driver_virtio_blk",
|
||||
"ygg_driver_virtio_gpu",
|
||||
"ygg_driver_virtio_net",
|
||||
"yggdrasil-abi",
|
||||
|
@ -30,6 +30,7 @@ ygg_driver_net_core = { path = "driver/net/core" }
|
||||
ygg_driver_net_loopback = { path = "driver/net/loopback" }
|
||||
ygg_driver_virtio_net = { path = "driver/virtio/net", features = ["pci"] }
|
||||
ygg_driver_virtio_gpu = { path = "driver/virtio/gpu", features = ["pci"] }
|
||||
ygg_driver_virtio_blk = { path = "driver/virtio/blk", features = ["pci"] }
|
||||
ygg_driver_nvme = { path = "driver/block/nvme" }
|
||||
ygg_driver_ahci = { path = "driver/block/ahci" }
|
||||
ygg_driver_input = { path = "driver/input" }
|
||||
@ -60,6 +61,7 @@ kernel-arch-aarch64.workspace = true
|
||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||
device-tree.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
|
||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||
yboot-proto.workspace = true
|
||||
@ -91,6 +93,7 @@ kernel-arch-aarch64.workspace = true
|
||||
kernel-arch-riscv64.workspace = true
|
||||
|
||||
ygg_driver_acpi.path = "driver/acpi"
|
||||
ygg_driver_net_stmmac.path = "driver/net/stmmac"
|
||||
|
||||
[features]
|
||||
default = ["fb_console"]
|
||||
|
@ -7,3 +7,4 @@ edition = "2021"
|
||||
kernel-arch-interface.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm-interface.workspace = true
|
||||
device-api.workspace = true
|
||||
|
@ -1,9 +1,11 @@
|
||||
#![feature(never_type)]
|
||||
#![feature(never_type, allocator_api, slice_ptr_get)]
|
||||
use std::{
|
||||
alloc::{Allocator, Global, Layout},
|
||||
marker::PhantomData,
|
||||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use device_api::dma::{DmaAllocation, DmaAllocator};
|
||||
use kernel_arch_interface::{
|
||||
cpu::{CpuData, IpiQueue},
|
||||
mem::{
|
||||
@ -105,6 +107,14 @@ impl Architecture for ArchitectureImpl {
|
||||
fn ipi_queue(_cpu_id: u32) -> Option<&'static IpiQueue<Self>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn load_barrier() {}
|
||||
|
||||
fn store_barrier() {}
|
||||
|
||||
fn memory_barrier() {}
|
||||
|
||||
fn flush_virtual_range(_range: std::ops::Range<usize>) {}
|
||||
}
|
||||
|
||||
impl KernelTableManager for KernelTableManagerImpl {
|
||||
@ -202,3 +212,19 @@ impl<K: KernelTableManager, PA: PhysicalMemoryAllocator> TaskContext<K, PA>
|
||||
extern "Rust" fn __signal_process_group(_group_id: ProcessGroupId, _signal: Signal) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
pub struct HostedDmaAllocator;
|
||||
|
||||
impl DmaAllocator for HostedDmaAllocator {
|
||||
fn allocate(&self, layout: Layout) -> Result<DmaAllocation, Error> {
|
||||
let ptr = Global.allocate(layout.align_to(0x1000).unwrap()).unwrap();
|
||||
let base = ptr.as_non_null_ptr();
|
||||
let addr: usize = base.addr().into();
|
||||
Ok(DmaAllocation {
|
||||
host_virtual: base.cast(),
|
||||
host_physical: addr as _,
|
||||
page_count: layout.size().div_ceil(0x1000),
|
||||
bus_address: addr as _,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -322,7 +322,9 @@ impl PciBusSegment {
|
||||
|
||||
for i in 0..6 {
|
||||
if (1 << i) & bar_mask != 0 {
|
||||
let orig_value = config.bar(i).unwrap();
|
||||
let Some(orig_value) = config.bar(i) else {
|
||||
continue;
|
||||
};
|
||||
let size = unsafe { config.bar_size(i) };
|
||||
|
||||
if size != 0 {
|
||||
|
@ -70,6 +70,7 @@ pub fn handle(packet: L2Packet) {
|
||||
match ty {
|
||||
EtherType::ARP => l3::arp::handle_packet(packet),
|
||||
EtherType::IPV4 => l3::ip::handle_v4_packet(packet),
|
||||
EtherType(0x0027) => (),
|
||||
p => {
|
||||
log::debug!(
|
||||
"Unrecognized L2 protocol: {:#06x}",
|
||||
|
18
kernel/driver/net/stmmac/Cargo.toml
Normal file
18
kernel/driver/net/stmmac/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "ygg_driver_net_stmmac"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
device-tree.workspace = true
|
||||
device-api.workspace = true
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk.workspace = true
|
||||
ygg_driver_net_core.path = "../core"
|
||||
|
||||
tock-registers.workspace = true
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
429
kernel/driver/net/stmmac/src/lib.rs
Normal file
429
kernel/driver/net/stmmac/src/lib.rs
Normal file
@ -0,0 +1,429 @@
|
||||
#![no_std]
|
||||
|
||||
use core::{mem::MaybeUninit, time::Duration};
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{
|
||||
device_tree_driver, util::read_mac_address, InitSequence, Node, ProbeContext,
|
||||
};
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk::{
|
||||
device::external_interrupt_controller,
|
||||
dma::DmaBuffer,
|
||||
error::Error,
|
||||
task::runtime::{self, psleep, pwait},
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::{event::BitmapEvent, sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use regs::{
|
||||
dma::{DMACiCR, DMACiIER, DMACiSR, DMACiTXCR, DMAC0RXCR, DMAMR, DMASBMR},
|
||||
mac::{
|
||||
MACAiHR, MACAiLR, MACCR, MACIER, MACISR, MACPFR, MACPHYCSR, MACQ0TXFCR, MACRXFCR, MACRXQC0R,
|
||||
},
|
||||
mtl::{MTLRXQiOMR, MTLTXQiOMR, MTLOMR},
|
||||
Regs,
|
||||
};
|
||||
use ring::{RxDescriptor, RxRing, TxDescriptor, TxRing};
|
||||
use tock_registers::interfaces::{ReadWriteable, Readable, Writeable};
|
||||
use ygg_driver_net_core::{
|
||||
ephy::{PhyAccess, GBESR},
|
||||
interface::{NetworkDevice, NetworkInterfaceType},
|
||||
RxPacket,
|
||||
};
|
||||
use yggdrasil_abi::net::{
|
||||
link::{Duplex, EthernetLinkState, EthernetSpeed, LinkState},
|
||||
MacAddress,
|
||||
};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
pub mod regs;
|
||||
pub mod ring;
|
||||
|
||||
struct Inner {
|
||||
regs: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
|
||||
tx_ring: IrqSafeSpinlock<TxRing>,
|
||||
rx_ring: IrqSafeSpinlock<RxRing>,
|
||||
}
|
||||
|
||||
struct Stmmac {
|
||||
base: PhysicalAddress,
|
||||
dma: OneTimeInit<Arc<dyn DmaAllocator>>,
|
||||
mac: MacAddress,
|
||||
|
||||
clk_stmmaceth: ClockHandle,
|
||||
clk_pclk: ClockHandle,
|
||||
clk_ptp_ref: ClockHandle,
|
||||
clk_tx: ClockHandle,
|
||||
clk_gtx: ClockHandle,
|
||||
|
||||
rst_stmmaceth: ResetHandle,
|
||||
rst_ahb: ResetHandle,
|
||||
|
||||
irq: FullIrq,
|
||||
softirq_events: BitmapEvent<AtomicWaker>,
|
||||
|
||||
inner: OneTimeInit<Inner>,
|
||||
iface_id: OneTimeInit<u32>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn link_state(&self) -> LinkState {
|
||||
let status = self.regs.lock().MAC.MACPHYCSR.extract();
|
||||
let link = if status.matches_all(MACPHYCSR::LNKSTS::Up) {
|
||||
let speed = match status.read_as_enum(MACPHYCSR::LNKSPEED) {
|
||||
Some(MACPHYCSR::LNKSPEED::Value::Speed2_5MHz) => EthernetSpeed::Speed10,
|
||||
Some(MACPHYCSR::LNKSPEED::Value::Speed25MHz) => EthernetSpeed::Speed100,
|
||||
Some(MACPHYCSR::LNKSPEED::Value::Speed125MHz) => EthernetSpeed::Speed1000,
|
||||
_ => EthernetSpeed::Unknown,
|
||||
};
|
||||
let duplex = if status.matches_all(MACPHYCSR::LNKMOD::FullDuplex) {
|
||||
Duplex::Full
|
||||
} else {
|
||||
Duplex::Half
|
||||
};
|
||||
EthernetLinkState::Up(speed, duplex)
|
||||
} else {
|
||||
EthernetLinkState::Down
|
||||
};
|
||||
|
||||
LinkState::Ethernet(link)
|
||||
}
|
||||
}
|
||||
|
||||
impl Stmmac {
|
||||
const SOFTIRQ_GMII_STATUS: u64 = 1 << 0;
|
||||
const SOFTIRQ_TX_STATUS: u64 = 1 << 1;
|
||||
const SOFTIRQ_RX_STATUS: u64 = 1 << 2;
|
||||
|
||||
pub fn start_xmit(&self, frame: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
let inner = self.inner.get();
|
||||
let regs = inner.regs.lock();
|
||||
|
||||
let mut tx_ring = inner.tx_ring.lock();
|
||||
let index = tx_ring.push_xmit(frame)?;
|
||||
let ring_pos = tx_ring
|
||||
.buffer_base()
|
||||
.add(size_of::<TxDescriptor>() * index)
|
||||
.try_into_u32()
|
||||
.unwrap();
|
||||
|
||||
regs.DMA.DMAC0TXDTPR.set(ring_pos);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn softirq(&self) {
|
||||
let inner = self.inner.get();
|
||||
let dma = self.dma.get();
|
||||
let iface = *self.iface_id.get();
|
||||
|
||||
loop {
|
||||
let events = self.softirq_events.wait().await;
|
||||
|
||||
if events & Self::SOFTIRQ_GMII_STATUS != 0 {
|
||||
let link_status = inner.link_state();
|
||||
log::info!("stmmac: link is {link_status}");
|
||||
}
|
||||
|
||||
if events & Self::SOFTIRQ_TX_STATUS != 0 {
|
||||
inner.tx_ring.lock().consume().ok();
|
||||
}
|
||||
|
||||
if events & Self::SOFTIRQ_RX_STATUS != 0 {
|
||||
let mut rx_ring = inner.rx_ring.lock();
|
||||
rx_ring
|
||||
.consume(dma.as_ref(), |packet| {
|
||||
let packet = RxPacket::new(packet, 0, iface);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for Stmmac {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
let inner = self.inner.get();
|
||||
let regs = inner.regs.lock();
|
||||
let mac_isr = regs.MAC.MACISR.extract();
|
||||
let dma0_sr = regs.DMA.DMAC0SR.extract();
|
||||
|
||||
if mac_isr.matches_all(MACISR::RGSMIIIS::SET) {
|
||||
let _ = regs.MAC.MACPHYCSR.get();
|
||||
self.softirq_events.signal(Self::SOFTIRQ_GMII_STATUS);
|
||||
}
|
||||
|
||||
if dma0_sr.matches_all(DMACiSR::TI::SET) {
|
||||
regs.DMA.DMAC0SR.modify(DMACiSR::TI::SET);
|
||||
self.softirq_events.signal(Self::SOFTIRQ_TX_STATUS);
|
||||
}
|
||||
if dma0_sr.matches_all(DMACiSR::RI::SET) {
|
||||
regs.DMA.DMAC0SR.modify(DMACiSR::RI::SET);
|
||||
self.softirq_events.signal(Self::SOFTIRQ_RX_STATUS);
|
||||
}
|
||||
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Stmmac {
|
||||
unsafe fn init(self: Arc<Self>, cx: DeviceInitContext) -> Result<(), Error> {
|
||||
// TODO this is a dirty fix for the device tree double initialization problem
|
||||
if self.dma.is_initialized() {
|
||||
return Err(Error::AlreadyExists);
|
||||
}
|
||||
let dma = self.dma.init(cx.dma_allocator.clone());
|
||||
|
||||
let intc = external_interrupt_controller()?;
|
||||
|
||||
intc.register_irq(self.irq.irq, self.irq.options, self.clone())?;
|
||||
|
||||
let tx_ring_capacity = 32;
|
||||
let rx_ring_capacity = 32;
|
||||
|
||||
log::info!("stmmac: setup {:#x}, mac={}", self.base, self.mac);
|
||||
|
||||
self.clk_stmmaceth.enable()?;
|
||||
self.clk_pclk.enable()?;
|
||||
self.clk_ptp_ref.enable()?;
|
||||
self.clk_gtx.enable()?;
|
||||
self.clk_tx.enable()?;
|
||||
|
||||
self.rst_ahb.deassert()?;
|
||||
|
||||
self.rst_stmmaceth.assert()?;
|
||||
psleep(Duration::from_millis(1));
|
||||
self.rst_stmmaceth.deassert()?;
|
||||
|
||||
psleep(Duration::from_millis(20));
|
||||
|
||||
let regs = unsafe { DeviceMemoryIo::<Regs>::map(self.base, Default::default()) }?;
|
||||
|
||||
// DMA initialization
|
||||
|
||||
// Perform a software reset
|
||||
regs.DMA.DMAMR.modify(DMAMR::SWR::SET);
|
||||
pwait(
|
||||
Duration::from_millis(100),
|
||||
Duration::from_millis(10),
|
||||
|| regs.DMA.DMAMR.matches_all(DMAMR::SWR::CLEAR),
|
||||
)?;
|
||||
|
||||
// Setup DMA parameters
|
||||
// TODO get these params from device tree
|
||||
regs.DMA.DMASBMR.write(
|
||||
DMASBMR::FB::SET
|
||||
+ DMASBMR::BLEN256::SET
|
||||
+ DMASBMR::BLEN128::SET
|
||||
+ DMASBMR::BLEN64::SET
|
||||
+ DMASBMR::BLEN32::SET
|
||||
+ DMASBMR::EN_LPI::SET
|
||||
+ DMASBMR::WR_OSR_LMT.val(3)
|
||||
+ DMASBMR::RD_OSR_LMT.val(3),
|
||||
);
|
||||
|
||||
// Setup DMA Tx/Rx rings
|
||||
let tx_ring = TxRing::with_capacity(dma.as_ref(), tx_ring_capacity)?;
|
||||
let rx_ring = RxRing::with_capacity(dma.as_ref(), rx_ring_capacity)?;
|
||||
let tx_ring_base = tx_ring.buffer_base().try_into_u32().unwrap();
|
||||
let rx_ring_base = rx_ring.buffer_base().try_into_u32().unwrap();
|
||||
|
||||
regs.DMA.DMAC0TXRLR.set(tx_ring_capacity as u32 - 1);
|
||||
regs.DMA.DMAC0TXDLAR.set(tx_ring_base);
|
||||
regs.DMA.DMAC0TXDTPR.set(tx_ring_base);
|
||||
|
||||
regs.DMA.DMAC0RXRLR.set(rx_ring_capacity as u32 - 1);
|
||||
regs.DMA.DMAC0RXDLAR.set(rx_ring_base);
|
||||
regs.DMA
|
||||
.DMAC0RXDTPR
|
||||
.set(rx_ring_base + (rx_ring_capacity * size_of::<RxDescriptor>()) as u32);
|
||||
|
||||
// Setup DMA maximum segmen size, Rx buffer size + max burst len
|
||||
regs.DMA.DMAC0CR.write(DMACiCR::PBLX8::CLEAR);
|
||||
regs.DMA
|
||||
.DMAC0TXCR
|
||||
.write(DMACiTXCR::TXPBL.val(32) + DMACiTXCR::OSF::SET);
|
||||
regs.DMA
|
||||
.DMAC0RXCR
|
||||
.write(DMAC0RXCR::RBSZ.val(4096) + DMAC0RXCR::RXPBL.val(32));
|
||||
|
||||
// Enable DMA interrupts
|
||||
// TODO enable abnormal interrupts to handle errors properly
|
||||
regs.DMA.DMAC0IER.write(
|
||||
DMACiIER::NIE::SET
|
||||
+ DMACiIER::RIE::SET
|
||||
+ DMACiIER::TIE::SET
|
||||
+ DMACiIER::RBUE::SET
|
||||
+ DMACiIER::RSE::SET,
|
||||
);
|
||||
|
||||
// Start Tx/Rx DMAs
|
||||
regs.DMA.DMAC0TXCR.modify(DMACiTXCR::ST::SET);
|
||||
regs.DMA.DMAC0RXCR.modify(DMAC0RXCR::SR::SET);
|
||||
|
||||
// MTL initialization
|
||||
|
||||
regs.MTL
|
||||
.MTLOMR
|
||||
.write(MTLOMR::RAA::AlgStrictPrio + MTLOMR::SCHALG::AlgStrictPrio);
|
||||
|
||||
// TODO get TQS, RQS from device tree
|
||||
regs.MTL
|
||||
.MTLTXQ0OMR
|
||||
.write(MTLTXQiOMR::TSF::SET + MTLTXQiOMR::TQS.val(7) + MTLTXQiOMR::TXQEN::Enable);
|
||||
regs.MTL.MTLRXQ0OMR.write(
|
||||
MTLRXQiOMR::RQS.val(7)
|
||||
+ MTLRXQiOMR::DIS_TCP_EF::SET
|
||||
+ MTLRXQiOMR::RSF::SET
|
||||
+ MTLRXQiOMR::FEP::SET
|
||||
+ MTLRXQiOMR::FUP::SET,
|
||||
);
|
||||
|
||||
// MAC initialization
|
||||
|
||||
// Setup the MAC address
|
||||
let mac_bytes: [u8; 6] = self.mac.into();
|
||||
regs.MAC.MACA0HR.write(
|
||||
MACAiHR::AE::SET
|
||||
+ MACAiHR::ADDR4.val(mac_bytes[4] as u32)
|
||||
+ MACAiHR::ADDR5.val(mac_bytes[5] as u32),
|
||||
);
|
||||
regs.MAC.MACA0LR.write(
|
||||
MACAiLR::ADDR0.val(mac_bytes[0] as u32)
|
||||
+ MACAiLR::ADDR1.val(mac_bytes[1] as u32)
|
||||
+ MACAiLR::ADDR2.val(mac_bytes[2] as u32)
|
||||
+ MACAiLR::ADDR3.val(mac_bytes[3] as u32),
|
||||
);
|
||||
|
||||
// TODO proper filtering, don't use promiscuous mode all the time
|
||||
regs.MAC.MACPFR.write(MACPFR::PR::SET + MACPFR::RA::SET);
|
||||
|
||||
// Setup Tx/Rx flow control, enable Rx queue 0
|
||||
regs.MAC.MACQ0TXFCR.write(MACQ0TXFCR::TFE::SET);
|
||||
regs.MAC.MACRXFCR.write(MACRXFCR::RFE::SET);
|
||||
regs.MAC.MACRXQC0R.write(MACRXQC0R::RXQ0EN::Enable);
|
||||
|
||||
// Setup MAC-level interrupts
|
||||
regs.MAC
|
||||
.MACIER
|
||||
.write(MACIER::TXSTSIE::SET + MACIER::RXSTSIE::SET + MACIER::RGSMIIIE::SET);
|
||||
|
||||
// Setup link information
|
||||
regs.MAC
|
||||
.MACCR
|
||||
.modify(MACCR::PS::Ps1000Mbps + MACCR::DM::FullDuplex);
|
||||
|
||||
// Start Tx/Rx
|
||||
regs.MAC.MACCR.modify(MACCR::TE::SET + MACCR::RE::SET);
|
||||
|
||||
// Apply PHY settings
|
||||
regs.MAC
|
||||
.MACPHYCSR
|
||||
.modify(MACPHYCSR::LUD::SET + MACPHYCSR::TC::SET);
|
||||
|
||||
// Setup the PHY
|
||||
// TODO autodiscover phy
|
||||
let phy = PhyAccess::new(&*regs, 0x00);
|
||||
let (id0, id1) = phy.id()?;
|
||||
log::info!("stmmac: PHY {id0:04x}:{id1:04x}");
|
||||
phy.reset(Duration::from_millis(100))?;
|
||||
phy.setup_link(true, GBESR::empty())?;
|
||||
|
||||
self.inner.init(Inner {
|
||||
regs: IrqSafeSpinlock::new(regs),
|
||||
|
||||
tx_ring: IrqSafeSpinlock::new(tx_ring),
|
||||
rx_ring: IrqSafeSpinlock::new(rx_ring),
|
||||
});
|
||||
|
||||
let iface =
|
||||
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||
self.iface_id.init(iface.id());
|
||||
|
||||
intc.enable_irq(self.irq.irq)?;
|
||||
|
||||
let p = self.clone();
|
||||
runtime::spawn(async move { p.softirq().await })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Synopsys DesignWare Ethernet MAC"
|
||||
}
|
||||
}
|
||||
|
||||
impl NetworkDevice for Stmmac {
|
||||
fn allocate_transmit_buffer(&self, len: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
DmaBuffer::new_uninit_slice(&**self.dma.get(), len)
|
||||
}
|
||||
|
||||
fn transmit_buffer(&self, buffer: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
self.start_xmit(buffer)
|
||||
}
|
||||
|
||||
fn packet_prefix_size(&self) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn read_hardware_address(&self) -> MacAddress {
|
||||
self.mac
|
||||
}
|
||||
|
||||
fn link_state(&self) -> LinkState {
|
||||
self.inner.get().link_state()
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-dwmac"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
||||
let clk_stmmaceth = node.named_clock("stmmaceth")?;
|
||||
let clk_pclk = node.named_clock("pclk")?;
|
||||
let clk_ptp_ref = node.named_clock("ptp_ref")?;
|
||||
let clk_tx = node.named_clock("tx")?;
|
||||
let clk_gtx = node.named_clock("gtx")?;
|
||||
|
||||
let rst_stmmaceth = node.named_reset("stmmaceth")?;
|
||||
let rst_ahb = node.named_reset("ahb")?;
|
||||
|
||||
let mac = read_mac_address(node)?;
|
||||
// TODO named_interrupt()
|
||||
let irq = node.interrupt(0)?;
|
||||
|
||||
context.sequence = Some(InitSequence::Late);
|
||||
|
||||
Some(Arc::new(Stmmac {
|
||||
base,
|
||||
mac,
|
||||
irq,
|
||||
|
||||
clk_stmmaceth,
|
||||
clk_pclk,
|
||||
clk_ptp_ref,
|
||||
clk_tx,
|
||||
clk_gtx,
|
||||
rst_stmmaceth,
|
||||
rst_ahb,
|
||||
|
||||
softirq_events: BitmapEvent::new(AtomicWaker::new()),
|
||||
|
||||
dma: OneTimeInit::new(),
|
||||
inner: OneTimeInit::new(),
|
||||
iface_id: OneTimeInit::new(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
243
kernel/driver/net/stmmac/src/regs/dma.rs
Normal file
243
kernel/driver/net/stmmac/src/regs/dma.rs
Normal file
@ -0,0 +1,243 @@
|
||||
use tock_registers::{register_bitfields, register_structs, registers::ReadWrite};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub DMAMR [
|
||||
/// Interrupt mode
|
||||
INTM OFFSET(16) NUMBITS(2) [],
|
||||
/// Transmit priority
|
||||
TXPR OFFSET(11) NUMBITS(1) [],
|
||||
/// Descriptor posted write
|
||||
DSPW OFFSET(8) NUMBITS(1) [],
|
||||
/// Transmit arbitration algorithm
|
||||
TAA OFFSET(2) NUMBITS(3) [],
|
||||
/// Software reset
|
||||
///
|
||||
/// When this bit is set, the MAC and the DMA controllers are reset. This bit is cleared
|
||||
/// automatically when the MAC/DMA reset completes.
|
||||
SWR OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub DMASBMR [
|
||||
/// Enable Low Power interface
|
||||
EN_LPI OFFSET(31) NUMBITS(1) [],
|
||||
/// Unlock on Magic packet or Remote Wakeup packet
|
||||
LPI_XIT_PKT OFFSET(30) NUMBITS(1) [],
|
||||
/// AXI maximum write outstanding request limit
|
||||
///
|
||||
/// Maximum outstanding requests = WR_OSR_LMT + 1
|
||||
WR_OSR_LMT OFFSET(24) NUMBITS(2) [],
|
||||
/// AXI maximum reaad outstanding request limit
|
||||
///
|
||||
/// Maximum outstanding requests = RD_OSR_LMT + 1
|
||||
RD_OSR_LMT OFFSET(16) NUMBITS(2) [],
|
||||
/// 1 Kbyte boundary crossing enable for the AXI master
|
||||
///
|
||||
/// If set, the burst transfers performed by the AXI master do not cross 1 Kbyte boundary
|
||||
/// If not, the transfers do not cross 4 Kbyte boundary
|
||||
ONEKBBE OFFSET(13) NUMBITS(1) [],
|
||||
/// Address aligned bursts
|
||||
///
|
||||
/// If set, the master performs addres-aligned burst transfers on read and write channels
|
||||
AAL OFFSET(12) NUMBITS(1) [],
|
||||
/// Automatic AXI LPI enable
|
||||
///
|
||||
/// When set, enables AXI master to enter into LPI state when there is no activity on the
|
||||
/// Ethernet peripheral for a number of clock cycles programmed in ETH_DMALPIEI register.
|
||||
AALE OFFSET(10) NUMBITS(1) [],
|
||||
/// AXI burst length 256
|
||||
BLEN256 OFFSET(7) NUMBITS(1) [],
|
||||
/// AXI burst length 128
|
||||
BLEN128 OFFSET(6) NUMBITS(1) [],
|
||||
/// AXI burst length 64
|
||||
BLEN64 OFFSET(5) NUMBITS(1) [],
|
||||
/// AXI burst length 32
|
||||
BLEN32 OFFSET(4) NUMBITS(1) [],
|
||||
/// AXI burst length 16
|
||||
BLEN16 OFFSET(3) NUMBITS(1) [],
|
||||
/// AXI burst length 8
|
||||
BLEN8 OFFSET(2) NUMBITS(1) [],
|
||||
/// AXI burst length 4
|
||||
BLEN4 OFFSET(1) NUMBITS(1) [],
|
||||
/// Fixed burst length
|
||||
FB OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub DMACiCR [
|
||||
/// Descriptor skip length
|
||||
DSL OFFSET(18) NUMBITS(1) [],
|
||||
/// 8xPBL mode
|
||||
PBLX8 OFFSET(16) NUMBITS(1) [],
|
||||
/// Maximum segment size
|
||||
MSS OFFSET(0) NUMBITS(14) [],
|
||||
],
|
||||
|
||||
pub DMACiTXCR [
|
||||
/// Tx QOS
|
||||
TQOS OFFSET(24) NUMBITS(4) [],
|
||||
/// Transmit programmable burst length
|
||||
TXPBL OFFSET(16) NUMBITS(6) [],
|
||||
/// Ignore PBL requirement
|
||||
IPBL OFFSET(15) NUMBITS(1) [],
|
||||
/// TCP segmentation enabled
|
||||
TSE OFFSET(12) NUMBITS(1) [],
|
||||
/// Operate on second packet
|
||||
OSF OFFSET(4) NUMBITS(1) [],
|
||||
/// Tx channel weight
|
||||
TCW OFFSET(1) NUMBITS(3) [],
|
||||
/// Start or stop Tx command
|
||||
///
|
||||
/// When set, the DMA checks the transmit list at current position for a packet to be
|
||||
/// transmitted. If the DMA does not own the current descriptor, the transmission enters
|
||||
/// the Suspended state with TBU bit in DMACiSR set.
|
||||
ST OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub DMAC0RXCR [
|
||||
/// DMA Rx channel 0 packet flush
|
||||
///
|
||||
/// When set, the DMA automatically flushes the packet from the Rx queues destined to DMA
|
||||
/// Rx channel 0 when the DMA Rx 0 is stopped after a system bus error.
|
||||
RPF OFFSET(31) NUMBITS(1) [],
|
||||
/// Rx QOS
|
||||
RQOS OFFSET(24) NUMBITS(4) [],
|
||||
/// Rx programmable burst length
|
||||
RXPBL OFFSET(16) NUMBITS(6) [],
|
||||
/// Receive buffer size
|
||||
RBSZ OFFSET(1) NUMBITS(14) [],
|
||||
/// Start or stop Rx
|
||||
///
|
||||
/// When set, the DMA tries to acquire the descriptor from the receive list and process the
|
||||
/// incoming packets.
|
||||
SR OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub DMACiIER [
|
||||
/// Normal interrupt summary enable
|
||||
///
|
||||
/// When this bit is set, the following interrupts are enabled in the Channel i status
|
||||
/// register:
|
||||
///
|
||||
/// * Tx interrupt
|
||||
/// * Tx buffer unavailable
|
||||
/// * Rx interrupt
|
||||
/// * Early Rx interrupt
|
||||
NIE OFFSET(15) NUMBITS(1) [],
|
||||
/// Abnormal interrupt summary enable
|
||||
///
|
||||
/// When this bit is set, the following interrupt summary is enabled:
|
||||
///
|
||||
/// * Tx process stopped
|
||||
/// * Rx buffer unavailable
|
||||
/// * Rx process stopped
|
||||
/// * Rx watchdog timeout
|
||||
/// * Early Tx interrupt
|
||||
/// * Fatal bus error
|
||||
AIE OFFSET(14) NUMBITS(1) [],
|
||||
/// Context descriptor error enable (+AIE)
|
||||
CDEE OFFSET(13) NUMBITS(1) [],
|
||||
/// Fatal bus error enable (+AIE)
|
||||
FBEE OFFSET(12) NUMBITS(1) [],
|
||||
/// Early Rx interrupt enable
|
||||
///
|
||||
/// When this bit is set along with the NIE bit, the Early Rx interrupt is enabled
|
||||
ERIE OFFSET(11) NUMBITS(1) [],
|
||||
/// Early Tx interrupt enable
|
||||
///
|
||||
/// When this bit is set along with the AIE bit, the Early Tx interrupt is enabled
|
||||
ETIE OFFSET(10) NUMBITS(1) [],
|
||||
/// Receive watchdog timeout enable (+AIE)
|
||||
RWTE OFFSET(9) NUMBITS(1) [],
|
||||
/// Rx stopped enable (+AIE)
|
||||
RSE OFFSET(8) NUMBITS(1) [],
|
||||
/// Rx buffer unavailable enable (+AIE)
|
||||
RBUE OFFSET(7) NUMBITS(1) [],
|
||||
/// Rx interrupt enable (+NIE)
|
||||
RIE OFFSET(6) NUMBITS(1) [],
|
||||
/// Tx buffer unavailable enable (+NIE)
|
||||
TBUE OFFSET(2) NUMBITS(1) [],
|
||||
/// Tx stopped enable (+AIE)
|
||||
TXSE OFFSET(1) NUMBITS(1) [],
|
||||
/// Tx interrupt enable (+NIE)
|
||||
TIE OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub DMACiSR [
|
||||
/// Rx DMA error bits
|
||||
REB OFFSET(19) NUMBITS(3) [],
|
||||
/// Tx DMA error bits
|
||||
TEB OFFSET(16) NUMBITS(3) [],
|
||||
/// Normal interrupt summary
|
||||
NIS OFFSET(15) NUMBITS(1) [],
|
||||
/// Abnormal interrupt summary
|
||||
AIS OFFSET(14) NUMBITS(1) [],
|
||||
/// Context descriptor error
|
||||
CDE OFFSET(13) NUMBITS(1) [],
|
||||
/// Fatal bus error
|
||||
FBE OFFSET(12) NUMBITS(1) [],
|
||||
/// Early Rx interrupt
|
||||
ERI OFFSET(11) NUMBITS(1) [],
|
||||
/// Early Tx interrupt
|
||||
ETI OFFSET(10) NUMBITS(1) [],
|
||||
/// Rx watchdog timeout
|
||||
RWT OFFSET(9) NUMBITS(1) [],
|
||||
/// Rx process stopped
|
||||
RPS OFFSET(8) NUMBITS(1) [],
|
||||
/// Rx buffer unavailable. To resume processing Rx descriptors, the driver should change
|
||||
/// the ownership of the descriptors and issue a Rx Poll Demand command. In ring mode, the
|
||||
/// driver should advance the Rx descriptor tail pointer of a channel.
|
||||
RBU OFFSET(7) NUMBITS(1) [],
|
||||
/// Rx interrupt
|
||||
RI OFFSET(6) NUMBITS(1) [],
|
||||
/// Tx buffer unavailable (see RBU, but for Tx)
|
||||
TBU OFFSET(2) NUMBITS(1) [],
|
||||
/// Tx process stopped
|
||||
TPS OFFSET(1) NUMBITS(1) [],
|
||||
/// Tx interrupt
|
||||
TI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub DmaRegs {
|
||||
(0x000 => pub DMAMR: ReadWrite<u32, DMAMR::Register>),
|
||||
(0x004 => pub DMASBMR: ReadWrite<u32, DMASBMR::Register>),
|
||||
(0x008 => pub DMAISR: ReadWrite<u32>),
|
||||
(0x00C => pub DMADSR: ReadWrite<u32>),
|
||||
(0x010 => _0),
|
||||
(0x020 => pub DMAA4TXACR: ReadWrite<u32>),
|
||||
(0x024 => pub DMAA4RXACR: ReadWrite<u32>),
|
||||
(0x028 => pub DMAA4DACR: ReadWrite<u32>),
|
||||
(0x02C => _1),
|
||||
(0x040 => pub DMALPIEI: ReadWrite<u32>),
|
||||
(0x044 => _2),
|
||||
(0x100 => pub DMAC0CR: ReadWrite<u32, DMACiCR::Register>),
|
||||
(0x104 => pub DMAC0TXCR: ReadWrite<u32, DMACiTXCR::Register>),
|
||||
(0x108 => pub DMAC0RXCR: ReadWrite<u32, DMAC0RXCR::Register>),
|
||||
(0x10C => _3),
|
||||
(0x114 => pub DMAC0TXDLAR: ReadWrite<u32>),
|
||||
(0x118 => _4),
|
||||
(0x11C => pub DMAC0RXDLAR: ReadWrite<u32>),
|
||||
(0x120 => pub DMAC0TXDTPR: ReadWrite<u32>),
|
||||
(0x124 => _5),
|
||||
(0x128 => pub DMAC0RXDTPR: ReadWrite<u32>),
|
||||
(0x12C => pub DMAC0TXRLR: ReadWrite<u32>),
|
||||
(0x130 => pub DMAC0RXRLR: ReadWrite<u32>),
|
||||
(0x134 => pub DMAC0IER: ReadWrite<u32, DMACiIER::Register>),
|
||||
(0x138 => pub DMAC0RXIWTR: ReadWrite<u32>),
|
||||
(0x13C => pub DMAC0SFCSR: ReadWrite<u32>),
|
||||
(0x140 => _6),
|
||||
(0x144 => pub DMAC0CATXDR: ReadWrite<u32>),
|
||||
(0x148 => _7),
|
||||
(0x14C => pub DMAC0CARXDR: ReadWrite<u32>),
|
||||
(0x150 => _8),
|
||||
(0x154 => pub DMAC0CATXBR: ReadWrite<u32>),
|
||||
(0x158 => _9),
|
||||
(0x15C => pub DMAC0CARXBR: ReadWrite<u32>),
|
||||
(0x160 => pub DMAC0SR: ReadWrite<u32, DMACiSR::Register>),
|
||||
(0x164 => _10),
|
||||
(0x16C => pub DMAC0MFCR: ReadWrite<u32>),
|
||||
(0x170 => _11),
|
||||
(0x200 => @END),
|
||||
}
|
||||
}
|
425
kernel/driver/net/stmmac/src/regs/mac.rs
Normal file
425
kernel/driver/net/stmmac/src/regs/mac.rs
Normal file
@ -0,0 +1,425 @@
|
||||
use tock_registers::{
|
||||
register_bitfields, register_structs,
|
||||
registers::{ReadOnly, ReadWrite},
|
||||
};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub MACCR [
|
||||
/// * When set: MAC recognizes ARP requests, responds with ARP responses.
|
||||
/// * When clear: no additional ARP logic, frames are indicates as Type
|
||||
/// frame in the RxStatus.
|
||||
ARPEN OFFSET(31) NUMBITS(1) [],
|
||||
/// Source address insertion or replacement setting.
|
||||
SARC OFFSET(28) NUMBITS(3) [],
|
||||
/// IP checksum offload.
|
||||
///
|
||||
/// When set, enables IPv4 header checksum checking, IPv4, IPv6 TCP+UDP and ICMP
|
||||
/// payload checksum checking.
|
||||
IPC OFFSET(27) NUMBITS(1) [],
|
||||
/// Inter-packet gap setting.
|
||||
IPG OFFSET(24) NUMBITS(1) [],
|
||||
/// Giant packet size limit control enable.
|
||||
GPSCLE OFFSET(23) NUMBITS(1) [],
|
||||
/// IEEE 802.3as support for 2K packets.
|
||||
S2KP OFFSET(22) NUMBITS(1) [],
|
||||
/// CRC stripping for Type packets.
|
||||
CST OFFSET(21) NUMBITS(1) [],
|
||||
/// Automatic pad or CRC stripping.
|
||||
ACS OFFSET(20) NUMBITS(1) [],
|
||||
/// Watchdog disable.
|
||||
///
|
||||
/// When set, disables the watchdog timer on the receiver. The MAC can receive packets
|
||||
/// of up to 16383 bytes.
|
||||
/// When clear, the MAC does not allow packets of more than 2048 bytes (or 10240 with
|
||||
/// JE).
|
||||
WD OFFSET(19) NUMBITS(1) [],
|
||||
/// Packet burst enable.
|
||||
BE OFFSET(18) NUMBITS(1) [],
|
||||
/// Jabber disable.
|
||||
///
|
||||
/// When set, disables the jabber timer on the transmitter. The MAC can send packets of
|
||||
/// up to 16383 bytes.
|
||||
JD OFFSET(17) NUMBITS(1) [],
|
||||
/// Jumbo packet enable.
|
||||
JE OFFSET(16) NUMBITS(1) [],
|
||||
/// Port select
|
||||
PS OFFSET(15) NUMBITS(1) [
|
||||
Ps1000Mbps = 0,
|
||||
Ps10Or100Mbps = 1,
|
||||
],
|
||||
/// MAC speed
|
||||
FES OFFSET(14) NUMBITS(1) [
|
||||
Fes10Mbps = 0,
|
||||
Fes100Mbps = 1,
|
||||
],
|
||||
/// Duplex mode
|
||||
DM OFFSET(13) NUMBITS(1) [
|
||||
HalfDuplex = 0,
|
||||
FullDuplex = 1,
|
||||
],
|
||||
/// Loopback mode. When set, GMII output is looped back into input. The GMII RX clock
|
||||
/// input is required for the loopback to work, because the GMII TX clock is not
|
||||
/// looped back internally.
|
||||
LM OFFSET(12) NUMBITS(1) [],
|
||||
/// Enable carrier-sense before transmission in Full-Duplex mode
|
||||
ECRSFD OFFSET(11) NUMBITS(1) [],
|
||||
/// Disable receive own
|
||||
DO OFFSET(10) NUMBITS(1) [],
|
||||
/// Disable carrier-sense during transmission
|
||||
DCRS OFFSET(9) NUMBITS(1) [],
|
||||
/// Disable retry
|
||||
DR OFFSET(8) NUMBITS(1) [],
|
||||
/// Backoff limit
|
||||
BL OFFSET(5) NUMBITS(2) [
|
||||
/// k = min(n, 10)
|
||||
BackoffMin10 = 0,
|
||||
/// k = min(n, 8)
|
||||
BackoffMin8 = 1,
|
||||
/// k = min(n, 4)
|
||||
BackoffMin4 = 2,
|
||||
/// k = min(n, 1)
|
||||
BackoffMin1 = 3,
|
||||
],
|
||||
/// Deferral check
|
||||
DC OFFSET(4) NUMBITS(1) [],
|
||||
/// Preamble length
|
||||
PRELEN OFFSET(2) NUMBITS(2) [
|
||||
Pre7b = 0,
|
||||
Pre5b = 1,
|
||||
Pre3b = 2,
|
||||
],
|
||||
/// Transmitter enable
|
||||
TE OFFSET(1) NUMBITS(1) [],
|
||||
/// Receiver enable
|
||||
RE OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACPFR [
|
||||
/// Receive all. When set, the MAC receiver passes all received packets to the application,
|
||||
/// whether they pass the address filter or not.
|
||||
RA OFFSET(31) NUMBITS(1) [],
|
||||
/// Drop Non-TCP/UDP over IP packets
|
||||
DNTU OFFSET(21) NUMBITS(1) [],
|
||||
/// Layer 3 and 4 filter enable
|
||||
IPFE OFFSET(20) NUMBITS(1) [],
|
||||
/// VLAN filter enable
|
||||
VTFE OFFSET(16) NUMBITS(1) [],
|
||||
/// Hash or perfect filter
|
||||
HPF OFFSET(10) NUMBITS(1) [],
|
||||
/// Source address filter
|
||||
SAF OFFSET(9) NUMBITS(1) [],
|
||||
/// SA inverse filtering
|
||||
SAIF OFFSET(8) NUMBITS(1) [],
|
||||
/// Pass control checks
|
||||
PCF OFFSET(6) NUMBITS(2) [],
|
||||
/// Disable broadcast packets
|
||||
DBF OFFSET(5) NUMBITS(1) [],
|
||||
/// Pass all multicast
|
||||
PM OFFSET(4) NUMBITS(1) [],
|
||||
/// DA inverse filtering
|
||||
DAIF OFFSET(3) NUMBITS(1) [],
|
||||
/// Hash multicast
|
||||
HMC OFFSET(2) NUMBITS(1) [],
|
||||
/// Hash unicast
|
||||
HUC OFFSET(1) NUMBITS(1) [],
|
||||
/// Promiscuous mode
|
||||
PR OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACQ0TXFCR [
|
||||
/// Pause time
|
||||
PT OFFSET(16) NUMBITS(16) [],
|
||||
/// Disable zero-quanta pause
|
||||
DZPQ OFFSET(7) NUMBITS(1) [],
|
||||
/// Pause low threshold
|
||||
PLT OFFSET(4) NUMBITS(3) [],
|
||||
/// Transmit flow control enable
|
||||
TFE OFFSET(1) NUMBITS(1) [],
|
||||
/// Flow control busy or backpressure activate
|
||||
FCB_BPA OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACRXFCR [
|
||||
/// Unicast packet detect
|
||||
UP OFFSET(1) NUMBITS(1) [],
|
||||
/// Rx flow control enable
|
||||
RFE OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACRXQC0R [
|
||||
/// Rx queue 1 enable
|
||||
RXQ1EN OFFSET(2) NUMBITS(2) [
|
||||
Disable = 0b00,
|
||||
EnableAV = 0b01,
|
||||
Enable = 0b10,
|
||||
],
|
||||
/// Rx queue 0 enable
|
||||
RXQ0EN OFFSET(0) NUMBITS(2) [
|
||||
Disable = 0b00,
|
||||
EnableAV = 0b01,
|
||||
Enable = 0b10,
|
||||
],
|
||||
],
|
||||
|
||||
pub MACISR [
|
||||
/// Rx status interrupt
|
||||
RXSTSIS OFFSET(14) NUMBITS(1) [],
|
||||
/// Tx status interrupt
|
||||
TXSTSIS OFFSET(13) NUMBITS(1) [],
|
||||
/// Timestamp interrupt
|
||||
TSIS OFFSET(12) NUMBITS(1) [],
|
||||
/// MMC Tx interrupt
|
||||
MMCTXIS OFFSET(10) NUMBITS(1) [],
|
||||
/// MMC Rx interrupt
|
||||
MMCRXIS OFFSET(9) NUMBITS(1) [],
|
||||
/// MMC interrupt
|
||||
MMCIS OFFSET(8) NUMBITS(1) [],
|
||||
/// LPI interrupt
|
||||
LPIIS OFFSET(5) NUMBITS(1) [],
|
||||
/// PMT interrupt
|
||||
PMTIS OFFSET(4) NUMBITS(1) [],
|
||||
/// PHY interrupt
|
||||
PHYIS OFFSET(3) NUMBITS(1) [],
|
||||
/// RGMII interrupt
|
||||
RGSMIIIS OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACIER [
|
||||
/// Rx status interrupt enable
|
||||
RXSTSIE OFFSET(14) NUMBITS(1) [],
|
||||
/// Tx status interrupt enable
|
||||
TXSTSIE OFFSET(13) NUMBITS(1) [],
|
||||
/// Timestamp interrupt enable
|
||||
TSIE OFFSET(12) NUMBITS(1) [],
|
||||
/// LPI interrupt enable
|
||||
LPIIE OFFSET(5) NUMBITS(1) [],
|
||||
/// PMT interrupt enable
|
||||
PMTIE OFFSET(4) NUMBITS(1) [],
|
||||
/// PHY interrupt enable
|
||||
PHYIE OFFSET(3) NUMBITS(1) [],
|
||||
/// RGMII interrupt enable
|
||||
RGSMIIIE OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACPHYCSR [
|
||||
/// Link status
|
||||
LNKSTS OFFSET(19) NUMBITS(1) [
|
||||
Down = 0,
|
||||
Up = 1,
|
||||
],
|
||||
/// Link speed
|
||||
LNKSPEED OFFSET(17) NUMBITS(2) [
|
||||
Speed2_5MHz = 0b00,
|
||||
Speed25MHz = 0b01,
|
||||
Speed125MHz = 0b10,
|
||||
],
|
||||
/// Link mode
|
||||
LNKMOD OFFSET(16) NUMBITS(1) [
|
||||
HalfDuplex = 0,
|
||||
FullDuplex = 1,
|
||||
],
|
||||
/// Link up or down
|
||||
///
|
||||
/// This bit indicates whether the link is up or down during transmission of configuration
|
||||
/// in the RGMII interface
|
||||
LUD OFFSET(1) NUMBITS(1) [
|
||||
Down = 0,
|
||||
Up = 1,
|
||||
],
|
||||
/// Transmit configuration in RGMII
|
||||
TC OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACMDIOAR [
|
||||
/// Preamble suppress enable
|
||||
///
|
||||
/// If set, SMA suppresses the 32-bit preamble and transmits MDIO frames with only 1
|
||||
/// preamble bit
|
||||
PSE OFFSET(27) NUMBITS(1) [],
|
||||
/// Back to back transactions
|
||||
///
|
||||
/// If set and the NTC has value greater than 0, the MAC informs the completion of a read
|
||||
/// or write command at the end of frame transfer. Must not be set with NTC=0.
|
||||
BTB OFFSET(26) NUMBITS(1) [],
|
||||
/// Physical layer address
|
||||
///
|
||||
/// In Clause 22, indicates a PHY device the MAC is addressing.
|
||||
PA OFFSET(21) NUMBITS(5) [],
|
||||
/// Register/Device address
|
||||
///
|
||||
/// In Clause 22, selects the PHY register
|
||||
RDA OFFSET(16) NUMBITS(5) [],
|
||||
/// Number of trailing clocks
|
||||
NTC OFFSET(12) NUMBITS(3) [],
|
||||
/// CSR clock range
|
||||
CR OFFSET(8) NUMBITS(4) [
|
||||
CSR_20_TO_35_MHZ = 0b0010,
|
||||
],
|
||||
/// Skip address packet
|
||||
///
|
||||
/// If set, the SMA does not send the address packets before read, write or post-read
|
||||
/// increment address packets. Only valid with C45E set.
|
||||
SKAP OFFSET(4) NUMBITS(1) [],
|
||||
/// GMII operation command
|
||||
GOC OFFSET(2) NUMBITS(2) [
|
||||
Write = 0b01,
|
||||
C45EPostReadIncrement = 0b10,
|
||||
Read = 0b11
|
||||
],
|
||||
/// Clause 45 PHY enable
|
||||
C45E OFFSET(1) NUMBITS(1) [],
|
||||
/// GMII busy
|
||||
GB OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MACMDIODR [
|
||||
/// Register address
|
||||
///
|
||||
/// Specifies the PHY register when used with Clause 45
|
||||
RA OFFSET(16) NUMBITS(16) [],
|
||||
/// GMII data
|
||||
GD OFFSET(0) NUMBITS(16) [],
|
||||
],
|
||||
|
||||
pub MACAiHR [
|
||||
/// MAC address enable bit. Should always be set to 1 for MAC address 0.
|
||||
AE OFFSET(31) NUMBITS(1) [],
|
||||
ADDR5 OFFSET(8) NUMBITS(8) [],
|
||||
ADDR4 OFFSET(0) NUMBITS(8) [],
|
||||
],
|
||||
pub MACAiLR [
|
||||
ADDR3 OFFSET(24) NUMBITS(8) [],
|
||||
ADDR2 OFFSET(16) NUMBITS(8) [],
|
||||
ADDR1 OFFSET(8) NUMBITS(8) [],
|
||||
ADDR0 OFFSET(0) NUMBITS(8) [],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub MacRegs {
|
||||
/// Operating mode configuration register
|
||||
///
|
||||
/// Reset value: 0x0000 8000
|
||||
///
|
||||
/// Established the operating mode of the MAC.
|
||||
(0x000 => pub MACCR: ReadWrite<u32, MACCR::Register>),
|
||||
/// Extended operating mode configuration register
|
||||
///
|
||||
/// XXX
|
||||
(0x004 => pub MACECR: ReadWrite<u32>),
|
||||
/// Packet filtering control register
|
||||
(0x008 => pub MACPFR: ReadWrite<u32, MACPFR::Register>),
|
||||
/// Watchdog timeout register
|
||||
(0x00C => pub MACWTR: ReadWrite<u32>),
|
||||
/// Hash table 0 register
|
||||
(0x010 => pub MACHT0R: ReadWrite<u32>),
|
||||
/// Hash table 1 register
|
||||
(0x014 => pub MACHT1R: ReadWrite<u32>),
|
||||
(0x018 => _0),
|
||||
/// VLAN tag register
|
||||
(0x050 => pub MACVTR: ReadWrite<u32>),
|
||||
(0x054 => _1),
|
||||
/// VLAN hash table register
|
||||
(0x058 => pub MACVHTR: ReadWrite<u32>),
|
||||
(0x05C => _2),
|
||||
/// VLAN inclusion register
|
||||
(0x060 => pub MACVIR: ReadWrite<u32>),
|
||||
/// Inner VLAN inclusion register
|
||||
(0x064 => pub MACIVIR: ReadWrite<u32>),
|
||||
(0x068 => _3),
|
||||
/// Tx queue 0 flow control register
|
||||
(0x070 => pub MACQ0TXFCR: ReadWrite<u32, MACQ0TXFCR::Register>),
|
||||
(0x074 => _4),
|
||||
/// Rx flow control register
|
||||
(0x090 => pub MACRXFCR: ReadWrite<u32, MACRXFCR::Register>),
|
||||
/// Rx queue control register
|
||||
(0x094 => pub MACRXQCR: ReadWrite<u32>),
|
||||
(0x098 => _5),
|
||||
/// Rx queue control 0 register
|
||||
(0x0A0 => pub MACRXQC0R: ReadWrite<u32, MACRXQC0R::Register>),
|
||||
/// Rx queue control 1 register
|
||||
(0x0A4 => pub MACRXQC1R: ReadWrite<u32>),
|
||||
/// Rx queue control 2 register
|
||||
(0x0A8 => pub MACRXQC2R: ReadWrite<u32>),
|
||||
(0x0AC => _6),
|
||||
/// Interrupt status register
|
||||
(0x0B0 => pub MACISR: ReadWrite<u32, MACISR::Register>),
|
||||
/// Interrupt enable register
|
||||
(0x0B4 => pub MACIER: ReadWrite<u32, MACIER::Register>),
|
||||
/// Rx/Tx status register
|
||||
(0x0B8 => pub MACRXTXSR: ReadWrite<u32>),
|
||||
(0x0BC => _7),
|
||||
/// PMT control status register
|
||||
(0x0C0 => pub MACPCSR: ReadWrite<u32>),
|
||||
/// Remote wakeup packet filter register
|
||||
(0x0C4 => pub MACRWKPFR: ReadWrite<u32>),
|
||||
(0x0C8 => _8),
|
||||
/// LPI control and status register
|
||||
(0x0D0 => pub MACLCSR: ReadWrite<u32>),
|
||||
/// LPI timers control register
|
||||
(0x0D4 => pub MACLTCR: ReadWrite<u32>),
|
||||
/// Tx LPI entry timer
|
||||
(0x0D8 => pub MACLETR: ReadWrite<u32>),
|
||||
/// One-microsecond-tick counter register
|
||||
(0x0DC => pub MAC1USTCR: ReadWrite<u32>),
|
||||
(0x0E0 => _9),
|
||||
/// PHYIF control and status register
|
||||
(0x0F8 => pub MACPHYCSR: ReadWrite<u32, MACPHYCSR::Register>),
|
||||
(0x0FC => _10),
|
||||
/// Version register
|
||||
(0x110 => pub MACVR: ReadOnly<u32>),
|
||||
/// Debug register
|
||||
(0x114 => pub MACDR: ReadOnly<u32>),
|
||||
(0x118 => _11),
|
||||
/// HW feature 0 register
|
||||
(0x11C => pub MACHWF0R: ReadWrite<u32>),
|
||||
/// HW feature 1 register
|
||||
(0x120 => pub MACHWF1R: ReadWrite<u32>),
|
||||
/// HW feature 2 register
|
||||
(0x124 => pub MACHWF2R: ReadWrite<u32>),
|
||||
/// HW feature 3 register
|
||||
(0x128 => pub MACHWF3R: ReadWrite<u32>),
|
||||
(0x12C => _12),
|
||||
/// MDIO address register
|
||||
(0x200 => pub MACMDIOAR: ReadWrite<u32, MACMDIOAR::Register>),
|
||||
/// MDIO data register
|
||||
(0x204 => pub MACMDIODR: ReadWrite<u32, MACMDIODR::Register>),
|
||||
(0x208 => _13),
|
||||
/// ARP address register
|
||||
(0x210 => pub MACARPAR: ReadWrite<u32>),
|
||||
(0x214 => _14),
|
||||
/// CSR software control register
|
||||
(0x230 => pub MACCSRSWCR: ReadWrite<u32>),
|
||||
(0x234 => _15),
|
||||
/// MAC address 0 high register
|
||||
(0x300 => pub MACA0HR: ReadWrite<u32, MACAiHR::Register>),
|
||||
/// MAC address 0 low register
|
||||
(0x304 => pub MACA0LR: ReadWrite<u32, MACAiLR::Register>),
|
||||
/// MAC address 1 high register
|
||||
(0x308 => pub MACA1HR: ReadWrite<u32>),
|
||||
/// MAC address 1 low register
|
||||
(0x30C => pub MACA1LR: ReadWrite<u32>),
|
||||
/// MAC address 2 high register
|
||||
(0x310 => pub MACA2HR: ReadWrite<u32>),
|
||||
/// MAC address 2 low register
|
||||
(0x314 => pub MACA2LR: ReadWrite<u32>),
|
||||
/// MAC address 3 high register
|
||||
(0x318 => pub MACA3HR: ReadWrite<u32>),
|
||||
/// MAC address 3 low register
|
||||
(0x31C => pub MACA3LR: ReadWrite<u32>),
|
||||
(0x320 => _16),
|
||||
/// MMC control register
|
||||
(0x700 => pub MMC_CONTROL: ReadWrite<u32>),
|
||||
/// MMC Rx interrupt register
|
||||
(0x704 => pub MMC_RX_INTERRUPT: ReadWrite<u32>),
|
||||
/// MMC Tx interrupt register
|
||||
(0x708 => pub MMC_TX_INTERRUPT: ReadWrite<u32>),
|
||||
/// MMC Rx interrupt mask register
|
||||
(0x70C => pub MMC_RX_INTERRUPT_MASK: ReadWrite<u32>),
|
||||
/// MMC Tx interrupt mask register
|
||||
(0x710 => pub MMC_TX_INTERRUPT_MASK: ReadWrite<u32>),
|
||||
(0x714 => _17),
|
||||
(0xC00 => @END),
|
||||
}
|
||||
}
|
61
kernel/driver/net/stmmac/src/regs/mod.rs
Normal file
61
kernel/driver/net/stmmac/src/regs/mod.rs
Normal file
@ -0,0 +1,61 @@
|
||||
#![allow(non_snake_case)]
|
||||
|
||||
use core::time::Duration;
|
||||
|
||||
use libk::{error::Error, task::runtime::pwait};
|
||||
use mac::{MACMDIOAR, MACMDIODR};
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
};
|
||||
use ygg_driver_net_core::ephy::MdioBus;
|
||||
|
||||
pub mod dma;
|
||||
pub mod mac;
|
||||
pub mod mtl;
|
||||
|
||||
register_structs! {
|
||||
pub Regs {
|
||||
(0x0000 => pub MAC: mac::MacRegs),
|
||||
(0x0C00 => pub MTL: mtl::MtlRegs),
|
||||
(0x1000 => pub DMA: dma::DmaRegs),
|
||||
(0x1200 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
impl Regs {
|
||||
fn mdio_wait_busy(&self) -> Result<(), Error> {
|
||||
pwait(Duration::from_millis(50), Duration::from_millis(1), || {
|
||||
self.MAC.MACMDIOAR.matches_all(MACMDIOAR::GB::CLEAR)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl MdioBus for Regs {
|
||||
fn mii_read(&self, phyaddr: u8, regaddr: u8) -> Result<u16, Error> {
|
||||
self.mdio_wait_busy()?;
|
||||
self.MAC.MACMDIOAR.write(
|
||||
MACMDIOAR::GOC::Read
|
||||
+ MACMDIOAR::CR.val(1)
|
||||
+ MACMDIOAR::PA.val(phyaddr as u32)
|
||||
+ MACMDIOAR::RDA.val(regaddr as u32)
|
||||
+ MACMDIOAR::GB::SET,
|
||||
);
|
||||
self.mdio_wait_busy()?;
|
||||
Ok(self.MAC.MACMDIODR.read(MACMDIODR::GD) as u16)
|
||||
}
|
||||
|
||||
fn mii_write(&self, phyaddr: u8, regaddr: u8, value: u16) -> Result<(), Error> {
|
||||
self.mdio_wait_busy()?;
|
||||
self.MAC.MACMDIODR.write(MACMDIODR::GD.val(value as u32));
|
||||
self.MAC.MACMDIOAR.write(
|
||||
MACMDIOAR::GOC::Write
|
||||
+ MACMDIOAR::CR.val(1)
|
||||
+ MACMDIOAR::PA.val(phyaddr as u32)
|
||||
+ MACMDIOAR::RDA.val(regaddr as u32)
|
||||
+ MACMDIOAR::GB::SET,
|
||||
);
|
||||
self.mdio_wait_busy()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
132
kernel/driver/net/stmmac/src/regs/mtl.rs
Normal file
132
kernel/driver/net/stmmac/src/regs/mtl.rs
Normal file
@ -0,0 +1,132 @@
|
||||
use tock_registers::{register_bitfields, register_structs, registers::ReadWrite};
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
pub MTLOMR [
|
||||
CNTCLR OFFSET(9) NUMBITS(1) [],
|
||||
CNTPRST OFFSET(8) NUMBITS(1) [],
|
||||
SCHALG OFFSET(5) NUMBITS(2) [
|
||||
AlgWeightedRR = 0b00,
|
||||
AlgStrictPrio = 0b11,
|
||||
],
|
||||
RAA OFFSET(2) NUMBITS(1) [
|
||||
AlgStrictPrio = 0,
|
||||
AlgWeightedPrio = 1
|
||||
],
|
||||
DTXSTS OFFSET(1) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MTLTXQiOMR [
|
||||
TQS OFFSET(16) NUMBITS(4) [],
|
||||
TTC OFFSET(4) NUMBITS(3) [],
|
||||
TXQEN OFFSET(2) NUMBITS(2) [
|
||||
Disable = 0b00,
|
||||
EnableAv = 0b01,
|
||||
Enable = 0b11,
|
||||
],
|
||||
TSF OFFSET(1) NUMBITS(1) [],
|
||||
FTQ OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
|
||||
pub MTLRXQiOMR [
|
||||
RQS OFFSET(20) NUMBITS(4) [],
|
||||
/// Threshold for deactivating flow control
|
||||
RFD OFFSET(14) NUMBITS(3) [
|
||||
FullMinus1K = 0,
|
||||
FullMinus1_5K = 1,
|
||||
FullMinus2K = 2,
|
||||
FullMinus2_5K = 3,
|
||||
FullMinus3K = 4,
|
||||
FullMinus3_5K = 5
|
||||
],
|
||||
/// Threshold for activating flow control. See [RFD].
|
||||
RFA OFFSET(8) NUMBITS(3) [],
|
||||
/// Enable hardware flow control. When set, the flow control signal operation, based on the
|
||||
/// fill-level of the Rx queue, is enabled.
|
||||
EHFC OFFSET(7) NUMBITS(1) [],
|
||||
/// Disable dropping of TCP/IP checksum error packets
|
||||
DIS_TCP_EF OFFSET(6) NUMBITS(1) [],
|
||||
/// Receive queue store and forward. When set, the Ethernet peripheral reads a packet from
|
||||
/// the Rx queue only after the complete packet has been written to it, ignoring the RTC
|
||||
/// field.
|
||||
RSF OFFSET(5) NUMBITS(1) [],
|
||||
/// Forward error packets. When set, the Rx queue does not drop the packets with CRC
|
||||
/// errors, watchdog timeouts, overflows and receive errors.
|
||||
FEP OFFSET(4) NUMBITS(1) [],
|
||||
/// Forward undersized good packets. When set, the Rx queue does not drop packets with no
|
||||
/// errors, but with a length of less than 64 bytes.
|
||||
FUP OFFSET(3) NUMBITS(1) [],
|
||||
/// Rx threshold level
|
||||
RTC OFFSET(0) NUMBITS(2) [
|
||||
Threshold64 = 0b00,
|
||||
Threshold32 = 0b01,
|
||||
Threshold96 = 0b10,
|
||||
Threshold128 = 0b11,
|
||||
],
|
||||
],
|
||||
}
|
||||
|
||||
register_structs! {
|
||||
pub MtlRegs {
|
||||
/// MTL operating mode register
|
||||
(0x000 => pub MTLOMR: ReadWrite<u32, MTLOMR::Register>),
|
||||
(0x004 => _0),
|
||||
/// Interrupt status register
|
||||
(0x020 => pub MTLISR: ReadWrite<u32>),
|
||||
(0x024 => _1),
|
||||
/// Tx queue 0 operating mode register
|
||||
(0x100 => pub MTLTXQ0OMR: ReadWrite<u32, MTLTXQiOMR::Register>),
|
||||
/// Tx queue 0 underflow register
|
||||
(0x104 => pub MTLTXQ0UR: ReadWrite<u32>),
|
||||
/// Tx queue 0 debug register
|
||||
(0x108 => pub MTLTXQ0DR: ReadWrite<u32>),
|
||||
(0x10C => _2),
|
||||
/// Tx queue 0 ETS status register
|
||||
(0x114 => pub MTLTXQ0ESR: ReadWrite<u32>),
|
||||
/// Tx queue 0 quantum weight register
|
||||
(0x118 => pub MTLTXQ0QWR: ReadWrite<u32>),
|
||||
(0x11C => _3),
|
||||
/// Queue 0 interrupt control status register
|
||||
(0x12C => pub MTLQ0ICSR: ReadWrite<u32>),
|
||||
/// Rx queue 0 operating mode register
|
||||
(0x130 => pub MTLRXQ0OMR: ReadWrite<u32, MTLRXQiOMR::Register>),
|
||||
/// Rx queue 0 missed packet and overflow counter register
|
||||
(0x134 => pub MTLRXQ0MPOCR: ReadWrite<u32>),
|
||||
/// Rx queue 0 debug register
|
||||
(0x138 => pub MTLRXQ0DR: ReadWrite<u32>),
|
||||
/// Rx queue 0 control register
|
||||
(0x13C => pub MTLRXQ0CR: ReadWrite<u32>),
|
||||
/// Tx queue 1 operating mode register
|
||||
(0x140 => pub MTLTXQ1OMR: ReadWrite<u32>),
|
||||
/// Tx queue 1 underflow register
|
||||
(0x144 => pub MTLTXQ1UR: ReadWrite<u32>),
|
||||
/// Tx queue 1 debug register
|
||||
(0x148 => pub MTLTXQ1DR: ReadWrite<u32>),
|
||||
(0x14C => _4),
|
||||
/// Tx queue 1 ETS control register
|
||||
(0x150 => pub MTLTXQ1ECR: ReadWrite<u32>),
|
||||
/// Tx queue 1 ETS status register
|
||||
(0x154 => pub MTLTXQ1ESR: ReadWrite<u32>),
|
||||
/// Tx queue 1 quantum weight register
|
||||
(0x158 => pub MTLTXQ1QWR: ReadWrite<u32>),
|
||||
/// Tx queue 1 send slope credit register
|
||||
(0x15C => pub MTLTXQ1SSCR: ReadWrite<u32>),
|
||||
/// Tx queue 1 hiCredit register
|
||||
(0x160 => pub MTLTXQ1HCR: ReadWrite<u32>),
|
||||
/// Tx queue 1 loCredit register
|
||||
(0x164 => pub MTLTXQ1LCR: ReadWrite<u32>),
|
||||
(0x168 => _5),
|
||||
/// Queue 1 interrupt control status register
|
||||
(0x16C => pub MTLQ1ICSR: ReadWrite<u32>),
|
||||
/// Rx queue 1 operating mode register
|
||||
(0x170 => pub MTLRXQ1OMR: ReadWrite<u32>),
|
||||
/// Rx queue 1 missed packet and overflow counter register
|
||||
(0x174 => pub MTLRXQ1MPOCR: ReadWrite<u32>),
|
||||
/// Rx queue 1 debug register
|
||||
(0x178 => pub MTLRXQ1DR: ReadWrite<u32>),
|
||||
/// Rx queue 1 control register
|
||||
(0x17C => pub MTLRXQ1CR: ReadWrite<u32>),
|
||||
(0x180 => _6),
|
||||
(0x400 => @END),
|
||||
}
|
||||
}
|
254
kernel/driver/net/stmmac/src/ring.rs
Normal file
254
kernel/driver/net/stmmac/src/ring.rs
Normal file
@ -0,0 +1,254 @@
|
||||
use core::mem::{self, MaybeUninit};
|
||||
|
||||
use alloc::vec::Vec;
|
||||
use device_api::dma::DmaAllocator;
|
||||
use libk::{
|
||||
dma::{BusAddress, DmaBuffer},
|
||||
error::Error,
|
||||
};
|
||||
|
||||
pub struct TxRing {
|
||||
entries: DmaBuffer<[TxDescriptor]>,
|
||||
buffers: Vec<Option<DmaBuffer<[u8]>>>,
|
||||
|
||||
wr: usize,
|
||||
rd: usize,
|
||||
}
|
||||
|
||||
pub struct RxRing {
|
||||
entries: DmaBuffer<[RxDescriptor]>,
|
||||
buffers: Vec<DmaBuffer<[MaybeUninit<u8>]>>,
|
||||
|
||||
rd: usize,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct TxDescriptor {
|
||||
tdes0: u32,
|
||||
tdes1: u32,
|
||||
tdes2: u32,
|
||||
tdes3: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct RxDescriptor {
|
||||
rdes0: u32,
|
||||
rdes1: u32,
|
||||
rdes2: u32,
|
||||
rdes3: u32,
|
||||
}
|
||||
|
||||
impl TxRing {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let entries = DmaBuffer::new_slice(dma, TxDescriptor::empty(), capacity)?;
|
||||
let buffers = (0..capacity).map(|_| None).collect();
|
||||
Ok(Self {
|
||||
entries,
|
||||
buffers,
|
||||
|
||||
wr: 0,
|
||||
rd: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn buffer_base(&self) -> BusAddress {
|
||||
self.entries.bus_address()
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.entries.len()
|
||||
}
|
||||
|
||||
pub fn can_xmit(&self) -> bool {
|
||||
self.wr.wrapping_add(1) != self.rd
|
||||
}
|
||||
|
||||
pub fn outstanding_tx(&self) -> bool {
|
||||
self.wr != self.rd
|
||||
}
|
||||
|
||||
pub fn push_xmit(&mut self, frame: DmaBuffer<[u8]>) -> Result<usize, Error> {
|
||||
if !self.can_xmit() {
|
||||
return Err(Error::WouldBlock);
|
||||
}
|
||||
|
||||
let address = frame.bus_address();
|
||||
let frame_len = frame.len();
|
||||
|
||||
let capacity = self.entries.len();
|
||||
let index = self.wr % capacity;
|
||||
assert!(self.buffers[index].is_none());
|
||||
|
||||
frame.cache_flush_all(true);
|
||||
self.entries[index].setup_tx(address, frame_len, true)?;
|
||||
self.entries.cache_flush_element(index, true);
|
||||
|
||||
self.buffers[index] = Some(frame);
|
||||
self.wr = self.wr.wrapping_add(1);
|
||||
|
||||
Ok(self.wr % self.capacity())
|
||||
}
|
||||
|
||||
pub fn consume(&mut self) -> Result<usize, Error> {
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
let index = self.rd % self.entries.len();
|
||||
let entry = &self.entries[index];
|
||||
|
||||
if self.rd == self.wr {
|
||||
break;
|
||||
}
|
||||
|
||||
if let Some(status) = entry.tx_status() {
|
||||
if status != 0 {
|
||||
log::warn!("tx_ring[{index}] error: {status:#x}");
|
||||
}
|
||||
let _ = self.buffers[index].take().unwrap();
|
||||
self.rd = self.rd.wrapping_add(1);
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl TxDescriptor {
|
||||
const TDES3_OWN: u32 = 1 << 31;
|
||||
const TDES3_FD: u32 = 1 << 29;
|
||||
const TDES3_LD: u32 = 1 << 28;
|
||||
const TDES2_IOC: u32 = 1 << 31;
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
tdes0: 0,
|
||||
tdes1: 0,
|
||||
tdes2: 0,
|
||||
tdes3: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn tx_status(&self) -> Option<u32> {
|
||||
if self.tdes3 & Self::TDES3_OWN == 0 {
|
||||
Some(self.tdes3 & !(0xFFFF << 16))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_tx(
|
||||
&mut self,
|
||||
frame: BusAddress,
|
||||
frame_len: usize,
|
||||
ioc: bool,
|
||||
) -> Result<(), Error> {
|
||||
let tdes0 = frame.try_into_u32().map_err(|_| Error::InvalidArgument)?;
|
||||
if frame_len & !0x3FFF != 0 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let mut tdes2 = frame_len as u32;
|
||||
if ioc {
|
||||
tdes2 |= Self::TDES2_IOC;
|
||||
}
|
||||
let tdes3 = Self::TDES3_OWN | Self::TDES3_FD | Self::TDES3_LD;
|
||||
|
||||
self.tdes0 = tdes0;
|
||||
self.tdes1 = 0;
|
||||
self.tdes2 = tdes2;
|
||||
self.tdes3 = tdes3;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl RxRing {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let mut entries = DmaBuffer::new_slice(dma, RxDescriptor::empty(), capacity)?;
|
||||
let buffers = (0..capacity)
|
||||
.map(|_| DmaBuffer::new_uninit_slice(dma, 4096))
|
||||
.collect::<Result<Vec<_>, _>>()?;
|
||||
for i in 0..capacity {
|
||||
entries[i].setup_rx(buffers[i].bus_address(), true)?;
|
||||
}
|
||||
Ok(Self {
|
||||
buffers,
|
||||
entries,
|
||||
|
||||
rd: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn buffer_base(&self) -> BusAddress {
|
||||
self.entries.bus_address()
|
||||
}
|
||||
|
||||
pub fn consume<F: Fn(DmaBuffer<[u8]>)>(
|
||||
&mut self,
|
||||
dma: &dyn DmaAllocator,
|
||||
packet_handler: F,
|
||||
) -> Result<usize, Error> {
|
||||
let mut count = 0;
|
||||
|
||||
loop {
|
||||
let index = self.rd % self.entries.len();
|
||||
let entry = &mut self.entries[index];
|
||||
|
||||
if let Some(_) = entry.rx_completed() {
|
||||
// Grab the current buffer (the one just written to by the DMA), replace it
|
||||
// with the newly allocated one, and mark the descriptor as DMA-owned again
|
||||
let new_buffer = DmaBuffer::new_uninit_slice(dma, 4096)?;
|
||||
let new_buffer_address = new_buffer.bus_address();
|
||||
let buffer = mem::replace(&mut self.buffers[index], new_buffer);
|
||||
let buffer = unsafe { DmaBuffer::assume_init_slice(buffer) };
|
||||
// TODO packet size hint
|
||||
packet_handler(buffer);
|
||||
entry.setup_rx(new_buffer_address, true)?;
|
||||
self.rd = self.rd.wrapping_add(1);
|
||||
count += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(count)
|
||||
}
|
||||
}
|
||||
|
||||
impl RxDescriptor {
|
||||
const RDES3_OWN: u32 = 1 << 31;
|
||||
const RDES3_IOC: u32 = 1 << 30;
|
||||
const RDES3_BUF1V: u32 = 1 << 24;
|
||||
|
||||
pub const fn empty() -> Self {
|
||||
Self {
|
||||
rdes0: 0,
|
||||
rdes1: 0,
|
||||
rdes2: 0,
|
||||
rdes3: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rx_completed(&self) -> Option<usize> {
|
||||
if self.rdes3 & Self::RDES3_OWN == 0 {
|
||||
Some((self.rdes3 & 0x7FFF) as usize)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn setup_rx(&mut self, buffer: BusAddress, ioc: bool) -> Result<(), Error> {
|
||||
self.rdes0 = buffer.try_into_u32().map_err(|_| Error::InvalidArgument)?;
|
||||
self.rdes1 = 0;
|
||||
self.rdes2 = 0;
|
||||
self.rdes3 = Self::RDES3_BUF1V;
|
||||
if ioc {
|
||||
self.rdes3 |= Self::RDES3_IOC;
|
||||
}
|
||||
self.rdes3 |= Self::RDES3_OWN;
|
||||
Ok(())
|
||||
}
|
||||
}
|
23
kernel/driver/virtio/blk/Cargo.toml
Normal file
23
kernel/driver/virtio/blk/Cargo.toml
Normal file
@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "ygg_driver_virtio_blk"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
yggdrasil-abi.workspace = true
|
||||
libk-util.workspace = true
|
||||
libk-mm.workspace = true
|
||||
libk.workspace = true
|
||||
device-api = { workspace = true, features = ["derive"] }
|
||||
|
||||
ygg_driver_virtio_core = { path = "../core" }
|
||||
ygg_driver_pci = { path = "../../bus/pci", optional = true }
|
||||
|
||||
log.workspace = true
|
||||
bytemuck.workspace = true
|
||||
tock-registers.workspace = true
|
||||
async-trait.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
pci = ["ygg_driver_pci", "ygg_driver_virtio_core/pci"]
|
403
kernel/driver/virtio/blk/src/lib.rs
Normal file
403
kernel/driver/virtio/blk/src/lib.rs
Normal file
@ -0,0 +1,403 @@
|
||||
#![no_std]
|
||||
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
|
||||
use async_trait::async_trait;
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
|
||||
};
|
||||
use libk::{
|
||||
device::{block::BlockDevice, manager::probe_partitions},
|
||||
dma::{DmaBuffer, DmaSlice, DmaSliceMut},
|
||||
error::Error,
|
||||
fs::devfs,
|
||||
task::runtime,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, table::MapAttributes, PageProvider};
|
||||
use libk_util::sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock};
|
||||
use ygg_driver_pci::{
|
||||
device::{PciDeviceInfo, PreferredInterruptMode},
|
||||
macros::pci_driver,
|
||||
};
|
||||
use ygg_driver_virtio_core::{
|
||||
queue::VirtQueue,
|
||||
transport::{pci::PciTransport, Transport},
|
||||
DeviceStatus,
|
||||
};
|
||||
use yggdrasil_abi::{bitflags, io::FileMode};
|
||||
|
||||
extern crate alloc;
|
||||
|
||||
bitflags! {
|
||||
pub struct Features: u64 {
|
||||
const F_SIZE_MAX: bit 1;
|
||||
const F_SEG_MAX: bit 2;
|
||||
const F_RO: bit 5;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct DeviceConfig {
|
||||
capacity: u64,
|
||||
size_max: u32,
|
||||
seg_max: u32,
|
||||
// virtio_blk_geometry {
|
||||
cylinders: u16,
|
||||
heads: u8,
|
||||
sectors: u8,
|
||||
// }
|
||||
blk_size: u32,
|
||||
// virtio_blk_topology {
|
||||
physical_block_exp: u8,
|
||||
alignment_offset: u8,
|
||||
min_io_size: u16,
|
||||
opt_io_size: u32,
|
||||
// }
|
||||
writeback: u8,
|
||||
_0: u8,
|
||||
num_queues: u16,
|
||||
max_discard_sectors: u32,
|
||||
max_discard_seg: u32,
|
||||
discard_sector_alignment: u32,
|
||||
max_write_zeroes_sectors: u32,
|
||||
max_write_zeroes_seg: u32,
|
||||
write_zeroes_may_unmap: u8,
|
||||
_1: [u8; 3],
|
||||
max_secure_erase_sectors: u32,
|
||||
max_secure_erase_seg: u32,
|
||||
secure_erase_sector_alignment: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
struct CommandHeader {
|
||||
ty: u32,
|
||||
_0: u32,
|
||||
sector: u64,
|
||||
}
|
||||
|
||||
pub struct VirtioBlk<T: Transport + 'static> {
|
||||
transport: IrqSafeSpinlock<T>,
|
||||
pci_device_info: PciDeviceInfo,
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
|
||||
segment_size: usize,
|
||||
read_only: bool,
|
||||
capacity: u64,
|
||||
request_queue: VirtQueue,
|
||||
}
|
||||
|
||||
impl CommandHeader {
|
||||
const TYPE_READ: u32 = 0;
|
||||
const TYPE_WRITE: u32 = 1;
|
||||
|
||||
pub fn for_read(lba: u64) -> Self {
|
||||
Self {
|
||||
ty: Self::TYPE_READ,
|
||||
_0: 0,
|
||||
sector: lba,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn for_write(lba: u64) -> Self {
|
||||
Self {
|
||||
ty: Self::TYPE_WRITE,
|
||||
_0: 0,
|
||||
sector: lba,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> VirtioBlk<T> {
|
||||
// Only one VQ
|
||||
const VQ_REQUEST_0: u16 = 0;
|
||||
|
||||
fn new(
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
mut transport: T,
|
||||
pci_device_info: PciDeviceInfo,
|
||||
) -> Result<Self, Error> {
|
||||
let features = Features::from(transport.read_device_features());
|
||||
let device_cfg = transport
|
||||
.device_cfg()
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::error!("virtio-blk does not expose device configuration"))?;
|
||||
let device_cfg: &DeviceConfig =
|
||||
bytemuck::from_bytes(&device_cfg[..size_of::<DeviceConfig>()]);
|
||||
|
||||
let read_only = features.contains(Features::F_RO);
|
||||
let segment_size = if features.contains(Features::F_SIZE_MAX) {
|
||||
device_cfg.size_max as usize
|
||||
} else {
|
||||
// I guess no limit then?
|
||||
262144
|
||||
};
|
||||
let segment_limit = if features.contains(Features::F_SEG_MAX) {
|
||||
device_cfg.seg_max as usize
|
||||
} else {
|
||||
// I guess no limit then?
|
||||
8
|
||||
};
|
||||
let capacity = device_cfg.capacity;
|
||||
|
||||
if segment_limit < 3 {
|
||||
// Won't be able to send header + data + status
|
||||
log::error!("virtio-blk: allowed segment count too small");
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let request_queue = VirtQueue::with_capacity(&*dma, Self::VQ_REQUEST_0, 256)?;
|
||||
|
||||
Ok(Self {
|
||||
transport: IrqSafeSpinlock::new(transport),
|
||||
dma,
|
||||
pci_device_info,
|
||||
|
||||
segment_size,
|
||||
read_only,
|
||||
capacity,
|
||||
request_queue,
|
||||
})
|
||||
}
|
||||
|
||||
fn begin_init(&self) -> Result<DeviceStatus, Error> {
|
||||
let mut transport = self.transport.lock();
|
||||
let mut status = DeviceStatus::RESET_VALUE;
|
||||
|
||||
log::debug!("Reset device");
|
||||
transport.write_device_status(status);
|
||||
status |= DeviceStatus::ACKNOWLEDGE;
|
||||
transport.write_device_status(status);
|
||||
status |= DeviceStatus::DRIVER;
|
||||
transport.write_device_status(status);
|
||||
|
||||
let _device_features = transport.read_device_features();
|
||||
|
||||
// TODO blah blah blah
|
||||
|
||||
transport.write_driver_features(0);
|
||||
|
||||
status |= DeviceStatus::FEATURES_OK;
|
||||
transport.write_device_status(status);
|
||||
|
||||
if !transport
|
||||
.read_device_status()
|
||||
.contains(DeviceStatus::FEATURES_OK)
|
||||
{
|
||||
return Err(Error::InvalidOperation);
|
||||
}
|
||||
|
||||
Ok(status)
|
||||
}
|
||||
|
||||
fn finish_init(&self, status: DeviceStatus) {
|
||||
let mut transport = self.transport.lock();
|
||||
|
||||
transport.write_device_status(status | DeviceStatus::DRIVER_OK);
|
||||
}
|
||||
|
||||
fn setup_queues(self: &Arc<Self>) -> Result<(), Error> {
|
||||
self.pci_device_info
|
||||
.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
let msi_info = self
|
||||
.pci_device_info
|
||||
.map_interrupt(InterruptAffinity::Any, self.clone())?;
|
||||
let vector = msi_info.map(|msi| msi.vector as u16);
|
||||
|
||||
let mut transport = self.transport.lock();
|
||||
|
||||
transport.set_queue(Self::VQ_REQUEST_0, &self.request_queue, vector);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> InterruptHandler for VirtioBlk<T> {
|
||||
fn handle_irq(self: Arc<Self>, _vector: IrqVector) -> bool {
|
||||
// Only one queue
|
||||
self.request_queue.handle_notify();
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> Device for VirtioBlk<T> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let status = self.begin_init()?;
|
||||
self.setup_queues()?;
|
||||
self.finish_init(status);
|
||||
register_virtio_block_device(self.clone());
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"VirtIO Block Device"
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait]
|
||||
impl<T: Transport + 'static> BlockDevice for VirtioBlk<T> {
|
||||
fn allocate_buffer(&self, size: usize) -> Result<DmaBuffer<[MaybeUninit<u8>]>, Error> {
|
||||
DmaBuffer::new_uninit_slice(&*self.dma, size)
|
||||
}
|
||||
|
||||
async fn read_aligned(
|
||||
&self,
|
||||
position: u64,
|
||||
buffer: DmaSliceMut<'_, MaybeUninit<u8>>,
|
||||
) -> Result<(), Error> {
|
||||
if position % 512 != 0 || buffer.len() % 512 != 0 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let lba = position / 512;
|
||||
let lba_count = buffer.len() / 512;
|
||||
if lba + lba_count as u64 >= self.capacity {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut header = DmaBuffer::new_slice(&*self.dma, 0, size_of::<CommandHeader>())?;
|
||||
*bytemuck::from_bytes_mut(&mut header[..]) = CommandHeader::for_read(lba);
|
||||
let mut status = DmaBuffer::new_uninit_slice(&*self.dma, 1)?;
|
||||
|
||||
self.request_queue
|
||||
.enqueue_wait(
|
||||
&[header.slice(0..size_of::<CommandHeader>())],
|
||||
&[buffer, status.slice_mut(0..1)],
|
||||
|| {
|
||||
self.transport.lock().notify(Self::VQ_REQUEST_0);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let status = unsafe { DmaBuffer::assume_init_slice(status) }[0];
|
||||
if status == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
async fn write_aligned(&self, position: u64, buffer: DmaSlice<'_, u8>) -> Result<(), Error> {
|
||||
if self.read_only {
|
||||
return Err(Error::ReadOnly);
|
||||
}
|
||||
|
||||
if position % 512 != 0 || buffer.len() % 512 != 0 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
let lba = position / 512;
|
||||
let lba_count = buffer.len() / 512;
|
||||
if lba + lba_count as u64 >= self.capacity {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
let mut header = DmaBuffer::new_slice(&*self.dma, 0, size_of::<CommandHeader>())?;
|
||||
*bytemuck::from_bytes_mut(&mut header[..]) = CommandHeader::for_write(lba);
|
||||
let mut status = DmaBuffer::new_uninit_slice(&*self.dma, 1)?;
|
||||
|
||||
self.request_queue
|
||||
.enqueue_wait(
|
||||
&[header.slice(0..size_of::<CommandHeader>()), buffer],
|
||||
&[status.slice_mut(0..1)],
|
||||
|| {
|
||||
self.transport.lock().notify(Self::VQ_REQUEST_0);
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
|
||||
let status = unsafe { DmaBuffer::assume_init_slice(status) }[0];
|
||||
if status == 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
fn block_size(&self) -> usize {
|
||||
512
|
||||
}
|
||||
fn block_count(&self) -> u64 {
|
||||
self.capacity
|
||||
}
|
||||
fn max_blocks_per_request(&self) -> usize {
|
||||
// TODO this limit can be bumped or scatter-gather operations
|
||||
self.segment_size / 512
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> PageProvider for VirtioBlk<T> {
|
||||
fn get_page(&self, _offset: u64) -> Result<PhysicalAddress, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn clone_page(
|
||||
&self,
|
||||
_offset: u64,
|
||||
_src_phys: PhysicalAddress,
|
||||
_src_attrs: MapAttributes,
|
||||
) -> Result<PhysicalAddress, Error> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn release_page(&self, _offset: u64, _phys: PhysicalAddress) -> Result<(), Error> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
static DEVICES: IrqSafeRwLock<Vec<Arc<dyn BlockDevice>>> = IrqSafeRwLock::new(Vec::new());
|
||||
|
||||
fn register_virtio_block_device(device: Arc<dyn BlockDevice>) {
|
||||
let index = {
|
||||
let mut devices = DEVICES.write();
|
||||
let index = devices.len();
|
||||
devices.push(device.clone());
|
||||
index
|
||||
};
|
||||
|
||||
let name = format!("vb{index}");
|
||||
devfs::add_named_block_device(device.clone(), name.clone(), FileMode::new(0o600)).ok();
|
||||
|
||||
runtime::spawn(async move {
|
||||
let name = name;
|
||||
log::info!("Probing partitions for {name}");
|
||||
probe_partitions(device, |index, partition| {
|
||||
let partition_name = format!("{name}p{}", index + 1);
|
||||
devfs::add_named_block_device(
|
||||
Arc::new(partition),
|
||||
partition_name,
|
||||
FileMode::new(0o600),
|
||||
)
|
||||
.ok();
|
||||
})
|
||||
.await
|
||||
.ok();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
pci_driver! {
|
||||
matches: [device (0x1AF4:0x1001)],
|
||||
driver: {
|
||||
fn probe(
|
||||
&self,
|
||||
info: &PciDeviceInfo,
|
||||
dma: &Arc<dyn DmaAllocator>,
|
||||
) -> Result<Arc<dyn Device>, Error> {
|
||||
let space = &info.config_space;
|
||||
|
||||
let transport = PciTransport::from_config_space(space).unwrap();
|
||||
let device = VirtioBlk::new(dma.clone(), transport, info.clone())?;
|
||||
|
||||
let device = Arc::new(device);
|
||||
|
||||
Ok(device)
|
||||
}
|
||||
|
||||
fn driver_name(&self) -> &str {
|
||||
"virtio-blk"
|
||||
}
|
||||
}
|
||||
}
|
@ -16,6 +16,12 @@ log.workspace = true
|
||||
bitflags.workspace = true
|
||||
tock-registers.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
kernel-arch-hosted.path = "../../../arch/hosted"
|
||||
|
||||
[features]
|
||||
default = []
|
||||
pci = ["ygg_driver_pci"]
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
@ -1,361 +1,535 @@
|
||||
//! VirtIO queue implementation.
|
||||
//!
|
||||
//! # Note
|
||||
//!
|
||||
//! The code is poorly borrowed from `virtio-drivers` crate. I want to rewrite it properly myself.
|
||||
use core::{
|
||||
mem::MaybeUninit,
|
||||
sync::atomic::{fence, Ordering},
|
||||
|
||||
use core::{future::poll_fn, mem::MaybeUninit, task::Poll};
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use device_api::dma::DmaAllocator;
|
||||
use libk::{
|
||||
dma::{BusAddress, DmaBuffer, DmaSlice, DmaSliceMut},
|
||||
error::Error,
|
||||
};
|
||||
use libk_util::{
|
||||
event::OneTimeEvent, hash_table::DefaultHashTable, sync::IrqSafeSpinlock, waker::QueueWaker,
|
||||
};
|
||||
|
||||
use device_api::dma::DmaAllocator;
|
||||
use libk::dma::{BusAddress, DmaBuffer};
|
||||
|
||||
use crate::{error::Error, transport::Transport};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
#[repr(C)]
|
||||
struct Descriptor {
|
||||
address: BusAddress,
|
||||
len: u32,
|
||||
length: u32,
|
||||
flags: u16,
|
||||
next: u16,
|
||||
}
|
||||
|
||||
// Layout:
|
||||
// {
|
||||
// flags: u16,
|
||||
// idx: u16,
|
||||
// ring: [u16; QUEUE_SIZE],
|
||||
// used_event: u16
|
||||
// }
|
||||
struct AvailableRing {
|
||||
data: DmaBuffer<[MaybeUninit<u16>]>,
|
||||
mapping: DmaBuffer<[u16]>,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
// Layout:
|
||||
// {
|
||||
// flags: u16,
|
||||
// idx: u16,
|
||||
// ring: [UsedElem; QUEUE_SIZE],
|
||||
// avail_event: u16,
|
||||
// _pad: u16
|
||||
// }
|
||||
struct UsedRing {
|
||||
data: DmaBuffer<[MaybeUninit<u32>]>,
|
||||
used_count: usize,
|
||||
mapping: DmaBuffer<[u32]>,
|
||||
last_seen_used: u16,
|
||||
capacity: usize,
|
||||
}
|
||||
|
||||
pub struct VirtQueue {
|
||||
descriptor_table: DmaBuffer<[MaybeUninit<Descriptor>]>,
|
||||
pub struct DescriptorTable {
|
||||
descriptors: DmaBuffer<[Descriptor]>,
|
||||
free_count: usize,
|
||||
first_free: Option<u16>,
|
||||
last_free: Option<u16>,
|
||||
}
|
||||
|
||||
struct VqInner {
|
||||
descriptors: DescriptorTable,
|
||||
available: AvailableRing,
|
||||
used: UsedRing,
|
||||
}
|
||||
|
||||
pub struct VirtQueue<N: VqNotificationMechanism = VqAsyncNotification> {
|
||||
inner: IrqSafeSpinlock<VqInner>,
|
||||
free_descriptor_notify: QueueWaker,
|
||||
|
||||
#[allow(unused)]
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
|
||||
queue_index: u16,
|
||||
free_head: u16,
|
||||
used_notify: N,
|
||||
}
|
||||
|
||||
avail_idx: u16,
|
||||
last_used_idx: u16,
|
||||
pub trait VqNotificationMechanism {
|
||||
type Token;
|
||||
|
||||
msix_vector: u16,
|
||||
fn notify_used(&self, head: u16, length: u32);
|
||||
fn create_token(&self, head: u16) -> Self::Token;
|
||||
}
|
||||
|
||||
pub struct VqAsyncNotification {
|
||||
completions: IrqSafeSpinlock<DefaultHashTable<u16, Arc<OneTimeEvent<u32>>>>,
|
||||
}
|
||||
|
||||
pub struct VqManualNotification;
|
||||
|
||||
pub struct VqCallbackNotification(Box<dyn Fn(u16, u32) + Sync + Send>);
|
||||
|
||||
impl VqNotificationMechanism for VqAsyncNotification {
|
||||
type Token = Arc<OneTimeEvent<u32>>;
|
||||
|
||||
fn notify_used(&self, head: u16, length: u32) {
|
||||
let mut completions = self.completions.lock();
|
||||
if let Some(completion) = completions.remove(&head) {
|
||||
log::trace!("vq: completion #{head}");
|
||||
completion.signal(length);
|
||||
}
|
||||
}
|
||||
|
||||
fn create_token(&self, head: u16) -> Self::Token {
|
||||
let mut completions = self.completions.lock();
|
||||
let token = Arc::new(OneTimeEvent::new());
|
||||
completions.insert(head, token.clone());
|
||||
token
|
||||
}
|
||||
}
|
||||
|
||||
impl VqNotificationMechanism for VqCallbackNotification {
|
||||
type Token = u16;
|
||||
|
||||
fn create_token(&self, head: u16) -> Self::Token {
|
||||
head
|
||||
}
|
||||
|
||||
fn notify_used(&self, head: u16, length: u32) {
|
||||
(self.0)(head, length);
|
||||
}
|
||||
}
|
||||
|
||||
impl VqNotificationMechanism for VqManualNotification {
|
||||
type Token = u16;
|
||||
|
||||
fn create_token(&self, head: u16) -> Self::Token {
|
||||
head
|
||||
}
|
||||
|
||||
fn notify_used(&self, _head: u16, _length: u32) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl AvailableRing {
|
||||
const FLAGS: usize = 0;
|
||||
const IDX: usize = 1;
|
||||
const RING: usize = 2;
|
||||
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
no_irq: bool,
|
||||
capacity: usize,
|
||||
no_interrupt: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let mut data = DmaBuffer::new_zeroed_slice(dma, capacity + 3)?;
|
||||
|
||||
if no_irq {
|
||||
data[0].write(1);
|
||||
// flags + idx + [ring] + used_event
|
||||
let mut mapping = DmaBuffer::new_slice(dma, 0u16, (capacity + 6) & !3)?;
|
||||
if no_interrupt {
|
||||
mapping[Self::FLAGS] |= 1 << 0;
|
||||
}
|
||||
|
||||
data[1].write(0);
|
||||
|
||||
Ok(Self { data })
|
||||
Ok(Self { mapping, capacity })
|
||||
}
|
||||
|
||||
pub fn set_head(&mut self, slot: u16, head: u16) {
|
||||
self.data[slot as usize + 2].write(head);
|
||||
}
|
||||
|
||||
pub fn set_index(&mut self, index: u16) {
|
||||
self.data[1].write(index);
|
||||
pub fn push(&mut self, head: u16) -> u16 {
|
||||
log::trace!("enqueue #{head}");
|
||||
let idx = self.mapping[Self::IDX];
|
||||
let index = idx as usize % self.capacity;
|
||||
self.mapping[Self::RING + index] = head;
|
||||
self.mapping.cache_flush_element(Self::RING + index, true);
|
||||
let idx = idx.wrapping_add(1);
|
||||
self.mapping[Self::IDX] = idx;
|
||||
self.mapping.cache_flush_element(Self::IDX, true);
|
||||
idx
|
||||
}
|
||||
}
|
||||
|
||||
impl UsedRing {
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let mut data = DmaBuffer::new_zeroed_slice(dma, capacity * 2 + 2)?;
|
||||
|
||||
data[0].write(0);
|
||||
const FLAGS_IDX: usize = 0;
|
||||
const RING: usize = 1;
|
||||
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
capacity: usize,
|
||||
no_notify: bool,
|
||||
) -> Result<Self, Error> {
|
||||
// 2x u16 (flags + idx) + [ring x 2 x u32] + avail_event
|
||||
let mut mapping = DmaBuffer::new_slice(dma, 0, capacity * 2 + 1)?;
|
||||
if no_notify {
|
||||
mapping[Self::FLAGS_IDX] |= 1 << 0;
|
||||
}
|
||||
mapping.cache_flush_element(Self::FLAGS_IDX, true);
|
||||
Ok(Self {
|
||||
data,
|
||||
used_count: 0,
|
||||
mapping,
|
||||
capacity,
|
||||
last_seen_used: 0,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn read_slot(&self, index: u16) -> (u32, u32) {
|
||||
let index = unsafe { self.data[1 + index as usize * 2].assume_init() };
|
||||
let len = unsafe { self.data[2 + index as usize * 2].assume_init() };
|
||||
(index, len)
|
||||
}
|
||||
|
||||
pub fn index(&self) -> u16 {
|
||||
unsafe { (self.data[0].assume_init() >> 16) as u16 }
|
||||
pub fn consume<F: FnMut(u16, u32)>(&mut self, mut handler: F) -> usize {
|
||||
self.mapping.cache_flush_element(Self::FLAGS_IDX, false);
|
||||
let idx = (self.mapping[Self::FLAGS_IDX] >> 16) as u16;
|
||||
let mut count = 0;
|
||||
while self.last_seen_used != idx {
|
||||
let index = self.last_seen_used as usize % self.capacity;
|
||||
self.mapping
|
||||
.cache_flush_range(Self::RING + index..Self::RING + index + 1, false);
|
||||
let head = self.mapping[Self::RING + index * 2] as u16;
|
||||
let len = self.mapping[Self::RING + index * 2 + 1];
|
||||
handler(head, len);
|
||||
count += 1;
|
||||
self.last_seen_used = self.last_seen_used.wrapping_add(1);
|
||||
}
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtQueue {
|
||||
pub fn with_capacity<T: Transport>(
|
||||
transport: &mut T,
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
msix_vector: Option<u16>,
|
||||
no_avail_irq: bool,
|
||||
) -> Result<Self, Error> {
|
||||
// TODO check if queue is already set up
|
||||
impl Descriptor {
|
||||
pub const EMPTY: Self = Self {
|
||||
address: BusAddress::ZERO,
|
||||
length: 0,
|
||||
flags: 0,
|
||||
next: 0,
|
||||
};
|
||||
|
||||
let max_capacity = transport.max_queue_size(index);
|
||||
pub const F_NEXT: u16 = 1 << 0;
|
||||
pub const F_WRITE: u16 = 1 << 1;
|
||||
}
|
||||
|
||||
if !capacity.is_power_of_two() || capacity > u16::MAX.into() {
|
||||
return Err(Error::InvalidQueueSize);
|
||||
impl DescriptorTable {
|
||||
const FREE_CHAIN_END: u16 = u16::MAX;
|
||||
|
||||
pub fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
if capacity >= Self::FREE_CHAIN_END as usize - 1 {
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
|
||||
if capacity > max_capacity as usize {
|
||||
return Err(Error::QueueTooLarge);
|
||||
let mut descriptors = DmaBuffer::new_slice(dma, Descriptor::EMPTY, capacity)?;
|
||||
for i in 0..capacity {
|
||||
if i == capacity - 1 {
|
||||
// Last descriptor of the free chain
|
||||
descriptors[i].next = Self::FREE_CHAIN_END;
|
||||
} else {
|
||||
descriptors[i].next = (i + 1) as u16;
|
||||
}
|
||||
}
|
||||
|
||||
let descriptor_table = DmaBuffer::new_zeroed_slice(dma, capacity)?;
|
||||
let available = AvailableRing::with_capacity(dma, no_avail_irq, capacity)?;
|
||||
let used = UsedRing::with_capacity(dma, capacity)?;
|
||||
|
||||
transport.set_queue(
|
||||
index,
|
||||
capacity as u16,
|
||||
descriptor_table.bus_address(),
|
||||
available.data.bus_address(),
|
||||
used.data.bus_address(),
|
||||
msix_vector,
|
||||
);
|
||||
|
||||
Ok(Self {
|
||||
descriptor_table,
|
||||
available,
|
||||
used,
|
||||
|
||||
capacity,
|
||||
|
||||
queue_index: index,
|
||||
free_head: 0,
|
||||
|
||||
avail_idx: 0,
|
||||
last_used_idx: 0,
|
||||
|
||||
msix_vector: msix_vector.unwrap_or(0xFFFF),
|
||||
descriptors,
|
||||
free_count: capacity,
|
||||
first_free: Some(0),
|
||||
last_free: Some(capacity as u16 - 1),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> usize {
|
||||
self.capacity
|
||||
}
|
||||
|
||||
pub fn with_max_capacity<T: Transport>(
|
||||
transport: &mut T,
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
msix_vector: Option<u16>,
|
||||
no_avail_irq: bool,
|
||||
) -> Result<Self, Error> {
|
||||
let max_capacity = transport.max_queue_size(index);
|
||||
let capacity = capacity.min(max_capacity as usize);
|
||||
|
||||
Self::with_capacity(transport, dma, index, capacity, msix_vector, no_avail_irq)
|
||||
// Allocate a chain of descriptors
|
||||
fn alloc_descriptors(
|
||||
&mut self,
|
||||
h2d: &[DmaSlice<u8>],
|
||||
d2h: &[DmaSliceMut<MaybeUninit<u8>>],
|
||||
) -> Result<u16, Error> {
|
||||
if d2h.len() + h2d.len() == 0 {
|
||||
// Empty transfer
|
||||
return Err(Error::InvalidArgument);
|
||||
}
|
||||
if self.free_count < d2h.len() + h2d.len() {
|
||||
// Not enough descriptor "slots" to place the buffers
|
||||
return Err(Error::WouldBlock);
|
||||
}
|
||||
// Implied by free_count
|
||||
debug_assert!(self.first_free.is_some());
|
||||
debug_assert!(self.last_free.is_some());
|
||||
let head = unsafe {
|
||||
self.follow_descriptor_chain(h2d.len() + d2h.len(), |i, desc| {
|
||||
if i < h2d.len() {
|
||||
desc.address = h2d[i].bus_address();
|
||||
desc.length = h2d[i].len() as u32;
|
||||
desc.flags = 0;
|
||||
} else {
|
||||
let i = i - h2d.len();
|
||||
desc.address = d2h[i].bus_address();
|
||||
desc.length = d2h[i].len() as u32;
|
||||
desc.flags = Descriptor::F_WRITE;
|
||||
}
|
||||
})
|
||||
};
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// Invariants: DmaBuffer remains valid and allocated until it is properly dequeued.
|
||||
pub unsafe fn add<'a, 'b>(
|
||||
/// The following invariants must hold:
|
||||
///
|
||||
/// * **There must actually be `count` free descriptors**.
|
||||
/// * **`count` should not be zero**.
|
||||
/// * The VQ must be in a consistent state: first_free/last_free/free_count must be consistent
|
||||
/// in terms of tracking the current state of the descriptor allocation.
|
||||
/// * Free descriptors in the table must be chained.
|
||||
/// * Last free descriptor must have [Self::FREE_CHAIN_END] as its `next` field.
|
||||
unsafe fn follow_descriptor_chain<F: Fn(usize, &mut Descriptor)>(
|
||||
&mut self,
|
||||
input: &'a [&'b mut DmaBuffer<[MaybeUninit<u8>]>],
|
||||
output: &'a [&'b DmaBuffer<[u8]>],
|
||||
) -> Result<u16, Error> {
|
||||
if input.is_empty() && output.is_empty() {
|
||||
return Err(Error::EmptyTransaction);
|
||||
}
|
||||
let n_desc = input.len() + output.len();
|
||||
|
||||
if self.used.used_count + 1 > self.capacity || self.used.used_count + n_desc > self.capacity
|
||||
{
|
||||
return Err(Error::QueueFull);
|
||||
}
|
||||
|
||||
let head = self.add_direct(input, output);
|
||||
let avail_slot = self.avail_idx % self.capacity as u16;
|
||||
|
||||
self.available.set_head(avail_slot, head);
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
self.avail_idx = self.avail_idx.wrapping_add(1);
|
||||
self.available.set_index(self.avail_idx);
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
unsafe fn add_direct<'a, 'b>(
|
||||
&mut self,
|
||||
input: &'a [&'b mut DmaBuffer<[MaybeUninit<u8>]>],
|
||||
output: &'a [&'b DmaBuffer<[u8]>],
|
||||
count: usize,
|
||||
visitor: F,
|
||||
) -> u16 {
|
||||
let head = self.free_head;
|
||||
let mut last = self.free_head;
|
||||
|
||||
for item in output {
|
||||
assert_ne!(item.len(), 0);
|
||||
let desc = &mut self.descriptor_table[usize::from(self.free_head)];
|
||||
let next = (self.free_head + 1) % self.capacity as u16;
|
||||
|
||||
desc.write(Descriptor {
|
||||
address: item.bus_address(),
|
||||
len: item.len().try_into().unwrap(),
|
||||
// TODO
|
||||
flags: (1 << 0),
|
||||
next,
|
||||
});
|
||||
|
||||
last = self.free_head;
|
||||
self.free_head = next;
|
||||
debug_assert_ne!(count, 0);
|
||||
let mut current = self.first_free.unwrap_unchecked();
|
||||
let head = current;
|
||||
for i in 0..count {
|
||||
let descriptor = &mut self.descriptors[current as usize];
|
||||
log::trace!("vq: alloc desc #{current}");
|
||||
visitor(i, descriptor);
|
||||
if i == count - 1 {
|
||||
debug_assert_eq!(descriptor.flags & Descriptor::F_NEXT, 0);
|
||||
current = descriptor.next;
|
||||
descriptor.next = 0;
|
||||
} else {
|
||||
current = descriptor.next;
|
||||
descriptor.flags |= Descriptor::F_NEXT;
|
||||
debug_assert_ne!(current, Self::FREE_CHAIN_END);
|
||||
};
|
||||
}
|
||||
|
||||
for item in input {
|
||||
assert_ne!(item.len(), 0);
|
||||
let desc = &mut self.descriptor_table[usize::from(self.free_head)];
|
||||
let next = (self.free_head + 1) % self.capacity as u16;
|
||||
|
||||
desc.write(Descriptor {
|
||||
address: item.bus_address(),
|
||||
len: item.len().try_into().unwrap(),
|
||||
// TODO MAGIC
|
||||
flags: (1 << 0) | (1 << 1),
|
||||
next,
|
||||
});
|
||||
|
||||
last = self.free_head;
|
||||
self.free_head = next;
|
||||
if current == Self::FREE_CHAIN_END {
|
||||
// No free descriptors left
|
||||
debug_assert_eq!(self.free_count, count);
|
||||
self.first_free = None;
|
||||
self.last_free = None;
|
||||
} else {
|
||||
self.first_free = Some(current);
|
||||
}
|
||||
|
||||
{
|
||||
let last_desc = self.descriptor_table[last as usize].assume_init_mut();
|
||||
|
||||
// TODO
|
||||
last_desc.flags &= !(1 << 0);
|
||||
}
|
||||
|
||||
self.used.used_count += input.len() + output.len();
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
self.free_count -= count;
|
||||
|
||||
head
|
||||
}
|
||||
|
||||
pub fn add_notify_wait_pop<'a, 'b, T: Transport>(
|
||||
&mut self,
|
||||
input: &'a [&'b mut DmaBuffer<[MaybeUninit<u8>]>],
|
||||
output: &'a [&'b DmaBuffer<[u8]>],
|
||||
transport: &mut T,
|
||||
) -> Result<u32, Error> {
|
||||
let token = unsafe { self.add(input, output) }?;
|
||||
fn add_free_descriptor(&mut self, idx: u16) {
|
||||
log::trace!("vq: free descriptor #{idx}");
|
||||
self.descriptors[idx as usize] = Descriptor {
|
||||
next: Self::FREE_CHAIN_END,
|
||||
..Descriptor::EMPTY
|
||||
};
|
||||
|
||||
transport.notify(self.queue_index);
|
||||
|
||||
while self.is_used_empty() {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
unsafe { self.pop_used(token) }
|
||||
}
|
||||
|
||||
pub fn is_used_empty(&self) -> bool {
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
self.last_used_idx == self.used.index()
|
||||
}
|
||||
|
||||
pub fn pop_last_used(&mut self) -> Option<(u16, u32)> {
|
||||
let token = self.peek_used()?;
|
||||
let len = unsafe { self.pop_used(token) }.unwrap();
|
||||
|
||||
Some((token, len))
|
||||
}
|
||||
|
||||
fn peek_used(&mut self) -> Option<u16> {
|
||||
if !self.is_used_empty() {
|
||||
let last_used = self.last_used_idx % self.capacity as u16;
|
||||
Some(self.used.read_slot(last_used).0 as u16)
|
||||
if let Some(last) = self.last_free {
|
||||
// Implies first free is Some as well
|
||||
self.descriptors[last as usize].next = idx;
|
||||
// last -> idx
|
||||
self.last_free = Some(idx);
|
||||
} else {
|
||||
None
|
||||
// Implies first free is None as well
|
||||
self.first_free = Some(idx);
|
||||
self.last_free = Some(idx);
|
||||
}
|
||||
|
||||
self.free_count += 1;
|
||||
}
|
||||
|
||||
unsafe fn pop_used(&mut self, token: u16) -> Result<u32, Error> {
|
||||
if self.is_used_empty() {
|
||||
return Err(Error::QueueEmpty);
|
||||
}
|
||||
|
||||
let last_used_slot = self.last_used_idx % self.capacity as u16;
|
||||
let (index, len) = self.used.read_slot(last_used_slot);
|
||||
|
||||
if index != token as u32 {
|
||||
return Err(Error::WrongToken);
|
||||
}
|
||||
|
||||
self.free_descriptor_chain(token);
|
||||
|
||||
fence(Ordering::SeqCst);
|
||||
|
||||
self.last_used_idx = self.last_used_idx.wrapping_add(1);
|
||||
|
||||
Ok(len)
|
||||
}
|
||||
|
||||
unsafe fn free_descriptor_chain(&mut self, head: u16) -> usize {
|
||||
let mut current_node = Some(self.descriptor_table[usize::from(head)].assume_init_mut());
|
||||
fn free_descriptor_chain(&mut self, first: u16) -> usize {
|
||||
let mut current = first;
|
||||
let mut count = 0;
|
||||
loop {
|
||||
let descriptor = &self.descriptors[current as usize];
|
||||
let next = if descriptor.flags & Descriptor::F_NEXT != 0 {
|
||||
Some(descriptor.next)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
while let Some(current) = current_node {
|
||||
assert_ne!(current.len, 0);
|
||||
let next_head = (current.flags & (1 << 0) != 0).then_some(current.next);
|
||||
|
||||
current.address = BusAddress::ZERO;
|
||||
current.flags = 0;
|
||||
current.next = 0;
|
||||
current.len = 0;
|
||||
|
||||
self.used.used_count -= 1;
|
||||
count += 1;
|
||||
self.add_free_descriptor(current);
|
||||
|
||||
current_node =
|
||||
next_head.map(|head| self.descriptor_table[usize::from(head)].assume_init_mut());
|
||||
if let Some(next) = next {
|
||||
current = next;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self.free_head = head;
|
||||
count
|
||||
}
|
||||
}
|
||||
|
||||
pub fn msix_vector(&self) -> u16 {
|
||||
self.msix_vector
|
||||
impl VqInner {
|
||||
fn with_capacity(dma: &dyn DmaAllocator, capacity: usize) -> Result<Self, Error> {
|
||||
let descriptors = DescriptorTable::with_capacity(dma, capacity)?;
|
||||
let available = AvailableRing::with_capacity(dma, capacity, false)?;
|
||||
let used = UsedRing::with_capacity(dma, capacity, false)?;
|
||||
|
||||
Ok(Self {
|
||||
descriptors,
|
||||
available,
|
||||
used,
|
||||
})
|
||||
}
|
||||
|
||||
fn try_enqueue(
|
||||
&mut self,
|
||||
h2d: &[DmaSlice<u8>],
|
||||
d2h: &[DmaSliceMut<MaybeUninit<u8>>],
|
||||
) -> Result<u16, Error> {
|
||||
let head = self.descriptors.alloc_descriptors(h2d, d2h)?;
|
||||
self.available.push(head);
|
||||
Ok(head)
|
||||
}
|
||||
|
||||
fn consume<F: FnMut(u16, u32)>(&mut self, free_notify: &QueueWaker, mut handler: F) -> usize {
|
||||
self.used.consume(|head, len| {
|
||||
log::trace!("vq: used #{head}, len={len}");
|
||||
|
||||
self.descriptors.free_descriptor_chain(head);
|
||||
free_notify.wake_all();
|
||||
|
||||
handler(head, len);
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtQueue<VqAsyncNotification> {
|
||||
pub fn with_capacity(
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let used_notify = VqAsyncNotification::new();
|
||||
Self::with_capacity_and_notify(dma, index, capacity, used_notify)
|
||||
}
|
||||
|
||||
pub async fn enqueue_wait<F: FnOnce()>(
|
||||
&self,
|
||||
h2d: &[DmaSlice<'_, u8>],
|
||||
d2h: &[DmaSliceMut<'_, MaybeUninit<u8>>],
|
||||
notify_queue: F,
|
||||
) -> Result<u32, Error> {
|
||||
let completion = self.enqueue(h2d, d2h).await?;
|
||||
notify_queue();
|
||||
let result = completion.wait_copy().await;
|
||||
Ok(result)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtQueue<VqCallbackNotification> {
|
||||
pub fn with_capacity_and_callback(
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
used_callback: Box<dyn Fn(u16, u32) + Sync + Send>,
|
||||
) -> Result<Self, Error> {
|
||||
let used_notify = VqCallbackNotification(used_callback);
|
||||
Self::with_capacity_and_notify(dma, index, capacity, used_notify)
|
||||
}
|
||||
}
|
||||
|
||||
impl VirtQueue<VqManualNotification> {
|
||||
pub fn with_capacity_manual(
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
) -> Result<Self, Error> {
|
||||
let used_notify = VqManualNotification;
|
||||
Self::with_capacity_and_notify(dma, index, capacity, used_notify)
|
||||
}
|
||||
|
||||
pub fn handle_notify_manual<F: FnMut(u16, u32)>(&self, handler: F) -> usize {
|
||||
self.inner
|
||||
.lock()
|
||||
.consume(&self.free_descriptor_notify, handler)
|
||||
}
|
||||
|
||||
// Used when queue does not support device-side used notification
|
||||
pub fn enqueue_blocking<F: FnOnce()>(
|
||||
&self,
|
||||
h2d: &[DmaSlice<u8>],
|
||||
d2h: &[DmaSliceMut<MaybeUninit<u8>>],
|
||||
notify_queue: F,
|
||||
) -> Result<u32, Error> {
|
||||
let token = self.try_enqueue(h2d, d2h)?;
|
||||
let mut length = 0;
|
||||
notify_queue();
|
||||
loop {
|
||||
self.handle_notify_manual(|head, len| {
|
||||
assert_eq!(head, token);
|
||||
length = len;
|
||||
});
|
||||
if length != 0 {
|
||||
break;
|
||||
}
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
Ok(length)
|
||||
}
|
||||
}
|
||||
|
||||
impl<N: VqNotificationMechanism> VirtQueue<N> {
|
||||
pub fn with_capacity_and_notify(
|
||||
dma: &dyn DmaAllocator,
|
||||
index: u16,
|
||||
capacity: usize,
|
||||
used_notify: N,
|
||||
) -> Result<Self, Error> {
|
||||
let inner = VqInner::with_capacity(dma, capacity)?;
|
||||
Ok(Self {
|
||||
inner: IrqSafeSpinlock::new(inner),
|
||||
|
||||
index,
|
||||
capacity,
|
||||
used_notify,
|
||||
free_descriptor_notify: QueueWaker::new(),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn descriptor_table_base(&self) -> BusAddress {
|
||||
self.inner.lock().descriptors.descriptors.bus_address()
|
||||
}
|
||||
|
||||
pub fn available_ring_base(&self) -> BusAddress {
|
||||
self.inner.lock().available.mapping.bus_address()
|
||||
}
|
||||
|
||||
pub fn used_ring_base(&self) -> BusAddress {
|
||||
self.inner.lock().used.mapping.bus_address()
|
||||
}
|
||||
|
||||
pub fn capacity(&self) -> u16 {
|
||||
self.capacity as u16
|
||||
}
|
||||
|
||||
pub fn handle_notify(&self) -> usize {
|
||||
self.inner
|
||||
.lock()
|
||||
.consume(&self.free_descriptor_notify, |head, len| {
|
||||
self.used_notify.notify_used(head, len);
|
||||
})
|
||||
}
|
||||
|
||||
pub fn try_enqueue(
|
||||
&self,
|
||||
h2d: &[DmaSlice<'_, u8>],
|
||||
d2h: &[DmaSliceMut<'_, MaybeUninit<u8>>],
|
||||
) -> Result<N::Token, Error> {
|
||||
let head = self.inner.lock().try_enqueue(h2d, d2h)?;
|
||||
Ok(self.used_notify.create_token(head))
|
||||
}
|
||||
|
||||
pub async fn enqueue(
|
||||
&self,
|
||||
h2d: &[DmaSlice<'_, u8>],
|
||||
d2h: &[DmaSliceMut<'_, MaybeUninit<u8>>],
|
||||
) -> Result<N::Token, Error> {
|
||||
poll_fn(|cx| match self.try_enqueue(h2d, d2h) {
|
||||
Err(Error::WouldBlock) => {
|
||||
self.free_descriptor_notify.register(cx.waker());
|
||||
Poll::Pending
|
||||
}
|
||||
result => {
|
||||
self.free_descriptor_notify.remove(cx.waker());
|
||||
Poll::Ready(result)
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
}
|
||||
|
||||
impl VqAsyncNotification {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
completions: IrqSafeSpinlock::new(DefaultHashTable::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,12 @@ use tock_registers::{
|
||||
registers::WriteOnly,
|
||||
};
|
||||
|
||||
use crate::{CommonConfiguration, DeviceStatus};
|
||||
use crate::{
|
||||
queue::{VirtQueue, VqNotificationMechanism},
|
||||
CommonConfiguration, DeviceStatus,
|
||||
};
|
||||
|
||||
#[cfg(any(feature = "pci", rust_analyzer))]
|
||||
pub mod pci;
|
||||
|
||||
pub trait Transport: Send {
|
||||
@ -53,7 +57,23 @@ pub trait Transport: Send {
|
||||
cfg.queue_size.get().into()
|
||||
}
|
||||
|
||||
fn set_queue(
|
||||
fn set_queue<N: VqNotificationMechanism>(
|
||||
&mut self,
|
||||
index: u16,
|
||||
queue: &VirtQueue<N>,
|
||||
msix_vector: Option<u16>,
|
||||
) {
|
||||
self.set_queue_raw(
|
||||
index,
|
||||
queue.capacity(),
|
||||
queue.descriptor_table_base(),
|
||||
queue.available_ring_base(),
|
||||
queue.used_ring_base(),
|
||||
msix_vector,
|
||||
);
|
||||
}
|
||||
|
||||
fn set_queue_raw(
|
||||
&mut self,
|
||||
queue: u16,
|
||||
capacity: u16,
|
||||
|
@ -7,8 +7,11 @@ use libk::{
|
||||
dma::{BusAddress, DmaBuffer},
|
||||
error::Error,
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlockGuard;
|
||||
use ygg_driver_virtio_core::{queue::VirtQueue, transport::Transport};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use ygg_driver_virtio_core::{
|
||||
queue::{VirtQueue, VqManualNotification},
|
||||
transport::Transport,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy, Pod, Zeroable)]
|
||||
#[repr(C)]
|
||||
@ -93,19 +96,12 @@ pub struct TransferToHost2d {
|
||||
pub _0: u32,
|
||||
}
|
||||
|
||||
pub struct ControlLock<'a, T: Transport> {
|
||||
control: IrqSafeSpinlockGuard<'a, VirtQueue>,
|
||||
transport: IrqSafeSpinlockGuard<'a, T>,
|
||||
pub struct CommandExecution<'a, T: Transport> {
|
||||
pub(super) transport: &'a IrqSafeSpinlock<T>,
|
||||
pub(super) control: &'a VirtQueue<VqManualNotification>,
|
||||
}
|
||||
|
||||
impl<'a, T: Transport> ControlLock<'a, T> {
|
||||
pub const fn new(
|
||||
control: IrqSafeSpinlockGuard<'a, VirtQueue>,
|
||||
transport: IrqSafeSpinlockGuard<'a, T>,
|
||||
) -> Self {
|
||||
Self { control, transport }
|
||||
}
|
||||
|
||||
impl<'a, T: Transport> CommandExecution<'a, T> {
|
||||
fn send_recv<'r, Req: Pod>(
|
||||
&mut self,
|
||||
dma: &dyn DmaAllocator,
|
||||
@ -116,13 +112,11 @@ impl<'a, T: Transport> ControlLock<'a, T> {
|
||||
let mut request = unsafe { DmaBuffer::assume_init_slice(request) };
|
||||
request.copy_from_slice(bytemuck::bytes_of(req));
|
||||
|
||||
let len = self
|
||||
.control
|
||||
.add_notify_wait_pop(&[buffer], &[&request], &mut *self.transport)
|
||||
.inspect_err(|error| {
|
||||
log::warn!("virtio queue: {error:?}");
|
||||
})
|
||||
.map_err(|_| Error::InvalidArgument)? as usize;
|
||||
let len = self.control.enqueue_blocking(
|
||||
&[request.slice(0..size_of::<Req>())],
|
||||
&[buffer.slice_mut(0..buffer.len())],
|
||||
|| self.transport.lock().notify(0),
|
||||
)? as usize;
|
||||
|
||||
if len < size_of::<ControlHeader>() {
|
||||
log::warn!("virtio-gpu: invalid device response length: {len}");
|
||||
|
@ -6,7 +6,7 @@ extern crate alloc;
|
||||
use core::mem::MaybeUninit;
|
||||
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
use command::{ControlLock, ScanoutInfo};
|
||||
use command::{CommandExecution, ScanoutInfo};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
@ -31,7 +31,7 @@ use libk_util::{
|
||||
};
|
||||
use ygg_driver_pci::{device::PciDeviceInfo, macros::pci_driver};
|
||||
use ygg_driver_virtio_core::{
|
||||
queue::VirtQueue,
|
||||
queue::{VirtQueue, VqManualNotification},
|
||||
transport::{pci::PciTransport, Transport},
|
||||
DeviceStatus,
|
||||
};
|
||||
@ -40,7 +40,7 @@ use yggdrasil_abi::error::Error;
|
||||
mod command;
|
||||
|
||||
struct Queues {
|
||||
control: IrqSafeSpinlock<VirtQueue>,
|
||||
control: VirtQueue<VqManualNotification>,
|
||||
}
|
||||
|
||||
struct Framebuffer {
|
||||
@ -64,7 +64,7 @@ struct Config {
|
||||
pub struct VirtioGpu<T: Transport> {
|
||||
transport: IrqSafeSpinlock<T>,
|
||||
#[allow(unused)]
|
||||
pci_device_info: Option<PciDeviceInfo>,
|
||||
pci_device_info: PciDeviceInfo,
|
||||
|
||||
queues: OneTimeInit<Queues>,
|
||||
config: IrqSafeRwLock<Config>,
|
||||
@ -78,7 +78,7 @@ impl<T: Transport + 'static> VirtioGpu<T> {
|
||||
pub fn new(
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
transport: T,
|
||||
info: Option<PciDeviceInfo>,
|
||||
info: PciDeviceInfo,
|
||||
) -> Result<Self, Error> {
|
||||
// Read num-scanouts from device config
|
||||
let Some(device_cfg) = transport.device_cfg() else {
|
||||
@ -149,34 +149,31 @@ impl<T: Transport + 'static> VirtioGpu<T> {
|
||||
transport.write_device_status(status | DeviceStatus::DRIVER_OK);
|
||||
}
|
||||
|
||||
fn setup_queues(&self) -> Result<(), Error> {
|
||||
fn setup_queues(self: &Arc<Self>) -> Result<(), Error> {
|
||||
// TODO cursorq
|
||||
let mut transport = self.transport.lock();
|
||||
|
||||
let control = VirtQueue::with_max_capacity(&mut *transport, &*self.dma, 0, 128, None, true)
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let control = VirtQueue::with_capacity_manual(&*self.dma, 0, 128)?;
|
||||
transport.set_queue(0, &control, None);
|
||||
|
||||
self.queues.init(Queues {
|
||||
control: IrqSafeSpinlock::new(control),
|
||||
});
|
||||
self.queues.init(Queues { control });
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn control(&self) -> ControlLock<T> {
|
||||
let queues = self.queues.get();
|
||||
let control = queues.control.lock();
|
||||
let transport = self.transport.lock();
|
||||
|
||||
ControlLock::new(control, transport)
|
||||
fn begin_command(&self) -> CommandExecution<T> {
|
||||
CommandExecution {
|
||||
transport: &self.transport,
|
||||
control: &self.queues.get().control,
|
||||
}
|
||||
}
|
||||
|
||||
fn setup_display(&self) -> Result<(), Error> {
|
||||
let mut control = self.control();
|
||||
let mut config = self.config.write();
|
||||
let mut command = self.begin_command();
|
||||
|
||||
let scanouts =
|
||||
control.query_scanouts(&*self.dma, self.num_scanouts, &mut config.response)?;
|
||||
command.query_scanouts(&*self.dma, self.num_scanouts, &mut config.response)?;
|
||||
for (i, scanout) in scanouts.iter().enumerate() {
|
||||
log::info!(
|
||||
"virtio-gpu: [{i}] {}x{} + {},{}",
|
||||
@ -214,23 +211,23 @@ impl<T: Transport + 'static> VirtioGpu<T> {
|
||||
|
||||
let dma_buffer = DmaBuffer::new_uninit_slice(&*self.dma, size)?;
|
||||
|
||||
let mut control = self.control();
|
||||
let mut command = self.begin_command();
|
||||
|
||||
let resource_id = control.create_resource_2d(
|
||||
let resource_id = command.create_resource_2d(
|
||||
&*self.dma,
|
||||
&mut config.response,
|
||||
w,
|
||||
h,
|
||||
PixelFormat::R8G8B8A8,
|
||||
)?;
|
||||
control.attach_backing(
|
||||
command.attach_backing(
|
||||
&*self.dma,
|
||||
&mut config.response,
|
||||
resource_id,
|
||||
dma_buffer.bus_address(),
|
||||
size.try_into().unwrap(),
|
||||
)?;
|
||||
control.set_scanout(
|
||||
command.set_scanout(
|
||||
&*self.dma,
|
||||
&mut config.response,
|
||||
index as u32,
|
||||
@ -259,7 +256,7 @@ impl<T: Transport + 'static> VirtioGpu<T> {
|
||||
let framebuffer = config.framebuffer.as_ref().ok_or(Error::DoesNotExist)?;
|
||||
let r = config.scanouts[framebuffer.scanout_index].r;
|
||||
|
||||
let mut control = self.control();
|
||||
let mut command = self.begin_command();
|
||||
|
||||
if framebuffer.double {
|
||||
// Flip the buffer
|
||||
@ -267,8 +264,8 @@ impl<T: Transport + 'static> VirtioGpu<T> {
|
||||
} else {
|
||||
let resource_id = framebuffer.resource_id;
|
||||
|
||||
control.transfer_to_host_2d(&*self.dma, &mut config.response, resource_id, r)?;
|
||||
control.resource_flush(&*self.dma, &mut config.response, resource_id, r)?;
|
||||
command.transfer_to_host_2d(&*self.dma, &mut config.response, resource_id, r)?;
|
||||
command.resource_flush(&*self.dma, &mut config.response, resource_id, r)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -283,7 +280,6 @@ impl<T: Transport + 'static> Device for VirtioGpu<T> {
|
||||
|
||||
// Set up some initial mode
|
||||
self.setup_display()?;
|
||||
|
||||
self.setup_mode(0)?;
|
||||
|
||||
DEVICE_REGISTRY.display.register(self.clone(), false)?;
|
||||
@ -427,7 +423,7 @@ pci_driver! {
|
||||
log::error!("Couldn't set up PCI virtio transport: {error:?}");
|
||||
})
|
||||
.map_err(|_| Error::InvalidArgument)?;
|
||||
let device = VirtioGpu::new(dma.clone(), transport, Some(info.clone()))?;
|
||||
let device = VirtioGpu::new(dma.clone(), transport, info.clone())?;
|
||||
let device = Arc::new(device);
|
||||
|
||||
Ok(device)
|
||||
|
@ -18,6 +18,7 @@ log.workspace = true
|
||||
bitflags.workspace = true
|
||||
tock-registers.workspace = true
|
||||
bytemuck.workspace = true
|
||||
futures-util.workspace = true
|
||||
|
||||
[features]
|
||||
default = []
|
||||
|
@ -5,16 +5,19 @@ extern crate alloc;
|
||||
|
||||
use core::mem::{size_of, MaybeUninit};
|
||||
|
||||
use alloc::{collections::BTreeMap, sync::Arc};
|
||||
use alloc::{boxed::Box, sync::Arc};
|
||||
use bytemuck::{Pod, Zeroable};
|
||||
use device_api::{
|
||||
device::{Device, DeviceInitContext},
|
||||
dma::DmaAllocator,
|
||||
interrupt::{InterruptAffinity, InterruptHandler, IrqVector},
|
||||
};
|
||||
use libk::dma::DmaBuffer;
|
||||
use futures_util::task::AtomicWaker;
|
||||
use libk::{dma::DmaBuffer, task::runtime};
|
||||
use libk_util::{
|
||||
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock, IrqSafeSpinlockGuard},
|
||||
event::BitmapEvent,
|
||||
hash_table::DefaultHashTable,
|
||||
sync::{spin_rwlock::IrqSafeRwLock, IrqSafeSpinlock},
|
||||
OneTimeInit,
|
||||
};
|
||||
use ygg_driver_net_core::{
|
||||
@ -26,28 +29,25 @@ use ygg_driver_pci::{
|
||||
macros::pci_driver,
|
||||
};
|
||||
use ygg_driver_virtio_core::{
|
||||
queue::VirtQueue,
|
||||
queue::{VirtQueue, VqCallbackNotification, VqManualNotification},
|
||||
transport::{pci::PciTransport, Transport},
|
||||
DeviceStatus,
|
||||
};
|
||||
use yggdrasil_abi::{error::Error, net::MacAddress};
|
||||
|
||||
struct Queues {
|
||||
receive: IrqSafeSpinlock<VirtQueue>,
|
||||
transmit: IrqSafeSpinlock<VirtQueue>,
|
||||
}
|
||||
|
||||
pub struct VirtioNet<T: Transport> {
|
||||
transport: IrqSafeSpinlock<T>,
|
||||
queues: OneTimeInit<Queues>,
|
||||
interface_id: OneTimeInit<u32>,
|
||||
|
||||
mac: IrqSafeRwLock<MacAddress>,
|
||||
|
||||
pending_packets: IrqSafeRwLock<BTreeMap<u16, DmaBuffer<[MaybeUninit<u8>]>>>,
|
||||
pci_device_info: PciDeviceInfo,
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
|
||||
pci_device_info: Option<PciDeviceInfo>,
|
||||
interface_id: OneTimeInit<u32>,
|
||||
mac: IrqSafeRwLock<MacAddress>,
|
||||
|
||||
rx_queue: OneTimeInit<VirtQueue<VqManualNotification>>,
|
||||
tx_queue: OneTimeInit<VirtQueue<VqCallbackNotification>>,
|
||||
tx_in_flight: IrqSafeSpinlock<DefaultHashTable<u16, DmaBuffer<[u8]>>>,
|
||||
|
||||
softirq: BitmapEvent<AtomicWaker>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Pod, Zeroable)]
|
||||
@ -61,23 +61,15 @@ struct VirtioPacketHeader {
|
||||
csum_offset: u16,
|
||||
}
|
||||
|
||||
impl Queues {
|
||||
pub fn try_receive(&self, _index: usize) -> Option<(u16, IrqSafeSpinlockGuard<VirtQueue>)> {
|
||||
let mut queue = self.receive.lock();
|
||||
// TODO use len for packet size hint
|
||||
let (token, _len) = queue.pop_last_used()?;
|
||||
Some((token, queue))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> VirtioNet<T> {
|
||||
const PACKET_SIZE: usize = 4096;
|
||||
const VQ_RX: u16 = 0;
|
||||
const VQ_TX: u16 = 1;
|
||||
|
||||
pub fn new(
|
||||
dma: Arc<dyn DmaAllocator>,
|
||||
transport: T,
|
||||
pci_device_info: Option<PciDeviceInfo>,
|
||||
) -> Self {
|
||||
pci_device_info: PciDeviceInfo,
|
||||
) -> Result<Self, Error> {
|
||||
// Read MAC from device config
|
||||
let device_cfg = transport
|
||||
.device_cfg()
|
||||
@ -86,60 +78,21 @@ impl<T: Transport + 'static> VirtioNet<T> {
|
||||
mac_bytes.copy_from_slice(&device_cfg[..6]);
|
||||
let mac = MacAddress::from(mac_bytes);
|
||||
|
||||
Self {
|
||||
pci_device_info.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
|
||||
Ok(Self {
|
||||
transport: IrqSafeSpinlock::new(transport),
|
||||
queues: OneTimeInit::new(),
|
||||
interface_id: OneTimeInit::new(),
|
||||
|
||||
mac: IrqSafeRwLock::new(mac),
|
||||
|
||||
pending_packets: IrqSafeRwLock::new(BTreeMap::new()),
|
||||
|
||||
pci_device_info,
|
||||
dma,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn listen(&self, buffers: usize) {
|
||||
let queues = self.queues.get();
|
||||
let mut queue = queues.receive.lock();
|
||||
let mut packets = self.pending_packets.write();
|
||||
interface_id: OneTimeInit::new(),
|
||||
mac: IrqSafeRwLock::new(mac),
|
||||
|
||||
for _ in 0..buffers {
|
||||
let mut packet = DmaBuffer::new_uninit_slice(&*self.dma, Self::PACKET_SIZE).unwrap();
|
||||
let token = unsafe { queue.add(&[&mut packet], &[]).unwrap() };
|
||||
packets.insert(token, packet);
|
||||
}
|
||||
|
||||
let mut transport = self.transport.lock();
|
||||
transport.notify(0);
|
||||
}
|
||||
|
||||
fn handle_receive_interrupt(&self, queue: usize) -> bool {
|
||||
let queues = self.queues.get();
|
||||
let interface_id = *self.interface_id.get();
|
||||
let mut count = 0;
|
||||
|
||||
while let Some((token, mut queue)) = queues.try_receive(queue) {
|
||||
let mut pending_packets = self.pending_packets.write();
|
||||
let packet = pending_packets.remove(&token).unwrap();
|
||||
|
||||
let mut buffer = DmaBuffer::new_uninit_slice(&*self.dma, Self::PACKET_SIZE).unwrap();
|
||||
|
||||
let token = unsafe { queue.add(&[&mut buffer], &[]).unwrap() };
|
||||
pending_packets.insert(token, buffer);
|
||||
|
||||
let packet = unsafe { DmaBuffer::assume_init_slice(packet) };
|
||||
let packet = RxPacket::new(packet, size_of::<VirtioPacketHeader>(), interface_id);
|
||||
ygg_driver_net_core::receive_packet(packet).unwrap();
|
||||
count += 1
|
||||
}
|
||||
|
||||
if count != 0 {
|
||||
self.transport.lock().notify(0);
|
||||
}
|
||||
|
||||
count != 0
|
||||
rx_queue: OneTimeInit::new(),
|
||||
tx_queue: OneTimeInit::new(),
|
||||
tx_in_flight: IrqSafeSpinlock::new(DefaultHashTable::new()),
|
||||
softirq: BitmapEvent::new(AtomicWaker::new()),
|
||||
})
|
||||
}
|
||||
|
||||
fn begin_init(&self) -> Result<DeviceStatus, Error> {
|
||||
@ -178,46 +131,85 @@ impl<T: Transport + 'static> VirtioNet<T> {
|
||||
transport.write_device_status(status | DeviceStatus::DRIVER_OK);
|
||||
}
|
||||
|
||||
unsafe fn setup_queues(
|
||||
self: Arc<Self>,
|
||||
receive_count: usize,
|
||||
transmit_count: usize,
|
||||
) -> Result<(), Error> {
|
||||
let receive_vector = if let Some(pci) = self.pci_device_info.as_ref() {
|
||||
pci.init_interrupts(PreferredInterruptMode::Msi(true))?;
|
||||
let info = pci.map_interrupt(InterruptAffinity::Any, self.clone())?;
|
||||
|
||||
info.map(|info| info.vector as u16)
|
||||
unsafe fn setup_queues(self: &Arc<Self>) -> Result<(), Error> {
|
||||
let (rx_vector, tx_vector) = if let Ok(msis) =
|
||||
self.pci_device_info
|
||||
.map_interrupt_multiple(0..2, InterruptAffinity::Any, self.clone())
|
||||
{
|
||||
// Bound a MSI(-x) range, use per-queue vectors
|
||||
(Some(msis[0].vector as u16), Some(msis[1].vector as u16))
|
||||
} else {
|
||||
None
|
||||
// TODO support non-MSI-x/non-multivec setups
|
||||
todo!();
|
||||
};
|
||||
|
||||
// TODO multiqueue capability
|
||||
assert_eq!(receive_count, 1);
|
||||
assert_eq!(transmit_count, 1);
|
||||
// Setup a callback to remove pending buffers and pass them to the network stack
|
||||
let rx_queue = VirtQueue::with_capacity_manual(&*self.dma, Self::VQ_RX, 64)?;
|
||||
|
||||
let p = self.clone();
|
||||
// Setup a callback to remove buffers from "in flight owned buffers" list
|
||||
let tx_queue = VirtQueue::with_capacity_and_callback(
|
||||
&*self.dma,
|
||||
Self::VQ_TX,
|
||||
64,
|
||||
Box::new(move |head, _| {
|
||||
p.tx_in_flight.lock().remove(&head);
|
||||
}),
|
||||
)?;
|
||||
|
||||
let rx_queue = self.rx_queue.init(rx_queue);
|
||||
let tx_queue = self.tx_queue.init(tx_queue);
|
||||
|
||||
let mut transport = self.transport.lock();
|
||||
|
||||
// Setup the virtqs
|
||||
let rx = VirtQueue::with_max_capacity(
|
||||
&mut *transport,
|
||||
&*self.dma,
|
||||
0,
|
||||
128,
|
||||
receive_vector,
|
||||
false,
|
||||
)
|
||||
.map_err(cvt_error)?;
|
||||
let tx = VirtQueue::with_max_capacity(&mut *transport, &*self.dma, 1, 128, None, true)
|
||||
.map_err(cvt_error)?;
|
||||
|
||||
self.queues.init(Queues {
|
||||
receive: IrqSafeSpinlock::new(rx),
|
||||
transmit: IrqSafeSpinlock::new(tx),
|
||||
});
|
||||
transport.set_queue(Self::VQ_RX, rx_queue, rx_vector);
|
||||
transport.set_queue(Self::VQ_TX, tx_queue, tx_vector);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn softirq(&self) -> Result<(), Error> {
|
||||
const RX_SIZE: usize = 4096;
|
||||
const RX_IN_FLIGHT: usize = 32;
|
||||
|
||||
let rx_queue = self.rx_queue.get();
|
||||
let tx_queue = self.tx_queue.get();
|
||||
let nic = *self.interface_id.get();
|
||||
|
||||
let mut rx_in_flight = DefaultHashTable::<u16, DmaBuffer<[MaybeUninit<u8>]>>::new();
|
||||
|
||||
// Setup initial Rx set
|
||||
for _ in 0..RX_IN_FLIGHT {
|
||||
let mut buffer = DmaBuffer::new_uninit_slice(&*self.dma, RX_SIZE)?;
|
||||
let token = rx_queue.try_enqueue(&[], &[buffer.slice_mut(0..RX_SIZE)])?;
|
||||
rx_in_flight.insert(token, buffer);
|
||||
}
|
||||
|
||||
loop {
|
||||
let events = self.softirq.wait().await;
|
||||
|
||||
if events & (1 << Self::VQ_RX) != 0 {
|
||||
let refill_rx = rx_queue.handle_notify_manual(|head, _| {
|
||||
if let Some(packet) = rx_in_flight.remove(&head) {
|
||||
let packet = unsafe { DmaBuffer::assume_init_slice(packet) };
|
||||
let packet = RxPacket::new(packet, size_of::<VirtioPacketHeader>(), nic);
|
||||
ygg_driver_net_core::receive_packet(packet).ok();
|
||||
}
|
||||
});
|
||||
|
||||
// Refill Rx buffers
|
||||
for _ in 0..refill_rx {
|
||||
let mut buffer = DmaBuffer::new_uninit_slice(&*self.dma, RX_SIZE)?;
|
||||
let token = rx_queue.try_enqueue(&[], &[buffer.slice_mut(0..RX_SIZE)])?;
|
||||
rx_in_flight.insert(token, buffer);
|
||||
}
|
||||
}
|
||||
|
||||
if events & (1 << Self::VQ_TX) != 0 {
|
||||
tx_queue.handle_notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Transport + 'static> NetworkDevice for VirtioNet<T> {
|
||||
@ -226,13 +218,13 @@ impl<T: Transport + 'static> NetworkDevice for VirtioNet<T> {
|
||||
}
|
||||
|
||||
fn transmit_buffer(&self, mut packet: DmaBuffer<[u8]>) -> Result<(), Error> {
|
||||
let queues = self.queues.get();
|
||||
let mut tx = queues.transmit.lock();
|
||||
let mut transport = self.transport.lock();
|
||||
let tx_queue = self.tx_queue.get();
|
||||
packet[..size_of::<VirtioPacketHeader>()].fill(0);
|
||||
let _len = tx
|
||||
.add_notify_wait_pop(&[], &[&packet], &mut *transport)
|
||||
.unwrap();
|
||||
let token = tx_queue.try_enqueue(&[packet.slice(0..packet.len())], &[])?;
|
||||
// Add the packet to "in flight" list to make sure it doesn't get dropped and invalidated
|
||||
// immediately after returning from this function
|
||||
self.tx_in_flight.lock().insert(token, packet);
|
||||
self.transport.lock().notify(Self::VQ_TX);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -248,20 +240,12 @@ impl<T: Transport + 'static> NetworkDevice for VirtioNet<T> {
|
||||
impl<T: Transport + 'static> InterruptHandler for VirtioNet<T> {
|
||||
fn handle_irq(self: Arc<Self>, vector: IrqVector) -> bool {
|
||||
match vector {
|
||||
IrqVector::Msi(_) => {
|
||||
// MSI/MSI-X
|
||||
self.handle_receive_interrupt(0)
|
||||
}
|
||||
IrqVector::Irq(_) => {
|
||||
// Legacy IRQ
|
||||
let (queue_irq, config_irq) = self.transport.lock().read_interrupt_status();
|
||||
|
||||
if queue_irq {
|
||||
self.handle_receive_interrupt(0);
|
||||
}
|
||||
|
||||
queue_irq || config_irq
|
||||
IrqVector::Msi(vector) => {
|
||||
self.softirq.signal(1 << vector);
|
||||
true
|
||||
}
|
||||
// TODO non-multivec/legacy IRQ setup
|
||||
IrqVector::Irq(_) => todo!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -274,15 +258,16 @@ impl<T: Transport + 'static> Device for VirtioNet<T> {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let status = self.begin_init()?;
|
||||
|
||||
// TODO multiqueue
|
||||
self.clone().setup_queues(1, 1)?;
|
||||
self.setup_queues()?;
|
||||
|
||||
self.finish_init(status);
|
||||
|
||||
let iface =
|
||||
ygg_driver_net_core::register_interface(NetworkInterfaceType::Ethernet, self.clone());
|
||||
self.interface_id.init(iface.id());
|
||||
self.listen(64);
|
||||
|
||||
let p = self.clone();
|
||||
runtime::spawn(async move { p.softirq().await })?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -292,14 +277,6 @@ impl<T: Transport + 'static> Device for VirtioNet<T> {
|
||||
}
|
||||
}
|
||||
|
||||
fn cvt_error(error: ygg_driver_virtio_core::error::Error) -> Error {
|
||||
use ygg_driver_virtio_core::error::Error as VirtioError;
|
||||
match error {
|
||||
VirtioError::OsError(err) => err,
|
||||
_ => Error::InvalidOperation,
|
||||
}
|
||||
}
|
||||
|
||||
pci_driver! {
|
||||
matches: [device (0x1AF4:0x1000)],
|
||||
driver: {
|
||||
@ -311,7 +288,7 @@ pci_driver! {
|
||||
let space = &info.config_space;
|
||||
|
||||
let transport = PciTransport::from_config_space(space).unwrap();
|
||||
let device = VirtioNet::new(dma.clone(), transport, Some(info.clone()));
|
||||
let device = VirtioNet::new(dma.clone(), transport, info.clone())?;
|
||||
|
||||
let device = Arc::new(device);
|
||||
|
||||
|
@ -1,10 +1,73 @@
|
||||
use alloc::sync::Arc;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::device::Device;
|
||||
|
||||
// TODO refine `clock` parameter types somehow
|
||||
|
||||
pub trait ClockController: Device {
|
||||
fn enable_clock(&self, clock: u32) -> Result<(), Error>;
|
||||
fn disable_clock(&self, clock: u32) -> Result<(), Error>;
|
||||
pub struct ClockHandle {
|
||||
pub parent: Arc<dyn ClockController>,
|
||||
pub clock: Option<u32>,
|
||||
}
|
||||
|
||||
pub struct ResetHandle {
|
||||
pub parent: Arc<dyn ResetController>,
|
||||
pub reset: Option<u32>,
|
||||
}
|
||||
|
||||
pub trait ClockController: Device {
|
||||
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error>;
|
||||
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error>;
|
||||
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
|
||||
let _ = clock;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
fn set_clock_rate(&self, clock: Option<u32>, rate: u64) -> Result<u64, Error> {
|
||||
let _ = clock;
|
||||
let _ = rate;
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait ResetController: Device {
|
||||
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error>;
|
||||
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error>;
|
||||
}
|
||||
|
||||
impl ClockHandle {
|
||||
pub fn enable(&self) -> Result<(), Error> {
|
||||
self.parent.enable_clock(self.clock)
|
||||
}
|
||||
|
||||
pub fn disable(&self) -> Result<(), Error> {
|
||||
self.parent.disable_clock(self.clock)
|
||||
}
|
||||
|
||||
pub fn rate(&self) -> Result<u64, Error> {
|
||||
self.parent.clock_rate(self.clock)
|
||||
}
|
||||
|
||||
pub fn set_rate(&self, rate: u64) -> Result<u64, Error> {
|
||||
self.parent.set_clock_rate(self.clock, rate)
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetHandle {
|
||||
pub fn assert(&self) -> Result<(), Error> {
|
||||
self.parent.assert_reset(self.reset)
|
||||
}
|
||||
|
||||
pub fn deassert(&self) -> Result<(), Error> {
|
||||
self.parent.deassert_reset(self.reset)
|
||||
}
|
||||
|
||||
pub fn assert_for_cycles(&self, cycles: u64) -> Result<(), Error> {
|
||||
self.assert()?;
|
||||
for _ in 0..cycles {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
self.deassert()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,57 @@
|
||||
//! Device-tree handling for controllers of various types: interrupt, clock etc.
|
||||
use alloc::sync::Arc;
|
||||
use device_api::interrupt::FullIrq;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
interrupt::FullIrq,
|
||||
};
|
||||
use fdt_rs::spec::Phandle;
|
||||
|
||||
use crate::TProp;
|
||||
use crate::{DeviceTreePropertyRead, TProp};
|
||||
|
||||
use super::{lookup_phandle, Node};
|
||||
|
||||
pub(crate) struct ClockIter<'dt> {
|
||||
pub(crate) clocks: TProp<'dt>,
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
pub(crate) struct ResetIter<'dt> {
|
||||
pub(crate) resets: TProp<'dt>,
|
||||
pub(crate) offset: usize,
|
||||
}
|
||||
|
||||
impl Iterator for ClockIter<'_> {
|
||||
type Item = ClockHandle;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.clocks.len() {
|
||||
return None;
|
||||
}
|
||||
let phandle = self.clocks.read_cell(self.offset, 1)? as Phandle;
|
||||
let clkc = lookup_phandle(phandle, true)?;
|
||||
let clkc = clkc.clock_controler.try_get()?;
|
||||
let (clock, len) = clkc.clone().map_clock(&self.clocks, self.offset + 1)?;
|
||||
self.offset += len + 1;
|
||||
Some(clock)
|
||||
}
|
||||
}
|
||||
|
||||
impl Iterator for ResetIter<'_> {
|
||||
type Item = ResetHandle;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.offset >= self.resets.len() {
|
||||
return None;
|
||||
}
|
||||
let phandle = self.resets.read_cell(self.offset, 1)? as Phandle;
|
||||
let rstc = lookup_phandle(phandle, true)?;
|
||||
let rstc = rstc.reset_controller.try_get()?;
|
||||
let (reset, len) = rstc.clone().map_reset(&self.resets, self.offset + 1)?;
|
||||
self.offset += len + 1;
|
||||
Some(reset)
|
||||
}
|
||||
}
|
||||
|
||||
// Interrupt controller handling
|
||||
|
||||
/// Reads interrupt information, as interpreted by `interrupt_controller`, from `property` at a
|
||||
|
@ -14,9 +14,21 @@ pub mod util;
|
||||
pub use controller::{map_interrupt, map_interrupt_at};
|
||||
pub use macros::device_tree_driver;
|
||||
pub use registry::{lookup_phandle, register_driver};
|
||||
pub use traits::{DeviceTreeInterruptController, Driver, ProbeContext};
|
||||
pub use traits::{
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
|
||||
ProbeContext,
|
||||
};
|
||||
pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
|
||||
|
||||
/// Specifies initialization sequence requirement for a driver
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum InitSequence {
|
||||
/// Driver can be initialized during early boot
|
||||
Early = 1,
|
||||
/// Driver must be initialized when sleep function is available
|
||||
Late = 2,
|
||||
}
|
||||
|
||||
/// Performs a walk of the device tree and initializes nodes which haven't already been
|
||||
/// initialized/probed.
|
||||
///
|
||||
@ -25,9 +37,10 @@ pub use tree::{find_node, unflatten_device_tree, walk_device_tree, Node};
|
||||
pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
success_handler: S,
|
||||
error_handler: F,
|
||||
sequence: InitSequence,
|
||||
) {
|
||||
walk_device_tree::<(), _>(|node| {
|
||||
match node.clone().lazy_init() {
|
||||
match node.clone().lazy_init(sequence) {
|
||||
Some(Ok(())) => success_handler(node),
|
||||
Some(Err(error)) => error_handler(node, error),
|
||||
None => (),
|
||||
@ -44,9 +57,10 @@ pub fn lazy_init<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
pub fn init_irqs<S: Fn(&Arc<Node>), F: Fn(&Arc<Node>, Error)>(
|
||||
success_handler: S,
|
||||
error_handler: F,
|
||||
sequence: InitSequence,
|
||||
) {
|
||||
walk_device_tree::<(), _>(|node| {
|
||||
match node.clone().init_irqs() {
|
||||
match node.clone().init_irqs(sequence) {
|
||||
Some(Ok(())) => success_handler(node),
|
||||
Some(Err(error)) => error_handler(node, error),
|
||||
None => (),
|
||||
|
@ -43,7 +43,7 @@ pub fn lookup_phandle(phandle: Phandle, ensure_probed: bool) -> Option<Arc<Node>
|
||||
if let Some(node) = node.as_ref()
|
||||
&& ensure_probed
|
||||
{
|
||||
node.clone().probe()?;
|
||||
node.probe()?;
|
||||
}
|
||||
node
|
||||
}
|
||||
|
@ -4,18 +4,19 @@ use core::ops::Range;
|
||||
use alloc::sync::{Arc, Weak};
|
||||
use device_api::{
|
||||
bus::Bus,
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::Device,
|
||||
interrupt::{ExternalInterruptController, FullIrq},
|
||||
};
|
||||
|
||||
use crate::TProp;
|
||||
|
||||
use super::Node;
|
||||
use super::{InitSequence, Node};
|
||||
|
||||
/// Device tree driver interface
|
||||
pub trait Driver: Sync {
|
||||
/// Constructs a [Device] for a given matching node
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>>;
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>>;
|
||||
}
|
||||
|
||||
/// Device-tree based interrupt mapper/controller interface
|
||||
@ -28,9 +29,25 @@ pub trait DeviceTreeInterruptController {
|
||||
fn as_interrupt_controller(self: Arc<Self>) -> Arc<dyn ExternalInterruptController>;
|
||||
}
|
||||
|
||||
/// Device-tree based clock source interface
|
||||
pub trait DeviceTreeClockController {
|
||||
/// Reads clock information from `property` at given `offset` and maps it to a
|
||||
/// [ClockHandle], returning the handle + the size of the clock entry.
|
||||
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)>;
|
||||
}
|
||||
|
||||
/// Device-tree based reset source interface
|
||||
pub trait DeviceTreeResetController {
|
||||
/// Reads reset information from `property` at given `offset` and maps it to a
|
||||
/// [ResetHandle], returning the handle + the size of the reset entry.
|
||||
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)>;
|
||||
}
|
||||
|
||||
/// Context passed to the driver's `probe` function
|
||||
pub struct ProbeContext {
|
||||
pub(crate) bus: Option<Weak<dyn Bus>>,
|
||||
/// Can be used to set a specific initialization sequence for the device
|
||||
pub sequence: Option<InitSequence>,
|
||||
}
|
||||
|
||||
impl ProbeContext {
|
||||
|
@ -7,7 +7,7 @@ use alloc::{
|
||||
};
|
||||
use device_api::{
|
||||
bus::Bus,
|
||||
clock::ClockController,
|
||||
clock::{ClockController, ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{ExternalInterruptController, FullIrq, MessageInterruptController},
|
||||
};
|
||||
@ -20,9 +20,11 @@ use yggdrasil_abi::error::Error;
|
||||
use crate::{DeviceTree, DeviceTreeNodeExt, DeviceTreePropertyRead, TNode, TProp};
|
||||
|
||||
use super::{
|
||||
controller::{ClockIter, ResetIter},
|
||||
lookup_phandle, map_interrupt,
|
||||
registry::{register_phandle, DEVICE_TREE, DRIVERS, ROOT},
|
||||
DeviceTreeInterruptController, Driver, ProbeContext,
|
||||
DeviceTreeClockController, DeviceTreeInterruptController, DeviceTreeResetController, Driver,
|
||||
InitSequence, ProbeContext,
|
||||
};
|
||||
|
||||
/// Represents a single node in the device tree, which may or may not:
|
||||
@ -46,26 +48,27 @@ pub struct Node {
|
||||
|
||||
// Driver/device info
|
||||
device: OneTimeInit<NodeDevice>,
|
||||
init_token: OneTimeInit<()>,
|
||||
pub(crate) interrupt_controller: OneTimeInit<Arc<dyn DeviceTreeInterruptController>>,
|
||||
pub(crate) msi_controller: OneTimeInit<Arc<dyn MessageInterruptController>>,
|
||||
pub(crate) clock_controler: OneTimeInit<Arc<dyn DeviceTreeClockController>>,
|
||||
pub(crate) reset_controller: OneTimeInit<Arc<dyn DeviceTreeResetController>>,
|
||||
}
|
||||
|
||||
pub(crate) struct ProbedDevice {
|
||||
driver: &'static dyn Driver,
|
||||
sequence: InitSequence,
|
||||
init_token: OneTimeInit<Result<(), Error>>,
|
||||
irq_init_token: OneTimeInit<Result<(), Error>>,
|
||||
device: Arc<dyn Device>,
|
||||
}
|
||||
|
||||
enum NodeDevice {
|
||||
// Node probed, no device found
|
||||
Missing,
|
||||
// Node probed and driver found
|
||||
Present {
|
||||
driver: &'static dyn Driver,
|
||||
device: Arc<dyn Device>,
|
||||
},
|
||||
Present(ProbedDevice),
|
||||
}
|
||||
|
||||
// struct NodeDevice {
|
||||
// driver: &'static dyn Driver,
|
||||
// device: Arc<dyn Device>,
|
||||
// }
|
||||
|
||||
struct EnumerationContext {
|
||||
address_cells: usize,
|
||||
size_cells: usize,
|
||||
@ -74,23 +77,39 @@ struct EnumerationContext {
|
||||
}
|
||||
|
||||
impl NodeDevice {
|
||||
fn as_device(&self) -> Option<Arc<dyn Device>> {
|
||||
fn as_probed(&self) -> Option<&ProbedDevice> {
|
||||
match self {
|
||||
Self::Missing => None,
|
||||
Self::Present { device, .. } => Some(device.clone()),
|
||||
}
|
||||
}
|
||||
|
||||
fn driver(&self) -> Option<&'static dyn Driver> {
|
||||
match self {
|
||||
Self::Missing => None,
|
||||
Self::Present { driver, .. } => Some(*driver),
|
||||
Self::Present(probed) => Some(probed),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node {
|
||||
fn probe_upwards(self: Arc<Self>) -> (Option<Arc<dyn Device>>, Option<Weak<dyn Bus>>) {
|
||||
fn probe_single(node: &Arc<Node>, cx: &mut ProbeContext) -> Option<ProbedDevice> {
|
||||
let compatible = node.compatible?;
|
||||
let drivers = DRIVERS.read();
|
||||
let driver = drivers.iter().find(|d| d.matches(compatible));
|
||||
if driver.is_none() {
|
||||
// log::warn!("No driver for {compatible:?}");
|
||||
}
|
||||
let driver = driver?;
|
||||
|
||||
let device = driver.imp.probe(node, cx);
|
||||
|
||||
// Move to early init unless driver wants to sleep
|
||||
let sequence = cx.sequence.unwrap_or(InitSequence::Early);
|
||||
|
||||
device.map(|device| ProbedDevice {
|
||||
driver: driver.imp,
|
||||
sequence,
|
||||
device,
|
||||
init_token: OneTimeInit::new(),
|
||||
irq_init_token: OneTimeInit::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn probe_upwards(self: &Arc<Self>) -> (Option<&ProbedDevice>, Option<Weak<dyn Bus>>) {
|
||||
let mut parent_bus = None;
|
||||
if let Some(parent) = self.parent.as_ref().and_then(Weak::upgrade) {
|
||||
let (_, bus) = parent.probe_upwards();
|
||||
@ -105,47 +124,33 @@ impl Node {
|
||||
}
|
||||
}
|
||||
|
||||
let cx = ProbeContext {
|
||||
let mut cx = ProbeContext {
|
||||
bus: parent_bus.clone(),
|
||||
sequence: None,
|
||||
};
|
||||
|
||||
let inner = self.device.or_init_with_opt(|| {
|
||||
let compatible = self.compatible?;
|
||||
let drivers = DRIVERS.read();
|
||||
let driver = drivers.iter().find(|d| d.matches(compatible));
|
||||
if driver.is_none() {
|
||||
// log::warn!("No driver for {compatible:?}");
|
||||
}
|
||||
let driver = driver?;
|
||||
|
||||
let device = driver.imp.probe(&self, &cx);
|
||||
|
||||
let slot = match device {
|
||||
Some(device) => NodeDevice::Present {
|
||||
driver: driver.imp,
|
||||
device,
|
||||
},
|
||||
let inner = self
|
||||
.device
|
||||
.or_init_with(|| match Self::probe_single(&self, &mut cx) {
|
||||
Some(probed) => NodeDevice::Present(probed),
|
||||
None => NodeDevice::Missing,
|
||||
};
|
||||
Some(slot)
|
||||
});
|
||||
});
|
||||
let probed = inner.as_probed();
|
||||
|
||||
let device = inner.and_then(|d| d.as_device());
|
||||
|
||||
let bus = if let Some(device) = device.as_ref() {
|
||||
device.clone().as_bus().as_ref().map(Arc::downgrade)
|
||||
let bus = if let Some(probed) = probed.as_ref() {
|
||||
probed.device.clone().as_bus().as_ref().map(Arc::downgrade)
|
||||
} else {
|
||||
parent_bus
|
||||
};
|
||||
|
||||
(device, bus)
|
||||
(probed, bus)
|
||||
}
|
||||
|
||||
/// Performs a "probe" of the node by looking up a matching driver. If the node
|
||||
/// has a parent, its parent is probed first.
|
||||
///
|
||||
/// If the node has already been probed, the device reference is just returned instead.
|
||||
pub fn probe(self: Arc<Self>) -> Option<Arc<dyn Device>> {
|
||||
pub(crate) fn probe(self: &Arc<Self>) -> Option<&ProbedDevice> {
|
||||
let (device, _) = self.probe_upwards();
|
||||
device
|
||||
}
|
||||
@ -170,9 +175,22 @@ impl Node {
|
||||
self.msi_controller.init(intc);
|
||||
}
|
||||
|
||||
/// Informs the node of its capability as a reset controller, allowing `resets` to be mapped
|
||||
/// through this device.
|
||||
pub fn make_reset_controller(&self, rstc: Arc<dyn DeviceTreeResetController>) {
|
||||
self.reset_controller.init(rstc);
|
||||
}
|
||||
|
||||
/// Informs the node of its capability as a clock controller, allowing `clocks` to be mapped
|
||||
/// through this device.
|
||||
pub fn make_clock_controller(&self, clkc: Arc<dyn DeviceTreeClockController>) {
|
||||
self.clock_controler.init(clkc);
|
||||
}
|
||||
|
||||
/// Returns the device driver associated with this node, if any was probed.
|
||||
pub fn driver(&self) -> Option<&'static dyn Driver> {
|
||||
self.device.try_get()?.driver()
|
||||
let probed = self.device.try_get()?.as_probed()?;
|
||||
Some(probed.driver)
|
||||
}
|
||||
|
||||
/// Performs a lazy initialization of the node:
|
||||
@ -186,17 +204,20 @@ impl Node {
|
||||
/// * `Some(Ok(()))` - device was probed and initialized (now or before).
|
||||
/// * `Some(Err(...))` - device was probed, but initialization failed.
|
||||
/// * `None` - no driver matched the device.
|
||||
pub fn lazy_init(self: Arc<Self>) -> Option<Result<(), Error>> {
|
||||
let device = self.clone().probe()?;
|
||||
let result = self.init_token.or_try_init_with(|| {
|
||||
let cx = self.make_init_context();
|
||||
unsafe { device.init(cx) }?;
|
||||
Ok(())
|
||||
});
|
||||
match result {
|
||||
Ok(()) => Some(Ok(())),
|
||||
Err(error) => Some(Err(error)),
|
||||
pub fn lazy_init(self: Arc<Self>, sequence: InitSequence) -> Option<Result<(), Error>> {
|
||||
let probed = self.probe()?;
|
||||
|
||||
// Needs to be initialized later
|
||||
if probed.sequence > sequence {
|
||||
return None;
|
||||
}
|
||||
|
||||
let state = probed.init_token.or_init_with(|| {
|
||||
let cx = self.make_init_context();
|
||||
unsafe { probed.device.clone().init(cx) }
|
||||
});
|
||||
|
||||
Some(*state)
|
||||
}
|
||||
|
||||
/// Performs an IRQ initialization for the node's device:
|
||||
@ -205,15 +226,20 @@ impl Node {
|
||||
/// 2. If [Node::lazy_init] succeeds and a device exists, calls [Device::init_irq].
|
||||
///
|
||||
/// Return value is the same as in [Node::lazy_init].
|
||||
pub fn init_irqs(self: Arc<Self>) -> Option<Result<(), Error>> {
|
||||
match self.clone().lazy_init() {
|
||||
pub fn init_irqs(self: Arc<Self>, sequence: InitSequence) -> Option<Result<(), Error>> {
|
||||
let probed = match self.clone().lazy_init(sequence) {
|
||||
Some(Ok(())) => {
|
||||
let device = self.device.get();
|
||||
let status = unsafe { device.as_device()?.init_irq() };
|
||||
Some(status)
|
||||
device.as_probed()?
|
||||
}
|
||||
Some(Err(_)) | None => None,
|
||||
}
|
||||
Some(Err(_)) | None => return None,
|
||||
};
|
||||
|
||||
let state = probed
|
||||
.irq_init_token
|
||||
.or_init_with(|| unsafe { probed.device.clone().init_irq() });
|
||||
|
||||
Some(*state)
|
||||
}
|
||||
|
||||
/// "Forces" an initialization of the device. This is a stricter version of [Node::lazy_init]
|
||||
@ -227,19 +253,20 @@ impl Node {
|
||||
/// * `Ok(())` - if probe/init succeeded.
|
||||
/// * `Err(Error::DoesNotExist)` - couldn't find a device/driver for this node.
|
||||
/// * `Err(other)` - initialization failed.
|
||||
pub fn force_init(self: Arc<Self>) -> Result<(), Error> {
|
||||
let device = self
|
||||
.clone()
|
||||
.probe()
|
||||
.ok_or(Error::DoesNotExist)
|
||||
.inspect_err(|_| log::error!("Does not exist: probe({:?})", self.name))?;
|
||||
pub fn force_init(self: Arc<Self>, sequence: InitSequence) -> Result<(), Error> {
|
||||
let probed = self.probe().ok_or(Error::DoesNotExist)?;
|
||||
|
||||
self.init_token.try_init_with_opt(|| {
|
||||
// Needs to be initialized later
|
||||
if probed.sequence > sequence {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let state = probed.init_token.try_init_with(|| {
|
||||
let cx = self.make_init_context();
|
||||
unsafe { device.init(cx) }?;
|
||||
Ok(())
|
||||
unsafe { probed.device.clone().init(cx) }
|
||||
})?;
|
||||
Ok(())
|
||||
|
||||
*state
|
||||
}
|
||||
|
||||
fn make_init_context(&self) -> DeviceInitContext {
|
||||
@ -287,6 +314,44 @@ impl Node {
|
||||
.map(|range| PhysicalAddress::from_u64(range.start))
|
||||
}
|
||||
|
||||
/// Returns an input `clock` handle for a given reset name
|
||||
pub fn named_clock(&self, name: &str) -> Option<ClockHandle> {
|
||||
self.property("clock-names")?
|
||||
.as_str_list()
|
||||
.position(|n| n == name)
|
||||
.and_then(|i| self.clock(i))
|
||||
}
|
||||
|
||||
/// Returns a `reset` handle for a given reset name
|
||||
pub fn named_reset(&self, name: &str) -> Option<ResetHandle> {
|
||||
self.property("reset-names")?
|
||||
.as_str_list()
|
||||
.position(|n| n == name)
|
||||
.and_then(|i| self.reset(i))
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's input clocks
|
||||
pub fn clocks(&self) -> Option<impl Iterator<Item = ClockHandle>> {
|
||||
let clocks = self.property("clocks")?;
|
||||
Some(ClockIter { clocks, offset: 0 })
|
||||
}
|
||||
|
||||
/// Returns an iterator over the node's resets
|
||||
pub fn resets(&self) -> Option<impl Iterator<Item = ResetHandle>> {
|
||||
let resets = self.property("resets")?;
|
||||
Some(ResetIter { resets, offset: 0 })
|
||||
}
|
||||
|
||||
/// Returns the `index`th input clock of the node
|
||||
pub fn clock(&self, index: usize) -> Option<ClockHandle> {
|
||||
self.clocks()?.nth(index)
|
||||
}
|
||||
|
||||
/// Returns the `index`th reset of the node
|
||||
pub fn reset(&self, index: usize) -> Option<ResetHandle> {
|
||||
self.resets()?.nth(index)
|
||||
}
|
||||
|
||||
/// Reads interrupt information from `interrupts[index]` property, mapped by the node's
|
||||
/// `interrupt-parent`.
|
||||
pub fn interrupt(&self, index: usize) -> Option<FullIrq> {
|
||||
@ -309,8 +374,9 @@ impl Node {
|
||||
|
||||
/// Attempts to get a clock controller represented by this node, if any
|
||||
pub fn as_clock_controller(&self) -> Option<Arc<dyn ClockController>> {
|
||||
let device = self.device.try_get()?;
|
||||
device.as_device()?.as_clock_controller()
|
||||
todo!()
|
||||
// let device = self.device.try_get()?;
|
||||
// device.as_device()?.as_clock_controller()
|
||||
}
|
||||
|
||||
/// Attempts to get an interrupt controller represented by this node, if any
|
||||
@ -349,6 +415,11 @@ impl Node {
|
||||
pub fn prop_usize(&self, name: &str) -> Option<usize> {
|
||||
self.dt_node.prop_cell_usize(name)
|
||||
}
|
||||
|
||||
/// Interprets property `name` as a string value
|
||||
pub fn prop_str(&self, name: &str) -> Option<&'static str> {
|
||||
self.dt_node.prop_string(name)
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Node {
|
||||
@ -396,9 +467,11 @@ fn unflatten_node(
|
||||
parent,
|
||||
|
||||
device: OneTimeInit::new(),
|
||||
init_token: OneTimeInit::new(),
|
||||
|
||||
interrupt_controller: OneTimeInit::new(),
|
||||
msi_controller: OneTimeInit::new(),
|
||||
clock_controler: OneTimeInit::new(),
|
||||
reset_controller: OneTimeInit::new(),
|
||||
});
|
||||
|
||||
if let Some(phandle) = phandle {
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
use alloc::sync::Arc;
|
||||
use device_api::interrupt::{FullIrq, MessageInterruptController};
|
||||
use fdt_rs::prelude::PropReader;
|
||||
use yggdrasil_abi::net::MacAddress;
|
||||
|
||||
use crate::{
|
||||
driver::{lookup_phandle, map_interrupt_at},
|
||||
@ -177,3 +179,19 @@ pub fn pcie_msi_map(node: &Arc<Node>) -> Option<PcieMsiMapIter<'static>> {
|
||||
let msi_map = node.property("msi-map")?;
|
||||
Some(PcieMsiMapIter { msi_map, offset: 0 })
|
||||
}
|
||||
|
||||
/// Reads the configured MAC address from the node's fields in the following order:
|
||||
///
|
||||
/// * "mac-address"
|
||||
/// * "local-mac-address"
|
||||
/// * "address"
|
||||
///
|
||||
/// If none are present, [None] is returned.
|
||||
pub fn read_mac_address(node: &Arc<Node>) -> Option<MacAddress> {
|
||||
let property = node
|
||||
.property("mac-address")
|
||||
.or_else(|| node.property("local-mac-address"))
|
||||
.or_else(|| node.property("address"))?;
|
||||
let mac_bytes: [u8; 6] = property.raw().try_into().ok()?;
|
||||
Some(MacAddress::from(mac_bytes))
|
||||
}
|
||||
|
@ -1,4 +1,6 @@
|
||||
//! Device tree nodes' property accessors
|
||||
use core::marker::PhantomData;
|
||||
|
||||
use fdt_rs::{
|
||||
base::iters::StringPropIter,
|
||||
index::DevTreeIndexProp,
|
||||
@ -9,7 +11,7 @@ use fdt_rs::{
|
||||
pub trait CellTuple: Sized {
|
||||
type Sizes: Copy;
|
||||
|
||||
fn read<P: DeviceTreePropertyRead + ?Sized>(
|
||||
fn read<'a, P: DeviceTreePropertyRead<'a> + ?Sized>(
|
||||
property: &P,
|
||||
offset: usize,
|
||||
sizes: Self::Sizes,
|
||||
@ -18,16 +20,16 @@ pub trait CellTuple: Sized {
|
||||
}
|
||||
|
||||
/// Accessor trait for a device tree property
|
||||
pub trait DeviceTreePropertyRead {
|
||||
pub trait DeviceTreePropertyRead<'a> {
|
||||
/// Reads a cell value of `size` at a given `offset`.
|
||||
///
|
||||
/// **NOTE** supported cell sizes are 1 and 2. If the size is 3 and larger,
|
||||
/// perform multiple split reads instead.
|
||||
fn read_cell(&self, offset: usize, size: usize) -> Option<u64>;
|
||||
/// Reads the property as a single string
|
||||
fn as_str(&self) -> Option<&str>;
|
||||
fn as_str(&self) -> Option<&'a str>;
|
||||
/// Reads the property as an iterator over a `<stringlist>`
|
||||
fn as_str_list(&self) -> impl Iterator<Item = &str>;
|
||||
fn as_str_list(&self) -> impl Iterator<Item = &'a str>;
|
||||
/// Returns the length of the property in bytes
|
||||
fn len(&self) -> usize;
|
||||
|
||||
@ -44,11 +46,12 @@ pub trait DeviceTreePropertyRead {
|
||||
|
||||
/// Reads the property as an iterator over uniformly-sized tuples, specified by the
|
||||
/// `sizes` parameter.
|
||||
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<Self, T> {
|
||||
fn iter_cells<T: CellTuple>(&self, sizes: T::Sizes) -> CellTupleIter<'a, '_, Self, T> {
|
||||
CellTupleIter {
|
||||
property: self,
|
||||
offset: 0,
|
||||
sizes,
|
||||
_pd: PhantomData,
|
||||
}
|
||||
}
|
||||
|
||||
@ -59,10 +62,11 @@ pub trait DeviceTreePropertyRead {
|
||||
}
|
||||
|
||||
/// An iterator over the tuple cells of some device tree node's property
|
||||
pub struct CellTupleIter<'a, P: DeviceTreePropertyRead + ?Sized, T: CellTuple> {
|
||||
property: &'a P,
|
||||
pub struct CellTupleIter<'a, 'p, P: DeviceTreePropertyRead<'a> + ?Sized, T: CellTuple> {
|
||||
property: &'p P,
|
||||
offset: usize,
|
||||
sizes: T::Sizes,
|
||||
_pd: PhantomData<&'a ()>,
|
||||
}
|
||||
|
||||
/// An iterator over the string list propoerty
|
||||
@ -70,7 +74,7 @@ pub struct StringListIter<'a> {
|
||||
inner: StringPropIter<'a>,
|
||||
}
|
||||
|
||||
impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
|
||||
impl<'a> DeviceTreePropertyRead<'a> for DevTreeIndexProp<'a, '_, '_> {
|
||||
fn read_cell(&self, offset: usize, size: usize) -> Option<u64> {
|
||||
match size {
|
||||
1 => self.u32(offset).ok().map(Into::into),
|
||||
@ -84,11 +88,11 @@ impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
fn as_str(&self) -> Option<&str> {
|
||||
fn as_str(&self) -> Option<&'a str> {
|
||||
self.str().ok()
|
||||
}
|
||||
|
||||
fn as_str_list(&self) -> impl Iterator<Item = &str> {
|
||||
fn as_str_list(&self) -> impl Iterator<Item = &'a str> {
|
||||
StringListIter {
|
||||
inner: self.iter_str(),
|
||||
}
|
||||
@ -99,7 +103,9 @@ impl DeviceTreePropertyRead for DevTreeIndexProp<'_, '_, '_> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<P: DeviceTreePropertyRead + ?Sized, T: CellTuple> Iterator for CellTupleIter<'_, P, T> {
|
||||
impl<'a, P: DeviceTreePropertyRead<'a> + ?Sized, T: CellTuple> Iterator
|
||||
for CellTupleIter<'a, '_, P, T>
|
||||
{
|
||||
type Item = T;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
@ -153,7 +159,7 @@ macro impl_cell_tuples(
|
||||
impl CellTuple for impl_cell_tuple_types!($($index (u64))+) {
|
||||
type Sizes = impl_cell_tuple_types!($($index (usize))+);
|
||||
|
||||
fn read<P: DeviceTreePropertyRead + ?Sized>(
|
||||
fn read<'a, P: DeviceTreePropertyRead<'a> + ?Sized>(
|
||||
property: &P,
|
||||
offset: usize,
|
||||
sizes: Self::Sizes,
|
||||
|
@ -49,6 +49,24 @@ impl<K: Hash + Eq, V, const N: usize> HashTable<K, V, DefaultHashBuilder, N> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove<Q>(&mut self, key: &Q) -> Option<V>
|
||||
where
|
||||
Q: Hash + Eq + ?Sized,
|
||||
K: Borrow<Q>,
|
||||
{
|
||||
let h = self.hasher.hash_one(key);
|
||||
let bucket = &mut self.buckets[h as usize % self.buckets.len()];
|
||||
|
||||
for i in 0..bucket.len() {
|
||||
if bucket[i].0.borrow() == key {
|
||||
let (_, value) = bucket.remove(i);
|
||||
return Some(value);
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn get<Q>(&self, key: &Q) -> Option<&V>
|
||||
where
|
||||
Q: Hash + Eq + ?Sized,
|
||||
|
@ -4,14 +4,19 @@ use alloc::{
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
};
|
||||
use core::{fmt, str::FromStr};
|
||||
use core::{
|
||||
fmt,
|
||||
str::FromStr,
|
||||
sync::atomic::{AtomicBool, AtomicU32, Ordering},
|
||||
};
|
||||
use ring::RingLoggerSink;
|
||||
|
||||
use libk_util::{sync::IrqSafeSpinlock, OneTimeInit};
|
||||
use libk_util::OneTimeInit;
|
||||
use sink::DEBUG_SINKS;
|
||||
use yggdrasil_abi::error::Error;
|
||||
|
||||
use crate::{
|
||||
arch::Cpu,
|
||||
fs::sysfs::{
|
||||
attribute::{StringAttribute, StringAttributeOps},
|
||||
object::KObject,
|
||||
@ -27,7 +32,23 @@ pub use panic::{panic_log, PanicLoggerSink};
|
||||
pub use ring::add_kernel_log_file;
|
||||
pub use sink::{add_early_sink, add_serial_sink, add_sink, disable_early_sinks, DebugSink};
|
||||
|
||||
static DEBUG_LOCK: IrqSafeSpinlock<()> = IrqSafeSpinlock::new(());
|
||||
static DEBUG_LOCK: AtomicU32 = AtomicU32::new(u32::MAX);
|
||||
static MUTE_DEBUG: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
pub struct MuteGuard(bool);
|
||||
|
||||
impl MuteGuard {
|
||||
pub fn acquire() -> Self {
|
||||
let muted = MUTE_DEBUG.swap(true, Ordering::Acquire);
|
||||
Self(muted)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MuteGuard {
|
||||
fn drop(&mut self) {
|
||||
MUTE_DEBUG.store(self.0, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
struct KernelLoggerSink;
|
||||
|
||||
@ -84,24 +105,42 @@ impl From<log::Level> for LogLevel {
|
||||
}
|
||||
}
|
||||
|
||||
fn lock_debug() -> bool {
|
||||
let cpu = Cpu::try_local().map_or(0, |cpu| cpu.id());
|
||||
loop {
|
||||
match DEBUG_LOCK.compare_exchange(u32::MAX, cpu, Ordering::Acquire, Ordering::Relaxed) {
|
||||
Ok(_) => return true,
|
||||
Err(x) if x == cpu => return false,
|
||||
_ => core::hint::spin_loop(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl log::Log for KernelLoggerSink {
|
||||
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||
metadata.target() != "io"
|
||||
}
|
||||
|
||||
fn log(&self, record: &log::Record) {
|
||||
if MUTE_DEBUG.load(Ordering::Acquire) {
|
||||
return;
|
||||
}
|
||||
|
||||
if !self.enabled(record.metadata()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RingLoggerSink.log(record);
|
||||
|
||||
let _guard = DEBUG_LOCK.lock();
|
||||
if !lock_debug() {
|
||||
return;
|
||||
}
|
||||
for sink in DEBUG_SINKS.read().iter() {
|
||||
if sink.enabled(record.metadata()) {
|
||||
sink.log(record);
|
||||
}
|
||||
}
|
||||
DEBUG_LOCK.store(u32::MAX, Ordering::Release);
|
||||
}
|
||||
|
||||
fn flush(&self) {}
|
||||
|
@ -247,6 +247,7 @@ impl<T: ?Sized> Drop for DmaBuffer<T> {
|
||||
log::trace!("Drop DmaBuffer @ {:#x}", self.host_physical);
|
||||
unsafe {
|
||||
ptr::drop_in_place(self.host_pointer.as_ptr());
|
||||
#[cfg(any(rust_analyzer, target_os = "none"))]
|
||||
for i in 0..self.page_count {
|
||||
phys::free_page(self.host_physical.add(i * L3_PAGE_SIZE));
|
||||
}
|
||||
|
@ -7,7 +7,10 @@ use device_api::{
|
||||
interrupt::{IpiDeliveryTarget, IpiMessage},
|
||||
ResetDevice,
|
||||
};
|
||||
use device_tree::{driver::unflatten_device_tree, DeviceTree, DeviceTreeNodeExt};
|
||||
use device_tree::{
|
||||
driver::{unflatten_device_tree, InitSequence},
|
||||
DeviceTree, DeviceTreeNodeExt,
|
||||
};
|
||||
use kernel_arch::Architecture;
|
||||
use kernel_arch_riscv64::{
|
||||
mem::{self, KERNEL_VIRT_OFFSET},
|
||||
@ -225,6 +228,7 @@ impl Riscv64 {
|
||||
|node, error| {
|
||||
log::error!("{}: {error:?}", node.name().unwrap_or("<unknown>"));
|
||||
},
|
||||
InitSequence::Early,
|
||||
);
|
||||
device_tree::driver::init_irqs(
|
||||
|_| (),
|
||||
@ -234,6 +238,7 @@ impl Riscv64 {
|
||||
node.name().unwrap_or("<unknown>")
|
||||
);
|
||||
},
|
||||
InitSequence::Early,
|
||||
);
|
||||
|
||||
PciBusManager::probe_bus_devices()?;
|
||||
@ -283,7 +288,7 @@ impl Riscv64 {
|
||||
|
||||
if let Some(node) = node {
|
||||
log::info!("Probe chosen stdout: {:?}", node.name());
|
||||
node.force_init()?;
|
||||
node.force_init(InitSequence::Early)?;
|
||||
}
|
||||
|
||||
// No stdout
|
||||
|
@ -121,7 +121,7 @@ fn make_msi_map(node: &Arc<Node>) -> Option<PciMsiMap> {
|
||||
device_tree_driver! {
|
||||
compatible: ["pci-host-ecam-generic"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let ecam_base = node.map_base(context, 0)?;
|
||||
let ranges = node.property("ranges")?;
|
||||
|
||||
|
@ -58,7 +58,7 @@ impl Bus for SimpleBus {
|
||||
device_tree_driver! {
|
||||
compatible: ["simple-bus"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
// Format per DT spec: (child-bus-address, parent-bus-address, length)
|
||||
// Where:
|
||||
// child-bus-address: #address-cells of this node
|
||||
|
75
kernel/src/device/clock/fixed.rs
Normal file
75
kernel/src/device/clock/fixed.rs
Normal file
@ -0,0 +1,75 @@
|
||||
//! Fixed clock driver
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{device_tree_driver, DeviceTreeClockController, Node, ProbeContext},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
|
||||
struct FixedClock {
|
||||
name: &'static str,
|
||||
frequency: u64,
|
||||
}
|
||||
|
||||
impl Device for FixedClock {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
self.name
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockController for FixedClock {
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
|
||||
debug_assert!(clock.is_none());
|
||||
Ok(self.frequency)
|
||||
}
|
||||
|
||||
fn set_clock_rate(&self, _clock: Option<u32>, _rate: u64) -> Result<u64, Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn enable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
|
||||
fn disable_clock(&self, _clock: Option<u32>) -> Result<(), Error> {
|
||||
Err(Error::InvalidOperation)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for FixedClock {
|
||||
fn map_clock(
|
||||
self: Arc<Self>,
|
||||
_property: &TProp,
|
||||
_offset: usize,
|
||||
) -> Option<(ClockHandle, usize)> {
|
||||
Some((
|
||||
ClockHandle {
|
||||
clock: None,
|
||||
parent: self,
|
||||
},
|
||||
0,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["fixed-clock"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, _context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let name = node.name()?;
|
||||
let frequency = node.property("clock-frequency")?.read_cell(0, 1)?;
|
||||
log::info!("fixed-clock {name}: {frequency}");
|
||||
let fixed = Arc::new(FixedClock { name, frequency });
|
||||
node.make_clock_controller(fixed.clone());
|
||||
Some(fixed)
|
||||
}
|
||||
}
|
||||
}
|
211
kernel/src/device/clock/jh7110_aoncrg.rs
Normal file
211
kernel/src/device/clock/jh7110_aoncrg.rs
Normal file
@ -0,0 +1,211 @@
|
||||
//! Starfive JH7110 Always-on CRG
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetController, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver, DeviceTreeClockController, DeviceTreeResetController, Node,
|
||||
ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIo};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
use tock_registers::{
|
||||
interfaces::{Readable, Writeable},
|
||||
register_structs,
|
||||
registers::ReadWrite,
|
||||
};
|
||||
|
||||
const PARENT_OSC: usize = 0;
|
||||
// const PARENT_GMAC0_RMII_REFIN: usize = 1;
|
||||
// const PARENT_GMAC0_RGMII_RXIN: usize = 2;
|
||||
const PARENT_STG_AXIAHB: usize = 3;
|
||||
// const PARENT_APB_BUS: usize = 4;
|
||||
// const PARENT_GMAC0_GTXCLK: usize = 5;
|
||||
// const PARENT_RTC_OSC: usize = 6;
|
||||
|
||||
register_structs! {
|
||||
#[allow(non_snake_case)]
|
||||
Regs {
|
||||
(0x000 => CLOCKS: [ReadWrite<u32>; 14]),
|
||||
(0x038 => RESETS: ReadWrite<u32>),
|
||||
(0x03C => AONCRG_RESET_STATUS: ReadWrite<u32>),
|
||||
(0x040 => @END),
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockRegister {
|
||||
Gate(u32, ClockParent),
|
||||
#[allow(unused)]
|
||||
FixedDiv(u32, ClockParent),
|
||||
// TODO
|
||||
Fixed,
|
||||
Unimp,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockParent {
|
||||
Int(u32),
|
||||
Ext(usize),
|
||||
}
|
||||
|
||||
/// JH7110 AONCRG driver
|
||||
pub struct Aoncrg {
|
||||
base: PhysicalAddress,
|
||||
clock_parents: [ClockHandle; 7],
|
||||
mapping: IrqSafeSpinlock<DeviceMemoryIo<'static, Regs>>,
|
||||
}
|
||||
|
||||
impl Aoncrg {
|
||||
fn clk_enable_int(&self, index: u32) -> Result<(), Error> {
|
||||
let (name, reg) = Self::map_clock_index(index)
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", index))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Fixed => Ok(()),
|
||||
ClockRegister::Gate(bit, parent) => {
|
||||
self.clk_enable_parent(parent)?;
|
||||
log::info!("jh7110-aoncrg: enable {name:?} @ {index}");
|
||||
let lock = self.mapping.lock();
|
||||
lock.CLOCKS[index as usize].set(lock.CLOCKS[index as usize].get() | (1 << bit));
|
||||
Ok(())
|
||||
}
|
||||
ClockRegister::Unimp => {
|
||||
log::warn!("jh7110-aoncrg: unimplemented clock {name:?}");
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
ClockRegister::FixedDiv(_, parent) => {
|
||||
self.clk_enable_parent(parent)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clk_enable_parent(&self, parent: ClockParent) -> Result<(), Error> {
|
||||
match parent {
|
||||
ClockParent::Int(index) => self.clk_enable_int(index),
|
||||
ClockParent::Ext(clock) => self.clock_parents[clock].enable(),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_clock_index(index: u32) -> Option<(&'static str, ClockRegister)> {
|
||||
use ClockParent::*;
|
||||
use ClockRegister::*;
|
||||
|
||||
const CLOCK_MAP: &[(&'static str, ClockRegister)] = &[
|
||||
/* 0 */ ("clk_osc", FixedDiv(4, Ext(PARENT_OSC))),
|
||||
/* 1 */ ("clk_aon_apb_func", Unimp),
|
||||
/* 2 */ ("clk_gmac5_ahb", Gate(31, Ext(PARENT_STG_AXIAHB))),
|
||||
/* 3 */ ("clk_gmac5_axi", Gate(31, Ext(PARENT_STG_AXIAHB))),
|
||||
/* 4 */ ("clk_gmac5_rmii_rtx", Unimp),
|
||||
/* 5 */ ("clk_gmac5_axi64_tx", Fixed),
|
||||
/* 6 */ ("clk_gmac5_axi64_tx_inv", Gate(30, Int(5))),
|
||||
];
|
||||
|
||||
CLOCK_MAP.get(index as usize).copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Aoncrg {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
log::warn!("TODO: init jh7110-aoncrg @ {:#x}", self.base);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Starfive JH7110 AONCRG"
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockController for Aoncrg {
|
||||
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
let index = clock.ok_or(Error::InvalidArgument)?;
|
||||
self.clk_enable_int(index)
|
||||
}
|
||||
|
||||
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
log::warn!("jh7110: disable clock {clock:?}");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetController for Aoncrg {
|
||||
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
let regs = self.mapping.lock();
|
||||
let val = regs.RESETS.get();
|
||||
regs.RESETS.set(val | (1 << reset));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
let regs = self.mapping.lock();
|
||||
let val = regs.RESETS.get();
|
||||
regs.RESETS.set(val & !(1 << reset));
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for Aoncrg {
|
||||
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
|
||||
let cell = property.read_cell(offset, 1)? as u32;
|
||||
Some((
|
||||
ClockHandle {
|
||||
parent: self,
|
||||
clock: Some(cell),
|
||||
},
|
||||
1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeResetController for Aoncrg {
|
||||
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)> {
|
||||
let cell = property.read_cell(offset, 1)? as u32;
|
||||
Some((
|
||||
ResetHandle {
|
||||
parent: self,
|
||||
reset: Some(cell),
|
||||
},
|
||||
1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-aoncrg"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
|
||||
let clock_parents = [
|
||||
node.named_clock("osc")?,
|
||||
node.named_clock("gmac0_rmii_refin")?,
|
||||
node.named_clock("gmac0_rgmii_rxin")?,
|
||||
node.named_clock("stg_axiahb")?,
|
||||
node.named_clock("apb_bus")?,
|
||||
node.named_clock("gmac0_gtxclk")?,
|
||||
node.named_clock("rtc_osc")?,
|
||||
];
|
||||
|
||||
let mapping = unsafe { DeviceMemoryIo::map(base, Default::default()) }.ok()?;
|
||||
|
||||
let aoncrg = Arc::new(Aoncrg {
|
||||
base,
|
||||
clock_parents,
|
||||
mapping: IrqSafeSpinlock::new(mapping)
|
||||
});
|
||||
|
||||
node.make_reset_controller(aoncrg.clone());
|
||||
node.make_clock_controller(aoncrg.clone());
|
||||
Some(aoncrg)
|
||||
}
|
||||
}
|
||||
}
|
263
kernel/src/device/clock/jh7110_syscrg.rs
Normal file
263
kernel/src/device/clock/jh7110_syscrg.rs
Normal file
@ -0,0 +1,263 @@
|
||||
//! Starfive JH7110 System Control Registers
|
||||
|
||||
use abi::error::Error;
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockController, ClockHandle, ResetController, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
};
|
||||
use device_tree::{
|
||||
driver::{
|
||||
device_tree_driver, DeviceTreeClockController, DeviceTreeResetController, Node,
|
||||
ProbeContext,
|
||||
},
|
||||
DeviceTreePropertyRead, TProp,
|
||||
};
|
||||
use libk_mm::{address::PhysicalAddress, device::DeviceMemoryIoMut};
|
||||
use libk_util::sync::IrqSafeSpinlock;
|
||||
|
||||
const PARENT_CLK_OSC: usize = 0;
|
||||
|
||||
struct Syscrg {
|
||||
base: PhysicalAddress,
|
||||
parents: [ClockHandle; 1],
|
||||
mapping: IrqSafeSpinlock<DeviceMemoryIoMut<'static, [u32]>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockParent {
|
||||
Int(u32),
|
||||
Ext(usize),
|
||||
None,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum ClockRegister {
|
||||
// Clock gate
|
||||
Gate(u32, ClockParent),
|
||||
// Clock inverter
|
||||
Inv(u32, ClockParent),
|
||||
// Clock divider (TODO)
|
||||
Div(ClockParent),
|
||||
Unimp,
|
||||
}
|
||||
|
||||
impl Syscrg {
|
||||
fn clk_enable_int(&self, index: u32, depth: u32) -> Result<(), Error> {
|
||||
let (name, reg) = Self::map_clock_index(index)
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", index))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Gate(bit, parent) => {
|
||||
log::info!("jh7110-syscrg: enable {name:?} @ {index} /{depth}");
|
||||
self.clk_enable_parent(parent, depth)?;
|
||||
let mut lock = self.mapping.lock();
|
||||
lock[index as usize] |= 1 << bit;
|
||||
Ok(())
|
||||
}
|
||||
ClockRegister::Inv(bit, parent) => {
|
||||
log::info!("jh7110-syscrg: enable clk inv {name:?} @ {index}");
|
||||
self.clk_enable_parent(parent, depth)?;
|
||||
let mut lock = self.mapping.lock();
|
||||
lock[index as usize] |= 1 << bit;
|
||||
Ok(())
|
||||
}
|
||||
ClockRegister::Div(parent) => self.clk_enable_parent(parent, depth),
|
||||
ClockRegister::Unimp => {
|
||||
log::warn!("jh7110-syscrg: clock not implemented: {name:?}");
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clk_enable_parent(&self, parent: ClockParent, depth: u32) -> Result<(), Error> {
|
||||
match parent {
|
||||
ClockParent::Int(index) => self.clk_enable_int(index, depth + 1),
|
||||
ClockParent::Ext(clock) => self.parents.get(clock).ok_or(Error::DoesNotExist)?.enable(),
|
||||
ClockParent::None => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
fn map_clock_index(index: u32) -> Option<(&'static str, ClockRegister)> {
|
||||
const CLOCKS: &[(&'static str, ClockRegister)] = &const {
|
||||
use ClockParent::*;
|
||||
use ClockRegister::*;
|
||||
|
||||
let mut t = [("", Unimp); 256];
|
||||
|
||||
t[0x01C / 4] = ("clk_axi_cfg0", Div(None));
|
||||
t[0x020 / 4] = ("clk_stg_axiahb", Div(Int(0x01C / 4)));
|
||||
t[0x024 / 4] = ("clk_ahb0", Gate(31, Int(0x020 / 4)));
|
||||
|
||||
t[0x184 / 4] = ("clk_gmac5_axi64_ahb", Gate(31, Int(0x024 / 4)));
|
||||
t[0x188 / 4] = ("clk_gmac5_axi64_axi", Gate(31, Int(0x020 / 4)));
|
||||
t[0x18C / 4] = ("clk_gmac_source", Div(None));
|
||||
t[0x190 / 4] = ("clk_gmac1_gtx", Gate(31, None));
|
||||
t[0x194 / 4] = ("clk_gmac1_rmii_rtx", Unimp);
|
||||
t[0x198 / 4] = ("clk_gmac5_axi64_ptp", Gate(31, Int(0x18C / 4)));
|
||||
t[0x19C / 4] = ("clk_gmac5_axi64_rx", Unimp);
|
||||
t[0x1A0 / 4] = ("clk_gmac5_axi64_rx_inv", Inv(30, Int(0x19C / 4)));
|
||||
t[0x1A4 / 4] = ("clk_gmac5_axi64_tx", Gate(31, None));
|
||||
t[0x1A8 / 4] = ("clk_gmac5_axi64_tx_inv", Inv(30, Int(0x1A4 / 4)));
|
||||
t[0x1AC / 4] = ("clk_gmac1_gtxc", Gate(31, Int(0x190 / 4)));
|
||||
t[0x1B0 / 4] = ("clk_gmac0_gtx", Gate(31, None));
|
||||
t[0x1B4 / 4] = ("clk_gmac0_ptp", Gate(31, Int(0x18C / 4)));
|
||||
t[0x1B8 / 4] = ("clk_gmac_phy", Gate(31, None));
|
||||
t[0x1BC / 4] = ("clk_gmac0_gtxc", Gate(31, Int(0x1B0 / 4)));
|
||||
|
||||
t[0x244 / 4] = ("clk_u0_uart_apb", Gate(31, None));
|
||||
t[0x248 / 4] = ("clk_u0_uart_core", Gate(31, Ext(PARENT_CLK_OSC)));
|
||||
|
||||
t
|
||||
};
|
||||
|
||||
let index = index as usize;
|
||||
CLOCKS.get(index).copied()
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for Syscrg {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
log::warn!("TODO: init jh7110-syscrg @ {:#x}", self.base);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn display_name(&self) -> &str {
|
||||
"Starfive JH7110 SYSCRG"
|
||||
}
|
||||
}
|
||||
|
||||
impl ClockController for Syscrg {
|
||||
fn enable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
let index = clock.ok_or(Error::InvalidArgument)?;
|
||||
self.clk_enable_int(index, 0)
|
||||
}
|
||||
|
||||
fn disable_clock(&self, clock: Option<u32>) -> Result<(), Error> {
|
||||
log::warn!("TODO: jh7110-syscrg: disable clock {clock:?}");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn clock_rate(&self, clock: Option<u32>) -> Result<u64, Error> {
|
||||
let (name, reg) = clock
|
||||
.and_then(Self::map_clock_index)
|
||||
.ok_or(Error::InvalidArgument)
|
||||
.inspect_err(|_| log::warn!("jh7110-syscrg: undefined clock {:?}", clock))?;
|
||||
|
||||
match reg {
|
||||
ClockRegister::Gate(_, parent) => match parent {
|
||||
ClockParent::Ext(n) => self.parents[n].rate(),
|
||||
ClockParent::Int(_) => {
|
||||
log::warn!("jh7110-syscrg: todo internal clock parents: {:?}", name);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
ClockParent::None => {
|
||||
log::warn!("jh7110-syscrg: clock parent not specified {:?}", name);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
},
|
||||
ClockRegister::Unimp | ClockRegister::Div(_) | ClockRegister::Inv(_, _) => {
|
||||
log::warn!("jh7110-syscrg: unimplemented clock {:?}", name);
|
||||
Err(Error::NotImplemented)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_clock_rate(&self, clock: Option<u32>, rate: u64) -> Result<u64, Error> {
|
||||
log::warn!("TODO: jh7110-syscrg: set rate {clock:?} -> {rate}");
|
||||
Ok(rate)
|
||||
}
|
||||
}
|
||||
|
||||
impl Syscrg {
|
||||
fn rst_trigger(&self, reset: u32, assert: bool) -> Result<(), Error> {
|
||||
let reg = (190 + reset / 32) as usize;
|
||||
let bit = reset % 32;
|
||||
let mut regs = self.mapping.lock();
|
||||
|
||||
let expect = if assert {
|
||||
regs[reg] |= 1 << bit;
|
||||
1 << bit
|
||||
} else {
|
||||
regs[reg] &= !(1 << bit);
|
||||
0
|
||||
};
|
||||
|
||||
let mut timeout = 1 << 20;
|
||||
while timeout > 0 && regs[reg] & (1 << bit) != expect {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
|
||||
if timeout > 0 {
|
||||
Ok(())
|
||||
} else {
|
||||
log::warn!("jh7110-syscrg: reset timeout {reset}");
|
||||
Err(Error::TimedOut)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ResetController for Syscrg {
|
||||
fn assert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
self.rst_trigger(reset, true)
|
||||
}
|
||||
|
||||
fn deassert_reset(&self, reset: Option<u32>) -> Result<(), Error> {
|
||||
let reset = reset.ok_or(Error::InvalidArgument)?;
|
||||
self.rst_trigger(reset, false)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeClockController for Syscrg {
|
||||
fn map_clock(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ClockHandle, usize)> {
|
||||
let clock = property.read_cell(offset, 1)? as u32;
|
||||
Some((
|
||||
ClockHandle {
|
||||
clock: Some(clock),
|
||||
parent: self.clone(),
|
||||
},
|
||||
1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceTreeResetController for Syscrg {
|
||||
fn map_reset(self: Arc<Self>, property: &TProp, offset: usize) -> Option<(ResetHandle, usize)> {
|
||||
let reset = property.read_cell(offset, 1)? as u32;
|
||||
Some((
|
||||
ResetHandle {
|
||||
reset: Some(reset),
|
||||
parent: self.clone(),
|
||||
},
|
||||
1,
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl Sync for Syscrg {}
|
||||
unsafe impl Send for Syscrg {}
|
||||
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-syscrg"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let osc = node.named_clock("osc")?;
|
||||
let mapping = unsafe { DeviceMemoryIoMut::map_slice(base, 256, Default::default()) }
|
||||
.inspect_err(|error| log::error!("jh7110-syscrg: {error:?}"))
|
||||
.ok()?;
|
||||
|
||||
let syscrg = Arc::new(Syscrg {
|
||||
base,
|
||||
parents: [osc],
|
||||
mapping: IrqSafeSpinlock::new(mapping),
|
||||
});
|
||||
node.make_reset_controller(syscrg.clone());
|
||||
node.make_clock_controller(syscrg.clone());
|
||||
Some(syscrg)
|
||||
}
|
||||
}
|
||||
}
|
@ -2,3 +2,11 @@
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", rust_analyzer))]
|
||||
pub mod bcm2835_aux;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_aoncrg;
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod jh7110_syscrg;
|
||||
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64", rust_analyzer))]
|
||||
pub mod fixed;
|
||||
|
@ -82,7 +82,7 @@ struct Context {
|
||||
enable: IrqSafeRwLock<DeviceMemoryIo<'static, ContextEnableRegs>>,
|
||||
control: IrqSafeRwLock<DeviceMemoryIo<'static, ContextControlRegs>>,
|
||||
// TODO scale the table depending on effective MAX_IRQS value
|
||||
table: IrqSafeRwLock<FixedInterruptTable<64>>,
|
||||
table: IrqSafeRwLock<FixedInterruptTable<128>>,
|
||||
}
|
||||
|
||||
struct Inner {
|
||||
@ -288,7 +288,7 @@ fn map_context_to_hart(target: u32) -> Option<u32> {
|
||||
device_tree_driver! {
|
||||
compatible: ["starfive,jh7110-plic", "sifive,plic-1.0.0", "riscv,plic0"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let ndev = node.prop_usize("riscv,ndev")?;
|
||||
let iext = node.property("interrupts-extended")?;
|
||||
|
@ -190,7 +190,7 @@ impl DebugSink for Ns16550a {
|
||||
device_tree_driver!(
|
||||
compatible: ["ns16550a"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
log::debug!("ns16550a base = {base:#x}");
|
||||
let irq = node.interrupt(0)?;
|
||||
|
@ -2,12 +2,13 @@
|
||||
use abi::{error::Error, io::TerminalOptions};
|
||||
use alloc::sync::Arc;
|
||||
use device_api::{
|
||||
clock::{ClockHandle, ResetHandle},
|
||||
device::{Device, DeviceInitContext},
|
||||
interrupt::{FullIrq, InterruptHandler, IrqVector},
|
||||
};
|
||||
use device_tree::driver::{device_tree_driver, Node, ProbeContext};
|
||||
use libk::{
|
||||
debug::DebugSink,
|
||||
debug::{self, DebugSink},
|
||||
device::{external_interrupt_controller, manager::DEVICE_REGISTRY},
|
||||
vfs::{Terminal, TerminalInput, TerminalOutput},
|
||||
};
|
||||
@ -21,20 +22,29 @@ use tock_registers::{
|
||||
|
||||
register_bitfields! {
|
||||
u32,
|
||||
LCR [
|
||||
DLAB OFFSET(7) NUMBITS(1) [],
|
||||
DLS OFFSET(0) NUMBITS(2) [
|
||||
DL8b = 0b11,
|
||||
],
|
||||
],
|
||||
IER [
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
PTIME OFFSET(7) NUMBITS(1) [],
|
||||
EDSSI OFFSET(3) NUMBITS(1) [],
|
||||
ELSI OFFSET(2) NUMBITS(1) [],
|
||||
// Transmit buffer available
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
ETBEI OFFSET(1) NUMBITS(1) [],
|
||||
// Receive data available
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
ERBFI OFFSET(0) NUMBITS(1) [],
|
||||
],
|
||||
LSR [
|
||||
// Data ready bit
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
DR OFFSET(0) NUMBITS(1) [],
|
||||
// Transmitter holding register empty
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
THRE OFFSET(5) NUMBITS(1) [],
|
||||
],
|
||||
USR [
|
||||
BUSY OFFSET(0) NUMBITS(1) [],
|
||||
]
|
||||
}
|
||||
|
||||
@ -50,7 +60,7 @@ register_structs! {
|
||||
// Read: interrupt identification register/Write: frame control register
|
||||
(0x008 => IIR: ReadWrite<u32>),
|
||||
// Line control register
|
||||
(0x00C => LCR: ReadWrite<u32>),
|
||||
(0x00C => LCR: ReadWrite<u32, LCR::Register>),
|
||||
// Modem control register
|
||||
(0x010 => MCR: ReadWrite<u32>),
|
||||
// Line status register
|
||||
@ -69,7 +79,7 @@ register_structs! {
|
||||
(0x070 => FAR: ReadWrite<u32>),
|
||||
(0x074 => TFR: ReadOnly<u32>),
|
||||
(0x078 => RFW: WriteOnly<u32>),
|
||||
(0x07C => USR: ReadOnly<u32>),
|
||||
(0x07C => USR: ReadOnly<u32, USR::Register>),
|
||||
(0x080 => TFL: ReadOnly<u32>),
|
||||
(0x084 => RFL: ReadOnly<u32>),
|
||||
(0x088 => SRR: WriteOnly<u32>),
|
||||
@ -101,6 +111,11 @@ struct Inner {
|
||||
pub struct DwUart {
|
||||
base: PhysicalAddress,
|
||||
irq: FullIrq,
|
||||
clk_baud: ClockHandle,
|
||||
#[allow(unused)]
|
||||
clk_apb: Option<ClockHandle>,
|
||||
rst: Option<ResetHandle>,
|
||||
|
||||
inner: OneTimeInit<Arc<Terminal<Inner>>>,
|
||||
}
|
||||
|
||||
@ -117,8 +132,33 @@ impl Io {
|
||||
self.regs.DR.set(byte as u32);
|
||||
}
|
||||
|
||||
fn init(&mut self) {
|
||||
fn init(&mut self, baud_clock: u64, baud_rate: u64) {
|
||||
let divisor = (baud_clock / (baud_rate * 16)) as u32;
|
||||
|
||||
self.wait_busy();
|
||||
self.regs.IER.set(0);
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.LSR.get();
|
||||
}
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLAB::SET);
|
||||
self.wait_busy();
|
||||
self.regs.DR.set(divisor & 0xFF);
|
||||
self.regs.IER.set((divisor >> 8) & 0xFF);
|
||||
self.wait_busy();
|
||||
self.regs.LCR.write(LCR::DLS::DL8b);
|
||||
self.wait_busy();
|
||||
self.regs.IIR.set(0x01);
|
||||
self.wait_busy();
|
||||
self.regs.MCR.set(0x00);
|
||||
let _ = self.regs.LSR.get();
|
||||
for _ in 0..100 {
|
||||
let _ = self.regs.DR.get();
|
||||
}
|
||||
self.regs.SCR.set(0x00);
|
||||
}
|
||||
|
||||
fn handle_irq(&mut self) -> Option<u8> {
|
||||
@ -130,6 +170,17 @@ impl Io {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn wait_busy(&self) {
|
||||
for _ in 0..100000 {
|
||||
core::hint::spin_loop();
|
||||
}
|
||||
let mut timeout = 1000000;
|
||||
while timeout > 0 && self.regs.USR.matches_all(USR::BUSY::SET) {
|
||||
core::hint::spin_loop();
|
||||
timeout -= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl InterruptHandler for DwUart {
|
||||
@ -149,9 +200,26 @@ impl InterruptHandler for DwUart {
|
||||
|
||||
impl Device for DwUart {
|
||||
unsafe fn init(self: Arc<Self>, _cx: DeviceInitContext) -> Result<(), Error> {
|
||||
let regs = DeviceMemoryIo::map(self.base, Default::default())?;
|
||||
let baud_rate = 115200;
|
||||
let clk_baud_rate = self.clk_baud.rate()?;
|
||||
|
||||
// Prevent firmware (SBI in riscv64) from printing to UART while it's being
|
||||
// reset/initialized
|
||||
let guard = debug::MuteGuard::acquire();
|
||||
|
||||
let regs = DeviceMemoryIo::<Regs>::map(self.base, Default::default())?;
|
||||
let mut io = Io { regs };
|
||||
io.init();
|
||||
|
||||
if let Some(reset) = self.rst.as_ref() {
|
||||
reset.assert_for_cycles(100000)?;
|
||||
}
|
||||
|
||||
io.init(clk_baud_rate, baud_rate);
|
||||
|
||||
io.send(b'\r');
|
||||
io.send(b'\n');
|
||||
|
||||
drop(guard);
|
||||
|
||||
let input = TerminalInput::with_capacity(64)?;
|
||||
let output = Inner {
|
||||
@ -216,13 +284,20 @@ impl TerminalOutput for Inner {
|
||||
device_tree_driver! {
|
||||
compatible: ["snps,dw-apb-uart"],
|
||||
driver: {
|
||||
fn probe(&self, node: &Arc<Node>, context: &ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
fn probe(&self, node: &Arc<Node>, context: &mut ProbeContext) -> Option<Arc<dyn Device>> {
|
||||
let base = node.map_base(context, 0)?;
|
||||
let irq = node.interrupt(0)?;
|
||||
let clk_baud = node.named_clock("baudclk")?;
|
||||
let clk_apb = node.named_clock("apb_pclk");
|
||||
let rst = node.reset(0);
|
||||
|
||||
Some(Arc::new(DwUart {
|
||||
base,
|
||||
irq,
|
||||
clk_baud,
|
||||
clk_apb,
|
||||
rst,
|
||||
|
||||
inner: OneTimeInit::new()
|
||||
}))
|
||||
}
|
||||
|
@ -33,6 +33,29 @@ pub fn kinit() -> Result<(), Error> {
|
||||
assert!(!ArchitectureImpl::interrupt_mask());
|
||||
log::info!("In main");
|
||||
|
||||
// Initialize late device tree devices
|
||||
#[cfg(any(rust_analyzer, target_arch = "riscv64", target_arch = "aarch64"))]
|
||||
{
|
||||
use device_tree::driver::InitSequence;
|
||||
|
||||
device_tree::driver::lazy_init(
|
||||
|_| (),
|
||||
|node, error| {
|
||||
log::error!("{}: {error:?}", node.name().unwrap_or("<unknown>"));
|
||||
},
|
||||
InitSequence::Late,
|
||||
);
|
||||
device_tree::driver::init_irqs(
|
||||
|_| (),
|
||||
|node, error| {
|
||||
log::error!(
|
||||
"{}: irq init error: {error:?}",
|
||||
node.name().unwrap_or("<unknown>")
|
||||
);
|
||||
},
|
||||
InitSequence::Late,
|
||||
);
|
||||
}
|
||||
// Initialize PCI devices
|
||||
if let Err(error) = PciBusManager::setup_bus_devices(false) {
|
||||
log::error!("pci bus setup error: {error:?}");
|
||||
|
@ -66,6 +66,7 @@ extern crate ygg_driver_ahci;
|
||||
extern crate ygg_driver_net_rtl81xx;
|
||||
extern crate ygg_driver_nvme;
|
||||
extern crate ygg_driver_usb_xhci;
|
||||
extern crate ygg_driver_virtio_blk;
|
||||
extern crate ygg_driver_virtio_gpu;
|
||||
extern crate ygg_driver_virtio_net;
|
||||
|
||||
@ -78,6 +79,9 @@ cfg_if::cfg_if! {
|
||||
#[macro_use]
|
||||
pub mod arch;
|
||||
|
||||
#[cfg(any(target_arch = "riscv64", rust_analyzer))]
|
||||
extern crate ygg_driver_net_stmmac;
|
||||
|
||||
pub mod device;
|
||||
pub mod fs;
|
||||
pub mod init;
|
||||
|
@ -1,9 +1,17 @@
|
||||
//! System function call handlers
|
||||
|
||||
use abi::{error::Error, io::RawFd, process::SignalEntryData, SyscallFunction};
|
||||
use abi::{
|
||||
error::Error,
|
||||
io::RawFd,
|
||||
process::{ExitCode, Signal, SignalEntryData},
|
||||
SyscallFunction,
|
||||
};
|
||||
use kernel_arch::task::TaskFrame;
|
||||
use libk::{
|
||||
task::process::{Process, ProcessIo},
|
||||
task::{
|
||||
process::{Process, ProcessIo},
|
||||
thread::Thread,
|
||||
},
|
||||
vfs::NodeRef,
|
||||
};
|
||||
use libk_util::sync::IrqSafeSpinlockGuard;
|
||||
@ -53,7 +61,10 @@ pub unsafe fn handle_signal_exit<F: TaskFrame>(frame: &mut F) {
|
||||
let saved_data: &SignalEntryData = match arg::ref_const(frame.argument() as _) {
|
||||
Ok(r) => r,
|
||||
Err(err) => {
|
||||
todo!("Invalid SignalEntryData pointer: {:?}", err)
|
||||
let thread = Thread::current();
|
||||
let process = thread.process();
|
||||
log::warn!("#{}: invalid SignalEntryData struct: {err:?}", process.id);
|
||||
thread.exit_process(ExitCode::BySignal(Ok(Signal::MemoryAccessViolation)));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -56,6 +56,10 @@ struct Args {
|
||||
symbol_out: PathBuf,
|
||||
}
|
||||
|
||||
pub enum ImageHeader {
|
||||
Riscv(u64),
|
||||
}
|
||||
|
||||
pub struct GenData {
|
||||
pub kernel_start: u64,
|
||||
pub kernel_end: u64,
|
||||
@ -65,6 +69,13 @@ pub struct GenData {
|
||||
pub kernel_virt_offset: u64,
|
||||
}
|
||||
|
||||
pub struct BuiltTables {
|
||||
pub image_size: u64,
|
||||
pub image_header: Option<ImageHeader>,
|
||||
pub tables: Option<(AnyTables, u64)>,
|
||||
pub symbol_table: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
fn kernel_image_range<F: Read + Seek>(
|
||||
elf: &mut ElfStream<AnyEndian, F>,
|
||||
kernel_virt_offset: u64,
|
||||
@ -113,16 +124,41 @@ fn kernel_virt_offset<F: Read + Seek>(elf: &mut ElfStream<AnyEndian, F>) -> Resu
|
||||
Err(GenError::MissingSymbol("KERNEL_VIRT_OFFSET"))
|
||||
}
|
||||
|
||||
fn find_tables<F: Read + Seek>(elf: &mut ElfStream<AnyEndian, F>) -> Result<(u64, u64), GenError> {
|
||||
fn find_tables<F: Read + Seek>(
|
||||
elf: &mut ElfStream<AnyEndian, F>,
|
||||
) -> Result<(Option<ImageHeader>, u64, u64), GenError> {
|
||||
let section_size = match elf.ehdr.e_machine {
|
||||
EM_AARCH64 => size_of::<memtables::aarch64::FixedTables>(),
|
||||
EM_X86_64 => size_of::<memtables::x86_64::FixedTables>(),
|
||||
EM_RISCV => size_of::<memtables::riscv64::FixedTables>(),
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
|
||||
let image_header = if let Some(text_entry) = elf.section_header_by_name(".text.entry")? {
|
||||
let header = text_entry.clone();
|
||||
let section_offset = header.sh_offset;
|
||||
let (data, _) = elf.section_data(&header)?;
|
||||
if data.len() >= 64 {
|
||||
let version = u32::from_le_bytes(data[32..36].try_into().unwrap());
|
||||
let magic0 = &data[48..56];
|
||||
let magic1 = &data[56..60];
|
||||
|
||||
match (version, magic0, magic1) {
|
||||
(2, b"RISCV\x00\x00\x00", b"RSC\x05") => Some(ImageHeader::Riscv(section_offset)),
|
||||
(_, _, _) => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let (shdrs, strtab) = elf.section_headers_with_strtab()?;
|
||||
let strtab = strtab.ok_or_else(|| GenError::MissingSection(".strtab"))?;
|
||||
|
||||
let mut tables = None;
|
||||
|
||||
for shdr in shdrs {
|
||||
let name = strtab.get(shdr.sh_name as _)?;
|
||||
|
||||
@ -135,11 +171,13 @@ fn find_tables<F: Read + Seek>(elf: &mut ElfStream<AnyEndian, F>) -> Result<(u64
|
||||
}
|
||||
|
||||
// TODO section checks
|
||||
return Ok((shdr.sh_offset, shdr.sh_addr));
|
||||
tables = Some((shdr.sh_offset, shdr.sh_addr));
|
||||
}
|
||||
}
|
||||
|
||||
Err(GenError::MissingSection(".data.tables"))
|
||||
let (tables_offset, tables_addr) = tables.ok_or(GenError::MissingSection(".data.tables"))?;
|
||||
|
||||
Ok((image_header, tables_offset, tables_addr))
|
||||
}
|
||||
|
||||
fn extract_symbols<F: Read + Seek>(
|
||||
@ -194,20 +232,23 @@ fn into_any<T: Into<AnyTables>, U, V>((x, y, z): (T, U, V)) -> (AnyTables, U, V)
|
||||
(x.into(), y, z)
|
||||
}
|
||||
|
||||
fn build_tables<F: Read + Seek>(
|
||||
file: F,
|
||||
) -> Result<(Option<(AnyTables, u64)>, HashMap<String, usize>), GenError> {
|
||||
fn build_tables<F: Read + Seek>(file: F) -> Result<BuiltTables, GenError> {
|
||||
let mut elf = ElfStream::<AnyEndian, F>::open_stream(file)?;
|
||||
|
||||
if elf.ehdr.e_machine == EM_386 {
|
||||
// Locate symbol table
|
||||
let symbol_table = extract_symbols(&mut elf)?;
|
||||
return Ok((None, symbol_table));
|
||||
return Ok(BuiltTables {
|
||||
image_size: 0,
|
||||
image_header: None,
|
||||
tables: None,
|
||||
symbol_table,
|
||||
});
|
||||
}
|
||||
|
||||
let kernel_virt_offset = kernel_virt_offset(&mut elf)?;
|
||||
let (kernel_start, kernel_end) = kernel_image_range(&mut elf, kernel_virt_offset)?;
|
||||
let (table_offset, table_virt_addr) = find_tables(&mut elf)?;
|
||||
let (image_header, table_offset, table_virt_addr) = find_tables(&mut elf)?;
|
||||
let table_physical_address = table_virt_addr
|
||||
.checked_sub(kernel_virt_offset)
|
||||
.ok_or_else(|| GenError::IncorrectTablesPlacement(table_virt_addr))?;
|
||||
@ -230,7 +271,12 @@ fn build_tables<F: Read + Seek>(
|
||||
_ => todo!(),
|
||||
}?;
|
||||
|
||||
Ok((Some((tables, table_offset)), symbol_table))
|
||||
Ok(BuiltTables {
|
||||
image_size: kernel_end - kernel_start,
|
||||
image_header,
|
||||
tables: Some((tables, table_offset)),
|
||||
symbol_table,
|
||||
})
|
||||
}
|
||||
|
||||
fn write_tables<F: Write + Seek>(
|
||||
@ -243,6 +289,22 @@ fn write_tables<F: Write + Seek>(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_image_header<F: Write + Seek>(
|
||||
file: &mut F,
|
||||
header: ImageHeader,
|
||||
image_size: u64,
|
||||
) -> Result<(), GenError> {
|
||||
match header {
|
||||
ImageHeader::Riscv(offset) => {
|
||||
let size_bytes = image_size.to_le_bytes();
|
||||
println!("Writing RISC-V image header: image_size={image_size}");
|
||||
file.seek(SeekFrom::Start(offset + 16))?;
|
||||
file.write_all(&size_bytes)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn write_symbol_table(
|
||||
out: impl AsRef<Path>,
|
||||
table: HashMap<String, usize>,
|
||||
@ -268,9 +330,12 @@ fn gentables(image: impl AsRef<Path>, symbol_out: impl AsRef<Path>) -> Result<()
|
||||
.truncate(false)
|
||||
.open(image)?;
|
||||
|
||||
let (tables, symbol_table) = build_tables(&mut file)?;
|
||||
write_symbol_table(symbol_out, symbol_table)?;
|
||||
if let Some((tables, file_offset)) = tables {
|
||||
let built = build_tables(&mut file)?;
|
||||
write_symbol_table(symbol_out, built.symbol_table)?;
|
||||
if let Some(header) = built.image_header {
|
||||
write_image_header(&mut file, header, built.image_size)?;
|
||||
}
|
||||
if let Some((tables, file_offset)) = built.tables {
|
||||
write_tables(file, file_offset, tables)?;
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ pub struct EthernetFrame {
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[cfg_attr(feature = "bytemuck", derive(bytemuck::Pod, bytemuck::Zeroable))]
|
||||
#[repr(transparent)]
|
||||
pub struct EtherType(u16);
|
||||
pub struct EtherType(pub u16);
|
||||
|
||||
impl EtherType {
|
||||
pub const ARP: Self = Self(0x0806);
|
||||
|
@ -13,6 +13,7 @@ pub enum QemuNic {
|
||||
pub enum QemuDrive {
|
||||
Nvme,
|
||||
Sata,
|
||||
VirtioBlk,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
@ -115,6 +116,18 @@ impl IntoArgs for QemuDevice {
|
||||
file.display()
|
||||
));
|
||||
}
|
||||
QemuDrive::VirtioBlk => {
|
||||
command.arg("-drive");
|
||||
command.arg(format!("file={},if=none,id=drive0", file.display()));
|
||||
command.arg("-device");
|
||||
let mut device = "virtio-blk-pci".to_owned();
|
||||
if let Some(serial) = serial {
|
||||
device.push_str(",serial=");
|
||||
device.push_str(serial);
|
||||
}
|
||||
device.push_str(",drive=drive0");
|
||||
command.arg(device);
|
||||
}
|
||||
}
|
||||
// command.arg("-drive");
|
||||
// command.arg(format!("file={},if=none,id=drive0", file.display()));
|
||||
|
@ -1,3 +1,3 @@
|
||||
#!/bin/sh
|
||||
|
||||
/sbin/service start -- /bin/dnsq -n 11.0.0.1 -s 127.0.0.1:53
|
||||
/sbin/service start -- /bin/dnsq -n 1.1.1.1 -s 127.0.0.1:53
|
||||
|
@ -73,10 +73,10 @@ struct DhcpMessage {
|
||||
|
||||
#[derive(Debug)]
|
||||
struct DhcpOffer {
|
||||
router_address: Ipv4Addr,
|
||||
router_address: Option<Ipv4Addr>,
|
||||
server_address: Ipv4Addr,
|
||||
your_address: Ipv4Addr,
|
||||
subnet_mask: u32,
|
||||
subnet_mask: Option<u32>,
|
||||
}
|
||||
|
||||
fn pad_to_align(buffer: &mut Vec<u8>) {
|
||||
@ -242,7 +242,9 @@ impl DhcpMessage {
|
||||
}
|
||||
|
||||
fn as_dhcp_acknowledge(&self, requested_address: Ipv4Addr) -> Option<()> {
|
||||
let ty = self.message_type()?;
|
||||
let Some(ty) = self.message_type() else {
|
||||
return None;
|
||||
};
|
||||
|
||||
if ty != DhcpMessageType::Acknowledge {
|
||||
return None;
|
||||
@ -286,13 +288,11 @@ impl DhcpMessage {
|
||||
}
|
||||
}
|
||||
|
||||
let router_address = router?;
|
||||
let server_address = server?;
|
||||
let subnet_mask = subnet_mask?;
|
||||
|
||||
Some(DhcpOffer {
|
||||
server_address,
|
||||
router_address,
|
||||
router_address: router,
|
||||
your_address: Ipv4Addr::from(yiaddr),
|
||||
subnet_mask,
|
||||
})
|
||||
@ -467,6 +467,7 @@ fn attempt_request(
|
||||
op: 1,
|
||||
htype: 1,
|
||||
hlen: 6,
|
||||
flags: (1u16 << 15).to_be(),
|
||||
xid: transaction_id.to_be(),
|
||||
chaddr: [0; 16],
|
||||
cookie: 0x63825363u32.to_be(),
|
||||
@ -486,11 +487,11 @@ fn attempt_request(
|
||||
let offer = wait_for_dhcp_offer(poll, timer, socket)?;
|
||||
|
||||
println!(
|
||||
"Got DHCP offer: address={} router={} server={} mask={}",
|
||||
"Got DHCP offer: address={} router={:?} server={} mask={:?}",
|
||||
offer.your_address,
|
||||
offer.router_address,
|
||||
offer.server_address,
|
||||
Ipv4Addr::from(offer.subnet_mask)
|
||||
offer.subnet_mask.map(Ipv4Addr::from)
|
||||
);
|
||||
|
||||
// Send DHCP request
|
||||
@ -503,6 +504,7 @@ fn attempt_request(
|
||||
chaddr: [0; 16],
|
||||
siaddr: u32::from(offer.server_address).to_be(),
|
||||
cookie: 0x63825363u32.to_be(),
|
||||
flags: (1u16 << 15).to_be(),
|
||||
..DhcpMessageHeader::zeroed()
|
||||
},
|
||||
options: vec![
|
||||
@ -548,11 +550,12 @@ fn request_address(interface: &str) -> Result<DhcpOffer, Error> {
|
||||
|
||||
fn configure_address(interface: &str, offer: DhcpOffer) -> Result<(), Error> {
|
||||
let mut netconf = NetConfig::open()?;
|
||||
let router_address = offer.router_address.unwrap_or(offer.server_address);
|
||||
netconf.set_interface_address(interface, IpAddr::V4(offer.your_address))?;
|
||||
netconf.add_route(
|
||||
interface,
|
||||
SubnetAddr::V4(SubnetV4Addr::UNSPECIFIED),
|
||||
Some(IpAddr::V4(offer.router_address)),
|
||||
Some(router_address.into())
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ enum QemuDiskInterface {
|
||||
#[default]
|
||||
Nvme,
|
||||
Ahci,
|
||||
VirtioBlk,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
||||
@ -139,6 +140,7 @@ impl From<QemuDiskInterface> for QemuDrive {
|
||||
match value {
|
||||
QemuDiskInterface::Nvme => Self::Nvme,
|
||||
QemuDiskInterface::Ahci => Self::Sata,
|
||||
QemuDiskInterface::VirtioBlk => Self::VirtioBlk,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user