forked from LowLevelDevs/FoundryOS
Compare commits
6 Commits
43b1db41ca
...
dev
| Author | SHA1 | Date | |
|---|---|---|---|
| d9b66f8008 | |||
| 2be5b63af7 | |||
| 7e62bdae35 | |||
| b8aa203c05 | |||
| 9f83c5f295 | |||
| 914cb4e409 |
+9
-8
@@ -9,15 +9,16 @@ build-std-features = ["compiler-builtins-mem"]
|
|||||||
[env]
|
[env]
|
||||||
RUST_TARGET_PATH = { value = "kernel", relative = true }
|
RUST_TARGET_PATH = { value = "kernel", relative = true }
|
||||||
|
|
||||||
[target.x86_64-kernel]
|
# Run in debug mode.
|
||||||
runner = "scripts/run.sh"
|
[target.'cfg(all(target_arch = "x86_64", target_os = "none", debug_assertions))']
|
||||||
|
runner = "scripts/run_debug.sh"
|
||||||
|
|
||||||
[registry]
|
# Otherwise we run in release mode.
|
||||||
default = "gitea"
|
[target.'cfg(all(target_arch = "x86_64", target_os = "none", not(debug_assertions)))']
|
||||||
|
runner = "scripts/run_release.sh"
|
||||||
|
|
||||||
|
# [registry]
|
||||||
|
# default = "gitea"
|
||||||
|
|
||||||
[registries.gitea]
|
[registries.gitea]
|
||||||
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
index = "sparse+https://git.zxq5.dev/api/packages/OsDev/cargo/" # Sparse index
|
||||||
# index = "https://gitea.example.com/{owner}/_cargo-index.git" # Git
|
|
||||||
|
|
||||||
# [net]
|
|
||||||
# git-fetch-with-cli = true
|
|
||||||
|
|||||||
Vendored
+2
-2
@@ -4,6 +4,6 @@
|
|||||||
"[rust]": {
|
"[rust]": {
|
||||||
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
"editor.defaultFormatter": "rust-lang.rust-analyzer",
|
||||||
"editor.formatOnSave": true
|
"editor.formatOnSave": true
|
||||||
}
|
},
|
||||||
|
"rust-analyzer.check.command": "clippy",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+5
-2
@@ -4,12 +4,15 @@ resolver = "2"
|
|||||||
|
|
||||||
[workspace.package]
|
[workspace.package]
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2024"
|
||||||
authors = ["The Foundry OS contributors"]
|
authors = ["The FoundryOS Contributors"]
|
||||||
|
|
||||||
[profile.dev]
|
[profile.dev]
|
||||||
opt-level = "z"
|
opt-level = "z"
|
||||||
debug = true
|
debug = true
|
||||||
|
# Leave this on to force Cargo to use the debug runner,
|
||||||
|
# which adds the necessary flags for GDB debugging if you
|
||||||
|
# set $USE_GDB (to any value) e.g. USE_GDB=1 cargo run
|
||||||
debug-assertions = true
|
debug-assertions = true
|
||||||
overflow-checks = true
|
overflow-checks = true
|
||||||
lto = false
|
lto = false
|
||||||
|
|||||||
@@ -5,18 +5,20 @@
|
|||||||
Here are some simple steps to get started:
|
Here are some simple steps to get started:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# If you have not yet cloned the repo:
|
git clone https://git.zxq5.dev/OsDev/FoundryOS.git
|
||||||
git clone --recurse-submodules https://git.zxq5.dev/OsDev/FoundryOS.git
|
|
||||||
# If you already cloned the repo:
|
|
||||||
git submodule update --init --recursive
|
|
||||||
|
|
||||||
cargo build
|
|
||||||
# This will build the binaries if required - no need to call cargo build.
|
|
||||||
cargo run
|
cargo run
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Running in GDB:
|
||||||
|
```sh
|
||||||
|
USE_GDB=1 cargo run
|
||||||
|
```
|
||||||
|
|
||||||
### Build dependencies
|
### Build dependencies
|
||||||
|
|
||||||
* jq: checks whether the app is to be run in debugging mode.
|
|
||||||
* libisoburn: creates ISO images to be booted from.
|
* libisoburn: creates ISO images to be booted from.
|
||||||
* qemu: to run the kernel.
|
* qemu: to run the kernel.
|
||||||
|
|
||||||
|
Optionally,
|
||||||
|
|
||||||
|
* GDB: for debugging the kernel.
|
||||||
+4
-2
@@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "foundry_os"
|
name = "foundry_os"
|
||||||
version = "0.1.0"
|
edition.workspace = true
|
||||||
edition = "2021"
|
version.workspace = true
|
||||||
|
authors.workspace = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
limine = "0.3.1"
|
limine = "0.3.1"
|
||||||
@@ -20,3 +21,4 @@ default = []
|
|||||||
[[bin]]
|
[[bin]]
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
path = "src/main.rs"
|
path = "src/main.rs"
|
||||||
|
test = false
|
||||||
|
|||||||
@@ -7,11 +7,11 @@ use limine::{
|
|||||||
use spin::Lazy;
|
use spin::Lazy;
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[link_section = ".requests"]
|
#[unsafe(link_section = ".requests")]
|
||||||
static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new();
|
static MEMORY_MAP_REQUEST: MemoryMapRequest = MemoryMapRequest::new();
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[link_section = ".requests"]
|
#[unsafe(link_section = ".requests")]
|
||||||
static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new();
|
static HIGHER_HALF_DIRECT_MAP_REQUEST: HhdmRequest = HhdmRequest::new();
|
||||||
|
|
||||||
/// ```rs
|
/// ```rs
|
||||||
@@ -26,7 +26,7 @@ pub static PHYSICAL_MEMORY_OFFSET: Lazy<u64> = Lazy::new(|| {
|
|||||||
});
|
});
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[link_section = ".requests"]
|
#[unsafe(link_section = ".requests")]
|
||||||
static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new();
|
static KERNEL_ADDRESS_REQUEST: KernelAddressRequest = KernelAddressRequest::new();
|
||||||
|
|
||||||
/// Converts virtual addresses in the kernel to a physical address like this:
|
/// Converts virtual addresses in the kernel to a physical address like this:
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
// use lib_alloc::allocator::FoundryAllocator;
|
// use lib_alloc::allocator::FoundryAllocator;
|
||||||
use limine::{memory_map::EntryType, response::MemoryMapResponse};
|
use limine::{memory_map::EntryType, response::MemoryMapResponse};
|
||||||
use x86_64::{
|
use x86_64::{
|
||||||
|
PhysAddr,
|
||||||
|
VirtAddr,
|
||||||
// addr,
|
// addr,
|
||||||
registers::control::Cr3,
|
registers::control::Cr3,
|
||||||
structures::paging::{
|
structures::paging::{
|
||||||
@@ -11,8 +13,6 @@ use x86_64::{
|
|||||||
PhysFrame,
|
PhysFrame,
|
||||||
Size4KiB,
|
Size4KiB,
|
||||||
},
|
},
|
||||||
PhysAddr,
|
|
||||||
VirtAddr,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Returns a mutable reference to the current level 4 page table.
|
/// Returns a mutable reference to the current level 4 page table.
|
||||||
@@ -27,7 +27,7 @@ unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut Page
|
|||||||
|
|
||||||
let phys_addr = level_4_frame.start_address();
|
let phys_addr = level_4_frame.start_address();
|
||||||
let virt = phys_addr.as_u64() + physical_memory_offset.as_u64();
|
let virt = phys_addr.as_u64() + physical_memory_offset.as_u64();
|
||||||
&mut *(virt as *mut PageTable)
|
unsafe { &mut *(virt as *mut PageTable) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initializes the `OffsetPageTable` for the current CPU architecture.
|
/// Initializes the `OffsetPageTable` for the current CPU architecture.
|
||||||
@@ -49,8 +49,10 @@ unsafe fn active_l4_table(physical_memory_offset: VirtAddr) -> &'static mut Page
|
|||||||
/// Returns an `OffsetPageTable` that allows for manipulation of the page
|
/// Returns an `OffsetPageTable` that allows for manipulation of the page
|
||||||
/// tables for the current CPU architecture.
|
/// tables for the current CPU architecture.
|
||||||
pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> {
|
pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> {
|
||||||
let l4_table = active_l4_table(physical_memory_offset);
|
unsafe {
|
||||||
OffsetPageTable::new(l4_table, physical_memory_offset)
|
let l4_table = active_l4_table(physical_memory_offset);
|
||||||
|
OffsetPageTable::new(l4_table, physical_memory_offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) struct FoundryOSFrameAllocator {
|
pub(crate) struct FoundryOSFrameAllocator {
|
||||||
@@ -76,7 +78,7 @@ impl FoundryOSFrameAllocator {
|
|||||||
/// Yields one `PhysFrame` for each available 4KiB frame in the memory map.
|
/// Yields one `PhysFrame` for each available 4KiB frame in the memory map.
|
||||||
///
|
///
|
||||||
/// This function is used to allocate frames for the pagemap.
|
/// This function is used to allocate frames for the pagemap.
|
||||||
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
|
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> + use<> {
|
||||||
let regions = self.memory_map.entries().iter();
|
let regions = self.memory_map.entries().iter();
|
||||||
let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE);
|
let usable_regions = regions.filter(|region| region.entry_type == EntryType::USABLE);
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -18,7 +18,7 @@ mod arch;
|
|||||||
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
/// Be sure to mark all limine requests with #[used], otherwise they may be removed by the compiler.
|
||||||
#[used]
|
#[used]
|
||||||
// The .requests section allows limine to find the requests faster and more safely.
|
// The .requests section allows limine to find the requests faster and more safely.
|
||||||
#[link_section = ".requests"]
|
#[unsafe(link_section = ".requests")]
|
||||||
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
static BASE_REVISION: BaseRevision = BaseRevision::new();
|
||||||
|
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
|
|||||||
+1
-1
@@ -9,7 +9,7 @@ use libk::{
|
|||||||
scheduling::task::{Executor, Task},
|
scheduling::task::{Executor, Task},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[no_mangle]
|
#[unsafe(no_mangle)]
|
||||||
extern "C" fn kmain() -> ! {
|
extern "C" fn kmain() -> ! {
|
||||||
println_log!(" [ Initialising Kernel Systems ] ");
|
println_log!(" [ Initialising Kernel Systems ] ");
|
||||||
if let Err(err) = foundry_os::boot() {
|
if let Err(err) = foundry_os::boot() {
|
||||||
|
|||||||
+35
-29
@@ -3,7 +3,7 @@ use spin::{Lazy, Mutex};
|
|||||||
use x86_64::instructions::interrupts;
|
use x86_64::instructions::interrupts;
|
||||||
|
|
||||||
pub use super::framebuffer::screensize_px;
|
pub use super::framebuffer::screensize_px;
|
||||||
use super::framebuffer::{Color, FRAMEBUFFER_WRITER};
|
use super::framebuffer::{Colour, FRAMEBUFFER_WRITER};
|
||||||
|
|
||||||
mod font;
|
mod font;
|
||||||
use font::FONT;
|
use font::FONT;
|
||||||
@@ -28,32 +28,39 @@ pub struct Writer {
|
|||||||
/// 8 pixels wide.
|
/// 8 pixels wide.
|
||||||
text_col: u32,
|
text_col: u32,
|
||||||
|
|
||||||
fg_color: Color,
|
fg_color: Colour,
|
||||||
bg_color: Color,
|
bg_color: Colour,
|
||||||
|
|
||||||
offset1: usize,
|
offset1: usize,
|
||||||
offset2: usize,
|
offset2: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Writer {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Writer {
|
impl Writer {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
FRAMEBUFFER_WRITER.lock().as_mut().map_or_else(
|
||||||
Self {
|
|| {
|
||||||
screen_width: writer.width() as u32 / 8,
|
panic!("Framebuffer writer not initialized.");
|
||||||
screen_height: writer.height() as u32 / 16,
|
},
|
||||||
|
|writer| Self {
|
||||||
|
screen_width: writer.width() / 8,
|
||||||
|
screen_height: writer.height() / 16,
|
||||||
text_line: 0,
|
text_line: 0,
|
||||||
text_col: 0,
|
text_col: 0,
|
||||||
fg_color: Color::White,
|
fg_color: Colour::White,
|
||||||
bg_color: Color::Black,
|
bg_color: Colour::Black,
|
||||||
offset1: 16,
|
offset1: 16,
|
||||||
offset2: 0,
|
offset2: 0,
|
||||||
}
|
},
|
||||||
} else {
|
)
|
||||||
panic!("Framebuffer writer not initialized");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn write_char(&mut self, c: u16) {
|
pub fn write_glyph(&mut self, c: u16) {
|
||||||
if c as u8 == b'\n' {
|
if c as u8 == b'\n' {
|
||||||
self.newline();
|
self.newline();
|
||||||
return;
|
return;
|
||||||
@@ -63,8 +70,7 @@ impl Writer {
|
|||||||
let data: &[u8] = &FONT[c as usize * 16..(c as usize + 1) * 16];
|
let data: &[u8] = &FONT[c as usize * 16..(c as usize + 1) * 16];
|
||||||
|
|
||||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
||||||
for row in 0..16 {
|
for (row, line) in data.iter().enumerate().take(16) {
|
||||||
let line: u8 = data[row];
|
|
||||||
for col in 0..8 {
|
for col in 0..8 {
|
||||||
let pixel_x: u32 = self.text_col * FONT_WIDTH + col;
|
let pixel_x: u32 = self.text_col * FONT_WIDTH + col;
|
||||||
let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32;
|
let pixel_y: u32 = self.text_line * FONT_HEIGHT + row as u32;
|
||||||
@@ -88,20 +94,20 @@ impl Writer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_offset(&mut self, offset1: usize, offset2: usize) {
|
pub const fn set_offset(&mut self, offset1: usize, offset2: usize) {
|
||||||
self.offset1 = offset1;
|
self.offset1 = offset1;
|
||||||
self.offset2 = offset2;
|
self.offset2 = offset2;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dimensions(&self) -> (u32, u32) {
|
pub const fn dimensions(&self) -> (u32, u32) {
|
||||||
(self.screen_width, self.screen_height)
|
(self.screen_width, self.screen_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn next_char(&mut self) {
|
pub const fn next_char(&mut self) {
|
||||||
self.text_col += 1;
|
self.text_col += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn newline(&mut self) {
|
pub const fn newline(&mut self) {
|
||||||
self.text_col = 0;
|
self.text_col = 0;
|
||||||
|
|
||||||
if self.text_line + 1 >= self.screen_height {
|
if self.text_line + 1 >= self.screen_height {
|
||||||
@@ -113,18 +119,18 @@ impl Writer {
|
|||||||
|
|
||||||
pub fn write_string(&mut self, s: &str) {
|
pub fn write_string(&mut self, s: &str) {
|
||||||
for c in s.chars() {
|
for c in s.chars() {
|
||||||
self.write_char(c as u16);
|
self.write_glyph(c as u16);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_colour(&mut self, fg: Color, bg: Color) {
|
pub const fn set_colour(&mut self, fg: Colour, bg: Colour) {
|
||||||
self.fg_color = fg;
|
self.fg_color = fg;
|
||||||
self.bg_color = bg;
|
self.bg_color = bg;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn reset_colour(&mut self) {
|
pub const fn reset_colour(&mut self) {
|
||||||
self.fg_color = Color::White;
|
self.fg_color = Colour::White;
|
||||||
self.bg_color = Color::Black;
|
self.bg_color = Colour::Black;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -135,7 +141,7 @@ impl core::fmt::Write for Writer {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write(args: fmt::Arguments, fg: Color, bg: Color) {
|
fn write(args: fmt::Arguments, fg: Colour, bg: Colour) {
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
|
|
||||||
interrupts::without_interrupts(|| {
|
interrupts::without_interrupts(|| {
|
||||||
@@ -148,19 +154,19 @@ fn write(args: fmt::Arguments, fg: Color, bg: Color) {
|
|||||||
|
|
||||||
pub fn _print(args: fmt::Arguments) {
|
pub fn _print(args: fmt::Arguments) {
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
write(args, Color::White, Color::Black);
|
write(args, Colour::White, Colour::Black);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _print_err(args: fmt::Arguments) {
|
pub fn _print_err(args: fmt::Arguments) {
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
write(args, Color::Red, Color::Black);
|
write(args, Colour::Red, Colour::Black);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn _print_log(args: fmt::Arguments) {
|
pub fn _print_log(args: fmt::Arguments) {
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
write(args, Color::Yellow, Color::Black);
|
write(args, Colour::Yellow, Colour::Black);
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+54
-38
@@ -1,52 +1,69 @@
|
|||||||
use limine::request::FramebufferRequest;
|
use limine::request::FramebufferRequest;
|
||||||
|
|
||||||
#[used]
|
#[used]
|
||||||
#[link_section = ".requests"]
|
#[unsafe(link_section = ".requests")]
|
||||||
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new();
|
||||||
|
|
||||||
mod colour;
|
mod colour;
|
||||||
|
|
||||||
pub use colour::Color;
|
pub use colour::Colour;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
|
|
||||||
use limine::framebuffer::Framebuffer;
|
use limine::framebuffer::Framebuffer;
|
||||||
use spin::{Lazy, Mutex};
|
use spin::{Lazy, Mutex};
|
||||||
|
|
||||||
// use crate::{colour::Color, FRAMEBUFFER_REQUEST};
|
|
||||||
|
|
||||||
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(|| {
|
pub static FRAMEBUFFER_WRITER: Lazy<Mutex<Option<FramebufferWriter>>> = Lazy::new(|| {
|
||||||
Mutex::new(
|
Mutex::new(FRAMEBUFFER_REQUEST.get_response().map_or_else(
|
||||||
if let Some(framebuffer_response) = FRAMEBUFFER_REQUEST.get_response() {
|
|| {
|
||||||
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
|
|
||||||
Some(FramebufferWriter::new(framebuffer))
|
|
||||||
} else {
|
|
||||||
panic!("Framebuffer request failed");
|
panic!("Framebuffer request failed");
|
||||||
},
|
},
|
||||||
)
|
|framebuffer_response| {
|
||||||
|
let framebuffer = framebuffer_response.framebuffers().next().unwrap();
|
||||||
|
Some(FramebufferWriter::new(framebuffer))
|
||||||
|
},
|
||||||
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
pub struct FramebufferWriter<'a> {
|
/// The updated writer stores necessary fields from the [Framebuffer].
|
||||||
framebuffer: Framebuffer<'a>,
|
/// This ensures that the contained types are Send, as Framebuffer was
|
||||||
|
/// not marked as Send.
|
||||||
|
///
|
||||||
|
/// It also avoids the requirement for lifetimes.
|
||||||
|
///
|
||||||
|
/// Note this does not implement Writer as these functions only handle drawing pixels.
|
||||||
|
pub struct FramebufferWriter {
|
||||||
|
pitch: u64,
|
||||||
|
bpp: u16,
|
||||||
|
addr: *mut u8,
|
||||||
|
width: u64,
|
||||||
|
height: u64,
|
||||||
}
|
}
|
||||||
|
|
||||||
unsafe impl<'a> Send for FramebufferWriter<'a> {}
|
unsafe impl Send for FramebufferWriter {}
|
||||||
unsafe impl<'a> Sync for FramebufferWriter<'a> {}
|
unsafe impl Sync for FramebufferWriter {}
|
||||||
impl<'a> FramebufferWriter<'a> {
|
|
||||||
pub fn new(framebuffer: Framebuffer<'a>) -> Self {
|
|
||||||
Self { framebuffer }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn write_pixel(&self, x: usize, y: usize, color: Color) {
|
impl FramebufferWriter {
|
||||||
let pitch = self.framebuffer.pitch() as usize;
|
pub fn new(framebuffer: Framebuffer) -> Self {
|
||||||
let bpp = (self.framebuffer.bpp() / 8) as usize;
|
Self {
|
||||||
let pixel_offset = y * pitch + x * bpp;
|
pitch: framebuffer.pitch(),
|
||||||
|
bpp: framebuffer.bpp(),
|
||||||
unsafe {
|
addr: framebuffer.addr(),
|
||||||
*(self.framebuffer.addr().add(pixel_offset) as *mut u32) = color.into();
|
width: framebuffer.width(),
|
||||||
|
height: framebuffer.height(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_frame(&self, buffer: &[Color; 1280 * 800]) {
|
pub fn write_pixel(&self, x: usize, y: usize, color: Colour) {
|
||||||
|
let pitch = self.pitch as usize;
|
||||||
|
let bpp = (self.bpp / 8) as usize;
|
||||||
|
let pixel_offset = y * pitch + x * bpp;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
*(self.addr.add(pixel_offset) as *mut u32) = color.into();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_frame(&self, buffer: &[Colour; 1280 * 800]) {
|
||||||
for (y, row) in buffer.chunks(1280).enumerate() {
|
for (y, row) in buffer.chunks(1280).enumerate() {
|
||||||
for (x, pixel) in row.iter().enumerate() {
|
for (x, pixel) in row.iter().enumerate() {
|
||||||
self.write_pixel(x, y, *pixel);
|
self.write_pixel(x, y, *pixel);
|
||||||
@@ -54,30 +71,29 @@ impl<'a> FramebufferWriter<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn width(&self) -> u32 {
|
pub const fn width(&self) -> u32 {
|
||||||
self.framebuffer.width() as u32
|
self.width as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn height(&self) -> u32 {
|
pub const fn height(&self) -> u32 {
|
||||||
self.framebuffer.height() as u32
|
self.height as u32
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(&self) {
|
pub fn clear(&self) {
|
||||||
let width = self.framebuffer.width() as usize;
|
let width = self.width as usize;
|
||||||
let height = self.framebuffer.height() as usize;
|
let height = self.height as usize;
|
||||||
|
|
||||||
for y in 0..height {
|
for y in 0..height {
|
||||||
for x in 0..width {
|
for x in 0..width {
|
||||||
self.write_pixel(x, y, Color::Black);
|
self.write_pixel(x, y, Colour::Black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn screensize_px() -> (u32, u32) {
|
pub fn screensize_px() -> (u32, u32) {
|
||||||
if let Some(writer) = FRAMEBUFFER_WRITER.lock().as_mut() {
|
FRAMEBUFFER_WRITER
|
||||||
(writer.width(), writer.height())
|
.lock()
|
||||||
} else {
|
.as_mut()
|
||||||
(0, 0)
|
.map_or_else(|| (0, 0), |writer| (writer.width(), writer.height()))
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#[repr(u32)]
|
#[repr(u32)]
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
||||||
pub enum Color {
|
pub enum Colour {
|
||||||
ARGB(u8, u8, u8, u8),
|
ARGB(u8, u8, u8, u8),
|
||||||
RGB(u8, u8, u8),
|
RGB(u8, u8, u8),
|
||||||
HexARGB(u32),
|
HexARGB(u32),
|
||||||
@@ -14,40 +14,41 @@ pub enum Color {
|
|||||||
White = 0xFFFFFFFF,
|
White = 0xFFFFFFFF,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<u32> for Color {
|
#[allow(clippy::use_self)]
|
||||||
fn into(self) -> u32 {
|
impl From<Colour> for u32 {
|
||||||
match self {
|
fn from(val: Colour) -> Self {
|
||||||
Color::ARGB(a, r, g, b) => {
|
match val {
|
||||||
|
Colour::ARGB(a, r, g, b) => {
|
||||||
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32)
|
(a as u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32)
|
||||||
}
|
}
|
||||||
Color::RGB(r, g, b) => (0u32) << 24 | (r as u32) << 16 | (g as u32) << 8 | (b as u32),
|
Colour::RGB(r, g, b) => ((r as u32) << 16) | (g as u32) << 8 | (b as u32),
|
||||||
Color::HexARGB(hex) => hex,
|
Colour::HexARGB(hex) => hex,
|
||||||
Color::Black => 0xFF000000,
|
Colour::Black => 0xFF000000,
|
||||||
Color::Blue => 0xFF0000FF,
|
Colour::Blue => 0xFF0000FF,
|
||||||
Color::Green => 0xFF00FF00,
|
Colour::Green => 0xFF00FF00,
|
||||||
Color::Cyan => 0xFF00FFFF,
|
Colour::Cyan => 0xFF00FFFF,
|
||||||
Color::Red => 0xFFFF0000,
|
Colour::Red => 0xFFFF0000,
|
||||||
Color::Magenta => 0xFFFF00FF,
|
Colour::Magenta => 0xFFFF00FF,
|
||||||
Color::Yellow => 0xFFFFFF00,
|
Colour::Yellow => 0xFFFFFF00,
|
||||||
Color::White => 0xFFFFFFFF,
|
Colour::White => 0xFFFFFFFF,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl core::fmt::Display for Color {
|
impl core::fmt::Display for Colour {
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
match self {
|
match self {
|
||||||
Color::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
|
Self::ARGB(r, g, b, a) => write!(f, "RGBA(#{:x}{:x}{:x}{:x})", r, g, b, a),
|
||||||
Color::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
|
Self::RGB(r, g, b) => write!(f, "RGB(#{:x}{:x}{:x})", r, g, b),
|
||||||
Color::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
|
Self::HexARGB(hex) => write!(f, "Hex(#{:x})", hex),
|
||||||
Color::Black => write!(f, "Black"),
|
Self::Black => write!(f, "Black"),
|
||||||
Color::Blue => write!(f, "Blue"),
|
Self::Blue => write!(f, "Blue"),
|
||||||
Color::Green => write!(f, "Green"),
|
Self::Green => write!(f, "Green"),
|
||||||
Color::Cyan => write!(f, "Cyan"),
|
Self::Cyan => write!(f, "Cyan"),
|
||||||
Color::Red => write!(f, "Red"),
|
Self::Red => write!(f, "Red"),
|
||||||
Color::Magenta => write!(f, "Magenta"),
|
Self::Magenta => write!(f, "Magenta"),
|
||||||
Color::Yellow => write!(f, "Yellow"),
|
Self::Yellow => write!(f, "Yellow"),
|
||||||
Color::White => write!(f, "White"),
|
Self::White => write!(f, "White"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-17
@@ -5,9 +5,9 @@ use core::{
|
|||||||
task::{Context, Poll},
|
task::{Context, Poll},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{print, println};
|
use crate::prelude::*;
|
||||||
use crossbeam::queue::ArrayQueue;
|
use crossbeam::queue::ArrayQueue;
|
||||||
use futures_util::{task::AtomicWaker, Stream};
|
use futures_util::{Stream, task::AtomicWaker};
|
||||||
use spin::Once;
|
use spin::Once;
|
||||||
|
|
||||||
static KBD_QUEUE: Once<ArrayQueue<u8>> = Once::new();
|
static KBD_QUEUE: Once<ArrayQueue<u8>> = Once::new();
|
||||||
@@ -15,7 +15,7 @@ static WAKER: AtomicWaker = AtomicWaker::new();
|
|||||||
|
|
||||||
pub fn add_scancode(scancode: u8) {
|
pub fn add_scancode(scancode: u8) {
|
||||||
if let Some(queue) = KBD_QUEUE.get() {
|
if let Some(queue) = KBD_QUEUE.get() {
|
||||||
if let Err(_) = queue.push(scancode) {
|
if queue.push(scancode).is_err() {
|
||||||
println!("WARNING: scancode queue full; dropping keyboard input");
|
println!("WARNING: scancode queue full; dropping keyboard input");
|
||||||
} else {
|
} else {
|
||||||
println!("waking waker");
|
println!("waking waker");
|
||||||
@@ -33,7 +33,14 @@ pub struct ScanCodeStream {
|
|||||||
impl ScanCodeStream {
|
impl ScanCodeStream {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
KBD_QUEUE.call_once(|| ArrayQueue::new(5));
|
KBD_QUEUE.call_once(|| ArrayQueue::new(5));
|
||||||
ScanCodeStream { _private: () }
|
|
||||||
|
Self { _private: () }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ScanCodeStream {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -42,30 +49,30 @@ impl Stream for ScanCodeStream {
|
|||||||
|
|
||||||
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
|
||||||
let queue = KBD_QUEUE.get().expect("scancode queue not initialized");
|
let queue = KBD_QUEUE.get().expect("scancode queue not initialized");
|
||||||
print!("polling or smth");
|
serial_println!("This is called once.");
|
||||||
|
WAKER.register(cx.waker());
|
||||||
|
|
||||||
// fast path
|
// fast path
|
||||||
if let Some(scancode) = queue.pop() {
|
if let Some(scancode) = queue.pop() {
|
||||||
return Poll::Ready(Some(scancode));
|
return Poll::Ready(Some(scancode));
|
||||||
}
|
}
|
||||||
|
|
||||||
WAKER.register(&cx.waker());
|
queue.pop().map_or_else(
|
||||||
match queue.pop() {
|
|| {
|
||||||
Some(scancode) => {
|
print!("Returning");
|
||||||
print!("scancode found");
|
Poll::Pending
|
||||||
|
},
|
||||||
|
|scancode| {
|
||||||
|
print!("Scancode found");
|
||||||
WAKER.take();
|
WAKER.take();
|
||||||
Poll::Ready(Some(scancode))
|
Poll::Ready(Some(scancode))
|
||||||
}
|
},
|
||||||
None => {
|
)
|
||||||
print!("returning");
|
|
||||||
Poll::Pending
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
use futures_util::stream::StreamExt;
|
use futures_util::stream::StreamExt;
|
||||||
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
|
use pc_keyboard::{DecodedKey, HandleControl, Keyboard, ScancodeSet1, layouts};
|
||||||
|
|
||||||
pub async fn print_keypresses() {
|
pub async fn print_keypresses() {
|
||||||
let mut scancodes = ScanCodeStream::new();
|
let mut scancodes = ScanCodeStream::new();
|
||||||
@@ -75,7 +82,7 @@ pub async fn print_keypresses() {
|
|||||||
HandleControl::Ignore,
|
HandleControl::Ignore,
|
||||||
);
|
);
|
||||||
|
|
||||||
println!("OK!!!");
|
serial_println!("Printing keypresses.");
|
||||||
|
|
||||||
while let Some(scancode) = scancodes.next().await {
|
while let Some(scancode) = scancodes.next().await {
|
||||||
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
|
||||||
|
|||||||
+18
-14
@@ -3,23 +3,27 @@
|
|||||||
use core::arch::asm;
|
use core::arch::asm;
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn inb(port: u16) -> u8 {
|
pub fn inb(port: u16) -> u8 {
|
||||||
let value: u8;
|
let value: u8;
|
||||||
asm!(
|
unsafe {
|
||||||
"in al, dx",
|
asm!(
|
||||||
out("al") value,
|
"in al, dx",
|
||||||
in("dx") port,
|
out("al") value,
|
||||||
options(nomem, nostack, preserves_flags)
|
in("dx") port,
|
||||||
);
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub unsafe fn outb(port: u16, value: u8) {
|
pub fn outb(port: u16, value: u8) {
|
||||||
asm!(
|
unsafe {
|
||||||
"out dx, al",
|
asm!(
|
||||||
in("dx") port,
|
"out dx, al",
|
||||||
in("al") value,
|
in("dx") port,
|
||||||
options(nomem, nostack, preserves_flags)
|
in("al") value,
|
||||||
);
|
options(nomem, nostack, preserves_flags)
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+18
-22
@@ -45,14 +45,14 @@ pub fn serial_read() -> &'static str {
|
|||||||
|
|
||||||
let i = BUFFER_LEN.load(Ordering::SeqCst);
|
let i = BUFFER_LEN.load(Ordering::SeqCst);
|
||||||
|
|
||||||
return unsafe {
|
unsafe {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
core::str::from_utf8(&BUFFER[..i - 1]).unwrap()
|
core::str::from_utf8(&BUFFER[..i - 1]).unwrap()
|
||||||
} else {
|
} else {
|
||||||
serial_println!("empty string");
|
serial_println!("empty string");
|
||||||
""
|
""
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static PORT: u16 = 0x3f8;
|
static PORT: u16 = 0x3f8;
|
||||||
@@ -83,15 +83,13 @@ impl Writer {
|
|||||||
pub fn write_byte(&self, data: u8) {
|
pub fn write_byte(&self, data: u8) {
|
||||||
unsafe {
|
unsafe {
|
||||||
while !self.write_success() {}
|
while !self.write_success() {}
|
||||||
outb(PORT + 0, data);
|
outb(PORT, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init() -> Result<(), &'static str> {
|
pub fn init() -> Result<(), &'static str> {
|
||||||
if let Err(e) = test() {
|
test()?;
|
||||||
return Err(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
if READER.lock().is_none() {
|
if READER.lock().is_none() {
|
||||||
*READER.lock() = Some(Reader);
|
*READER.lock() = Some(Reader);
|
||||||
@@ -105,24 +103,22 @@ pub fn init() -> Result<(), &'static str> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn test() -> Result<(), &'static str> {
|
pub fn test() -> Result<(), &'static str> {
|
||||||
unsafe {
|
outb(PORT + 1, 0x00); // Disable all interrupts
|
||||||
outb(PORT + 1, 0x00); // Disable all interrupts
|
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
|
||||||
outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor)
|
outb(PORT, 0x03); // Set divisor to 3 (lo byte) 38400 baud
|
||||||
outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud
|
outb(PORT + 1, 0x00); // (hi byte)
|
||||||
outb(PORT + 1, 0x00); // (hi byte)
|
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
|
||||||
outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit
|
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-bytethreshold
|
||||||
outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-bytethreshold
|
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
||||||
outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set
|
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
||||||
outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip
|
outb(PORT, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
||||||
outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial returns same byte)
|
|
||||||
|
|
||||||
if inb(PORT + 0) != 0xAE {
|
if inb(PORT) != 0xAE {
|
||||||
return Err("serial test failed");
|
return Err("serial test failed");
|
||||||
}
|
|
||||||
|
|
||||||
outb(PORT + 4, 0x0F);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
outb(PORT + 4, 0x0F);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -153,7 +149,7 @@ impl Reader {
|
|||||||
pub fn read(&self) -> u8 {
|
pub fn read(&self) -> u8 {
|
||||||
unsafe {
|
unsafe {
|
||||||
while !self.read_ready() {}
|
while !self.read_ready() {}
|
||||||
return inb(PORT + 0);
|
inb(PORT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-2
@@ -1,5 +1,6 @@
|
|||||||
#![no_std]
|
#![no_std]
|
||||||
|
#![warn(tail_expr_drop_order)]
|
||||||
|
#![warn(clippy::correctness, clippy::perf, clippy::nursery)]
|
||||||
// alloc
|
// alloc
|
||||||
// io : serial, framebuffer, ascii(?), keyboard
|
// io : serial, framebuffer, ascii(?), keyboard
|
||||||
// ?????
|
// ?????
|
||||||
@@ -11,5 +12,5 @@ pub mod scheduling;
|
|||||||
|
|
||||||
/// Re-exports most of the IO macros.
|
/// Re-exports most of the IO macros.
|
||||||
pub mod prelude {
|
pub mod prelude {
|
||||||
pub use crate::{print_log, println, println_log, serial_print, serial_println};
|
pub use crate::{print, print_log, println, println_log, serial_print, serial_println};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ pub struct Task {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Task {
|
impl Task {
|
||||||
pub fn new(future: impl Future<Output = ()> + 'static) -> Task {
|
pub fn new(future: impl Future<Output = ()> + 'static) -> Self {
|
||||||
Task {
|
Self {
|
||||||
id: TaskId::new(),
|
id: TaskId::new(),
|
||||||
future: Box::pin(future),
|
future: Box::pin(future),
|
||||||
}
|
}
|
||||||
@@ -32,9 +32,15 @@ pub struct Executor {
|
|||||||
waker_cache: BTreeMap<TaskId, Waker>,
|
waker_cache: BTreeMap<TaskId, Waker>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for Executor {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Executor {
|
impl Executor {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Executor {
|
Self {
|
||||||
tasks: BTreeMap::new(),
|
tasks: BTreeMap::new(),
|
||||||
task_queue: Arc::new(ArrayQueue::new(100)),
|
task_queue: Arc::new(ArrayQueue::new(100)),
|
||||||
waker_cache: BTreeMap::new(),
|
waker_cache: BTreeMap::new(),
|
||||||
@@ -63,7 +69,7 @@ impl Executor {
|
|||||||
};
|
};
|
||||||
let waker = waker_cache
|
let waker = waker_cache
|
||||||
.entry(task_id)
|
.entry(task_id)
|
||||||
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
|
.or_insert_with(|| TaskWaker::waker(task_id, task_queue.clone()));
|
||||||
let mut context = Context::from_waker(waker);
|
let mut context = Context::from_waker(waker);
|
||||||
match task.poll(&mut context) {
|
match task.poll(&mut context) {
|
||||||
Poll::Ready(()) => {
|
Poll::Ready(()) => {
|
||||||
@@ -98,8 +104,8 @@ struct TaskWaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl TaskWaker {
|
impl TaskWaker {
|
||||||
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
|
fn waker(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
|
||||||
Waker::from(Arc::new(TaskWaker {
|
Waker::from(Arc::new(Self {
|
||||||
task_id,
|
task_id,
|
||||||
task_queue,
|
task_queue,
|
||||||
}))
|
}))
|
||||||
@@ -128,6 +134,6 @@ struct TaskId(u64);
|
|||||||
impl TaskId {
|
impl TaskId {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
static NEXT: AtomicU64 = AtomicU64::new(0);
|
static NEXT: AtomicU64 = AtomicU64::new(0);
|
||||||
TaskId(NEXT.fetch_add(1, Ordering::Relaxed))
|
Self(NEXT.fetch_add(1, Ordering::Relaxed))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script originally written by zxq5, I added separate scripts to remove `jq` dependency.
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
GREEN='\033[0;32m'
|
GREEN='\033[0;32m'
|
||||||
BLUE='\033[0;34m'
|
BLUE='\033[0;34m'
|
||||||
@@ -16,6 +18,8 @@ trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR
|
|||||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||||
project_root=$(cd "$script_dir/.." &>/dev/null && pwd)
|
project_root=$(cd "$script_dir/.." &>/dev/null && pwd)
|
||||||
|
|
||||||
|
echo -e "${GREEN}${BOLD} Running kernel in debug mode."
|
||||||
|
|
||||||
# Logging functions
|
# Logging functions
|
||||||
info() {
|
info() {
|
||||||
echo -e "${BLUE}${BOLD}info${NC}: $1"
|
echo -e "${BLUE}${BOLD}info${NC}: $1"
|
||||||
@@ -124,8 +128,8 @@ else
|
|||||||
kvm_flag=""
|
kvm_flag=""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if we're running in debug mode
|
# Enable using GDB with $USE_GDB.
|
||||||
if [[ "$(cargo metadata --format-version=1 | jq -r '.workspace_members[0]' | cut -d' ' -f2)" == "(debug)" ]]; then
|
if [ $USE_GDB ]; then
|
||||||
debug_flags="-s -S"
|
debug_flags="-s -S"
|
||||||
else
|
else
|
||||||
debug_flags=""
|
debug_flags=""
|
||||||
Executable
+186
@@ -0,0 +1,186 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Script originally written by zxq5, I added separate scripts to remove `jq` dependency.
|
||||||
|
|
||||||
|
# Colors
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
BLUE='\033[0;34m'
|
||||||
|
YELLOW='\033[0;33m'
|
||||||
|
RED='\033[0;31m'
|
||||||
|
BOLD='\033[1m'
|
||||||
|
NC='\033[0m' # No Color
|
||||||
|
|
||||||
|
# Error handling
|
||||||
|
set -e
|
||||||
|
trap 'echo -e "${RED}${BOLD}error${NC}: build failed" >&2' ERR
|
||||||
|
|
||||||
|
# Get absolute path to project root
|
||||||
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" &>/dev/null && pwd)
|
||||||
|
project_root=$(cd "$script_dir/.." &>/dev/null && pwd)
|
||||||
|
|
||||||
|
echo -e "${GREEN}${BOLD} Running kernel in release mode."
|
||||||
|
|
||||||
|
# Logging functions
|
||||||
|
info() {
|
||||||
|
echo -e "${BLUE}${BOLD}info${NC}: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
compiling() {
|
||||||
|
echo -e "${GREEN}${BOLD}Compiling${NC}: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
warning() {
|
||||||
|
echo -e "${YELLOW}${BOLD}warning${NC}: $1" >&2
|
||||||
|
}
|
||||||
|
|
||||||
|
building() {
|
||||||
|
echo -e "${GREEN}${BOLD}Building${NC}: $1"
|
||||||
|
}
|
||||||
|
|
||||||
|
copying() {
|
||||||
|
echo -e "${GREEN}${BOLD} Copying${NC}: $1 to $2"
|
||||||
|
}
|
||||||
|
|
||||||
|
error() {
|
||||||
|
echo -e "${RED}${BOLD}error${NC}: $1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
copy_file() {
|
||||||
|
copying $1 $2
|
||||||
|
cp "$1" "$2" || error $3
|
||||||
|
}
|
||||||
|
|
||||||
|
build_dir="$project_root/build"
|
||||||
|
iso_root="$build_dir/iso_root"
|
||||||
|
|
||||||
|
# Check if we're running tests
|
||||||
|
is_test=0
|
||||||
|
if [[ $1 == *"deps"* ]]; then
|
||||||
|
is_test=1
|
||||||
|
kernel_path="$1"
|
||||||
|
else
|
||||||
|
# Build the kernel normally
|
||||||
|
cd "$project_root"
|
||||||
|
# cargo build
|
||||||
|
kernel_path="$build_dir/target/x86_64-kernel/release/kernel"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check for required tools
|
||||||
|
check_tools() {
|
||||||
|
local missing=0
|
||||||
|
for tool in xorriso git qemu-system-x86_64; do
|
||||||
|
if ! command -v $tool >/dev/null 2>&1; then
|
||||||
|
error "required tool '$tool' is not installed"
|
||||||
|
missing=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ $missing -eq 1 ]; then
|
||||||
|
error "missing required tools"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Create build directory structure
|
||||||
|
info "Creating build directory structure"
|
||||||
|
mkdir -p "$iso_root/boot/limine"
|
||||||
|
mkdir -p "$iso_root/EFI/BOOT"
|
||||||
|
|
||||||
|
# Clone Limine if needed
|
||||||
|
if [ ! -d "$build_dir/limine" ]; then
|
||||||
|
compiling "limine bootloader"
|
||||||
|
cd "$build_dir"
|
||||||
|
git clone https://github.com/limine-bootloader/limine.git --branch=v9.x-binary --depth=1 "$build_dir/limine" || error "failed to clone limine"
|
||||||
|
make -C "$build_dir/limine" || error "failed to build limine"
|
||||||
|
cd "$project_root"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy files
|
||||||
|
info "Copying files to ISO root"
|
||||||
|
copy_file "$kernel_path" "$iso_root/boot/kernel" "failed to copy kernel"
|
||||||
|
copy_file "$project_root/config/limine.conf" "$iso_root/boot/limine/limine.conf" "failed to copy limine config"
|
||||||
|
copy_file "$build_dir/limine/limine-bios-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-bios-cd.bin"
|
||||||
|
copy_file "$build_dir/limine/limine-uefi-cd.bin" "$iso_root/boot/limine/" "failed to copy limine-uefi-cd.bin"
|
||||||
|
copy_file "$build_dir/limine/limine-bios.sys" "$iso_root/boot/limine/" "failed to copy limine-bios.sys"
|
||||||
|
copy_file "$build_dir/limine/BOOTX64.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTX64.EFI"
|
||||||
|
copy_file "$build_dir/limine/BOOTIA32.EFI" "$iso_root/EFI/BOOT/" "failed to copy BOOTIA32.EFI"
|
||||||
|
|
||||||
|
# Create ISO
|
||||||
|
building "bootable ISO image"
|
||||||
|
xorriso -as mkisofs -R -r -J -b boot/limine/limine-bios-cd.bin \
|
||||||
|
-no-emul-boot -boot-load-size 4 -boot-info-table -hfsplus \
|
||||||
|
-apm-block-size 2048 --efi-boot boot/limine/limine-uefi-cd.bin \
|
||||||
|
-efi-boot-part --efi-boot-image --protective-msdos-label \
|
||||||
|
"$iso_root" -o "$build_dir/image.iso" || error "failed to create ISO"
|
||||||
|
|
||||||
|
# Install Limine
|
||||||
|
info "Installing Limine bootloader"
|
||||||
|
"$build_dir/limine/limine" bios-install "$build_dir/image.iso" || error "failed to install limine"
|
||||||
|
|
||||||
|
# Check if KVM is available
|
||||||
|
if [ "${KVM_FLAG:-enable}" = "disable" ]; then
|
||||||
|
warning "KVM acceleration disabled by user"
|
||||||
|
kvm_flag=""
|
||||||
|
elif [ -c "/dev/kvm" ] && [ -w "/dev/kvm" ]; then
|
||||||
|
info "KVM acceleration enabled"
|
||||||
|
kvm_flag="-enable-kvm"
|
||||||
|
else
|
||||||
|
warning "KVM acceleration not available (is kvm module loaded?)"
|
||||||
|
kvm_flag=""
|
||||||
|
fi
|
||||||
|
|
||||||
|
# I'm lazy but I just remove GDB flags when running this script.
|
||||||
|
debug_flags=""
|
||||||
|
|
||||||
|
# Set up test-specific flags
|
||||||
|
if [ $is_test -eq 1 ]; then
|
||||||
|
test_flags="-device isa-debug-exit,iobase=0xf4,iosize=0x04 -display none"
|
||||||
|
serial_flags="-serial stdio"
|
||||||
|
else
|
||||||
|
test_flags=""
|
||||||
|
# serial_flags="-serial tcp:127.0.0.1:1234,server -monitor telnet:127.0.0.1:1235,server"
|
||||||
|
serial_flags="-serial stdio"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Run in QEMU
|
||||||
|
if [[ ${QEMU_FLAGS} == *-S* ]]; then
|
||||||
|
info "Running OS in QEMU with GDB debugging enabled"
|
||||||
|
info "To connect GDB, run: gdb"
|
||||||
|
info "At the GDB prompt, type: target remote localhost:1234"
|
||||||
|
else
|
||||||
|
info "Running OS in QEMU..."
|
||||||
|
fi
|
||||||
|
|
||||||
|
check_test_res() {
|
||||||
|
qemu_exit_code=$?
|
||||||
|
if [ $qemu_exit_code -eq 33 ]; then
|
||||||
|
# Success case (0x10 << 1) | 1 = 33
|
||||||
|
info "All tests passed"
|
||||||
|
exit 0
|
||||||
|
elif [ $qemu_exit_code -eq 35 ]; then
|
||||||
|
# Failure case (0x11 << 1) | 1 = 35
|
||||||
|
warning "Some tests failed"
|
||||||
|
exit 1
|
||||||
|
else
|
||||||
|
# Any other exit code is treated as a failure
|
||||||
|
warning "Some tests failed"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
kvm_flag=""
|
||||||
|
|
||||||
|
trap 'check_test_res "tests completed"' ERR
|
||||||
|
|
||||||
|
cd "$project_root"
|
||||||
|
qemu-system-x86_64 -M q35 \
|
||||||
|
${kvm_flag} \
|
||||||
|
-cdrom "$build_dir/image.iso" \
|
||||||
|
-boot d \
|
||||||
|
-m 2G \
|
||||||
|
${serial_flags} \
|
||||||
|
-no-reboot \
|
||||||
|
${test_flags} \
|
||||||
|
${debug_flags} \
|
||||||
|
${QEMU_FLAGS:-}
|
||||||
|
|
||||||
Reference in New Issue
Block a user