RustForge

rust-kernel

Rustbelt: a hobby x86_64 kernel written in Rust

rust-kernel / tests / block.rs
5147 bytes ยท raw
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
//! Block layer: PCI enumeration, the virtio-blk driver (against the
//! target/disk.img QEMU attaches), partition table parsing, RAM disks.
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(rustbelt::test_runner)]
#![reexport_test_harness_main = "test_main"]

extern crate alloc;

use alloc::{sync::Arc, vec};
use bootloader::{BootInfo, entry_point};
use core::panic::PanicInfo;
use rustbelt::block::{self, BlockDevice, RamDisk, SECTOR_SIZE, partition};
use rustbelt::errno::Errno;
use rustbelt::pci;

entry_point!(main);

fn main(boot_info: &'static BootInfo) -> ! {
    rustbelt::init();
    rustbelt::init_memory(boot_info);
    rustbelt::init_smp(); // all 4 CPUs run threads
    block::init(&pci::scan());
    test_main();
    rustbelt::hlt_loop();
}

#[test_case]
fn pci_scan_finds_the_virtio_disk() {
    let devices = pci::scan();
    // QEMU's i440FX machine always has the host bridge at 00:00.0.
    assert_eq!((devices[0].bus, devices[0].device, devices[0].vendor_id), (0, 0, 0x8086));
    assert!(devices.iter().any(|d| d.vendor_id == 0x1af4 && d.device_id == 0x1001));
}

#[test_case]
fn virtio_disk_reads_the_gpt_image() {
    let disk = block::get("vda").expect("no vda: is target/disk.img attached?");
    assert_eq!(disk.sector_count(), 64 * 1024 * 1024 / 512);
    let mut sector = [0u8; SECTOR_SIZE];
    disk.read_sectors(0, &mut sector).unwrap();
    assert_eq!(&sector[510..], &[0x55, 0xAA]); // protective MBR signature
    assert_eq!(sector[446 + 4], 0xEE); // partition type "GPT protective"
    disk.read_sectors(1, &mut sector).unwrap();
    assert_eq!(&sector[..8], b"EFI PART");
}

#[test_case]
fn multi_sector_reads_match_single_sector_reads() {
    // 20 sectors = more than one 4 KiB bounce-buffer page per request.
    let disk = block::get("vda").unwrap();
    let mut big = vec![0u8; 20 * SECTOR_SIZE];
    disk.read_sectors(2048, &mut big).unwrap();
    let mut one = [0u8; SECTOR_SIZE];
    for i in 0..20 {
        disk.read_sectors(2048 + i as u64, &mut one).unwrap();
        assert_eq!(&big[i * SECTOR_SIZE..(i + 1) * SECTOR_SIZE], &one[..]);
    }
}

#[test_case]
fn virtio_write_then_read_back() {
    // QEMU runs the disk with snapshot=on, so this doesn't touch the file.
    let disk = block::get("vda").unwrap();
    let data: alloc::vec::Vec<u8> = (0..2 * SECTOR_SIZE).map(|i| (i % 251) as u8).collect();
    disk.write_sectors(100, &data).unwrap();
    let mut back = vec![0u8; 2 * SECTOR_SIZE];
    disk.read_sectors(100, &mut back).unwrap();
    assert_eq!(back, data);
}

#[test_case]
fn out_of_range_and_partial_sectors_fail() {
    let disk = block::get("vda").unwrap();
    let mut sector = [0u8; SECTOR_SIZE];
    assert_eq!(disk.read_sectors(disk.sector_count(), &mut sector), Err(Errno::EIO));
    assert_eq!(disk.read_sectors(0, &mut sector[..100]), Err(Errno::EINVAL));
}

#[test_case]
fn gpt_partition_is_found_and_registered() {
    let disk = block::get("vda").unwrap();
    let (scheme, parts) = partition::scan(&disk).unwrap();
    assert_eq!(scheme, "GPT");
    assert_eq!(parts.len(), 1);
    assert_eq!((parts[0].start, parts[0].sectors), (2048, 126976));
    assert_eq!(parts[0].name, "fat32");
    // vda1 is a block device whose sector 0 is the disk's sector 2048:
    // the FAT boot sector (ends in 55 AA, says "FAT32   " at offset 82).
    let vda1 = block::get("vda1").unwrap();
    assert_eq!(vda1.sector_count(), 126976);
    let mut sector = [0u8; SECTOR_SIZE];
    vda1.read_sectors(0, &mut sector).unwrap();
    assert_eq!(&sector[82..90], b"FAT32   ");
    assert_eq!(vda1.read_sectors(126976, &mut sector), Err(Errno::EIO)); // past its end
}

#[test_case]
fn mbr_partitions_on_a_ram_disk() {
    // Build a classic MBR by hand: two primary partitions.
    let disk: Arc<dyn BlockDevice> = Arc::new(RamDisk::new(1000));
    let mut mbr = [0u8; SECTOR_SIZE];
    let entry = |mbr: &mut [u8; SECTOR_SIZE], i: usize, kind: u8, start: u32, len: u32| {
        let e = 446 + 16 * i;
        mbr[e + 4] = kind;
        mbr[e + 8..e + 12].copy_from_slice(&start.to_le_bytes());
        mbr[e + 12..e + 16].copy_from_slice(&len.to_le_bytes());
    };
    entry(&mut mbr, 0, 0x0c, 63, 400); // FAT32 (LBA)
    entry(&mut mbr, 1, 0x83, 500, 300); // Linux
    // No signature yet -> not a partition table.
    assert!(matches!(partition::scan(&disk), Err(partition::PartitionError::NoSignature)));
    mbr[510] = 0x55;
    mbr[511] = 0xAA;
    disk.write_sectors(0, &mbr).unwrap();
    let (scheme, parts) = partition::scan(&disk).unwrap();
    assert_eq!(scheme, "MBR");
    assert_eq!(parts.len(), 2);
    assert_eq!((parts[0].start, parts[0].sectors), (63, 400));
    assert_eq!((parts[1].start, parts[1].sectors), (500, 300));

    // A partition device maps its sector 0 to the partition's start.
    let part = partition::Partition::new(disk.clone(), 500, 300);
    part.write_sectors(0, &[7u8; SECTOR_SIZE]).unwrap();
    let mut back = [0u8; SECTOR_SIZE];
    disk.read_sectors(500, &mut back).unwrap();
    assert_eq!(back, [7u8; SECTOR_SIZE]);
}

#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
    rustbelt::test_panic_handler(info)
}