-
Notifications
You must be signed in to change notification settings - Fork 7
For qcom/quality improvements #18
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -24,7 +24,7 @@ mod initrd; | |
| mod memcardinfo; | ||
|
|
||
| mod peimage; | ||
| use peimage::{handle_peimage, is_peimage}; | ||
| use peimage::{expected_pe_machine, handle_peimage, is_peimage, pe_machine}; | ||
|
|
||
| mod proto; | ||
|
|
||
|
|
@@ -74,27 +74,42 @@ fn fastboot_open(serial_number: &CStr16) -> Result<ScopedProtocol<EfiUsbDevice>> | |
| Ok(usb_device) | ||
| } | ||
|
|
||
| fn fastboot_respond(usb_device: &ScopedProtocol<EfiUsbDevice>, response: &str) -> Result { | ||
| let buf = usb_device | ||
| .allocate_transfer_buffer(64) | ||
| .expect("failed to allocate command buffer"); | ||
|
|
||
| fn fastboot_respond( | ||
| usb_device: &ScopedProtocol<EfiUsbDevice>, | ||
| response_buffer: *mut u8, | ||
| response: &str, | ||
| ) -> Result { | ||
| let mut payload = response.as_bytes().to_vec(); | ||
| let payload_len = payload.len().min(64); | ||
| payload.push(0); | ||
|
|
||
| unsafe { | ||
| ptr::copy_nonoverlapping(payload.as_ptr(), buf, payload_len); | ||
| ptr::copy_nonoverlapping(payload.as_ptr(), response_buffer, payload_len); | ||
| } | ||
|
|
||
| usb_device | ||
| .send(usb_device::ENDPOINT_IN, payload_len, buf) | ||
| .send(usb_device::ENDPOINT_IN, payload_len, response_buffer) | ||
| .expect("failed to send response"); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn handle_download(usb_device: &ScopedProtocol<EfiUsbDevice>, size: usize) -> Result<&[u8]> { | ||
| fn parse_fastboot_request(data: &[u8]) -> core::result::Result<&str, ()> { | ||
| let request_end = data | ||
| .iter() | ||
| .position(|&byte| byte == 0) | ||
| .unwrap_or(data.len()); | ||
| let request = core::str::from_utf8(&data[..request_end]).map_err(|_| ())?; | ||
| let request = request.trim_end_matches(|ch| ch == '\r' || ch == '\n'); | ||
|
|
||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| Ok(request) | ||
| } | ||
|
|
||
| fn handle_download( | ||
| usb_device: &ScopedProtocol<EfiUsbDevice>, | ||
| response_buffer: *mut u8, | ||
| size: usize, | ||
| ) -> Result<&[u8]> { | ||
| let mut download_remains = size; | ||
|
|
||
| let target = boot::allocate_pool(MemoryType::BOOT_SERVICES_DATA, size).unwrap(); | ||
|
|
@@ -107,7 +122,7 @@ fn handle_download(usb_device: &ScopedProtocol<EfiUsbDevice>, size: usize) -> Re | |
| .allocate_transfer_buffer(16 * 1024 * 1024) | ||
| .expect("failed to allocate command buffer"); | ||
|
|
||
| fastboot_respond(usb_device, &format!("DATA{size:08x}"))?; | ||
| fastboot_respond(usb_device, response_buffer, &format!("DATA{size:08x}"))?; | ||
|
|
||
| usb_device | ||
| .send( | ||
|
|
@@ -146,7 +161,7 @@ fn handle_download(usb_device: &ScopedProtocol<EfiUsbDevice>, size: usize) -> Re | |
| usb_device.free_transfer_buffer(receive_buffer)?; | ||
|
|
||
| if offset == target_slice.len() { | ||
| fastboot_respond(usb_device, "OKAY")?; | ||
| fastboot_respond(usb_device, response_buffer, "OKAY")?; | ||
| } | ||
|
|
||
| Ok(target_slice) | ||
|
|
@@ -245,37 +260,103 @@ fn create_empty_rt_properties_table() -> Result<FastbootBuffer> { | |
| Ok(buf) | ||
| } | ||
|
|
||
| fn handle_boot(usb_device: &ScopedProtocol<EfiUsbDevice>, payload: &[u8]) -> Result { | ||
| fn handle_boot( | ||
| usb_device: &ScopedProtocol<EfiUsbDevice>, | ||
| response_buffer: *mut u8, | ||
| payload: &[u8], | ||
| ) -> Result { | ||
| let (handle, _initrd) = if is_peimage(payload) { | ||
| (handle_peimage(payload)?, None) | ||
| let result = handle_peimage(payload); | ||
| if let Err(err) = result { | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!("FAILfailed: {:?}", err.status()), | ||
| )?; | ||
| return Ok(()); | ||
| } | ||
| (result.unwrap(), None) | ||
| } else if let Some(machine) = pe_machine(payload) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My immediate reaction would be to move this near the: if machine != PE_ARM64 {
return false;
}check in is_peimage, however that function currently only returns a boolean.. but OTOH it may be useful to have it define a couple of related error types |
||
| info!( | ||
| "rejecting EFI payload with unsupported machine {:04x}, expected {:04x}", | ||
| machine, | ||
| expected_pe_machine() | ||
| ); | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!( | ||
| "FAILunsupported EFI arch {:04x}, need {:04x}", | ||
| machine, | ||
| expected_pe_machine() | ||
| ), | ||
| )?; | ||
| return Ok(()); | ||
| } else if is_bootimg_v0(payload) { | ||
| let result = handle_bootimg_v0(payload); | ||
| if let Err(err) = result { | ||
| fastboot_respond(usb_device, &format!("FAILfailed: {}", err.data()))?; | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!("FAILfailed: {}", err.data()), | ||
| )?; | ||
| return Ok(()); | ||
| } | ||
| (result.unwrap(), None) | ||
| } else if is_bootimg_v2(payload) { | ||
| let result = handle_bootimg_v2(payload); | ||
| if let Err(err) = result { | ||
| fastboot_respond(usb_device, &format!("FAILfailed: {}", err.data()))?; | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!("FAILfailed: {}", err.data()), | ||
| )?; | ||
| return Ok(()); | ||
| } | ||
| result.unwrap() | ||
| } else { | ||
| fastboot_respond(usb_device, "FAIL")?; | ||
| return Err(uefi::Error::new(Status::INVALID_PARAMETER, ())); | ||
| info!("rejecting payload with unsupported format"); | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| "FAILunsupported boot image format", | ||
| )?; | ||
| return Ok(()); | ||
| }; | ||
|
|
||
| create_empty_rt_properties_table()?.install_configuration_table(&EFI_RT_PROPERTIES_TABLE)?; | ||
| let rt_properties = match create_empty_rt_properties_table() { | ||
| Ok(table) => table, | ||
| Err(err) => { | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!("FAILfailed: {:?}", err.status()), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not quite sure, but I think that this will only print the textual representation of the UEFI error, but give no clues to what function it came from |
||
| )?; | ||
| return Ok(()); | ||
| } | ||
| }; | ||
| if let Err(err) = rt_properties.install_configuration_table(&EFI_RT_PROPERTIES_TABLE) { | ||
| fastboot_respond( | ||
| usb_device, | ||
| response_buffer, | ||
| &format!("FAILfailed: {:?}", err.status()), | ||
| )?; | ||
| return Ok(()); | ||
| } | ||
|
|
||
| fastboot_respond(usb_device, "OKAY")?; | ||
| boot::start_image(handle)?; | ||
| fastboot_respond(usb_device, response_buffer, "OKAY")?; | ||
| if let Err(err) = boot::start_image(handle) { | ||
| info!("boot image returned after OKAY: {:?}", err.status()); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
||
| fn handle_getvar(usb_device: &ScopedProtocol<EfiUsbDevice>, variable: &str) -> Result { | ||
| fn handle_getvar( | ||
| usb_device: &ScopedProtocol<EfiUsbDevice>, | ||
| response_buffer: *mut u8, | ||
| variable: &str, | ||
| ) -> Result { | ||
| let response = match variable { | ||
| "version" => Some("0.4"), | ||
| "version-bootloader" => Some(env!("BUILD_VERSION")), | ||
|
|
@@ -287,7 +368,7 @@ fn handle_getvar(usb_device: &ScopedProtocol<EfiUsbDevice>, variable: &str) -> R | |
| None => format!("FAILunknown variable: {variable}"), | ||
| }; | ||
|
|
||
| fastboot_respond(usb_device, &response).expect("Failed to send response"); | ||
| fastboot_respond(usb_device, response_buffer, &response).expect("Failed to send response"); | ||
|
|
||
| Ok(()) | ||
| } | ||
|
|
@@ -328,6 +409,9 @@ fn main() -> Status { | |
| let command_buffer = usb_device | ||
| .allocate_transfer_buffer(1024 * 1024) | ||
| .expect("failed to allocate command buffer"); | ||
| let response_buffer = usb_device | ||
| .allocate_transfer_buffer(64) | ||
| .expect("failed to allocate response buffer"); | ||
|
|
||
| let mut loaded_data: Option<&[u8]> = None; | ||
|
|
||
|
|
@@ -342,22 +426,57 @@ fn main() -> Status { | |
| .expect("failed to queue command buffer"); | ||
| } | ||
| usb_device::EfiUsbDeviceEvent::OutData(data) => { | ||
| let request = core::str::from_utf8(data).unwrap(); | ||
| let request = match parse_fastboot_request(data) { | ||
| Ok(request) => request, | ||
| Err(()) => { | ||
| fastboot_respond( | ||
| &usb_device, | ||
| response_buffer, | ||
| "FAILmalformed fastboot command", | ||
| ) | ||
| .expect("Failed to send response"); | ||
| usb_device | ||
| .send(usb_device::ENDPOINT_OUT, 1024 * 1024, command_buffer) | ||
| .expect("failed to queue command buffer"); | ||
| continue; | ||
| } | ||
| }; | ||
|
|
||
| if request.starts_with("download:") { | ||
| let parts = request.split(':').nth(1).unwrap(); | ||
| let size = usize::from_str_radix(parts, 16).unwrap(); | ||
| let Some(parts) = request.strip_prefix("download:") else { | ||
| fastboot_respond(&usb_device, response_buffer, "FAILinvalid download") | ||
| .expect("Failed to send response"); | ||
| usb_device | ||
| .send(usb_device::ENDPOINT_OUT, 1024 * 1024, command_buffer) | ||
| .expect("failed to queue command buffer"); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: there are some |
||
| continue; | ||
| }; | ||
| let Ok(size) = usize::from_str_radix(parts, 16) else { | ||
| fastboot_respond(&usb_device, response_buffer, "FAILinvalid download size") | ||
| .expect("Failed to send response"); | ||
| usb_device | ||
| .send(usb_device::ENDPOINT_OUT, 1024 * 1024, command_buffer) | ||
| .expect("failed to queue command buffer"); | ||
| continue; | ||
| }; | ||
|
|
||
| loaded_data = Some(handle_download(&usb_device, size).unwrap()); | ||
| loaded_data = | ||
| Some(handle_download(&usb_device, response_buffer, size).unwrap()); | ||
| } else if request == "boot" { | ||
| if let Some(payload) = loaded_data { | ||
| handle_boot(&usb_device, payload).expect("Failed to handle boot command"); | ||
| if let Err(err) = handle_boot(&usb_device, response_buffer, payload) { | ||
| info!("Failed to handle boot command: {:?}", err.status()); | ||
| } | ||
| } else { | ||
| fastboot_respond(&usb_device, "FAILdownload something first") | ||
| .expect("Failed to send response"); | ||
| fastboot_respond( | ||
| &usb_device, | ||
| response_buffer, | ||
| "FAILdownload something first", | ||
| ) | ||
| .expect("Failed to send response"); | ||
| }; | ||
| } else if request == "reboot" { | ||
| let _ = fastboot_respond(&usb_device, "OKAY"); | ||
| let _ = fastboot_respond(&usb_device, response_buffer, "OKAY"); | ||
|
|
||
| let reset_data = cstr16!("RESET_PARAM"); | ||
| runtime::reset( | ||
|
|
@@ -366,19 +485,22 @@ fn main() -> Status { | |
| Some(reset_data.as_bytes()), | ||
| ); | ||
| } else if request == "continue" { | ||
| let _ = fastboot_respond(&usb_device, "OKAY"); | ||
| let _ = fastboot_respond(&usb_device, response_buffer, "OKAY"); | ||
|
|
||
| break 'message_loop; | ||
| } else if request.starts_with("getvar") { | ||
| let Some(variable) = request.split(':').nth(1) else { | ||
| fastboot_respond(&usb_device, "FAILinvalid getvar") | ||
| } else if request == "getvar" { | ||
| fastboot_respond(&usb_device, response_buffer, "FAILinvalid getvar") | ||
| .expect("Failed to send response"); | ||
| } else if let Some(variable) = request.strip_prefix("getvar:") { | ||
| if variable.is_empty() { | ||
| fastboot_respond(&usb_device, response_buffer, "FAILinvalid getvar") | ||
| .expect("Failed to send response"); | ||
| continue; | ||
| }; | ||
|
|
||
| handle_getvar(&usb_device, variable).expect("Failed to handle getvar command"); | ||
| } else { | ||
| handle_getvar(&usb_device, response_buffer, variable) | ||
| .expect("Failed to handle getvar command"); | ||
| } | ||
| } else { | ||
| fastboot_respond(&usb_device, "FAILunknown command") | ||
| fastboot_respond(&usb_device, response_buffer, "FAILunknown command") | ||
| .expect("Failed to send response"); | ||
| } | ||
|
|
||
|
|
@@ -394,6 +516,9 @@ fn main() -> Status { | |
| usb_device | ||
| .free_transfer_buffer(command_buffer) | ||
| .expect("Failed to free transfer buffer"); | ||
| usb_device | ||
| .free_transfer_buffer(response_buffer) | ||
| .expect("Failed to free response buffer"); | ||
|
|
||
| Status::SUCCESS | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we do without the boilerplate argument, i.e. perhaps by declaring a global
Option<T>and initializing it in main?