--- a/src/os/uefi/device.rs 2026-05-04 00:20:20.137148917 +0100 +++ b/src/os/uefi/device.rs 2026-05-04 00:22:16.976840526 +0100 @@ -12,6 +12,146 @@ use uefi_std::{fs::FileSystem, loaded_image::LoadedImage, proto::Protocol}; use super::disk::{DiskEfi, DiskOrFileEfi}; +use redoxfs::Disk; + +const LINUX_FS_GUID: [u8; 16] = [ + 0xAF, 0x3D, 0xC6, 0x0F, 0x83, 0x84, 0x72, 0x47, + 0x8E, 0x79, 0x3D, 0x69, 0xD8, 0x47, 0x7D, 0xE4, +]; + +fn gpt_find_redoxfs_offset(disk: &mut DiskEfi) -> u64 { + let block_size = disk.0.Media.BlockSize as u64; + if block_size == 0 { + return 2 * crate::MIBI as u64; + } + + let mut header_buf = [0u8; 512]; + let lba1_block = block_size / redoxfs::BLOCK_SIZE; + let header_read_len = if 512 <= redoxfs::BLOCK_SIZE as usize { + redoxfs::BLOCK_SIZE as usize + } else { + 512 + }; + let mut read_buf = vec![0u8; header_read_len]; + if unsafe { disk.read_at(lba1_block, &mut read_buf) }.is_err() { + log::warn!("GPT: failed to read LBA 1"); + return 2 * crate::MIBI as u64; + } + let copy_len = 512.min(read_buf.len()); + header_buf[..copy_len].copy_from_slice(&read_buf[..copy_len]); + + if &header_buf[0..8] != b"EFI PART" { + log::warn!("GPT: no valid signature at LBA 1"); + return 2 * crate::MIBI as u64; + } + + let read_le_u32 = |off: usize| -> u32 { + u32::from_le_bytes(header_buf[off..off + 4].try_into().unwrap_or([0; 4])) + }; + let read_le_u64 = |off: usize| -> u64 { + u64::from_le_bytes(header_buf[off..off + 8].try_into().unwrap_or([0; 8])) + }; + + let _revision = read_le_u32(8); + let _header_size = read_le_u32(12); + let partition_entry_start_lba = read_le_u64(72); + let num_entries = read_le_u32(80); + let entry_size = read_le_u32(84); + + if num_entries == 0 || entry_size == 0 || num_entries > 128 { + log::warn!("GPT: invalid entry count {} or size {}", num_entries, entry_size); + return 2 * crate::MIBI as u64; + } + + let entries_byte_offset = partition_entry_start_lba * block_size; + let entries_start_block = entries_byte_offset / redoxfs::BLOCK_SIZE; + + let total_entries_bytes = num_entries as usize * entry_size as usize; + let read_size = total_entries_bytes.min(4096); + let mut entries_buf = vec![0u8; read_size + redoxfs::BLOCK_SIZE as usize]; + let entries_read_blocks = (read_size as u64).div_ceil(redoxfs::BLOCK_SIZE) as usize; + if unsafe { disk.read_at(entries_start_block, &mut entries_buf[..entries_read_blocks * redoxfs::BLOCK_SIZE as usize]) }.is_err() { + log::warn!("GPT: failed to read partition entries"); + return 2 * crate::MIBI as u64; + } + + let entries_per_chunk = read_size / entry_size as usize; + for i in 0..entries_per_chunk.min(num_entries as usize) { + let off = i * entry_size as usize; + if off + entry_size as usize > entries_buf.len() { + break; + } + let entry = &entries_buf[off..off + entry_size as usize]; + + let type_guid = &entry[0..16]; + if type_guid == [0u8; 16] { + continue; + } + + if type_guid == LINUX_FS_GUID { + let first_lba = u64::from_le_bytes( + entry[32..40].try_into().unwrap_or([0; 8]), + ); + let offset_bytes = first_lba * block_size; + log::debug!("GPT: found REDOXFS/Linux partition at LBA {} (offset {} bytes)", first_lba, offset_bytes); + return offset_bytes; + } + } + + log::warn!("GPT: no Linux filesystem partition found, falling back to 2 MiB"); + 2 * crate::MIBI as u64 +} + +fn gpt_find_redoxfs_offset_from_slice(data: &[u8]) -> u64 { + if data.len() < 520 { + return 2 * crate::MIBI as u64; + } + + let header = &data[512..]; + if &header[0..8] != b"EFI PART" { + return 2 * crate::MIBI as u64; + } + + let read_le_u32 = |off: usize| -> u32 { + u32::from_le_bytes(header[off..off + 4].try_into().unwrap_or([0; 4])) + }; + let read_le_u64 = |off: usize| -> u64 { + u64::from_le_bytes(header[off..off + 8].try_into().unwrap_or([0; 8])) + }; + + let partition_entry_start_lba = read_le_u64(72); + let num_entries = read_le_u32(80); + let entry_size = read_le_u32(84); + + if num_entries == 0 || entry_size == 0 || num_entries > 128 { + return 2 * crate::MIBI as u64; + } + + let gpt_lba_size: u64 = 512; + let entries_byte_offset = partition_entry_start_lba * gpt_lba_size; + + for i in 0..num_entries as usize { + let off = entries_byte_offset as usize + i * entry_size as usize; + if off + entry_size as usize > data.len() { + break; + } + let entry = &data[off..off + entry_size as usize]; + + let type_guid = &entry[0..16]; + if type_guid == [0u8; 16] { + continue; + } + + if type_guid == LINUX_FS_GUID { + let first_lba = u64::from_le_bytes( + entry[32..40].try_into().unwrap_or([0; 8]), + ); + return first_lba * gpt_lba_size; + } + } + + 2 * crate::MIBI as u64 +} #[derive(Debug)] enum DevicePathRelation { @@ -126,17 +266,15 @@ }; if cfg!(feature = "live") { - // First try to get a live image from redox-live.iso. This is required to support netbooting. if let Some(buffer) = esp_live_image(esp_handle, esp_device_path.0) { + let partition_offset = if buffer.len() > 520 && &buffer[512..520] == b"EFI PART" { + gpt_find_redoxfs_offset_from_slice(&buffer) + } else { + 0 + }; return vec![DiskDevice { handle: esp_handle, - // Support both a copy of livedisk.iso and a standalone redoxfs partition - partition_offset: if &buffer[512..520] == b"EFI PART" { - //TODO: get block from partition table - 2 * crate::MIBI as u64 - } else { - 0 - }, + partition_offset, disk: DiskOrFileEfi::File(buffer), device_path: esp_device_path, file_path: Some("redox-live.iso"), @@ -154,7 +292,7 @@ }; let mut devices = Vec::with_capacity(handles.len()); for handle in handles { - let disk = match DiskEfi::handle_protocol(handle) { + let mut disk = match DiskEfi::handle_protocol(handle) { Ok(ok) => ok, Err(err) => { log::warn!( @@ -182,14 +320,14 @@ } }; + let partition_offset = if disk.0.Media.LogicalPartition { + 0 + } else { + gpt_find_redoxfs_offset(&mut disk) + }; devices.push(DiskDevice { handle, - partition_offset: if disk.0.Media.LogicalPartition { - 0 - } else { - //TODO: get block from partition table - 2 * crate::MIBI as u64 - }, + partition_offset, disk: DiskOrFileEfi::Disk(disk), device_path, file_path: None,