Giter Club home page Giter Club logo

Comments (17)

nicholasbishop avatar nicholasbishop commented on June 16, 2024 2

Using EFI_SERIAL_IO_PROTOCOL as an example, it's defined in the spec as:

typedef struct {
 UINT32                       Revision;
 EFI_SERIAL_RESET             Reset;
 EFI_SERIAL_SET_ATTRIBUTES    SetAttributes;
 EFI_SERIAL_SET_CONTROL_BITS  SetControl;
 EFI_SERIAL_GET_CONTROL_BITS  GetControl;
 EFI_SERIAL_WRITE             Write;
 EFI_SERIAL_READ              Read;
 SERIAL_IO_MODE               *Mode;
 CONST EFI_GUID               *DeviceTypeGuid; // Revision 1.1
} EFI_SERIAL_IO_PROTOCOL;

The type of SetControl is EFI_SERIAL_SET_CONTROL_BITS, which is a typedef defined under EFI_SERIAL_IO_PROTOCOL.SetControl:

typedef
EFI_STATUS
(EFIAPI *EFI_SERIAL_SET_CONTROL_BITS) (
 IN EFI_SERIAL_IO_PROTOCOL                   *This,
 IN UINT32                                   Control
 );

You can usually translate these function types pretty directly into Rust. Note that we don't bother with creating a type alias in the Rust API, you can just write the function pointer type inline in the struct definition.

One last note, our definition of the function does look a little different from C in a couple ways:

  • The name of the field is set_control_bits instead of set_control; that's actually just a mistake :)
  • The second arg's type is ControlBits instead of a u32, but that ControlBits type is itself a wrapper around a u32 type.

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024 1

I've put up a PR to add support for this protocol: #1109

Unfortunately, despite this having been added to the UEFI spec a while ago, it doesn't seem to be well supported. I tested a recent Lenovo, as well as OVMF in QEMU, and neither one has this protocol.

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024 1

Actually, the things I wanna ask you if some design about the callback,

Regarding how to use and implement callbacks, a good example is BootService::create_event:

pub unsafe fn create_event(
&self,
event_ty: EventType,
notify_tpl: Tpl,
notify_fn: Option<EventNotifyFn>,
notify_ctx: Option<NonNull<c_void>>,
) -> Result<Event> {
let mut event = ptr::null_mut();
// Safety: the argument types of the function pointers are defined
// differently, but are compatible and can be safely transmuted.
let notify_fn: Option<uefi_raw::table::boot::EventNotifyFn> = mem::transmute(notify_fn);
let notify_ctx = opt_nonnull_to_ptr(notify_ctx);
// Now we're ready to call UEFI
(self.0.create_event)(event_ty, notify_tpl, notify_fn, notify_ctx, &mut event)
.to_result_with_val(
// OK to unwrap: event is non-null for Status::SUCCESS.
|| Event::from_ptr(event).unwrap(),
)
}

And here's an example of calling it:

fn test_event_callback(bt: &BootServices) {
extern "efiapi" fn callback(_event: Event, _ctx: Option<NonNull<c_void>>) {
info!("Inside the event callback");
}
let event =
unsafe { bt.create_event(EventType::NOTIFY_WAIT, Tpl::CALLBACK, Some(callback), None) }
.expect("Failed to create custom event");
bt.check_event(event).expect("Failed to check event");
}

Note that although Rust does have closures (|param| { body }), those can't be used here directly because we need to pass in a function with the correct ABI (efiapi).

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024 1

Regarding the timestamp protocol, it seems to me that for whatever reason edk2 doesn't broadly enable it. I'm not 100% sure, but I don't think there's an easy way to just enable it at compile time; some code changes would be needed.

For the uefi-test-runner, it's intentional that it only works under QEMU. Originally it was more flexible and would only warn instead of fail if the environment was different, but this made the test less useful for actually finding errors. Several times I introduced a change that accidentally broke something, but we didn't notice because the test runner still "passed". So, we changed it to require QEMU, and provided prebuilt OVMF files that have various extra things enabled. See #553 for some more info.

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024

Hi, that protocol is not currently implemented in uefi-rs. (Contributions welcome, if you are interested in implementing it!) Depending on what your use case is, https://docs.rs/uefi/latest/uefi/table/runtime/struct.RuntimeServices.html#method.get_time might be an acceptable alternative.

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

Hi, that protocol is not currently implemented in uefi-rs. (Contributions welcome, if you are interested in implementing it!) Depending on what your use case is, https://docs.rs/uefi/latest/uefi/table/runtime/struct.RuntimeServices.html#method.get_time might be an acceptable alternative.

if I want to implement it, how could I found the efiapi which should be used. Is there any references or docs?

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024

First add the raw protocol in this directory: https://github.com/rust-osdev/uefi-rs/tree/main/uefi-raw/src/protocol. This could go in a new misc.rs module. See protocol/loaded_image.rs for an example of what this would look like -- basically just adding a TimestampProtocol struct containing two function pointers, matching what's in the spec, and an associated GUID constant.

Then you'll add the higher-level version to the uefi package, in this directory: https://github.com/rust-osdev/uefi-rs/tree/main/uefi/src/proto. Again this would be a misc.rs module, see proto/loaded_image.rs for an example. Add a repr(transparent) Timestamp struct, and implement get_timestamp and get_properties.

That's the general outline, happy to answer questions about any of the details. And/or, feel free to put up a PR even if it's not complete, and I can make comments there too.

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

First add the raw protocol in this directory: https://github.com/rust-osdev/uefi-rs/tree/main/uefi-raw/src/protocol. This could go in a new misc.rs module. See protocol/loaded_image.rs for an example of what this would look like -- basically just adding a TimestampProtocol struct containing two function pointers, matching what's in the spec, and an associated GUID constant.

Then you'll add the higher-level version to the uefi package, in this directory: https://github.com/rust-osdev/uefi-rs/tree/main/uefi/src/proto. Again this would be a misc.rs module, see proto/loaded_image.rs for an example. Add a repr(transparent) Timestamp struct, and implement get_timestamp and get_properties.

That's the general outline, happy to answer questions about any of the details. And/or, feel free to put up a PR even if it's not complete, and I can make comments there too.

like this:

#[derive(Debug)]
#[repr(C)]
pub struct SerialIoProtocol {
pub revision: u32,
pub reset: unsafe extern "efiapi" fn(*mut Self) -> Status,
pub set_attributes: unsafe extern "efiapi" fn(
*const Self,
baud_rate: u64,
receive_fifo_depth: u32,
timeout: u32,
parity: Parity,
data_bits: u8,
stop_bits_type: StopBits,
) -> Status,
pub set_control_bits: unsafe extern "efiapi" fn(*mut Self, ControlBits) -> Status,
pub get_control_bits: unsafe extern "efiapi" fn(*const Self, *mut ControlBits) -> Status,
pub write: unsafe extern "efiapi" fn(*mut Self, *mut usize, *const u8) -> Status,
pub read: unsafe extern "efiapi" fn(*mut Self, *mut usize, *mut u8) -> Status,
pub mode: *const SerialIoMode,
}

but I didn't know how to found out the "efiapi" 's set_control_bits func definition.

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

Weee!! I just completed the support of TimestampProtocol today yeah.
And another things, I have a doubt about ResetNotificationProtocol and didn't know how to code it, it has a pointer to func which confuses me.

Docs:

EDK2:

https://github.com/tianocore/edk2/blob/37f63deeefa89b7ac28285bc59eca22a5c00eb92/MdePkg/Include/Protocol/ResetNotification.h#L21-L48

https://github.com/tianocore/edk2/blob/37f63deeefa89b7ac28285bc59eca22a5c00eb92/MdePkg/Include/Uefi/UefiSpec.h#L1072-L1095

image

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

I've put up a PR to add support for this protocol: #1109

Unfortunately, despite this having been added to the UEFI spec a while ago, it doesn't seem to be well supported. I tested a recent Lenovo, as well as OVMF in QEMU, and neither one has this protocol.

I think its implementation is related to the UEFI Spec version number commonly used by vendors, probably an ancient version. It seem that I should find another way to get timestamp.

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024

One option you could try (if you are on x86) is the RDTSC instruction, or RDTSCP. These can be used to read a timestamp counter.

Depending on exactly what your needs are, this can be somewhat complex; see How to Benchmark Code Execution
Times on Intel IA-32 and IA-64 Instruction Set Architectures
for details of execution barriers. Whether those details matter probably depends on whether you are microbenchmarking or looking at something longer running.

There's also the question of units. On new-ish processors the timestamp frequency is invariant, and on sufficiently new processors you can directly query the scaling factors needed to convert timestamps to seconds. See for example this code in edk2. On older processors you might need to figure out the frequency by measuring the timestamp on both sides of a call to BootServices::stall. On the other hand, you can skip all of that if you only care about relative duration and don't need to convert to seconds.

from uefi-rs.

nicholasbishop avatar nicholasbishop commented on June 16, 2024

And another things, I have a doubt about ResetNotificationProtocol and didn't know how to code it, it has a pointer to func which confuses me.

A function pointer in Rust looks very much like a regular function definition. Here's an example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b8ce6ab497a1119b2db8e1a22ac98742

extern "C" fn f2() {}

fn f1(fn_ptr: extern "C" fn() -> ()) {
    dbg!(fn_ptr);
}

fn main() {
    f1(f2);
}

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

And another things, I have a doubt about ResetNotificationProtocol and didn't know how to code it, it has a pointer to func which confuses me.

A function pointer in Rust looks very much like a regular function definition. Here's an example: https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b8ce6ab497a1119b2db8e1a22ac98742

extern "C" fn f2() {}

fn f1(fn_ptr: extern "C" fn() -> ()) {
    dbg!(fn_ptr);
}

fn main() {
    f1(f2);
}





It seems that nothing different with C.
Actually, the things I wanna ask you if some design about the callback, I am new ruster, and for other language like Golang/Java has lambda function design. rust also has something like

let func_ptr = |x| -> { 
  fmt.println(x);
  unsafe{ 
      /* some code for ABI call */
  }
}


set_callback(type, func_ptr);

I wanna know whether uefi-rs has any design or paradigm? The uefi-rs would gives user or developer what kinds of ways/forms. the docs doesn't list any advices. I don't wanna break the rules.

Sorry for my poor English.
And Thanks for your guidance!

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

One option you could try (if you are on x86) is the RDTSC instruction, or RDTSCP. These can be used to read a timestamp counter.

Depending on exactly what your needs are, this can be somewhat complex; see How to Benchmark Code Execution Times on Intel IA-32 and IA-64 Instruction Set Architectures for details of execution barriers. Whether those details matter probably depends on whether you are microbenchmarking or looking at something longer running.

There's also the question of units. On new-ish processors the timestamp frequency is invariant, and on sufficiently new processors you can directly query the scaling factors needed to convert timestamps to seconds. See for example this code in edk2. On older processors you might need to figure out the frequency by measuring the timestamp on both sides of a call to BootServices::stall. On the other hand, you can skip all of that if you only care about relative duration and don't need to convert to seconds.

yeah, the one that I need now is only the duration counter. I am an embeded developer for Arm Soc, it has a timer/counter which meet demand, And x86 should also has it. But now I try develop uefi app/driver at x86, just for fun, coding fun! but x86 is really complex which I couldn't easy to get the way.





thank you, I read the code, found they are using the reg named TSC (Time Stamp Counter), though affected by cpu hz. I ever use it in Linux.

and also could use the the ACPI Timer, HPET, or High Precision Event Timer, AP IC Timer/PIC and so on. see:

I found blog on the internet which recommend constant TSC > HPET > ACPI_PM > TSC > i8254 in Linux.

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

PR #1116 , though in OVMF run at cargo run xtask, review it please, and thanks for your teach. It print warn log as I code, hope that I don't make some mistake:

[ INFO]: uefi-test-runner\src\proto\misc.rs@020: Running loaded Timestamp Protocol test
[ WARN]: uefi-test-runner\src\proto\misc.rs@034: Failed to open Timestamp Protocol: Error { status: UNSUPPORTED, data: () }
[ INFO]: uefi-test-runner\src\proto\misc.rs@041: Running loaded ResetNotification protocol test
[ WARN]: uefi-test-runner\src\proto\misc.rs@076: Failed to open ResetNotification Protocol: Error { status: UNSUPPORTED, data: () }

the new log after I use readonly hook, I think it related to the runtime hook:

/// ```sh
[ INFO]: uefi-test-runner\src\proto\misc.rs@020: Running loaded Timestamp Protocol test
[ WARN]: uefi-test-runner\src\proto\misc.rs@037: Failed to found Timestamp Protocol: Error { status: NOT_FOUND, data: () }
[ INFO]: uefi-test-runner\src\proto\misc.rs@043: Running loaded ResetNotification protocol test
[ INFO]: uefi-test-runner\src\proto\misc.rs@053: ResetNotification Protocol register null test: Err(Error { status: INVALID_PARAMETER, data: () })
[ INFO]: uefi-test-runner\src\proto\misc.rs@059: ResetNotification Protocol unregister null test: Err(Error { status: INVALID_PARAMETER, data: () })
[ INFO]: uefi-test-runner\src\proto\misc.rs@078: ResetNotification Protocol register efi_reset_fn test: Ok(())
[ INFO]: uefi-test-runner\src\proto\misc.rs@084: ResetNotification Protocol unregister efi_reset_fn test: Ok(())

from uefi-rs.

phip1611 avatar phip1611 commented on June 16, 2024

Well, OVMF doesn't has the protocol compiled in, I suppose? Is there an easy way to add it to our pre-build OVMF version, @nicholasbishop ?

from uefi-rs.

sky5454 avatar sky5454 commented on June 16, 2024

Well, OVMF doesn't has the protocol compiled in, I suppose? Is there an easy way to add it to our pre-build OVMF version, @nicholasbishop ?

I run target/x86_64-unknown-uefi/debug/uefi-test-runner.efi in VMware, and uefi-test-runner has a assert like this:

image
image
image

#[entry]
fn efi_main(image: Handle, mut st: SystemTable<Boot>) -> Status {
// Initialize utilities (logging, memory allocation...)
uefi_services::init(&mut st).expect("Failed to initialize utilities");
// unit tests here
let firmware_vendor = st.firmware_vendor();
info!("Firmware Vendor: {}", firmware_vendor);
assert_eq!(firmware_vendor.to_string(), "EDK II");
// Test print! and println! macros.
let (print, println) = ("print!", "println!"); // necessary for clippy to ignore
print!("Testing {} macro with formatting: {:#010b} ", print, 155u8);
println!(
"Testing {} macro with formatting: {:#010b} ",
println, 155u8
);

I think it should be replaced as warnning if want to test on other platforms.

edit those two line assert code, I finally Test failed for VMware uefi firmware is old, even not support the console serial.
image

from uefi-rs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    πŸ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. πŸ“ŠπŸ“ˆπŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❀️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.