diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1259284da2..f95064be82 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -142,7 +142,6 @@ jobs:
             flags: --features hermit/pci-ids
           - arch: riscv64
             packages: qemu-system-misc
-            flags: --no-default-features
 
     steps:
       - name: Checkout hermit-rs
diff --git a/.vscode/settings.json b/.vscode/settings.json
index 33f432a79b..100b79bb14 100644
--- a/.vscode/settings.json
+++ b/.vscode/settings.json
@@ -17,7 +17,7 @@
         "--quiet",
         "--message-format=json",
         "--target=aarch64-unknown-none-softfloat",
-        // "--target=riscv64gc-unknown-none-elf",
+        "--target=riscv64gc-unknown-none-elf",
         "--target=x86_64-unknown-none",
     ],
     "rust-analyzer.check.targets": [
diff --git a/Cargo.lock b/Cargo.lock
index c172335335..237a89213f 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -9,7 +9,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "bc85366f9bad15a101c93ca48d7907eba7610abe9fb9d7ab2fb55f74c8073baf"
 dependencies = [
  "aarch64-cpu",
- "tock-registers",
+ "tock-registers 0.8.1",
 ]
 
 [[package]]
@@ -18,7 +18,7 @@ version = "9.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "ac42a04a61c19fc8196dd728022a784baecc5d63d7e256c01ad1b3fbfab26287"
 dependencies = [
- "tock-registers",
+ "tock-registers 0.8.1",
 ]
 
 [[package]]
@@ -578,6 +578,7 @@ dependencies = [
  "take-static",
  "talc",
  "time",
+ "tock-registers 0.9.0",
  "trapframe",
  "uart_16550",
  "virtio-def",
@@ -1348,6 +1349,12 @@ version = "0.8.1"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "696941a0aee7e276a165a978b37918fd5d22c55c3d6bda197813070ca9c0f21c"
 
+[[package]]
+name = "tock-registers"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b9e2fdb3a1e862c0661768b7ed25390811df1947a8acbfbefe09b47078d93c4"
+
 [[package]]
 name = "trapframe"
 version = "0.9.0"
diff --git a/Cargo.toml b/Cargo.toml
index e86764940e..baa53654ef 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -55,7 +55,7 @@ dhcpv4 = [
 fs = ["fuse"]
 fuse = ["pci"]
 fsgsbase = []
-gem-net = ["tcp"]
+gem-net = ["tcp", "dep:tock-registers"]
 newlib = []
 pci = []
 rtl8139 = ["tcp", "pci"]
@@ -140,6 +140,7 @@ semihosting = { version = "0.1", optional = true }
 [target.'cfg(target_arch = "riscv64")'.dependencies]
 riscv = "0.11"
 sbi-rt = "0.0.3"
+tock-registers = { version = "0.9", optional = true }
 trapframe = "0.9"
 semihosting = { version = "0.1", optional = true }
 
diff --git a/src/arch/mod.rs b/src/arch/mod.rs
index 81b75b3c33..aa3e3c9460 100644
--- a/src/arch/mod.rs
+++ b/src/arch/mod.rs
@@ -64,6 +64,8 @@ cfg_if::cfg_if! {
 
 		#[cfg(feature = "smp")]
 		pub(crate) use self::riscv64::kernel::application_processor_init;
+		#[cfg(feature = "pci")]
+		pub(crate) use self::riscv64::kernel::pci;
 		pub(crate) use self::riscv64::kernel::processor::{self, set_oneshot_timer, wakeup_core};
 		pub(crate) use self::riscv64::kernel::{
 			boot_application_processors,
diff --git a/src/arch/riscv64/kernel/devicetree.rs b/src/arch/riscv64/kernel/devicetree.rs
index c5e50e9389..e83a5ed0c4 100644
--- a/src/arch/riscv64/kernel/devicetree.rs
+++ b/src/arch/riscv64/kernel/devicetree.rs
@@ -1,5 +1,7 @@
 use fdt::Fdt;
 
+#[cfg(feature = "gem-net")]
+use crate::arch::mm::VirtAddr;
 use crate::arch::riscv64::kernel::get_dtb_ptr;
 use crate::arch::riscv64::kernel::interrupts::init_plic;
 #[cfg(all(feature = "tcp", not(feature = "pci")))]
@@ -7,10 +9,12 @@ use crate::arch::riscv64::kernel::mmio::{self, MmioDriver};
 use crate::arch::riscv64::mm::{paging, PhysAddr};
 #[cfg(feature = "gem-net")]
 use crate::drivers::net::gem;
-#[cfg(all(feature = "tcp", not(feature = "gem-net")))]
-use crate::drivers::virtio::transport::mmio as mmio_virtio;
-#[cfg(all(feature = "tcp", not(feature = "gem-net")))]
-use crate::drivers::virtio::transport::mmio::{DevId, MmioRegisterLayout, VirtioDriver};
+#[cfg(all(feature = "tcp", not(feature = "pci")))]
+use crate::drivers::virtio::transport::mmio::{
+	self as mmio_virtio, DevId, MmioRegisterLayout, VirtioDriver,
+};
+#[cfg(all(feature = "tcp", not(feature = "pci")))]
+use crate::kernel::mmio::register_driver;
 
 static mut PLATFORM_MODEL: Model = Model::Unknown;
 
@@ -154,7 +158,7 @@ pub fn init_drivers() {
 			}
 
 			// Init virtio-mmio
-			#[cfg(all(feature = "tcp", not(feature = "gem-net")))]
+			#[cfg(all(feature = "tcp", not(feature = "pci")))]
 			if let Some(virtio_node) = fdt.find_compatible(&["virtio,mmio"]) {
 				debug!("Found virtio mmio device");
 				let virtio_region = virtio_node
diff --git a/src/arch/riscv64/kernel/interrupts.rs b/src/arch/riscv64/kernel/interrupts.rs
index 36da402f25..9f774b3db0 100644
--- a/src/arch/riscv64/kernel/interrupts.rs
+++ b/src/arch/riscv64/kernel/interrupts.rs
@@ -44,6 +44,11 @@ pub fn enable() {
 	}
 }
 
+#[cfg(all(feature = "pci", feature = "tcp"))]
+pub fn add_irq_name(irq_number: u8, name: &'static str) {
+	warn!("add_irq_name({irq_number}, {name}) called but not implemented");
+}
+
 /// Waits for the next interrupt (Only Supervisor-level software/timer interrupt for now)
 /// and calls the specific handler
 #[inline]
diff --git a/src/arch/riscv64/kernel/mod.rs b/src/arch/riscv64/kernel/mod.rs
index 54a72983bd..68bf45f02a 100644
--- a/src/arch/riscv64/kernel/mod.rs
+++ b/src/arch/riscv64/kernel/mod.rs
@@ -3,6 +3,8 @@ mod devicetree;
 pub mod interrupts;
 #[cfg(all(feature = "tcp", not(feature = "pci")))]
 pub mod mmio;
+#[cfg(feature = "pci")]
+pub mod pci;
 pub mod processor;
 pub mod scheduler;
 mod start;
diff --git a/src/arch/riscv64/kernel/pci.rs b/src/arch/riscv64/kernel/pci.rs
new file mode 100644
index 0000000000..cbac337ee9
--- /dev/null
+++ b/src/arch/riscv64/kernel/pci.rs
@@ -0,0 +1,20 @@
+use pci_types::{ConfigRegionAccess, PciAddress};
+
+#[derive(Debug, Copy, Clone)]
+pub struct PciConfigRegion;
+
+impl ConfigRegionAccess for PciConfigRegion {
+	fn function_exists(&self, addr: PciAddress) -> bool {
+		warn!("pci_config_region.function_exits({addr}) called but not implemented");
+		false
+	}
+
+	unsafe fn read(&self, addr: PciAddress, offset: u16) -> u32 {
+		warn!("pci_config_region.read({addr}, {offset}) called but not implemented");
+		todo!()
+	}
+
+	unsafe fn write(&self, addr: PciAddress, offset: u16, value: u32) {
+		warn!("pci_config_region.write({addr}, {offset}, {value}) called but not implemented");
+	}
+}
diff --git a/src/drivers/net/gem.rs b/src/drivers/net/gem.rs
index 8165d354ed..b9ed56dde7 100644
--- a/src/drivers/net/gem.rs
+++ b/src/drivers/net/gem.rs
@@ -16,7 +16,6 @@ use tock_registers::{register_bitfields, register_structs};
 
 use crate::arch::kernel::core_local::core_scheduler;
 use crate::arch::kernel::interrupts::*;
-use crate::arch::kernel::pci;
 use crate::arch::mm::paging::virt_to_phys;
 use crate::arch::mm::VirtAddr;
 use crate::drivers::error::DriverError;
@@ -226,6 +225,7 @@ impl NetworkDriver for GEMDriver {
 		self.mtu
 	}
 
+	#[allow(clippy::modulo_one)]
 	fn send_packet<R, F>(&mut self, len: usize, f: F) -> R
 	where
 		F: FnOnce(&mut [u8]) -> R,
diff --git a/xtask/src/clippy.rs b/xtask/src/clippy.rs
index c8f0d13e6a..003e45491d 100644
--- a/xtask/src/clippy.rs
+++ b/xtask/src/clippy.rs
@@ -12,7 +12,7 @@ impl Clippy {
 	pub fn run(self) -> Result<()> {
 		let sh = crate::sh()?;
 
-		for target in [Arch::X86_64, Arch::Aarch64] {
+		for target in [Arch::X86_64, Arch::Aarch64, Arch::Riscv64] {
 			target.install()?;
 
 			let triple = target.triple();
@@ -24,6 +24,16 @@ impl Clippy {
 				.arg("--no-default-features")
 				.arg("--features=acpi,fsgsbase,pci,smp,vga")
 				.run()?;
+
+			if target == Arch::Riscv64 {
+				cmd!(sh, "cargo clippy --target={triple}")
+					.arg("--no-default-features")
+					.arg("--features=gem-net,tcp")
+					.arg("--")
+					.arg("-Wwarnings")
+					.run()?;
+			}
+
 			// TODO: Enable clippy for newlib
 			// https://github.com/hermit-os/kernel/issues/470
 			// cmd!(sh, "cargo clippy --target={triple}")
@@ -32,20 +42,6 @@ impl Clippy {
 			// 	.run()?;
 		}
 
-		{
-			let target = Arch::Riscv64;
-			target.install()?;
-
-			let triple = target.triple();
-			cmd!(sh, "cargo clippy --target={triple}")
-				.arg("--no-default-features")
-				.run()?;
-			cmd!(sh, "cargo clippy --target={triple}")
-				.arg("--no-default-features")
-				.arg("--features=smp,tcp")
-				.run()?;
-		}
-
 		cmd!(sh, "cargo clippy")
 			.arg("--manifest-path=hermit-builtins/Cargo.toml")
 			.arg("--target=x86_64-unknown-none")