vmbase: uart: Create abstraction layer over register write

This change creates an abstraction layer over register write accesses
(there are currently no read accesses) for 8250 UART driver. The direct
write volatile access have been replaced with a intermediate trait that
is accepted via newly introduced generic to `Uart`.

Bug: 362733888
Test: m libvmbase pvmfw_bin vmbase_example_bios_bin vmbase_example_kernel_bin
Test: atest vmbase_example.integration_test
Change-Id: I4ff012ac8ab433e82199c9c52f85ab53af205792
diff --git a/libs/libvmbase/src/arch.rs b/libs/libvmbase/src/arch.rs
index c60c651..9e4523e 100644
--- a/libs/libvmbase/src/arch.rs
+++ b/libs/libvmbase/src/arch.rs
@@ -29,6 +29,9 @@
 #[cfg(target_arch = "aarch64")]
 pub use aarch64::dbm;
 
+#[cfg(target_arch = "aarch64")]
+pub use aarch64::uart;
+
 /// Write with well-defined compiled behavior.
 ///
 /// See https://github.com/rust-lang/rust/issues/131894
diff --git a/libs/libvmbase/src/arch/aarch64.rs b/libs/libvmbase/src/arch/aarch64.rs
index 199423b..05b5566 100644
--- a/libs/libvmbase/src/arch/aarch64.rs
+++ b/libs/libvmbase/src/arch/aarch64.rs
@@ -21,6 +21,7 @@
 pub mod linker;
 pub mod page_table;
 pub mod platform;
+pub mod uart;
 
 /// Reads a value from a system register.
 #[macro_export]
diff --git a/libs/libvmbase/src/arch/aarch64/uart.rs b/libs/libvmbase/src/arch/aarch64/uart.rs
new file mode 100644
index 0000000..2ef7d7e
--- /dev/null
+++ b/libs/libvmbase/src/arch/aarch64/uart.rs
@@ -0,0 +1,66 @@
+// Copyright 2025, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Uart driver with backend for aarch64 using MMIO
+
+use crate::arch::write_volatile_u8;
+use crate::uart::UartBackend;
+
+/// Alias for default Uart for aarch64 backend with [`MmioBackend`]
+pub type Uart = crate::uart::Uart<MmioBackend>;
+
+/// Backend for [`crate::uart::Uart`] that uses [`crate::arch::write_volatile_u8`] for writing to
+/// hardware registers.
+pub struct MmioBackend {
+    base_address: *mut u8,
+}
+
+impl MmioBackend {
+    /// Constructs a new instance of the UART driver backend for a device at the given base address.
+    ///
+    /// # Safety
+    ///
+    /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+    /// device, which must be mapped into the address space of the process as device memory and not
+    /// have any other aliases.
+    pub unsafe fn new(base_address: usize) -> Self {
+        Self { base_address: base_address as *mut u8 }
+    }
+}
+
+impl UartBackend for MmioBackend {
+    fn write_register_u8(&self, offset: usize, byte: u8) {
+        // SAFETY: We know that the base address points to the control registers of a UART device
+        // which is appropriately mapped.
+        unsafe { write_volatile_u8(self.base_address.add(offset), byte) }
+    }
+}
+
+impl Uart {
+    /// Constructs a new instance of the UART driver for a device at the given base address.
+    ///
+    /// # Safety
+    ///
+    /// The given base address must point to the 8 MMIO control registers of an appropriate UART
+    /// device, which must be mapped into the address space of the process as device memory and not
+    /// have any other aliases.
+    pub unsafe fn new(base_address: usize) -> Self {
+        // SAFETY: Delegated to caller
+        unsafe { Self::create(MmioBackend::new(base_address)) }
+    }
+}
+
+// SAFETY: `MmioBackend` just contains a pointer to device memory, which can be accessed from any
+// context.
+unsafe impl Send for MmioBackend {}
diff --git a/libs/libvmbase/src/console.rs b/libs/libvmbase/src/console.rs
index 7b01bb6..533f2b6 100644
--- a/libs/libvmbase/src/console.rs
+++ b/libs/libvmbase/src/console.rs
@@ -14,7 +14,7 @@
 
 //! Console driver for 8250 UART.
 
-use crate::uart::Uart;
+use crate::arch::uart::Uart;
 use core::fmt::{write, Arguments, Write};
 use spin::{mutex::SpinMutex, Once};
 
diff --git a/libs/libvmbase/src/uart.rs b/libs/libvmbase/src/uart.rs
index 427499b..857a22e 100644
--- a/libs/libvmbase/src/uart.rs
+++ b/libs/libvmbase/src/uart.rs
@@ -15,36 +15,33 @@
 //! Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
 //! provided by crosvm, and won't work with real hardware.
 
-use crate::arch::write_volatile_u8;
 use core::fmt::{self, Write};
 
+/// The backend for [`Uart`] that abstracts the access to 8250 register map
+pub trait UartBackend {
+    /// Writes a byte value on the offset to the hardware registers.
+    fn write_register_u8(&self, offset: usize, byte: u8);
+}
+
 /// Minimal driver for an 8250 UART. This only implements enough to work with the emulated 8250
 /// provided by crosvm, and won't work with real hardware.
-pub struct Uart {
-    base_address: *mut u8,
+pub struct Uart<Backend: UartBackend> {
+    backend: Backend,
 }
 
-impl Uart {
-    /// Constructs a new instance of the UART driver for a device at the given base address.
-    ///
-    /// # Safety
-    ///
-    /// The given base address must point to the 8 MMIO control registers of an appropriate UART
-    /// device, which must be mapped into the address space of the process as device memory and not
-    /// have any other aliases.
-    pub unsafe fn new(base_address: usize) -> Self {
-        Self { base_address: base_address as *mut u8 }
+impl<Backend: UartBackend> Uart<Backend> {
+    /// Constructs a new instance of the UART driver with given backend.
+    pub(crate) fn create(backend: Backend) -> Self {
+        Self { backend }
     }
 
     /// Writes a single byte to the UART.
     pub fn write_byte(&self, byte: u8) {
-        // SAFETY: We know that the base address points to the control registers of a UART device
-        // which is appropriately mapped.
-        unsafe { write_volatile_u8(self.base_address, byte) }
+        self.backend.write_register_u8(0, byte)
     }
 }
 
-impl Write for Uart {
+impl<Backend: UartBackend> Write for Uart<Backend> {
     fn write_str(&mut self, s: &str) -> fmt::Result {
         for c in s.as_bytes() {
             self.write_byte(*c);
@@ -52,6 +49,3 @@
         Ok(())
     }
 }
-
-// SAFETY: `Uart` just contains a pointer to device memory, which can be accessed from any context.
-unsafe impl Send for Uart {}