Add partition support for block device
This commit is contained in:
parent
d954e3d006
commit
9e2f5adf9b
|
|
@ -98,6 +98,8 @@ dependencies = [
|
|||
"aster-util",
|
||||
"bitvec",
|
||||
"component",
|
||||
"device-id",
|
||||
"id-alloc",
|
||||
"int-to-c-enum",
|
||||
"log",
|
||||
"ostd",
|
||||
|
|
@ -171,6 +173,7 @@ dependencies = [
|
|||
"bittle",
|
||||
"component",
|
||||
"ctr",
|
||||
"device-id",
|
||||
"hashbrown 0.14.5",
|
||||
"inherit-methods-macro",
|
||||
"lending-iterator",
|
||||
|
|
@ -351,6 +354,7 @@ dependencies = [
|
|||
"aster-util",
|
||||
"bitflags 1.3.2",
|
||||
"component",
|
||||
"device-id",
|
||||
"id-alloc",
|
||||
"int-to-c-enum",
|
||||
"log",
|
||||
|
|
@ -670,6 +674,9 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "device-id"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"aster-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
|
|
|
|||
2
Makefile
2
Makefile
|
|
@ -183,7 +183,6 @@ NON_OSDK_CRATES := \
|
|||
kernel/libs/aster-rights-proc \
|
||||
kernel/libs/atomic-integer-wrapper \
|
||||
kernel/libs/cpio-decoder \
|
||||
kernel/libs/device-id \
|
||||
kernel/libs/int-to-c-enum \
|
||||
kernel/libs/int-to-c-enum/derive \
|
||||
kernel/libs/jhash \
|
||||
|
|
@ -216,6 +215,7 @@ OSDK_CRATES := \
|
|||
kernel/comps/pci \
|
||||
kernel/libs/aster-util \
|
||||
kernel/libs/aster-bigtcp \
|
||||
kernel/libs/device-id \
|
||||
kernel/libs/xarray
|
||||
|
||||
# OSDK dependencies
|
||||
|
|
|
|||
|
|
@ -9,9 +9,11 @@ edition = "2021"
|
|||
spin = "0.9.4"
|
||||
ostd = { path = "../../../ostd" }
|
||||
align_ext = { path = "../../../ostd/libs/align_ext" }
|
||||
id-alloc = { path = "../../../ostd/libs/id-alloc" }
|
||||
int-to-c-enum = { path = "../../libs/int-to-c-enum" }
|
||||
aster-util = { path = "../../libs/aster-util" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
device-id = { path = "../../libs/device-id" }
|
||||
log = "0.4"
|
||||
bitvec = { version = "1.0.1", default-features = false, features = ["alloc"] }
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use core::sync::atomic::AtomicU64;
|
||||
|
||||
use align_ext::AlignExt;
|
||||
use aster_util::mem_obj_slice::Slice;
|
||||
use bitvec::array::BitArray;
|
||||
|
|
@ -49,6 +51,7 @@ impl Bio {
|
|||
let inner = Arc::new(BioInner {
|
||||
type_,
|
||||
sid_range: start_sid..start_sid + nsectors,
|
||||
sid_offset: AtomicU64::new(0),
|
||||
segments,
|
||||
complete_fn,
|
||||
status: AtomicU32::new(BioStatus::Init as u32),
|
||||
|
|
@ -258,6 +261,16 @@ impl SubmittedBio {
|
|||
self.0.sid_range()
|
||||
}
|
||||
|
||||
/// Returns the offset of the first sector id.
|
||||
pub fn sid_offset(&self) -> u64 {
|
||||
self.0.sid_offset.load(Ordering::Relaxed)
|
||||
}
|
||||
|
||||
/// Sets the offset of the first sector id.
|
||||
pub fn set_sid_offset(&self, offset: u64) {
|
||||
self.0.sid_offset.store(offset, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
/// Returns the slice to the memory segments.
|
||||
pub fn segments(&self) -> &[BioSegment] {
|
||||
self.0.segments()
|
||||
|
|
@ -294,8 +307,10 @@ impl SubmittedBio {
|
|||
struct BioInner {
|
||||
/// The type of the I/O
|
||||
type_: BioType,
|
||||
/// The range of the sector id on device
|
||||
/// The logical range of target sectors on device
|
||||
sid_range: Range<Sid>,
|
||||
/// The offset of the first sector id, used to adjust the `sid_range` for partition devices
|
||||
sid_offset: AtomicU64,
|
||||
/// The memory segments in this `Bio`
|
||||
segments: Vec<BioSegment>,
|
||||
/// The I/O completion method
|
||||
|
|
|
|||
|
|
@ -0,0 +1,117 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::collections::btree_set::BTreeSet;
|
||||
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use id_alloc::IdAlloc;
|
||||
use ostd::sync::Mutex;
|
||||
use spin::Once;
|
||||
|
||||
use crate::Error;
|
||||
|
||||
/// The maximum value of the major device ID of a block device.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/block/genhd.c#L239>.
|
||||
pub const MAX_MAJOR: u16 = 511;
|
||||
|
||||
/// Block devices that request a dynamic allocation of major ID will
|
||||
/// take numbers starting from 254 and downward.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/block/genhd.c#L224>.
|
||||
const LAST_DYNAMIC_MAJOR: u16 = 254;
|
||||
|
||||
static MAJORS: Mutex<BTreeSet<u16>> = Mutex::new(BTreeSet::new());
|
||||
|
||||
/// Acquires a major ID.
|
||||
///
|
||||
/// The returned `MajorIdOwner` object represents the ownership to the major ID.
|
||||
/// Until the object is dropped, this major ID cannot be acquired via `acquire_major` or `allocate_major` again.
|
||||
pub fn acquire_major(major: MajorId) -> Result<MajorIdOwner, Error> {
|
||||
if major.get() > MAX_MAJOR {
|
||||
return Err(Error::InvalidArgs);
|
||||
}
|
||||
|
||||
if MAJORS.lock().insert(major.get()) {
|
||||
Ok(MajorIdOwner(major))
|
||||
} else {
|
||||
Err(Error::IdAcquired)
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates a major ID.
|
||||
///
|
||||
/// The returned `MajorIdOwner` object represents the ownership to the major ID.
|
||||
/// Until the object is dropped, this major ID cannot be acquired via `acquire_major` or `allocate_major` again.
|
||||
pub fn allocate_major() -> Result<MajorIdOwner, Error> {
|
||||
let mut majors = MAJORS.lock();
|
||||
for id in (1..LAST_DYNAMIC_MAJOR + 1).rev() {
|
||||
if majors.insert(id) {
|
||||
return Ok(MajorIdOwner(MajorId::new(id)));
|
||||
}
|
||||
}
|
||||
|
||||
Err(Error::IdExhausted)
|
||||
}
|
||||
|
||||
/// An owned major ID.
|
||||
///
|
||||
/// Each instances of this type will unregister the major ID when dropped.
|
||||
pub struct MajorIdOwner(MajorId);
|
||||
|
||||
impl MajorIdOwner {
|
||||
/// Returns the major ID.
|
||||
pub fn get(&self) -> MajorId {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for MajorIdOwner {
|
||||
fn drop(&mut self) {
|
||||
MAJORS.lock().remove(&self.0.get());
|
||||
}
|
||||
}
|
||||
|
||||
/// The major ID used for extended partitions when the number of disk partitions exceeds the standard limit.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/block/partitions/core.c#L352>.
|
||||
const EXTENDED_MAJOR: u16 = 259;
|
||||
|
||||
/// An allocator for extended device IDs.
|
||||
pub struct ExtendedDeviceIdAllocator {
|
||||
major: MajorIdOwner,
|
||||
minor_allocator: Mutex<IdAlloc>,
|
||||
}
|
||||
|
||||
impl ExtendedDeviceIdAllocator {
|
||||
fn new() -> Self {
|
||||
let major = MajorId::new(EXTENDED_MAJOR);
|
||||
let minor_allocator = IdAlloc::with_capacity(MinorId::MAX.get() as usize + 1);
|
||||
|
||||
Self {
|
||||
major: acquire_major(major).unwrap(),
|
||||
minor_allocator: Mutex::new(minor_allocator),
|
||||
}
|
||||
}
|
||||
|
||||
/// Allocates an extended device ID.
|
||||
pub fn allocate(&self) -> DeviceId {
|
||||
let minor = self.minor_allocator.lock().alloc().unwrap() as u32;
|
||||
|
||||
DeviceId::new(self.major.get(), MinorId::new(minor))
|
||||
}
|
||||
|
||||
/// Releases an extended device ID.
|
||||
pub fn release(&mut self, id: DeviceId) {
|
||||
if id.major() != self.major.get() {
|
||||
return;
|
||||
}
|
||||
|
||||
self.minor_allocator.lock().free(id.minor().get() as usize);
|
||||
}
|
||||
}
|
||||
|
||||
pub static EXTENDED_DEVICE_ID_ALLOCATOR: Once<ExtendedDeviceIdAllocator> = Once::new();
|
||||
|
||||
pub(super) fn init() {
|
||||
EXTENDED_DEVICE_ID_ALLOCATOR.call_once(ExtendedDeviceIdAllocator::new);
|
||||
}
|
||||
|
|
@ -34,14 +34,18 @@
|
|||
extern crate alloc;
|
||||
|
||||
pub mod bio;
|
||||
mod device_id;
|
||||
pub mod id;
|
||||
mod impl_block_device;
|
||||
mod partition;
|
||||
mod prelude;
|
||||
pub mod request_queue;
|
||||
|
||||
use ::device_id::DeviceId;
|
||||
use component::{init_component, ComponentInitError};
|
||||
use ostd::sync::SpinLock;
|
||||
use spin::Once;
|
||||
pub use device_id::{acquire_major, allocate_major, MajorIdOwner, EXTENDED_DEVICE_ID_ALLOCATOR};
|
||||
use ostd::sync::Mutex;
|
||||
pub use partition::{PartitionInfo, PartitionNode};
|
||||
|
||||
use self::{
|
||||
bio::{BioEnqueueError, SubmittedBio},
|
||||
|
|
@ -57,10 +61,29 @@ pub trait BlockDevice: Send + Sync + Any + Debug {
|
|||
|
||||
/// Returns the metadata of the block device.
|
||||
fn metadata(&self) -> BlockDeviceMeta;
|
||||
|
||||
/// Returns the name of the block device.
|
||||
fn name(&self) -> &str;
|
||||
|
||||
/// Returns the device ID of the block device.
|
||||
fn id(&self) -> DeviceId;
|
||||
|
||||
/// Returns whether the block device is a partition.
|
||||
fn is_partition(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
/// Sets the partitions of the block device.
|
||||
fn set_partitions(&self, _infos: Vec<Option<PartitionInfo>>) {}
|
||||
|
||||
/// Returns the partitions of the block device.
|
||||
fn partitions(&self) -> Option<Vec<Arc<dyn BlockDevice>>> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Metadata for a block device.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct BlockDeviceMeta {
|
||||
/// The upper limit for the number of segments per bio.
|
||||
pub max_nr_segments_per_bio: usize,
|
||||
|
|
@ -75,51 +98,70 @@ impl dyn BlockDevice {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn register_device(name: String, device: Arc<dyn BlockDevice>) {
|
||||
COMPONENT
|
||||
.get()
|
||||
.unwrap()
|
||||
.block_device_table
|
||||
.lock()
|
||||
.insert(name, device);
|
||||
/// The error type which is returned from the APIs of this crate.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum Error {
|
||||
/// Device registered
|
||||
Registered,
|
||||
/// Device not found
|
||||
NotFound,
|
||||
/// Invalid arguments
|
||||
InvalidArgs,
|
||||
/// Id Acquired
|
||||
IdAcquired,
|
||||
/// Id Exhausted
|
||||
IdExhausted,
|
||||
}
|
||||
|
||||
pub fn get_device(str: &str) -> Option<Arc<dyn BlockDevice>> {
|
||||
COMPONENT
|
||||
.get()
|
||||
.unwrap()
|
||||
.block_device_table
|
||||
.lock()
|
||||
.get(str)
|
||||
.cloned()
|
||||
}
|
||||
/// Registers a new block device.
|
||||
pub fn register(device: Arc<dyn BlockDevice>) -> Result<(), Error> {
|
||||
let mut registry = DEVICE_REGISTRY.lock();
|
||||
let id = device.id().to_raw();
|
||||
if registry.contains_key(&id) {
|
||||
return Err(Error::Registered);
|
||||
}
|
||||
registry.insert(id, device);
|
||||
|
||||
pub fn all_devices() -> Vec<(String, Arc<dyn BlockDevice>)> {
|
||||
let block_devs = COMPONENT.get().unwrap().block_device_table.lock();
|
||||
block_devs
|
||||
.iter()
|
||||
.map(|(name, device)| (name.clone(), device.clone()))
|
||||
.collect()
|
||||
}
|
||||
|
||||
static COMPONENT: Once<Component> = Once::new();
|
||||
|
||||
#[init_component]
|
||||
fn component_init() -> Result<(), ComponentInitError> {
|
||||
let a = Component::init()?;
|
||||
COMPONENT.call_once(|| a);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Component {
|
||||
block_device_table: SpinLock<BTreeMap<String, Arc<dyn BlockDevice>>>,
|
||||
/// Unregisters an existing block device, returning the device if found.
|
||||
pub fn unregister(id: DeviceId) -> Result<Arc<dyn BlockDevice>, Error> {
|
||||
DEVICE_REGISTRY
|
||||
.lock()
|
||||
.remove(&id.to_raw())
|
||||
.ok_or(Error::NotFound)
|
||||
}
|
||||
|
||||
impl Component {
|
||||
pub fn init() -> Result<Self, ComponentInitError> {
|
||||
Ok(Self {
|
||||
block_device_table: SpinLock::new(BTreeMap::new()),
|
||||
})
|
||||
}
|
||||
/// Collects all block devices.
|
||||
pub fn collect_all() -> Vec<Arc<dyn BlockDevice>> {
|
||||
DEVICE_REGISTRY.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
/// Looks up a block device of a given device ID.
|
||||
pub fn lookup(id: DeviceId) -> Option<Arc<dyn BlockDevice>> {
|
||||
DEVICE_REGISTRY.lock().get(&id.to_raw()).cloned()
|
||||
}
|
||||
|
||||
static DEVICE_REGISTRY: Mutex<BTreeMap<u32, Arc<dyn BlockDevice>>> = Mutex::new(BTreeMap::new());
|
||||
|
||||
#[init_component]
|
||||
fn init() -> Result<(), ComponentInitError> {
|
||||
device_id::init();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[init_component(process)]
|
||||
fn init_in_first_process() -> Result<(), component::ComponentInitError> {
|
||||
let devices = collect_all();
|
||||
for device in devices {
|
||||
let Some(partition_info) = partition::parse(&device) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
device.set_partitions(partition_info);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,286 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use ostd::{mm::VmIo, Pod};
|
||||
|
||||
use crate::{
|
||||
bio::{BioEnqueueError, SubmittedBio},
|
||||
prelude::*,
|
||||
BlockDevice, BlockDeviceMeta, SECTOR_SIZE,
|
||||
};
|
||||
|
||||
/// Represents a partition entry.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum PartitionInfo {
|
||||
Mbr(MbrEntry),
|
||||
Gpt(GptEntry),
|
||||
}
|
||||
|
||||
impl PartitionInfo {
|
||||
pub fn start_sector(&self) -> u64 {
|
||||
match self {
|
||||
PartitionInfo::Mbr(entry) => entry.start_sector as u64,
|
||||
PartitionInfo::Gpt(entry) => entry.start_lba,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_sectors(&self) -> u64 {
|
||||
match self {
|
||||
PartitionInfo::Mbr(entry) => entry.total_sectors as u64,
|
||||
PartitionInfo::Gpt(entry) => entry.end_lba - entry.start_lba + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A MBR (Master Boot Record) partition table header.
|
||||
///
|
||||
/// See <https://wiki.osdev.org/MBR_(x86)#MBR_Format>.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct MbrHeader {
|
||||
bootstrap_code: [u8; 440],
|
||||
id: u32,
|
||||
reserved: u16,
|
||||
entries: [MbrEntry; 4],
|
||||
signature: u16,
|
||||
}
|
||||
|
||||
impl MbrHeader {
|
||||
fn check_signature(&self) -> bool {
|
||||
self.signature == 0xAA55
|
||||
}
|
||||
}
|
||||
|
||||
/// A MBR (Master Boot Record) partition entry.
|
||||
///
|
||||
/// See <https://wiki.osdev.org/Partition_Table>.
|
||||
#[repr(C, packed)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod)]
|
||||
pub struct MbrEntry {
|
||||
flag: u8,
|
||||
start_chs: ChsAddr,
|
||||
type_: u8,
|
||||
end_chs: ChsAddr,
|
||||
start_sector: u32,
|
||||
total_sectors: u32,
|
||||
}
|
||||
|
||||
impl MbrEntry {
|
||||
fn is_extended(&self) -> bool {
|
||||
self.type_ == 0x05 || self.type_ == 0x0F
|
||||
}
|
||||
|
||||
fn is_valid(&self) -> bool {
|
||||
// A System ID byte value of 0 is the definitive indicator for an unused entry.
|
||||
// Any other illegal value (CHS Sector = 0 or Total Sectors = 0) may also indicate an unused entry.
|
||||
self.type_ != 0x00
|
||||
&& self.start_chs.0[1] != 0
|
||||
&& self.end_chs.0[1] != 0
|
||||
&& self.total_sectors != 0
|
||||
}
|
||||
}
|
||||
|
||||
/// A CHS (Cylinder-Head-Sector) address.
|
||||
///
|
||||
/// In CHS addressing, sector numbers always start at 1; there is no sector 0.
|
||||
///
|
||||
/// The CHS address is stored as a 3-byte field:
|
||||
/// - Byte 0: Head number (8 bits)
|
||||
/// - Byte 1: Bits 0–5 are the sector number (6 bits, valid values 1–63);
|
||||
/// bits 6–7 are the upper two bits of the cylinder number
|
||||
/// - Byte 2: Lower 8 bits of the cylinder number (bits 0–7)
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod)]
|
||||
struct ChsAddr([u8; 3]);
|
||||
|
||||
/// A GPT (GUID Partition Table) header.
|
||||
///
|
||||
/// See <https://wiki.osdev.org/GPT#LBA_1:_Partition_Table_Header>.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, Pod)]
|
||||
struct GptHeader {
|
||||
signature: u64,
|
||||
revision: u32,
|
||||
size: u32,
|
||||
crc32: u32,
|
||||
reserved: u32,
|
||||
current_lba: u64,
|
||||
backup_lba: u64,
|
||||
first_usable_lba: u64,
|
||||
last_usable_lba: u64,
|
||||
guid: [u8; 16],
|
||||
partition_entry_lba: u64,
|
||||
nr_partition_entries: u32,
|
||||
size_of_partition_entry: u32,
|
||||
crc32_of_partition_entries: u32,
|
||||
_padding: [u8; 420],
|
||||
}
|
||||
|
||||
impl GptHeader {
|
||||
fn check_signature(&self) -> bool {
|
||||
&self.signature.to_le_bytes() == b"EFI PART"
|
||||
}
|
||||
}
|
||||
|
||||
/// A GPT (GUID Partition Table) partition entry.
|
||||
///
|
||||
/// See <https://wiki.osdev.org/GPT#LBA_2:_Partition_Entries>.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Pod)]
|
||||
pub struct GptEntry {
|
||||
// Unique ID that defines the purpose and type of this Partition.
|
||||
// A value of zero defines that this partition entry is not being used.
|
||||
type_guid: [u8; 16],
|
||||
// GUID that is unique for every partition entry.
|
||||
guid: [u8; 16],
|
||||
start_lba: u64,
|
||||
end_lba: u64,
|
||||
attributes: u64,
|
||||
// Null-terminated string containing a human-readable name of the partition.
|
||||
name: [u8; 72],
|
||||
}
|
||||
|
||||
impl GptEntry {
|
||||
fn is_valid(&self) -> bool {
|
||||
self.type_guid != [0; 16]
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn parse(device: &Arc<dyn BlockDevice>) -> Option<Vec<Option<PartitionInfo>>> {
|
||||
let mbr = device.read_val::<MbrHeader>(0).unwrap();
|
||||
|
||||
// 0xEE indicates a GPT Protective MBR, a fake partition covering the entire disk.
|
||||
let partitions = if mbr.check_signature() && mbr.entries[0].type_ != 0xEE {
|
||||
parse_mbr(device, &mbr)
|
||||
} else {
|
||||
parse_gpt(device)
|
||||
};
|
||||
|
||||
partitions.iter().any(|p| p.is_some()).then_some(partitions)
|
||||
}
|
||||
|
||||
fn parse_mbr(device: &Arc<dyn BlockDevice>, mbr: &MbrHeader) -> Vec<Option<PartitionInfo>> {
|
||||
let mut partitions = Vec::new();
|
||||
let mut extended_partition = None;
|
||||
for entry in mbr.entries {
|
||||
if entry.is_extended() {
|
||||
extended_partition = Some(entry.start_sector);
|
||||
}
|
||||
|
||||
if entry.is_valid() {
|
||||
partitions.push(Some(PartitionInfo::Mbr(entry)));
|
||||
} else {
|
||||
partitions.push(None);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(start_sector) = extended_partition {
|
||||
parse_ebr(device, &mut partitions, start_sector, 0);
|
||||
}
|
||||
|
||||
partitions
|
||||
}
|
||||
|
||||
fn parse_ebr(
|
||||
device: &Arc<dyn BlockDevice>,
|
||||
partitions: &mut Vec<Option<PartitionInfo>>,
|
||||
start_sector: u32,
|
||||
offset: u32,
|
||||
) {
|
||||
let ebr_sector = start_sector + offset;
|
||||
let mut ebr = device
|
||||
.read_val::<MbrHeader>(ebr_sector as usize * SECTOR_SIZE)
|
||||
.unwrap();
|
||||
if ebr.entries[0].is_valid() {
|
||||
ebr.entries[0].start_sector += ebr_sector;
|
||||
partitions.push(Some(PartitionInfo::Mbr(ebr.entries[0])));
|
||||
}
|
||||
|
||||
if ebr.entries[1].is_extended() {
|
||||
parse_ebr(
|
||||
device,
|
||||
partitions,
|
||||
start_sector,
|
||||
ebr.entries[1].start_sector,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_gpt(device: &Arc<dyn BlockDevice>) -> Vec<Option<PartitionInfo>> {
|
||||
let mut partitions = Vec::new();
|
||||
|
||||
// The primary GPT Header must be located in LBA 1.
|
||||
let gpt = device.read_val::<GptHeader>(SECTOR_SIZE).unwrap();
|
||||
|
||||
if !gpt.check_signature() {
|
||||
return partitions;
|
||||
}
|
||||
|
||||
// TODO: Check the CRC32 of the header and the partition entries, check the backup GPT header.
|
||||
|
||||
let entry_size = gpt.size_of_partition_entry as usize;
|
||||
let entries_per_sector = SECTOR_SIZE / entry_size;
|
||||
let total_sectors = gpt.nr_partition_entries as usize / entries_per_sector;
|
||||
for i in 0..total_sectors {
|
||||
let mut buf = [0u8; SECTOR_SIZE];
|
||||
let offset = (gpt.partition_entry_lba as usize + i) * SECTOR_SIZE;
|
||||
device.read_bytes(offset, buf.as_mut_slice()).unwrap();
|
||||
|
||||
for j in 0..entries_per_sector {
|
||||
let entry_offset = j * gpt.size_of_partition_entry as usize;
|
||||
let entry = GptEntry::from_bytes(&buf[entry_offset..entry_offset + entry_size]);
|
||||
if entry.is_valid() {
|
||||
partitions.push(Some(PartitionInfo::Gpt(entry)));
|
||||
} else {
|
||||
partitions.push(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
partitions
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PartitionNode {
|
||||
id: DeviceId,
|
||||
name: String,
|
||||
device: Arc<dyn BlockDevice>,
|
||||
info: PartitionInfo,
|
||||
}
|
||||
|
||||
impl BlockDevice for PartitionNode {
|
||||
fn enqueue(&self, bio: SubmittedBio) -> Result<(), BioEnqueueError> {
|
||||
bio.set_sid_offset(self.info.start_sector());
|
||||
self.device.enqueue(bio)
|
||||
}
|
||||
|
||||
fn metadata(&self) -> BlockDeviceMeta {
|
||||
let mut metadata = self.device.metadata();
|
||||
metadata.nr_sectors = self.info.total_sectors() as usize;
|
||||
metadata
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.id
|
||||
}
|
||||
}
|
||||
|
||||
impl PartitionNode {
|
||||
pub fn new(
|
||||
id: DeviceId,
|
||||
name: String,
|
||||
device: Arc<dyn BlockDevice>,
|
||||
info: PartitionInfo,
|
||||
) -> Self {
|
||||
Self {
|
||||
id,
|
||||
name,
|
||||
device,
|
||||
info,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -130,16 +130,24 @@ impl Debug for BioRequestSingleQueue {
|
|||
}
|
||||
}
|
||||
|
||||
/// The block I/O request.
|
||||
/// A block I/O request dequeued from [`BioRequestSingleQueue`].
|
||||
///
|
||||
/// The advantage of this data structure is to merge several `SubmittedBio`s that are
|
||||
/// contiguous on the target device's sector address, allowing them to be collectively
|
||||
/// processed in a queue.
|
||||
/// This `BioRequest` type is more friendly to storage medium than `SubmittedBio` for two reasons.
|
||||
///
|
||||
/// First, a `BioRequest` can represent a merged request over multiple `SubmittedBio`s
|
||||
/// that (1) are of the same request type and (2) are contiguous in terms of target sectors.
|
||||
/// This helps reduce the number of I/O requests submitted to the underlying storage medium.
|
||||
///
|
||||
/// Second, a `BioRequest` provides the physical sector addresses suitable for storage medium.
|
||||
/// The sector addresses returned from `SubmittedBio::sid_range()` are logical ones:
|
||||
/// they need to be adjusted with `SubmittedBio::sid_offset()` to calculate the physical ones.
|
||||
/// This calculation is handled internally by `BioRequest`.
|
||||
/// One can simply call `BioRequest::sid_range()` to obtain the physical sector addresses.
|
||||
#[derive(Debug)]
|
||||
pub struct BioRequest {
|
||||
/// The type of the I/O
|
||||
type_: BioType,
|
||||
/// The range of target sectors on the device
|
||||
/// The physical range of target sectors on the device
|
||||
sid_range: Range<Sid>,
|
||||
/// The number of segments
|
||||
num_segments: usize,
|
||||
|
|
@ -181,8 +189,10 @@ impl BioRequest {
|
|||
return false;
|
||||
}
|
||||
|
||||
rq_bio.sid_range().start == self.sid_range.end
|
||||
|| rq_bio.sid_range().end == self.sid_range.start
|
||||
let sid_offset = rq_bio.sid_offset();
|
||||
|
||||
rq_bio.sid_range().start + sid_offset == self.sid_range.end
|
||||
|| rq_bio.sid_range().end + sid_offset == self.sid_range.start
|
||||
}
|
||||
|
||||
/// Merges the `SubmittedBio` into this request.
|
||||
|
|
@ -196,12 +206,13 @@ impl BioRequest {
|
|||
assert!(self.can_merge(&rq_bio));
|
||||
|
||||
let rq_bio_nr_segments = rq_bio.segments().len();
|
||||
let sid_offset = rq_bio.sid_offset();
|
||||
|
||||
if rq_bio.sid_range().start == self.sid_range.end {
|
||||
self.sid_range.end = rq_bio.sid_range().end;
|
||||
if rq_bio.sid_range().start + sid_offset == self.sid_range.end {
|
||||
self.sid_range.end = rq_bio.sid_range().end + sid_offset;
|
||||
self.bios.push_back(rq_bio);
|
||||
} else {
|
||||
self.sid_range.start = rq_bio.sid_range().start;
|
||||
self.sid_range.start = rq_bio.sid_range().start + sid_offset;
|
||||
self.bios.push_front(rq_bio);
|
||||
}
|
||||
|
||||
|
|
@ -211,9 +222,13 @@ impl BioRequest {
|
|||
|
||||
impl From<SubmittedBio> for BioRequest {
|
||||
fn from(bio: SubmittedBio) -> Self {
|
||||
let mut sid_range = bio.sid_range().clone();
|
||||
sid_range.start = sid_range.start + bio.sid_offset();
|
||||
sid_range.end = sid_range.start + bio.sid_offset();
|
||||
|
||||
Self {
|
||||
type_: bio.type_(),
|
||||
sid_range: bio.sid_range().clone(),
|
||||
sid_range,
|
||||
num_segments: bio.segments().len(),
|
||||
bios: {
|
||||
let mut bios = VecDeque::with_capacity(1);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ inherit-methods-macro = {git = "https://github.com/asterinas/inherit-methods-mac
|
|||
ostd-pod = { git = "https://github.com/asterinas/ostd-pod", rev = "c4644be", version = "0.1.1" }
|
||||
component = { path = "../../libs/comp-sys/component" }
|
||||
aster-block = { path = "../block" }
|
||||
device-id = { path = "../../libs/device-id" }
|
||||
ostd = { path = "../../../ostd" }
|
||||
# Enable `force-soft` feature to disable `AES-NI` and `CLMUL` intrinsics, ensuring that the implementation
|
||||
# relies solely on software, and in the software implementation, unsafe code is rarely used.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ use core::{
|
|||
sync::atomic::{AtomicBool, Ordering},
|
||||
};
|
||||
|
||||
use device_id::DeviceId;
|
||||
use ostd::mm::{HasSize, VmIo};
|
||||
use ostd_pod::Pod;
|
||||
|
||||
|
|
@ -163,6 +164,14 @@ impl<D: BlockSet + 'static> aster_block::BlockDevice for MlsDisk<D> {
|
|||
nr_sectors: (BLOCK_SIZE / SECTOR_SIZE) * self.total_blocks(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: BlockSet + 'static> MlsDisk<D> {
|
||||
|
|
|
|||
|
|
@ -5,7 +5,8 @@
|
|||
#![feature(let_chains)]
|
||||
#![feature(negative_impls)]
|
||||
#![feature(slice_as_chunks)]
|
||||
#![expect(dead_code, unused_imports)]
|
||||
#![allow(unfulfilled_lint_expectations)]
|
||||
#![expect(dead_code, deprecated, unused_imports)]
|
||||
|
||||
mod error;
|
||||
mod layers;
|
||||
|
|
@ -25,6 +26,7 @@ use aster_block::{
|
|||
BlockDevice, SECTOR_SIZE,
|
||||
};
|
||||
use component::{init_component, ComponentInitError};
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use ostd::{
|
||||
mm::{io_util::HasVmReaderWriter, VmIo},
|
||||
prelude::*,
|
||||
|
|
@ -42,15 +44,16 @@ pub use self::{
|
|||
|
||||
#[init_component]
|
||||
fn init() -> core::result::Result<(), ComponentInitError> {
|
||||
// FIXME: add a virtio-blk-pci device in qemu and a image file.
|
||||
let Some(device) = aster_block::get_device("raw_mlsdisk") else {
|
||||
// FIXME: how to find a valid device used to format mlsdisk.
|
||||
let id = DeviceId::new(MajorId::new(255), MinorId::new(0));
|
||||
let Some(device) = aster_block::lookup(id) else {
|
||||
return Err(ComponentInitError::Unknown);
|
||||
};
|
||||
let raw_disk = RawDisk::new(device);
|
||||
let root_key = AeadKey::random();
|
||||
let device =
|
||||
MlsDisk::create(raw_disk, root_key, None).map_err(|_| ComponentInitError::Unknown)?;
|
||||
aster_block::register_device("mlsdisk".to_string(), Arc::new(device));
|
||||
let _ = aster_block::register(Arc::new(device));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -134,6 +137,7 @@ mod test {
|
|||
bio::{BioEnqueueError, BioStatus, BioType, SubmittedBio},
|
||||
BlockDevice, BlockDeviceMeta, SECTOR_SIZE,
|
||||
};
|
||||
use device_id::DeviceId;
|
||||
use ostd::{
|
||||
mm::{FrameAllocOptions, Segment, VmIo},
|
||||
prelude::*,
|
||||
|
|
@ -190,6 +194,14 @@ mod test {
|
|||
nr_sectors: self.blocks.size() / SECTOR_SIZE,
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
fn create_rawdisk(nblocks: usize) -> RawDisk {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ aster-bigtcp = { path = "../../libs/aster-bigtcp" }
|
|||
aster-pci = { path = "../pci" }
|
||||
aster-softirq = { path = "../softirq"}
|
||||
aster-systree = { path = "../systree" }
|
||||
device-id = { path = "../../libs/device-id" }
|
||||
id-alloc = { path = "../../../ostd/libs/id-alloc" }
|
||||
typeflags-util = { path = "../../libs/typeflags-util" }
|
||||
ostd = { path = "../../../ostd" }
|
||||
|
|
|
|||
|
|
@ -3,19 +3,23 @@
|
|||
use alloc::{
|
||||
boxed::Box,
|
||||
collections::BTreeMap,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec,
|
||||
format,
|
||||
string::String,
|
||||
sync::{Arc, Weak},
|
||||
vec::Vec,
|
||||
};
|
||||
use core::{fmt::Debug, hint::spin_loop};
|
||||
use core::{
|
||||
fmt::Debug,
|
||||
sync::atomic::{AtomicU32, Ordering},
|
||||
};
|
||||
|
||||
use aster_block::{
|
||||
bio::{bio_segment_pool_init, BioEnqueueError, BioStatus, BioType, SubmittedBio},
|
||||
request_queue::{BioRequest, BioRequestSingleQueue},
|
||||
BlockDeviceMeta,
|
||||
BlockDeviceMeta, PartitionInfo, PartitionNode, EXTENDED_DEVICE_ID_ALLOCATOR,
|
||||
};
|
||||
use aster_util::mem_obj_slice::Slice;
|
||||
use device_id::{DeviceId, MinorId};
|
||||
use id_alloc::IdAlloc;
|
||||
use log::{debug, info};
|
||||
use ostd::{
|
||||
|
|
@ -33,37 +37,76 @@ use crate::{
|
|||
},
|
||||
queue::VirtQueue,
|
||||
transport::{ConfigManager, VirtioTransport},
|
||||
VIRTIO_BLOCK_MAJOR_ID,
|
||||
};
|
||||
|
||||
/// The number of minor device numbers allocated for each virtio disk,
|
||||
/// including the whole disk and its partitions. If a disk has more than
|
||||
/// 16 partitions, then allocate a device ID via `EXTENDED_DEVICE_ID_ALLOCATOR`.
|
||||
const VIRTIO_DEVICE_MINORS: u32 = 16;
|
||||
|
||||
/// The number of virtio block devices, used to assign minor device numbers.
|
||||
static NR_BLOCK_DEVICE: AtomicU32 = AtomicU32::new(0);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct BlockDevice {
|
||||
device: Arc<DeviceInner>,
|
||||
/// The software staging queue.
|
||||
queue: BioRequestSingleQueue,
|
||||
id: DeviceId,
|
||||
name: String,
|
||||
partitions: SpinLock<Option<Vec<Arc<PartitionNode>>>>,
|
||||
weak_self: Weak<Self>,
|
||||
}
|
||||
|
||||
impl BlockDevice {
|
||||
/// Returns the formatted device name.
|
||||
///
|
||||
/// The device name starts at "vda". The 26th device is "vdz" and the 27th is "vdaa".
|
||||
/// The last one for two lettered suffix is "vdzz" which is followed by "vdaaa".
|
||||
fn formatted_device_name(mut index: u32) -> String {
|
||||
const VIRTIO_DISK_PREFIX: &str = "vd";
|
||||
|
||||
let mut suffix = Vec::new();
|
||||
loop {
|
||||
suffix.push((b'a' + (index % 26) as u8) as char);
|
||||
index /= 26;
|
||||
if index == 0 {
|
||||
break;
|
||||
}
|
||||
index -= 1;
|
||||
}
|
||||
suffix.reverse();
|
||||
let mut name = String::from(VIRTIO_DISK_PREFIX);
|
||||
name.extend(suffix);
|
||||
name
|
||||
}
|
||||
|
||||
/// Creates a new VirtIO-Block driver and registers it.
|
||||
pub(crate) fn init(transport: Box<dyn VirtioTransport>) -> Result<(), VirtioDeviceError> {
|
||||
let is_legacy = transport.is_legacy_version();
|
||||
let device = DeviceInner::init(transport)?;
|
||||
let device_id = if is_legacy {
|
||||
// FIXME: legacy device do not support `GetId` request.
|
||||
"legacy_blk".to_string()
|
||||
} else {
|
||||
device.request_device_id()
|
||||
};
|
||||
|
||||
let block_device = Arc::new(Self {
|
||||
let index = NR_BLOCK_DEVICE.fetch_add(1, Ordering::Relaxed);
|
||||
let id = DeviceId::new(
|
||||
VIRTIO_BLOCK_MAJOR_ID.get().unwrap().get(),
|
||||
MinorId::new(index * VIRTIO_DEVICE_MINORS),
|
||||
);
|
||||
let name = Self::formatted_device_name(index);
|
||||
|
||||
let block_device = Arc::new_cyclic(|weak_self| BlockDevice {
|
||||
device,
|
||||
// Each bio request includes an additional 1 request and 1 response descriptor,
|
||||
// therefore this upper bound is set to (QUEUE_SIZE - 2).
|
||||
queue: BioRequestSingleQueue::with_max_nr_segments_per_bio(
|
||||
(DeviceInner::QUEUE_SIZE - 2) as usize,
|
||||
),
|
||||
id,
|
||||
name,
|
||||
partitions: SpinLock::new(None),
|
||||
weak_self: weak_self.clone(),
|
||||
});
|
||||
|
||||
aster_block::register_device(device_id, block_device);
|
||||
aster_block::register(block_device).unwrap();
|
||||
|
||||
bio_segment_pool_init();
|
||||
Ok(())
|
||||
|
|
@ -101,6 +144,58 @@ impl aster_block::BlockDevice for BlockDevice {
|
|||
nr_sectors: self.device.config_manager.capacity_sectors(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
&self.name
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.id
|
||||
}
|
||||
|
||||
fn set_partitions(&self, infos: Vec<Option<PartitionInfo>>) {
|
||||
let mut partitions = self.partitions.lock();
|
||||
if let Some(old_partitions) = partitions.take() {
|
||||
for partition in old_partitions {
|
||||
let _ = aster_block::unregister(partition.id());
|
||||
}
|
||||
}
|
||||
|
||||
let mut new_partitions = Vec::new();
|
||||
for (index, info_opt) in infos.iter().enumerate() {
|
||||
let Some(info) = info_opt else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let index = index as u32 + 1;
|
||||
let id = if index < VIRTIO_DEVICE_MINORS {
|
||||
DeviceId::new(self.id.major(), MinorId::new(self.id.minor().get() + index))
|
||||
} else {
|
||||
EXTENDED_DEVICE_ID_ALLOCATOR.get().unwrap().allocate()
|
||||
};
|
||||
let name = format!("{}{}", self.name(), index);
|
||||
let device = self.weak_self.upgrade().unwrap();
|
||||
|
||||
let partition = Arc::new(PartitionNode::new(id, name, device, *info));
|
||||
new_partitions.push(partition);
|
||||
}
|
||||
|
||||
for partition in new_partitions.iter() {
|
||||
let _ = aster_block::register(partition.clone());
|
||||
}
|
||||
|
||||
*partitions = Some(new_partitions);
|
||||
}
|
||||
|
||||
fn partitions(&self) -> Option<Vec<Arc<dyn aster_block::BlockDevice>>> {
|
||||
let partitions = self.partitions.lock();
|
||||
let devices = partitions
|
||||
.as_ref()?
|
||||
.iter()
|
||||
.map(|p| p.clone() as Arc<dyn aster_block::BlockDevice>)
|
||||
.collect();
|
||||
Some(devices)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
|
@ -240,73 +335,6 @@ impl DeviceInner {
|
|||
info!("Virtio block device config space change");
|
||||
}
|
||||
|
||||
// TODO: Most logic is the same as read and write, there should be a refactor.
|
||||
// TODO: Should return an Err instead of panic if the device fails.
|
||||
fn request_device_id(&self) -> String {
|
||||
let id = self.id_allocator.disable_irq().lock().alloc().unwrap();
|
||||
let req_slice = {
|
||||
let req_slice = Slice::new(&self.block_requests, id * REQ_SIZE..(id + 1) * REQ_SIZE);
|
||||
let req = BlockReq {
|
||||
type_: ReqType::GetId as _,
|
||||
reserved: 0,
|
||||
sector: 0,
|
||||
};
|
||||
req_slice.write_val(0, &req).unwrap();
|
||||
req_slice.sync().unwrap();
|
||||
req_slice
|
||||
};
|
||||
|
||||
let resp_slice = {
|
||||
let resp_slice =
|
||||
Slice::new(&self.block_responses, id * RESP_SIZE..(id + 1) * RESP_SIZE);
|
||||
resp_slice.write_val(0, &BlockResp::default()).unwrap();
|
||||
resp_slice
|
||||
};
|
||||
const MAX_ID_LENGTH: usize = 20;
|
||||
let device_id_stream = {
|
||||
let segment = FrameAllocOptions::new()
|
||||
.zeroed(false)
|
||||
.alloc_segment(1)
|
||||
.unwrap();
|
||||
Arc::new(DmaStream::map(segment.into(), DmaDirection::FromDevice, false).unwrap())
|
||||
};
|
||||
let device_id_slice = Slice::new(&device_id_stream, 0..MAX_ID_LENGTH);
|
||||
let outputs = vec![&device_id_slice, &resp_slice];
|
||||
|
||||
let mut queue = self.queue.disable_irq().lock();
|
||||
let token = queue
|
||||
.add_dma_buf(&[&req_slice], outputs.as_slice())
|
||||
.expect("add queue failed");
|
||||
if queue.should_notify() {
|
||||
queue.notify();
|
||||
}
|
||||
while !queue.can_pop() {
|
||||
spin_loop();
|
||||
}
|
||||
queue.pop_used_with_token(token).expect("pop used failed");
|
||||
|
||||
resp_slice.sync().unwrap();
|
||||
self.id_allocator.disable_irq().lock().free(id);
|
||||
let resp: BlockResp = resp_slice.read_val(0).unwrap();
|
||||
match RespStatus::try_from(resp.status).unwrap() {
|
||||
RespStatus::Ok => {}
|
||||
_ => panic!("io error in block device"),
|
||||
};
|
||||
|
||||
let device_id = {
|
||||
device_id_slice.sync().unwrap();
|
||||
let mut device_id = vec![0u8; MAX_ID_LENGTH];
|
||||
let _ = device_id_slice.read_bytes(0, &mut device_id);
|
||||
let len = device_id
|
||||
.iter()
|
||||
.position(|&b| b == 0)
|
||||
.unwrap_or(MAX_ID_LENGTH);
|
||||
device_id.truncate(len);
|
||||
device_id
|
||||
};
|
||||
String::from_utf8(device_id).unwrap()
|
||||
}
|
||||
|
||||
/// Reads data from the device, this function is non-blocking.
|
||||
fn read(&self, bio_request: BioRequest) {
|
||||
let id = self.id_allocator.disable_irq().lock().alloc().unwrap();
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ extern crate alloc;
|
|||
use alloc::boxed::Box;
|
||||
use core::hint::spin_loop;
|
||||
|
||||
use aster_block::MajorIdOwner;
|
||||
use bitflags::bitflags;
|
||||
use component::{init_component, ComponentInitError};
|
||||
use device::{
|
||||
|
|
@ -22,6 +23,7 @@ use device::{
|
|||
VirtioDeviceType,
|
||||
};
|
||||
use log::{error, warn};
|
||||
use spin::Once;
|
||||
use transport::{mmio::VIRTIO_MMIO_DRIVER, pci::VIRTIO_PCI_DRIVER, DeviceStatus};
|
||||
|
||||
use crate::transport::VirtioTransport;
|
||||
|
|
@ -31,8 +33,12 @@ mod dma_buf;
|
|||
pub mod queue;
|
||||
mod transport;
|
||||
|
||||
static VIRTIO_BLOCK_MAJOR_ID: Once<MajorIdOwner> = Once::new();
|
||||
|
||||
#[init_component]
|
||||
fn virtio_component_init() -> Result<(), ComponentInitError> {
|
||||
VIRTIO_BLOCK_MAJOR_ID.call_once(|| aster_block::allocate_major().unwrap());
|
||||
|
||||
// Find all devices and register them to the corresponding crate
|
||||
transport::init();
|
||||
// For vsock table static init
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloc::{boxed::Box, collections::vec_deque::VecDeque, sync::Arc};
|
||||
|
||||
use aster_pci::{
|
||||
bus::{PciDevice, PciDriver},
|
||||
|
|
@ -17,17 +17,17 @@ use crate::transport::{
|
|||
|
||||
#[derive(Debug)]
|
||||
pub struct VirtioPciDriver {
|
||||
devices: SpinLock<Vec<Box<dyn VirtioTransport>>>,
|
||||
devices: SpinLock<VecDeque<Box<dyn VirtioTransport>>>,
|
||||
}
|
||||
|
||||
impl VirtioPciDriver {
|
||||
pub fn pop_device_transport(&self) -> Option<Box<dyn VirtioTransport>> {
|
||||
self.devices.lock().pop()
|
||||
self.devices.lock().pop_front()
|
||||
}
|
||||
|
||||
pub(super) fn new() -> Self {
|
||||
VirtioPciDriver {
|
||||
devices: SpinLock::new(Vec::new()),
|
||||
devices: SpinLock::new(VecDeque::new()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -66,7 +66,7 @@ impl PciDriver for VirtioPciDriver {
|
|||
}
|
||||
_ => return Err((BusProbeError::DeviceNotMatch, device)),
|
||||
};
|
||||
self.devices.lock().push(transport);
|
||||
self.devices.lock().push_back(transport);
|
||||
|
||||
Ok(Arc::new(VirtioPciDevice::new(device_id)))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
aster-util = { path = "../aster-util" }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
|
|
|||
|
|
@ -12,59 +12,92 @@
|
|||
#![no_std]
|
||||
#![deny(unsafe_code)]
|
||||
|
||||
/// A device ID, containing a major device number and a minor device number.
|
||||
use aster_util::ranged_integer::{RangedU16, RangedU32};
|
||||
|
||||
/// A device ID, embedding the major ID and minor ID.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
||||
pub struct DeviceId {
|
||||
major: u32,
|
||||
minor: u32,
|
||||
}
|
||||
pub struct DeviceId(u32);
|
||||
|
||||
impl DeviceId {
|
||||
/// Creates a device ID from the major device number and the minor device number.
|
||||
pub fn new(major: u32, minor: u32) -> Self {
|
||||
Self { major, minor }
|
||||
pub fn new(major: MajorId, minor: MinorId) -> Self {
|
||||
Self(((major.get() as u32) << 20) | minor.get())
|
||||
}
|
||||
|
||||
/// Returns the encoded `u32` value.
|
||||
pub fn to_raw(&self) -> u32 {
|
||||
self.0
|
||||
}
|
||||
|
||||
/// Returns the major device number.
|
||||
pub fn major(&self) -> u32 {
|
||||
self.major
|
||||
pub fn major(&self) -> MajorId {
|
||||
MajorId::new((self.0 >> 20) as u16)
|
||||
}
|
||||
|
||||
/// Returns the minor device number.
|
||||
pub fn minor(&self) -> u32 {
|
||||
self.minor
|
||||
pub fn minor(&self) -> MinorId {
|
||||
MinorId::new(self.0 & 0xf_ffff)
|
||||
}
|
||||
}
|
||||
|
||||
impl DeviceId {
|
||||
/// Creates a device ID from the encoded `u64` value.
|
||||
///
|
||||
/// See [`as_encoded_u64`] for details about how to encode a device ID to a `u64` value.
|
||||
///
|
||||
/// [`as_encoded_u64`]: Self::as_encoded_u64
|
||||
/// Panics if the major or minor device number is not falling in the valid range.
|
||||
pub fn from_encoded_u64(raw: u64) -> Self {
|
||||
let major = ((raw >> 32) & 0xffff_f000 | (raw >> 8) & 0x0000_0fff) as u32;
|
||||
let minor = ((raw >> 12) & 0xffff_ff00 | raw & 0x0000_00ff) as u32;
|
||||
Self::new(major, minor)
|
||||
let (major, minor) = decode_device_numbers(raw);
|
||||
Self::new(MajorId::new(major as u16), MinorId::new(minor))
|
||||
}
|
||||
|
||||
/// Encodes the device ID as a `u64` value.
|
||||
///
|
||||
/// The lower 32 bits use the same encoding strategy as Linux. See the Linux implementation at:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/kdev_t.h#L39-L44>.
|
||||
///
|
||||
/// If the major or minor device number is too large, the additional bits will be recorded
|
||||
/// using the higher 32 bits. Note that as of 2025, the Linux kernel still has no support for
|
||||
/// 64-bit device IDs:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/types.h#L18>.
|
||||
/// So this encoding follows the implementation in glibc:
|
||||
/// <https://github.com/bminor/glibc/blob/632d895f3e5d98162f77b9c3c1da4ec19968b671/bits/sysmacros.h#L26-L34>.
|
||||
pub fn as_encoded_u64(&self) -> u64 {
|
||||
let major = self.major() as u64;
|
||||
let minor = self.minor() as u64;
|
||||
((major & 0xffff_f000) << 32)
|
||||
| ((major & 0x0000_0fff) << 8)
|
||||
| ((minor & 0xffff_ff00) << 12)
|
||||
| (minor & 0x0000_00ff)
|
||||
encode_device_numbers(self.major().get() as u32, self.minor().get())
|
||||
}
|
||||
}
|
||||
|
||||
/// Decodes the major and minor numbers from the encoded `u64` value.
|
||||
///
|
||||
/// See [`DeviceId::as_encoded_u64`] for details about how to encode a device ID to a `u64` value.
|
||||
pub fn decode_device_numbers(raw: u64) -> (u32, u32) {
|
||||
let major = ((raw >> 32) & 0xffff_f000 | (raw >> 8) & 0x0000_0fff) as u32;
|
||||
let minor = ((raw >> 12) & 0xffff_ff00 | raw & 0x0000_00ff) as u32;
|
||||
(major, minor)
|
||||
}
|
||||
|
||||
/// Encodes the major and minor numbers as a `u64` value.
|
||||
///
|
||||
/// The lower 32 bits use the same encoding strategy as Linux. See the Linux implementation at:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/kdev_t.h#L39-L44>.
|
||||
///
|
||||
/// If the major or minor device number is too large, the additional bits will be recorded
|
||||
/// using the higher 32 bits. Note that as of 2025, the Linux kernel still has no support for
|
||||
/// 64-bit device IDs:
|
||||
/// <https://github.com/torvalds/linux/blob/0ff41df1cb268fc69e703a08a57ee14ae967d0ca/include/linux/types.h#L18>.
|
||||
/// So this encoding follows the implementation in glibc:
|
||||
/// <https://github.com/bminor/glibc/blob/632d895f3e5d98162f77b9c3c1da4ec19968b671/bits/sysmacros.h#L26-L34>.
|
||||
pub fn encode_device_numbers(major: u32, minor: u32) -> u64 {
|
||||
let major = major as u64;
|
||||
let minor = minor as u64;
|
||||
((major & 0xffff_f000) << 32)
|
||||
| ((major & 0x0000_0fff) << 8)
|
||||
| ((minor & 0xffff_ff00) << 12)
|
||||
| (minor & 0x0000_00ff)
|
||||
}
|
||||
|
||||
const MAX_MAJOR_ID: u16 = 0x0fff;
|
||||
|
||||
const MAX_MINOR_ID: u32 = 0x000f_ffff;
|
||||
|
||||
/// The major component of a device ID.
|
||||
///
|
||||
/// A major ID is a non-zero, 12-bit integer, thus falling in the range of `1..(1u16 << 12)`.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/include/linux/kdev_t.h#L10>.
|
||||
pub type MajorId = RangedU16<1, MAX_MAJOR_ID>;
|
||||
|
||||
/// The minor component of a device ID.
|
||||
///
|
||||
/// A minor ID is a 20-bit integer, thus falling in the range of `0..(1u32 << 20)`.
|
||||
///
|
||||
/// Reference: <https://elixir.bootlin.com/linux/v6.13/source/include/linux/kdev_t.h#L11>.
|
||||
pub type MinorId = RangedU32<0, MAX_MINOR_ID>;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,112 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use aster_block::BlockDevice;
|
||||
use aster_virtio::device::block::device::BlockDevice as VirtIoBlockDevice;
|
||||
use device_id::DeviceId;
|
||||
use ostd::mm::VmIo;
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
fs::{
|
||||
device::{add_node, Device, DeviceType},
|
||||
fs_resolver::FsResolver,
|
||||
inode_handle::FileIo,
|
||||
utils::{InodeIo, StatusFlags},
|
||||
},
|
||||
prelude::*,
|
||||
process::signal::{PollHandle, Pollable},
|
||||
thread::kernel_thread::ThreadOptions,
|
||||
};
|
||||
|
||||
pub(super) fn init_in_first_kthread() {
|
||||
for device in aster_block::collect_all() {
|
||||
if device.is_partition() {
|
||||
continue;
|
||||
}
|
||||
|
||||
let task_fn = move || {
|
||||
info!("spawn the virt-io-block thread");
|
||||
let virtio_block_device = device.downcast_ref::<VirtIoBlockDevice>().unwrap();
|
||||
loop {
|
||||
virtio_block_device.handle_requests();
|
||||
}
|
||||
};
|
||||
ThreadOptions::new(task_fn).spawn();
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn init_in_first_process(fs_resolver: &FsResolver) -> Result<()> {
|
||||
for device in aster_block::collect_all() {
|
||||
let name = device.name().to_string();
|
||||
let device = Arc::new(BlockFile::new(device));
|
||||
add_node(device, &name, fs_resolver)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct BlockFile(Arc<dyn BlockDevice>);
|
||||
|
||||
impl BlockFile {
|
||||
fn new(device: Arc<dyn BlockDevice>) -> Self {
|
||||
Self(device)
|
||||
}
|
||||
}
|
||||
|
||||
impl InodeIo for BlockFile {
|
||||
fn read_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
writer: &mut VmWriter,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let total = writer.avail();
|
||||
self.0.read(offset, writer)?;
|
||||
let avail = writer.avail();
|
||||
Ok(total - avail)
|
||||
}
|
||||
|
||||
fn write_at(
|
||||
&self,
|
||||
offset: usize,
|
||||
reader: &mut VmReader,
|
||||
_status_flags: StatusFlags,
|
||||
) -> Result<usize> {
|
||||
let total = reader.remain();
|
||||
self.0.write(offset, reader)?;
|
||||
let remain = reader.remain();
|
||||
Ok(total - remain)
|
||||
}
|
||||
}
|
||||
|
||||
impl Pollable for BlockFile {
|
||||
fn poll(&self, mask: IoEvents, _: Option<&mut PollHandle>) -> IoEvents {
|
||||
let events = IoEvents::IN | IoEvents::OUT;
|
||||
events & mask
|
||||
}
|
||||
}
|
||||
|
||||
impl FileIo for BlockFile {
|
||||
fn check_seekable(&self) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn is_offset_aware(&self) -> bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
impl Device for BlockFile {
|
||||
fn type_(&self) -> DeviceType {
|
||||
DeviceType::Block
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
self.0.id()
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
Ok(Box::new(BlockFile(self.0.clone())))
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
|
|
@ -22,7 +22,7 @@ impl Device for Full {
|
|||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 7)
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(7))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
mod disk;
|
||||
mod full;
|
||||
mod null;
|
||||
mod pty;
|
||||
|
|
@ -29,6 +30,10 @@ use crate::{
|
|||
prelude::*,
|
||||
};
|
||||
|
||||
pub fn init_in_first_kthread() {
|
||||
disk::init_in_first_kthread();
|
||||
}
|
||||
|
||||
/// Init the device node in fs, must be called after mounting rootfs.
|
||||
pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
||||
let fs = ctx.thread_local.borrow_fs();
|
||||
|
|
@ -74,6 +79,8 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
|||
|
||||
shm::init_in_first_process(&fs_resolver, ctx)?;
|
||||
|
||||
disk::init_in_first_process(&fs_resolver)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
@ -82,8 +89,8 @@ pub fn init_in_first_process(ctx: &Context) -> Result<()> {
|
|||
// a registration mechanism should be used to allow each driver to
|
||||
// allocate device IDs either statically or dynamically.
|
||||
pub fn get_device(devid: DeviceId) -> Result<Arc<dyn Device>> {
|
||||
let major = devid.major();
|
||||
let minor = devid.minor();
|
||||
let major = devid.major().get();
|
||||
let minor = devid.minor().get();
|
||||
|
||||
match (major, minor) {
|
||||
(1, 3) => Ok(Arc::new(null::Null)),
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
|
|
@ -22,7 +22,7 @@ impl Device for Null {
|
|||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 3)
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(3))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use super::Urandom;
|
||||
use crate::{
|
||||
|
|
@ -30,7 +30,7 @@ impl Device for Random {
|
|||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 8)
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(8))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ use core::{
|
|||
};
|
||||
|
||||
use aster_util::{field_ptr, safe_ptr::SafePtr};
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use ostd::{
|
||||
const_assert,
|
||||
mm::{
|
||||
|
|
@ -40,7 +40,7 @@ impl Device for TdxGuest {
|
|||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(0xa, 0x7b)
|
||||
DeviceId::new(MajorId::new(0xa), MinorId::new(0x7b))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
|
|
@ -31,6 +31,6 @@ impl Device for TtyDevice {
|
|||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(5, 0)
|
||||
DeviceId::new(MajorId::new(5), MinorId::new(0))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ use aster_console::{
|
|||
mode::{ConsoleMode, KeyboardMode},
|
||||
AnyConsoleDevice,
|
||||
};
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
use ostd::sync::LocalIrqDisabled;
|
||||
|
||||
use self::{line_discipline::LineDiscipline, termio::CFontOp};
|
||||
|
|
@ -401,7 +401,10 @@ impl<D: TtyDriver> Device for Tty<D> {
|
|||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(D::DEVICE_MAJOR_ID, self.index)
|
||||
DeviceId::new(
|
||||
MajorId::new(D::DEVICE_MAJOR_ID as u16),
|
||||
MinorId::new(self.index),
|
||||
)
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
|
|
@ -47,7 +47,7 @@ impl Device for Urandom {
|
|||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 9)
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(9))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use crate::{
|
||||
events::IoEvents,
|
||||
|
|
@ -22,7 +22,7 @@ impl Device for Zero {
|
|||
|
||||
fn id(&self) -> DeviceId {
|
||||
// The same value as Linux
|
||||
DeviceId::new(1, 5)
|
||||
DeviceId::new(MajorId::new(1), MinorId::new(5))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -37,7 +37,6 @@ impl Debug for dyn Device {
|
|||
#[derive(Debug)]
|
||||
pub enum DeviceType {
|
||||
Char,
|
||||
#[expect(dead_code)]
|
||||
Block,
|
||||
#[expect(dead_code)]
|
||||
Misc,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
use device_id::{DeviceId, MajorId, MinorId};
|
||||
|
||||
use super::*;
|
||||
use crate::fs::{
|
||||
|
|
@ -9,7 +9,7 @@ use crate::fs::{
|
|||
};
|
||||
|
||||
/// Same major number with Linux.
|
||||
const PTMX_MAJOR_NUM: u32 = 5;
|
||||
const PTMX_MAJOR_NUM: u16 = 5;
|
||||
/// Same minor number with Linux.
|
||||
const PTMX_MINOR_NUM: u32 = 2;
|
||||
|
||||
|
|
@ -162,7 +162,7 @@ impl Device for Inner {
|
|||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
DeviceId::new(PTMX_MAJOR_NUM, PTMX_MINOR_NUM)
|
||||
DeviceId::new(MajorId::new(PTMX_MAJOR_NUM), MinorId::new(PTMX_MINOR_NUM))
|
||||
}
|
||||
|
||||
fn open(&self) -> Result<Box<dyn FileIo>> {
|
||||
|
|
|
|||
|
|
@ -10,8 +10,6 @@ mod super_block;
|
|||
mod upcase_table;
|
||||
mod utils;
|
||||
|
||||
pub use fs::{ExfatFs, ExfatMountOptions};
|
||||
|
||||
use crate::fs::exfat::fs::ExfatType;
|
||||
|
||||
pub(super) fn init() {
|
||||
|
|
@ -26,18 +24,17 @@ mod test {
|
|||
bio::{BioEnqueueError, BioStatus, BioType, SubmittedBio},
|
||||
BlockDevice, BlockDeviceMeta,
|
||||
};
|
||||
use device_id::DeviceId;
|
||||
use ostd::{
|
||||
mm::{io_util::HasVmReaderWriter, FrameAllocOptions, Segment, VmIo, PAGE_SIZE},
|
||||
prelude::*,
|
||||
};
|
||||
use rand::{rngs::SmallRng, RngCore, SeedableRng};
|
||||
|
||||
use super::fs::{ExfatFs, ExfatMountOptions};
|
||||
use crate::{
|
||||
fs::{
|
||||
exfat::{
|
||||
constants::{EXFAT_RESERVED_CLUSTERS, MAX_NAME_LENGTH},
|
||||
ExfatFs, ExfatMountOptions,
|
||||
},
|
||||
exfat::constants::{EXFAT_RESERVED_CLUSTERS, MAX_NAME_LENGTH},
|
||||
utils::{generate_random_operation, new_fs_in_memory, Inode, InodeMode, InodeType},
|
||||
},
|
||||
prelude::*,
|
||||
|
|
@ -111,6 +108,14 @@ mod test {
|
|||
nr_sectors: self.sectors_count(),
|
||||
}
|
||||
}
|
||||
|
||||
fn name(&self) -> &str {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn id(&self) -> DeviceId {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
/// Exfat disk image
|
||||
static EXFAT_IMAGE: &[u8] = include_bytes!("../../../../test/build/exfat.img");
|
||||
|
|
|
|||
|
|
@ -24,38 +24,15 @@ pub mod thread_info;
|
|||
pub mod tmpfs;
|
||||
pub mod utils;
|
||||
|
||||
use aster_block::BlockDevice;
|
||||
use aster_virtio::device::block::device::BlockDevice as VirtIoBlockDevice;
|
||||
|
||||
use crate::{
|
||||
fs::{
|
||||
exfat::{ExfatFs, ExfatMountOptions},
|
||||
ext2::Ext2,
|
||||
file_table::FdFlags,
|
||||
fs_resolver::{FsPath, FsResolver},
|
||||
utils::{mkmod, AccessMode, OpenArgs},
|
||||
},
|
||||
prelude::*,
|
||||
thread::kernel_thread::ThreadOptions,
|
||||
};
|
||||
|
||||
fn start_block_device(device_name: &str) -> Result<Arc<dyn BlockDevice>> {
|
||||
if let Some(device) = aster_block::get_device(device_name) {
|
||||
let cloned_device = device.clone();
|
||||
let task_fn = move || {
|
||||
info!("spawn the virt-io-block thread");
|
||||
let virtio_block_device = cloned_device.downcast_ref::<VirtIoBlockDevice>().unwrap();
|
||||
loop {
|
||||
virtio_block_device.handle_requests();
|
||||
}
|
||||
};
|
||||
ThreadOptions::new(task_fn).spawn();
|
||||
Ok(device)
|
||||
} else {
|
||||
return_errno_with_message!(Errno::ENOENT, "Device does not exist")
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
registry::init();
|
||||
|
||||
|
|
@ -84,27 +61,9 @@ pub fn init_in_first_kthread(fs_resolver: &FsResolver) {
|
|||
}
|
||||
|
||||
pub fn init_in_first_process(ctx: &Context) {
|
||||
//The device name is specified in qemu args as --serial={device_name}
|
||||
let ext2_device_name = "vext2";
|
||||
let exfat_device_name = "vexfat";
|
||||
|
||||
let fs = ctx.thread_local.borrow_fs();
|
||||
let fs_resolver = fs.resolver().read();
|
||||
|
||||
if let Ok(block_device_ext2) = start_block_device(ext2_device_name) {
|
||||
let ext2_fs = Ext2::open(block_device_ext2).unwrap();
|
||||
let target_path = FsPath::try_from("/ext2").unwrap();
|
||||
println!("[kernel] Mount Ext2 fs at {:?} ", target_path);
|
||||
self::rootfs::mount_fs_at(ext2_fs, &target_path, &fs_resolver, ctx).unwrap();
|
||||
}
|
||||
|
||||
if let Ok(block_device_exfat) = start_block_device(exfat_device_name) {
|
||||
let exfat_fs = ExfatFs::open(block_device_exfat, ExfatMountOptions::default()).unwrap();
|
||||
let target_path = FsPath::try_from("/exfat").unwrap();
|
||||
println!("[kernel] Mount ExFat fs at {:?} ", target_path);
|
||||
self::rootfs::mount_fs_at(exfat_fs, &target_path, &fs_resolver, ctx).unwrap();
|
||||
}
|
||||
|
||||
// Initialize the file table for the first process.
|
||||
let tty_path = FsPath::try_from("/dev/console").unwrap();
|
||||
let stdin = {
|
||||
|
|
|
|||
|
|
@ -8,12 +8,9 @@ use ostd::boot::boot_info;
|
|||
|
||||
use super::{
|
||||
fs_resolver::{FsPath, FsResolver},
|
||||
utils::{FileSystem, InodeMode, InodeType},
|
||||
};
|
||||
use crate::{
|
||||
fs::path::{is_dot, PerMountFlags},
|
||||
prelude::*,
|
||||
utils::{InodeMode, InodeType},
|
||||
};
|
||||
use crate::{fs::path::is_dot, prelude::*};
|
||||
|
||||
struct BoxedReader<'a>(Box<dyn Read + 'a>);
|
||||
|
||||
|
|
@ -109,14 +106,3 @@ pub fn init_in_first_kthread(fs_resolver: &FsResolver) -> Result<()> {
|
|||
println!("[kernel] rootfs is ready");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn mount_fs_at(
|
||||
fs: Arc<dyn FileSystem>,
|
||||
fs_path: &FsPath,
|
||||
fs_resolver: &FsResolver,
|
||||
ctx: &Context,
|
||||
) -> Result<()> {
|
||||
let target_path = fs_resolver.lookup(fs_path)?;
|
||||
target_path.mount(fs, PerMountFlags::default(), ctx)?;
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,6 @@ impl InodeType {
|
|||
*self == InodeType::Dir
|
||||
}
|
||||
|
||||
#[expect(dead_code)]
|
||||
pub fn is_device(&self) -> bool {
|
||||
*self == InodeType::BlockDevice || *self == InodeType::CharDevice
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,6 +162,7 @@ fn init_in_first_kthread(fs_resolver: &FsResolver) {
|
|||
// Work queue should be initialized before interrupt is enabled,
|
||||
// in case any irq handler uses work queue as bottom half
|
||||
crate::thread::work_queue::init_in_first_kthread();
|
||||
crate::device::init_in_first_kthread();
|
||||
crate::net::init_in_first_kthread();
|
||||
crate::fs::init_in_first_kthread(fs_resolver);
|
||||
crate::ipc::init_in_first_kthread();
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
use device_id::DeviceId;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
fs::{
|
||||
|
|
@ -227,10 +229,25 @@ fn get_fs(
|
|||
|
||||
let disk = if fs_type.properties().contains(FsProperties::NEED_DISK) {
|
||||
let devname = user_space.read_cstring(src_name_addr, MAX_FILENAME_LEN)?;
|
||||
Some(
|
||||
aster_block::get_device(devname.to_str().unwrap())
|
||||
.ok_or(Error::with_message(Errno::ENOENT, "device does not exist"))?,
|
||||
)
|
||||
let path = devname.to_string_lossy();
|
||||
let fs_path = FsPath::from_fd_and_path(AT_FDCWD, path.as_ref())?;
|
||||
let path = ctx
|
||||
.thread_local
|
||||
.borrow_fs()
|
||||
.resolver()
|
||||
.read()
|
||||
.lookup_no_follow(&fs_path)?;
|
||||
if !path.type_().is_device() {
|
||||
return_errno_with_message!(Errno::ENODEV, "the path is not a device file");
|
||||
}
|
||||
|
||||
let id = DeviceId::from_encoded_u64(path.metadata().rdev);
|
||||
let device = aster_block::lookup(id);
|
||||
if device.is_none() {
|
||||
return_errno_with_message!(Errno::ENODEV, "the device is not found");
|
||||
}
|
||||
|
||||
device
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
use core::time::Duration;
|
||||
|
||||
use device_id::DeviceId;
|
||||
|
||||
use super::SyscallReturn;
|
||||
use crate::{
|
||||
fs::{file_table::FileDesc, fs_resolver::FsPath, utils::Metadata},
|
||||
|
|
@ -123,8 +121,8 @@ pub struct Statx {
|
|||
|
||||
impl From<Metadata> for Statx {
|
||||
fn from(info: Metadata) -> Self {
|
||||
let devid = DeviceId::from_encoded_u64(info.dev);
|
||||
let rdevid = DeviceId::from_encoded_u64(info.rdev);
|
||||
let (stx_dev_major, stx_dev_minor) = device_id::decode_device_numbers(info.dev);
|
||||
let (stx_rdev_major, stx_rdev_minor) = device_id::decode_device_numbers(info.rdev);
|
||||
|
||||
// FIXME: We assume it is always not mount_root.
|
||||
let stx_attributes = 0;
|
||||
|
|
@ -162,10 +160,10 @@ impl From<Metadata> for Statx {
|
|||
stx_btime: StatxTimestamp::from(info.atime),
|
||||
stx_ctime: StatxTimestamp::from(info.ctime),
|
||||
stx_mtime: StatxTimestamp::from(info.ctime),
|
||||
stx_rdev_major: rdevid.major(),
|
||||
stx_rdev_minor: rdevid.minor(),
|
||||
stx_dev_major: devid.major(),
|
||||
stx_dev_minor: devid.minor(),
|
||||
stx_rdev_major,
|
||||
stx_rdev_minor,
|
||||
stx_dev_major,
|
||||
stx_dev_minor,
|
||||
stx_mnt_id: 0,
|
||||
stx_dio_mem_align: 0,
|
||||
stx_dio_offset_align: 0,
|
||||
|
|
|
|||
|
|
@ -7,4 +7,6 @@
|
|||
mount -t sysfs none /sys
|
||||
mount -t proc none /proc
|
||||
mount -t cgroup2 none /sys/fs/cgroup
|
||||
mount -t configfs none /sys/kernel/config
|
||||
mount -t configfs none /sys/kernel/config
|
||||
mount -t ext2 /dev/vda /ext2
|
||||
mount -t exfat /dev/vdb /exfat
|
||||
Loading…
Reference in New Issue