diff --git a/src/config.rs b/src/config.rs index d35f7f5..62627be 100644 --- a/src/config.rs +++ b/src/config.rs @@ -15,6 +15,7 @@ const HTTP_BOOT_VENDOR: VariableVendor = pub struct Config { pub multicast_group: IpAddress, pub multicast_port: u16, + pub source_ip: IpAddress, pub client_uuid: String, } @@ -37,6 +38,10 @@ pub fn read_config(runtime_services: &RuntimeServices) -> Result { 0xff, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, ]), + source_ip: IpAddress::new_v6([ + 0xfe, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x01, + ]), multicast_port: 42, client_uuid: String::from("24609104-7ec6-423f-b57f-632b99b7566a"), }; @@ -59,6 +64,11 @@ pub fn read_config(runtime_services: &RuntimeServices) -> Result { conf.multicast_group = IpAddress::new_v6(group_buf); debug!("Got group {:?}", conf.multicast_group); + let mut ip_buf: [u8; 16] = [0; 16]; // at most ipv6 is 16 bytes + get_var(runtime_services, cstr16!("HTTP_BOOT_IP"), &mut ip_buf)?; + conf.source_ip = IpAddress::new_v6(ip_buf); + debug!("Got ip address {:?}", conf.source_ip); + let mut uid_buf: [u8; 36] = [0; 36]; // uuid is 36 bytes get_var(runtime_services, cstr16!("HTTP_BOOT_UUID"), &mut uid_buf)?; conf.client_uuid = match String::from_utf8(uid_buf.to_vec()) { diff --git a/src/main.rs b/src/main.rs index 2fefba1..3d984d9 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,23 +4,54 @@ pub mod config; pub mod multicast; -use log::info; +use alloc::boxed::Box; +use log::{debug, info}; use uefi::fs::PathBuf; use uefi::prelude::*; +use uefi::proto::debug; +use uefi::proto::device_path::{DevicePath, LoadedImageDevicePath}; use uefi::table::boot::LoadImageSource; use uefi::{Error, Result, Status}; -fn load_efi_app_from_path(boot_services: &BootServices, app_path: PathBuf) -> Result { - let mut fs_handler = boot_services.get_image_file_system(boot_services.image_handle())?; - let app_content = match fs_handler.read(app_path) { - Err(_e) => return Err(Error::from(Status::PROTOCOL_ERROR)), - Ok(x) => x, - }; - let image_source = LoadImageSource::FromBuffer { - buffer: &app_content, - file_path: None, +use uefi::proto::device_path::text::{AllowShortcuts, DevicePathToText, DisplayOnly}; +use uefi::proto::loaded_image::LoadedImage; +use uefi::table::boot::SearchType; +use uefi::Identify; +extern crate alloc; + +fn load_efi_app_from_path(boot_services: &BootServices, app_path: Box) -> Result { + debug!("checking value for image path"); + let device_path_to_text_handle = *boot_services + .locate_handle_buffer(SearchType::ByProtocol(&DevicePathToText::GUID))? + .first() + .expect("DevicePathToText is missing"); + + let device_path_to_text = + boot_services.open_protocol_exclusive::(device_path_to_text_handle)?; + + let scoped_app_path = &*app_path; + + let image_device_path_text = device_path_to_text + .convert_device_path_to_text( + boot_services, + scoped_app_path, + DisplayOnly(true), + AllowShortcuts(false), + ) + .expect("convert_device_path_to_text failed"); + + info!("Image path: {}", &*image_device_path_text); + + debug!("Loading image source"); + let image_source = LoadImageSource::FromDevicePath { + device_path: scoped_app_path, + from_boot_manager: false, }; + + debug!("Getting image handler"); let img_handler = boot_services.load_image(boot_services.image_handle(), image_source)?; + + debug!("Starting new image"); boot_services.start_image(img_handler) } @@ -36,6 +67,27 @@ fn main(_handle: Handle, mut system_table: SystemTable) -> Status { info!("init runtime services"); let runtime_services = system_table.runtime_services(); + let loaded_image = boot_services + .open_protocol_exclusive::(boot_services.image_handle()) + .unwrap(); + + let device_path_to_text_handle = *boot_services + .locate_handle_buffer(SearchType::ByProtocol(&DevicePathToText::GUID)) + .unwrap() + .first() + .expect("DevicePathToText is missing"); + + let device_path_to_text = boot_services + .open_protocol_exclusive::(device_path_to_text_handle) + .unwrap(); + + let image_device_path_text = loaded_image + .to_string(boot_services, DisplayOnly(false), AllowShortcuts(false)) + .expect("File path is not set") + .expect("File path is not set 2"); + + info!("Image path: {}", image_device_path_text); + let conf = config::read_config(runtime_services).unwrap(); let app_path = multicast::send_mcast(boot_services, &conf).unwrap(); load_efi_app_from_path(boot_services, app_path).unwrap(); diff --git a/src/multicast.rs b/src/multicast.rs index f7771b1..8da8f81 100644 --- a/src/multicast.rs +++ b/src/multicast.rs @@ -1,14 +1,17 @@ +use alloc::boxed::Box; use alloc::string::String; use log::{debug, error, info}; use uefi::prelude::*; use uefi::proto::network::pxe::{BaseCode, UdpOpFlags}; -use uefi::proto::network::IpAddress; use uefi::table::boot::{ScopedProtocol, SearchType}; use uefi::{Error, Identify, Result}; extern crate alloc; use crate::config::Config; use uefi::data_types::CStr16; use uefi::fs::{Path, PathBuf}; +use uefi::proto::device_path::text::DevicePathFromText; +use uefi::proto::device_path::DevicePath; +use uefi::proto::network::IpAddress; fn split_arg(raw_val: &str) -> Option<(&str, &str)> { let mut splitted = raw_val.split("="); @@ -23,9 +26,35 @@ fn split_arg(raw_val: &str) -> Option<(&str, &str)> { return Some((arg_name, arg_value)); } -fn get_boot_path<'a>(msg: String) -> Result { - debug!("Splitting message"); +fn get_boot_path<'a>( + boot_services: &BootServices, + msg: String, +) -> Result, &'a str> { + debug!("Initializing DevicePathFromTextProtocol"); + let device_path_from_text_handle = match boot_services + .locate_handle_buffer(SearchType::ByProtocol(&DevicePathFromText::GUID)) + { + Err(e) => { + return Err(Error::new(e.status(), "failed to locate handle buffer")); + } + Ok(h) => match h.first() { + None => return Err(Error::new(Status::UNSUPPORTED, "no handle buffer")), + Some(h) => *h, + }, + }; + let device_path_from_text = match boot_services + .open_protocol_exclusive::(device_path_from_text_handle) + { + Err(e) => { + return Err(Error::new( + e.status(), + "failed to open device_path_from_text protocol", + )) + } + Ok(d) => d, + }; + debug!("Splitting message"); // a message has the following format: "ACTION arg1=val1,arg2=val2" let mut splitted_msg_iter = msg.split_ascii_whitespace(); @@ -44,20 +73,21 @@ fn get_boot_path<'a>(msg: String) -> Result { None => return Err(Error::new(Status::UNSUPPORTED, "empty list of arguments")), Some(args_list) => { // split all the arguments in a comma separated list - for arg in args_list.split(",") { + for arg in args_list.split(";") { match split_arg(arg) { None => debug!("unexpected format for argument {}", arg), Some((arg_name, arg_value)) => match arg_name { "id" => debug!("found id {}", arg_value), "efi_app" => { debug!("found efi app {}", arg_value); - let mut buf = [0; 128]; + let mut buf = [0; 2000]; let path_raw = CStr16::from_str_with_buf(arg_value, &mut buf) .expect("invalid str format"); - let p = Path::new(path_raw); - let mut res = PathBuf::new(); - res.push(p); - return Ok(res); + + return Ok(device_path_from_text + .convert_text_to_device_path(path_raw) + .expect("failed to parse device path") + .to_boxed()); } _ => debug!("unexpected argument {} with value {}", arg_name, arg_value), }, @@ -68,8 +98,11 @@ fn get_boot_path<'a>(msg: String) -> Result { Err(Error::new(Status::UNSUPPORTED, "no efi app defined")) } -fn read_mcast_response<'a>(base_client: &mut ScopedProtocol<'_, BaseCode>) -> Result { - let mut buf: [u8; 200] = [0; 200]; +fn read_mcast_response<'a>( + boot_services: &BootServices, + base_client: &mut ScopedProtocol<'_, BaseCode>, +) -> Result> { + let mut buf: [u8; 10000] = [0; 10000]; let n = base_client .udp_read( @@ -89,7 +122,7 @@ fn read_mcast_response<'a>(base_client: &mut ScopedProtocol<'_, BaseCode>) -> Re info!("Read {} bytes", n); let received_msg = String::from_utf8(buf[..n].to_vec()).unwrap(); info!("Received message {}", received_msg); - match get_boot_path(received_msg) { + match get_boot_path(boot_services, received_msg) { Ok(r) => Ok(r), Err(e) => { error!("failed to get boot path: {}", e.data()); @@ -98,21 +131,14 @@ fn read_mcast_response<'a>(base_client: &mut ScopedProtocol<'_, BaseCode>) -> Re } } -pub fn send_mcast<'a>(boot_service: &BootServices, conf: &Config) -> Result { +pub fn send_mcast<'a>(boot_services: &BootServices, conf: &Config) -> Result> { debug!("Initializing base bootservice client handle"); - let base_client_handle = *boot_service + let base_client_handle = *boot_services .locate_handle_buffer(SearchType::ByProtocol(&BaseCode::GUID))? .first() .expect("DevicePathToText is missing"); - let mut base_client = boot_service.open_protocol_exclusive::(base_client_handle)?; - - debug!("Initializing source IP address"); - let src_bytes: [u8; 16] = [ - 0x20, 0x01, 0x0d, 0xb8, 0x00, 0x00, 0x00, 0x00, 0x02, 0x16, 0x3e, 0xff, 0xfe, 0xc2, 0x16, - 0xb7, - ]; - let src_address: IpAddress = IpAddress::new_v6(src_bytes); + let mut base_client = boot_services.open_protocol_exclusive::(base_client_handle)?; let mut req_msg = String::from("BOOT_REQUEST id="); req_msg.push_str(&conf.client_uuid); @@ -121,8 +147,24 @@ pub fn send_mcast<'a>(boot_service: &BootServices, conf: &Config) -> Result(boot_service: &BootServices, conf: &Config) -> Result