Skip to content

Commit

Permalink
Improve filesystem (#24)
Browse files Browse the repository at this point in the history
* Add doc about fs

* Add console on diskless boot

* Enumerate all disks

* Avoid hardcoded allocation of root dir

* Refactor ATA code

* Add BlockDevice

* Add mkfs command

* Update readme

* Add warning in the readme about disk modifications

* Use all bits inside BlockBitmap data

* Add makefile

* Overwrite only bootloader and kernel in disk image

* Update readme

* Add doc about mkfs

* Update mkfs command
  • Loading branch information
vinc authored Feb 13, 2020
1 parent d5ff99c commit 0a4b26a
Show file tree
Hide file tree
Showing 12 changed files with 262 additions and 139 deletions.
33 changes: 33 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.PHONY: setup image qemu
.EXPORT_ALL_VARIABLES:

setup:
curl https://sh.rustup.rs -sSf | sh
rustup install nightly
rustup default nightly
rustup component add rust-src
rustup component add llvm-tools-preview
cargo install cargo-xbuild bootimage

output = vga
keyboard = qwerty

bin=target/x86_64-moros/release/bootimage-moros.bin
img=disk.img

$(img):
qemu-img create $(img) 32M

# Rebuild MOROS if the features list changed
image: $(img)
touch src/lib.rs
cargo bootimage --no-default-features --features $(output),$(keyboard) --release
dd conv=notrunc if=$(bin) of=$(img)

opts = -cpu max -nic model=rtl8139 -hda $(img)
ifeq ($(output),serial)
opts += -display none -serial stdio
endif

qemu:
qemu-system-x86_64 $(opts)
32 changes: 11 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,36 +52,26 @@ Install tools:
rustup component add llvm-tools-preview
cargo install cargo-xbuild bootimage

Create disk:

qemu-img create disk.img 128M

## Usage

QEMU with VGA Text Mode:

cargo xrun --release -- \
-cpu phenom \
-nic model=rtl8139 \
-hdc disk.img
Build image:

QEMU with a serial console:
make image output=vga keyboard=qwerty

cargo xrun --release --no-default-features --features serial,dvorak -- \
-cpu phenom \
-nic model=rtl8139 \
-serial stdio \
-display none \
-hdc disk.img
Run on QEMU:

Bochs instead of QEMU:
make qemu output=vga

sh run/bochs.sh
Run on a native x86 computer:

Or with `cool-retro-term` for a retro console look:
sudo dd if=target/x86_64-moros/release/bootimage-moros.bin of=/dev/sdx && sync
sudo reboot

sh run/cool-retro-term.sh
MOROS will open a console in diskless mode after boot if no filesystem is
detected. Use `mkfs` to create a filesystem on a disk.

**Be careful not to overwrite the disk of your OS when using `dd` inside your OS
or `mkfs` inside MOROS.**

## LICENSE

Expand Down
71 changes: 71 additions & 0 deletions doc/filesystem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# MOROS Filesystem

## Disk

A disk is separated in block of 512 bytes, grouped into three areas. The first
is reserved for future uses, the second is used as a bitmap mapping the
allocated blocks in the third area. The data stored on the disk use the blocks
of the third area.

During the first boot of the OS, the root dir will be allocated, using the
first block of the data area.

A location on the tree of dirs and files is named a path:

- The root dir is represented by a slash: `/`
- A dir inside the root will have its name appended to the slash: `/usr`
- Subsequent dirs will append a slash and their names: `/usr/admin`

### Creation with QEMU

$ qemu-img create disk.img 128M
Formatting 'disk.img', fmt=raw size=134217728

### Setup in diskless console

During boot MOROS will detect the disks present on the ATA buses, then the
filesystems on those disks. If no filesystem is found, MOROS will open a
console in diskless mode to allow the user to create one with the `mkfs`
command:

> mkfs /dev/ata/0/0

## Data

### BlockBitmap

Bitmap of allocated blocks in the data area.

### Block

A block is small area of 512 bytes on a disk, and it is also part of linked
list representing a file or a directory.

The first 4 bytes of a block is the address of the next block on the list and
the rest of block is the data stored in the block.

### DirEntry

A directory entry represent a file or a directory contained inside a directory.
Each entry use a variable number of bytes that must fit inside the data of one
block. Those bytes represent the kind of entry (file or dir), the address of
the first block, the filesize (max 4GB), and the filename (max 255 chars) of
the entry.

Structure:

- 0..1: kind
- 1..5: addr
- 5..10: size
- 10..11: name len
- 11..n: name buf

### Dir

A directory contains the address of the first block where its directory entries
are stored.

### File

A file contains the address of its first block along with its filesize and
filename, and a reference to its parent directory.
2 changes: 1 addition & 1 deletion doc/net.md → doc/network.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# MOROS Net
# MOROS Network

## NET

Expand Down
3 changes: 0 additions & 3 deletions run/bochs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,6 @@ set -e

dir=$(dirname "$0")

# Build image if needed
cd "$dir/.." && cargo bootimage --release

# Clean up lock files that Bochs creates
rm -f "$dir/../target/x86_64-moros/release/bootimage-moros.bin.lock"
rm -f "$dir/../disk.img.lock"
Expand Down
8 changes: 2 additions & 6 deletions run/cool-retro-term.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,10 @@
set -e

dir=$(dirname "$0")
image="target/x86_64-moros/release/bootimage-moros.bin"
qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 -hdc disk.img"
qemu="qemu-system-x86_64 -display curses -cpu max -nic model=rtl8139 disk.img"
#qemu="qemu-system-x86_64 -display curses -cpu max -hdc disk.img -netdev user,id=u1,hostfwd=tcp::2222-:22 -device rtl8139,netdev=u1 -object filter-dump,id=f1,netdev=u1,file=/tmp/qemu.pcap"

# Build image if needed
cd "$dir/.." && cargo bootimage --release

echo "The MOROS theme at '$dir/cool-retro-term.json' have to be manually imported."

# Launch qemu inside cool-retro-term
cool-retro-term --fullscreen --profile "MOROS" --workdir "$dir/.." -e sh -c "$qemu $image 2>/dev/null"
cool-retro-term --fullscreen --profile "MOROS" --workdir "$dir/.." -e sh -c "$qemu 2>/dev/null"
83 changes: 26 additions & 57 deletions src/kernel/ata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,8 @@ impl Bus {
}

pub fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) {
assert!(buf.len() == 512);

self.setup(drive, block);

self.write_command(Command::Read);
Expand All @@ -208,6 +210,8 @@ impl Bus {
}

pub fn write(&mut self, drive: u8, block: u32, buf: &[u8]) {
assert!(buf.len() == 512);

self.setup(drive, block);

self.write_command(Command::Write);
Expand All @@ -226,7 +230,7 @@ impl Bus {
}

lazy_static! {
pub static ref ATA_BUSES: Mutex<Vec<Bus>> = Mutex::new(Vec::new());
pub static ref BUSES: Mutex<Vec<Bus>> = Mutex::new(Vec::new());
}

fn disk_size(sectors: u32) -> (u32, String) {
Expand All @@ -239,74 +243,39 @@ fn disk_size(sectors: u32) -> (u32, String) {
}

pub fn init() {
let mut buses = ATA_BUSES.lock();
let mut buses = BUSES.lock();
buses.push(Bus::new(0, 0x1F0, 0x3F6, 14));
buses.push(Bus::new(1, 0x170, 0x376, 15));

let bus = 1;
let drive = 0;
if let Some(buf) = buses[bus].identify_drive(drive) {
let mut serial = String::new();
for i in 10..20 {
for &b in &buf[i].to_be_bytes() {
serial.push(b as char);
}
}
let mut model = String::new();
for i in 27..47 {
for &b in &buf[i].to_be_bytes() {
model.push(b as char);
for bus in 0..2 {
for drive in 0..2 {
if let Some(buf) = buses[bus].identify_drive(drive) {
let mut serial = String::new();
for i in 10..20 {
for &b in &buf[i].to_be_bytes() {
serial.push(b as char);
}
}
let mut model = String::new();
for i in 27..47 {
for &b in &buf[i].to_be_bytes() {
model.push(b as char);
}
}
let sectors = (buf[61] as u32) << 16 | (buf[60] as u32);
let (size, unit) = disk_size(sectors);
log!("ATA {}:{} {} {} ({} {})\n", bus, drive, model.trim(), serial.trim(), size, unit);
}
}
let sectors = (buf[61] as u32) << 16 | (buf[60] as u32);
let (size, unit) = disk_size(sectors);
log!("ATA {}:{} {} {} ({} {})\n", bus, drive, model.trim(), serial.trim(), size, unit);
}

/*
let block = 1;
let mut buf = [0u8; 512];
buses[1].read(drive, block, &mut buf);
for i in 0..256 {
if i % 8 == 0 {
print!("\n{:08X} ", i * 2);
}
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
}
print!("\n");
buf[0x42] = 'H' as u8;
buf[0x43] = 'e' as u8;
buf[0x44] = 'l' as u8;
buf[0x45] = 'l' as u8;
buf[0x46] = 'o' as u8;
for i in 0..256 {
if i % 8 == 0 {
print!("\n{:08X} ", i * 2);
}
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
}
print!("\n");
buses[1].write(drive, block, &mut buf);
let mut buf = [0u8; 512];
buses[1].read(drive, block, &mut buf);
for i in 0..256 {
if i % 8 == 0 {
print!("\n{:08X} ", i * 2);
}
print!("{:02X}{:02X} ", buf[i * 2], buf[i * 2 + 1]);
}
print!("\n");
*/
}

pub fn read(bus: u8, drive: u8, block: u32, mut buf: &mut [u8]) {
let mut buses = ATA_BUSES.lock();
let mut buses = BUSES.lock();
buses[bus as usize].read(drive, block, &mut buf);
}

pub fn write(bus: u8, drive: u8, block: u32, buf: &[u8]) {
let mut buses = ATA_BUSES.lock();
let mut buses = BUSES.lock();
buses[bus as usize].write(drive, block, &buf);
}
Loading

0 comments on commit 0a4b26a

Please sign in to comment.