mirror of
https://github.com/sgmarz/osblog.git
synced 2024-11-24 02:16:19 +04:00
Added comments.
This commit is contained in:
parent
cb06702afa
commit
cbff6afeca
@ -5,9 +5,16 @@
|
||||
|
||||
use crate::{kmem::{kfree, kmalloc, talloc, tfree},
|
||||
page::{zalloc, PAGE_SIZE},
|
||||
process::{add_kernel_process_args, set_running, set_waiting, get_by_pid},
|
||||
process::{add_kernel_process_args,
|
||||
get_by_pid,
|
||||
set_running,
|
||||
set_waiting},
|
||||
virtio,
|
||||
virtio::{Descriptor, MmioOffsets, Queue, StatusField, VIRTIO_RING_SIZE}};
|
||||
virtio::{Descriptor,
|
||||
MmioOffsets,
|
||||
Queue,
|
||||
StatusField,
|
||||
VIRTIO_RING_SIZE}};
|
||||
use core::mem::size_of;
|
||||
|
||||
#[repr(C)]
|
||||
@ -139,7 +146,8 @@ pub enum BlockErrors {
|
||||
// value type to signal that the variable exists, but not the
|
||||
// queue itself. We will replace this with an actual queue when
|
||||
// we initialize the block system.
|
||||
static mut BLOCK_DEVICES: [Option<BlockDevice>; 8] = [None, None, None, None, None, None, None, None];
|
||||
static mut BLOCK_DEVICES: [Option<BlockDevice>; 8] =
|
||||
[None, None, None, None, None, None, None, None];
|
||||
|
||||
pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
unsafe {
|
||||
@ -148,44 +156,55 @@ pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
// 0x1000_2000 is index 1
|
||||
// ...
|
||||
// 0x1000_8000 is index 7
|
||||
// To get the number that changes over, we shift right 12 places (3 hex digits)
|
||||
// To get the number that changes over, we shift right 12 places
|
||||
// (3 hex digits)
|
||||
let idx = (ptr as usize - virtio::MMIO_VIRTIO_START) >> 12;
|
||||
// [Driver] Device Initialization
|
||||
// 1. Reset the device (write 0 into status)
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(0);
|
||||
let mut status_bits = StatusField::Acknowledge.val32();
|
||||
// 2. Set ACKNOWLEDGE status bit
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
|
||||
ptr.add(MmioOffsets::Status.scale32())
|
||||
.write_volatile(status_bits);
|
||||
// 3. Set the DRIVER status bit
|
||||
status_bits |= StatusField::DriverOk.val32();
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
|
||||
ptr.add(MmioOffsets::Status.scale32())
|
||||
.write_volatile(status_bits);
|
||||
// 4. Read device feature bits, write subset of feature
|
||||
// bits understood by OS and driver to the device.
|
||||
let host_features = ptr.add(MmioOffsets::HostFeatures.scale32()).read_volatile();
|
||||
let host_features =
|
||||
ptr.add(MmioOffsets::HostFeatures.scale32())
|
||||
.read_volatile();
|
||||
let guest_features = host_features & !(1 << VIRTIO_BLK_F_RO);
|
||||
let ro = host_features & (1 << VIRTIO_BLK_F_RO) != 0;
|
||||
ptr.add(MmioOffsets::GuestFeatures.scale32()).write_volatile(guest_features);
|
||||
ptr.add(MmioOffsets::GuestFeatures.scale32())
|
||||
.write_volatile(guest_features);
|
||||
// 5. Set the FEATURES_OK status bit
|
||||
status_bits |= StatusField::FeaturesOk.val32();
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
|
||||
ptr.add(MmioOffsets::Status.scale32())
|
||||
.write_volatile(status_bits);
|
||||
// 6. Re-read status to ensure FEATURES_OK is still set.
|
||||
// Otherwise, it doesn't support our features.
|
||||
let status_ok = ptr.add(MmioOffsets::Status.scale32()).read_volatile();
|
||||
let status_ok =
|
||||
ptr.add(MmioOffsets::Status.scale32()).read_volatile();
|
||||
// If the status field no longer has features_ok set,
|
||||
// that means that the device couldn't accept
|
||||
// the features that we request. Therefore, this is
|
||||
// considered a "failed" state.
|
||||
if false == StatusField::features_ok(status_ok) {
|
||||
print!("features fail...");
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(StatusField::Failed.val32());
|
||||
ptr.add(MmioOffsets::Status.scale32())
|
||||
.write_volatile(StatusField::Failed.val32());
|
||||
return false;
|
||||
}
|
||||
// 7. Perform device-specific setup.
|
||||
// Set the queue num. We have to make sure that the
|
||||
// queue size is valid because the device can only take
|
||||
// a certain size.
|
||||
let qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32()).read_volatile();
|
||||
ptr.add(MmioOffsets::QueueNum.scale32()).write_volatile(VIRTIO_RING_SIZE as u32);
|
||||
let qnmax = ptr.add(MmioOffsets::QueueNumMax.scale32())
|
||||
.read_volatile();
|
||||
ptr.add(MmioOffsets::QueueNum.scale32())
|
||||
.write_volatile(VIRTIO_RING_SIZE as u32);
|
||||
if VIRTIO_RING_SIZE as u32 > qnmax {
|
||||
print!("queue size fail...");
|
||||
return false;
|
||||
@ -195,7 +214,8 @@ pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
// divide to truncate the decimal. We don't add 4096,
|
||||
// because if it is exactly 4096 bytes, we would get two
|
||||
// pages, not one.
|
||||
let num_pages = (size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
let num_pages =
|
||||
(size_of::<Queue>() + PAGE_SIZE - 1) / PAGE_SIZE;
|
||||
// println!("np = {}", num_pages);
|
||||
// We allocate a page for each device. This will the the
|
||||
// descriptor where we can communicate with the block
|
||||
@ -208,19 +228,21 @@ pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
// what is called a memory "fence" or barrier.
|
||||
ptr.add(MmioOffsets::QueueSel.scale32()).write_volatile(0);
|
||||
// Alignment is very important here. This is the memory address
|
||||
// alignment between the available and used rings. If this is wrong,
|
||||
// then we and the device will refer to different memory addresses
|
||||
// and hence get the wrong data in the used ring.
|
||||
// alignment between the available and used rings. If this is
|
||||
// wrong, then we and the device will refer to different memory
|
||||
// addresses and hence get the wrong data in the used ring.
|
||||
// ptr.add(MmioOffsets::QueueAlign.scale32()).write_volatile(2);
|
||||
let queue_ptr = zalloc(num_pages) as *mut Queue;
|
||||
let queue_pfn = queue_ptr as u32;
|
||||
ptr.add(MmioOffsets::GuestPageSize.scale32()).write_volatile(PAGE_SIZE as u32);
|
||||
ptr.add(MmioOffsets::GuestPageSize.scale32())
|
||||
.write_volatile(PAGE_SIZE as u32);
|
||||
// QueuePFN is a physical page number, however it
|
||||
// appears for QEMU we have to write the entire memory
|
||||
// address. This is a physical memory address where we
|
||||
// (the OS) and the block device have in common for
|
||||
// making and receiving requests.
|
||||
ptr.add(MmioOffsets::QueuePfn.scale32()).write_volatile(queue_pfn / PAGE_SIZE as u32);
|
||||
ptr.add(MmioOffsets::QueuePfn.scale32())
|
||||
.write_volatile(queue_pfn / PAGE_SIZE as u32);
|
||||
// We need to store all of this data as a "BlockDevice"
|
||||
// structure We will be referring to this structure when
|
||||
// making block requests AND when handling responses.
|
||||
@ -233,7 +255,8 @@ pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
|
||||
// 8. Set the DRIVER_OK status bit. Device is now "live"
|
||||
status_bits |= StatusField::DriverOk.val32();
|
||||
ptr.add(MmioOffsets::Status.scale32()).write_volatile(status_bits);
|
||||
ptr.add(MmioOffsets::Status.scale32())
|
||||
.write_volatile(status_bits);
|
||||
|
||||
true
|
||||
}
|
||||
@ -241,16 +264,20 @@ pub fn setup_block_device(ptr: *mut u32) -> bool {
|
||||
|
||||
pub fn fill_next_descriptor(bd: &mut BlockDevice, desc: Descriptor) -> u16 {
|
||||
unsafe {
|
||||
// The ring structure increments here first. This allows us to skip
|
||||
// index 0, which then in the used ring will show that .id > 0. This
|
||||
// is one way to error check. We will eventually get back to 0 as
|
||||
// this index is cyclical. However, it shows if the first read/write
|
||||
// actually works.
|
||||
// The ring structure increments here first. This allows us to
|
||||
// skip index 0, which then in the used ring will show that .id
|
||||
// > 0. This is one way to error check. We will eventually get
|
||||
// back to 0 as this index is cyclical. However, it shows if the
|
||||
// first read/write actually works.
|
||||
bd.idx = (bd.idx + 1) % VIRTIO_RING_SIZE as u16;
|
||||
(*bd.queue).desc[bd.idx as usize] = desc;
|
||||
if (*bd.queue).desc[bd.idx as usize].flags & virtio::VIRTIO_DESC_F_NEXT != 0 {
|
||||
if (*bd.queue).desc[bd.idx as usize].flags
|
||||
& virtio::VIRTIO_DESC_F_NEXT
|
||||
!= 0
|
||||
{
|
||||
// If the next flag is set, we need another descriptor.
|
||||
(*bd.queue).desc[bd.idx as usize].next = (bd.idx + 1) % VIRTIO_RING_SIZE as u16;
|
||||
(*bd.queue).desc[bd.idx as usize].next =
|
||||
(bd.idx + 1) % VIRTIO_RING_SIZE as u16;
|
||||
}
|
||||
bd.idx
|
||||
}
|
||||
@ -258,9 +285,9 @@ pub fn fill_next_descriptor(bd: &mut BlockDevice, desc: Descriptor) -> u16 {
|
||||
/// This is now a common block operation for both reads and writes. Therefore,
|
||||
/// when one thing needs to change, we can change it for both reads and writes.
|
||||
/// There is a lot of error checking that I haven't done. The block device reads
|
||||
/// sectors at a time, which are 512 bytes. Therefore, our buffer must be capable
|
||||
/// of storing multiples of 512 bytes depending on the size. The size is also
|
||||
/// a multiple of 512, but we don't really check that.
|
||||
/// sectors at a time, which are 512 bytes. Therefore, our buffer must be
|
||||
/// capable of storing multiples of 512 bytes depending on the size. The size is
|
||||
/// also a multiple of 512, but we don't really check that.
|
||||
/// We DO however, check that we aren't writing to an R/O device. This would
|
||||
/// cause a I/O error if we tried to write to a R/O device.
|
||||
pub fn block_op(dev: usize,
|
||||
@ -273,7 +300,8 @@ pub fn block_op(dev: usize,
|
||||
{
|
||||
unsafe {
|
||||
if let Some(bdev) = BLOCK_DEVICES[dev - 1].as_mut() {
|
||||
// Check to see if we are trying to write to a read only device.
|
||||
// Check to see if we are trying to write to a read only
|
||||
// device.
|
||||
if bdev.read_only && write {
|
||||
println!("Trying to write to read/only!");
|
||||
return Err(BlockErrors::ReadOnly);
|
||||
@ -282,32 +310,40 @@ pub fn block_op(dev: usize,
|
||||
return Err(BlockErrors::InvalidArgument);
|
||||
}
|
||||
let sector = offset / 512;
|
||||
// TODO: Before we get here, we are NOT allowed to schedule a read or
|
||||
// write OUTSIDE of the disk's size. So, we can read capacity from
|
||||
// the configuration space to ensure we stay within bounds.
|
||||
// TODO: Before we get here, we are NOT allowed to
|
||||
// schedule a read or write OUTSIDE of the disk's size.
|
||||
// So, we can read capacity from the configuration space
|
||||
// to ensure we stay within bounds.
|
||||
let blk_request_size = size_of::<Request>();
|
||||
let blk_request = kmalloc(blk_request_size) as *mut Request;
|
||||
let desc = Descriptor { addr: &(*blk_request).header as *const Header as u64,
|
||||
let blk_request =
|
||||
kmalloc(blk_request_size) as *mut Request;
|
||||
let desc =
|
||||
Descriptor { addr: &(*blk_request).header
|
||||
as *const Header
|
||||
as u64,
|
||||
len: size_of::<Header>() as u32,
|
||||
flags: virtio::VIRTIO_DESC_F_NEXT,
|
||||
next: 0, };
|
||||
let head_idx = fill_next_descriptor(bdev, desc);
|
||||
(*blk_request).header.sector = sector;
|
||||
// A write is an "out" direction, whereas a read is an "in" direction.
|
||||
// A write is an "out" direction, whereas a read is an
|
||||
// "in" direction.
|
||||
(*blk_request).header.blktype = if write {
|
||||
VIRTIO_BLK_T_OUT
|
||||
}
|
||||
else {
|
||||
VIRTIO_BLK_T_IN
|
||||
};
|
||||
// We put 111 in the status. Whenever the device finishes, it will write into
|
||||
// status. If we read status and it is 111, we know that it wasn't written to by
|
||||
// the device.
|
||||
// We put 111 in the status. Whenever the device
|
||||
// finishes, it will write into status. If we read
|
||||
// status and it is 111, we know that it wasn't written
|
||||
// to by the device.
|
||||
(*blk_request).data.data = buffer;
|
||||
(*blk_request).header.reserved = 0;
|
||||
(*blk_request).status.status = 111;
|
||||
(*blk_request).watcher = watcher;
|
||||
let desc = Descriptor { addr: buffer as u64,
|
||||
let desc =
|
||||
Descriptor { addr: buffer as u64,
|
||||
len: size,
|
||||
flags: virtio::VIRTIO_DESC_F_NEXT
|
||||
| if !write {
|
||||
@ -318,17 +354,24 @@ pub fn block_op(dev: usize,
|
||||
},
|
||||
next: 0, };
|
||||
let _data_idx = fill_next_descriptor(bdev, desc);
|
||||
let desc = Descriptor { addr: &(*blk_request).status as *const Status as u64,
|
||||
let desc =
|
||||
Descriptor { addr: &(*blk_request).status
|
||||
as *const Status
|
||||
as u64,
|
||||
len: size_of::<Status>() as u32,
|
||||
flags: virtio::VIRTIO_DESC_F_WRITE,
|
||||
next: 0, };
|
||||
let _status_idx = fill_next_descriptor(bdev, desc);
|
||||
(*bdev.queue).avail.ring[(*bdev.queue).avail.idx as usize % virtio::VIRTIO_RING_SIZE] =
|
||||
head_idx;
|
||||
(*bdev.queue).avail.idx = (*bdev.queue).avail.idx.wrapping_add(1);
|
||||
// The only queue a block device has is 0, which is the request
|
||||
// queue.
|
||||
bdev.dev.add(MmioOffsets::QueueNotify.scale32()).write_volatile(0);
|
||||
(*bdev.queue).avail.ring[(*bdev.queue).avail.idx
|
||||
as usize
|
||||
% virtio::VIRTIO_RING_SIZE] = head_idx;
|
||||
(*bdev.queue).avail.idx =
|
||||
(*bdev.queue).avail.idx.wrapping_add(1);
|
||||
// The only queue a block device has is 0, which is the
|
||||
// request queue.
|
||||
bdev.dev
|
||||
.add(MmioOffsets::QueueNotify.scale32())
|
||||
.write_volatile(0);
|
||||
Ok(size)
|
||||
}
|
||||
else {
|
||||
@ -337,11 +380,21 @@ pub fn block_op(dev: usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read(dev: usize, buffer: *mut u8, size: u32, offset: u64) -> Result<u32, BlockErrors> {
|
||||
pub fn read(dev: usize,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u64)
|
||||
-> Result<u32, BlockErrors>
|
||||
{
|
||||
block_op(dev, buffer, size, offset, false, 0)
|
||||
}
|
||||
|
||||
pub fn write(dev: usize, buffer: *mut u8, size: u32, offset: u64) -> Result<u32, BlockErrors> {
|
||||
pub fn write(dev: usize,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u64)
|
||||
-> Result<u32, BlockErrors>
|
||||
{
|
||||
block_op(dev, buffer, size, offset, true, 0)
|
||||
}
|
||||
|
||||
@ -354,19 +407,25 @@ pub fn pending(bd: &mut BlockDevice) {
|
||||
unsafe {
|
||||
let ref queue = *bd.queue;
|
||||
while bd.ack_used_idx != queue.used.idx {
|
||||
let ref elem = queue.used.ring[bd.ack_used_idx as usize % VIRTIO_RING_SIZE];
|
||||
let ref elem = queue.used.ring
|
||||
[bd.ack_used_idx as usize % VIRTIO_RING_SIZE];
|
||||
bd.ack_used_idx = bd.ack_used_idx.wrapping_add(1);
|
||||
// Requests stay resident on the heap until this function, so we can recapture the address here
|
||||
let rq = queue.desc[elem.id as usize].addr as *const Request;
|
||||
// Requests stay resident on the heap until this
|
||||
// function, so we can recapture the address here
|
||||
let rq = queue.desc[elem.id as usize].addr
|
||||
as *const Request;
|
||||
|
||||
// A process might be waiting for this interrupt. Awaken the process attached here.
|
||||
// A process might be waiting for this interrupt. Awaken
|
||||
// the process attached here.
|
||||
let pid_of_watcher = (*rq).watcher;
|
||||
// A PID of 0 means that we don't have a watcher.
|
||||
if pid_of_watcher > 0 {
|
||||
set_running(pid_of_watcher);
|
||||
let proc = get_by_pid(pid_of_watcher);
|
||||
(*(*proc).get_frame_mut()).regs[10] = (*rq).status.status as usize;
|
||||
// TODO: Set GpA0 to the value of the return status.
|
||||
(*(*proc).get_frame_mut()).regs[10] =
|
||||
(*rq).status.status as usize;
|
||||
// TODO: Set GpA0 to the value of the return
|
||||
// status.
|
||||
}
|
||||
kfree(rq as *mut u8);
|
||||
}
|
||||
@ -381,7 +440,10 @@ pub fn handle_interrupt(idx: usize) {
|
||||
pending(bdev);
|
||||
}
|
||||
else {
|
||||
println!("Invalid block device for interrupt {}", idx + 1);
|
||||
println!(
|
||||
"Invalid block device for interrupt {}",
|
||||
idx + 1
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -401,14 +463,27 @@ struct ProcArgs {
|
||||
fn read_proc(args_addr: usize) {
|
||||
let args_ptr = args_addr as *mut ProcArgs;
|
||||
let args = unsafe { args_ptr.as_ref().unwrap() };
|
||||
let _ = block_op(args.dev, args.buffer, args.size, args.offset, false, args.pid);
|
||||
let _ = block_op(
|
||||
args.dev,
|
||||
args.buffer,
|
||||
args.size,
|
||||
args.offset,
|
||||
false,
|
||||
args.pid,
|
||||
);
|
||||
tfree(args_ptr);
|
||||
// This should be handled by the RA now.
|
||||
// syscall_exit();
|
||||
}
|
||||
|
||||
pub fn process_read(pid: u16, dev: usize, buffer: *mut u8, size: u32, offset: u64) {
|
||||
// println!("Block read {}, {}, 0x{:x}, {}, {}", pid, dev, buffer as usize, size, offset);
|
||||
pub fn process_read(pid: u16,
|
||||
dev: usize,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u64)
|
||||
{
|
||||
// println!("Block read {}, {}, 0x{:x}, {}, {}", pid, dev, buffer as
|
||||
// usize, size, offset);
|
||||
let args = talloc::<ProcArgs>().unwrap();
|
||||
args.pid = pid;
|
||||
args.dev = dev;
|
||||
@ -416,19 +491,34 @@ pub fn process_read(pid: u16, dev: usize, buffer: *mut u8, size: u32, offset: u6
|
||||
args.size = size;
|
||||
args.offset = offset;
|
||||
set_waiting(pid);
|
||||
let _ = add_kernel_process_args(read_proc, args as *mut ProcArgs as usize);
|
||||
let _ = add_kernel_process_args(
|
||||
read_proc,
|
||||
args as *mut ProcArgs as usize,
|
||||
);
|
||||
}
|
||||
|
||||
fn write_proc(args_addr: usize) {
|
||||
let args_ptr = args_addr as *mut ProcArgs;
|
||||
let args = unsafe { args_ptr.as_ref().unwrap() };
|
||||
|
||||
let _ = block_op(args.dev, args.buffer, args.size, args.offset, true, args.pid);
|
||||
let _ = block_op(
|
||||
args.dev,
|
||||
args.buffer,
|
||||
args.size,
|
||||
args.offset,
|
||||
true,
|
||||
args.pid,
|
||||
);
|
||||
tfree(args_ptr);
|
||||
// syscall_exit();
|
||||
}
|
||||
|
||||
pub fn process_write(pid: u16, dev: usize, buffer: *mut u8, size: u32, offset: u64) {
|
||||
pub fn process_write(pid: u16,
|
||||
dev: usize,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u64)
|
||||
{
|
||||
let args = talloc::<ProcArgs>().unwrap();
|
||||
args.pid = pid;
|
||||
args.dev = dev;
|
||||
@ -436,5 +526,8 @@ pub fn process_write(pid: u16, dev: usize, buffer: *mut u8, size: u32, offset: u
|
||||
args.size = size;
|
||||
args.offset = offset;
|
||||
set_waiting(pid);
|
||||
let _ = add_kernel_process_args(write_proc, args as *mut ProcArgs as usize);
|
||||
let _ = add_kernel_process_args(
|
||||
write_proc,
|
||||
args as *mut ProcArgs as usize,
|
||||
);
|
||||
}
|
||||
|
@ -62,8 +62,7 @@ impl TrapFrame {
|
||||
hartid: 0,
|
||||
qm: 1,
|
||||
pid: 0,
|
||||
mode: 0,
|
||||
}
|
||||
mode: 0, }
|
||||
}
|
||||
}
|
||||
|
||||
@ -237,9 +236,7 @@ pub fn satp_fence_asid(asid: usize) {
|
||||
const MMIO_MTIME: *const u64 = 0x0200_BFF8 as *const u64;
|
||||
|
||||
pub fn get_mtime() -> usize {
|
||||
unsafe {
|
||||
(*MMIO_MTIME) as usize
|
||||
}
|
||||
unsafe { (*MMIO_MTIME) as usize }
|
||||
}
|
||||
|
||||
/// Copy one data from one memory location to another.
|
||||
@ -269,4 +266,3 @@ pub fn dump_registers(frame: *const TrapFrame) {
|
||||
}
|
||||
println!();
|
||||
}
|
||||
|
||||
|
@ -121,7 +121,12 @@ impl MinixFileSystem {
|
||||
// Here is a little memory trick. We have a reference and it will refer to the
|
||||
// top portion of our buffer. Since we won't be using the super block and inode
|
||||
// simultaneously, we can overlap the memory regions.
|
||||
|
||||
// For Rust-ers, I'm showing two ways here. The first way is to get a reference
|
||||
// from a pointer. You will see the &* a lot in Rust for references. Rust
|
||||
// makes dereferencing a pointer cumbersome, which lends to not using them.
|
||||
let super_block = unsafe { &*(buffer.get_mut() as *mut SuperBlock) };
|
||||
// I opted for a pointer here instead of a reference because we will be offsetting the inode by a certain amount.
|
||||
let inode = buffer.get_mut() as *mut Inode;
|
||||
// Read from the block device. The size is 1 sector (512 bytes) and our offset is past
|
||||
// the boot block (first 1024 bytes). This is where the superblock sits.
|
||||
@ -139,9 +144,7 @@ impl MinixFileSystem {
|
||||
// inode_offset. However, we're going to be reading a group of inodes.
|
||||
syc_read(desc, buffer.get_mut(), 512, inode_offset as u32);
|
||||
|
||||
// There are 512 / size_of<Inode>() inodes in each read that we can do. However, we
|
||||
// need to figure out which inode in that group we need to read. We just take
|
||||
// the % of this to find out.
|
||||
// There are 512 / size_of<Inode>() inodes in each read that we can do. However, we need to figure out which inode in that group we need to read. We just take the % of this to find out.
|
||||
let read_this_node = (inode_num as usize - 1) % (512 / size_of::<Inode>());
|
||||
|
||||
// We copy the inode over. This might not be the best thing since the Inode will
|
||||
@ -193,24 +196,13 @@ impl FileSystem for MinixFileSystem {
|
||||
size
|
||||
};
|
||||
let mut bytes_read = 0u32;
|
||||
// The block buffer automatically drops when we quit early due to an error or we've read enough.
|
||||
// This will be the holding port when we go out and read a block. Recall that even if we want
|
||||
// 10 bytes, we have to read the entire block (really only 512 bytes of the block) first.
|
||||
// So, we use the block_buffer as the middle man, which is then copied into the buffer.
|
||||
// The block buffer automatically drops when we quit early due to an error or we've read enough. This will be the holding port when we go out and read a block. Recall that even if we want 10 bytes, we have to read the entire block (really only 512 bytes of the block) first. So, we use the block_buffer as the middle man, which is then copied into the buffer.
|
||||
let mut block_buffer = BlockBuffer::new(BLOCK_SIZE);
|
||||
// Triply indirect zones point to a block of pointers (BLOCK_SIZE / 4). Each one of those pointers
|
||||
// points to another block of pointers (BLOCK_SIZE / 4). Each one of those pointers yet again points
|
||||
// to another block of pointers (BLOCK_SIZE / 4). This is why we have indirect, iindirect (doubly),
|
||||
// and iiindirect (triply).
|
||||
// Triply indirect zones point to a block of pointers (BLOCK_SIZE / 4). Each one of those pointers points to another block of pointers (BLOCK_SIZE / 4). Each one of those pointers yet again points to another block of pointers (BLOCK_SIZE / 4). This is why we have indirect, iindirect (doubly), and iiindirect (triply).
|
||||
let mut indirect_buffer = BlockBuffer::new(BLOCK_SIZE);
|
||||
let mut iindirect_buffer = BlockBuffer::new(BLOCK_SIZE);
|
||||
let mut iiindirect_buffer = BlockBuffer::new(BLOCK_SIZE);
|
||||
// I put the pointers *const u32 here. That means we will allocate the indirect,
|
||||
// doubly indirect, and triply indirect even for small files. I initially had these
|
||||
// in their respective scopes, but that required us to recreate the indirect buffer for
|
||||
// doubly indirect and both the indirect and doubly indirect buffers for the
|
||||
// triply indirect. Not sure which is better, but I probably wasted brain cells
|
||||
// on this.
|
||||
// I put the pointers *const u32 here. That means we will allocate the indirect, doubly indirect, and triply indirect even for small files. I initially had these in their respective scopes, but that required us to recreate the indirect buffer for doubly indirect and both the indirect and doubly indirect buffers for the triply indirect. Not sure which is better, but I probably wasted brain cells on this.
|
||||
let izones = indirect_buffer.get() as *const u32;
|
||||
let iizones = iindirect_buffer.get() as *const u32;
|
||||
let iiizones = iiindirect_buffer.get() as *const u32;
|
||||
@ -222,9 +214,7 @@ impl FileSystem for MinixFileSystem {
|
||||
// 0..7 means 0 through to 7 but not including 7. If we want to include 7, we
|
||||
// would use the syntax 0..=7.
|
||||
for i in 0..7 {
|
||||
// There are 7 direct zones in the Minix 3 file system. So, we can just read them
|
||||
// one by one. Any zone that has the value 0 is skipped and we check the next
|
||||
// zones. This might happen as we start writing and truncating.
|
||||
// There are 7 direct zones in the Minix 3 file system. So, we can just read them one by one. Any zone that has the value 0 is skipped and we check the next zones. This might happen as we start writing and truncating.
|
||||
if inode.zones[i] == 0 {
|
||||
continue;
|
||||
}
|
||||
|
@ -3,8 +3,20 @@
|
||||
// Stephen Marz
|
||||
// 27 Nov 2019
|
||||
|
||||
use crate::{cpu::{build_satp, get_mtime, satp_fence_asid, CpuMode, SatpMode, TrapFrame},
|
||||
page::{alloc, dealloc, map, unmap, zalloc, EntryBits, Table, PAGE_SIZE},
|
||||
use crate::{cpu::{build_satp,
|
||||
get_mtime,
|
||||
satp_fence_asid,
|
||||
CpuMode,
|
||||
SatpMode,
|
||||
TrapFrame},
|
||||
page::{alloc,
|
||||
dealloc,
|
||||
map,
|
||||
unmap,
|
||||
zalloc,
|
||||
EntryBits,
|
||||
Table,
|
||||
PAGE_SIZE},
|
||||
syscall::syscall_exit};
|
||||
use alloc::collections::vec_deque::VecDeque;
|
||||
use core::ptr::null_mut;
|
||||
@ -97,7 +109,10 @@ pub fn set_sleeping(pid: u16, duration: usize) -> bool {
|
||||
for proc in pl.iter_mut() {
|
||||
if proc.pid == pid {
|
||||
proc.set_state(ProcessState::Sleeping);
|
||||
proc.set_sleep_until(get_mtime() + duration);
|
||||
proc.set_sleep_until(
|
||||
get_mtime()
|
||||
+ duration,
|
||||
);
|
||||
retval = true;
|
||||
break;
|
||||
}
|
||||
@ -119,8 +134,8 @@ pub fn delete_process(pid: u16) {
|
||||
for i in 0..pl.len() {
|
||||
let p = pl.get_mut(i).unwrap();
|
||||
if p.get_pid() == pid {
|
||||
// When the structure gets dropped, all of the
|
||||
// allocations get deallocated.
|
||||
// When the structure gets dropped, all
|
||||
// of the allocations get deallocated.
|
||||
pl.remove(i);
|
||||
break;
|
||||
}
|
||||
@ -161,12 +176,14 @@ fn init_process() {
|
||||
// the scheduler is called in an interrupt context, nothing else
|
||||
// can happen until a process becomes available.
|
||||
println!("Init is still here :), alright, back to sleep.");
|
||||
// 500 wfi's should take 500 context switches before we print Init is still here.
|
||||
// Depending on our context switch time, this might be around 3 seconds.
|
||||
// 500 wfi's should take 500 context switches before we print
|
||||
// Init is still here. Depending on our context switch time,
|
||||
// this might be around 3 seconds.
|
||||
for _ in 0..500 {
|
||||
// We can only write wfi here because init_process is being ran
|
||||
// as a kernel process. If we ran this as a user process, it'd
|
||||
// need a system call to execute a privileged instruction.
|
||||
// We can only write wfi here because init_process is
|
||||
// being ran as a kernel process. If we ran this as a
|
||||
// user process, it'd need a system call to execute a
|
||||
// privileged instruction.
|
||||
unsafe { asm!("wfi") };
|
||||
}
|
||||
}
|
||||
@ -222,7 +239,8 @@ pub fn add_kernel_process(func: fn()) -> u16 {
|
||||
// we start getting into multi-hart processing. For now, we want
|
||||
// a process. Get it to work, then improve it!
|
||||
let my_pid = unsafe { NEXT_PID };
|
||||
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
let mut ret_proc =
|
||||
Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
stack: zalloc(STACK_PAGES),
|
||||
pid: my_pid,
|
||||
root: zalloc(1) as *mut Table,
|
||||
@ -242,10 +260,12 @@ pub fn add_kernel_process(func: fn()) -> u16 {
|
||||
// bottom of the memory and far away from heap allocations.
|
||||
unsafe {
|
||||
(*ret_proc.frame).pc = func_vaddr;
|
||||
// 1 is the return address register. This makes it so we don't have to do
|
||||
// syscall_exit() when a kernel process finishes.
|
||||
// 1 is the return address register. This makes it so we
|
||||
// don't have to do syscall_exit() when a kernel process
|
||||
// finishes.
|
||||
(*ret_proc.frame).regs[1] = ra_delete_proc as usize;
|
||||
(*ret_proc.frame).regs[2] = ret_proc.stack as usize + STACK_PAGES * 4096;
|
||||
(*ret_proc.frame).regs[2] =
|
||||
ret_proc.stack as usize + STACK_PAGES * 4096;
|
||||
(*ret_proc.frame).mode = CpuMode::Machine as usize;
|
||||
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
||||
}
|
||||
@ -298,7 +318,8 @@ pub fn add_kernel_process_args(func: fn(args_ptr: usize), args: usize) -> u16 {
|
||||
// we start getting into multi-hart processing. For now, we want
|
||||
// a process. Get it to work, then improve it!
|
||||
let my_pid = unsafe { NEXT_PID };
|
||||
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
let mut ret_proc =
|
||||
Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
stack: zalloc(STACK_PAGES),
|
||||
pid: my_pid,
|
||||
root: zalloc(1) as *mut Table,
|
||||
@ -319,10 +340,12 @@ pub fn add_kernel_process_args(func: fn(args_ptr: usize), args: usize) -> u16 {
|
||||
unsafe {
|
||||
(*ret_proc.frame).pc = func_vaddr;
|
||||
(*ret_proc.frame).regs[10] = args;
|
||||
// 1 is the return address register. This makes it so we don't have to do
|
||||
// syscall_exit() when a kernel process finishes.
|
||||
// 1 is the return address register. This makes it so we
|
||||
// don't have to do syscall_exit() when a kernel process
|
||||
// finishes.
|
||||
(*ret_proc.frame).regs[1] = ra_delete_proc as usize;
|
||||
(*ret_proc.frame).regs[2] = ret_proc.stack as usize + STACK_PAGES * 4096;
|
||||
(*ret_proc.frame).regs[2] =
|
||||
ret_proc.stack as usize + STACK_PAGES * 4096;
|
||||
(*ret_proc.frame).mode = CpuMode::Machine as usize;
|
||||
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
||||
}
|
||||
@ -448,7 +471,8 @@ impl Process {
|
||||
// We will convert NEXT_PID below into an atomic increment when
|
||||
// we start getting into multi-hart processing. For now, we want
|
||||
// a process. Get it to work, then improve it!
|
||||
let mut ret_proc = Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
let mut ret_proc =
|
||||
Process { frame: zalloc(1) as *mut TrapFrame,
|
||||
stack: alloc(STACK_PAGES),
|
||||
pid: unsafe { NEXT_PID },
|
||||
root: zalloc(1) as *mut Table,
|
||||
@ -470,7 +494,8 @@ impl Process {
|
||||
let saddr = ret_proc.stack as usize;
|
||||
unsafe {
|
||||
(*ret_proc.frame).pc = func_vaddr;
|
||||
(*ret_proc.frame).regs[2] = STACK_ADDR + PAGE_SIZE * STACK_PAGES;
|
||||
(*ret_proc.frame).regs[2] =
|
||||
STACK_ADDR + PAGE_SIZE * STACK_PAGES;
|
||||
(*ret_proc.frame).mode = CpuMode::User as usize;
|
||||
(*ret_proc.frame).pid = ret_proc.pid as usize;
|
||||
}
|
||||
@ -478,20 +503,38 @@ impl Process {
|
||||
let pt;
|
||||
unsafe {
|
||||
pt = &mut *ret_proc.root;
|
||||
(*ret_proc.frame).satp = build_satp(SatpMode::Sv39, ret_proc.pid as usize, ret_proc.root as usize);
|
||||
(*ret_proc.frame).satp =
|
||||
build_satp(
|
||||
SatpMode::Sv39,
|
||||
ret_proc.pid as usize,
|
||||
ret_proc.root as usize,
|
||||
);
|
||||
}
|
||||
// We need to map the stack onto the user process' virtual
|
||||
// memory This gets a little hairy because we need to also map
|
||||
// the function code too.
|
||||
for i in 0..STACK_PAGES {
|
||||
let addr = i * PAGE_SIZE;
|
||||
map(pt, STACK_ADDR + addr, saddr + addr, EntryBits::UserReadWrite.val(), 0);
|
||||
// println!("Set stack from 0x{:016x} -> 0x{:016x}", STACK_ADDR + addr, saddr + addr);
|
||||
map(
|
||||
pt,
|
||||
STACK_ADDR + addr,
|
||||
saddr + addr,
|
||||
EntryBits::UserReadWrite.val(),
|
||||
0,
|
||||
);
|
||||
// println!("Set stack from 0x{:016x} -> 0x{:016x}",
|
||||
// STACK_ADDR + addr, saddr + addr);
|
||||
}
|
||||
// Map the program counter on the MMU and other bits
|
||||
for i in 0..=100 {
|
||||
let modifier = i * 0x1000;
|
||||
map(pt, func_vaddr + modifier, func_addr + modifier, EntryBits::UserReadWriteExecute.val(), 0);
|
||||
map(
|
||||
pt,
|
||||
func_vaddr + modifier,
|
||||
func_addr + modifier,
|
||||
EntryBits::UserReadWriteExecute.val(),
|
||||
0,
|
||||
);
|
||||
}
|
||||
ret_proc
|
||||
}
|
||||
|
@ -24,9 +24,6 @@ pub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
|
||||
match syscall_number {
|
||||
0 | 93 => {
|
||||
// Exit
|
||||
// Currently, we cannot kill a process, it runs forever. We will delete
|
||||
// the process later and free the resources, but for now, we want to get
|
||||
// used to how processes will be scheduled on the CPU.
|
||||
delete_process((*frame).pid as u16);
|
||||
0
|
||||
},
|
||||
@ -41,44 +38,58 @@ pub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
|
||||
},
|
||||
63 => {
|
||||
// Read system call
|
||||
// This is an asynchronous call. This will get the process going. We won't hear the answer until
|
||||
// This is an asynchronous call. This will get the
|
||||
// process going. We won't hear the answer until
|
||||
// we an interrupt back.
|
||||
// TODO: The buffer is a virtual memory address that needs to be translated to a physical memory
|
||||
// location.
|
||||
// TODO: The buffer is a virtual memory address that
|
||||
// needs to be translated to a physical memory location.
|
||||
// This needs to be put into a process and ran.
|
||||
// The buffer (regs[12]) needs to be translated when ran from a user process using virt_to_phys.
|
||||
// If this turns out to be a page fault, we need to NOT proceed with the read!
|
||||
// The buffer (regs[12]) needs to be translated when ran
|
||||
// from a user process using virt_to_phys. If this turns
|
||||
// out to be a page fault, we need to NOT proceed with
|
||||
// the read!
|
||||
let mut physical_buffer = (*frame).regs[12];
|
||||
// If the MMU is turned on, we have to translate the address. Eventually, I will put this
|
||||
// code into a convenient function, but for now, it will show how translation will be done.
|
||||
// If the MMU is turned on, we have to translate the
|
||||
// address. Eventually, I will put this code into a
|
||||
// convenient function, but for now, it will show how
|
||||
// translation will be done.
|
||||
if (*frame).satp != 0 {
|
||||
let p = get_by_pid((*frame).pid as u16);
|
||||
let table = ((*p).get_table_address() as *mut Table).as_ref().unwrap();
|
||||
let paddr = virt_to_phys(table, (*frame).regs[12]);
|
||||
let table = ((*p).get_table_address()
|
||||
as *mut Table)
|
||||
.as_ref()
|
||||
.unwrap();
|
||||
let paddr =
|
||||
virt_to_phys(table, (*frame).regs[12]);
|
||||
if paddr.is_none() {
|
||||
(*frame).regs[10] = -1isize as usize;
|
||||
return mepc + 4;
|
||||
}
|
||||
physical_buffer = paddr.unwrap();
|
||||
}
|
||||
// TODO: Not only do we need to check the buffer, but it is possible that the buffer spans
|
||||
// multiple pages. We need to check all pages that this might span. We can't just do paddr
|
||||
// and paddr + size, since there could be a missing page somewhere in between.
|
||||
// TODO: Not only do we need to check the buffer, but it
|
||||
// is possible that the buffer spans multiple pages. We
|
||||
// need to check all pages that this might span. We
|
||||
// can't just do paddr and paddr + size, since there
|
||||
// could be a missing page somewhere in between.
|
||||
let _ = minixfs::process_read(
|
||||
(*frame).pid as u16,
|
||||
(*frame).regs[10] as usize,
|
||||
(*frame).regs[10]
|
||||
as usize,
|
||||
(*frame).regs[11] as u32,
|
||||
physical_buffer as *mut u8,
|
||||
physical_buffer
|
||||
as *mut u8,
|
||||
(*frame).regs[13] as u32,
|
||||
(*frame).regs[14] as u32,
|
||||
);
|
||||
// If we return 0, the trap handler will schedule another process.
|
||||
// If we return 0, the trap handler will schedule
|
||||
// another process.
|
||||
0
|
||||
},
|
||||
180 => {
|
||||
// println!(
|
||||
// "Pid: {}, Dev: {}, Buffer: 0x{:x}, Size: {}, Offset: {}",
|
||||
// (*frame).pid,
|
||||
// "Pid: {}, Dev: {}, Buffer: 0x{:x}, Size: {},
|
||||
// Offset: {}", (*frame).pid,
|
||||
// (*frame).regs[10],
|
||||
// (*frame).regs[11],
|
||||
// (*frame).regs[12],
|
||||
@ -103,10 +114,25 @@ pub unsafe fn do_syscall(mepc: usize, frame: *mut TrapFrame) -> usize {
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
fn make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize;
|
||||
fn make_syscall(sysno: usize,
|
||||
arg0: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize,
|
||||
arg5: usize)
|
||||
-> usize;
|
||||
}
|
||||
|
||||
fn do_make_syscall(sysno: usize, arg0: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize, arg5: usize) -> usize {
|
||||
fn do_make_syscall(sysno: usize,
|
||||
arg0: usize,
|
||||
arg1: usize,
|
||||
arg2: usize,
|
||||
arg3: usize,
|
||||
arg4: usize,
|
||||
arg5: usize)
|
||||
-> usize
|
||||
{
|
||||
unsafe { make_syscall(sysno, arg0, arg1, arg2, arg3, arg4, arg5) }
|
||||
}
|
||||
|
||||
@ -114,12 +140,39 @@ pub fn syscall_exit() {
|
||||
let _ = do_make_syscall(93, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
pub fn syscall_fs_read(dev: usize, inode: u32, buffer: *mut u8, size: u32, offset: u32) -> usize {
|
||||
do_make_syscall(63, dev, inode as usize, buffer as usize, size as usize, offset as usize, 0)
|
||||
pub fn syscall_fs_read(dev: usize,
|
||||
inode: u32,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u32)
|
||||
-> usize
|
||||
{
|
||||
do_make_syscall(
|
||||
63,
|
||||
dev,
|
||||
inode as usize,
|
||||
buffer as usize,
|
||||
size as usize,
|
||||
offset as usize,
|
||||
0,
|
||||
)
|
||||
}
|
||||
|
||||
pub fn syscall_block_read(dev: usize, buffer: *mut u8, size: u32, offset: u32) -> u8 {
|
||||
do_make_syscall(180, dev, buffer as usize, size as usize, offset as usize, 0, 0) as u8
|
||||
pub fn syscall_block_read(dev: usize,
|
||||
buffer: *mut u8,
|
||||
size: u32,
|
||||
offset: u32)
|
||||
-> u8
|
||||
{
|
||||
do_make_syscall(
|
||||
180,
|
||||
dev,
|
||||
buffer as usize,
|
||||
size as usize,
|
||||
offset as usize,
|
||||
0,
|
||||
0,
|
||||
) as u8
|
||||
}
|
||||
|
||||
// These system call numbers come from libgloss so that we can use newlib
|
||||
|
@ -8,7 +8,8 @@ pub fn test_block() {
|
||||
let bytes_to_read = 1024 * 50;
|
||||
let buffer = kmalloc(bytes_to_read);
|
||||
unsafe {
|
||||
let bytes_read = syscall_fs_read(8, 5, buffer, bytes_to_read as u32, 0);
|
||||
let bytes_read =
|
||||
syscall_fs_read(8, 5, buffer, bytes_to_read as u32, 0);
|
||||
println!("FS Read returned {} bytes", bytes_read);
|
||||
for i in 0..16 * 4 {
|
||||
print!("{:02x} ", buffer.add(i).read());
|
||||
|
Loading…
Reference in New Issue
Block a user