Skip to content

Commit b3207db

Browse files
committed
Build and load a second stage
1 parent 54adb4c commit b3207db

File tree

19 files changed

+941
-41
lines changed

19 files changed

+941
-41
lines changed

Cargo.lock

Lines changed: 4 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ members = [
1616
"uefi",
1717
"bios",
1818
"bios/boot_sector",
19+
"bios/second_stage",
1920
"tests/runner",
2021
"tests/test_kernels/default_settings",
2122
"tests/test_kernels/map_phys_mem",

bios/boot_sector/src/error.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/// The boot sector did not find the second stage partition.
2+
///
3+
/// The BIOS bootloader requires a special second stage partition with partition type 0x20.
4+
pub const NO_SECOND_STAGE_PARTITION: u8 = b'x';

bios/boot_sector/src/fat.rs

Lines changed: 102 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -2,47 +2,44 @@
22

33
use super::split_array_ref;
44

5+
// Size of single directory entry in bytes
6+
const DIR_ENTRY_SIZE: u32 = 32;
7+
58
pub(crate) struct BootSector {
6-
bootjmp: [u8; 3],
7-
oem_name: [u8; 8],
89
pub(crate) bpb: BiosParameterBlock,
9-
boot_code: [u8; 448],
10-
boot_sig: [u8; 2],
1110
}
1211

1312
impl BootSector {
1413
pub(crate) fn deserialize(bytes: &[u8]) -> Self {
1514
let mut boot = Self::default();
16-
let (&bootjmp, bytes) = split_array_ref(bytes);
17-
let (&oem_name, bytes) = split_array_ref(bytes);
15+
// let (&bootjmp, bytes) = split_array_ref(bytes);
16+
// let (&oem_name, bytes) = split_array_ref(bytes);
17+
18+
let bytes = &bytes[3 + 8..];
1819

19-
boot.bootjmp = bootjmp;
20-
boot.oem_name = oem_name;
20+
// boot.bootjmp = bootjmp;
21+
// boot.oem_name = oem_name;
2122
boot.bpb = BiosParameterBlock::deserialize(bytes);
2223

23-
let bytes = if boot.bpb.is_fat32() {
24-
let (boot_code, bytes): (&[_; 420], _) = split_array_ref(bytes);
25-
boot.boot_code[0..420].copy_from_slice(&boot_code[..]);
26-
bytes
27-
} else {
28-
let (&boot_code, bytes) = split_array_ref(bytes);
29-
boot.boot_code = boot_code;
30-
bytes
31-
};
32-
let (&boot_sig, bytes) = split_array_ref(bytes);
33-
boot.boot_sig = boot_sig;
24+
// let bytes = if boot.bpb.is_fat32() {
25+
// let (boot_code, bytes): (&[_; 420], _) = split_array_ref(bytes);
26+
// boot.boot_code[0..420].copy_from_slice(&boot_code[..]);
27+
// bytes
28+
// } else {
29+
// let (&boot_code, bytes) = split_array_ref(bytes);
30+
// boot.boot_code = boot_code;
31+
// bytes
32+
// };
33+
// let (&boot_sig, bytes) = split_array_ref(bytes);
34+
// boot.boot_sig = boot_sig;
3435
boot
3536
}
3637
}
3738

3839
impl Default for BootSector {
3940
fn default() -> Self {
4041
Self {
41-
bootjmp: Default::default(),
42-
oem_name: Default::default(),
4342
bpb: BiosParameterBlock::default(),
44-
boot_code: [0; 448],
45-
boot_sig: Default::default(),
4643
}
4744
}
4845
}
@@ -158,4 +155,86 @@ impl BiosParameterBlock {
158155
// this provides a simple way to detect FAT32
159156
self.sectors_per_fat_16 == 0
160157
}
158+
159+
pub(crate) fn sectors_per_fat(&self) -> u32 {
160+
if self.is_fat32() {
161+
self.sectors_per_fat_32
162+
} else {
163+
u32::from(self.sectors_per_fat_16)
164+
}
165+
}
166+
167+
pub(crate) fn total_sectors(&self) -> u32 {
168+
if self.total_sectors_16 == 0 {
169+
self.total_sectors_32
170+
} else {
171+
u32::from(self.total_sectors_16)
172+
}
173+
}
174+
175+
pub(crate) fn reserved_sectors(&self) -> u32 {
176+
u32::from(self.reserved_sectors)
177+
}
178+
179+
pub(crate) fn root_dir_sectors(&self) -> u32 {
180+
let root_dir_bytes = u32::from(self.root_entries) * DIR_ENTRY_SIZE;
181+
(root_dir_bytes + u32::from(self.bytes_per_sector) - 1) / u32::from(self.bytes_per_sector)
182+
}
183+
184+
pub(crate) fn sectors_per_all_fats(&self) -> u32 {
185+
u32::from(self.fats) * self.sectors_per_fat()
186+
}
187+
188+
pub(crate) fn first_data_sector(&self) -> u32 {
189+
let root_dir_sectors = self.root_dir_sectors();
190+
let fat_sectors = self.sectors_per_all_fats();
191+
self.reserved_sectors() + fat_sectors + root_dir_sectors
192+
}
193+
194+
pub(crate) fn total_clusters(&self) -> u32 {
195+
let total_sectors = self.total_sectors();
196+
let first_data_sector = self.first_data_sector();
197+
let data_sectors = total_sectors - first_data_sector;
198+
data_sectors / u32::from(self.sectors_per_cluster)
199+
}
200+
201+
pub fn fat_type(&self) -> FatType {
202+
FatType::from_clusters(self.total_clusters())
203+
}
204+
205+
/// Returns a root directory object allowing for futher penetration of a filesystem structure.
206+
pub fn check_root_dir(&self) {
207+
match self.fat_type() {
208+
FatType::Fat12 | FatType::Fat16 => crate::fail(b'y'),
209+
FatType::Fat32 => {
210+
self.root_dir_first_cluster;
211+
crate::fail(b'z');
212+
}
213+
}
214+
}
215+
}
216+
217+
pub enum FatType {
218+
/// 12 bits per FAT entry
219+
Fat12,
220+
/// 16 bits per FAT entry
221+
Fat16,
222+
/// 32 bits per FAT entry
223+
Fat32,
224+
}
225+
226+
impl FatType {
227+
const FAT16_MIN_CLUSTERS: u32 = 4085;
228+
const FAT32_MIN_CLUSTERS: u32 = 65525;
229+
const FAT32_MAX_CLUSTERS: u32 = 0x0FFF_FFF4;
230+
231+
pub(crate) fn from_clusters(total_clusters: u32) -> Self {
232+
if total_clusters < Self::FAT16_MIN_CLUSTERS {
233+
FatType::Fat12
234+
} else if total_clusters < Self::FAT32_MIN_CLUSTERS {
235+
FatType::Fat16
236+
} else {
237+
FatType::Fat32
238+
}
239+
}
161240
}

bios/boot_sector/src/main.rs

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,21 @@ use core::{
55
arch::{asm, global_asm},
66
slice,
77
};
8+
use error::NO_SECOND_STAGE_PARTITION;
89
use fail::{fail, print_char, UnwrapOrFail};
910

1011
global_asm!(include_str!("boot.s"));
1112

1213
mod dap;
14+
mod error;
1315
mod fail;
1416
mod fat;
1517
mod mbr;
1618

1719
extern "C" {
1820
static _mbr_start: u8;
1921
static _partition_table: u8;
22+
static _second_stage_start: u8;
2023
}
2124

2225
fn mbr_start() -> *const u8 {
@@ -27,41 +30,65 @@ unsafe fn partition_table() -> *const u8 {
2730
unsafe { &_partition_table }
2831
}
2932

33+
fn second_stage_start() -> *const () {
34+
let ptr: *const u8 = unsafe { &_second_stage_start };
35+
ptr as *const ()
36+
}
37+
3038
#[no_mangle]
3139
pub extern "C" fn first_stage(disk_number: u16) {
3240
print_char(b'1');
3341
let partition_table = &unsafe { slice::from_raw_parts(partition_table(), 16 * 4) };
34-
let boot_partition = mbr::boot_partition(partition_table).unwrap_or_fail(b'x');
42+
let second_stage_partition =
43+
mbr::boot_partition(partition_table).unwrap_or_fail(NO_SECOND_STAGE_PARTITION);
3544

3645
print_char(b'2');
37-
let partition_buf = u16::try_from(mbr_start() as usize).unwrap_or_fail(b'a') + 512;
46+
let target_addr = u16::try_from(second_stage_start() as usize).unwrap_or_fail(b'a');
3847

3948
// load boot partition into buffer
4049
// TODO: only load headers
4150
let dap = dap::DiskAddressPacket::from_lba(
42-
partition_buf,
43-
boot_partition.logical_block_address.into(),
44-
1, // partition.sector_count.try_into().unwrap_or_fail(b'b'),
51+
target_addr,
52+
second_stage_partition.logical_block_address.into(),
53+
second_stage_partition
54+
.sector_count
55+
.try_into()
56+
.unwrap_or_fail(b'b'),
4557
);
4658
unsafe {
4759
dap.perform_load(disk_number);
4860
}
49-
if boot_partition.sector_count == 0 {
61+
if second_stage_partition.sector_count == 0 {
5062
fail(b'c');
5163
}
5264

5365
print_char(b'3');
5466

67+
// jump to second stage
68+
let second_stage_entry_point: extern "C" fn(disk_number: u16) =
69+
unsafe { core::mem::transmute(target_addr as *const ()) };
70+
unsafe { second_stage_entry_point(disk_number) }
71+
72+
print_char(b'R');
73+
print_char(b'R');
74+
print_char(b'R');
75+
print_char(b'R');
76+
print_char(b'R');
77+
loop {}
78+
5579
// try to parse FAT file system
5680
let fat_slice = unsafe {
5781
slice::from_raw_parts(
58-
partition_buf as *const u8,
59-
usize::try_from(boot_partition.sector_count).unwrap_or_else(|_| fail(b'a')) * 512,
82+
target_addr as *const u8,
83+
usize::try_from(second_stage_partition.sector_count).unwrap_or_else(|_| fail(b'a'))
84+
* 512,
6085
)
6186
};
6287

6388
print_char(b'4');
6489
let boot_sector = fat::BootSector::deserialize(fat_slice);
90+
let root_dir = boot_sector.bpb.root_dir_first_cluster;
91+
boot_sector.bpb.check_root_dir();
6592

6693
print_char(b'5');
6794

bios/boot_sector/src/mbr.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
use super::fail::{fail, UnwrapOrFail};
44

5+
/// We use this partition type to store the second bootloader stage;
6+
const BOOTLOADER_SECOND_STAGE_PARTITION_TYPE: u8 = 0x20;
7+
58
/// Returns the first bootable partition in the partition table.
69
pub fn boot_partition(partitions_raw: &[u8]) -> Option<PartitionTableEntry> {
710
for index in 0..4 {
811
let entry = get_partition(partitions_raw, index);
9-
if entry.bootable {
12+
if entry.partition_type == BOOTLOADER_SECOND_STAGE_PARTITION_TYPE {
1013
return Some(entry);
1114
}
1215
}
@@ -49,6 +52,7 @@ const ENTRY_SIZE: usize = 16;
4952
/// The type of a particular partition.
5053
#[derive(Copy, Clone, Eq, PartialEq)]
5154
pub enum PartitionType {
55+
BootloaderSecondStage,
5256
Unused,
5357
Unknown(u8),
5458
Fat12(u8),
@@ -64,6 +68,8 @@ impl PartitionType {
6468
/// Parses a partition type from the type byte in the MBR's table.
6569
pub fn from_mbr_tag_byte(tag: u8) -> PartitionType {
6670
match tag {
71+
// we use partition type 0x20 to store the second bootloader stage
72+
0x20 => PartitionType::BootloaderSecondStage,
6773
0x0 => PartitionType::Unused,
6874
0x01 => PartitionType::Fat12(tag),
6975
0x04 | 0x06 | 0x0e => PartitionType::Fat16(tag),

bios/second_stage/.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/target

bios/second_stage/Cargo.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
[package]
2+
name = "bootloader-x86_64-bios-second-stage"
3+
version = "0.1.0"
4+
authors = ["Philipp Oppermann <[email protected]>"]
5+
edition = "2021"
6+
7+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
8+
9+
[dependencies]

bios/second_stage/build.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
use std::path::Path;
2+
3+
fn main() {
4+
let local_path = Path::new(env!("CARGO_MANIFEST_DIR"));
5+
println!(
6+
"cargo:rustc-link-arg-bins=--script={}",
7+
local_path.join("second-stage-link.ld").display()
8+
)
9+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
ENTRY(_start)
2+
3+
SECTIONS {
4+
. = 0x7c00 + 512;
5+
.text :
6+
{
7+
*(.text .text.*)
8+
}
9+
.rodata :
10+
{
11+
*(.rodata .rodata.*)
12+
}
13+
.data :
14+
{
15+
*(.rodata .rodata.*)
16+
*(.data .data.*)
17+
*(.got .got.*)
18+
}
19+
}

0 commit comments

Comments
 (0)