Skip to content

Commit 7800353

Browse files
committed
uefi: Implement get/set attributes
1 parent 4fcb5d5 commit 7800353

File tree

4 files changed

+176
-41
lines changed

4 files changed

+176
-41
lines changed

uefi-raw/src/protocol/pci/resource.rs

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use static_assertions::assert_eq_size;
77
#[repr(C, packed)]
88
#[derive(Debug)]
99
pub struct QWordAddressSpaceDescriptor {
10-
tag: u8,
11-
descriptor_length: u16,
10+
pub tag: u8,
11+
pub descriptor_length: u16,
1212
pub resource_type: ResourceType,
1313
pub flags: GeneralFlags,
1414
pub type_flags: u8,
@@ -57,12 +57,18 @@ impl QWordAddressSpaceDescriptor {
5757
pub fn verify(&self) {
5858
let tag = self.tag;
5959
if tag != 0x8A {
60-
panic!("Tag value for QWordAddressSpaceDescriptor should be 0x8A, not {}", tag);
60+
panic!(
61+
"Tag value for QWordAddressSpaceDescriptor should be 0x8A, not {}",
62+
tag
63+
);
6164
}
6265

6366
let length = self.descriptor_length;
6467
if self.descriptor_length != 0x2B {
65-
panic!("Length value for QWordAddressSpaceDescriptor should be 0x2B, not {}", length);
68+
panic!(
69+
"Length value for QWordAddressSpaceDescriptor should be 0x2B, not {}",
70+
length
71+
);
6672
}
6773

6874
if self.flags.bits() & 0b11110000 != 0 {
@@ -93,7 +99,10 @@ impl QWordAddressSpaceDescriptor {
9399
let min = self.range_min;
94100
let max = self.range_max;
95101
if max < min {
96-
panic!("Address range is invalid. Max(0x{:X}) is smaller than Min(0x{:X}).", max, min);
102+
panic!(
103+
"Address range is invalid. Max(0x{:X}) is smaller than Min(0x{:X}).",
104+
max, min
105+
);
97106
}
98107
}
99108
}

uefi-test-runner/src/proto/pci/root_bridge.rs

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
// SPDX-License-Identifier: MIT OR Apache-2.0
22

33
use core::ptr;
4-
use uefi::{println, Handle};
54
use uefi::boot::{OpenProtocolAttributes, OpenProtocolParams, ScopedProtocol, image_handle};
65
use uefi::proto::ProtocolPointer;
76
use uefi::proto::pci::PciIoAddress;
8-
use uefi::proto::pci::root_bridge::PciRootBridgeIo;
7+
use uefi::proto::pci::root_bridge::{AttributeReport, PciRootBridgeIo};
8+
use uefi::{Handle, println};
99
use uefi_raw::protocol::pci::root_bridge::{
1010
PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation,
1111
};
@@ -223,6 +223,20 @@ pub fn test_config() {
223223
}
224224
}
225225

226+
pub fn test_attributes() {
227+
let pci_handles = uefi::boot::find_handles::<PciRootBridgeIo>().unwrap();
228+
229+
for pci_handle in pci_handles {
230+
let pci_proto = get_open_protocol::<PciRootBridgeIo>(pci_handle);
231+
let AttributeReport { supported, .. } = pci_proto.get_attributes();
232+
233+
pci_proto
234+
.set_attributes(PciRootBridgeIoProtocolAttribute::empty(), None)
235+
.unwrap();
236+
pci_proto.set_attributes(supported, None).unwrap();
237+
}
238+
}
239+
226240
fn get_open_protocol<P: ProtocolPointer + ?Sized>(handle: Handle) -> ScopedProtocol<P> {
227241
let open_opts = OpenProtocolParams {
228242
handle,

uefi/src/proto/pci/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
//! PCI Bus specific protocols.
44
55
use core::cmp::Ordering;
6-
6+
use core::fmt::Debug;
77
use uefi_raw::protocol::pci::root_bridge::PciRootBridgeIoProtocolWidth;
88

99
pub mod buffer;
@@ -95,7 +95,7 @@ impl Ord for PciIoAddress {
9595

9696
/// Trait implemented by all data types that can natively be read from a PCI device.
9797
/// Note: Not all of them have to actually be supported by the hardware at hand.
98-
pub trait PciIoUnit: Sized + Default + Into<u64> {}
98+
pub trait PciIoUnit: Sized + Default + Into<u64> + Debug {}
9999
impl PciIoUnit for u8 {}
100100
impl PciIoUnit for u16 {}
101101
impl PciIoUnit for u32 {}

uefi/src/proto/pci/root_bridge.rs

Lines changed: 144 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,18 @@ use core::marker::PhantomData;
1212
use core::mem::MaybeUninit;
1313
use core::num::NonZeroUsize;
1414
use core::ptr;
15-
use core::ptr::{NonNull, slice_from_raw_parts};
15+
use core::ptr::NonNull;
1616
use core::time::Duration;
1717
use log::debug;
1818
use uefi::proto::pci::PciIoMode;
1919
use uefi::proto::pci::root_bridge::io_access::IoAccessType;
2020
use uefi_macros::unsafe_protocol;
21-
use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor;
2221
use uefi_raw::Status;
23-
use uefi_raw::protocol::pci::root_bridge::{PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute, PciRootBridgeIoProtocolOperation, PciRootBridgeIoProtocolWidth};
22+
use uefi_raw::protocol::pci::resource::QWordAddressSpaceDescriptor;
23+
use uefi_raw::protocol::pci::root_bridge::{
24+
PciRootBridgeIoAccess, PciRootBridgeIoProtocol, PciRootBridgeIoProtocolAttribute,
25+
PciRootBridgeIoProtocolOperation,
26+
};
2427
use uefi_raw::table::boot::{AllocateType, MemoryType, PAGE_SIZE};
2528

2629
#[cfg(doc)]
@@ -191,9 +194,10 @@ impl PciRootBridgeIo {
191194
///
192195
/// # Returns
193196
/// [`Ok`] on successful copy.
197+
///
194198
/// [`Err`] otherwise.
195-
/// * [`Status::INVALID_PARAMETER`]: Width is invalid for this PCI root bridge.
196-
/// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
199+
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
200+
/// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
197201
/// # Question
198202
/// Should this support other types than just primitives?
199203
#[cfg(feature = "alloc")]
@@ -219,15 +223,16 @@ impl PciRootBridgeIo {
219223
///
220224
/// # Returns
221225
/// [`Ok`] when it successfully retrieved current configuration.
226+
///
222227
/// [`Err`] when it failed to retrieve current configuration.
223-
/// * Status value will be [`Status::UNSUPPORTED`]
228+
/// - Its Status value will be [`Status::UNSUPPORTED`]
224229
///
225230
/// # Panic
226231
/// It may panic if pci devices or drivers for those provided by boot service misbehave.
227232
/// There are multiple verifications put in place, and they will panic if invariants
228233
/// are broken, such as when invalid enum variant value was received
229234
/// or reserved bits are not 0
230-
pub fn configuration(&self) -> crate::Result<&'static [QWordAddressSpaceDescriptor]> {
235+
pub fn configuration(&self) -> crate::Result<&[QWordAddressSpaceDescriptor]> {
231236
let mut configuration_address = 0u64;
232237
let configuration_status = unsafe {
233238
(self.0.configuration)(
@@ -249,8 +254,10 @@ impl PciRootBridgeIo {
249254
cursor_ref.verify();
250255
count += 1;
251256
if count >= 1024 {
252-
panic!("Timed out while fetching configurations:\
253-
There are more than 1024 configuration spaces");
257+
panic!(
258+
"Timed out while fetching configurations:\
259+
There are more than 1024 configuration spaces"
260+
);
254261
}
255262
}
256263
0x79 => {
@@ -271,7 +278,7 @@ impl PciRootBridgeIo {
271278
}
272279
};
273280
let list: &[QWordAddressSpaceDescriptor] =
274-
unsafe { slice_from_raw_parts(head, count).as_ref().unwrap() };
281+
unsafe { ptr::slice_from_raw_parts(head, count).as_ref().unwrap() };
275282
Ok(list)
276283
}
277284
e => Err(e.into()),
@@ -282,77 +289,170 @@ impl PciRootBridgeIo {
282289
/// The criteria in question is met when value read from provided reference
283290
/// equals to provided value when masked:
284291
/// `(*to_poll) & mask == value`
285-
/// /// Refer to [`Self::poll_io`] for polling io port instead.
292+
///
293+
/// Refer to [`Self::poll_io`] for polling io port instead.
286294
///
287295
/// # Returns
288296
/// [`Ok`]: Criteria was met before timeout.
297+
///
289298
/// [`Err`]: One of below error happened:
290-
/// * [`Status::TIMEOUT`]: Delay expired before a match occurred.
291-
/// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
299+
/// - [`Status::TIMEOUT`]: Delay expired before a match occurred.
300+
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
301+
/// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
292302
///
293303
/// # Panic
294304
/// Panics when delay is too large (longer than 58494 years).
295-
pub fn poll_mem<U: PciIoUnit>(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> {
296-
let mut result = U::default();
305+
pub fn poll_mem<U: PciIoUnit>(
306+
&self,
307+
to_poll: &U,
308+
mask: U,
309+
value: U,
310+
delay: Duration,
311+
) -> crate::Result<(), u64> {
312+
let mut result = 0u64;
297313
let delay = delay.as_nanos().div_ceil(100).try_into().unwrap();
298314
let status = unsafe {
299315
(self.0.poll_mem)(
300316
ptr::from_ref(&self.0).cast_mut(),
301317
encode_io_mode_and_unit::<U>(PciIoMode::Normal),
302318
ptr::from_ref(to_poll).addr() as u64,
303319
mask.into(),
304-
value,
320+
value.into(),
305321
delay,
306-
&mut result
322+
&mut result,
307323
)
308324
};
309325

310-
match status {
311-
Status::SUCCESS => {
312-
Ok(())
313-
}
314-
e => Err(e.into()),
315-
}
326+
status.to_result_with_err(|_| result)
316327
}
317328

318329
/// Polls a same io port until criteria is met.
319330
/// The criteria in question is met when value read from provided reference
320331
/// equals to provided value when masked:
321332
/// `(*to_poll) & mask == value`
333+
///
322334
/// Refer to [`Self::poll_mem`] for polling memory instead.
323335
///
324336
/// # Returns
325337
/// [`Ok`]: Criteria was met before timeout.
338+
///
326339
/// [`Err`]: One of below error happened:
327-
/// * [`Status::TIMEOUT`]: Delay expired before a match occurred.
328-
/// * [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
340+
/// - [`Status::TIMEOUT`]: Delay expired before a match occurred.
341+
/// - [`Status::INVALID_PARAMETER`] The requested width is invalid for this PCI root bridge.
342+
/// - [`Status::OUT_OF_RESOURCES`]: The request could not be completed due to a lack of resources.
329343
///
330344
/// # Panic
331345
/// Panics when delay is too large (longer than 58494 years).
332-
pub fn poll_io<U: PciIoUnit>(&self, to_poll: &U, mask: U, value: U, delay: Duration) -> crate::Result<(), U> {
333-
let mut result = U::default();
346+
pub fn poll_io<U: PciIoUnit>(
347+
&self,
348+
to_poll: &U,
349+
mask: U,
350+
value: U,
351+
delay: Duration,
352+
) -> crate::Result<(), u64> {
353+
let mut result = 0u64;
334354
let delay = delay.as_nanos().div_ceil(100).try_into().unwrap();
335355
let status = unsafe {
336356
(self.0.poll_io)(
337357
ptr::from_ref(&self.0).cast_mut(),
338358
encode_io_mode_and_unit::<U>(PciIoMode::Normal),
339359
ptr::from_ref(to_poll).addr() as u64,
340360
mask.into(),
341-
value,
361+
value.into(),
342362
delay,
343-
&mut result
363+
&mut result,
364+
)
365+
};
366+
367+
status.to_result_with_err(|_| result)
368+
}
369+
370+
/// Returns available and used attributes of this root bridge.
371+
///
372+
/// # Returns
373+
/// Both supported and used attribute will be returned in struct [`AttributeReport`]
374+
pub fn get_attributes(&self) -> AttributeReport {
375+
let mut supports = PciRootBridgeIoProtocolAttribute::empty();
376+
let mut attributes = PciRootBridgeIoProtocolAttribute::empty();
377+
let status = unsafe {
378+
(self.0.get_attributes)(
379+
&self.0,
380+
ptr::from_mut(&mut supports).cast(),
381+
ptr::from_mut(&mut attributes).cast(),
382+
)
383+
};
384+
385+
match status {
386+
Status::SUCCESS => AttributeReport {
387+
supported: supports,
388+
used: attributes,
389+
},
390+
Status::INVALID_PARAMETER => unreachable!(),
391+
e => panic!("Unexpected error occurred: {:?}", e),
392+
}
393+
}
394+
395+
/// Sets attributes to use for this root bridge.
396+
/// Specified attributes must be supported. Otherwise, it will return error.
397+
/// Supported attributes can be requested with [`Self::get_attributes`]
398+
///
399+
/// # Returns
400+
/// [`Ok`]: Optional resource range. It will only be available when resource
401+
/// parameter is Some and one of:
402+
/// - [`PciRootBridgeIoProtocolAttribute::MEMORY_WRITE_COMBINE`]
403+
/// - [`PciRootBridgeIoProtocolAttribute::MEMORY_CACHED`]
404+
/// - [`PciRootBridgeIoProtocolAttribute::MEMORY_DISABLE`]
405+
/// is set.
406+
///
407+
/// [`Err`]: Possible error cases:
408+
/// - [`Status::UNSUPPORTED`]: A bit is set in Attributes that is not supported by the PCI Root Bridge.
409+
/// The supported attribute bits are reported by [`Self::get_attributes`]
410+
/// - [`Status::INVALID_PARAMETER`]: More than one attribute bit is set in Attributes that requires a resource parameter.
411+
/// - [`Status::OUT_OF_RESOURCES`]: There are not enough resources to set the attributes on the resource range specified by resource parameter.
412+
pub fn set_attributes<'a, 'p>(
413+
&'p self,
414+
attributes: PciRootBridgeIoProtocolAttribute,
415+
resource: Option<&'a [u64]>,
416+
) -> crate::Result<Option<&'a [u64]>>
417+
where
418+
'p: 'a,
419+
{
420+
let (mut base, mut length) = match resource {
421+
Some(v) => {
422+
let ptr: *const [u64] = v;
423+
let base = ptr.addr() as u64;
424+
let length = ptr.len() as u64;
425+
(base, length)
426+
}
427+
None => (0, 0),
428+
};
429+
let status = unsafe {
430+
(self.0.set_attributes)(
431+
ptr::from_ref(&self.0).cast_mut(),
432+
attributes.bits(),
433+
&mut base,
434+
&mut length,
344435
)
345436
};
346437

347438
match status {
348439
Status::SUCCESS => {
349-
Ok(())
440+
let to_return = if length != 0 {
441+
unsafe {
442+
Some(
443+
ptr::slice_from_raw_parts(base as *const u64, length as usize)
444+
.as_ref()
445+
.unwrap(),
446+
)
447+
}
448+
} else {
449+
None
450+
};
451+
Ok(to_return)
350452
}
351453
e => Err(e.into()),
352454
}
353455
}
354-
355-
// TODO: get/set attributes
356456
}
357457

358458
/// Struct for performing PCI I/O operations on a root bridge.
@@ -575,3 +675,15 @@ impl<T: IoAccessType> PciIoAccessPci<'_, T> {
575675
}
576676
}
577677
}
678+
679+
/// Struct containing return value for [`PciRootBridgeIo::get_attributes`]
680+
/// This is to minimize confusion by giving both of them names.
681+
#[derive(Debug)]
682+
pub struct AttributeReport {
683+
/// Attributes supported by this bridge.
684+
/// Only attributes in this set can be used as parameter for [`PciRootBridgeIo::set_attributes`]
685+
pub supported: PciRootBridgeIoProtocolAttribute,
686+
687+
/// Attributes currently being used.
688+
pub used: PciRootBridgeIoProtocolAttribute,
689+
}

0 commit comments

Comments
 (0)