vi and busybox

This commit is contained in:
wcjbr
2026-07-29 10:56:30 +08:00
commit 86dbf7482f
47 changed files with 9647 additions and 0 deletions
+2
View File
@@ -0,0 +1,2 @@
[build]
target = "x86_64-unknown-uefi"
+1
View File
@@ -0,0 +1 @@
/target
+20
View File
@@ -0,0 +1,20 @@
{
"MicroPython.executeButton": [
{
"text": "▶",
"tooltip": "运行",
"alignment": "left",
"command": "extension.executeFile",
"priority": 3.5
}
],
"MicroPython.syncButton": [
{
"text": "$(sync)",
"tooltip": "同步",
"alignment": "left",
"command": "extension.execute",
"priority": 4
}
]
}
Generated
+11
View File
@@ -0,0 +1,11 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 4
[[package]]
name = "ZeroOS"
version = "0.1.0"
[[package]]
name = "zeroos-loader"
version = "0.1.0"
+15
View File
@@ -0,0 +1,15 @@
[package]
name = "ZeroOS"
version = "0.1.0"
edition = "2024"
[workspace]
members = ["boot"]
[dependencies]
[profile.dev]
panic = "abort"
[profile.release]
panic = "abort"
+6
View File
@@ -0,0 +1,6 @@
[package]
name = "zeroos-loader"
version = "0.1.0"
edition = "2024"
[dependencies]
+352
View File
@@ -0,0 +1,352 @@
#![no_std]
#![no_main]
use core::mem::size_of;
use core::panic::PanicInfo;
use core::ptr::{copy_nonoverlapping, null_mut};
type EfiHandle = *mut core::ffi::c_void;
type EfiStatus = usize;
const EFI_SUCCESS: EfiStatus = 0;
const EFI_LOAD_ERROR: EfiStatus = 1;
const EFI_BUFFER_TOO_SMALL: EfiStatus = 5;
const EFI_LOADED_IMAGE_PROTOCOL_GUID: EfiGuid = EfiGuid::new(
0x5b1b31a1,
0x9562,
0x11d2,
[0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
);
const EFI_DEVICE_PATH_PROTOCOL_GUID: EfiGuid = EfiGuid::new(
0x09576e91,
0x6d3f,
0x11d2,
[0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
);
const MEDIA_DEVICE_PATH: u8 = 0x04;
const MEDIA_FILEPATH_DP: u8 = 0x04;
const END_DEVICE_PATH_TYPE: u8 = 0x7f;
const END_ENTIRE_DEVICE_PATH_SUBTYPE: u8 = 0xff;
static ZEROOS_PATH: [u16; 23] = [
'\\' as u16,
'E' as u16,
'F' as u16,
'I' as u16,
'\\' as u16,
'Z' as u16,
'e' as u16,
'r' as u16,
'o' as u16,
'O' as u16,
'S' as u16,
'\\' as u16,
'Z' as u16,
'e' as u16,
'r' as u16,
'o' as u16,
'O' as u16,
'S' as u16,
'.' as u16,
'E' as u16,
'F' as u16,
'I' as u16,
0,
];
static LOADING: [u16; 22] = [
'L' as u16,
'o' as u16,
'a' as u16,
'd' as u16,
'i' as u16,
'n' as u16,
'g' as u16,
' ' as u16,
'Z' as u16,
'e' as u16,
'r' as u16,
'o' as u16,
'O' as u16,
'S' as u16,
'.' as u16,
'.' as u16,
'.' as u16,
'\r' as u16,
'\n' as u16,
0,
0,
0,
];
#[repr(C)]
#[derive(Clone, Copy)]
struct EfiGuid {
data1: u32,
data2: u16,
data3: u16,
data4: [u8; 8],
}
impl EfiGuid {
const fn new(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self {
Self {
data1,
data2,
data3,
data4,
}
}
}
#[repr(C)]
struct EfiTableHeader {
signature: u64,
revision: u32,
header_size: u32,
crc32: u32,
reserved: u32,
}
#[repr(C)]
struct EfiSystemTable {
hdr: EfiTableHeader,
firmware_vendor: *mut u16,
firmware_revision: u32,
console_in_handle: EfiHandle,
con_in: *mut core::ffi::c_void,
console_out_handle: EfiHandle,
con_out: *mut EfiSimpleTextOutputProtocol,
standard_error_handle: EfiHandle,
std_err: *mut EfiSimpleTextOutputProtocol,
runtime_services: *mut core::ffi::c_void,
boot_services: *mut EfiBootServices,
number_of_table_entries: usize,
configuration_table: *mut core::ffi::c_void,
}
#[repr(C)]
struct EfiSimpleTextOutputProtocol {
reset: usize,
output_string:
extern "efiapi" fn(this: *mut EfiSimpleTextOutputProtocol, string: *const u16) -> EfiStatus,
}
#[repr(C)]
struct EfiBootServices {
hdr: EfiTableHeader,
raise_tpl: usize,
restore_tpl: usize,
allocate_pages: usize,
free_pages: usize,
get_memory_map: usize,
allocate_pool: usize,
free_pool: usize,
create_event: usize,
set_timer: usize,
wait_for_event: usize,
signal_event: usize,
close_event: usize,
check_event: usize,
install_protocol_interface: usize,
reinstall_protocol_interface: usize,
uninstall_protocol_interface: usize,
handle_protocol: extern "efiapi" fn(
handle: EfiHandle,
protocol: *const EfiGuid,
interface: *mut *mut core::ffi::c_void,
) -> EfiStatus,
reserved: usize,
register_protocol_notify: usize,
locate_handle: usize,
locate_device_path: usize,
install_configuration_table: usize,
load_image: extern "efiapi" fn(
boot_policy: u8,
parent_image_handle: EfiHandle,
device_path: *const EfiDevicePathProtocol,
source_buffer: *mut core::ffi::c_void,
source_size: usize,
image_handle: *mut EfiHandle,
) -> EfiStatus,
start_image: extern "efiapi" fn(
image_handle: EfiHandle,
exit_data_size: *mut usize,
exit_data: *mut *mut u16,
) -> EfiStatus,
}
#[repr(C)]
struct EfiLoadedImageProtocol {
revision: u32,
parent_handle: EfiHandle,
system_table: *mut EfiSystemTable,
device_handle: EfiHandle,
file_path: *mut EfiDevicePathProtocol,
reserved: *mut core::ffi::c_void,
load_options_size: u32,
load_options: *mut core::ffi::c_void,
image_base: *mut core::ffi::c_void,
image_size: u64,
image_code_type: u32,
image_data_type: u32,
unload: usize,
}
#[repr(C, packed)]
struct EfiDevicePathProtocol {
ty: u8,
sub_type: u8,
length: [u8; 2],
}
#[unsafe(no_mangle)]
extern "efiapi" fn efi_main(
image_handle: EfiHandle,
system_table: *mut EfiSystemTable,
) -> EfiStatus {
unsafe {
if system_table.is_null() || (*system_table).boot_services.is_null() {
return EFI_LOAD_ERROR;
}
write(system_table, LOADING.as_ptr());
let boot_services = (*system_table).boot_services;
let mut loaded_image = null_mut();
let status = ((*boot_services).handle_protocol)(
image_handle,
&EFI_LOADED_IMAGE_PROTOCOL_GUID,
&mut loaded_image,
);
if status != EFI_SUCCESS {
return status;
}
let loaded_image = loaded_image as *mut EfiLoadedImageProtocol;
let mut partition_device_path = null_mut();
let status = ((*boot_services).handle_protocol)(
(*loaded_image).device_handle,
&EFI_DEVICE_PATH_PROTOCOL_GUID,
&mut partition_device_path,
);
if status != EFI_SUCCESS {
return status;
}
let mut device_path_buffer = [0u8; 1024];
let device_path = match build_zeroos_device_path(
partition_device_path as *const EfiDevicePathProtocol,
&mut device_path_buffer,
) {
Ok(path) => path,
Err(status) => return status,
};
let mut zeroos_image = null_mut();
let status = ((*boot_services).load_image)(
0,
image_handle,
device_path,
null_mut(),
0,
&mut zeroos_image,
);
if status != EFI_SUCCESS {
return status;
}
((*boot_services).start_image)(zeroos_image, null_mut(), null_mut())
}
}
unsafe fn build_zeroos_device_path<'a>(
partition_path: *const EfiDevicePathProtocol,
buffer: &'a mut [u8],
) -> Result<*const EfiDevicePathProtocol, EfiStatus> {
if partition_path.is_null() {
return Err(EFI_LOAD_ERROR);
}
let partition_len = unsafe { device_path_len_without_end(partition_path)? };
let file_path_node_len = 4 + ZEROOS_PATH.len() * size_of::<u16>();
let total_len = partition_len + file_path_node_len + 4;
if total_len > buffer.len() || file_path_node_len > u16::MAX as usize {
return Err(EFI_BUFFER_TOO_SMALL);
}
unsafe {
copy_nonoverlapping(
partition_path as *const u8,
buffer.as_mut_ptr(),
partition_len,
);
}
let file_node = &mut buffer[partition_len..partition_len + file_path_node_len];
file_node[0] = MEDIA_DEVICE_PATH;
file_node[1] = MEDIA_FILEPATH_DP;
file_node[2] = (file_path_node_len & 0xff) as u8;
file_node[3] = (file_path_node_len >> 8) as u8;
unsafe {
copy_nonoverlapping(
ZEROOS_PATH.as_ptr() as *const u8,
file_node[4..].as_mut_ptr(),
ZEROOS_PATH.len() * size_of::<u16>(),
);
}
let end_node = &mut buffer[partition_len + file_path_node_len..total_len];
end_node[0] = END_DEVICE_PATH_TYPE;
end_node[1] = END_ENTIRE_DEVICE_PATH_SUBTYPE;
end_node[2] = 4;
end_node[3] = 0;
Ok(buffer.as_ptr() as *const EfiDevicePathProtocol)
}
unsafe fn device_path_len_without_end(
mut node: *const EfiDevicePathProtocol,
) -> Result<usize, EfiStatus> {
let mut len = 0;
for _ in 0..256 {
let node_len = unsafe { device_path_node_len(node) };
if node_len < 4 {
return Err(EFI_LOAD_ERROR);
}
if unsafe {
(*node).ty == END_DEVICE_PATH_TYPE && (*node).sub_type == END_ENTIRE_DEVICE_PATH_SUBTYPE
} {
return Ok(len);
}
len += node_len;
node = unsafe { (node as *const u8).add(node_len) as *const EfiDevicePathProtocol };
}
Err(EFI_LOAD_ERROR)
}
unsafe fn device_path_node_len(node: *const EfiDevicePathProtocol) -> usize {
unsafe { ((*node).length[0] as usize) | ((*node).length[1] as usize) << 8 }
}
unsafe fn write(system_table: *mut EfiSystemTable, text: *const u16) {
unsafe {
if !system_table.is_null() && !(*system_table).con_out.is_null() {
((*(*system_table).con_out).output_string)((*system_table).con_out, text);
}
}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
Binary file not shown.
+147
View File
@@ -0,0 +1,147 @@
#!/usr/bin/env bash
set -euo pipefail
repo_dir="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)"
target_dir="${repo_dir}/target/x86_64-unknown-uefi/debug"
esp_dir="${repo_dir}/target/esp"
disk_image="${repo_dir}/target/zeroos.raw"
esp_image="${repo_dir}/target/zeroos-esp.fat"
root_image="${repo_dir}/target/zeroos-root.ext4"
root_dir="${repo_dir}/target/rootfs"
ovmf_vars="${repo_dir}/target/OVMF_VARS.4m.fd"
ovmf_code="/usr/share/edk2/x64/OVMF_CODE.4m.fd"
ovmf_vars_template="/usr/share/edk2/x64/OVMF_VARS.4m.fd"
qemu_display="${QEMU_DISPLAY:-gtk}"
qemu_serial="${QEMU_SERIAL:-stdio}"
qemu_extra_args="${QEMU_EXTRA_ARGS:--no-reboot}"
qemu_accel="${QEMU_ACCEL:-auto}"
qemu_debug_log="${QEMU_DEBUG_LOG:-${repo_dir}/target/qemu-debug.log}"
zeroos_sh="${ZEROOS_SH:-/home/archzero/C++/busybox/busybox}"
rebuild_disk="${ZEROOS_REBUILD_DISK:-0}"
update_rootfs="${ZEROOS_UPDATE_ROOTFS:-0}"
esp_start=2048
esp_sectors=131072
root_start=$((esp_start + esp_sectors))
root_sectors=389120
esp_size=$((esp_sectors * 512))
root_size=$((root_sectors * 512))
disk_size=$(((root_start + root_sectors + 2048) * 512))
if [[ ! -r "${ovmf_code}" || ! -r "${ovmf_vars_template}" ]]; then
echo "OVMF firmware not found under /usr/share/edk2/x64." >&2
echo "Install the OVMF/edk2 package for your distribution." >&2
exit 1
fi
cargo build --manifest-path "${repo_dir}/Cargo.toml" --target x86_64-unknown-uefi --workspace
mkdir -p "${esp_dir}/EFI/BOOT" "${esp_dir}/EFI/ZeroOS"
cp "${target_dir}/zeroos-loader.efi" "${esp_dir}/EFI/BOOT/BOOTX64.EFI"
cp "${target_dir}/ZeroOS.efi" "${esp_dir}/EFI/ZeroOS/ZeroOS.EFI"
truncate -s "${esp_size}" "${esp_image}"
mformat -i "${esp_image}" -F ::
mmd -i "${esp_image}" ::/EFI ::/EFI/BOOT ::/EFI/ZeroOS
mcopy -i "${esp_image}" "${esp_dir}/EFI/BOOT/BOOTX64.EFI" ::/EFI/BOOT/BOOTX64.EFI
mcopy -i "${esp_image}" "${esp_dir}/EFI/ZeroOS/ZeroOS.EFI" ::/EFI/ZeroOS/ZeroOS.EFI
if [[ ! -f "${disk_image}" ]]; then
rebuild_disk=1
fi
if [[ "${rebuild_disk}" == "1" || "${update_rootfs}" == "1" ]]; then
if [[ ! -r "${zeroos_sh}" ]]; then
echo "sh source is not readable: ${zeroos_sh}" >&2
echo "Set ZEROOS_SH=/path/to/static/busybox-or-sh." >&2
exit 1
fi
rm -rf "${root_dir}"
mkdir -p "${root_dir}/bin" "${root_dir}/usr/bin" "${root_dir}/dev" "${root_dir}/etc" "${root_dir}/tmp"
cp "${zeroos_sh}" "${root_dir}/usr/bin/busybox"
while IFS= read -r applet; do
[[ -n "${applet}" ]] || continue
if [[ "${applet}" != "busybox" ]]; then
ln -f "${root_dir}/usr/bin/busybox" "${root_dir}/usr/bin/${applet}"
fi
ln -f "${root_dir}/usr/bin/busybox" "${root_dir}/bin/${applet}"
done < <("${zeroos_sh}" --list)
ln -f "${root_dir}/usr/bin/busybox" "${root_dir}/usr/bin/bash"
ln -f "${root_dir}/usr/bin/busybox" "${root_dir}/bin/bash"
truncate -s "${root_size}" "${root_image}"
mkfs.ext4 -q -F -b 4096 -O '^64bit,^metadata_csum,^has_journal,^dir_index' -d "${root_dir}" "${root_image}"
fi
if [[ "${rebuild_disk}" == "1" ]]; then
truncate -s "${disk_size}" "${disk_image}"
sgdisk --clear \
--new=1:${esp_start}:$((esp_start + esp_sectors - 1)) \
--typecode=1:ef00 \
--change-name=1:ZeroOSESP \
--new=2:${root_start}:$((root_start + root_sectors - 1)) \
--typecode=2:8300 \
--change-name=2:ZeroOSRoot \
"${disk_image}" >/dev/null
dd if="${root_image}" of="${disk_image}" bs=512 seek="${root_start}" conv=notrunc status=none
elif [[ "${update_rootfs}" == "1" ]]; then
dd if="${root_image}" of="${disk_image}" bs=512 seek="${root_start}" conv=notrunc status=none
fi
dd if="${esp_image}" of="${disk_image}" bs=512 seek="${esp_start}" conv=notrunc status=none
if [[ ! -f "${ovmf_vars}" ]]; then
cp "${ovmf_vars_template}" "${ovmf_vars}"
fi
extra_args=()
if [[ -n "${qemu_extra_args}" ]]; then
read -r -a extra_args <<< "${qemu_extra_args}"
fi
debug_args=()
if [[ "${QEMU_DEBUG:-1}" != "0" ]]; then
debug_args=(-d int,cpu_reset -D "${qemu_debug_log}")
rm -f "${qemu_debug_log}"
fi
accel_args=()
case "${qemu_accel}" in
auto)
if [[ -r /dev/kvm && -w /dev/kvm ]]; then
accel_args=(-enable-kvm -cpu host)
fi
;;
kvm)
accel_args=(-enable-kvm -cpu host)
;;
tcg|off|none)
;;
*)
echo "invalid QEMU_ACCEL: ${qemu_accel}" >&2
echo "Use QEMU_ACCEL=auto, kvm, or tcg." >&2
exit 1
;;
esac
qemu-system-x86_64 \
-machine q35 \
"${accel_args[@]}" \
-m 256M \
-drive "if=pflash,format=raw,readonly=on,file=${ovmf_code}" \
-drive "if=pflash,format=raw,file=${ovmf_vars}" \
-device "ich9-ahci,id=ahci0" \
-drive "id=zeroosdisk,if=none,format=raw,file=${disk_image}" \
-device "ide-hd,drive=zeroosdisk,bus=ahci0.0" \
-serial "${qemu_serial}" \
-display "${qemu_display}" \
"${debug_args[@]}" \
"${extra_args[@]}"
status=$?
echo "qemu exited with status ${status}" >&2
if [[ -f "${qemu_debug_log}" ]]; then
echo "qemu debug log: ${qemu_debug_log}" >&2
fi
exit "${status}"
+99
View File
@@ -0,0 +1,99 @@
use crate::drivers::input::ps2;
use crate::io::{restore_flags, save_flags_and_disable_interrupts};
use crate::tty::Tty;
static mut TTY: *mut Tty = core::ptr::null_mut();
static mut RAW_MODE: bool = false;
pub unsafe fn init(tty: *mut Tty) {
unsafe {
TTY = tty;
}
}
pub fn write(bytes: &[u8]) -> usize {
let rflags = save_flags_and_disable_interrupts();
let written = write_unlocked(bytes);
restore_flags(rflags);
written
}
pub fn write_panic(bytes: &[u8]) -> usize {
write_unlocked(bytes)
}
pub fn set_raw_mode(enabled: bool) {
unsafe {
RAW_MODE = enabled;
}
}
pub fn reset_terminal() {
set_raw_mode(false);
unsafe {
if !TTY.is_null() {
(*TTY).reset();
}
}
}
pub fn size() -> (usize, usize) {
unsafe {
if TTY.is_null() {
(80, 25)
} else {
((*TTY).columns(), (*TTY).rows())
}
}
}
fn write_unlocked(bytes: &[u8]) -> usize {
unsafe {
if TTY.is_null() {
0
} else {
let tty = &mut *TTY;
for byte in bytes {
tty.put_char(*byte);
}
bytes.len()
}
}
}
pub fn read(buffer: &mut [u8]) -> usize {
let mut read = 0;
while read < buffer.len() {
if let Some(ch) = ps2::read_char() {
if unsafe { RAW_MODE } {
buffer[read] = ch;
read += 1;
break;
}
match ch {
0x08 => {
if read > 0 {
read -= 1;
}
}
b'\r' | b'\n' => {
buffer[read] = b'\n';
read += 1;
break;
}
ch => {
buffer[read] = ch;
read += 1;
}
}
} else {
break;
}
}
read
}
pub fn has_input() -> bool {
ps2::has_char()
}
+1
View File
@@ -0,0 +1 @@
pub mod pci;
+73
View File
@@ -0,0 +1,73 @@
use crate::io::{inl, outl};
const CONFIG_ADDRESS: u16 = 0xcf8;
const CONFIG_DATA: u16 = 0xcfc;
const INVALID_VENDOR_ID: u16 = 0xffff;
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct PciDevice {
pub bus: u8,
pub device: u8,
pub function: u8,
pub vendor_id: u16,
pub device_id: u16,
pub class_code: u8,
pub subclass: u8,
pub prog_if: u8,
}
pub trait Visitor {
fn visit(&mut self, device: PciDevice);
}
pub fn scan<V: Visitor>(visitor: &mut V) {
for bus in 0..=0 {
for device in 0..32 {
let header = read_config(bus, device, 0, 0x0c);
let multifunction = header & (1 << 23) != 0;
let functions = if multifunction { 8 } else { 1 };
for function in 0..functions {
let vendor_device = read_config(bus, device, function, 0x00);
let vendor_id = vendor_device as u16;
if vendor_id == INVALID_VENDOR_ID {
continue;
}
let class = read_config(bus, device, function, 0x08);
visitor.visit(PciDevice {
bus,
device,
function,
vendor_id,
device_id: (vendor_device >> 16) as u16,
class_code: (class >> 24) as u8,
subclass: (class >> 16) as u8,
prog_if: (class >> 8) as u8,
});
}
}
}
}
pub fn read_bar(device: PciDevice, index: u8) -> u32 {
read_config(
device.bus,
device.device,
device.function,
0x10 + (index as u8 * 4),
)
}
pub fn read_config(bus: u8, device: u8, function: u8, offset: u8) -> u32 {
let address = 0x8000_0000
| ((bus as u32) << 16)
| ((device as u32) << 11)
| ((function as u32) << 8)
| ((offset as u32) & 0xfc);
unsafe {
outl(CONFIG_ADDRESS, address);
inl(CONFIG_DATA)
}
}
+1
View File
@@ -0,0 +1 @@
pub mod ps2;
+328
View File
@@ -0,0 +1,328 @@
use crate::drivers::platform::pic;
use crate::io::{disable_interrupts, enable_interrupts, inb};
const DATA_PORT: u16 = 0x60;
const STATUS_PORT: u16 = 0x64;
const OUTPUT_FULL: u8 = 1 << 0;
const BUFFER_SIZE: usize = 256;
static mut KEYBOARD: Ps2Keyboard = Ps2Keyboard::new();
pub struct Ps2Keyboard {
buffer: [u8; BUFFER_SIZE],
read_index: usize,
write_index: usize,
shift: bool,
extended: bool,
}
impl Ps2Keyboard {
const fn new() -> Self {
Self {
buffer: [0; BUFFER_SIZE],
read_index: 0,
write_index: 0,
shift: false,
extended: false,
}
}
fn push(&mut self, byte: u8) {
let next = (self.write_index + 1) % BUFFER_SIZE;
if next != self.read_index {
self.buffer[self.write_index] = byte;
self.write_index = next;
}
}
fn pop(&mut self) -> Option<u8> {
if self.read_index == self.write_index {
return None;
}
let byte = self.buffer[self.read_index];
self.read_index = (self.read_index + 1) % BUFFER_SIZE;
Some(byte)
}
fn handle_scancode(&mut self, scancode: u8) {
if scancode == 0xe0 {
self.extended = true;
return;
}
if self.extended {
self.extended = false;
if scancode & 0x80 != 0 {
return;
}
match scancode {
0x48 => self.push_escape_sequence(b"\x1b[A"),
0x50 => self.push_escape_sequence(b"\x1b[B"),
0x4b => self.push_escape_sequence(b"\x1b[D"),
0x4d => self.push_escape_sequence(b"\x1b[C"),
0x47 => self.push_escape_sequence(b"\x1b[H"),
0x4f => self.push_escape_sequence(b"\x1b[F"),
0x53 => self.push_escape_sequence(b"\x1b[3~"),
_ => {}
}
return;
}
let released = scancode & 0x80 != 0;
let code = scancode & 0x7f;
match code {
0x2a | 0x36 => {
self.shift = !released;
return;
}
_ if released => return,
_ => {}
}
if let Some(ascii) = scancode_set1_to_ascii(code, self.shift) {
self.push(ascii);
}
}
fn push_escape_sequence(&mut self, bytes: &[u8]) {
for byte in bytes {
self.push(*byte);
}
}
}
pub unsafe fn init() {
unsafe {
drain_output_buffer();
pic::unmask_irq(pic::KEYBOARD_IRQ);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_ps2_keyboard_interrupt() {
unsafe {
if inb(STATUS_PORT) & OUTPUT_FULL != 0 {
let scancode = inb(DATA_PORT);
(*(&raw mut KEYBOARD)).handle_scancode(scancode);
}
pic::end_of_interrupt(pic::KEYBOARD_IRQ);
}
}
pub fn read_char() -> Option<u8> {
disable_interrupts();
let ch = unsafe { (*(&raw mut KEYBOARD)).pop() };
enable_interrupts();
ch
}
pub fn has_char() -> bool {
disable_interrupts();
let ready = unsafe {
let keyboard = &*(&raw const KEYBOARD);
keyboard.read_index != keyboard.write_index
};
enable_interrupts();
ready
}
unsafe fn drain_output_buffer() {
unsafe {
while inb(STATUS_PORT) & OUTPUT_FULL != 0 {
let _ = inb(DATA_PORT);
}
}
}
fn scancode_set1_to_ascii(code: u8, shift: bool) -> Option<u8> {
let byte = match code {
0x01 => 0x1b,
0x02 => {
if shift {
b'!'
} else {
b'1'
}
}
0x03 => {
if shift {
b'@'
} else {
b'2'
}
}
0x04 => {
if shift {
b'#'
} else {
b'3'
}
}
0x05 => {
if shift {
b'$'
} else {
b'4'
}
}
0x06 => {
if shift {
b'%'
} else {
b'5'
}
}
0x07 => {
if shift {
b'^'
} else {
b'6'
}
}
0x08 => {
if shift {
b'&'
} else {
b'7'
}
}
0x09 => {
if shift {
b'*'
} else {
b'8'
}
}
0x0a => {
if shift {
b'('
} else {
b'9'
}
}
0x0b => {
if shift {
b')'
} else {
b'0'
}
}
0x0c => {
if shift {
b'_'
} else {
b'-'
}
}
0x0d => {
if shift {
b'+'
} else {
b'='
}
}
0x0e => 0x08,
0x0f => b'\t',
0x10 => letter(b'q', shift),
0x11 => letter(b'w', shift),
0x12 => letter(b'e', shift),
0x13 => letter(b'r', shift),
0x14 => letter(b't', shift),
0x15 => letter(b'y', shift),
0x16 => letter(b'u', shift),
0x17 => letter(b'i', shift),
0x18 => letter(b'o', shift),
0x19 => letter(b'p', shift),
0x1a => {
if shift {
b'{'
} else {
b'['
}
}
0x1b => {
if shift {
b'}'
} else {
b']'
}
}
0x1c => b'\r',
0x1e => letter(b'a', shift),
0x1f => letter(b's', shift),
0x20 => letter(b'd', shift),
0x21 => letter(b'f', shift),
0x22 => letter(b'g', shift),
0x23 => letter(b'h', shift),
0x24 => letter(b'j', shift),
0x25 => letter(b'k', shift),
0x26 => letter(b'l', shift),
0x27 => {
if shift {
b':'
} else {
b';'
}
}
0x28 => {
if shift {
b'"'
} else {
b'\''
}
}
0x29 => {
if shift {
b'~'
} else {
b'`'
}
}
0x2b => {
if shift {
b'|'
} else {
b'\\'
}
}
0x2c => letter(b'z', shift),
0x2d => letter(b'x', shift),
0x2e => letter(b'c', shift),
0x2f => letter(b'v', shift),
0x30 => letter(b'b', shift),
0x31 => letter(b'n', shift),
0x32 => letter(b'm', shift),
0x33 => {
if shift {
b'<'
} else {
b','
}
}
0x34 => {
if shift {
b'>'
} else {
b'.'
}
}
0x35 => {
if shift {
b'?'
} else {
b'/'
}
}
0x39 => b' ',
_ => return None,
};
Some(byte)
}
const fn letter(lower: u8, shift: bool) -> u8 {
if shift { lower - 32 } else { lower }
}
+4
View File
@@ -0,0 +1,4 @@
pub mod bus;
pub mod input;
pub mod platform;
pub mod storage;
+2
View File
@@ -0,0 +1,2 @@
pub mod pic;
pub mod timer;
+79
View File
@@ -0,0 +1,79 @@
use crate::io::{disable_interrupts, inb, outb};
const PIC1_COMMAND: u16 = 0x20;
const PIC1_DATA: u16 = 0x21;
const PIC2_COMMAND: u16 = 0xa0;
const PIC2_DATA: u16 = 0xa1;
const ICW1_INIT: u8 = 0x10;
const ICW1_ICW4: u8 = 0x01;
const ICW4_8086: u8 = 0x01;
const PIC_EOI: u8 = 0x20;
pub const PIC1_OFFSET: u8 = 0x20;
pub const PIC2_OFFSET: u8 = 0x28;
pub const TIMER_IRQ: u8 = 0;
pub const KEYBOARD_IRQ: u8 = 1;
pub unsafe fn remap_and_mask_all() {
unsafe {
disable_interrupts();
let mask1 = inb(PIC1_DATA);
let mask2 = inb(PIC2_DATA);
outb(PIC1_DATA, 0xff);
outb(PIC2_DATA, 0xff);
io_wait();
outb(PIC1_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC2_COMMAND, ICW1_INIT | ICW1_ICW4);
io_wait();
outb(PIC1_DATA, PIC1_OFFSET);
io_wait();
outb(PIC2_DATA, PIC2_OFFSET);
io_wait();
outb(PIC1_DATA, 4);
io_wait();
outb(PIC2_DATA, 2);
io_wait();
outb(PIC1_DATA, ICW4_8086);
io_wait();
outb(PIC2_DATA, ICW4_8086);
io_wait();
outb(PIC1_DATA, mask1 | 0xff);
outb(PIC2_DATA, mask2 | 0xff);
}
}
pub unsafe fn unmask_irq(irq: u8) {
let (port, bit) = if irq < 8 {
(PIC1_DATA, irq)
} else {
(PIC2_DATA, irq - 8)
};
unsafe {
let mask = inb(port) & !(1 << bit);
outb(port, mask);
}
}
pub unsafe fn end_of_interrupt(irq: u8) {
unsafe {
if irq >= 8 {
outb(PIC2_COMMAND, PIC_EOI);
}
outb(PIC1_COMMAND, PIC_EOI);
}
}
unsafe fn io_wait() {
unsafe {
core::arch::asm!("pause", options(nomem, nostack, preserves_flags));
}
}
+46
View File
@@ -0,0 +1,46 @@
use core::sync::atomic::{AtomicU64, Ordering};
use crate::{drivers::platform::pic, io::outb};
const PIT_CHANNEL0: u16 = 0x40;
const PIT_COMMAND: u16 = 0x43;
const PIT_FREQUENCY: u32 = 1_193_182;
pub const HZ: u64 = 100;
static TICKS: AtomicU64 = AtomicU64::new(0);
static WALL_CLOCK_BOOT_SEC: AtomicU64 = AtomicU64::new(0);
pub unsafe fn init() {
let divisor = (PIT_FREQUENCY / HZ as u32) as u16;
unsafe {
outb(PIT_COMMAND, 0x36);
outb(PIT_CHANNEL0, divisor as u8);
outb(PIT_CHANNEL0, (divisor >> 8) as u8);
pic::unmask_irq(pic::TIMER_IRQ);
}
}
pub fn tick() {
TICKS.fetch_add(1, Ordering::Relaxed);
}
pub fn ticks() -> u64 {
TICKS.load(Ordering::Relaxed)
}
pub fn monotonic_time() -> (u64, u64) {
let ticks = ticks();
let sec = ticks / HZ;
let nsec = (ticks % HZ) * (1_000_000_000 / HZ);
(sec, nsec)
}
pub fn set_wall_clock_boot_time(unix_seconds: u64) {
WALL_CLOCK_BOOT_SEC.store(unix_seconds, Ordering::Relaxed);
}
pub fn realtime() -> (u64, u64) {
let (uptime_sec, nsec) = monotonic_time();
let boot_sec = WALL_CLOCK_BOOT_SEC.load(Ordering::Relaxed);
(boot_sec.saturating_add(uptime_sec), nsec)
}
+493
View File
@@ -0,0 +1,493 @@
use crate::drivers::{
bus::pci::{self, PciDevice, Visitor},
storage::block::{BlockDevice, BlockError},
};
use crate::memory::PhysicalMemoryManager;
use core::ptr::{addr_of, addr_of_mut};
const AHCI_CLASS_MASS_STORAGE: u8 = 0x01;
const AHCI_SUBCLASS_SATA: u8 = 0x06;
const AHCI_PROG_IF: u8 = 0x01;
const AHCI_BAR_INDEX: u8 = 5;
const MAX_AHCI_CONTROLLERS: usize = 4;
const MAX_AHCI_DISKS: usize = 8;
const SATA_SIG_ATA: u32 = 0x0000_0101;
const SATA_SIG_ATAPI: u32 = 0xeb14_0101;
const HBA_PORT_DET_PRESENT: u32 = 3;
const HBA_PORT_IPM_ACTIVE: u32 = 1;
const HBA_PX_CMD_ST: u32 = 1 << 0;
const HBA_PX_CMD_FRE: u32 = 1 << 4;
const HBA_PX_CMD_FR: u32 = 1 << 14;
const HBA_PX_CMD_CR: u32 = 1 << 15;
const HBA_PX_IS_TFES: u32 = 1 << 30;
const ATA_CMD_READ_DMA_EXT: u8 = 0x25;
const ATA_CMD_WRITE_DMA_EXT: u8 = 0x35;
const FIS_TYPE_REG_H2D: u8 = 0x27;
#[repr(C)]
pub struct HbaMemory {
cap: u32,
ghc: u32,
is: u32,
pi: u32,
vs: u32,
ccc_ctl: u32,
ccc_pts: u32,
em_loc: u32,
em_ctl: u32,
cap2: u32,
bohc: u32,
reserved: [u8; 0xa0 - 0x2c],
vendor: [u8; 0x100 - 0xa0],
ports: [HbaPort; 32],
}
#[repr(C)]
pub struct HbaPort {
clb: u32,
clbu: u32,
fb: u32,
fbu: u32,
is: u32,
ie: u32,
cmd: u32,
reserved0: u32,
tfd: u32,
sig: u32,
ssts: u32,
sctl: u32,
serr: u32,
sact: u32,
ci: u32,
sntf: u32,
fbs: u32,
reserved1: [u32; 11],
vendor: [u32; 4],
}
#[repr(C)]
struct HbaCommandHeader {
flags: u16,
prdtl: u16,
prdbc: u32,
ctba: u32,
ctbau: u32,
reserved: [u32; 4],
}
#[repr(C)]
struct HbaPrdtEntry {
dba: u32,
dbau: u32,
reserved: u32,
dbc_i: u32,
}
#[repr(C)]
struct HbaCommandTable {
cfis: [u8; 64],
acmd: [u8; 16],
reserved: [u8; 48],
prdt: [HbaPrdtEntry; 1],
}
#[repr(C, align(4096))]
struct AlignedPage([u8; 4096]);
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct AhciController {
pub pci: PciDevice,
pub abar: u64,
pub ports_implemented: u32,
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct AhciDisk {
slot: usize,
controller: usize,
port: usize,
signature: u32,
}
impl AhciDisk {
pub const fn empty() -> Self {
Self {
slot: usize::MAX,
controller: usize::MAX,
port: usize::MAX,
signature: 0,
}
}
#[allow(dead_code)]
pub fn is_present(self) -> bool {
self.controller != usize::MAX
}
#[allow(dead_code)]
pub fn port(self) -> usize {
self.port
}
#[allow(dead_code)]
pub fn is_ata(self) -> bool {
self.signature == SATA_SIG_ATA
}
}
impl BlockDevice for AhciDisk {
fn read_sector(&mut self, lba: u64, buffer: &mut [u8]) -> Result<(), BlockError> {
if buffer.len() < 512 || self.slot >= MAX_AHCI_DISKS || !self.is_ata() {
return Err(BlockError::Invalid);
}
unsafe {
let controller = CONTROLLERS[self.controller];
let hba = controller.abar as *mut HbaMemory;
let port = addr_of_mut!((*hba).ports[self.port]);
read_dma_ext(self.slot, port, lba)?;
let read_buffer = READ_BUFFERS[self.slot].0.as_ptr();
core::ptr::copy_nonoverlapping(read_buffer, buffer.as_mut_ptr(), 512);
}
Ok(())
}
fn write_sector(&mut self, lba: u64, buffer: &[u8]) -> Result<(), BlockError> {
if buffer.len() < 512 || self.slot >= MAX_AHCI_DISKS || !self.is_ata() {
return Err(BlockError::Invalid);
}
unsafe {
core::ptr::copy_nonoverlapping(
buffer.as_ptr(),
READ_BUFFERS[self.slot].0.as_mut_ptr(),
512,
);
let controller = CONTROLLERS[self.controller];
let hba = controller.abar as *mut HbaMemory;
let port = addr_of_mut!((*hba).ports[self.port]);
write_dma_ext(self.slot, port, lba)?;
}
Ok(())
}
}
struct AhciProbe {
controllers: [AhciController; MAX_AHCI_CONTROLLERS],
controller_count: usize,
disks: [AhciDisk; MAX_AHCI_DISKS],
disk_count: usize,
}
impl AhciProbe {
const fn new() -> Self {
Self {
controllers: [AhciController {
pci: PciDevice {
bus: 0,
device: 0,
function: 0,
vendor_id: 0,
device_id: 0,
class_code: 0,
subclass: 0,
prog_if: 0,
},
abar: 0,
ports_implemented: 0,
}; MAX_AHCI_CONTROLLERS],
controller_count: 0,
disks: [AhciDisk::empty(); MAX_AHCI_DISKS],
disk_count: 0,
}
}
}
impl Visitor for AhciProbe {
fn visit(&mut self, device: PciDevice) {
if device.class_code != AHCI_CLASS_MASS_STORAGE
|| device.subclass != AHCI_SUBCLASS_SATA
|| device.prog_if != AHCI_PROG_IF
|| self.controller_count >= MAX_AHCI_CONTROLLERS
{
return;
}
let bar5 = pci::read_bar(device, AHCI_BAR_INDEX);
let abar = (bar5 & 0xffff_fff0) as u64;
if abar == 0 {
return;
}
let hba = abar as *mut HbaMemory;
let pi = unsafe { core::ptr::read_volatile(&(*hba).pi) };
let controller_index = self.controller_count;
self.controllers[controller_index] = AhciController {
pci: device,
abar,
ports_implemented: pi,
};
self.controller_count += 1;
for port in 0..32 {
if pi & (1 << port) == 0 || self.disk_count >= MAX_AHCI_DISKS {
continue;
}
let port_ref = unsafe { addr_of!((*hba).ports[port]) };
if !port_has_device(port_ref) {
continue;
}
let signature = unsafe { core::ptr::read_volatile(addr_of!((*port_ref).sig)) };
if signature != SATA_SIG_ATA && signature != SATA_SIG_ATAPI {
continue;
}
self.disks[self.disk_count] = AhciDisk {
slot: self.disk_count,
controller: controller_index,
port,
signature,
};
self.disk_count += 1;
}
}
}
static mut CONTROLLERS: [AhciController; MAX_AHCI_CONTROLLERS] = [AhciController {
pci: PciDevice {
bus: 0,
device: 0,
function: 0,
vendor_id: 0,
device_id: 0,
class_code: 0,
subclass: 0,
prog_if: 0,
},
abar: 0,
ports_implemented: 0,
}; MAX_AHCI_CONTROLLERS];
static mut CONTROLLER_COUNT: usize = 0;
static mut DISKS: [AhciDisk; MAX_AHCI_DISKS] = [AhciDisk::empty(); MAX_AHCI_DISKS];
static mut DISK_COUNT: usize = 0;
static mut COMMAND_LISTS: [AlignedPage; MAX_AHCI_DISKS] =
[const { AlignedPage([0; 4096]) }; MAX_AHCI_DISKS];
static mut RECEIVED_FIS: [AlignedPage; MAX_AHCI_DISKS] =
[const { AlignedPage([0; 4096]) }; MAX_AHCI_DISKS];
static mut COMMAND_TABLES: [AlignedPage; MAX_AHCI_DISKS] =
[const { AlignedPage([0; 4096]) }; MAX_AHCI_DISKS];
static mut READ_BUFFERS: [AlignedPage; MAX_AHCI_DISKS] =
[const { AlignedPage([0; 4096]) }; MAX_AHCI_DISKS];
pub unsafe fn init(_memory: &mut PhysicalMemoryManager) -> usize {
let mut probe = AhciProbe::new();
pci::scan(&mut probe);
unsafe {
CONTROLLER_COUNT = probe.controller_count;
DISK_COUNT = probe.disk_count;
for index in 0..probe.controller_count {
CONTROLLERS[index] = probe.controllers[index];
}
for index in 0..probe.disk_count {
DISKS[index] = probe.disks[index];
configure_disk(index);
}
DISK_COUNT
}
}
#[allow(dead_code)]
pub fn disk_count() -> usize {
unsafe { DISK_COUNT }
}
pub fn first_disk() -> Option<AhciDisk> {
unsafe {
if DISK_COUNT == 0 {
None
} else {
Some(DISKS[0])
}
}
}
fn port_has_device(port: *const HbaPort) -> bool {
let ssts = unsafe { core::ptr::read_volatile(addr_of!((*port).ssts)) };
let det = ssts & 0x0f;
let ipm = (ssts >> 8) & 0x0f;
det == HBA_PORT_DET_PRESENT && ipm == HBA_PORT_IPM_ACTIVE
}
unsafe fn configure_disk(index: usize) {
unsafe {
let disk = DISKS[index];
let controller = CONTROLLERS[disk.controller];
let hba = controller.abar as *mut HbaMemory;
let port = addr_of_mut!((*hba).ports[disk.port]);
stop_command_engine(port);
core::ptr::write_bytes(COMMAND_LISTS[index].0.as_mut_ptr(), 0, 4096);
core::ptr::write_bytes(RECEIVED_FIS[index].0.as_mut_ptr(), 0, 4096);
core::ptr::write_bytes(COMMAND_TABLES[index].0.as_mut_ptr(), 0, 4096);
core::ptr::write_volatile(
addr_of_mut!((*port).clb),
COMMAND_LISTS[index].0.as_ptr() as u32,
);
core::ptr::write_volatile(addr_of_mut!((*port).clbu), 0);
core::ptr::write_volatile(
addr_of_mut!((*port).fb),
RECEIVED_FIS[index].0.as_ptr() as u32,
);
core::ptr::write_volatile(addr_of_mut!((*port).fbu), 0);
core::ptr::write_volatile(addr_of_mut!((*port).serr), u32::MAX);
core::ptr::write_volatile(addr_of_mut!((*port).is), u32::MAX);
start_command_engine(port);
}
}
unsafe fn read_dma_ext(slot: usize, port: *mut HbaPort, lba: u64) -> Result<(), BlockError> {
unsafe {
while core::ptr::read_volatile(addr_of!((*port).tfd)) & 0x88 != 0 {}
let command_list = COMMAND_LISTS[slot].0.as_mut_ptr() as *mut HbaCommandHeader;
let command_table = COMMAND_TABLES[slot].0.as_mut_ptr() as *mut HbaCommandTable;
core::ptr::write_bytes(
command_table.cast::<u8>(),
0,
core::mem::size_of::<HbaCommandTable>(),
);
(*command_list).flags = 5;
(*command_list).prdtl = 1;
(*command_list).prdbc = 0;
(*command_list).ctba = command_table as u32;
(*command_list).ctbau = 0;
(*command_table).prdt[0].dba = READ_BUFFERS[slot].0.as_mut_ptr() as u32;
(*command_table).prdt[0].dbau = 0;
(*command_table).prdt[0].reserved = 0;
(*command_table).prdt[0].dbc_i = (512 - 1) | (1 << 31);
let cfis = &mut (*command_table).cfis;
cfis[0] = FIS_TYPE_REG_H2D;
cfis[1] = 1 << 7;
cfis[2] = ATA_CMD_READ_DMA_EXT;
cfis[4] = lba as u8;
cfis[5] = (lba >> 8) as u8;
cfis[6] = (lba >> 16) as u8;
cfis[7] = 1 << 6;
cfis[8] = (lba >> 24) as u8;
cfis[9] = (lba >> 32) as u8;
cfis[10] = (lba >> 40) as u8;
cfis[12] = 1;
cfis[13] = 0;
core::ptr::write_volatile(addr_of_mut!((*port).is), u32::MAX);
core::ptr::write_volatile(addr_of_mut!((*port).ci), 1);
let mut timeout = 10_000_000usize;
while core::ptr::read_volatile(addr_of!((*port).ci)) & 1 != 0 {
if core::ptr::read_volatile(addr_of!((*port).is)) & HBA_PX_IS_TFES != 0 {
return Err(BlockError::Io);
}
timeout = timeout.saturating_sub(1);
if timeout == 0 {
return Err(BlockError::Io);
}
}
if core::ptr::read_volatile(addr_of!((*port).is)) & HBA_PX_IS_TFES != 0 {
Err(BlockError::Io)
} else {
Ok(())
}
}
}
unsafe fn write_dma_ext(slot: usize, port: *mut HbaPort, lba: u64) -> Result<(), BlockError> {
unsafe {
while core::ptr::read_volatile(addr_of!((*port).tfd)) & 0x88 != 0 {}
let command_list = COMMAND_LISTS[slot].0.as_mut_ptr() as *mut HbaCommandHeader;
let command_table = COMMAND_TABLES[slot].0.as_mut_ptr() as *mut HbaCommandTable;
core::ptr::write_bytes(
command_table.cast::<u8>(),
0,
core::mem::size_of::<HbaCommandTable>(),
);
(*command_list).flags = 5 | (1 << 6);
(*command_list).prdtl = 1;
(*command_list).prdbc = 0;
(*command_list).ctba = command_table as u32;
(*command_list).ctbau = 0;
(*command_table).prdt[0].dba = READ_BUFFERS[slot].0.as_mut_ptr() as u32;
(*command_table).prdt[0].dbau = 0;
(*command_table).prdt[0].reserved = 0;
(*command_table).prdt[0].dbc_i = (512 - 1) | (1 << 31);
let cfis = &mut (*command_table).cfis;
cfis[0] = FIS_TYPE_REG_H2D;
cfis[1] = 1 << 7;
cfis[2] = ATA_CMD_WRITE_DMA_EXT;
cfis[4] = lba as u8;
cfis[5] = (lba >> 8) as u8;
cfis[6] = (lba >> 16) as u8;
cfis[7] = 1 << 6;
cfis[8] = (lba >> 24) as u8;
cfis[9] = (lba >> 32) as u8;
cfis[10] = (lba >> 40) as u8;
cfis[12] = 1;
cfis[13] = 0;
core::ptr::write_volatile(addr_of_mut!((*port).is), u32::MAX);
core::ptr::write_volatile(addr_of_mut!((*port).ci), 1);
let mut timeout = 10_000_000usize;
while core::ptr::read_volatile(addr_of!((*port).ci)) & 1 != 0 {
if core::ptr::read_volatile(addr_of!((*port).is)) & HBA_PX_IS_TFES != 0 {
return Err(BlockError::Io);
}
timeout = timeout.saturating_sub(1);
if timeout == 0 {
return Err(BlockError::Io);
}
}
if core::ptr::read_volatile(addr_of!((*port).is)) & HBA_PX_IS_TFES != 0 {
Err(BlockError::Io)
} else {
Ok(())
}
}
}
unsafe fn stop_command_engine(port: *mut HbaPort) {
unsafe {
let mut cmd = core::ptr::read_volatile(addr_of!((*port).cmd));
cmd &= !HBA_PX_CMD_ST;
core::ptr::write_volatile(addr_of_mut!((*port).cmd), cmd);
while core::ptr::read_volatile(addr_of!((*port).cmd)) & HBA_PX_CMD_CR != 0 {}
cmd = core::ptr::read_volatile(addr_of!((*port).cmd));
cmd &= !HBA_PX_CMD_FRE;
core::ptr::write_volatile(addr_of_mut!((*port).cmd), cmd);
while core::ptr::read_volatile(addr_of!((*port).cmd)) & HBA_PX_CMD_FR != 0 {}
}
}
unsafe fn start_command_engine(port: *mut HbaPort) {
unsafe {
let mut cmd = core::ptr::read_volatile(addr_of!((*port).cmd));
cmd |= HBA_PX_CMD_FRE;
core::ptr::write_volatile(addr_of_mut!((*port).cmd), cmd);
cmd |= HBA_PX_CMD_ST;
core::ptr::write_volatile(addr_of_mut!((*port).cmd), cmd);
}
}
+67
View File
@@ -0,0 +1,67 @@
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum BlockError {
NoDevice,
Io,
Unsupported,
Invalid,
}
pub trait BlockDevice {
fn sector_size(&self) -> usize {
512
}
fn read_sector(&mut self, lba: u64, buffer: &mut [u8]) -> Result<(), BlockError>;
fn write_sector(&mut self, _lba: u64, _buffer: &[u8]) -> Result<(), BlockError> {
Err(BlockError::Unsupported)
}
fn read_at(&mut self, offset: u64, buffer: &mut [u8]) -> Result<(), BlockError> {
let sector_size = self.sector_size();
if sector_size == 0 {
return Err(BlockError::Invalid);
}
let mut done = 0;
let mut scratch = [0u8; 512];
while done < buffer.len() {
let absolute = offset + done as u64;
let lba = absolute / sector_size as u64;
let sector_offset = (absolute % sector_size as u64) as usize;
self.read_sector(lba, &mut scratch)?;
let chunk = (sector_size - sector_offset).min(buffer.len() - done);
buffer[done..done + chunk]
.copy_from_slice(&scratch[sector_offset..sector_offset + chunk]);
done += chunk;
}
Ok(())
}
fn write_at(&mut self, offset: u64, buffer: &[u8]) -> Result<(), BlockError> {
let sector_size = self.sector_size();
if sector_size == 0 {
return Err(BlockError::Invalid);
}
let mut done = 0;
let mut scratch = [0u8; 512];
while done < buffer.len() {
let absolute = offset + done as u64;
let lba = absolute / sector_size as u64;
let sector_offset = (absolute % sector_size as u64) as usize;
self.read_sector(lba, &mut scratch)?;
let chunk = (sector_size - sector_offset).min(buffer.len() - done);
scratch[sector_offset..sector_offset + chunk]
.copy_from_slice(&buffer[done..done + chunk]);
self.write_sector(lba, &scratch)?;
done += chunk;
}
Ok(())
}
}
+3
View File
@@ -0,0 +1,3 @@
pub mod ahci;
pub mod block;
pub mod partition;
+102
View File
@@ -0,0 +1,102 @@
use crate::drivers::storage::block::{BlockDevice, BlockError};
const GPT_HEADER_LBA: u64 = 1;
const GPT_SIGNATURE: &[u8; 8] = b"EFI PART";
const GPT_ENTRY_SIZE_MIN: usize = 128;
const LINUX_FILESYSTEM_GUID: [u8; 16] = [
0xaf, 0x3d, 0xc6, 0x0f, 0x83, 0x84, 0x72, 0x47, 0x8e, 0x79, 0x3d, 0x69, 0xd8, 0x47, 0x7d, 0xe4,
];
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct Partition {
pub first_lba: u64,
pub last_lba: u64,
}
#[derive(Clone, Copy)]
pub struct PartitionBlockDevice<D: BlockDevice> {
inner: D,
first_lba: u64,
}
impl<D: BlockDevice> PartitionBlockDevice<D> {
pub const fn new(inner: D, partition: Partition) -> Self {
Self {
inner,
first_lba: partition.first_lba,
}
}
}
impl<D: BlockDevice> BlockDevice for PartitionBlockDevice<D> {
fn sector_size(&self) -> usize {
self.inner.sector_size()
}
fn read_sector(&mut self, lba: u64, buffer: &mut [u8]) -> Result<(), BlockError> {
self.inner.read_sector(self.first_lba + lba, buffer)
}
fn write_sector(&mut self, lba: u64, buffer: &[u8]) -> Result<(), BlockError> {
self.inner.write_sector(self.first_lba + lba, buffer)
}
}
pub fn find_linux_partition<D: BlockDevice>(device: &mut D) -> Result<Partition, BlockError> {
let mut sector = [0u8; 512];
device.read_sector(GPT_HEADER_LBA, &mut sector)?;
if &sector[0..8] != GPT_SIGNATURE {
return Err(BlockError::Unsupported);
}
let entries_lba = le_u64(&sector, 0x48);
let entry_count = le_u32(&sector, 0x50) as usize;
let entry_size = le_u32(&sector, 0x54) as usize;
if entry_size < GPT_ENTRY_SIZE_MIN || entry_size > 512 {
return Err(BlockError::Unsupported);
}
let entries_per_sector = 512 / entry_size;
for index in 0..entry_count {
let sector_lba = entries_lba + (index / entries_per_sector) as u64;
let sector_offset = (index % entries_per_sector) * entry_size;
device.read_sector(sector_lba, &mut sector)?;
let entry = &sector[sector_offset..sector_offset + entry_size];
if entry[0..16] == LINUX_FILESYSTEM_GUID {
let first_lba = le_u64(entry, 0x20);
let last_lba = le_u64(entry, 0x28);
if first_lba != 0 && last_lba >= first_lba {
return Ok(Partition {
first_lba,
last_lba,
});
}
}
}
Err(BlockError::NoDevice)
}
fn le_u32(buffer: &[u8], offset: usize) -> u32 {
u32::from_le_bytes([
buffer[offset],
buffer[offset + 1],
buffer[offset + 2],
buffer[offset + 3],
])
}
fn le_u64(buffer: &[u8], offset: usize) -> u64 {
u64::from_le_bytes([
buffer[offset],
buffer[offset + 1],
buffer[offset + 2],
buffer[offset + 3],
buffer[offset + 4],
buffer[offset + 5],
buffer[offset + 6],
buffer[offset + 7],
])
}
+402
View File
@@ -0,0 +1,402 @@
pub type EfiHandle = *mut core::ffi::c_void;
pub type EfiEvent = *mut core::ffi::c_void;
pub type EfiStatus = usize;
pub const EFI_SUCCESS: EfiStatus = 0;
pub const EFI_NOT_FOUND: EfiStatus = 14;
const EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID: EfiGuid = EfiGuid::new(
0x9042a9de,
0x23dc,
0x4a38,
[0x96, 0xfb, 0x7a, 0xde, 0xd0, 0x80, 0x51, 0x6a],
);
pub const EFI_LOADED_IMAGE_PROTOCOL_GUID: EfiGuid = EfiGuid::new(
0x5b1b31a1,
0x9562,
0x11d2,
[0x8e, 0x3f, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
);
pub const EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID: EfiGuid = EfiGuid::new(
0x0964e5b22,
0x6459,
0x11d2,
[0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
);
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EfiGuid {
data1: u32,
data2: u16,
data3: u16,
data4: [u8; 8],
}
impl EfiGuid {
pub const fn new(data1: u32, data2: u16, data3: u16, data4: [u8; 8]) -> Self {
Self {
data1,
data2,
data3,
data4,
}
}
}
#[repr(C)]
pub struct EfiTableHeader {
signature: u64,
revision: u32,
header_size: u32,
crc32: u32,
reserved: u32,
}
#[repr(C)]
pub struct EfiSystemTable {
hdr: EfiTableHeader,
firmware_vendor: *mut u16,
firmware_revision: u32,
console_in_handle: EfiHandle,
pub con_in: *mut EfiSimpleTextInputProtocol,
console_out_handle: EfiHandle,
con_out: *mut core::ffi::c_void,
standard_error_handle: EfiHandle,
std_err: *mut core::ffi::c_void,
pub runtime_services: *mut EfiRuntimeServices,
pub boot_services: *mut EfiBootServices,
number_of_table_entries: usize,
configuration_table: *mut core::ffi::c_void,
}
#[repr(C)]
pub struct EfiInputKey {
pub scan_code: u16,
pub unicode_char: u16,
}
#[repr(C)]
pub struct EfiSimpleTextInputProtocol {
reset: usize,
pub read_key_stroke: extern "efiapi" fn(
this: *mut EfiSimpleTextInputProtocol,
key: *mut EfiInputKey,
) -> EfiStatus,
pub wait_for_key: EfiEvent,
}
#[repr(C)]
pub struct EfiRuntimeServices {
hdr: EfiTableHeader,
pub get_time:
extern "efiapi" fn(time: *mut EfiTime, capabilities: *mut EfiTimeCapabilities) -> EfiStatus,
set_time: usize,
get_wakeup_time: usize,
set_wakeup_time: usize,
set_virtual_address_map: usize,
convert_pointer: usize,
get_variable: usize,
get_next_variable_name: usize,
set_variable: usize,
get_next_high_mono_count: usize,
reset_system: usize,
update_capsule: usize,
query_capsule_capabilities: usize,
query_variable_info: usize,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EfiTime {
pub year: u16,
pub month: u8,
pub day: u8,
pub hour: u8,
pub minute: u8,
pub second: u8,
pub pad1: u8,
pub nanosecond: u32,
pub timezone: i16,
pub daylight: u8,
pub pad2: u8,
}
#[repr(C)]
pub struct EfiTimeCapabilities {
resolution: u32,
accuracy: u32,
sets_to_zero: bool,
}
#[repr(C)]
pub struct EfiBootServices {
hdr: EfiTableHeader,
raise_tpl: usize,
restore_tpl: usize,
allocate_pages: usize,
free_pages: usize,
pub get_memory_map: extern "efiapi" fn(
memory_map_size: *mut usize,
memory_map: *mut EfiMemoryDescriptor,
map_key: *mut usize,
descriptor_size: *mut usize,
descriptor_version: *mut u32,
) -> EfiStatus,
allocate_pool: usize,
free_pool: usize,
create_event: usize,
set_timer: usize,
pub wait_for_event: extern "efiapi" fn(
number_of_events: usize,
event: *mut EfiEvent,
index: *mut usize,
) -> EfiStatus,
signal_event: usize,
close_event: usize,
check_event: usize,
install_protocol_interface: usize,
reinstall_protocol_interface: usize,
uninstall_protocol_interface: usize,
pub handle_protocol: extern "efiapi" fn(
handle: EfiHandle,
protocol: *const EfiGuid,
interface: *mut *mut core::ffi::c_void,
) -> EfiStatus,
reserved: usize,
register_protocol_notify: usize,
locate_handle: usize,
locate_device_path: usize,
install_configuration_table: usize,
load_image: usize,
start_image: usize,
exit: usize,
unload_image: usize,
pub exit_boot_services:
extern "efiapi" fn(image_handle: EfiHandle, map_key: usize) -> EfiStatus,
get_next_monotonic_count: usize,
stall: usize,
set_watchdog_timer: usize,
connect_controller: usize,
disconnect_controller: usize,
open_protocol: usize,
close_protocol: usize,
open_protocol_information: usize,
protocols_per_handle: usize,
locate_handle_buffer: usize,
pub locate_protocol: extern "efiapi" fn(
protocol: *const EfiGuid,
registration: *mut core::ffi::c_void,
interface: *mut *mut core::ffi::c_void,
) -> EfiStatus,
}
#[repr(C)]
pub struct EfiLoadedImageProtocol {
revision: u32,
parent_handle: EfiHandle,
system_table: *mut EfiSystemTable,
pub device_handle: EfiHandle,
file_path: *mut core::ffi::c_void,
reserved: *mut core::ffi::c_void,
load_options_size: u32,
load_options: *mut core::ffi::c_void,
image_base: *mut core::ffi::c_void,
image_size: u64,
image_code_type: u32,
image_data_type: u32,
unload: usize,
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct EfiMemoryDescriptor {
pub ty: u32,
pub physical_start: u64,
pub virtual_start: u64,
pub number_of_pages: u64,
pub attribute: u64,
}
pub const EFI_CONVENTIONAL_MEMORY: u32 = 7;
#[derive(Clone, Copy)]
pub struct MemoryMap {
pub ptr: *const EfiMemoryDescriptor,
pub byte_len: usize,
pub key: usize,
pub descriptor_size: usize,
}
#[repr(C)]
pub struct EfiGraphicsOutputProtocol {
query_mode: usize,
set_mode: usize,
blt: usize,
pub mode: *mut EfiGraphicsOutputProtocolMode,
}
#[repr(C)]
pub struct EfiGraphicsOutputProtocolMode {
max_mode: u32,
mode: u32,
pub info: *mut EfiGraphicsOutputModeInformation,
size_of_info: usize,
pub frame_buffer_base: u64,
frame_buffer_size: usize,
}
#[repr(C)]
pub struct EfiGraphicsOutputModeInformation {
version: u32,
pub horizontal_resolution: u32,
pub vertical_resolution: u32,
pub pixel_format: EfiGraphicsPixelFormat,
pixel_information: EfiPixelBitmask,
pub pixels_per_scan_line: u32,
}
#[repr(u32)]
#[derive(Clone, Copy, PartialEq, Eq)]
#[allow(dead_code)]
pub enum EfiGraphicsPixelFormat {
RedGreenBlueReserved8BitPerColor = 0,
BlueGreenRedReserved8BitPerColor = 1,
BitMask = 2,
BltOnly = 3,
FormatMax = 4,
}
#[repr(C)]
struct EfiPixelBitmask {
red_mask: u32,
green_mask: u32,
blue_mask: u32,
reserved_mask: u32,
}
pub unsafe fn boot_services(system_table: *mut EfiSystemTable) -> Option<*mut EfiBootServices> {
if system_table.is_null() || unsafe { (*system_table).boot_services.is_null() } {
None
} else {
Some(unsafe { (*system_table).boot_services })
}
}
pub unsafe fn read_unix_time(system_table: *mut EfiSystemTable) -> Option<u64> {
if system_table.is_null() || unsafe { (*system_table).runtime_services.is_null() } {
return None;
}
let mut time = EfiTime {
year: 0,
month: 0,
day: 0,
hour: 0,
minute: 0,
second: 0,
pad1: 0,
nanosecond: 0,
timezone: 0,
daylight: 0,
pad2: 0,
};
let status =
unsafe { ((*(*system_table).runtime_services).get_time)(&mut time, core::ptr::null_mut()) };
if status != EFI_SUCCESS {
return None;
}
efi_time_to_unix_seconds(time)
}
fn efi_time_to_unix_seconds(time: EfiTime) -> Option<u64> {
if time.year < 1970 || time.month == 0 || time.month > 12 || time.day == 0 || time.day > 31 {
return None;
}
if time.hour > 23 || time.minute > 59 || time.second > 59 {
return None;
}
let mut days = 0u64;
let mut year = 1970u16;
while year < time.year {
days += if is_leap_year(year) { 366 } else { 365 };
year += 1;
}
let mut month = 1u8;
while month < time.month {
days += days_in_month(time.year, month) as u64;
month += 1;
}
days += (time.day - 1) as u64;
Some(days * 86_400 + time.hour as u64 * 3_600 + time.minute as u64 * 60 + time.second as u64)
}
fn is_leap_year(year: u16) -> bool {
(year.is_multiple_of(4) && !year.is_multiple_of(100)) || year.is_multiple_of(400)
}
fn days_in_month(year: u16, month: u8) -> u8 {
match month {
1 | 3 | 5 | 7 | 8 | 10 | 12 => 31,
4 | 6 | 9 | 11 => 30,
2 if is_leap_year(year) => 29,
2 => 28,
_ => 0,
}
}
pub unsafe fn locate_gop(
boot_services: *mut EfiBootServices,
) -> Option<*mut EfiGraphicsOutputProtocol> {
let mut gop = core::ptr::null_mut();
let status = unsafe {
((*boot_services).locate_protocol)(
&EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID,
core::ptr::null_mut(),
&mut gop,
)
};
if status != EFI_SUCCESS || gop.is_null() {
None
} else {
Some(gop as *mut EfiGraphicsOutputProtocol)
}
}
pub unsafe fn get_memory_map(
boot_services: *mut EfiBootServices,
buffer: *mut u8,
buffer_len: usize,
) -> Option<MemoryMap> {
let mut memory_map_size = buffer_len;
let mut map_key = 0;
let mut descriptor_size = 0;
let mut descriptor_version = 0;
let status = unsafe {
((*boot_services).get_memory_map)(
&mut memory_map_size,
buffer as *mut EfiMemoryDescriptor,
&mut map_key,
&mut descriptor_size,
&mut descriptor_version,
)
};
if status != EFI_SUCCESS || descriptor_size < core::mem::size_of::<EfiMemoryDescriptor>() {
None
} else {
Some(MemoryMap {
ptr: buffer as *const EfiMemoryDescriptor,
byte_len: memory_map_size,
key: map_key,
descriptor_size,
})
}
}
+259
View File
@@ -0,0 +1,259 @@
use core::ptr::{copy_nonoverlapping, write_bytes};
use crate::fs::uefi::LoadedFile;
use crate::memory::PhysicalMemoryManager;
const EI_CLASS: usize = 4;
const EI_DATA: usize = 5;
const ELFCLASS64: u8 = 2;
const ELFDATA2LSB: u8 = 1;
const ET_EXEC: u16 = 2;
const ET_DYN: u16 = 3;
const EM_X86_64: u16 = 62;
const PT_LOAD: u32 = 1;
const SHT_RELA: u32 = 4;
const R_X86_64_RELATIVE: u32 = 8;
const R_X86_64_IRELATIVE: u32 = 37;
const USER_DYN_LOAD_BIAS: u64 = 0x4000_0000;
const MAX_USER_LOAD_END: u64 = 0x0000_8000_0000_0000;
pub struct UserProgram {
pub entry: u64,
pub phdr: u64,
pub phent: u64,
pub phnum: u64,
}
#[repr(C)]
struct Elf64Header {
ident: [u8; 16],
ty: u16,
machine: u16,
version: u32,
entry: u64,
phoff: u64,
shoff: u64,
flags: u32,
ehsize: u16,
phentsize: u16,
phnum: u16,
shentsize: u16,
shnum: u16,
shstrndx: u16,
}
#[repr(C)]
struct Elf64ProgramHeader {
ty: u32,
flags: u32,
offset: u64,
vaddr: u64,
paddr: u64,
filesz: u64,
memsz: u64,
align: u64,
}
#[repr(C)]
struct Elf64SectionHeader {
name: u32,
ty: u32,
flags: u64,
addr: u64,
offset: u64,
size: u64,
link: u32,
info: u32,
addralign: u64,
entsize: u64,
}
#[repr(C)]
struct Elf64Rela {
offset: u64,
info: u64,
addend: i64,
}
pub unsafe fn load_user_elf(
image: &LoadedFile,
_memory: &mut PhysicalMemoryManager,
) -> Option<UserProgram> {
let bytes = unsafe { core::slice::from_raw_parts(image.ptr, image.len) };
let header = parse_header(bytes)?;
let load_bias = if header.ty == ET_DYN {
USER_DYN_LOAD_BIAS
} else {
0
};
for index in 0..header.phnum as usize {
let ph = program_header(bytes, header, index)?;
if ph.ty != PT_LOAD {
continue;
}
if ph.filesz > ph.memsz {
return None;
}
let file_start = usize::try_from(ph.offset).ok()?;
let file_size = usize::try_from(ph.filesz).ok()?;
let file_end = file_start.checked_add(file_size)?;
if file_end > bytes.len() {
return None;
}
let load_start = load_bias.checked_add(ph.vaddr)?;
let load_end = load_start.checked_add(ph.memsz)?;
if load_start < 0x1000 || load_end > MAX_USER_LOAD_END {
return None;
}
unsafe {
copy_nonoverlapping(
bytes.as_ptr().add(file_start),
load_start as *mut u8,
file_size,
);
if ph.memsz > ph.filesz {
write_bytes(
(load_start + ph.filesz) as *mut u8,
0,
usize::try_from(ph.memsz - ph.filesz).ok()?,
);
}
}
}
unsafe {
apply_relocations(bytes, header, load_bias)?;
}
Some(UserProgram {
entry: load_bias.checked_add(header.entry)?,
phdr: program_header_address(bytes, header, load_bias)?,
phent: header.phentsize as u64,
phnum: header.phnum as u64,
})
}
fn parse_header(bytes: &[u8]) -> Option<&Elf64Header> {
if bytes.len() < core::mem::size_of::<Elf64Header>() {
return None;
}
let header = unsafe { &*(bytes.as_ptr() as *const Elf64Header) };
if &header.ident[0..4] != b"\x7fELF" {
return None;
}
if header.ident[EI_CLASS] != ELFCLASS64 || header.ident[EI_DATA] != ELFDATA2LSB {
return None;
}
if header.machine != EM_X86_64 || (header.ty != ET_EXEC && header.ty != ET_DYN) {
return None;
}
if header.phentsize as usize != core::mem::size_of::<Elf64ProgramHeader>() {
return None;
}
Some(header)
}
unsafe fn apply_relocations(bytes: &[u8], header: &Elf64Header, load_bias: u64) -> Option<()> {
if header.shoff == 0 || header.shentsize as usize != core::mem::size_of::<Elf64SectionHeader>()
{
return Some(());
}
for index in 0..header.shnum as usize {
let section = section_header(bytes, header, index)?;
if section.ty != SHT_RELA {
continue;
}
if section.entsize as usize != core::mem::size_of::<Elf64Rela>() {
continue;
}
let offset = usize::try_from(section.offset).ok()?;
let size = usize::try_from(section.size).ok()?;
let end = offset.checked_add(size)?;
if end > bytes.len() {
return None;
}
let count = size / core::mem::size_of::<Elf64Rela>();
for rela_index in 0..count {
let rela = unsafe {
&*(bytes
.as_ptr()
.add(offset + rela_index * core::mem::size_of::<Elf64Rela>())
as *const Elf64Rela)
};
let ty = (rela.info & 0xffff_ffff) as u32;
let target = load_bias.checked_add(rela.offset)? as *mut u64;
match ty {
R_X86_64_RELATIVE => unsafe {
target.write(load_bias.wrapping_add(rela.addend as u64));
},
R_X86_64_IRELATIVE => unsafe {
let resolver_address = load_bias.wrapping_add(rela.addend as u64);
let resolver: extern "C" fn() -> u64 =
core::mem::transmute(resolver_address as usize);
target.write(resolver());
},
_ => {}
}
}
}
Some(())
}
fn section_header<'a>(
bytes: &'a [u8],
header: &Elf64Header,
index: usize,
) -> Option<&'a Elf64SectionHeader> {
let shoff = usize::try_from(header.shoff).ok()?;
let offset = shoff.checked_add(index.checked_mul(header.shentsize as usize)?)?;
let end = offset.checked_add(core::mem::size_of::<Elf64SectionHeader>())?;
if end > bytes.len() {
return None;
}
Some(unsafe { &*(bytes.as_ptr().add(offset) as *const Elf64SectionHeader) })
}
fn program_header<'a>(
bytes: &'a [u8],
header: &Elf64Header,
index: usize,
) -> Option<&'a Elf64ProgramHeader> {
let phoff = usize::try_from(header.phoff).ok()?;
let offset = phoff.checked_add(index.checked_mul(header.phentsize as usize)?)?;
let end = offset.checked_add(core::mem::size_of::<Elf64ProgramHeader>())?;
if end > bytes.len() {
return None;
}
Some(unsafe { &*(bytes.as_ptr().add(offset) as *const Elf64ProgramHeader) })
}
fn program_header_address(bytes: &[u8], header: &Elf64Header, load_bias: u64) -> Option<u64> {
let phoff = header.phoff;
for index in 0..header.phnum as usize {
let ph = program_header(bytes, header, index)?;
if ph.ty != PT_LOAD {
continue;
}
if phoff >= ph.offset && phoff < ph.offset.checked_add(ph.filesz)? {
return load_bias
.checked_add(ph.vaddr)?
.checked_add(phoff.checked_sub(ph.offset)?);
}
}
load_bias.checked_add(phoff)
}
+3
View File
@@ -0,0 +1,3 @@
pub const GLYPH_WIDTH: usize = 8;
pub const GLYPH_HEIGHT: usize = 16;
pub const HANKAKU: &[u8; 4096] = include_bytes!("../resources/hankaku.bin");
+135
View File
@@ -0,0 +1,135 @@
use crate::efi::{EfiGraphicsOutputProtocol, EfiGraphicsPixelFormat};
#[derive(Clone, Copy)]
pub struct Rgb {
pub red: u8,
pub green: u8,
pub blue: u8,
}
pub struct Framebuffer {
base: *mut u8,
width: usize,
height: usize,
stride: usize,
pixel_format: EfiGraphicsPixelFormat,
}
impl Framebuffer {
pub unsafe fn from_gop(gop: *mut EfiGraphicsOutputProtocol) -> Option<Self> {
let mode = unsafe { (*gop).mode };
if mode.is_null() {
return None;
}
let info = unsafe { (*mode).info };
if info.is_null() {
return None;
}
let pixel_format = unsafe { (*info).pixel_format };
if pixel_format != EfiGraphicsPixelFormat::RedGreenBlueReserved8BitPerColor
&& pixel_format != EfiGraphicsPixelFormat::BlueGreenRedReserved8BitPerColor
{
return None;
}
Some(Self {
base: unsafe { (*mode).frame_buffer_base as *mut u8 },
width: unsafe { (*info).horizontal_resolution as usize },
height: unsafe { (*info).vertical_resolution as usize },
stride: unsafe { (*info).pixels_per_scan_line as usize },
pixel_format,
})
}
pub fn width(&self) -> usize {
self.width
}
pub fn height(&self) -> usize {
self.height
}
pub fn clear(&mut self, color: Rgb) {
self.fill_rect(0, 0, self.width, self.height, color);
}
pub fn fill_rect(&mut self, x: usize, y: usize, width: usize, height: usize, color: Rgb) {
let end_x = x.saturating_add(width).min(self.width);
let end_y = y.saturating_add(height).min(self.height);
for py in y..end_y {
for px in x..end_x {
self.put_pixel(px, py, color);
}
}
}
pub fn scroll_region_up(&mut self, top: usize, bottom: usize, pixels: usize, fill: Rgb) {
if pixels == 0 {
return;
}
let top = top.min(self.height);
let bottom = bottom.min(self.height);
if top >= bottom {
return;
}
if pixels >= bottom - top {
self.fill_rect(0, top, self.width, bottom - top, fill);
return;
}
for y in top..bottom {
for x in 0..self.width {
if y + pixels < bottom {
self.copy_pixel(x, y + pixels, x, y);
} else {
self.put_pixel(x, y, fill);
}
}
}
}
pub fn put_pixel(&mut self, x: usize, y: usize, color: Rgb) {
if x >= self.width || y >= self.height {
return;
}
let offset = (y * self.stride + x) * 4;
unsafe {
let pixel = self.base.add(offset);
match self.pixel_format {
EfiGraphicsPixelFormat::RedGreenBlueReserved8BitPerColor => {
pixel.write_volatile(color.red);
pixel.add(1).write_volatile(color.green);
pixel.add(2).write_volatile(color.blue);
pixel.add(3).write_volatile(0);
}
EfiGraphicsPixelFormat::BlueGreenRedReserved8BitPerColor => {
pixel.write_volatile(color.blue);
pixel.add(1).write_volatile(color.green);
pixel.add(2).write_volatile(color.red);
pixel.add(3).write_volatile(0);
}
_ => {}
}
}
}
fn copy_pixel(&mut self, source_x: usize, source_y: usize, target_x: usize, target_y: usize) {
let source_offset = (source_y * self.stride + source_x) * 4;
let target_offset = (target_y * self.stride + target_x) * 4;
unsafe {
let source = self.base.add(source_offset);
let target = self.base.add(target_offset);
target.write_volatile(source.read_volatile());
target.add(1).write_volatile(source.add(1).read_volatile());
target.add(2).write_volatile(source.add(2).read_volatile());
target.add(3).write_volatile(source.add(3).read_volatile());
}
}
}
+921
View File
@@ -0,0 +1,921 @@
use crate::drivers::storage::{
ahci::AhciDisk,
block::{BlockDevice, BlockError},
partition::PartitionBlockDevice,
};
use crate::fs::uefi::LoadedFile;
use crate::memory::{PAGE_SIZE, PhysicalMemoryManager};
const EXT4_SUPERBLOCK_OFFSET: u64 = 1024;
const EXT4_SUPER_MAGIC: u16 = 0xef53;
const EXT4_ROOT_INO: u32 = 2;
const EXT4_EXTENTS_FL: u32 = 0x0008_0000;
const EXT4_EXTENT_MAGIC: u16 = 0xf30a;
const EXT4_N_BLOCKS_OFFSET: usize = 40;
const EXT4_NAME_LEN: usize = 255;
const MAX_BLOCK_SIZE: usize = 4096;
const MAX_INODE_SIZE: usize = 256;
const EXT4_FT_REG_FILE: u8 = 1;
const EXT4_FT_DIR: u8 = 2;
const EXT4_S_IFREG: u16 = 0o100000;
const EXT4_S_IFDIR: u16 = 0o040000;
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct Ext4Superblock {
pub inodes_count: u32,
pub blocks_count_lo: u32,
pub first_data_block: u32,
pub log_block_size: u32,
pub blocks_per_group: u32,
pub inodes_per_group: u32,
pub free_blocks_count: u32,
pub free_inodes_count: u32,
pub first_ino: u32,
pub magic: u16,
pub inode_size: u16,
pub desc_size: u16,
pub feature_incompat: u32,
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
pub struct Ext4FileSystem {
pub block_size: u32,
pub first_data_block: u32,
pub inode_size: u16,
pub blocks_count: u32,
pub inodes_count: u32,
pub blocks_per_group: u32,
pub inodes_per_group: u32,
pub first_ino: u32,
pub desc_size: u16,
pub feature_incompat: u32,
}
#[derive(Clone, Copy)]
#[allow(dead_code)]
struct Ext4Inode {
mode: u16,
links: u16,
size: u64,
flags: u32,
block: [u8; 60],
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub enum Ext4Error {
Block(BlockError),
BadMagic,
NotFound,
Unsupported,
NoMemory,
Invalid,
NoSpace,
}
impl From<BlockError> for Ext4Error {
fn from(value: BlockError) -> Self {
Self::Block(value)
}
}
pub fn mount<D: BlockDevice>(device: &mut D) -> Result<Ext4FileSystem, Ext4Error> {
let superblock = read_superblock(device)?;
if superblock.magic != EXT4_SUPER_MAGIC {
return Err(Ext4Error::BadMagic);
}
let block_size = 1024u32
.checked_shl(superblock.log_block_size)
.ok_or(Ext4Error::Unsupported)?;
if !(1024..=MAX_BLOCK_SIZE as u32).contains(&block_size) {
return Err(Ext4Error::Unsupported);
}
Ok(Ext4FileSystem {
block_size,
first_data_block: superblock.first_data_block,
inode_size: if superblock.inode_size == 0 {
128
} else {
superblock.inode_size
},
blocks_count: superblock.blocks_count_lo,
inodes_count: superblock.inodes_count,
blocks_per_group: superblock.blocks_per_group,
inodes_per_group: superblock.inodes_per_group,
first_ino: if superblock.first_ino == 0 {
11
} else {
superblock.first_ino
},
desc_size: superblock.desc_size.max(32),
feature_incompat: superblock.feature_incompat,
})
}
static mut ROOT_DEVICE: Option<PartitionBlockDevice<AhciDisk>> = None;
static mut ROOT_FS: Option<Ext4FileSystem> = None;
pub unsafe fn register_root(device: PartitionBlockDevice<AhciDisk>, fs: Ext4FileSystem) {
unsafe {
ROOT_DEVICE = Some(device);
ROOT_FS = Some(fs);
}
}
pub fn create_file(path: &[u8]) -> Result<(), Ext4Error> {
unsafe {
let Some(mut device) = ROOT_DEVICE else {
return Err(Ext4Error::Unsupported);
};
let Some(fs) = ROOT_FS else {
return Err(Ext4Error::Unsupported);
};
let result = create_node(&mut device, &fs, path, false);
ROOT_DEVICE = Some(device);
result
}
}
pub fn create_dir(path: &[u8]) -> Result<(), Ext4Error> {
unsafe {
let Some(mut device) = ROOT_DEVICE else {
return Err(Ext4Error::Unsupported);
};
let Some(fs) = ROOT_FS else {
return Err(Ext4Error::Unsupported);
};
let result = create_node(&mut device, &fs, path, true);
ROOT_DEVICE = Some(device);
result
}
}
pub fn load_path<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
memory: &mut PhysicalMemoryManager,
path: &[u8],
) -> Result<LoadedFile, Ext4Error> {
let inode_no = lookup_path(device, fs, path)?;
let inode = read_inode(device, fs, inode_no)?;
let len = usize::try_from(inode.size).map_err(|_| Ext4Error::Unsupported)?;
let pages = len.div_ceil(PAGE_SIZE).max(1);
let Some(buffer) = memory.alloc_pages(pages) else {
return Err(Ext4Error::NoMemory);
};
let target = unsafe { core::slice::from_raw_parts_mut(buffer, pages * PAGE_SIZE) };
target[..len].fill(0);
if let Err(error) = read_inode_data(device, fs, &inode, &mut target[..len]) {
unsafe {
memory.free_pages(buffer, pages);
}
return Err(error);
}
Ok(LoadedFile {
ptr: buffer as *const u8,
len,
})
}
pub fn mount_vfs_tree<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
) -> Result<(), Ext4Error> {
let mut root = [0u8; 256];
root[0] = b'/';
mount_vfs_dir(device, fs, EXT4_ROOT_INO, &root, 0)
}
fn lookup_path<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
path: &[u8],
) -> Result<u32, Ext4Error> {
let mut current = EXT4_ROOT_INO;
let mut index = 0;
while index < path.len() {
while index < path.len() && path[index] == b'/' {
index += 1;
}
if index >= path.len() {
break;
}
let start = index;
while index < path.len() && path[index] != b'/' && path[index] != 0 {
index += 1;
}
let name = &path[start..index];
if name.is_empty() || name.len() > EXT4_NAME_LEN {
return Err(Ext4Error::NotFound);
}
let dir = read_inode(device, fs, current)?;
current = find_dir_entry(device, fs, &dir, name)?;
}
Ok(current)
}
fn find_dir_entry<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
dir: &Ext4Inode,
name: &[u8],
) -> Result<u32, Ext4Error> {
let mut block = [0u8; MAX_BLOCK_SIZE];
let block_size = fs.block_size as usize;
let mut logical_block = 0u32;
let total_blocks = dir.size.div_ceil(fs.block_size as u64) as u32;
while logical_block < total_blocks {
read_file_block(device, fs, dir, logical_block, &mut block[..block_size])?;
let mut offset = 0;
while offset + 8 <= block_size {
let inode = le_u32(&block, offset);
let rec_len = le_u16(&block, offset + 4) as usize;
let name_len = block[offset + 6] as usize;
if rec_len < 8 || offset + rec_len > block_size {
break;
}
if inode != 0
&& name_len == name.len()
&& &block[offset + 8..offset + 8 + name_len] == name
{
return Ok(inode);
}
offset += rec_len;
}
logical_block += 1;
}
Err(Ext4Error::NotFound)
}
fn mount_vfs_dir<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
ino: u32,
path: &[u8; 256],
depth: usize,
) -> Result<(), Ext4Error> {
if depth > 8 {
return Ok(());
}
let dir = read_inode(device, fs, ino)?;
let mut block = [0u8; MAX_BLOCK_SIZE];
let block_size = fs.block_size as usize;
let mut logical_block = 0u32;
let total_blocks = dir.size.div_ceil(fs.block_size as u64) as u32;
while logical_block < total_blocks {
read_file_block(device, fs, &dir, logical_block, &mut block[..block_size])?;
let mut offset = 0;
while offset + 8 <= block_size {
let child_ino = le_u32(&block, offset);
let rec_len = le_u16(&block, offset + 4) as usize;
let name_len = block[offset + 6] as usize;
let file_type = block[offset + 7];
if rec_len < 8 || offset + rec_len > block_size {
break;
}
if child_ino != 0
&& name_len > 0
&& offset + 8 + name_len <= block_size
&& !is_dot_dirent(&block[offset + 8..offset + 8 + name_len])
{
let mut child_path = [0u8; 256];
if join_path(
path,
&block[offset + 8..offset + 8 + name_len],
&mut child_path,
) {
match file_type {
EXT4_FT_DIR => {
let _ = crate::fs::vfs::mount_ext4_node(
&child_path,
crate::fs::vfs::NodeKind::Directory,
);
mount_vfs_dir(device, fs, child_ino, &child_path, depth + 1)?;
}
EXT4_FT_REG_FILE => {
let _ = crate::fs::vfs::mount_ext4_node(
&child_path,
crate::fs::vfs::NodeKind::Regular,
);
}
_ => {}
}
}
}
offset += rec_len;
}
logical_block += 1;
}
Ok(())
}
fn is_dot_dirent(name: &[u8]) -> bool {
name == b"." || name == b".."
}
fn join_path(parent: &[u8; 256], name: &[u8], output: &mut [u8; 256]) -> bool {
let parent_len = nul_len(parent);
if parent_len == 0 || name.is_empty() {
return false;
}
let mut index = 0;
while index < parent_len && index < output.len() - 1 {
output[index] = parent[index];
index += 1;
}
if !(index == 1 && output[0] == b'/') {
if index >= output.len() - 1 {
return false;
}
output[index] = b'/';
index += 1;
}
if index + name.len() >= output.len() {
return false;
}
output[index..index + name.len()].copy_from_slice(name);
true
}
fn read_inode_data<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
inode: &Ext4Inode,
output: &mut [u8],
) -> Result<(), Ext4Error> {
let block_size = fs.block_size as usize;
let mut block = [0u8; MAX_BLOCK_SIZE];
let mut done = 0;
let mut logical_block = 0u32;
while done < output.len() {
read_file_block(device, fs, inode, logical_block, &mut block[..block_size])?;
let chunk = block_size.min(output.len() - done);
output[done..done + chunk].copy_from_slice(&block[..chunk]);
done += chunk;
logical_block += 1;
}
Ok(())
}
fn read_file_block<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
inode: &Ext4Inode,
logical_block: u32,
output: &mut [u8],
) -> Result<(), Ext4Error> {
if inode.flags & EXT4_EXTENTS_FL == 0 {
return Err(Ext4Error::Unsupported);
}
let physical = match extent_lookup(device, fs, &inode.block, logical_block) {
Ok(physical) => physical,
Err(Ext4Error::NotFound) => {
output.fill(0);
return Ok(());
}
Err(error) => return Err(error),
};
read_block(device, fs, physical, output)
}
fn extent_lookup<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
root: &[u8; 60],
logical_block: u32,
) -> Result<u64, Ext4Error> {
let magic = le_u16(root, 0);
let entries = le_u16(root, 2) as usize;
let depth = le_u16(root, 6);
if magic != EXT4_EXTENT_MAGIC {
return Err(Ext4Error::Unsupported);
}
if depth == 0 {
return extent_leaf_lookup(root, entries, logical_block);
}
if depth != 1 {
return Err(Ext4Error::Unsupported);
}
let mut leaf_block = None;
for index in 0..entries {
let offset = 12 + index * 12;
if offset + 12 > root.len() {
break;
}
let first = le_u32(root, offset);
if first <= logical_block {
let lo = le_u32(root, offset + 4) as u64;
let hi = le_u16(root, offset + 8) as u64;
leaf_block = Some((hi << 32) | lo);
}
}
let Some(leaf_block) = leaf_block else {
return Err(Ext4Error::NotFound);
};
let mut block = [0u8; MAX_BLOCK_SIZE];
read_block(device, fs, leaf_block, &mut block[..fs.block_size as usize])?;
if le_u16(&block, 0) != EXT4_EXTENT_MAGIC {
return Err(Ext4Error::Unsupported);
}
extent_leaf_lookup(&block[..60], le_u16(&block, 2) as usize, logical_block)
}
fn extent_leaf_lookup(
extents: &[u8],
entries: usize,
logical_block: u32,
) -> Result<u64, Ext4Error> {
for index in 0..entries {
let offset = 12 + index * 12;
if offset + 12 > extents.len() {
break;
}
let start = le_u32(extents, offset);
let len = le_u16(extents, offset + 4) as u32 & 0x7fff;
let physical_hi = le_u16(extents, offset + 6) as u64;
let physical_lo = le_u32(extents, offset + 8) as u64;
if logical_block >= start && logical_block < start + len {
return Ok(((physical_hi << 32) | physical_lo) + (logical_block - start) as u64);
}
}
Err(Ext4Error::NotFound)
}
fn read_inode<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
inode_no: u32,
) -> Result<Ext4Inode, Ext4Error> {
if fs.inode_size as usize > MAX_INODE_SIZE {
return Err(Ext4Error::Unsupported);
}
let group = (inode_no - 1) / fs.inodes_per_group;
let index = (inode_no - 1) % fs.inodes_per_group;
let inode_table = read_inode_table_block(device, fs, group)?;
let inode_offset = inode_table
.checked_mul(fs.block_size as u64)
.and_then(|base| base.checked_add(index as u64 * fs.inode_size as u64))
.ok_or(Ext4Error::Unsupported)?;
let mut buffer = [0u8; MAX_INODE_SIZE];
device.read_at(inode_offset, &mut buffer[..fs.inode_size as usize])?;
let mut block = [0u8; 60];
block.copy_from_slice(&buffer[EXT4_N_BLOCKS_OFFSET..EXT4_N_BLOCKS_OFFSET + 60]);
let size_lo = le_u32(&buffer, 4) as u64;
let size_hi = le_u32(&buffer, 108) as u64;
Ok(Ext4Inode {
mode: le_u16(&buffer, 0),
links: le_u16(&buffer, 26),
size: size_lo | (size_hi << 32),
flags: le_u32(&buffer, 32),
block,
})
}
fn read_inode_table_block<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
group: u32,
) -> Result<u64, Ext4Error> {
let descriptor_table_block = if fs.block_size == 1024 { 2 } else { 1 };
let descriptor_offset =
descriptor_table_block as u64 * fs.block_size as u64 + group as u64 * fs.desc_size as u64;
let mut descriptor = [0u8; 64];
device.read_at(descriptor_offset, &mut descriptor[..fs.desc_size as usize])?;
let lo = le_u32(&descriptor, 8) as u64;
let hi = if fs.desc_size as usize >= 64 {
le_u32(&descriptor, 40) as u64
} else {
0
};
Ok((hi << 32) | lo)
}
fn read_superblock<D: BlockDevice>(device: &mut D) -> Result<Ext4Superblock, Ext4Error> {
let mut buffer = [0u8; 1024];
device.read_at(EXT4_SUPERBLOCK_OFFSET, &mut buffer)?;
Ok(parse_superblock(&buffer))
}
fn read_block<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
block: u64,
output: &mut [u8],
) -> Result<(), Ext4Error> {
let offset = block
.checked_mul(fs.block_size as u64)
.ok_or(Ext4Error::Unsupported)?;
device.read_at(offset, output)?;
Ok(())
}
fn parse_superblock(buffer: &[u8; 1024]) -> Ext4Superblock {
Ext4Superblock {
inodes_count: le_u32(buffer, 0x00),
blocks_count_lo: le_u32(buffer, 0x04),
first_data_block: le_u32(buffer, 0x14),
log_block_size: le_u32(buffer, 0x18),
blocks_per_group: le_u32(buffer, 0x20),
inodes_per_group: le_u32(buffer, 0x28),
free_blocks_count: le_u32(buffer, 0x0c),
free_inodes_count: le_u32(buffer, 0x10),
first_ino: le_u32(buffer, 0x54),
magic: le_u16(buffer, 0x38),
inode_size: le_u16(buffer, 0x58),
feature_incompat: le_u32(buffer, 0x60),
desc_size: le_u16(buffer, 0xfe),
}
}
fn create_node<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
path: &[u8],
is_dir: bool,
) -> Result<(), Ext4Error> {
let (parent_path, name) = split_parent(path)?;
if name.is_empty() || name.len() > EXT4_NAME_LEN {
return Err(Ext4Error::Invalid);
}
if lookup_path(device, fs, path).is_ok() {
return Ok(());
}
let parent_ino = lookup_path(device, fs, parent_path)?;
let mut parent_inode = read_inode(device, fs, parent_ino)?;
if parent_inode.mode & EXT4_S_IFDIR == 0 {
return Err(Ext4Error::Invalid);
}
let inode_no = alloc_inode(device, fs)?;
let mut inode = new_inode(is_dir);
if is_dir {
let block = alloc_block(device, fs)?;
inode.size = fs.block_size as u64;
inode.flags = EXT4_EXTENTS_FL;
inode.block = make_single_extent(block);
write_dir_block(device, fs, block, inode_no, parent_ino)?;
inode.links = 2;
} else {
inode.links = 1;
}
write_inode(device, fs, inode_no, &inode)?;
append_dir_entry(
device,
fs,
parent_ino,
&mut parent_inode,
inode_no,
name,
is_dir,
)?;
if is_dir {
parent_inode.links += 1;
write_inode(device, fs, parent_ino, &parent_inode)?;
}
Ok(())
}
fn new_inode(is_dir: bool) -> Ext4Inode {
let mode = if is_dir {
EXT4_S_IFDIR | 0o755
} else {
EXT4_S_IFREG | 0o644
};
Ext4Inode {
mode,
links: if is_dir { 2 } else { 1 },
size: 0,
flags: EXT4_EXTENTS_FL,
block: [0; 60],
}
}
fn make_single_extent(block: u64) -> [u8; 60] {
let mut block_bytes = [0u8; 60];
write_u16(&mut block_bytes, 0, EXT4_EXTENT_MAGIC);
write_u16(&mut block_bytes, 2, 1);
write_u16(&mut block_bytes, 6, 0);
write_u32(&mut block_bytes, 12, 0);
write_u16(&mut block_bytes, 16, 1);
write_u16(&mut block_bytes, 18, (block >> 32) as u16);
write_u32(&mut block_bytes, 20, block as u32);
block_bytes
}
fn write_dir_block<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
block: u64,
self_ino: u32,
parent_ino: u32,
) -> Result<(), Ext4Error> {
let mut data = [0u8; MAX_BLOCK_SIZE];
let block_size = fs.block_size as usize;
write_dirent_entry(&mut data, 0, self_ino, EXT4_FT_DIR, b".", 12);
write_dirent_entry(
&mut data,
12,
parent_ino,
EXT4_FT_DIR,
b"..",
block_size - 12,
);
device.write_at(block * fs.block_size as u64, &data[..block_size])?;
Ok(())
}
fn append_dir_entry<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
_dir_ino: u32,
dir: &mut Ext4Inode,
child_ino: u32,
name: &[u8],
is_dir: bool,
) -> Result<(), Ext4Error> {
let block_size = fs.block_size as usize;
let mut logical = 0u32;
let total_blocks = dir.size.div_ceil(fs.block_size as u64) as u32;
let mut block = [0u8; MAX_BLOCK_SIZE];
while logical < total_blocks {
read_file_block(device, fs, dir, logical, &mut block[..block_size])?;
let physical = extent_lookup(device, fs, &dir.block, logical)?;
let mut offset = 0usize;
let mut previous_offset = None;
while offset + 8 <= block_size {
let inode = le_u32(&block, offset);
let rec_len = le_u16(&block, offset + 4) as usize;
let name_len = block[offset + 6] as usize;
if rec_len < 8 || offset + rec_len > block_size {
break;
}
if inode != 0 {
previous_offset = Some(offset);
let ideal = align_up(8 + name_len, 4);
if rec_len.saturating_sub(ideal) >= align_up(8 + name.len(), 4) {
write_u16(&mut block, offset + 4, ideal as u16);
let insert = offset + ideal;
write_dirent_entry(
&mut block,
insert,
child_ino,
if is_dir {
EXT4_FT_DIR
} else {
EXT4_FT_REG_FILE
},
name,
rec_len - ideal,
);
device.write_at(physical * fs.block_size as u64, &block[..block_size])?;
return Ok(());
}
}
offset += rec_len;
}
if let Some(prev) = previous_offset {
let name_len = block[prev + 6] as usize;
let ideal = align_up(8 + name_len, 4);
let rec_len = le_u16(&block, prev + 4) as usize;
let new_rec = align_up(8 + name.len(), 4);
if rec_len > ideal + new_rec {
write_u16(&mut block, prev + 4, ideal as u16);
let insert = prev + ideal;
write_dirent_entry(
&mut block,
insert,
child_ino,
if is_dir {
EXT4_FT_DIR
} else {
EXT4_FT_REG_FILE
},
name,
rec_len - ideal,
);
device.write_at(physical * fs.block_size as u64, &block[..block_size])?;
return Ok(());
}
}
logical += 1;
}
Err(Ext4Error::NoSpace)
}
fn write_dirent_entry(
block: &mut [u8],
offset: usize,
ino: u32,
file_type: u8,
name: &[u8],
rec_len: usize,
) {
let rec_len = rec_len.max(align_up(8 + name.len(), 4));
write_u32(block, offset, ino);
write_u16(block, offset + 4, rec_len as u16);
block[offset + 6] = name.len() as u8;
block[offset + 7] = file_type;
block[offset + 8..offset + 8 + name.len()].copy_from_slice(name);
}
fn alloc_inode<D: BlockDevice>(device: &mut D, fs: &Ext4FileSystem) -> Result<u32, Ext4Error> {
let groups = fs.blocks_count.div_ceil(fs.blocks_per_group);
for group in 0..groups {
let inode_bitmap = group_desc_field(device, fs, group, 4)?;
let mut bitmap = [0u8; MAX_BLOCK_SIZE];
device.read_at(
inode_bitmap * fs.block_size as u64,
&mut bitmap[..fs.block_size as usize],
)?;
let start_inode = group * fs.inodes_per_group + 1;
let end_inode = (start_inode + fs.inodes_per_group - 1).min(fs.inodes_count);
for inode_no in start_inode..=end_inode {
if inode_no < fs.first_ino {
continue;
}
let bit = (inode_no - start_inode) as usize;
if bitmap[bit / 8] & (1 << (bit % 8)) == 0 {
bitmap[bit / 8] |= 1 << (bit % 8);
device.write_at(
inode_bitmap * fs.block_size as u64,
&bitmap[..fs.block_size as usize],
)?;
decrement_free_counters(device, fs, group, true, false)?;
return Ok(inode_no);
}
}
}
Err(Ext4Error::NoSpace)
}
fn alloc_block<D: BlockDevice>(device: &mut D, fs: &Ext4FileSystem) -> Result<u64, Ext4Error> {
let groups = fs.blocks_count.div_ceil(fs.blocks_per_group);
for group in 0..groups {
let block_bitmap = group_desc_field(device, fs, group, 0)?;
let mut bitmap = [0u8; MAX_BLOCK_SIZE];
device.read_at(
block_bitmap * fs.block_size as u64,
&mut bitmap[..fs.block_size as usize],
)?;
let start_block = group * fs.blocks_per_group + fs.first_data_block;
let end_block = (start_block + fs.blocks_per_group - 1).min(fs.blocks_count - 1);
for block_no in start_block..=end_block {
let bit = (block_no - start_block) as usize;
if bitmap[bit / 8] & (1 << (bit % 8)) == 0 {
bitmap[bit / 8] |= 1 << (bit % 8);
device.write_at(
block_bitmap * fs.block_size as u64,
&bitmap[..fs.block_size as usize],
)?;
decrement_free_counters(device, fs, group, false, true)?;
return Ok(block_no as u64);
}
}
}
Err(Ext4Error::NoSpace)
}
fn group_desc_field<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
group: u32,
offset: usize,
) -> Result<u64, Ext4Error> {
let descriptor_table_block = if fs.block_size == 1024 { 2 } else { 1 };
let descriptor_offset = descriptor_table_block as u64 * fs.block_size as u64
+ group as u64 * fs.desc_size as u64
+ offset as u64;
let mut buffer = [0u8; 8];
device.read_at(descriptor_offset, &mut buffer[..4])?;
Ok(le_u32(&buffer, 0) as u64)
}
fn decrement_free_counters<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
group: u32,
inode: bool,
block: bool,
) -> Result<(), Ext4Error> {
let mut sb = [0u8; 1024];
device.read_at(EXT4_SUPERBLOCK_OFFSET, &mut sb)?;
if inode {
let free = le_u32(&sb, 0x10).saturating_sub(1);
write_u32(&mut sb, 0x10, free);
}
if block {
let free = le_u32(&sb, 0x0c).saturating_sub(1);
write_u32(&mut sb, 0x0c, free);
}
device.write_at(EXT4_SUPERBLOCK_OFFSET, &sb)?;
let descriptor_table_block = if fs.block_size == 1024 { 2 } else { 1 };
let descriptor_offset =
descriptor_table_block as u64 * fs.block_size as u64 + group as u64 * fs.desc_size as u64;
let mut descriptor = [0u8; 64];
device.read_at(descriptor_offset, &mut descriptor[..fs.desc_size as usize])?;
if block {
let free = le_u16(&descriptor, 12).saturating_sub(1);
write_u16(&mut descriptor, 12, free);
}
if inode {
let free = le_u16(&descriptor, 14).saturating_sub(1);
write_u16(&mut descriptor, 14, free);
}
device.write_at(descriptor_offset, &descriptor[..fs.desc_size as usize])?;
Ok(())
}
fn write_inode<D: BlockDevice>(
device: &mut D,
fs: &Ext4FileSystem,
inode_no: u32,
inode: &Ext4Inode,
) -> Result<(), Ext4Error> {
let group = (inode_no - 1) / fs.inodes_per_group;
let index = (inode_no - 1) % fs.inodes_per_group;
let inode_table = read_inode_table_block(device, fs, group)?;
let inode_offset = inode_table
.checked_mul(fs.block_size as u64)
.and_then(|base| base.checked_add(index as u64 * fs.inode_size as u64))
.ok_or(Ext4Error::Unsupported)?;
let mut buffer = [0u8; MAX_INODE_SIZE];
device.read_at(inode_offset, &mut buffer[..fs.inode_size as usize])?;
write_u16(&mut buffer, 0, inode.mode);
write_u16(&mut buffer, 26, inode.links);
write_u32(&mut buffer, 4, inode.size as u32);
write_u32(&mut buffer, 108, (inode.size >> 32) as u32);
write_u32(&mut buffer, 32, inode.flags);
buffer[EXT4_N_BLOCKS_OFFSET..EXT4_N_BLOCKS_OFFSET + 60].copy_from_slice(&inode.block);
device.write_at(inode_offset, &buffer[..fs.inode_size as usize])?;
Ok(())
}
fn split_parent(path: &[u8]) -> Result<(&[u8], &[u8]), Ext4Error> {
let len = nul_len(path);
let path = &path[..len];
let Some(last) = path.iter().rposition(|byte| *byte == b'/') else {
return Err(Ext4Error::Invalid);
};
if last == 0 {
return Ok((&path[..1], &path[1..]));
}
Ok((&path[..last], &path[last + 1..]))
}
fn nul_len(path: &[u8]) -> usize {
path.iter()
.position(|byte| *byte == 0)
.unwrap_or(path.len())
}
fn align_up(value: usize, align: usize) -> usize {
(value + align - 1) & !(align - 1)
}
fn write_u16(buffer: &mut [u8], offset: usize, value: u16) {
buffer[offset..offset + 2].copy_from_slice(&value.to_le_bytes());
}
fn write_u32(buffer: &mut [u8], offset: usize, value: u32) {
buffer[offset..offset + 4].copy_from_slice(&value.to_le_bytes());
}
fn le_u16(buffer: &[u8], offset: usize) -> u16 {
u16::from_le_bytes([buffer[offset], buffer[offset + 1]])
}
fn le_u32(buffer: &[u8], offset: usize) -> u32 {
u32::from_le_bytes([
buffer[offset],
buffer[offset + 1],
buffer[offset + 2],
buffer[offset + 3],
])
}
+3
View File
@@ -0,0 +1,3 @@
pub mod ext4;
pub mod uefi;
pub mod vfs;
+179
View File
@@ -0,0 +1,179 @@
use core::ffi::c_void;
use crate::efi::{
EFI_LOADED_IMAGE_PROTOCOL_GUID, EFI_NOT_FOUND, EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
EFI_SUCCESS, EfiBootServices, EfiGuid, EfiHandle, EfiLoadedImageProtocol, EfiStatus,
};
use crate::memory::{PAGE_SIZE, PhysicalMemoryManager};
pub const BASH_PATH: &[u16] = &[
'\\' as u16,
'b' as u16,
'i' as u16,
'n' as u16,
'\\' as u16,
'b' as u16,
'a' as u16,
's' as u16,
'h' as u16,
0,
];
pub const SH_PATH: &[u16] = &[
'\\' as u16,
'b' as u16,
'i' as u16,
'n' as u16,
'\\' as u16,
's' as u16,
'h' as u16,
0,
];
const EFI_FILE_MODE_READ: u64 = 1;
const EFI_FILE_INFO_GUID: EfiGuid = EfiGuid::new(
0x09576e92,
0x6d3f,
0x11d2,
[0x8e, 0x39, 0x00, 0xa0, 0xc9, 0x69, 0x72, 0x3b],
);
pub struct LoadedFile {
pub ptr: *const u8,
pub len: usize,
}
#[repr(C)]
struct EfiSimpleFileSystemProtocol {
revision: u64,
open_volume: extern "efiapi" fn(
this: *mut EfiSimpleFileSystemProtocol,
root: *mut *mut EfiFileProtocol,
) -> EfiStatus,
}
#[repr(C)]
struct EfiFileProtocol {
revision: u64,
open: extern "efiapi" fn(
this: *mut EfiFileProtocol,
new_handle: *mut *mut EfiFileProtocol,
file_name: *const u16,
open_mode: u64,
attributes: u64,
) -> EfiStatus,
close: extern "efiapi" fn(this: *mut EfiFileProtocol) -> EfiStatus,
delete: usize,
read: extern "efiapi" fn(
this: *mut EfiFileProtocol,
buffer_size: *mut usize,
buffer: *mut c_void,
) -> EfiStatus,
write: usize,
get_position: usize,
set_position: usize,
get_info: extern "efiapi" fn(
this: *mut EfiFileProtocol,
information_type: *const EfiGuid,
buffer_size: *mut usize,
buffer: *mut c_void,
) -> EfiStatus,
}
#[repr(C)]
struct EfiFileInfoPrefix {
size: u64,
file_size: u64,
physical_size: u64,
}
pub unsafe fn load_file(
image_handle: EfiHandle,
boot_services: *mut EfiBootServices,
memory: &mut PhysicalMemoryManager,
path: &[u16],
) -> Option<LoadedFile> {
let mut loaded_image = core::ptr::null_mut();
let status = unsafe {
((*boot_services).handle_protocol)(
image_handle,
&EFI_LOADED_IMAGE_PROTOCOL_GUID,
&mut loaded_image,
)
};
if status != EFI_SUCCESS || loaded_image.is_null() {
return None;
}
let loaded_image = loaded_image as *mut EfiLoadedImageProtocol;
let mut fs = core::ptr::null_mut();
let status = unsafe {
((*boot_services).handle_protocol)(
(*loaded_image).device_handle,
&EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID,
&mut fs,
)
};
if status != EFI_SUCCESS || fs.is_null() {
return None;
}
let fs = fs as *mut EfiSimpleFileSystemProtocol;
let mut root = core::ptr::null_mut();
let status = unsafe { ((*fs).open_volume)(fs, &mut root) };
if status != EFI_SUCCESS || root.is_null() {
return None;
}
let mut file = core::ptr::null_mut();
let status = unsafe { ((*root).open)(root, &mut file, path.as_ptr(), EFI_FILE_MODE_READ, 0) };
unsafe {
((*root).close)(root);
}
if status == EFI_NOT_FOUND || status != EFI_SUCCESS || file.is_null() {
return None;
}
let size = unsafe { file_size(file)? };
let pages = size.div_ceil(PAGE_SIZE).max(1);
let buffer = memory.alloc_pages(pages)?;
let mut read_len = size;
let status = unsafe { ((*file).read)(file, &mut read_len, buffer.cast::<c_void>()) };
unsafe {
((*file).close)(file);
}
if status != EFI_SUCCESS || read_len == 0 {
unsafe {
memory.free_pages(buffer, pages);
}
return None;
}
Some(LoadedFile {
ptr: buffer as *const u8,
len: read_len,
})
}
unsafe fn file_size(file: *mut EfiFileProtocol) -> Option<usize> {
let mut info_buffer = [0u8; 1024];
let mut info_size = info_buffer.len();
let status = unsafe {
((*file).get_info)(
file,
&EFI_FILE_INFO_GUID,
&mut info_size,
info_buffer.as_mut_ptr().cast::<c_void>(),
)
};
if status != EFI_SUCCESS {
return None;
}
if info_size < core::mem::size_of::<EfiFileInfoPrefix>() {
return None;
}
let info = unsafe { &*(info_buffer.as_ptr() as *const EfiFileInfoPrefix) };
usize::try_from(info.file_size).ok()
}
+1070
View File
File diff suppressed because it is too large Load Diff
+127
View File
@@ -0,0 +1,127 @@
use core::arch::asm;
pub const KERNEL_CODE_SELECTOR: u16 = 0x08;
pub const KERNEL_DATA_SELECTOR: u16 = 0x10;
pub const SYSCALL_USER_SELECTOR_BASE: u16 = 0x18;
pub const USER_DATA_SELECTOR: u16 = 0x20 | 3;
pub const USER_CODE_SELECTOR: u16 = 0x28 | 3;
const TSS_SELECTOR: u16 = 0x30;
const KERNEL_STACK_SIZE: usize = 16 * 4096;
const DOUBLE_FAULT_STACK_SIZE: usize = 16 * 4096;
#[repr(C, align(8))]
#[derive(Clone, Copy)]
struct Gdt {
entries: [u64; 8],
}
#[repr(C, packed)]
struct GdtPointer {
limit: u16,
base: u64,
}
static GDT: Gdt = Gdt {
entries: [
0,
// 64-bit kernel code: present, ring 0, executable/readable, long mode.
0x00af_9a00_0000_ffff,
// Kernel data: present, ring 0, writable.
0x00cf_9200_0000_ffff,
// Sysret compatibility slot. In long mode this is unused, but STAR expects it.
0x00cf_f200_0000_ffff,
// User data: present, ring 3, writable.
0x00cf_f200_0000_ffff,
// 64-bit user code: present, ring 3, executable/readable, long mode.
0x00af_fa00_0000_ffff,
0,
0,
],
};
#[repr(C, packed)]
struct TaskStateSegment {
reserved0: u32,
rsp: [u64; 3],
reserved1: u64,
ist: [u64; 7],
reserved2: u64,
reserved3: u16,
io_map_base: u16,
}
static mut TSS: TaskStateSegment = TaskStateSegment {
reserved0: 0,
rsp: [0; 3],
reserved1: 0,
ist: [0; 7],
reserved2: 0,
reserved3: 0,
io_map_base: core::mem::size_of::<TaskStateSegment>() as u16,
};
#[repr(C, align(16))]
struct KernelStack([u8; KERNEL_STACK_SIZE]);
#[repr(C, align(16))]
struct DoubleFaultStack([u8; DOUBLE_FAULT_STACK_SIZE]);
static mut KERNEL_STACK: KernelStack = KernelStack([0; KERNEL_STACK_SIZE]);
static mut DOUBLE_FAULT_STACK: DoubleFaultStack = DoubleFaultStack([0; DOUBLE_FAULT_STACK_SIZE]);
static mut GDT_STORAGE: Gdt = GDT;
pub unsafe fn init() {
unsafe {
let stack_base = (&raw const KERNEL_STACK).cast::<u8>() as u64;
TSS.rsp[0] = stack_base + KERNEL_STACK_SIZE as u64;
let double_fault_stack_base = (&raw const DOUBLE_FAULT_STACK).cast::<u8>() as u64;
TSS.ist[0] = double_fault_stack_base + DOUBLE_FAULT_STACK_SIZE as u64;
install_tss_descriptor();
}
let gdt_ptr = GdtPointer {
limit: (core::mem::size_of::<Gdt>() - 1) as u16,
base: (&raw const GDT_STORAGE).cast::<u8>() as u64,
};
unsafe {
asm!(
"lgdt [{gdt_ptr}]",
"push {code}",
"lea rax, [rip + 2f]",
"push rax",
"retfq",
"2:",
"mov ax, {data:x}",
"mov ds, ax",
"mov es, ax",
"mov ss, ax",
"ltr {tss:x}",
gdt_ptr = in(reg) &gdt_ptr,
code = in(reg) KERNEL_CODE_SELECTOR as u64,
data = in(reg) KERNEL_DATA_SELECTOR,
tss = in(reg) TSS_SELECTOR,
out("rax") _,
options(preserves_flags),
);
}
}
unsafe fn install_tss_descriptor() {
let base = (&raw const TSS).cast::<u8>() as u64;
let limit = (core::mem::size_of::<TaskStateSegment>() - 1) as u64;
let low = (limit & 0xffff)
| ((base & 0x00ff_ffff) << 16)
| (0x89 << 40)
| ((limit & 0x000f_0000) << 32)
| ((base & 0xff00_0000) << 32);
let high = base >> 32;
unsafe {
GDT_STORAGE.entries[6] = low;
GDT_STORAGE.entries[7] = high;
}
}
+718
View File
@@ -0,0 +1,718 @@
use core::arch::{asm, global_asm};
use crate::{console, memory::PhysicalMemoryManager, syscall::TrapFrame, task};
const IDT_ENTRY_COUNT: usize = 256;
const INTERRUPT_GATE: u8 = 0x8e;
const USER_INTERRUPT_GATE: u8 = 0xee;
const DOUBLE_FAULT_IST: u8 = 1;
global_asm!(
r#"
.macro EXC_NOERR vector
.global zeroos_exception_\vector
zeroos_exception_\vector:
push 0
push \vector
jmp zeroos_exception_common
.endm
.macro EXC_ERR vector
.global zeroos_exception_\vector
zeroos_exception_\vector:
push \vector
jmp zeroos_exception_common
.endm
EXC_NOERR 0
EXC_NOERR 1
EXC_NOERR 2
EXC_NOERR 3
EXC_NOERR 4
EXC_NOERR 5
EXC_NOERR 6
EXC_NOERR 7
EXC_ERR 8
EXC_NOERR 9
EXC_ERR 10
EXC_ERR 11
EXC_ERR 12
EXC_ERR 13
EXC_ERR 14
EXC_NOERR 15
EXC_NOERR 16
EXC_ERR 17
EXC_NOERR 18
EXC_NOERR 19
EXC_NOERR 20
EXC_ERR 21
EXC_NOERR 22
EXC_NOERR 23
EXC_NOERR 24
EXC_NOERR 25
EXC_NOERR 26
EXC_NOERR 27
EXC_NOERR 28
EXC_ERR 29
EXC_ERR 30
EXC_NOERR 31
zeroos_exception_common:
cld
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rcx, rsp
sub rsp, 40
call zeroos_exception_handler
add rsp, 40
test rax, rax
jnz zeroos_exception_return
mov rcx, [rsp + 120]
mov rdx, [rsp + 128]
mov r8, [rsp + 136]
mov r9, [rsp + 144]
mov rax, [rsp + 152]
mov rbx, [rsp + 160]
sub rsp, 56
mov [rsp + 32], rax
mov [rsp + 40], rbx
call zeroos_exception_panic
add rsp, 56
1:
cli
hlt
jmp 1b
zeroos_exception_return:
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
add rsp, 16
iretq
.global zeroos_isr_ignore
zeroos_isr_ignore:
iretq
.macro IRQ_IGNORE irq
.global zeroos_irq_ignore_\irq
zeroos_irq_ignore_\irq:
cld
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
mov rcx, \irq
sub rsp, 40
call zeroos_irq_ignore_handler
add rsp, 40
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
iretq
.endm
IRQ_IGNORE 0
IRQ_IGNORE 2
IRQ_IGNORE 3
IRQ_IGNORE 4
IRQ_IGNORE 5
IRQ_IGNORE 6
IRQ_IGNORE 8
IRQ_IGNORE 9
IRQ_IGNORE 10
IRQ_IGNORE 11
IRQ_IGNORE 12
IRQ_IGNORE 13
IRQ_IGNORE 14
.global zeroos_irq0_timer
zeroos_irq0_timer:
cld
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
sub rsp, 40
call zeroos_timer_interrupt
add rsp, 40
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
iretq
.global zeroos_irq1_keyboard
zeroos_irq1_keyboard:
cld
push rax
push rcx
push rdx
push rbx
push rbp
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
sub rsp, 40
call zeroos_ps2_keyboard_interrupt
add rsp, 40
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rbp
pop rbx
pop rdx
pop rcx
pop rax
iretq
.global zeroos_int80_syscall
zeroos_int80_syscall:
cld
push rbx
push rbp
push rcx
push rdx
push rsi
push rdi
push r8
push r9
push r10
push r11
push r12
push r13
push r14
push r15
sub rsp, 72
mov [rsp + 32], r10
mov [rsp + 40], r8
mov [rsp + 48], r9
mov r9, rdx
mov r8, rsi
mov rdx, rdi
mov rcx, rax
call zeroos_int80_syscall_dispatch
add rsp, 72
pop r15
pop r14
pop r13
pop r12
pop r11
pop r10
pop r9
pop r8
pop rdi
pop rsi
pop rdx
pop rcx
pop rbp
pop rbx
iretq
"#
);
unsafe extern "C" {
fn zeroos_exception_0();
fn zeroos_exception_1();
fn zeroos_exception_2();
fn zeroos_exception_3();
fn zeroos_exception_4();
fn zeroos_exception_5();
fn zeroos_exception_6();
fn zeroos_exception_7();
fn zeroos_exception_8();
fn zeroos_exception_9();
fn zeroos_exception_10();
fn zeroos_exception_11();
fn zeroos_exception_12();
fn zeroos_exception_13();
fn zeroos_exception_14();
fn zeroos_exception_15();
fn zeroos_exception_16();
fn zeroos_exception_17();
fn zeroos_exception_18();
fn zeroos_exception_19();
fn zeroos_exception_20();
fn zeroos_exception_21();
fn zeroos_exception_22();
fn zeroos_exception_23();
fn zeroos_exception_24();
fn zeroos_exception_25();
fn zeroos_exception_26();
fn zeroos_exception_27();
fn zeroos_exception_28();
fn zeroos_exception_29();
fn zeroos_exception_30();
fn zeroos_exception_31();
fn zeroos_isr_ignore();
fn zeroos_irq_ignore_0();
fn zeroos_irq_ignore_2();
fn zeroos_irq_ignore_3();
fn zeroos_irq_ignore_4();
fn zeroos_irq_ignore_5();
fn zeroos_irq_ignore_6();
fn zeroos_irq_ignore_8();
fn zeroos_irq_ignore_9();
fn zeroos_irq_ignore_10();
fn zeroos_irq_ignore_11();
fn zeroos_irq_ignore_12();
fn zeroos_irq_ignore_13();
fn zeroos_irq_ignore_14();
fn zeroos_irq0_timer();
fn zeroos_irq1_keyboard();
fn zeroos_int80_syscall();
}
#[repr(C, packed)]
#[derive(Clone, Copy)]
struct IdtEntry {
offset_low: u16,
selector: u16,
ist: u8,
options: u8,
offset_mid: u16,
offset_high: u32,
reserved: u32,
}
impl IdtEntry {
const MISSING: Self = Self {
offset_low: 0,
selector: 0,
ist: 0,
options: 0,
offset_mid: 0,
offset_high: 0,
reserved: 0,
};
fn set(&mut self, handler: unsafe extern "C" fn(), selector: u16) {
self.set_with_options(handler, selector, INTERRUPT_GATE);
}
fn set_user(&mut self, handler: unsafe extern "C" fn(), selector: u16) {
self.set_with_options(handler, selector, USER_INTERRUPT_GATE);
}
fn set_with_options(&mut self, handler: unsafe extern "C" fn(), selector: u16, options: u8) {
self.set_with_ist(handler, selector, options, 0);
}
fn set_with_ist(
&mut self,
handler: unsafe extern "C" fn(),
selector: u16,
options: u8,
ist: u8,
) {
let addr = handler as usize as u64;
self.offset_low = addr as u16;
self.selector = selector;
self.ist = ist & 0x7;
self.options = options;
self.offset_mid = (addr >> 16) as u16;
self.offset_high = (addr >> 32) as u32;
self.reserved = 0;
}
}
#[repr(C, packed)]
struct IdtPointer {
limit: u16,
base: u64,
}
#[repr(C)]
pub struct ExceptionFrame {
r15: usize,
r14: usize,
r13: usize,
r12: usize,
r11: usize,
r10: usize,
r9: usize,
r8: usize,
rdi: usize,
rsi: usize,
rbp: usize,
rbx: usize,
rdx: usize,
rcx: usize,
rax: usize,
vector: usize,
error: usize,
rip: usize,
cs: usize,
rflags: usize,
rsp: usize,
ss: usize,
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_exception_handler(frame: *mut ExceptionFrame) -> usize {
if frame.is_null() {
return 0;
}
let frame = unsafe { &mut *frame };
if frame.cs & 3 != 3 {
return 0;
}
let recoverable_vfork_child = task::is_current_vfork_child();
if !recoverable_vfork_child {
console::write(b"user exception ");
write_hex(frame.vector as u64);
console::write(b" rip=");
write_hex(frame.rip as u64);
console::write(b" rsp=");
write_hex(frame.rsp as u64);
console::write(b" rax=");
write_hex(frame.rax as u64);
console::write(b" bytes=");
if crate::syscall::is_user_mapped(frame.rip, 8) {
unsafe {
let rip = frame.rip as *const u8;
for index in 0..8 {
write_hex_byte(rip.add(index).read());
}
}
} else {
console::write(b"<invalid-rip>");
}
console::write(b", killing task\n");
}
let mut trap = TrapFrame {
rax: frame.rax,
rcx: frame.rcx,
rdx: frame.rdx,
rbx: frame.rbx,
rbp: frame.rbp,
rsi: frame.rsi,
rdi: frame.rdi,
r8: frame.r8,
r9: frame.r9,
r10: frame.r10,
r11: frame.r11,
r12: frame.r12,
r13: frame.r13,
r14: frame.r14,
r15: frame.r15,
rip: frame.rip,
rflags: frame.rflags,
rsp: frame.rsp,
};
if !task::exit_current(128 + frame.vector as isize, &mut trap) {
loop {
unsafe {
asm!("sti; hlt", options(nomem, nostack, preserves_flags));
}
}
}
crate::syscall::restore_user_heap();
frame.rax = trap.rax;
frame.rcx = trap.rcx;
frame.rdx = trap.rdx;
frame.rbx = trap.rbx;
frame.rbp = trap.rbp;
frame.rsi = trap.rsi;
frame.rdi = trap.rdi;
frame.r8 = trap.r8;
frame.r9 = trap.r9;
frame.r10 = trap.r10;
frame.r11 = trap.r11;
frame.r12 = trap.r12;
frame.r13 = trap.r13;
frame.r14 = trap.r14;
frame.r15 = trap.r15;
frame.rip = trap.rip;
frame.rflags = trap.rflags;
frame.rsp = trap.rsp;
1
}
pub unsafe fn init(memory: &mut PhysicalMemoryManager) -> Option<()> {
let page = memory.alloc_page()?;
let idt = page as *mut [IdtEntry; IDT_ENTRY_COUNT];
unsafe {
(*idt).fill(IdtEntry::MISSING);
}
let selector = current_code_segment();
for vector in 0..IDT_ENTRY_COUNT {
unsafe {
(*idt)[vector].set(zeroos_isr_ignore, selector);
}
}
let exceptions: [unsafe extern "C" fn(); 32] = [
zeroos_exception_0,
zeroos_exception_1,
zeroos_exception_2,
zeroos_exception_3,
zeroos_exception_4,
zeroos_exception_5,
zeroos_exception_6,
zeroos_exception_7,
zeroos_exception_8,
zeroos_exception_9,
zeroos_exception_10,
zeroos_exception_11,
zeroos_exception_12,
zeroos_exception_13,
zeroos_exception_14,
zeroos_exception_15,
zeroos_exception_16,
zeroos_exception_17,
zeroos_exception_18,
zeroos_exception_19,
zeroos_exception_20,
zeroos_exception_21,
zeroos_exception_22,
zeroos_exception_23,
zeroos_exception_24,
zeroos_exception_25,
zeroos_exception_26,
zeroos_exception_27,
zeroos_exception_28,
zeroos_exception_29,
zeroos_exception_30,
zeroos_exception_31,
];
for (vector, handler) in exceptions.iter().enumerate() {
unsafe {
(*idt)[vector].set(*handler, selector);
}
}
unsafe {
// Vector 15 is reserved on x86. In practice, unexpected legacy/spurious
// interrupts can show up here while bringing interrupt controllers up.
(*idt)[0x0f].set(zeroos_isr_ignore, selector);
(*idt)[8].set_with_ist(
zeroos_exception_8,
selector,
INTERRUPT_GATE,
DOUBLE_FAULT_IST,
);
// Remapped PIC spurious IRQ7/IRQ15.
(*idt)[0x27].set(zeroos_isr_ignore, selector);
(*idt)[0x2f].set(zeroos_isr_ignore, selector);
}
let irq_ignore_handlers: [(usize, unsafe extern "C" fn()); 13] = [
(0x20, zeroos_irq_ignore_0),
(0x22, zeroos_irq_ignore_2),
(0x23, zeroos_irq_ignore_3),
(0x24, zeroos_irq_ignore_4),
(0x25, zeroos_irq_ignore_5),
(0x26, zeroos_irq_ignore_6),
(0x28, zeroos_irq_ignore_8),
(0x29, zeroos_irq_ignore_9),
(0x2a, zeroos_irq_ignore_10),
(0x2b, zeroos_irq_ignore_11),
(0x2c, zeroos_irq_ignore_12),
(0x2d, zeroos_irq_ignore_13),
(0x2e, zeroos_irq_ignore_14),
];
unsafe {
for (vector, handler) in irq_ignore_handlers {
(*idt)[vector].set(handler, selector);
}
(*idt)[0x27].set(zeroos_isr_ignore, selector);
(*idt)[0x2f].set(zeroos_isr_ignore, selector);
(*idt)[0x20].set(zeroos_irq0_timer, selector);
(*idt)[0x21].set(zeroos_irq1_keyboard, selector);
(*idt)[0x80].set_user(zeroos_int80_syscall, selector);
}
let idt_ptr = IdtPointer {
limit: (core::mem::size_of::<IdtEntry>() * IDT_ENTRY_COUNT - 1) as u16,
base: idt as u64,
};
unsafe {
asm!("lidt [{}]", in(reg) &idt_ptr, options(readonly, nostack, preserves_flags));
}
Some(())
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_irq_ignore_handler(irq: u64) {
unsafe {
crate::drivers::platform::pic::end_of_interrupt(irq as u8);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_timer_interrupt() {
crate::drivers::platform::timer::tick();
unsafe {
crate::drivers::platform::pic::end_of_interrupt(crate::drivers::platform::pic::TIMER_IRQ);
}
}
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_exception_panic(
vector: u64,
error_code: u64,
rip: u64,
cs: u64,
rflags: u64,
rsp: u64,
) -> ! {
console::write_panic(b"\r\nKernel panic - not syncing: CPU exception\r\n");
console::write_panic(b"vector: ");
write_hex(vector);
console::write_panic(b" error: ");
write_hex(error_code);
console::write_panic(b"\r\nrip: ");
write_hex(rip);
console::write_panic(b" cs: ");
write_hex(cs);
console::write_panic(b"\r\nrflags: ");
write_hex(rflags);
console::write_panic(b"\r\nrsp: ");
write_hex(rsp);
console::write_panic(b"\r\n");
loop {
unsafe {
asm!("cli", "hlt", options(nomem, nostack, preserves_flags));
}
}
}
fn write_hex(value: u64) {
console::write_panic(b"0x");
for index in 0..16 {
let shift = (15 - index) * 4;
let digit = ((value >> shift) & 0xf) as u8;
write_hex_digit(digit);
}
}
fn write_hex_digit(digit: u8) {
console::write_panic(match digit {
0 => b"0",
1 => b"1",
2 => b"2",
3 => b"3",
4 => b"4",
5 => b"5",
6 => b"6",
7 => b"7",
8 => b"8",
9 => b"9",
10 => b"a",
11 => b"b",
12 => b"c",
13 => b"d",
14 => b"e",
_ => b"f",
});
}
fn write_hex_byte(value: u8) {
write_hex_digit(value >> 4);
write_hex_digit(value & 0x0f);
}
fn current_code_segment() -> u16 {
let cs: u16;
unsafe {
asm!("mov {0:x}, cs", out(reg) cs, options(nomem, nostack, preserves_flags));
}
cs
}
+66
View File
@@ -0,0 +1,66 @@
use core::arch::asm;
pub unsafe fn inb(port: u16) -> u8 {
let value: u8;
unsafe {
asm!("in al, dx", out("al") value, in("dx") port, options(nomem, nostack, preserves_flags));
}
value
}
pub unsafe fn outb(port: u16, value: u8) {
unsafe {
asm!("out dx, al", in("dx") port, in("al") value, options(nomem, nostack, preserves_flags));
}
}
pub unsafe fn inl(port: u16) -> u32 {
let value: u32;
unsafe {
asm!("in eax, dx", out("eax") value, in("dx") port, options(nomem, nostack, preserves_flags));
}
value
}
pub unsafe fn outl(port: u16, value: u32) {
unsafe {
asm!("out dx, eax", in("dx") port, in("eax") value, options(nomem, nostack, preserves_flags));
}
}
pub fn enable_interrupts() {
unsafe {
asm!("sti", options(nomem, nostack, preserves_flags));
}
}
pub fn disable_interrupts() {
unsafe {
asm!("cli", options(nomem, nostack, preserves_flags));
}
}
pub fn save_flags_and_disable_interrupts() -> u64 {
let flags: u64;
unsafe {
asm!(
"pushfq",
"pop {}",
"cli",
out(reg) flags,
options(nomem, preserves_flags),
);
}
flags
}
pub fn restore_flags(flags: u64) {
unsafe {
asm!(
"push {}",
"popfq",
in(reg) flags,
options(nomem, preserves_flags),
);
}
}
+430
View File
@@ -0,0 +1,430 @@
#![allow(dead_code)]
use crate::task;
pub const IPC_FD_BASE: usize = 256;
pub const MAX_FDS: usize = 512;
const MAX_ENDPOINTS: usize = 256;
const IPC_BUFFER_SIZE: usize = 4096;
const EAFNOSUPPORT: isize = 97;
const EAGAIN: isize = 11;
const EBADF: isize = 9;
const EFAULT: isize = 14;
const EINVAL: isize = 22;
const EMFILE: isize = 24;
const ENFILE: isize = 23;
const ENOTCONN: isize = 107;
const EOPNOTSUPP: isize = 95;
const EPIPE: isize = 32;
const AF_UNIX: usize = 1;
const SOCK_STREAM: usize = 1;
const SOCK_DGRAM: usize = 2;
const SOCK_NONBLOCK: usize = 0o4000;
const SOCK_CLOEXEC: usize = 0o2000000;
const POLLIN: i16 = 0x0001;
const POLLOUT: i16 = 0x0004;
const POLLERR: i16 = 0x0008;
const POLLHUP: i16 = 0x0010;
const S_IFIFO: u32 = 0o010000;
const S_IFSOCK: u32 = 0o140000;
#[derive(Clone, Copy, PartialEq, Eq)]
enum EndpointKind {
Empty,
PipeRead,
PipeWrite,
UnixSocket,
}
#[derive(Clone, Copy)]
struct Endpoint {
kind: EndpointKind,
owner_pid: usize,
peer: Option<usize>,
refs: usize,
buffer: [u8; IPC_BUFFER_SIZE],
read_pos: usize,
write_pos: usize,
len: usize,
closed: bool,
peer_closed: bool,
}
impl Endpoint {
const fn empty() -> Self {
Self {
kind: EndpointKind::Empty,
owner_pid: 0,
peer: None,
refs: 0,
buffer: [0; IPC_BUFFER_SIZE],
read_pos: 0,
write_pos: 0,
len: 0,
closed: false,
peer_closed: false,
}
}
fn is_used(&self) -> bool {
self.kind != EndpointKind::Empty
}
}
#[derive(Clone, Copy)]
struct FileHandle {
endpoint: Option<usize>,
}
impl FileHandle {
const fn empty() -> Self {
Self { endpoint: None }
}
}
#[derive(Clone, Copy)]
pub struct Message {
pub sender: usize,
pub opcode: u16,
pub args: [usize; 6],
}
static mut ENDPOINTS: [Endpoint; MAX_ENDPOINTS] = [Endpoint::empty(); MAX_ENDPOINTS];
static mut FDS: [FileHandle; MAX_FDS] = [FileHandle::empty(); MAX_FDS];
pub unsafe fn init() {
unsafe {
let mut index = 0;
while index < MAX_ENDPOINTS {
ENDPOINTS[index] = Endpoint::empty();
index += 1;
}
let mut fd = 0;
while fd < MAX_FDS {
FDS[fd] = FileHandle::empty();
fd += 1;
}
}
}
pub fn pipe(pipefd: *mut i32) -> isize {
if pipefd.is_null() {
return -EFAULT;
}
unsafe {
let Some(read_endpoint) = alloc_endpoint(EndpointKind::PipeRead) else {
return -ENFILE;
};
let Some(write_endpoint) = alloc_endpoint(EndpointKind::PipeWrite) else {
free_endpoint(read_endpoint);
return -ENFILE;
};
ENDPOINTS[read_endpoint].peer = Some(write_endpoint);
ENDPOINTS[write_endpoint].peer = Some(read_endpoint);
let Some(read_fd) = alloc_fd_for_endpoint(read_endpoint, IPC_FD_BASE) else {
free_endpoint(write_endpoint);
free_endpoint(read_endpoint);
return -EMFILE;
};
let Some(write_fd) = alloc_fd_for_endpoint(write_endpoint, IPC_FD_BASE) else {
release_fd(read_fd);
free_endpoint(write_endpoint);
free_endpoint(read_endpoint);
return -EMFILE;
};
pipefd.write(read_fd as i32);
pipefd.add(1).write(write_fd as i32);
0
}
}
pub fn socketpair(domain: usize, socket_type: usize, protocol: usize, sv: *mut i32) -> isize {
if sv.is_null() {
return -EFAULT;
}
if domain != AF_UNIX {
return -EAFNOSUPPORT;
}
if protocol != 0 {
return -EOPNOTSUPP;
}
let base_type = socket_type & !(SOCK_NONBLOCK | SOCK_CLOEXEC);
if base_type != SOCK_STREAM && base_type != SOCK_DGRAM {
return -EOPNOTSUPP;
}
unsafe {
let Some(left_endpoint) = alloc_endpoint(EndpointKind::UnixSocket) else {
return -ENFILE;
};
let Some(right_endpoint) = alloc_endpoint(EndpointKind::UnixSocket) else {
free_endpoint(left_endpoint);
return -ENFILE;
};
ENDPOINTS[left_endpoint].peer = Some(right_endpoint);
ENDPOINTS[right_endpoint].peer = Some(left_endpoint);
let Some(left_fd) = alloc_fd_for_endpoint(left_endpoint, IPC_FD_BASE) else {
free_endpoint(right_endpoint);
free_endpoint(left_endpoint);
return -EMFILE;
};
let Some(right_fd) = alloc_fd_for_endpoint(right_endpoint, IPC_FD_BASE) else {
release_fd(left_fd);
free_endpoint(right_endpoint);
free_endpoint(left_endpoint);
return -EMFILE;
};
sv.write(left_fd as i32);
sv.add(1).write(right_fd as i32);
0
}
}
pub fn read(fd: usize, buffer: *mut u8, len: usize) -> Option<isize> {
let endpoint_index = endpoint_for_fd(fd)?;
if buffer.is_null() {
return Some(-EFAULT);
}
if len == 0 {
return Some(0);
}
unsafe {
let endpoint = &mut ENDPOINTS[endpoint_index];
match endpoint.kind {
EndpointKind::PipeWrite => Some(-EBADF),
EndpointKind::PipeRead | EndpointKind::UnixSocket => {
if endpoint.len == 0 {
return Some(if endpoint.peer_closed { 0 } else { -EAGAIN });
}
let count = len.min(endpoint.len);
for index in 0..count {
buffer.add(index).write(endpoint.buffer[endpoint.read_pos]);
endpoint.read_pos = (endpoint.read_pos + 1) % IPC_BUFFER_SIZE;
}
endpoint.len -= count;
Some(count as isize)
}
EndpointKind::Empty => Some(-EBADF),
}
}
}
pub fn write(fd: usize, buffer: *const u8, len: usize) -> Option<isize> {
let endpoint_index = endpoint_for_fd(fd)?;
if buffer.is_null() {
return Some(-EFAULT);
}
if len == 0 {
return Some(0);
}
unsafe {
let endpoint = ENDPOINTS[endpoint_index];
match endpoint.kind {
EndpointKind::PipeRead => Some(-EBADF),
EndpointKind::PipeWrite | EndpointKind::UnixSocket => {
let Some(peer_index) = endpoint.peer else {
return Some(-ENOTCONN);
};
let peer = &mut ENDPOINTS[peer_index];
if peer.closed || endpoint.peer_closed {
return Some(-EPIPE);
}
let space = IPC_BUFFER_SIZE - peer.len;
if space == 0 {
return Some(0);
}
let count = len.min(space);
for index in 0..count {
peer.buffer[peer.write_pos] = buffer.add(index).read();
peer.write_pos = (peer.write_pos + 1) % IPC_BUFFER_SIZE;
}
peer.len += count;
Some(count as isize)
}
EndpointKind::Empty => Some(-EBADF),
}
}
}
pub fn close(fd: usize) -> Option<isize> {
endpoint_for_fd(fd)?;
unsafe {
release_fd(fd);
}
Some(0)
}
pub fn dup(fd: usize, min_newfd: usize) -> Option<isize> {
let endpoint = endpoint_for_fd(fd)?;
unsafe {
alloc_fd_for_endpoint(endpoint, min_newfd)
.map(|newfd| newfd as isize)
.or(Some(-EMFILE))
}
}
pub fn dup2(oldfd: usize, newfd: usize) -> Option<isize> {
let endpoint = endpoint_for_fd(oldfd)?;
if newfd >= MAX_FDS {
return Some(-EBADF);
}
if oldfd == newfd {
return Some(newfd as isize);
}
unsafe {
if FDS[newfd].endpoint.is_some() {
release_fd(newfd);
}
FDS[newfd].endpoint = Some(endpoint);
ENDPOINTS[endpoint].refs += 1;
}
Some(newfd as isize)
}
pub fn poll(fd: usize, events: i16) -> Option<i16> {
let endpoint_index = endpoint_for_fd(fd)?;
unsafe {
let endpoint = ENDPOINTS[endpoint_index];
let mut revents = 0i16;
if events & POLLIN != 0 && endpoint.len > 0 {
revents |= POLLIN;
}
if events & POLLOUT != 0 {
if let Some(peer_index) = endpoint.peer {
let peer = ENDPOINTS[peer_index];
if !peer.closed && peer.len < IPC_BUFFER_SIZE {
revents |= POLLOUT;
}
} else {
revents |= POLLERR;
}
}
if endpoint.peer_closed {
revents |= POLLHUP;
}
Some(revents)
}
}
pub fn fstat(fd: usize, stat: *mut u8) -> Option<isize> {
let endpoint_index = endpoint_for_fd(fd)?;
if stat.is_null() {
return Some(-EFAULT);
}
unsafe {
core::ptr::write_bytes(stat, 0, 128);
let mode = match ENDPOINTS[endpoint_index].kind {
EndpointKind::PipeRead | EndpointKind::PipeWrite => S_IFIFO | 0o600,
EndpointKind::UnixSocket => S_IFSOCK | 0o600,
EndpointKind::Empty => return Some(-EBADF),
};
(stat.add(24) as *mut u32).write(mode);
}
Some(0)
}
pub fn is_fd(fd: usize) -> bool {
endpoint_for_fd(fd).is_some()
}
pub fn socket(_domain: usize, _socket_type: usize, _protocol: usize) -> isize {
-EOPNOTSUPP
}
pub fn unsupported_socket_op() -> isize {
-EOPNOTSUPP
}
fn endpoint_for_fd(fd: usize) -> Option<usize> {
if fd >= MAX_FDS {
return None;
}
unsafe { FDS[fd].endpoint }
}
unsafe fn alloc_endpoint(kind: EndpointKind) -> Option<usize> {
unsafe {
for index in 0..MAX_ENDPOINTS {
if !ENDPOINTS[index].is_used() {
ENDPOINTS[index] = Endpoint {
kind,
owner_pid: task::current_pid(),
peer: None,
refs: 0,
buffer: [0; IPC_BUFFER_SIZE],
read_pos: 0,
write_pos: 0,
len: 0,
closed: false,
peer_closed: false,
};
return Some(index);
}
}
}
None
}
unsafe fn alloc_fd_for_endpoint(endpoint: usize, min_fd: usize) -> Option<usize> {
unsafe {
let start = min_fd.min(MAX_FDS);
for fd in start..MAX_FDS {
if FDS[fd].endpoint.is_none() {
FDS[fd].endpoint = Some(endpoint);
ENDPOINTS[endpoint].refs += 1;
return Some(fd);
}
}
}
None
}
unsafe fn release_fd(fd: usize) {
unsafe {
if fd >= MAX_FDS {
return;
}
let Some(endpoint_index) = FDS[fd].endpoint.take() else {
return;
};
if ENDPOINTS[endpoint_index].refs > 0 {
ENDPOINTS[endpoint_index].refs -= 1;
}
if ENDPOINTS[endpoint_index].refs == 0 {
close_endpoint(endpoint_index);
}
}
}
unsafe fn close_endpoint(endpoint_index: usize) {
unsafe {
let peer = ENDPOINTS[endpoint_index].peer;
ENDPOINTS[endpoint_index].closed = true;
if let Some(peer_index) = peer {
ENDPOINTS[peer_index].peer_closed = true;
ENDPOINTS[peer_index].peer = None;
}
free_endpoint(endpoint_index);
}
}
unsafe fn free_endpoint(endpoint_index: usize) {
unsafe {
ENDPOINTS[endpoint_index] = Endpoint::empty();
}
}
+39
View File
@@ -0,0 +1,39 @@
use crate::tty::Tty;
pub enum Level {
Info,
Ok,
}
impl Level {
fn as_bytes(&self) -> &'static [u8] {
match self {
Self::Info => b"[ ] ",
Self::Ok => b"[ ok ] ",
}
}
}
pub struct KernelLogger<'a> {
tty: &'a mut Tty,
}
impl<'a> KernelLogger<'a> {
pub fn new(tty: &'a mut Tty) -> Self {
Self { tty }
}
pub fn info(&mut self, message: &[u8]) {
self.write(Level::Info, message);
}
pub fn ok(&mut self, message: &[u8]) {
self.write(Level::Ok, message);
}
fn write(&mut self, level: Level, message: &[u8]) {
self.tty.write_bytes(level.as_bytes());
self.tty.write_bytes(message);
self.tty.write_bytes(b"\r\n");
}
}
+346
View File
@@ -0,0 +1,346 @@
#![no_std]
#![no_main]
mod console;
mod drivers;
mod efi;
mod elf;
mod font;
mod framebuffer;
mod fs;
mod gdt;
mod interrupts;
mod io;
mod ipc;
mod log;
mod memory;
mod paging;
mod services;
mod syscall;
mod task;
mod tty;
mod user;
use core::panic::PanicInfo;
use efi::{EFI_SUCCESS, EfiHandle, EfiStatus, EfiSystemTable};
use framebuffer::Framebuffer;
use memory::PhysicalMemoryManager;
use paging::{AddressSpace, PageFlags};
use tty::Tty;
static mut MEMORY_MAP_BUFFER: [u8; 65536] = [0; 65536];
static mut PHYSICAL_MEMORY: PhysicalMemoryManager = PhysicalMemoryManager::new();
#[unsafe(no_mangle)]
extern "efiapi" fn efi_main(
image_handle: EfiHandle,
system_table: *mut EfiSystemTable,
) -> EfiStatus {
unsafe {
io::disable_interrupts();
let Some(boot_services) = efi::boot_services(system_table) else {
halt_forever();
};
let Some(gop) = efi::locate_gop(boot_services) else {
halt_forever();
};
let Some(framebuffer) = Framebuffer::from_gop(gop) else {
halt_forever();
};
let mut tty = Tty::new(framebuffer);
tty.clear();
console::init(&raw mut tty);
let mut logger = log::KernelLogger::new(&mut tty);
logger.info(b"ZeroOS kernel initializing");
drivers::platform::pic::remap_and_mask_all();
logger.ok(b"pic");
if let Some(unix_seconds) = efi::read_unix_time(system_table) {
drivers::platform::timer::set_wall_clock_boot_time(unix_seconds);
logger.ok(b"UEFI time");
} else {
logger.info(b"UEFI time unavailable");
}
let Some(memory_map) = efi::get_memory_map(
boot_services,
(&raw mut MEMORY_MAP_BUFFER).cast::<u8>(),
65536,
) else {
halt_forever();
};
logger.ok(b"UEFI memory map");
let memory = &mut *(&raw mut PHYSICAL_MEMORY);
task::init();
memory.init_from_uefi_memory_map(memory_map);
let stats = memory.stats();
let _ = (stats.total_pages, stats.free_pages, stats.used_pages);
logger.ok(b"physical memory manager");
fs::vfs::init();
logger.ok(b"vfs");
ipc::init();
logger.ok(b"ipc");
let ahci_disks = drivers::storage::ahci::init(memory);
let mut ext4_user_image = None;
let mut ext4_user_path = None;
if ahci_disks > 0 {
logger.ok(b"ahci");
if let Some(mut disk) = drivers::storage::ahci::first_disk() {
match drivers::storage::partition::find_linux_partition(&mut disk) {
Ok(partition) => {
let mut root =
drivers::storage::partition::PartitionBlockDevice::new(disk, partition);
match fs::ext4::mount(&mut root) {
Ok(ext4) => {
logger.ok(b"ext4");
let _ = fs::ext4::mount_vfs_tree(&mut root, &ext4);
fs::ext4::register_root(root, ext4);
match fs::ext4::load_path(
&mut root,
&ext4,
memory,
b"/usr/bin/busybox",
) {
Ok(image) => {
ext4_user_image = Some(image);
ext4_user_path = Some(b"/usr/bin/busybox" as &'static [u8]);
logger.ok(b"ext4 /usr/bin/busybox");
}
Err(_) => match fs::ext4::load_path(
&mut root,
&ext4,
memory,
b"/usr/bin/sh",
) {
Ok(image) => {
ext4_user_image = Some(image);
ext4_user_path = Some(b"/usr/bin/sh" as &'static [u8]);
logger.ok(b"ext4 /usr/bin/sh");
}
Err(_) => logger.info(b"ext4 userspace not loaded"),
},
}
}
Err(_) => logger.info(b"ext4 not mounted"),
}
}
Err(_) => logger.info(b"no linux gpt partition"),
}
}
} else {
logger.info(b"no ahci disk");
}
let (loaded_user_image, loaded_user_path) = if let Some(image) = ext4_user_image {
(Some(image), ext4_user_path)
} else if let Some(image) =
fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::BASH_PATH)
{
(Some(image), Some(b"/bin/bash" as &'static [u8]))
} else if let Some(image) =
fs::uefi::load_file(image_handle, boot_services, memory, fs::uefi::SH_PATH)
{
(Some(image), Some(b"/bin/sh" as &'static [u8]))
} else {
(None, None)
};
let userspace_image_found = loaded_user_image.is_some();
if loaded_user_image.is_some() {
if let (Some(image), Some(path)) = (&loaded_user_image, loaded_user_path) {
let _ = fs::vfs::mount_static_file(path, image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/busybox", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/busybox", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/sh", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/usr/bin/bash", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/sh", image.ptr, image.len);
let _ = fs::vfs::mount_static_file(b"/bin/bash", image.ptr, image.len);
}
logger.ok(b"userspace image loaded from esp");
} else {
logger.info(b"no /bin/bash or /bin/sh on esp, using built-in user program");
}
gdt::init();
logger.ok(b"gdt");
if interrupts::init(memory).is_none() {
halt_forever();
}
logger.ok(b"idt");
drivers::platform::timer::init();
logger.ok(b"pit timer");
drivers::input::ps2::init();
logger.ok(b"ps/2 keyboard");
let Some(mut address_space) = AddressSpace::new_kernel(memory) else {
halt_forever();
};
logger.ok(b"address space");
let mut userspace_program = None;
let user_entry = if let Some(image) = loaded_user_image {
match elf::load_user_elf(&image, memory) {
Some(program) => {
logger.ok(b"elf userspace program");
userspace_program = Some(user::UserAux {
entry: program.entry,
phdr: program.phdr,
phent: program.phent,
phnum: program.phnum,
});
Some(program.entry)
}
None => {
logger.info(b"elf load failed, using built-in user program");
None
}
}
} else {
None
};
let Some(test_page) = memory.alloc_page() else {
halt_forever();
};
if address_space
.map_page(
memory,
0xffff_8000_0000_0000,
test_page as u64,
PageFlags::WRITABLE,
)
.is_none()
{
halt_forever();
}
let _ = address_space.translate(0xffff_8000_0000_0000);
let _ = address_space.unmap_page(0xffff_8000_0000_0000);
memory.free_page(test_page);
let _ = (address_space.root_table(), address_space.identity_tables());
logger.ok(b"paging smoke test");
let Some(final_memory_map) = efi::get_memory_map(
boot_services,
(&raw mut MEMORY_MAP_BUFFER).cast::<u8>(),
65536,
) else {
logger.info(b"final UEFI memory map failed");
halt_forever();
};
logger.ok(b"final UEFI memory map");
let exit_status = ((*boot_services).exit_boot_services)(image_handle, final_memory_map.key);
if exit_status != EFI_SUCCESS {
logger.info(b"exit boot services failed");
halt_forever();
}
logger.ok(b"exit boot services");
address_space.activate();
logger.ok(b"cr3");
tty.clear();
if user_entry.is_some() {
if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/busybox sh\r\n");
} else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/sh\r\n");
} else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) {
console::write(b"enter userspace: /usr/bin/bash\r\n");
} else {
console::write(b"enter userspace: /bin/bash\r\n");
}
} else if userspace_image_found {
console::write(b"/bin/bash found but ELF load failed; enter built-in userspace\r\n");
} else {
console::write(b"/bin/bash not found on ESP; enter built-in userspace\r\n");
}
syscall::init(memory, &raw mut address_space);
io::enable_interrupts();
if let Some(entry) = user_entry {
if loaded_user_path == Some(b"/usr/bin/busybox" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/busybox", b"sh"],
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/usr/bin/sh" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/sh"],
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/usr/bin/bash" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/usr/bin/bash"],
user_program_or_default(user_entry, userspace_program),
);
} else if loaded_user_path == Some(b"/bin/sh" as &'static [u8]) {
user::enter_elf_with_args(
entry,
&[b"/bin/sh"],
user_program_or_default(user_entry, userspace_program),
);
} else {
user::enter_elf_with_args(
entry,
&[b"/bin/bash"],
user_program_or_default(user_entry, userspace_program),
);
}
} else {
user::enter();
}
}
}
#[allow(dead_code)]
fn kernel_loop(tty: &mut Tty) -> ! {
loop {
while let Some(ch) = drivers::input::ps2::read_char() {
match ch {
0x08 => tty.backspace(),
b'\r' => tty.write_bytes(b"\r\n"),
ch => tty.put_char(ch),
}
}
halt();
}
}
fn halt() {
unsafe {
core::arch::asm!("hlt", options(nomem, nostack, preserves_flags));
}
}
fn user_program_or_default(entry: Option<u64>, aux: Option<user::UserAux>) -> user::UserAux {
aux.unwrap_or(user::UserAux {
entry: entry.unwrap_or(0),
phdr: 0,
phent: 0,
phnum: 0,
})
}
fn halt_forever() -> ! {
loop {
halt();
}
}
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
halt_forever();
}
+194
View File
@@ -0,0 +1,194 @@
use crate::efi::{EFI_CONVENTIONAL_MEMORY, EfiMemoryDescriptor, MemoryMap};
pub const PAGE_SIZE: usize = 4096;
pub const MAX_PHYSICAL_MEMORY: u64 = 64 * 1024 * 1024 * 1024;
pub const MAX_PHYSICAL_PAGES: usize = MAX_PHYSICAL_MEMORY as usize / PAGE_SIZE;
const BITMAP_WORDS: usize = MAX_PHYSICAL_PAGES / 64;
const MIN_USABLE_PHYSICAL_ADDRESS: u64 = 0x100000;
const USER_ELF_LOW_START: u64 = 0x400000;
const USER_ELF_LOW_END: u64 = 0x4000000;
pub struct PhysicalMemoryManager {
bitmap: [u64; BITMAP_WORDS],
total_pages: usize,
free_pages: usize,
used_pages: usize,
next_search_page: usize,
}
#[derive(Clone, Copy)]
pub struct MemoryStats {
pub total_pages: usize,
pub free_pages: usize,
pub used_pages: usize,
}
impl PhysicalMemoryManager {
pub const fn new() -> Self {
Self {
// 1 means reserved/used. Start fully reserved until UEFI memory map marks pages free.
bitmap: [u64::MAX; BITMAP_WORDS],
total_pages: 0,
free_pages: 0,
used_pages: MAX_PHYSICAL_PAGES,
next_search_page: 0,
}
}
pub unsafe fn init_from_uefi_memory_map(&mut self, memory_map: MemoryMap) {
self.bitmap.fill(u64::MAX);
self.total_pages = 0;
self.free_pages = 0;
self.used_pages = MAX_PHYSICAL_PAGES;
self.next_search_page = page_index(MIN_USABLE_PHYSICAL_ADDRESS);
let mut offset = 0;
while offset < memory_map.byte_len {
let descriptor =
unsafe { (memory_map.ptr as *const u8).add(offset) as *const EfiMemoryDescriptor };
let descriptor = unsafe { *descriptor };
let start = align_up(
descriptor.physical_start.max(MIN_USABLE_PHYSICAL_ADDRESS),
PAGE_SIZE as u64,
);
let end = descriptor
.physical_start
.saturating_add(descriptor.number_of_pages.saturating_mul(PAGE_SIZE as u64))
.min(MAX_PHYSICAL_MEMORY);
if start < end {
self.total_pages += ((end - start) as usize) / PAGE_SIZE;
}
if descriptor.ty == EFI_CONVENTIONAL_MEMORY {
self.mark_range_free(start, end);
}
offset += memory_map.descriptor_size;
}
self.mark_range_used(USER_ELF_LOW_START, USER_ELF_LOW_END);
}
pub fn alloc_page(&mut self) -> Option<*mut u8> {
self.alloc_pages(1)
}
pub fn alloc_pages(&mut self, count: usize) -> Option<*mut u8> {
if count == 0 || count > MAX_PHYSICAL_PAGES {
return None;
}
let mut run_start = self.next_search_page;
let mut run_len = 0;
for page in self.next_search_page..MAX_PHYSICAL_PAGES {
if self.is_free(page) {
if run_len == 0 {
run_start = page;
}
run_len += 1;
if run_len == count {
for used_page in run_start..run_start + count {
self.set_used(used_page);
}
self.next_search_page = run_start + count;
let address = (run_start * PAGE_SIZE) as *mut u8;
unsafe {
core::ptr::write_bytes(address, 0, count * PAGE_SIZE);
}
return Some(address);
}
} else {
run_len = 0;
}
}
self.next_search_page = page_index(MIN_USABLE_PHYSICAL_ADDRESS);
None
}
pub unsafe fn free_page(&mut self, page: *mut u8) {
unsafe {
self.free_pages(page, 1);
}
}
pub unsafe fn free_pages(&mut self, base: *mut u8, count: usize) {
let start = page_index(base as u64);
for page in start..start.saturating_add(count).min(MAX_PHYSICAL_PAGES) {
self.set_free(page);
}
if start < self.next_search_page {
self.next_search_page = start;
}
}
pub fn stats(&self) -> MemoryStats {
MemoryStats {
total_pages: self.total_pages,
free_pages: self.free_pages,
used_pages: self.used_pages,
}
}
fn mark_range_free(&mut self, start: u64, end: u64) {
let start_page = page_index(start);
let end_page = page_index(end);
for page in start_page..end_page.min(MAX_PHYSICAL_PAGES) {
self.set_free(page);
}
}
fn mark_range_used(&mut self, start: u64, end: u64) {
let start_page = page_index(start);
let end_page = page_index(end);
for page in start_page..end_page.min(MAX_PHYSICAL_PAGES) {
self.set_used(page);
}
if self.next_search_page >= start_page && self.next_search_page < end_page {
self.next_search_page = end_page;
}
}
fn is_free(&self, page: usize) -> bool {
let word = self.bitmap[page / 64];
let bit = page % 64;
word & (1u64 << bit) == 0
}
fn set_free(&mut self, page: usize) {
let word = page / 64;
let bit = page % 64;
let mask = 1u64 << bit;
if self.bitmap[word] & mask != 0 {
self.bitmap[word] &= !mask;
self.free_pages += 1;
self.used_pages = self.used_pages.saturating_sub(1);
}
}
fn set_used(&mut self, page: usize) {
let word = page / 64;
let bit = page % 64;
let mask = 1u64 << bit;
if self.bitmap[word] & mask == 0 {
self.bitmap[word] |= mask;
self.free_pages = self.free_pages.saturating_sub(1);
self.used_pages += 1;
}
}
}
const fn page_index(address: u64) -> usize {
(address as usize) / PAGE_SIZE
}
const fn align_up(value: u64, align: u64) -> u64 {
(value + align - 1) & !(align - 1)
}
+258
View File
@@ -0,0 +1,258 @@
use core::arch::asm;
use crate::memory::{PAGE_SIZE, PhysicalMemoryManager};
const PRESENT: u64 = 1 << 0;
const WRITABLE: u64 = 1 << 1;
const HUGE_PAGE: u64 = 1 << 7;
const CR4_PSE: u64 = 1 << 4;
const ENTRY_COUNT: usize = 512;
const IDENTITY_MAP_GIB: usize = 512;
const PDPTE_COUNT: usize = IDENTITY_MAP_GIB / 2;
#[derive(Clone, Copy)]
pub struct PageFlags(u64);
impl PageFlags {
#[allow(dead_code)]
pub const PRESENT: Self = Self(PRESENT);
pub const WRITABLE: Self = Self(WRITABLE);
#[allow(dead_code)]
pub const USER: Self = Self(1 << 2);
#[allow(dead_code)]
pub const NO_EXECUTE: Self = Self(1 << 63);
pub const fn bits(self) -> u64 {
self.0
}
#[allow(dead_code)]
pub const fn union(self, other: Self) -> Self {
Self(self.0 | other.0)
}
}
#[repr(C, align(4096))]
pub struct PageTable {
entries: [u64; ENTRY_COUNT],
}
impl PageTable {
fn zero(&mut self) {
self.entries.fill(0);
}
}
pub struct AddressSpace {
pml4: *mut PageTable,
identity_pdpt: *mut PageTable,
identity_pds: [*mut PageTable; PDPTE_COUNT],
}
impl AddressSpace {
pub unsafe fn new_kernel(memory: &mut PhysicalMemoryManager) -> Option<Self> {
let pml4 = memory.alloc_page()? as *mut PageTable;
let identity_pdpt = memory.alloc_page()? as *mut PageTable;
let mut identity_pds = [core::ptr::null_mut(); PDPTE_COUNT];
let identity_flags = PRESENT | WRITABLE | PageFlags::USER.bits();
unsafe {
(*pml4).zero();
(*identity_pdpt).zero();
(*pml4).entries[0] = identity_pdpt as u64 | identity_flags;
}
for (pdpt_index, pd) in identity_pds.iter_mut().enumerate() {
*pd = memory.alloc_page()? as *mut PageTable;
unsafe {
(**pd).zero();
(*identity_pdpt).entries[pdpt_index] = *pd as u64 | identity_flags;
for pd_index in 0..ENTRY_COUNT {
let physical = ((pdpt_index * ENTRY_COUNT + pd_index) as u64) * 2 * 1024 * 1024;
(**pd).entries[pd_index] = physical | identity_flags | HUGE_PAGE;
}
}
}
Some(Self {
pml4,
identity_pdpt,
identity_pds,
})
}
pub unsafe fn map_page(
&mut self,
memory: &mut PhysicalMemoryManager,
virtual_address: u64,
physical_address: u64,
flags: PageFlags,
) -> Option<()> {
if !is_aligned(virtual_address) || !is_aligned(physical_address) {
return None;
}
let table = unsafe { self.walk_create(memory, virtual_address)? };
let index = pt_index(virtual_address);
unsafe {
if (*table).entries[index] & PRESENT != 0 {
return None;
}
(*table).entries[index] = physical_address | flags.bits() | PRESENT;
flush_tlb_one(virtual_address);
}
Some(())
}
pub unsafe fn unmap_page(&mut self, virtual_address: u64) -> Option<u64> {
let table = unsafe { self.walk(virtual_address)? };
let index = pt_index(virtual_address);
unsafe {
let entry = (*table).entries[index];
if entry & PRESENT == 0 {
return None;
}
(*table).entries[index] = 0;
flush_tlb_one(virtual_address);
Some(entry & 0x000f_ffff_ffff_f000)
}
}
pub unsafe fn translate(&self, virtual_address: u64) -> Option<u64> {
let pml4 = unsafe { &*self.pml4 };
let pml4e = pml4.entries[pml4_index(virtual_address)];
let pdpt = next_table(pml4e)?;
let pdpte = unsafe { (*pdpt).entries[pdpt_index(virtual_address)] };
if pdpte & HUGE_PAGE != 0 {
return Some((pdpte & 0x000f_ffff_c000_0000) | (virtual_address & 0x3fff_ffff));
}
let pd = next_table(pdpte)?;
let pde = unsafe { (*pd).entries[pd_index(virtual_address)] };
if pde & HUGE_PAGE != 0 {
return Some((pde & 0x000f_ffff_ffe0_0000) | (virtual_address & 0x1f_ffff));
}
let pt = next_table(pde)?;
let pte = unsafe { (*pt).entries[pt_index(virtual_address)] };
if pte & PRESENT == 0 {
None
} else {
Some((pte & 0x000f_ffff_ffff_f000) | (virtual_address & 0xfff))
}
}
pub unsafe fn activate(&self) {
unsafe {
let cr4: u64;
asm!("mov {}, cr4", out(reg) cr4, options(nostack, preserves_flags));
asm!(
"mov cr4, {}",
in(reg) cr4 | CR4_PSE,
options(nostack, preserves_flags)
);
asm!(
"mov cr3, {}",
in(reg) self.pml4 as u64,
options(nostack, preserves_flags)
);
}
}
pub fn root_table(&self) -> u64 {
self.pml4 as u64
}
pub fn identity_tables(&self) -> (*mut PageTable, [*mut PageTable; PDPTE_COUNT]) {
(self.identity_pdpt, self.identity_pds)
}
unsafe fn walk_create(
&mut self,
memory: &mut PhysicalMemoryManager,
virtual_address: u64,
) -> Option<*mut PageTable> {
let mut table = self.pml4;
for index in [
pml4_index(virtual_address),
pdpt_index(virtual_address),
pd_index(virtual_address),
] {
unsafe {
let entry = &mut (*table).entries[index];
if *entry & HUGE_PAGE != 0 {
return None;
}
if *entry & PRESENT == 0 {
let new_table = memory.alloc_page()? as *mut PageTable;
(*new_table).zero();
*entry = new_table as u64 | PRESENT | WRITABLE | PageFlags::USER.bits();
}
table = (*entry & 0x000f_ffff_ffff_f000) as *mut PageTable;
}
}
Some(table)
}
unsafe fn walk(&self, virtual_address: u64) -> Option<*mut PageTable> {
let mut table = self.pml4;
for index in [
pml4_index(virtual_address),
pdpt_index(virtual_address),
pd_index(virtual_address),
] {
unsafe {
let entry = (*table).entries[index];
if entry & PRESENT == 0 || entry & HUGE_PAGE != 0 {
return None;
}
table = (entry & 0x000f_ffff_ffff_f000) as *mut PageTable;
}
}
Some(table)
}
}
fn next_table(entry: u64) -> Option<*mut PageTable> {
if entry & PRESENT == 0 {
None
} else {
Some((entry & 0x000f_ffff_ffff_f000) as *mut PageTable)
}
}
fn pml4_index(address: u64) -> usize {
((address >> 39) & 0x1ff) as usize
}
fn pdpt_index(address: u64) -> usize {
((address >> 30) & 0x1ff) as usize
}
fn pd_index(address: u64) -> usize {
((address >> 21) & 0x1ff) as usize
}
fn pt_index(address: u64) -> usize {
((address >> 12) & 0x1ff) as usize
}
fn is_aligned(address: u64) -> bool {
address as usize & (PAGE_SIZE - 1) == 0
}
unsafe fn flush_tlb_one(virtual_address: u64) {
unsafe {
asm!("invlpg [{}]", in(reg) virtual_address, options(nostack, preserves_flags));
}
}
+17
View File
@@ -0,0 +1,17 @@
#![allow(dead_code)]
pub mod tty;
pub mod vfs;
pub const SERVICE_VFS: usize = 1;
pub const SERVICE_TTY: usize = 2;
#[derive(Clone, Copy)]
pub enum ServiceRequest {
Read,
Write,
Open,
Close,
Stat,
Ioctl,
}
+9
View File
@@ -0,0 +1,9 @@
use crate::ipc::Message;
pub const OP_READ: u16 = 1;
pub const OP_WRITE: u16 = 2;
pub const OP_IOCTL: u16 = 3;
pub fn dispatch(_message: Message) -> isize {
-38
}
+10
View File
@@ -0,0 +1,10 @@
use crate::ipc::Message;
pub const OP_OPEN: u16 = 1;
pub const OP_READ: u16 = 2;
pub const OP_WRITE: u16 = 3;
pub const OP_CLOSE: u16 = 4;
pub fn dispatch(_message: Message) -> isize {
-38
}
+1546
View File
File diff suppressed because it is too large Load Diff
+178
View File
@@ -0,0 +1,178 @@
use crate::{syscall::TrapFrame, user};
const MAX_TASKS: usize = 64;
#[derive(Clone, Copy, PartialEq, Eq)]
enum TaskState {
Empty,
Running,
Blocked,
Zombie,
}
#[derive(Clone, Copy)]
struct Task {
pid: usize,
ppid: usize,
state: TaskState,
exit_code: isize,
vfork_parent: bool,
}
impl Task {
const fn empty() -> Self {
Self {
pid: 0,
ppid: 0,
state: TaskState::Empty,
exit_code: 0,
vfork_parent: false,
}
}
}
static mut TASKS: [Task; MAX_TASKS] = [Task::empty(); MAX_TASKS];
static mut NEXT_PID: usize = 2;
static mut CURRENT_PID: usize = 1;
static mut SAVED_VFORK_PARENT: TrapFrame = TrapFrame::zero();
static mut SAVED_VFORK_PARENT_PID: usize = 0;
static mut SAVED_VFORK_CHILD_PID: usize = 0;
pub unsafe fn init() {
unsafe {
TASKS[0] = Task {
pid: 1,
ppid: 0,
state: TaskState::Running,
exit_code: 0,
vfork_parent: false,
};
NEXT_PID = 2;
CURRENT_PID = 1;
SAVED_VFORK_PARENT = TrapFrame::zero();
SAVED_VFORK_PARENT_PID = 0;
SAVED_VFORK_CHILD_PID = 0;
}
}
pub fn current_pid() -> usize {
unsafe { CURRENT_PID }
}
pub fn current_ppid() -> usize {
unsafe {
find_task(CURRENT_PID)
.map(|index| TASKS[index].ppid)
.unwrap_or(0)
}
}
pub fn is_current_vfork_child() -> bool {
unsafe { SAVED_VFORK_CHILD_PID != 0 && CURRENT_PID == SAVED_VFORK_CHILD_PID }
}
pub fn fork_like(frame: &TrapFrame) -> isize {
vfork(frame)
}
pub fn vfork(frame: &TrapFrame) -> isize {
unsafe {
let Some(slot) = find_empty_slot() else {
return -11;
};
let parent_pid = CURRENT_PID;
if SAVED_VFORK_PARENT_PID != 0 {
return -11;
}
let pid = NEXT_PID;
NEXT_PID += 1;
if let Some(parent_index) = find_task(parent_pid) {
TASKS[parent_index].state = TaskState::Blocked;
TASKS[parent_index].vfork_parent = true;
}
user::snapshot_current_image();
SAVED_VFORK_PARENT = *frame;
SAVED_VFORK_PARENT.rax = pid;
SAVED_VFORK_PARENT_PID = parent_pid;
SAVED_VFORK_CHILD_PID = pid;
TASKS[slot] = Task {
pid,
ppid: parent_pid,
state: TaskState::Running,
exit_code: 0,
vfork_parent: false,
};
CURRENT_PID = pid;
0
}
}
pub fn wait4(pid: isize, status: *mut i32) -> isize {
unsafe {
for index in 0..MAX_TASKS {
let task = TASKS[index];
if task.state != TaskState::Zombie || task.ppid != CURRENT_PID {
continue;
}
if pid > 0 && task.pid != pid as usize {
continue;
}
if !status.is_null() {
status.write((task.exit_code as i32) << 8);
}
TASKS[index] = Task::empty();
return task.pid as isize;
}
}
-10
}
pub fn exit_current(code: isize, frame: &mut TrapFrame) -> bool {
unsafe {
let exiting_pid = CURRENT_PID;
if let Some(index) = find_task(CURRENT_PID) {
TASKS[index].state = TaskState::Zombie;
TASKS[index].exit_code = code;
}
if SAVED_VFORK_CHILD_PID == exiting_pid && SAVED_VFORK_PARENT_PID != 0 {
let parent_pid = SAVED_VFORK_PARENT_PID;
user::restore_current_image();
*frame = SAVED_VFORK_PARENT;
if let Some(parent_index) = find_task(parent_pid) {
TASKS[parent_index].state = TaskState::Running;
TASKS[parent_index].vfork_parent = false;
}
CURRENT_PID = parent_pid;
SAVED_VFORK_PARENT = TrapFrame::zero();
SAVED_VFORK_PARENT_PID = 0;
SAVED_VFORK_CHILD_PID = 0;
return true;
}
}
false
}
unsafe fn find_empty_slot() -> Option<usize> {
unsafe {
for index in 0..MAX_TASKS {
if TASKS[index].state == TaskState::Empty {
return Some(index);
}
}
}
None
}
unsafe fn find_task(pid: usize) -> Option<usize> {
unsafe {
for index in 0..MAX_TASKS {
if TASKS[index].state != TaskState::Empty && TASKS[index].pid == pid {
return Some(index);
}
}
}
None
}
+532
View File
@@ -0,0 +1,532 @@
use crate::font::{GLYPH_HEIGHT, GLYPH_WIDTH, HANKAKU};
use crate::framebuffer::{Framebuffer, Rgb};
const FG: Rgb = Rgb {
red: 0xaa,
green: 0xaa,
blue: 0xaa,
};
const BG: Rgb = Rgb {
red: 0x00,
green: 0x00,
blue: 0x00,
};
const ANSI_COLORS: [Rgb; 8] = [
Rgb {
red: 0x00,
green: 0x00,
blue: 0x00,
},
Rgb {
red: 0xaa,
green: 0x00,
blue: 0x00,
},
Rgb {
red: 0x00,
green: 0xaa,
blue: 0x00,
},
Rgb {
red: 0xaa,
green: 0x55,
blue: 0x00,
},
Rgb {
red: 0x00,
green: 0x00,
blue: 0xaa,
},
Rgb {
red: 0xaa,
green: 0x00,
blue: 0xaa,
},
Rgb {
red: 0x00,
green: 0xaa,
blue: 0xaa,
},
Rgb {
red: 0xaa,
green: 0xaa,
blue: 0xaa,
},
];
const ANSI_BRIGHT_COLORS: [Rgb; 8] = [
Rgb {
red: 0x55,
green: 0x55,
blue: 0x55,
},
Rgb {
red: 0xff,
green: 0x55,
blue: 0x55,
},
Rgb {
red: 0x55,
green: 0xff,
blue: 0x55,
},
Rgb {
red: 0xff,
green: 0xff,
blue: 0x55,
},
Rgb {
red: 0x55,
green: 0x55,
blue: 0xff,
},
Rgb {
red: 0xff,
green: 0x55,
blue: 0xff,
},
Rgb {
red: 0x55,
green: 0xff,
blue: 0xff,
},
Rgb {
red: 0xff,
green: 0xff,
blue: 0xff,
},
];
#[derive(Clone, Copy)]
enum EscapeState {
Ground,
Escape,
Csi {
params: [u16; 8],
count: usize,
current: u16,
},
}
pub struct Tty {
framebuffer: Framebuffer,
cursor_x: usize,
cursor_y: usize,
saved_cursor_x: usize,
saved_cursor_y: usize,
columns: usize,
rows: usize,
scroll_top: usize,
scroll_bottom: usize,
fg: Rgb,
bg: Rgb,
bold: bool,
escape_state: EscapeState,
}
impl Tty {
pub fn new(framebuffer: Framebuffer) -> Self {
let columns = framebuffer.width() / GLYPH_WIDTH;
let rows = framebuffer.height() / GLYPH_HEIGHT;
Self {
framebuffer,
cursor_x: 0,
cursor_y: 0,
saved_cursor_x: 0,
saved_cursor_y: 0,
columns,
rows,
scroll_top: 0,
scroll_bottom: rows.saturating_sub(1),
fg: FG,
bg: BG,
bold: false,
escape_state: EscapeState::Ground,
}
}
pub fn clear(&mut self) {
self.framebuffer.clear(self.bg);
self.cursor_x = 0;
self.cursor_y = 0;
self.reset_scroll_region();
}
pub fn columns(&self) -> usize {
self.columns
}
pub fn rows(&self) -> usize {
self.rows
}
pub fn reset(&mut self) {
self.reset_colors();
self.reset_scroll_region();
self.cursor_x = 0;
self.cursor_y = 0;
self.saved_cursor_x = 0;
self.saved_cursor_y = 0;
self.escape_state = EscapeState::Ground;
}
pub fn write_bytes(&mut self, bytes: &[u8]) {
for byte in bytes {
self.put_char(*byte);
}
}
pub fn put_char(&mut self, ch: u8) {
if self.handle_escape(ch) {
return;
}
match ch {
b'\r' => self.cursor_x = 0,
b'\n' => self.newline(),
b'\t' => self.tab(),
0x08 => self.backspace(),
0x7f => self.backspace(),
ch => {
if self.cursor_x >= self.columns {
self.newline();
}
self.draw_char(ch, self.cursor_x, self.cursor_y, self.fg, self.bg);
self.cursor_x += 1;
}
}
}
pub fn backspace(&mut self) {
if self.cursor_x == 0 {
return;
}
self.cursor_x -= 1;
self.draw_char(b' ', self.cursor_x, self.cursor_y, self.fg, self.bg);
}
fn newline(&mut self) {
self.cursor_x = 0;
if self.cursor_y == self.scroll_bottom {
self.scroll_one_line();
} else if self.cursor_y + 1 >= self.rows {
self.cursor_y = self.rows.saturating_sub(1);
} else {
self.cursor_y += 1;
}
}
fn scroll_one_line(&mut self) {
let top = self.scroll_top * GLYPH_HEIGHT;
let bottom = (self.scroll_bottom + 1) * GLYPH_HEIGHT;
self.framebuffer
.scroll_region_up(top, bottom, GLYPH_HEIGHT, self.bg);
}
fn handle_escape(&mut self, ch: u8) -> bool {
match self.escape_state {
EscapeState::Ground => {
if ch == 0x1b {
self.escape_state = EscapeState::Escape;
true
} else {
false
}
}
EscapeState::Escape => {
self.escape_state = if ch == b'[' {
EscapeState::Csi {
params: [0; 8],
count: 0,
current: 0,
}
} else {
match ch {
b'7' => self.save_cursor(),
b'8' => self.restore_cursor(),
b'c' => self.reset_terminal(),
_ => {}
}
EscapeState::Ground
};
true
}
EscapeState::Csi {
mut params,
mut count,
mut current,
} => {
match ch {
b'0'..=b'9' => {
current = current
.saturating_mul(10)
.saturating_add((ch - b'0') as u16);
self.escape_state = EscapeState::Csi {
params,
count,
current,
};
}
b';' => {
if count < params.len() {
params[count] = current;
count += 1;
}
self.escape_state = EscapeState::Csi {
params,
count,
current: 0,
};
}
b'?' => {
self.escape_state = EscapeState::Csi {
params,
count,
current,
};
}
b'm' => {
if count < params.len() {
params[count] = current;
count += 1;
}
self.apply_sgr(&params[..count]);
self.escape_state = EscapeState::Ground;
}
b'@' | b'A' | b'B' | b'C' | b'D' | b'G' | b'H' | b'F' | b'L' | b'M' | b'P'
| b'X' | b'd' | b'f' | b'J' | b'K' | b'r' | b's' | b'u' | b'h' | b'l' => {
if count < params.len() {
params[count] = current;
count += 1;
}
self.apply_csi(ch, &params[..count]);
self.escape_state = EscapeState::Ground;
}
_ => self.escape_state = EscapeState::Ground,
}
true
}
}
}
fn tab(&mut self) {
let next_tab = (self.cursor_x + 8) & !7;
self.cursor_x = next_tab.min(self.columns.saturating_sub(1));
}
fn apply_csi(&mut self, command: u8, params: &[u16]) {
match command {
b'A' => self.cursor_y = self.cursor_y.saturating_sub(csi_param(params, 0, 1)),
b'B' => self.cursor_y = (self.cursor_y + csi_param(params, 0, 1)).min(self.rows - 1),
b'C' => self.cursor_x = (self.cursor_x + csi_param(params, 0, 1)).min(self.columns - 1),
b'D' => self.cursor_x = self.cursor_x.saturating_sub(csi_param(params, 0, 1)),
b'G' => {
let col = csi_param(params, 0, 1).saturating_sub(1);
self.cursor_x = col.min(self.columns - 1);
}
b'H' | b'f' => {
let row = csi_param(params, 0, 1).saturating_sub(1);
let col = csi_param(params, 1, 1).saturating_sub(1);
self.cursor_y = row.min(self.rows - 1);
self.cursor_x = col.min(self.columns - 1);
}
b'F' => {
self.cursor_y = self.rows - 1;
self.cursor_x = 0;
}
b'd' => {
let row = csi_param(params, 0, 1).saturating_sub(1);
self.cursor_y = row.min(self.rows - 1);
}
b'J' => self.erase_display(csi_param(params, 0, 0)),
b'K' => self.erase_line(csi_param(params, 0, 0)),
b'X' => self.erase_chars(csi_param(params, 0, 1)),
b's' => self.save_cursor(),
b'u' => self.restore_cursor(),
b'r' => self.set_scroll_region(params),
b'@' | b'L' | b'M' | b'P' => {}
b'h' | b'l' => self.set_private_mode(params, command == b'h'),
_ => {}
}
}
fn set_private_mode(&mut self, params: &[u16], _enabled: bool) {
for param in params {
match *param {
47 | 1047 | 1049 => {
self.reset_scroll_region();
self.cursor_x = 0;
self.cursor_y = 0;
}
25 => {}
_ => {}
}
}
}
fn set_scroll_region(&mut self, params: &[u16]) {
let top = csi_param(params, 0, 1).saturating_sub(1);
let bottom = if params.len() >= 2 && params[1] != 0 {
params[1] as usize - 1
} else {
self.rows.saturating_sub(1)
};
if top < bottom && bottom < self.rows {
self.scroll_top = top;
self.scroll_bottom = bottom;
self.cursor_x = 0;
self.cursor_y = top;
} else {
self.reset_scroll_region();
}
}
fn reset_scroll_region(&mut self) {
self.scroll_top = 0;
self.scroll_bottom = self.rows.saturating_sub(1);
}
fn erase_display(&mut self, mode: usize) {
match mode {
0 => {
self.erase_line(0);
let y = (self.cursor_y + 1) * GLYPH_HEIGHT;
self.framebuffer.fill_rect(
0,
y,
self.framebuffer.width(),
self.framebuffer.height(),
self.bg,
);
}
1 => {
self.erase_line(1);
self.framebuffer.fill_rect(
0,
0,
self.framebuffer.width(),
self.cursor_y * GLYPH_HEIGHT,
self.bg,
);
}
2 | 3 => self.clear(),
_ => {}
}
}
fn erase_line(&mut self, mode: usize) {
let y = self.cursor_y * GLYPH_HEIGHT;
match mode {
0 => self.framebuffer.fill_rect(
self.cursor_x * GLYPH_WIDTH,
y,
(self.columns - self.cursor_x) * GLYPH_WIDTH,
GLYPH_HEIGHT,
self.bg,
),
1 => self.framebuffer.fill_rect(
0,
y,
(self.cursor_x + 1) * GLYPH_WIDTH,
GLYPH_HEIGHT,
self.bg,
),
2 => {
self.framebuffer
.fill_rect(0, y, self.columns * GLYPH_WIDTH, GLYPH_HEIGHT, self.bg)
}
_ => {}
}
}
fn erase_chars(&mut self, count: usize) {
let cells = count.min(self.columns.saturating_sub(self.cursor_x));
self.framebuffer.fill_rect(
self.cursor_x * GLYPH_WIDTH,
self.cursor_y * GLYPH_HEIGHT,
cells * GLYPH_WIDTH,
GLYPH_HEIGHT,
self.bg,
);
}
fn save_cursor(&mut self) {
self.saved_cursor_x = self.cursor_x;
self.saved_cursor_y = self.cursor_y;
}
fn restore_cursor(&mut self) {
self.cursor_x = self.saved_cursor_x.min(self.columns - 1);
self.cursor_y = self.saved_cursor_y.min(self.rows - 1);
}
fn reset_terminal(&mut self) {
self.reset_colors();
self.reset_scroll_region();
self.clear();
}
fn apply_sgr(&mut self, params: &[u16]) {
if params.is_empty() {
self.reset_colors();
return;
}
for param in params {
match *param {
0 => self.reset_colors(),
1 => self.bold = true,
22 => {
self.bold = false;
self.fg = FG;
}
30..=37 => {
let index = (*param - 30) as usize;
self.fg = if self.bold {
ANSI_BRIGHT_COLORS[index]
} else {
ANSI_COLORS[index]
};
}
39 => self.fg = FG,
40..=47 => self.bg = ANSI_COLORS[(*param - 40) as usize],
49 => self.bg = BG,
90..=97 => self.fg = ANSI_BRIGHT_COLORS[(*param - 90) as usize],
100..=107 => self.bg = ANSI_BRIGHT_COLORS[(*param - 100) as usize],
_ => {}
}
}
}
fn reset_colors(&mut self) {
self.fg = FG;
self.bg = BG;
self.bold = false;
}
fn draw_char(&mut self, ch: u8, cell_x: usize, cell_y: usize, fg: Rgb, bg: Rgb) {
let glyph_base = ch as usize * GLYPH_HEIGHT;
let pixel_x = cell_x * GLYPH_WIDTH;
let pixel_y = cell_y * GLYPH_HEIGHT;
for row in 0..GLYPH_HEIGHT {
let bitmap = HANKAKU[glyph_base + row];
for col in 0..GLYPH_WIDTH {
let mask = 0x80 >> col;
let color = if bitmap & mask != 0 { fg } else { bg };
self.framebuffer
.put_pixel(pixel_x + col, pixel_y + row, color);
}
}
}
}
fn csi_param(params: &[u16], index: usize, default: usize) -> usize {
let value = params.get(index).copied().unwrap_or(0) as usize;
if value == 0 { default } else { value }
}
+221
View File
@@ -0,0 +1,221 @@
pub mod program;
pub mod stdio;
use core::arch::asm;
use crate::gdt::{USER_CODE_SELECTOR, USER_DATA_SELECTOR};
const USER_STACK_SIZE: usize = 16 * 4096;
const USER_ARG_AREA_SIZE: usize = 4096;
const AT_NULL: u64 = 0;
const AT_PHDR: u64 = 3;
const AT_PHENT: u64 = 4;
const AT_PHNUM: u64 = 5;
const AT_PAGESZ: u64 = 6;
const AT_BASE: u64 = 7;
const AT_FLAGS: u64 = 8;
const AT_ENTRY: u64 = 9;
const AT_UID: u64 = 11;
const AT_EUID: u64 = 12;
const AT_GID: u64 = 13;
const AT_EGID: u64 = 14;
const AT_PLATFORM: u64 = 15;
const AT_HWCAP: u64 = 16;
const AT_CLKTCK: u64 = 17;
const AT_SECURE: u64 = 23;
const AT_RANDOM: u64 = 25;
const AT_HWCAP2: u64 = 26;
const AT_EXECFN: u64 = 31;
#[repr(C, align(16))]
struct UserStack([u8; USER_STACK_SIZE]);
static mut USER_STACK: UserStack = UserStack([0; USER_STACK_SIZE]);
static mut USER_ARG_AREA: [u8; USER_ARG_AREA_SIZE] = [0; USER_ARG_AREA_SIZE];
static mut SAVED_USER_STACK: UserStack = UserStack([0; USER_STACK_SIZE]);
static mut SAVED_USER_ARG_AREA: [u8; USER_ARG_AREA_SIZE] = [0; USER_ARG_AREA_SIZE];
#[derive(Clone, Copy)]
pub struct UserAux {
pub entry: u64,
pub phdr: u64,
pub phent: u64,
pub phnum: u64,
}
pub unsafe fn enter() -> ! {
let entry = program::zeroos_user_main as *const () as usize as u64;
let stack_top = (&raw const USER_STACK).cast::<u8>() as u64 + USER_STACK_SIZE as u64;
unsafe {
enter_raw(entry, stack_top);
}
}
pub unsafe fn enter_elf_with_args(entry: u64, args: &[&[u8]], aux: UserAux) -> ! {
let stack_top = unsafe { build_initial_stack(args, aux) };
unsafe {
enter_raw(entry, stack_top);
}
}
pub unsafe fn prepare_elf_stack(args: &[&[u8]], aux: UserAux) -> u64 {
unsafe { build_initial_stack(args, aux) }
}
pub unsafe fn snapshot_current_image() {
unsafe {
core::ptr::copy_nonoverlapping(
(&raw const USER_STACK).cast::<u8>(),
(&raw mut SAVED_USER_STACK).cast::<u8>(),
USER_STACK_SIZE,
);
core::ptr::copy_nonoverlapping(
(&raw const USER_ARG_AREA).cast::<u8>(),
(&raw mut SAVED_USER_ARG_AREA).cast::<u8>(),
USER_ARG_AREA_SIZE,
);
}
}
pub unsafe fn restore_current_image() {
unsafe {
core::ptr::copy_nonoverlapping(
(&raw const SAVED_USER_STACK).cast::<u8>(),
(&raw mut USER_STACK).cast::<u8>(),
USER_STACK_SIZE,
);
core::ptr::copy_nonoverlapping(
(&raw const SAVED_USER_ARG_AREA).cast::<u8>(),
(&raw mut USER_ARG_AREA).cast::<u8>(),
USER_ARG_AREA_SIZE,
);
}
}
unsafe fn enter_raw(entry: u64, stack_top: u64) -> ! {
unsafe {
asm!(
"push {user_ss}",
"push {user_rsp}",
"pushfq",
"or qword ptr [rsp], 0x200",
"push {user_cs}",
"push {entry}",
"xor rax, rax",
"xor rbx, rbx",
"xor rcx, rcx",
"xor rdx, rdx",
"xor rbp, rbp",
"xor rsi, rsi",
"xor rdi, rdi",
"xor r8, r8",
"xor r9, r9",
"xor r10, r10",
"xor r11, r11",
"xor r12, r12",
"xor r13, r13",
"xor r14, r14",
"xor r15, r15",
"iretq",
user_ss = in(reg) USER_DATA_SELECTOR as u64,
user_rsp = in(reg) stack_top,
user_cs = in(reg) USER_CODE_SELECTOR as u64,
entry = in(reg) entry,
options(noreturn),
);
}
}
unsafe fn build_initial_stack(args: &[&[u8]], aux: UserAux) -> u64 {
let stack_base = (&raw mut USER_STACK).cast::<u8>() as usize;
let mut sp = stack_base + USER_STACK_SIZE;
let arg_base = (&raw mut USER_ARG_AREA).cast::<u8>();
let argc = args.len().min(16);
let mut arg_ptrs = [0u64; 16];
let mut arg_offset = 0usize;
for index in 0..argc {
let arg = args[index];
let remaining = USER_ARG_AREA_SIZE.saturating_sub(arg_offset);
if remaining == 0 {
break;
}
let copy_len = arg.len().min(remaining - 1);
unsafe {
core::ptr::copy_nonoverlapping(arg.as_ptr(), arg_base.add(arg_offset), copy_len);
*arg_base.add(arg_offset + copy_len) = 0;
}
arg_ptrs[index] = unsafe { arg_base.add(arg_offset) as u64 };
arg_offset += copy_len + 1;
}
let random_ptr = unsafe { arg_base.add(arg_offset) as u64 };
let random = [
0x37, 0x91, 0x42, 0xa5, 0xc3, 0x5e, 0x10, 0x77, 0x21, 0x6d, 0x9b, 0xe0, 0x4c, 0x18, 0xf2,
0xaa,
];
unsafe {
core::ptr::copy_nonoverlapping(random.as_ptr(), arg_base.add(arg_offset), random.len());
}
arg_offset += random.len();
let platform = b"x86_64";
let platform_ptr = unsafe { arg_base.add(arg_offset) as u64 };
unsafe {
core::ptr::copy_nonoverlapping(platform.as_ptr(), arg_base.add(arg_offset), platform.len());
*arg_base.add(arg_offset + platform.len()) = 0;
}
let execfn_ptr = arg_ptrs[0];
let aux_pairs = [
(AT_PHDR, aux.phdr),
(AT_PHENT, aux.phent),
(AT_PHNUM, aux.phnum),
(AT_PAGESZ, 4096),
(AT_BASE, 0),
(AT_FLAGS, 0),
(AT_ENTRY, aux.entry),
(AT_UID, 0),
(AT_EUID, 0),
(AT_GID, 0),
(AT_EGID, 0),
(AT_PLATFORM, platform_ptr),
(AT_HWCAP, 0),
(AT_CLKTCK, 100),
(AT_SECURE, 0),
(AT_RANDOM, random_ptr),
(AT_HWCAP2, 0),
(AT_EXECFN, execfn_ptr),
];
let push_count = 1 + argc + 1 + 1 + aux_pairs.len() * 2 + 2;
if push_count % 2 != 0 {
push_u64(&mut sp, 0);
}
// Linux/x86-64 process entry stack:
// argc, argv[], NULL, envp NULL, auxv AT_NULL.
push_u64(&mut sp, 0); // AT_NULL value
push_u64(&mut sp, AT_NULL); // AT_NULL type
for &(kind, value) in aux_pairs.iter().rev() {
push_u64(&mut sp, value);
push_u64(&mut sp, kind);
}
push_u64(&mut sp, 0); // envp terminator
push_u64(&mut sp, 0); // argv terminator
for index in (0..argc).rev() {
push_u64(&mut sp, arg_ptrs[index]);
}
push_u64(&mut sp, argc as u64);
sp as u64
}
fn push_u64(sp: &mut usize, value: u64) {
*sp -= core::mem::size_of::<u64>();
unsafe {
(*sp as *mut u64).write(value);
}
}
+13
View File
@@ -0,0 +1,13 @@
use super::stdio;
#[unsafe(no_mangle)]
pub extern "C" fn zeroos_user_main() -> ! {
stdio::puts(b"\x1b[92mHello, World!\x1b[0m");
stdio::puts(b"\x1b[96mPOSIX stdio via int 0x80\x1b[0m");
loop {
unsafe {
core::arch::asm!("pause", options(nomem, nostack, preserves_flags));
}
}
}
+119
View File
@@ -0,0 +1,119 @@
use core::arch::asm;
use crate::syscall::{
STDIN_FILENO, STDOUT_FILENO, SYS_CLOSE, SYS_EXIT, SYS_FSTAT, SYS_GETPID, SYS_ISATTY, SYS_LSEEK,
SYS_OPEN, SYS_READ, SYS_WRITE,
};
pub fn write(fd: usize, buffer: &[u8]) -> isize {
int80_syscall3(SYS_WRITE, fd, buffer.as_ptr() as usize, buffer.len())
}
#[allow(dead_code)]
pub fn read(fd: usize, buffer: &mut [u8]) -> isize {
int80_syscall3(SYS_READ, fd, buffer.as_mut_ptr() as usize, buffer.len())
}
pub fn putchar(ch: u8) -> isize {
write(STDOUT_FILENO, &[ch])
}
pub fn puts(text: &[u8]) -> isize {
let written = write(STDOUT_FILENO, text);
let _ = putchar(b'\n');
written
}
#[allow(dead_code)]
pub fn close(fd: usize) -> isize {
int80_syscall3(SYS_CLOSE, fd, 0, 0)
}
#[allow(dead_code)]
pub fn open(path: *const u8, flags: usize, mode: usize) -> isize {
int80_syscall3(SYS_OPEN, path as usize, flags, mode)
}
#[allow(dead_code)]
pub fn fstat(fd: usize, stat: *mut u8) -> isize {
int80_syscall3(SYS_FSTAT, fd, stat as usize, 0)
}
#[allow(dead_code)]
pub fn lseek(fd: usize, offset: usize, whence: usize) -> isize {
int80_syscall3(SYS_LSEEK, fd, offset, whence)
}
#[allow(dead_code)]
pub fn isatty(fd: usize) -> isize {
int80_syscall3(SYS_ISATTY, fd, 0, 0)
}
#[allow(dead_code)]
pub fn getpid() -> isize {
int80_syscall3(SYS_GETPID, 0, 0, 0)
}
#[allow(dead_code)]
pub fn exit(code: isize) -> ! {
let _ = int80_syscall3(SYS_EXIT, code as usize, 0, 0);
loop {
unsafe {
core::arch::asm!("pause", options(nomem, nostack, preserves_flags));
}
}
}
#[allow(dead_code)]
pub fn getchar() -> Option<u8> {
let mut byte = [0u8; 1];
if read(STDIN_FILENO, &mut byte) == 1 {
Some(byte[0])
} else {
None
}
}
#[allow(dead_code)]
#[allow(dead_code)]
fn syscall3(number: usize, arg0: usize, arg1: usize, arg2: usize) -> isize {
let ret: isize;
unsafe {
asm!(
"syscall",
inlateout("rax") number as isize => ret,
in("rdi") arg0,
in("rsi") arg1,
in("rdx") arg2,
lateout("rcx") _,
lateout("r11") _,
options(nostack),
);
}
ret
}
#[allow(dead_code)]
pub fn int80_write(fd: usize, buffer: &[u8]) -> isize {
int80_syscall3(SYS_WRITE, fd, buffer.as_ptr() as usize, buffer.len())
}
#[allow(dead_code)]
pub fn int80_read(fd: usize, buffer: &mut [u8]) -> isize {
int80_syscall3(SYS_READ, fd, buffer.as_mut_ptr() as usize, buffer.len())
}
fn int80_syscall3(number: usize, arg0: usize, arg1: usize, arg2: usize) -> isize {
let ret: isize;
unsafe {
asm!(
"int 0x80",
inlateout("rax") number as isize => ret,
in("rdi") arg0,
in("rsi") arg1,
in("rdx") arg2,
options(nostack, preserves_flags),
);
}
ret
}