mirror of
https://github.com/ZeroOSProject/ZeroOS.git
synced 2026-09-12 23:24:41 +08:00
vi and busybox
This commit is contained in:
@@ -0,0 +1,2 @@
|
||||
[build]
|
||||
target = "x86_64-unknown-uefi"
|
||||
@@ -0,0 +1 @@
|
||||
/target
|
||||
Vendored
+20
@@ -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
@@ -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
@@ -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"
|
||||
@@ -0,0 +1,6 @@
|
||||
[package]
|
||||
name = "zeroos-loader"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
@@ -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.
Executable
+147
@@ -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}"
|
||||
@@ -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()
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod pci;
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
pub mod ps2;
|
||||
@@ -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 }
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
pub mod bus;
|
||||
pub mod input;
|
||||
pub mod platform;
|
||||
pub mod storage;
|
||||
@@ -0,0 +1,2 @@
|
||||
pub mod pic;
|
||||
pub mod timer;
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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(())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod ahci;
|
||||
pub mod block;
|
||||
pub mod partition;
|
||||
@@ -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 §or[0..8] != GPT_SIGNATURE {
|
||||
return Err(BlockError::Unsupported);
|
||||
}
|
||||
|
||||
let entries_lba = le_u64(§or, 0x48);
|
||||
let entry_count = le_u32(§or, 0x50) as usize;
|
||||
let entry_size = le_u32(§or, 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 = §or[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
@@ -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
@@ -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)
|
||||
}
|
||||
@@ -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");
|
||||
@@ -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
@@ -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],
|
||||
])
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
pub mod ext4;
|
||||
pub mod uefi;
|
||||
pub mod vfs;
|
||||
+179
@@ -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
File diff suppressed because it is too large
Load Diff
+127
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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
@@ -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));
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
+178
@@ -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
@@ -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(¶ms[..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, ¶ms[..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
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user