Skip to content

Commit 226e55d

Browse files
committed
Make executable smaller and improve screen output
1 parent 708ee38 commit 226e55d

File tree

6 files changed

+164
-77
lines changed

6 files changed

+164
-77
lines changed

real_mode/first_stage/src/boot.s

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,13 @@ unreal_mode:
6565
mov word ptr ds:[eax], bx
6666

6767
check_int13h_extensions:
68+
push 'y' # error code
6869
mov ah, 0x41
6970
mov bx, 0x55aa
7071
# dl contains drive number
7172
int 0x13
72-
jc no_int13h_extensions
73+
jc fail
74+
pop ax # pop error code again
7375

7476
rust:
7577
# push arguments

real_mode/first_stage/src/dap.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,13 @@ impl DiskAddressPacket {
4141

4242
pub unsafe fn perform_load(&self, disk_number: u16) {
4343
let self_addr = self as *const Self as u16;
44-
asm!("mov {1:x}, si",
44+
asm!(
45+
"push 0x7a", // error code `z`, passed to `fail` on error
46+
"mov {1:x}, si",
4547
"mov si, {0:x}",
4648
"int 0x13",
47-
"jc dap_load_failed",
49+
"jc fail",
50+
"pop si", // remove error code again
4851
"mov si, {1:x}",
4952
in(reg) self_addr,
5053
out(reg) _,

real_mode/first_stage/src/fail.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
use core::arch::asm;
2+
3+
pub trait UnwrapOrFail {
4+
type Out;
5+
6+
fn unwrap_or_fail(self, code: u8) -> Self::Out;
7+
}
8+
9+
impl<T> UnwrapOrFail for Option<T> {
10+
type Out = T;
11+
12+
fn unwrap_or_fail(self, code: u8) -> Self::Out {
13+
match self {
14+
Some(v) => v,
15+
None => fail(code),
16+
}
17+
}
18+
}
19+
20+
impl<T, E> UnwrapOrFail for Result<T, E> {
21+
type Out = T;
22+
23+
fn unwrap_or_fail(self, code: u8) -> Self::Out {
24+
match self {
25+
Ok(v) => v,
26+
Err(_) => fail(code),
27+
}
28+
}
29+
}
30+
31+
#[no_mangle]
32+
pub extern "C" fn print_char(c: u8) {
33+
let ax = u16::from(c) | 0x0e00;
34+
unsafe {
35+
asm!("int 0x10", in("ax") ax, in("bx") 0);
36+
}
37+
}
38+
39+
#[cold]
40+
#[inline(never)]
41+
#[no_mangle]
42+
pub extern "C" fn fail(code: u8) -> ! {
43+
print_char(b'!');
44+
print_char(code);
45+
loop {
46+
hlt()
47+
}
48+
}
49+
50+
fn hlt() {
51+
unsafe {
52+
asm!("hlt");
53+
}
54+
}
55+
56+
#[panic_handler]
57+
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
58+
fail(b'P');
59+
}

real_mode/first_stage/src/fat.rs

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
// based on https://github.com/rafalh/rust-fatfs/
22

3+
use super::split_array_ref;
4+
35
pub(crate) struct BootSector {
46
bootjmp: [u8; 3],
57
oem_name: [u8; 8],
@@ -157,13 +159,3 @@ impl BiosParameterBlock {
157159
self.sectors_per_fat_16 == 0
158160
}
159161
}
160-
161-
/// Taken from https://github.com/rust-lang/rust/blob/e100ec5bc7cd768ec17d75448b29c9ab4a39272b/library/core/src/slice/mod.rs#L1673-L1677
162-
///
163-
/// TODO replace with `split_array` feature in stdlib as soon as it's stabilized,
164-
/// see https://github.com/rust-lang/rust/issues/90091
165-
fn split_array_ref<const N: usize, T>(slice: &[T]) -> (&[T; N], &[T]) {
166-
let (a, b) = slice.split_at(N);
167-
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at)
168-
unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
169-
}

real_mode/first_stage/src/main.rs

Lines changed: 25 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,13 @@ use core::{
55
arch::{asm, global_asm},
66
slice,
77
};
8+
use fail::{fail, print_char, UnwrapOrFail};
89
use mbr::MasterBootRecord;
910

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

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

@@ -23,34 +25,38 @@ fn mbr_start() -> *const u8 {
2325

2426
#[no_mangle]
2527
pub extern "C" fn first_stage(disk_number: u16) {
28+
print_char(b'1');
2629
let bytes = &unsafe { slice::from_raw_parts(mbr_start(), 512) };
27-
let mbr = MasterBootRecord::from_bytes(bytes);
30+
let partition = mbr::get_partition(bytes, 0);
2831

29-
let partition = mbr
30-
.partition_table_entries()
31-
.get(0)
32-
.unwrap_or_else(|| panic!());
33-
34-
let partition_buf = u16::try_from(mbr_start() as usize).unwrap_or_else(|_| panic!()) + 512;
32+
print_char(b'2');
33+
let partition_buf = u16::try_from(mbr_start() as usize).unwrap_or_fail(b'a') + 512;
3534

3635
// load first partition into buffer
3736
// TODO: only load headers
3837
let dap = dap::DiskAddressPacket::from_lba(
3938
partition_buf,
4039
partition.logical_block_address.into(),
41-
partition.sector_count.try_into().unwrap(),
40+
partition.sector_count.try_into().unwrap_or_fail(b'b'),
4241
);
4342
unsafe {
4443
dap.perform_load(disk_number);
4544
}
45+
if partition.sector_count == 0 {
46+
fail(b'c');
47+
}
48+
49+
print_char(b'3');
4650

4751
// try to parse FAT file system
4852
let fat_slice = unsafe {
4953
slice::from_raw_parts(
5054
partition_buf as *const u8,
51-
usize::try_from(partition.sector_count).unwrap_or_else(|_| panic!()) * 512,
55+
usize::try_from(partition.sector_count).unwrap_or_else(|_| fail(b'a')) * 512,
5256
)
5357
};
58+
59+
print_char(b'4');
5460
let boot_sector = fat::BootSector::deserialize(fat_slice);
5561

5662
// TODO: get root dir
@@ -81,42 +87,15 @@ fn load_second_stage(
8187
unsafe { dap.perform_load(disk_number) }
8288
}
8389

84-
#[no_mangle]
85-
pub extern "C" fn print_char(c: u8) {
86-
let ax = u16::from(c) | 0x0e00;
87-
unsafe {
88-
asm!("int 0x10", in("ax") ax, in("bx") 0);
89-
}
90-
}
91-
92-
#[no_mangle]
93-
pub extern "C" fn dap_load_failed() -> ! {
94-
err(b'1');
95-
}
96-
97-
#[no_mangle]
98-
pub extern "C" fn no_int13h_extensions() -> ! {
99-
err(b'2');
100-
}
101-
102-
#[cold]
103-
fn err(code: u8) -> ! {
104-
for &c in b"Err:" {
105-
print_char(c);
106-
}
107-
print_char(code);
108-
loop {
109-
hlt()
90+
/// Taken from https://github.com/rust-lang/rust/blob/e100ec5bc7cd768ec17d75448b29c9ab4a39272b/library/core/src/slice/mod.rs#L1673-L1677
91+
///
92+
/// TODO replace with `split_array` feature in stdlib as soon as it's stabilized,
93+
/// see https://github.com/rust-lang/rust/issues/90091
94+
fn split_array_ref<const N: usize, T>(slice: &[T]) -> (&[T; N], &[T]) {
95+
if N > slice.len() {
96+
fail(b'S');
11097
}
111-
}
112-
113-
fn hlt() {
114-
unsafe {
115-
asm!("hlt");
116-
}
117-
}
118-
119-
#[panic_handler]
120-
pub fn panic(_info: &core::panic::PanicInfo) -> ! {
121-
err(b'P');
98+
let (a, b) = slice.split_at(N);
99+
// SAFETY: a points to [T; N]? Yes it's [T] of length N (checked by split_at)
100+
unsafe { (&*(a.as_ptr() as *const [T; N]), b) }
122101
}

real_mode/first_stage/src/mbr.rs

Lines changed: 70 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,36 @@
11
// Based on https://docs.rs/mbr-nostd
22

3+
use super::fail::{fail, UnwrapOrFail};
4+
5+
pub fn get_partition(buffer: &[u8], index: usize) -> PartitionTableEntry {
6+
if buffer.len() < BUFFER_SIZE {
7+
fail(b'a');
8+
} else if buffer.get(BUFFER_SIZE - SUFFIX_BYTES.len()..BUFFER_SIZE) != Some(&SUFFIX_BYTES[..]) {
9+
fail(b'b');
10+
}
11+
12+
let offset = TABLE_OFFSET + index * ENTRY_SIZE;
13+
let buffer = buffer.get(offset..).unwrap_or_fail(b'c');
14+
15+
let partition_type = *buffer.get(4).unwrap_or_fail(b'd');
16+
17+
let lba = u32::from_le_bytes(
18+
buffer
19+
.get(8..)
20+
.and_then(|s| s.get(..4))
21+
.and_then(|s| s.try_into().ok())
22+
.unwrap_or_fail(b'e'),
23+
);
24+
let len = u32::from_le_bytes(
25+
buffer
26+
.get(12..)
27+
.and_then(|s| s.get(..4))
28+
.and_then(|s| s.try_into().ok())
29+
.unwrap_or_fail(b'f'),
30+
);
31+
PartitionTableEntry::new(partition_type, lba, len)
32+
}
33+
334
/// A struct representing an MBR partition table.
435
pub struct MasterBootRecord {
536
entries: [PartitionTableEntry; MAX_ENTRIES],
@@ -16,26 +47,38 @@ impl MasterBootRecord {
1647
1748
pub fn from_bytes(buffer: &[u8]) -> MasterBootRecord {
1849
if buffer.len() < BUFFER_SIZE {
19-
panic!();
20-
} else if buffer[BUFFER_SIZE - SUFFIX_BYTES.len()..BUFFER_SIZE] != SUFFIX_BYTES[..] {
21-
panic!();
50+
fail(b'1');
51+
} else if buffer.get(BUFFER_SIZE - SUFFIX_BYTES.len()..BUFFER_SIZE)
52+
!= Some(&SUFFIX_BYTES[..])
53+
{
54+
fail(b'2');
2255
}
2356
let mut entries = [PartitionTableEntry::empty(); MAX_ENTRIES];
57+
2458
for idx in 0..MAX_ENTRIES {
2559
let offset = TABLE_OFFSET + idx * ENTRY_SIZE;
26-
let partition_type = PartitionType::from_mbr_tag_byte(buffer[offset + 4]);
27-
if let PartitionType::Unknown(c) = partition_type {
28-
panic!();
29-
}
30-
let lba =
31-
u32::from_le_bytes(buffer[offset + 8..].try_into().unwrap_or_else(|_| panic!()));
60+
let buffer = buffer.get(offset..).unwrap_or_fail(b'8');
61+
62+
let partition_type = *buffer.get(4).unwrap_or_fail(b'4');
63+
64+
let lba = u32::from_le_bytes(
65+
buffer
66+
.get(8..)
67+
.and_then(|s| s.get(..4))
68+
.and_then(|s| s.try_into().ok())
69+
.unwrap_or_fail(b'5'),
70+
);
3271
let len = u32::from_le_bytes(
33-
buffer[offset + 12..]
34-
.try_into()
35-
.unwrap_or_else(|_| panic!()),
72+
buffer
73+
.get(12..)
74+
.and_then(|s| s.get(..4))
75+
.and_then(|s| s.try_into().ok())
76+
.unwrap_or_fail(b'6'),
3677
);
37-
entries[idx] = PartitionTableEntry::new(partition_type, lba, len);
78+
*entries.get_mut(idx).unwrap_or_fail(b'7') =
79+
PartitionTableEntry::new(partition_type, lba, len);
3880
}
81+
3982
MasterBootRecord { entries }
4083
}
4184

@@ -45,7 +88,7 @@ impl MasterBootRecord {
4588
}
4689

4790
/// The type of a particular partition.
48-
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
91+
#[derive(Copy, Clone, Eq, PartialEq)]
4992
pub enum PartitionType {
5093
Unused,
5194
Unknown(u8),
@@ -72,13 +115,22 @@ impl PartitionType {
72115
_ => PartitionType::Unknown(tag),
73116
}
74117
}
118+
119+
pub fn known_type(tag: u8) -> bool {
120+
match tag {
121+
0x0 | 0x01 | 0x04 | 0x06 | 0x0e | 0x0b | 0x0c | 0x1b | 0x1c | 0x83 | 0x07 | 0xaf => {
122+
true
123+
}
124+
_ => false,
125+
}
126+
}
75127
}
76128

77129
/// An entry in a partition table.
78-
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
130+
#[derive(Copy, Clone, Eq, PartialEq)]
79131
pub struct PartitionTableEntry {
80132
/// The type of partition in this entry.
81-
pub partition_type: PartitionType,
133+
pub partition_type: u8,
82134

83135
/// The index of the first block of this entry.
84136
pub logical_block_address: u32,
@@ -89,7 +141,7 @@ pub struct PartitionTableEntry {
89141

90142
impl PartitionTableEntry {
91143
pub fn new(
92-
partition_type: PartitionType,
144+
partition_type: u8,
93145
logical_block_address: u32,
94146
sector_count: u32,
95147
) -> PartitionTableEntry {
@@ -101,6 +153,6 @@ impl PartitionTableEntry {
101153
}
102154

103155
pub fn empty() -> PartitionTableEntry {
104-
PartitionTableEntry::new(PartitionType::Unused, 0, 0)
156+
PartitionTableEntry::new(0, 0, 0)
105157
}
106158
}

0 commit comments

Comments
 (0)