Skip to content

Commit 1b3fee4

Browse files
author
T. Andrew Davis
committed
Add bare-metal entry point and network stack implementation
- Implemented the bare-metal entry point in `baremetal.rs`, which includes stack allocation, exit boot services, and platform initialization. - Defined structures for framebuffer info and bare-metal entry configuration. - Established a static state for framebuffer info and a flag to indicate bare-metal mode. - Created the network stack entry point in `entry.rs`, handling NIC scanning, driver initialization, and download operations. - Introduced configuration structures for network downloads and defined results for download operations. - Implemented functions to run downloads with VirtIO and Intel e1000e drivers, including error handling and logging.
1 parent 6d586eb commit 1b3fee4

14 files changed

Lines changed: 1313 additions & 655 deletions

File tree

bootloader/src/baremetal.rs

Lines changed: 325 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,325 @@
1+
//! Bare-metal platform entry point.
2+
//!
3+
//! This module contains THE entry point to our bare-metal world.
4+
//! Once `enter_baremetal()` is called, we NEVER return to UEFI.
5+
//!
6+
//! # Architecture
7+
//!
8+
//! ```text
9+
//! efi_main (UEFI world)
10+
//! │
11+
//! ├── Gather GOP info (framebuffer)
12+
//! ├── Gather system table pointer
13+
//! │
14+
//! └── enter_baremetal() ──────────────────────────────────────────────┐
15+
//! │
16+
//! ┌───────────────────────────────────────────────────────────────┘
17+
//! │
18+
//! ▼ BARE-METAL WORLD (never returns)
19+
//! │
20+
//! ├── Allocate stack (from UEFI, as LoaderData)
21+
//! ├── ExitBootServices
22+
//! ├── Switch to our stack
23+
//! │
24+
//! ├── hwinit::platform_init_selfcontained()
25+
//! │ └── Memory, GDT, IDT, PIC, heap, TSC, DMA, bus mastering
26+
//! │
27+
//! ├── Initialize framebuffer display
28+
//! │
29+
//! └── Main TUI loop (forever)
30+
//! │
31+
//! ├── Render menus
32+
//! ├── Handle input
33+
//! ├── Download ISOs (network state machine)
34+
//! ├── Install distros (storage state machine)
35+
//! └── etc.
36+
//! ```
37+
//!
38+
//! # The Hard Border
39+
//!
40+
//! This is an INVARIANT: nothing from pre-EBS orchestrates post-EBS.
41+
//! We call EBS ourselves, we own everything after.
42+
//!
43+
//! The only data that crosses the border:
44+
//! - UEFI system table pointer (to call EBS)
45+
//! - GOP framebuffer info (address, resolution, format)
46+
//! - Image handle (for EBS)
47+
48+
#![allow(dead_code)]
49+
50+
use core::sync::atomic::{AtomicBool, Ordering};
51+
52+
// ═══════════════════════════════════════════════════════════════════════════
53+
// TYPES THAT CROSS THE BORDER (minimal, raw data only)
54+
// ═══════════════════════════════════════════════════════════════════════════
55+
56+
/// Framebuffer info gathered from GOP before EBS.
57+
/// This is raw data, no UEFI types.
58+
#[derive(Clone, Copy)]
59+
#[repr(C)]
60+
pub struct FramebufferInfo {
61+
/// Physical address of framebuffer
62+
pub base: u64,
63+
/// Size in bytes
64+
pub size: usize,
65+
/// Width in pixels
66+
pub width: u32,
67+
/// Height in pixels
68+
pub height: u32,
69+
/// Stride in pixels (pixels per scan line)
70+
pub stride: u32,
71+
/// Pixel format (0=RGBX, 1=BGRX, 2=BitMask, 3=BltOnly)
72+
pub format: u32,
73+
}
74+
75+
/// Minimal config passed to bare-metal entry.
76+
/// Raw pointers and values only - no UEFI types leak across.
77+
#[repr(C)]
78+
pub struct BaremetalEntryConfig {
79+
/// UEFI image handle (opaque, for EBS call)
80+
pub image_handle: *mut (),
81+
/// UEFI system table (opaque, for EBS call)
82+
pub system_table: *const (),
83+
/// GOP framebuffer info
84+
pub framebuffer: FramebufferInfo,
85+
}
86+
87+
// ═══════════════════════════════════════════════════════════════════════════
88+
// STATIC STATE (for post-EBS access)
89+
// ═══════════════════════════════════════════════════════════════════════════
90+
91+
/// Framebuffer info, available after entering bare-metal.
92+
static mut FRAMEBUFFER_INFO: FramebufferInfo = FramebufferInfo {
93+
base: 0,
94+
size: 0,
95+
width: 0,
96+
height: 0,
97+
stride: 0,
98+
format: 0,
99+
};
100+
101+
/// Flag indicating we're in bare-metal mode.
102+
static BAREMETAL_MODE: AtomicBool = AtomicBool::new(false);
103+
104+
/// Check if we're in bare-metal mode.
105+
pub fn is_baremetal() -> bool {
106+
BAREMETAL_MODE.load(Ordering::Relaxed)
107+
}
108+
109+
/// Get framebuffer info (only valid after entering bare-metal).
110+
pub fn get_framebuffer_info() -> Option<FramebufferInfo> {
111+
if is_baremetal() {
112+
Some(unsafe { FRAMEBUFFER_INFO })
113+
} else {
114+
None
115+
}
116+
}
117+
118+
// ═══════════════════════════════════════════════════════════════════════════
119+
// THE ENTRY POINT
120+
// ═══════════════════════════════════════════════════════════════════════════
121+
122+
/// Enter bare-metal world. NEVER RETURNS.
123+
///
124+
/// This function:
125+
/// 1. Allocates our own stack (from UEFI pool)
126+
/// 2. Calls ExitBootServices
127+
/// 3. Switches to our stack
128+
/// 4. Runs hwinit
129+
/// 5. Runs our platform forever
130+
///
131+
/// # Safety
132+
/// - Must be called from UEFI context with valid system table
133+
/// - NEVER RETURNS
134+
/// - All UEFI resources become invalid after this
135+
pub unsafe fn enter_baremetal(config: BaremetalEntryConfig) -> ! {
136+
use morpheus_hwinit::serial::puts;
137+
138+
// Save framebuffer info for post-EBS access
139+
FRAMEBUFFER_INFO = config.framebuffer;
140+
141+
// ─────────────────────────────────────────────────────────────────────
142+
// STEP 1: Allocate stack from UEFI (as LoaderData, survives EBS)
143+
// ─────────────────────────────────────────────────────────────────────
144+
const STACK_SIZE: usize = 256 * 1024; // 256KB stack
145+
const STACK_PAGES: usize = (STACK_SIZE + 4095) / 4096;
146+
147+
// Get boot services from system table
148+
#[repr(C)]
149+
struct MinimalSystemTable {
150+
_header: [u8; 24],
151+
_firmware_vendor: *const u16,
152+
_firmware_revision: u32,
153+
_console_in_handle: *const (),
154+
_con_in: *mut (),
155+
_console_out_handle: *const (),
156+
_con_out: *mut (),
157+
_stderr_handle: *const (),
158+
_stderr: *const (),
159+
_runtime_services: *const (),
160+
boot_services: *const MinimalBootServices,
161+
}
162+
163+
#[repr(C)]
164+
struct MinimalBootServices {
165+
_header: [u8; 24],
166+
_raise_tpl: usize,
167+
_restore_tpl: usize,
168+
allocate_pages: extern "efiapi" fn(
169+
alloc_type: u32,
170+
memory_type: u32,
171+
pages: usize,
172+
memory: *mut u64,
173+
) -> usize,
174+
_free_pages: usize,
175+
get_memory_map: extern "efiapi" fn(
176+
memory_map_size: *mut usize,
177+
memory_map: *mut u8,
178+
map_key: *mut usize,
179+
descriptor_size: *mut usize,
180+
descriptor_version: *mut u32,
181+
) -> usize,
182+
_allocate_pool: usize,
183+
_free_pool: usize,
184+
// ... many more fields before exit_boot_services
185+
_padding: [usize; 18], // Skip to exit_boot_services
186+
exit_boot_services: extern "efiapi" fn(
187+
image_handle: *mut (),
188+
map_key: usize,
189+
) -> usize,
190+
}
191+
192+
let st = &*(config.system_table as *const MinimalSystemTable);
193+
let bs = &*st.boot_services;
194+
195+
// Allocate stack pages
196+
let mut stack_base: u64 = 0;
197+
let status = (bs.allocate_pages)(
198+
0, // AllocateAnyPages
199+
2, // EfiLoaderData (survives EBS)
200+
STACK_PAGES,
201+
&mut stack_base,
202+
);
203+
204+
if status != 0 {
205+
// Fatal: can't allocate stack
206+
loop { core::hint::spin_loop(); }
207+
}
208+
209+
let stack_top = stack_base + STACK_SIZE as u64;
210+
211+
// ─────────────────────────────────────────────────────────────────────
212+
// STEP 2: Get memory map and call ExitBootServices
213+
// ─────────────────────────────────────────────────────────────────────
214+
215+
// Static buffer for memory map (must survive stack switch)
216+
static mut MMAP_BUF: [u8; 32768] = [0u8; 32768];
217+
static mut MMAP_SIZE: usize = 0;
218+
static mut DESC_SIZE: usize = 0;
219+
static mut DESC_VERSION: u32 = 0;
220+
221+
let mut map_size = MMAP_BUF.len();
222+
let mut map_key: usize = 0;
223+
let mut desc_size: usize = 0;
224+
let mut desc_version: u32 = 0;
225+
226+
// Get memory map
227+
let status = (bs.get_memory_map)(
228+
&mut map_size,
229+
MMAP_BUF.as_mut_ptr(),
230+
&mut map_key,
231+
&mut desc_size,
232+
&mut desc_version,
233+
);
234+
235+
if status != 0 {
236+
loop { core::hint::spin_loop(); }
237+
}
238+
239+
// Save for post-EBS
240+
MMAP_SIZE = map_size;
241+
DESC_SIZE = desc_size;
242+
DESC_VERSION = desc_version;
243+
244+
// Exit boot services
245+
let status = (bs.exit_boot_services)(config.image_handle, map_key);
246+
if status != 0 {
247+
// EBS failed - might need to re-get memory map
248+
// For now, just hang
249+
loop { core::hint::spin_loop(); }
250+
}
251+
252+
// ═══════════════════════════════════════════════════════════════════════
253+
// POINT OF NO RETURN - UEFI IS GONE
254+
// ═══════════════════════════════════════════════════════════════════════
255+
256+
BAREMETAL_MODE.store(true, Ordering::SeqCst);
257+
258+
// ─────────────────────────────────────────────────────────────────────
259+
// STEP 3: Switch to our stack
260+
// ─────────────────────────────────────────────────────────────────────
261+
core::arch::asm!(
262+
"mov rsp, {0}",
263+
in(reg) stack_top,
264+
options(nostack)
265+
);
266+
267+
// ─────────────────────────────────────────────────────────────────────
268+
// STEP 4: Enter platform (never returns)
269+
// ─────────────────────────────────────────────────────────────────────
270+
271+
// Serial init for debug
272+
puts("\n");
273+
puts("╔══════════════════════════════════════════════════════════════╗\n");
274+
puts("║ MORPHEUS BARE-METAL PLATFORM ║\n");
275+
puts("║ UEFI is gone. We own the machine. ║\n");
276+
puts("╚══════════════════════════════════════════════════════════════╝\n");
277+
puts("\n");
278+
279+
// Run hwinit
280+
let hwinit_config = morpheus_hwinit::SelfContainedConfig {
281+
memory_map_ptr: MMAP_BUF.as_ptr(),
282+
memory_map_size: MMAP_SIZE,
283+
descriptor_size: DESC_SIZE,
284+
descriptor_version: DESC_VERSION,
285+
};
286+
287+
let platform = match morpheus_hwinit::platform_init_selfcontained(hwinit_config) {
288+
Ok(p) => p,
289+
Err(e) => {
290+
puts("[FATAL] Platform init failed\n");
291+
loop { core::hint::spin_loop(); }
292+
}
293+
};
294+
295+
// ─────────────────────────────────────────────────────────────────────
296+
// STEP 5: Initialize our display
297+
// ─────────────────────────────────────────────────────────────────────
298+
// TODO: Initialize framebuffer display driver
299+
// morpheus_display::init_framebuffer(FRAMEBUFFER_INFO);
300+
301+
puts("[PLATFORM] Display: TODO - framebuffer init\n");
302+
303+
// ─────────────────────────────────────────────────────────────────────
304+
// STEP 6: Main platform loop (forever)
305+
// ─────────────────────────────────────────────────────────────────────
306+
307+
// TODO: This will be our TUI main loop running on our framebuffer
308+
// For now, just a placeholder
309+
310+
puts("\n");
311+
puts("╔══════════════════════════════════════════════════════════════╗\n");
312+
puts("║ PLATFORM READY ║\n");
313+
puts("║ ║\n");
314+
puts("║ Next steps: ║\n");
315+
puts("║ 1. Initialize framebuffer display ║\n");
316+
puts("║ 2. Port TUI to our display ║\n");
317+
puts("║ 3. Implement input driver (PS/2 keyboard) ║\n");
318+
puts("║ 4. Run menu system ║\n");
319+
puts("╚══════════════════════════════════════════════════════════════╝\n");
320+
puts("\n");
321+
322+
loop {
323+
core::hint::spin_loop();
324+
}
325+
}

bootloader/src/boot/mod.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ pub use memory::{
2424
allocate_boot_params, allocate_cmdline, allocate_kernel_memory, load_kernel_image,
2525
};
2626
pub use network_boot::{
27-
// New architecture entry point
27+
// NEW: Bare-metal world entry (hwinit is our ground truth)
28+
enter_baremetal_world, BaremetalEntryConfig, DownloadRequest, BaremetalResult,
29+
// Deprecated wrapper (calls enter_baremetal_world)
30+
enter_selfcontained_download, SelfContainedDownloadConfig,
31+
// Legacy architecture entry point
2832
enter_baremetal_download, BaremetalConfig, RunResult,
2933
// Legacy compatibility (will panic with migration message)
3034
enter_network_boot, enter_network_boot_url,

0 commit comments

Comments
 (0)