1 // Copyright 2024 Red Hat, Inc. 2 // Author(s): Paolo Bonzini <pbonzini@redhat.com> 3 // SPDX-License-Identifier: GPL-2.0-or-later 4 5 //! Bindings to access `sysbus` functionality from Rust. 6 7 use std::{ffi::CStr, ptr::addr_of_mut}; 8 9 pub use bindings::SysBusDeviceClass; 10 11 use crate::{ 12 bindings, 13 cell::{bql_locked, Opaque}, 14 irq::{IRQState, InterruptSource}, 15 memory::MemoryRegion, 16 prelude::*, 17 qdev::{DeviceImpl, DeviceState}, 18 qom::Owned, 19 }; 20 21 /// A safe wrapper around [`bindings::SysBusDevice`]. 22 #[repr(transparent)] 23 #[derive(Debug, qemu_api_macros::Wrapper)] 24 pub struct SysBusDevice(Opaque<bindings::SysBusDevice>); 25 26 unsafe impl Send for SysBusDevice {} 27 unsafe impl Sync for SysBusDevice {} 28 29 unsafe impl ObjectType for SysBusDevice { 30 type Class = SysBusDeviceClass; 31 const TYPE_NAME: &'static CStr = 32 unsafe { CStr::from_bytes_with_nul_unchecked(bindings::TYPE_SYS_BUS_DEVICE) }; 33 } 34 qom_isa!(SysBusDevice: DeviceState, Object); 35 36 // TODO: add virtual methods 37 pub trait SysBusDeviceImpl: DeviceImpl + IsA<SysBusDevice> {} 38 39 impl SysBusDeviceClass { 40 /// Fill in the virtual methods of `SysBusDeviceClass` based on the 41 /// definitions in the `SysBusDeviceImpl` trait. class_init<T: SysBusDeviceImpl>(self: &mut SysBusDeviceClass)42 pub fn class_init<T: SysBusDeviceImpl>(self: &mut SysBusDeviceClass) { 43 self.parent_class.class_init::<T>(); 44 } 45 } 46 47 /// Trait for methods of [`SysBusDevice`] and its subclasses. 48 pub trait SysBusDeviceMethods: ObjectDeref 49 where 50 Self::Target: IsA<SysBusDevice>, 51 { 52 /// Expose a memory region to the board so that it can give it an address 53 /// in guest memory. Note that the ordering of calls to `init_mmio` is 54 /// important, since whoever creates the sysbus device will refer to the 55 /// region with a number that corresponds to the order of calls to 56 /// `init_mmio`. init_mmio(&self, iomem: &MemoryRegion)57 fn init_mmio(&self, iomem: &MemoryRegion) { 58 assert!(bql_locked()); 59 unsafe { 60 bindings::sysbus_init_mmio(self.upcast().as_mut_ptr(), iomem.as_mut_ptr()); 61 } 62 } 63 64 /// Expose an interrupt source outside the device as a qdev GPIO output. 65 /// Note that the ordering of calls to `init_irq` is important, since 66 /// whoever creates the sysbus device will refer to the interrupts with 67 /// a number that corresponds to the order of calls to `init_irq`. init_irq(&self, irq: &InterruptSource)68 fn init_irq(&self, irq: &InterruptSource) { 69 assert!(bql_locked()); 70 unsafe { 71 bindings::sysbus_init_irq(self.upcast().as_mut_ptr(), irq.as_ptr()); 72 } 73 } 74 75 // TODO: do we want a type like GuestAddress here? mmio_addr(&self, id: u32) -> Option<u64>76 fn mmio_addr(&self, id: u32) -> Option<u64> { 77 assert!(bql_locked()); 78 // SAFETY: the BQL ensures that no one else writes to sbd.mmio[], and 79 // the SysBusDevice must be initialized to get an IsA<SysBusDevice>. 80 let sbd = unsafe { *self.upcast().as_ptr() }; 81 let id: usize = id.try_into().unwrap(); 82 if sbd.mmio[id].memory.is_null() { 83 None 84 } else { 85 Some(sbd.mmio[id].addr) 86 } 87 } 88 89 // TODO: do we want a type like GuestAddress here? mmio_map(&self, id: u32, addr: u64)90 fn mmio_map(&self, id: u32, addr: u64) { 91 assert!(bql_locked()); 92 let id: i32 = id.try_into().unwrap(); 93 unsafe { 94 bindings::sysbus_mmio_map(self.upcast().as_mut_ptr(), id, addr); 95 } 96 } 97 98 // Owned<> is used here because sysbus_connect_irq (via 99 // object_property_set_link) adds a reference to the IRQState, 100 // which can prolong its life connect_irq(&self, id: u32, irq: &Owned<IRQState>)101 fn connect_irq(&self, id: u32, irq: &Owned<IRQState>) { 102 assert!(bql_locked()); 103 let id: i32 = id.try_into().unwrap(); 104 let irq: &IRQState = irq; 105 unsafe { 106 bindings::sysbus_connect_irq(self.upcast().as_mut_ptr(), id, irq.as_mut_ptr()); 107 } 108 } 109 sysbus_realize(&self)110 fn sysbus_realize(&self) { 111 // TODO: return an Error 112 assert!(bql_locked()); 113 unsafe { 114 bindings::sysbus_realize( 115 self.upcast().as_mut_ptr(), 116 addr_of_mut!(bindings::error_fatal), 117 ); 118 } 119 } 120 } 121 122 impl<R: ObjectDeref> SysBusDeviceMethods for R where R::Target: IsA<SysBusDevice> {} 123