diff --git a/rust/wrapper-system.h b/rust/wrapper-system.h
new file mode 100644
index 0000000000..06f4c5279a
--- /dev/null
+++ b/rust/wrapper-system.h
@@ -0,0 +1,46 @@
+/*
+ * QEMU System Emulator
+ *
+ * Copyright (c) 2024 Linaro Ltd.
+ *
+ * Authors: Manos Pitsidianakis <manos.pitsidianakis@linaro.org>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*
+ * This header file is meant to be used as input to the `bindgen` application
+ * in order to generate C FFI compatible Rust bindings.
+ */
+
+/* The system emulator has all of the bindings tools have */
+#include "wrapper.h"
+
+#include "exec/address-spaces.h"
+#include "exec/memattrs.h"
+#include "exec/memory.h"
+#include "hw/clock.h"
+#include "hw/irq.h"
+#include "hw/qdev-clock.h"
+#include "hw/qdev-properties.h"
+#include "hw/qdev-properties-system.h"
+#include "hw/sysbus.h"
+#include "migration/vmstate.h"
+#include "system/system.h"
diff --git a/rust/wrapper.h b/rust/wrapper.h
index d927ad6799..f203fd13ac 100644
--- a/rust/wrapper.h
+++ b/rust/wrapper.h
@@ -50,18 +50,7 @@ typedef enum memory_order {
 #include "qemu/osdep.h"
 #include "qemu/module.h"
 #include "qemu-io.h"
-#include "system/system.h"
-#include "hw/sysbus.h"
-#include "exec/memory.h"
 #include "chardev/char-fe.h"
-#include "hw/clock.h"
-#include "hw/qdev-clock.h"
-#include "hw/qdev-properties.h"
-#include "hw/qdev-properties-system.h"
-#include "hw/irq.h"
 #include "qapi/error.h"
-#include "migration/vmstate.h"
 #include "chardev/char-serial.h"
-#include "exec/memattrs.h"
 #include "qemu/timer.h"
-#include "exec/address-spaces.h"
diff --git a/meson.build b/meson.build
index 8ed10b6624..6ee1757828 100644
--- a/meson.build
+++ b/meson.build
@@ -4102,10 +4102,17 @@ if have_rust
   # this case you must pass the path to `clang` and `libclang` to your build
   # command invocation using the environment variables CLANG_PATH and
   # LIBCLANG_PATH
-  bindings_rs = rust.bindgen(
+  bindings_rs_tools = rust.bindgen(
     input: 'rust/wrapper.h',
+    output: 'bindings_tools.inc.rs',
+    include_directories: include_directories('.', 'include'),
+    bindgen_version: ['>=0.60.0'],
+    args: bindgen_args,
+    )
+  bindings_rs_system = rust.bindgen(
+    input: 'rust/wrapper-system.h',
     dependencies: common_ss.all_dependencies(),
-    output: 'bindings.inc.rs',
+    output: 'bindings_system.inc.rs',
     include_directories: include_directories('.', 'include'),
     bindgen_version: ['>=0.60.0'],
     args: bindgen_args,
diff --git a/rust/hw/char/pl011/meson.build b/rust/hw/char/pl011/meson.build
index 547cca5a96..d2cfede5dc 100644
--- a/rust/hw/char/pl011/meson.build
+++ b/rust/hw/char/pl011/meson.build
@@ -12,9 +12,10 @@ _libpl011_rs = static_library(
   dependencies: [
     bilge_dep,
     bilge_impl_dep,
-    qemu_api,
+    qemu_api_system,
     qemu_api_macros,
   ],
+  rust_dependency_map: {'qemu_api_system': 'qemu_api'},
 )
 
 rust_devices_ss.add(when: 'CONFIG_X_PL011_RUST', if_true: [declare_dependency(
diff --git a/rust/hw/timer/hpet/meson.build b/rust/hw/timer/hpet/meson.build
index c2d7c0532c..94b256ae2f 100644
--- a/rust/hw/timer/hpet/meson.build
+++ b/rust/hw/timer/hpet/meson.build
@@ -4,9 +4,10 @@ _libhpet_rs = static_library(
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   dependencies: [
-    qemu_api,
+    qemu_api_system,
     qemu_api_macros,
   ],
+  rust_dependency_map: {'qemu_api_system': 'qemu_api'},
 )
 
 rust_devices_ss.add(when: 'CONFIG_X_HPET_RUST', if_true: [declare_dependency(
diff --git a/rust/meson.build b/rust/meson.build
index 91e52b8fb8..50eb23b072 100644
--- a/rust/meson.build
+++ b/rust/meson.build
@@ -9,18 +9,19 @@ if cargo.found()
   run_target('clippy',
     command: [config_host['MESON'], 'devenv',
               '--workdir', '@CURRENT_SOURCE_DIR@',
-              cargo, 'clippy', '--tests'],
-    depends: bindings_rs)
+              cargo, 'clippy', '--tests', '--features', 'system'],
+    depends: bindings_rs_system)
 
   run_target('rustfmt',
     command: [config_host['MESON'], 'devenv',
               '--workdir', '@CURRENT_SOURCE_DIR@',
               cargo, 'fmt'],
-    depends: bindings_rs)
+    depends: bindings_rs_system)
 
   run_target('rustdoc',
     command: [config_host['MESON'], 'devenv',
               '--workdir', '@CURRENT_SOURCE_DIR@',
-              cargo, 'doc', '--no-deps', '--document-private-items'],
-    depends: bindings_rs)
+              cargo, 'doc', '--no-deps', '--document-private-items',
+              '--features', 'system'],
+    depends: bindings_rs_system)
 endif
diff --git a/rust/qemu-api/Cargo.toml b/rust/qemu-api/Cargo.toml
index 57747bc934..bc0393add4 100644
--- a/rust/qemu-api/Cargo.toml
+++ b/rust/qemu-api/Cargo.toml
@@ -25,6 +25,7 @@ version_check = "~0.9"
 default = ["debug_cell"]
 allocator = []
 debug_cell = []
+system= []
 
 [lints]
 workspace = true
diff --git a/rust/qemu-api/build.rs b/rust/qemu-api/build.rs
index 471e6c633d..b14f9d4e4a 100644
--- a/rust/qemu-api/build.rs
+++ b/rust/qemu-api/build.rs
@@ -16,7 +16,13 @@ fn main() -> Result<()> {
     let path = env::var("MESON_BUILD_ROOT")
         .unwrap_or_else(|_| format!("{}/src", env!("CARGO_MANIFEST_DIR")));
 
-    let file = format!("{}/bindings.inc.rs", path);
+    let basename = if cfg!(feature = "system") {
+        "bindings_system.inc.rs"
+    } else {
+        "bindings_tools.inc.rs"
+    };
+
+    let file = format!("{}/{}", path, basename);
     let file = Path::new(&file);
     if !Path::new(&file).exists() {
         panic!(concat!(
@@ -31,7 +37,7 @@ fn main() -> Result<()> {
     }
 
     let out_dir = env::var("OUT_DIR").unwrap();
-    let dest_path = format!("{}/bindings.inc.rs", out_dir);
+    let dest_path = format!("{}/{}", out_dir, basename);
     let dest_path = Path::new(&dest_path);
     if dest_path.symlink_metadata().is_ok() {
         remove_file(dest_path)?;
diff --git a/rust/qemu-api/meson.build b/rust/qemu-api/meson.build
index bcf1cf780f..e0a3052c79 100644
--- a/rust/qemu-api/meson.build
+++ b/rust/qemu-api/meson.build
@@ -12,44 +12,64 @@ if get_option('debug_mutex')
   _qemu_api_cfg += ['--cfg', 'feature="debug_cell"']
 endif
 
-_qemu_api_rs = static_library(
-  'qemu_api',
+sources_core = [
+  'src/lib.rs',
+  'src/assertions.rs',
+  'src/bindings.rs',
+  'src/bitops.rs',
+  'src/callbacks.rs',
+  'src/cell.rs',
+  'src/chardev.rs',
+  'src/c_str.rs',
+  'src/errno.rs',
+  'src/module.rs',
+  'src/offset_of.rs',
+  'src/prelude.rs',
+  'src/qom.rs',
+  'src/timer.rs',
+  'src/zeroable.rs',
+]
+sources_system = sources_core + [
+  'src/irq.rs',
+  'src/memory.rs',
+  'src/qdev.rs',
+  'src/sysbus.rs',
+  'src/vmstate.rs',
+]
+
+
+_qemu_api_tools_rs = static_library(
+  'qemu_api_tools',
   structured_sources(
-    [
-      'src/lib.rs',
-      'src/assertions.rs',
-      'src/bindings.rs',
-      'src/bitops.rs',
-      'src/callbacks.rs',
-      'src/cell.rs',
-      'src/chardev.rs',
-      'src/c_str.rs',
-      'src/errno.rs',
-      'src/irq.rs',
-      'src/memory.rs',
-      'src/module.rs',
-      'src/offset_of.rs',
-      'src/prelude.rs',
-      'src/qdev.rs',
-      'src/qom.rs',
-      'src/sysbus.rs',
-      'src/timer.rs',
-      'src/vmstate.rs',
-      'src/zeroable.rs',
-    ],
-    {'.' : bindings_rs},
+    sources_core,
+    {'.' : bindings_rs_tools},
   ),
   override_options: ['rust_std=2021', 'build.rust_std=2021'],
   rust_abi: 'rust',
   rust_args: _qemu_api_cfg,
   dependencies: libc_dep,
 )
+_qemu_api_system_rs = static_library(
+  'qemu_api_system',
+  structured_sources(
+    sources_system,
+    {'.' : bindings_rs_system},
+  ),
+  override_options: ['rust_std=2021', 'build.rust_std=2021'],
+  rust_abi: 'rust',
+  rust_args: _qemu_api_cfg + ['--cfg', 'feature="system"'],
+  dependencies: libc_dep,
+)
 
-rust.test('rust-qemu-api-tests', _qemu_api_rs,
+rust.test('rust-qemu-api-tests', _qemu_api_system_rs,
           suite: ['unit', 'rust'])
 
-qemu_api = declare_dependency(
-  link_with: _qemu_api_rs,
+qemu_api_tools = declare_dependency(
+  link_with: _qemu_api_tools_rs,
+  dependencies: qemu_api_macros,
+)
+qemu_api_system = declare_dependency(
+  link_with: _qemu_api_system_rs,
   dependencies: qemu_api_macros,
 )
 
@@ -66,7 +86,8 @@ test('rust-qemu-api-integration',
         override_options: ['rust_std=2021', 'build.rust_std=2021'],
         rust_args: ['--test'],
         install: false,
-        dependencies: [qemu_api, qemu_api_macros],
+        dependencies: [qemu_api_system, qemu_api_macros],
+        rust_dependency_map: {'qemu_api_system': 'qemu_api'},
         link_whole: [rust_qemu_api_objs, libqemuutil]),
     args: [
         '--test', '--test-threads', '1',
diff --git a/rust/qemu-api/src/bindings.rs b/rust/qemu-api/src/bindings.rs
index d2868639ff..d93848c80c 100644
--- a/rust/qemu-api/src/bindings.rs
+++ b/rust/qemu-api/src/bindings.rs
@@ -17,11 +17,15 @@
 
 //! `bindgen`-generated declarations.
 
-#[cfg(MESON)]
-include!("bindings.inc.rs");
+#[cfg(all(MESON, not(feature="system")))]
+include!("bindings_tools.inc.rs");
+#[cfg(all(MESON, feature="system"))]
+include!("bindings_system.inc.rs");
 
-#[cfg(not(MESON))]
-include!(concat!(env!("OUT_DIR"), "/bindings.inc.rs"));
+#[cfg(all(not(MESON), not(feature="system")))]
+include!(concat!(env!("OUT_DIR"), "/bindings_tools.inc.rs"));
+#[cfg(all(not(MESON), feature="system"))]
+include!(concat!(env!("OUT_DIR"), "/bindings_system.inc.rs"));
 
 // SAFETY: these are implemented in C; the bindings need to assert that the
 // BQL is taken, either directly or via `BqlCell` and `BqlRefCell`.
@@ -49,29 +53,33 @@ unsafe impl Sync for ObjectClass {}
 unsafe impl Send for Object {}
 unsafe impl Sync for Object {}
 
-unsafe impl Send for SysBusDevice {}
-unsafe impl Sync for SysBusDevice {}
-
-// SAFETY: this is a pure data struct
-unsafe impl Send for CoalescedMemoryRange {}
-unsafe impl Sync for CoalescedMemoryRange {}
-
-// SAFETY: these are constants and vtables; the Send and Sync requirements
-// are deferred to the unsafe callbacks that they contain
-unsafe impl Send for MemoryRegionOps {}
-unsafe impl Sync for MemoryRegionOps {}
-
 unsafe impl Send for Property {}
 unsafe impl Sync for Property {}
 
 unsafe impl Send for TypeInfo {}
 unsafe impl Sync for TypeInfo {}
 
-unsafe impl Send for VMStateDescription {}
-unsafe impl Sync for VMStateDescription {}
+#[cfg(feature="system")]
+mod system_impls {
+    use super::*;
+
+    unsafe impl Send for SysBusDevice {}
+    unsafe impl Sync for SysBusDevice {}
+
+    // SAFETY: this is a pure data struct
+    unsafe impl Send for CoalescedMemoryRange {}
+    unsafe impl Sync for CoalescedMemoryRange {}
+
+    // SAFETY: these are constants and vtables; the Send and Sync requirements
+    // are deferred to the unsafe callbacks that they contain
+    unsafe impl Send for MemoryRegionOps {}
+    unsafe impl Sync for MemoryRegionOps {}
+
+    unsafe impl Send for VMStateDescription {}
+    unsafe impl Sync for VMStateDescription {}
 
-unsafe impl Send for VMStateField {}
-unsafe impl Sync for VMStateField {}
+    unsafe impl Send for VMStateField {}
+    unsafe impl Sync for VMStateField {}
 
-unsafe impl Send for VMStateInfo {}
-unsafe impl Sync for VMStateInfo {}
+    unsafe impl Send for VMStateInfo {}
+}
diff --git a/rust/qemu-api/src/lib.rs b/rust/qemu-api/src/lib.rs
index 05f38b51d3..825443abde 100644
--- a/rust/qemu-api/src/lib.rs
+++ b/rust/qemu-api/src/lib.rs
@@ -20,14 +20,19 @@
 pub mod cell;
 pub mod chardev;
 pub mod errno;
+#[cfg(feature = "system")]
 pub mod irq;
+#[cfg(feature = "system")]
 pub mod memory;
 pub mod module;
 pub mod offset_of;
+#[cfg(feature = "system")]
 pub mod qdev;
 pub mod qom;
+#[cfg(feature = "system")]
 pub mod sysbus;
 pub mod timer;
+#[cfg(feature = "system")]
 pub mod vmstate;
 pub mod zeroable;
 
diff --git a/rust/qemu-api/src/prelude.rs b/rust/qemu-api/src/prelude.rs
index 634acf37a8..94738869d1 100644
--- a/rust/qemu-api/src/prelude.rs
+++ b/rust/qemu-api/src/prelude.rs
@@ -11,6 +11,7 @@
 
 pub use crate::errno;
 
+#[cfg(feature="system")]
 pub use crate::qdev::DeviceMethods;
 
 pub use crate::qom::InterfaceType;
@@ -25,6 +26,8 @@
 
 pub use crate::qom_isa;
 
+#[cfg(feature="system")]
 pub use crate::sysbus::SysBusDeviceMethods;
 
+#[cfg(feature="system")]
 pub use crate::vmstate::VMState;
diff --git a/rust/qemu-api/src/zeroable.rs b/rust/qemu-api/src/zeroable.rs
index 47b6977828..bec5093366 100644
--- a/rust/qemu-api/src/zeroable.rs
+++ b/rust/qemu-api/src/zeroable.rs
@@ -58,6 +58,7 @@ pub unsafe trait Zeroable: Default {
 /// ## Differences with `core::mem::zeroed`
 ///
 /// `const_zero` zeroes padding bits, while `core::mem::zeroed` doesn't
+#[allow(unused)]
 #[macro_export]
 macro_rules! const_zero {
     // This macro to produce a type-generic zero constant is taken from the
@@ -80,6 +81,7 @@ union TypeAsBytes {
 }
 
 /// A wrapper to implement the `Zeroable` trait through the `const_zero` macro.
+#[allow(unused)]
 #[macro_export]
 macro_rules! impl_zeroable {
     ($type:ty) => {
@@ -89,20 +91,23 @@ unsafe impl $crate::zeroable::Zeroable for $type {
     };
 }
 
-// bindgen does not derive Default here
-#[allow(clippy::derivable_impls)]
-impl Default for crate::bindings::VMStateFlags {
-    fn default() -> Self {
-        Self(0)
+#[cfg(feature = "system")]
+mod system_impls {
+    // bindgen does not derive Default here
+    #[allow(clippy::derivable_impls)]
+    impl Default for crate::bindings::VMStateFlags {
+        fn default() -> Self {
+            Self(0)
+        }
     }
-}
 
-impl_zeroable!(crate::bindings::Property__bindgen_ty_1);
-impl_zeroable!(crate::bindings::Property);
-impl_zeroable!(crate::bindings::VMStateFlags);
-impl_zeroable!(crate::bindings::VMStateField);
-impl_zeroable!(crate::bindings::VMStateDescription);
-impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1);
-impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2);
-impl_zeroable!(crate::bindings::MemoryRegionOps);
-impl_zeroable!(crate::bindings::MemTxAttrs);
+    impl_zeroable!(crate::bindings::Property__bindgen_ty_1);
+    impl_zeroable!(crate::bindings::Property);
+    impl_zeroable!(crate::bindings::VMStateFlags);
+    impl_zeroable!(crate::bindings::VMStateField);
+    impl_zeroable!(crate::bindings::VMStateDescription);
+    impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_1);
+    impl_zeroable!(crate::bindings::MemoryRegionOps__bindgen_ty_2);
+    impl_zeroable!(crate::bindings::MemoryRegionOps);
+    impl_zeroable!(crate::bindings::MemTxAttrs);
+}
