zlib driver & experimental riscv support
this commit adds the last piece to get the initramfs completely finished
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,5 +1,6 @@
|
||||
/target
|
||||
/bin
|
||||
/ovmf
|
||||
|
||||
# Bochs
|
||||
bx_enh_dbg.ini
|
||||
|
||||
@@ -7,4 +7,4 @@ edition = "2021"
|
||||
limine = "0.1.10"
|
||||
|
||||
[profile.release]
|
||||
opt-level = 3
|
||||
opt-level = 3
|
||||
|
||||
48
Makefile
48
Makefile
@@ -5,13 +5,14 @@ MODE ?= release
|
||||
ARCH ?= x86_64
|
||||
MEMORY ?= 512M
|
||||
QEMU_OPTS ?=
|
||||
MKSQUASHFS_OPTS ?= -no-compression
|
||||
MKSQUASHFS_OPTS ?=
|
||||
|
||||
ISO_PATH = ${ARTIFACTS_PATH}/iso_root
|
||||
INITRAMFS_PATH = ${ARTIFACTS_PATH}/initramfs
|
||||
IMAGE_PATH = ${ARTIFACTS_PATH}/${IMAGE_NAME}
|
||||
CARGO_OPTS = --target=src/arch/${ARCH}/${ARCH}-unknown-none.json
|
||||
QEMU_OPTS += -m ${MEMORY} -drive format=raw,file=${IMAGE_PATH}
|
||||
QEMU_OPTS += -m ${MEMORY} -drive id=hd0,format=raw,file=${IMAGE_PATH}
|
||||
LIMINE_BOOT_VARIATION = X64
|
||||
|
||||
ifeq (${MODE},release)
|
||||
CARGO_OPTS += --release
|
||||
@@ -19,9 +20,18 @@ else
|
||||
QEMU_OPTS += -s -S
|
||||
endif
|
||||
|
||||
ifeq (${ARCH},riscv64)
|
||||
LIMINE_BOOT_VARIATION := RISCV64
|
||||
UEFI := true
|
||||
endif
|
||||
|
||||
ifneq (${UEFI},)
|
||||
RUN_OPTS := ovmf
|
||||
QEMU_OPTS += -bios bin/ovmf/OVMF.fd
|
||||
RUN_OPTS := ovmf-${ARCH}
|
||||
ifeq (${ARCH},riscv64)
|
||||
QEMU_OPTS += -drive if=pflash,unit=0,format=raw,file=bin/ovmf-riscv64/OVMF.fd -M virt
|
||||
else
|
||||
QEMU_OPTS += -bios ovmf/ovmf-${ARCH}/OVMF.fd
|
||||
endif
|
||||
endif
|
||||
|
||||
.PHONY: all check prepare-bin-files copy-initramfs-files compile-initramfs copy-iso-files build-iso compile-bootloader compile-binaries ovmf clean run build line-count
|
||||
@@ -44,7 +54,6 @@ prepare-bin-files:
|
||||
mkdir -p ${INITRAMFS_PATH}
|
||||
|
||||
copy-initramfs-files:
|
||||
# Stub for now ;)
|
||||
echo "Hello World from Initramfs" > ${INITRAMFS_PATH}/example.txt
|
||||
echo "Second file for testing" > ${INITRAMFS_PATH}/example2.txt
|
||||
mkdir -p ${INITRAMFS_PATH}/firstdir/seconddirbutlonger/
|
||||
@@ -60,7 +69,7 @@ copy-iso-files:
|
||||
mkdir -p ${ISO_PATH}/EFI/BOOT
|
||||
|
||||
cp -v limine.cfg limine/limine-bios.sys ${ISO_PATH}/boot/limine
|
||||
cp -v limine/BOOTX64.EFI ${ISO_PATH}/EFI/BOOT/
|
||||
cp -v limine/BOOT${LIMINE_BOOT_VARIATION}.EFI ${ISO_PATH}/EFI/BOOT/
|
||||
|
||||
# OS files
|
||||
cp -v target/${ARCH}-unknown-none/${MODE}/CappuccinOS.elf ${ISO_PATH}/boot
|
||||
@@ -100,8 +109,10 @@ build-iso: partition-iso
|
||||
fi
|
||||
python scripts/demangle-symbols.py
|
||||
mv scripts/symbols.table ${ISO_PATH}/boot
|
||||
# Install the Limine bootloader on the ISO
|
||||
ifeq (${ARCH},x86_64)
|
||||
# Install the Limine bootloader for bios installs
|
||||
./limine/limine bios-install ${IMAGE_PATH}
|
||||
endif
|
||||
|
||||
# Make a FAT32 FS and copy files in /bin/iso_root into the ISO starting at 1M or exactly 2048 sectors
|
||||
mformat -F -i ${IMAGE_PATH}@@1M
|
||||
@@ -120,15 +131,28 @@ compile-bootloader:
|
||||
compile-binaries:
|
||||
cargo build ${CARGO_OPTS}
|
||||
|
||||
ovmf:
|
||||
mkdir -p bin/ovmf
|
||||
cd bin/ovmf && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd
|
||||
ovmf-x86_64: ovmf
|
||||
mkdir -p ovmf/ovmf-x86_64
|
||||
@if [ ! -d "ovmf/ovmf-x86_64/OVMF.fd" ]; then \
|
||||
cd ovmf/ovmf-x86_64 && curl -Lo OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd; \
|
||||
fi
|
||||
|
||||
ovmf-riscv64: ovmf
|
||||
mkdir -p ovmf/ovmf-riscv64
|
||||
@if [ ! -d "ovmf/ovmf-riscv64/OVMF.fd" ]; then \
|
||||
cd ovmf/ovmf-riscv64 && curl -o OVMF.fd https://retrage.github.io/edk2-nightly/bin/RELEASERISCV64_VIRT_CODE.fd && dd if=/dev/zero of=OVMF.fd bs=1 count=0 seek=33554432; \
|
||||
fi
|
||||
|
||||
# In debug mode, open a terminal and run this command:
|
||||
# gdb target/x86_64-unknown-none/debug/CappuccinOS.elf -ex "target remote :1234"
|
||||
|
||||
run: build ${RUN_OPTS}
|
||||
qemu-system-x86_64 ${QEMU_OPTS}
|
||||
run: build ${RUN_OPTS} run-${ARCH}
|
||||
|
||||
run-x86_64:
|
||||
qemu-system-x86_64 ${QEMU_OPTS}
|
||||
|
||||
run-riscv64:
|
||||
qemu-system-riscv64 ${QEMU_OPTS} -M virt -cpu rv64 -device ramfb -device qemu-xhci -device usb-kbd -device virtio-scsi-pci,id=scsi -device scsi-hd,drive=hd0
|
||||
|
||||
line-count:
|
||||
cloc --quiet --exclude-dir=bin --csv src/ | tail -n 1 | awk -F, '{print $$5}'
|
||||
|
||||
17
README.md
17
README.md
@@ -11,7 +11,10 @@ CappuccinOS is a small x86-64 operating system written from scratch in rust. Thi
|
||||
- [X] ANSI color codes in console
|
||||
- [X] Heap allocation
|
||||
- [ ] Externalized kernel modules
|
||||
- [ ] Initramfs
|
||||
- [X] Initramfs
|
||||
- [X] Squashfs driver
|
||||
- [X] Programmatic reads
|
||||
- [X] Decompression
|
||||
- [ ] SMP
|
||||
- [ ] Use APIC instead of PIC
|
||||
- [ ] Pre-emptive multitasking
|
||||
@@ -27,7 +30,7 @@ CappuccinOS is a small x86-64 operating system written from scratch in rust. Thi
|
||||
- [ ] M.2 NVME device support
|
||||
- [ ] Basic shell
|
||||
- [X] Basic I/O
|
||||
- [ ] Executing Programs
|
||||
- [ ] Executing Programs from disk
|
||||
- [ ] Lua interpreter
|
||||
- [ ] Memory management
|
||||
- [ ] Network support
|
||||
@@ -37,7 +40,6 @@ CappuccinOS is a small x86-64 operating system written from scratch in rust. Thi
|
||||
- [ ] User authentication
|
||||
- [ ] Power management
|
||||
- [ ] Paging
|
||||
- [ ] Hardware abstraction layer
|
||||
- [ ] RTC Clock
|
||||
|
||||
## Setup
|
||||
@@ -45,7 +47,6 @@ Before building CappuccinOS, make sure you have the following installed on your
|
||||
|
||||
- rust
|
||||
- python
|
||||
- binutils
|
||||
- sgdisk
|
||||
- mtools
|
||||
- squashfs-tools
|
||||
@@ -66,7 +67,7 @@ Install the dependencies:
|
||||
<details>
|
||||
<summary>Arch</summary>
|
||||
|
||||
sudo pacman -S binutils gptfdisk mtools squashfs-tools python
|
||||
sudo pacman -S gptfdisk mtools squashfs-tools python
|
||||
# Optionally
|
||||
sudo pacman -S qemu-system-x86
|
||||
</details>
|
||||
@@ -75,7 +76,7 @@ Install the dependencies:
|
||||
<summary>Ubuntu</summary>
|
||||
Python should be installed by default, and if it's not, make an issue or a PR and I'll fix it
|
||||
|
||||
sudo apt install binutils gdisk mtools squashfs-tools
|
||||
sudo apt install gdisk mtools squashfs-tools
|
||||
# Optionally
|
||||
sudo apt install qemu
|
||||
</details>
|
||||
@@ -99,6 +100,10 @@ sudo dd if=bin/CappuccinOS.iso of=/dev/sdX bs=1M && sync
|
||||
```
|
||||
**Be careful not to overwrite your hard drive when using `dd`!**
|
||||
|
||||
## Supported Architectures
|
||||
- x86_64
|
||||
- RISC-V64 (experimental until I can get my hands on some RISC-V hardware)
|
||||
|
||||
## Credits an attributions
|
||||
Inspiration was mainly from [JDH's Tetris OS](https://www.youtube.com/watch?v=FaILnmUYS_U), mixed with a growing interest in low level in general and an interest in learning rust (yeah, I started this project with not that much rust experience, maybe a CLI app or two, and trust me it shows).
|
||||
|
||||
|
||||
10
TODO.md
10
TODO.md
@@ -2,10 +2,12 @@
|
||||
not really meant to be looked at, just writing down my thoughts
|
||||
|
||||
- [ ] finish fat fs
|
||||
- [ ] follow cluster chains
|
||||
- [ ] Search for file in code
|
||||
- [ ] search into folders
|
||||
- [ ] Integrate with VFS
|
||||
- [X] follow cluster chains
|
||||
- [X] Search for file in code
|
||||
- [X] search into folders
|
||||
- [X] Integrate with VFS
|
||||
- [ ] Writes
|
||||
- [ ] Read directory contents
|
||||
- [ ] Custom FS
|
||||
|
||||
2 partitions, one that is the FAT fs that the system boot from, the directory structure looks like this:
|
||||
|
||||
@@ -5,6 +5,6 @@ TIMEOUT=3
|
||||
PROTOCOL=limine
|
||||
|
||||
KERNEL_PATH=boot:///boot/CappuccinOS.elf
|
||||
KERNEL_CMDLINE=fat_in_mem=true big_fat_phony
|
||||
KERNEL_CMDLINE=fat_in_mem=false big_fat_phony
|
||||
|
||||
MODULE_PATH=boot:///boot/initramfs.img
|
||||
@@ -9,3 +9,9 @@ mod x86_64;
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
mod x86_common;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
mod riscv64;
|
||||
|
||||
#[cfg(target_arch = "riscv64")]
|
||||
pub use self::riscv64::*;
|
||||
|
||||
@@ -8,9 +8,10 @@
|
||||
"features": "-mmx,-sse,+soft-float",
|
||||
"os": "CappuccinOS",
|
||||
"arch": "x86_64",
|
||||
"linker-flavor": "ld",
|
||||
"linker": "rust-lld",
|
||||
"linker-flavor": "ld.lld",
|
||||
"pre-link-args": {
|
||||
"ld": [
|
||||
"ld.lld": [
|
||||
"-melf_x86_64",
|
||||
"--script=./src/arch/x86_64/linker.ld"
|
||||
]
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// use alloc::format;
|
||||
|
||||
// // piggyback off of the CappuccinOS allocator
|
||||
// // TODO: make a syscall for memory operations
|
||||
// // TODO: make a malloc lib for memory operations
|
||||
// #[allow(unused_imports)]
|
||||
// use CappuccinOS;
|
||||
|
||||
@@ -20,6 +20,7 @@ pub fn _start(_args: &[&str]) {
|
||||
}
|
||||
|
||||
fn print(message: &str) {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov rdi, 0x01", // write syscall
|
||||
|
||||
@@ -23,7 +23,7 @@ use super::vfs::{VfsDirectory, VfsFile, VfsFileSystem};
|
||||
// End Of Chain
|
||||
const EOC: u32 = 0x0FFFFFF8;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
enum FatType {
|
||||
Fat12,
|
||||
Fat16,
|
||||
@@ -47,6 +47,7 @@ pub struct BIOSParameterBlock {
|
||||
pub head_count: u16, // 10 00 (16)
|
||||
pub hidden_sectors: u32, // 00 00 00 00
|
||||
pub large_sector_count: u32, // 00 F8 01 00 (129024)
|
||||
// pub ebpb: [u8; 54],
|
||||
// ---------------------------------
|
||||
// - Extended BIOS Parameter Block -
|
||||
// ---------------------------------
|
||||
@@ -193,19 +194,15 @@ impl<'a> FatFs<'a> {
|
||||
|
||||
if crate::KERNEL_FEATURES.fat_in_mem {
|
||||
let mut fat_vec: Vec<u32> = Vec::with_capacity(bytes_per_fat / 4);
|
||||
unsafe { fat_vec.set_len(fat_vec.capacity()) };
|
||||
|
||||
for i in 0..(bpb.sectors_per_fat_ext as usize) {
|
||||
let sector = drive
|
||||
.read(fat_start + i as u64, 1)
|
||||
.expect("Failed to read FAT");
|
||||
for j in 0..(512 / 4) {
|
||||
fat_vec[i * (512 / 4) + j] = u32::from_le_bytes([
|
||||
sector[j * 4],
|
||||
sector[(j * 4) + 1],
|
||||
sector[(j * 4) + 2],
|
||||
sector[(j * 4) + 3],
|
||||
])
|
||||
fat_vec.push(u32::from_le_bytes(
|
||||
sector[j * 4..(j * 4 + 4)].try_into().unwrap(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -288,7 +285,7 @@ impl<'a> FatFs<'a> {
|
||||
continue; // Directory is unused, ignore it
|
||||
} else if bytes[11] == FileEntryAttributes::LongFileName as u8 {
|
||||
// Entry is LFN (step 3)
|
||||
// read long filename somehow (step 4)
|
||||
// read long filename (step 4)
|
||||
let long_filename_part: LongFileName;
|
||||
|
||||
unsafe {
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
use alloc::{sync::Arc, vec::Vec};
|
||||
|
||||
use crate::libs::mutex::Mutex;
|
||||
|
||||
#[derive(Debug)]
|
||||
#[repr(u8)]
|
||||
enum ZlibCompressionLevel {
|
||||
@@ -21,15 +25,14 @@ impl From<u8> for ZlibCompressionLevel {
|
||||
|
||||
// RFC 1950: "ZLIB Compressed Data Format Specification"
|
||||
// RFC 1951: "DEFLATE Compressed Data Format Specification"
|
||||
pub fn uncompress_data(bytes: &[u8]) -> &[u8] {
|
||||
pub fn uncompress_data(bytes: &[u8]) -> Result<Arc<[u8]>, ()> {
|
||||
assert!(bytes.len() > 2);
|
||||
|
||||
// Compression Method and flags
|
||||
let cmf = bytes[0];
|
||||
let flags = bytes[1];
|
||||
|
||||
if cmf & 0x0F != 0x08 {
|
||||
panic!("Compression method is not GZIP!",);
|
||||
if (cmf & 0x0F) != 0x08 {
|
||||
panic!("Compression method is not deflate!");
|
||||
}
|
||||
|
||||
let window_log2 = cmf >> 4 & 0x0F;
|
||||
@@ -38,10 +41,378 @@ pub fn uncompress_data(bytes: &[u8]) -> &[u8] {
|
||||
panic!("Unsupported window size {window_log2:X}!");
|
||||
}
|
||||
|
||||
let flags = bytes[1];
|
||||
if (cmf as u32 * 256 + flags as u32) % 31 != 0 {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
// TODO: Check if FCheck is valid
|
||||
|
||||
let present_dictionary = flags >> 5 & 0x01 != 0;
|
||||
let compression_level: ZlibCompressionLevel = (flags >> 6 & 0x03).into();
|
||||
let _compression_level: ZlibCompressionLevel = (flags >> 6 & 0x03).into();
|
||||
|
||||
todo!("Uncompress data");
|
||||
if present_dictionary {
|
||||
// cry
|
||||
return Err(());
|
||||
}
|
||||
|
||||
let mut inflate_context = InflateContext::new(&bytes[2..bytes.len() - 4]);
|
||||
|
||||
let data = inflate_context.decompress()?;
|
||||
|
||||
// last 4 bytes
|
||||
let checksum = u32::from_le_bytes(bytes[bytes.len() - 4..].try_into().unwrap());
|
||||
|
||||
if adler32(&data) != checksum {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
return Ok(data.into());
|
||||
}
|
||||
|
||||
fn adler32(bytes: &[u8]) -> u32 {
|
||||
let mut a = 1_u32;
|
||||
let mut b = 0_u32;
|
||||
|
||||
for &byte in bytes {
|
||||
a = (a + byte as u32) % 65521;
|
||||
b = (b + a) % 65521;
|
||||
}
|
||||
|
||||
return u32::from_be((b << 16) | a);
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Huff {
|
||||
counts: [u16; 16],
|
||||
symbols: [u16; 288],
|
||||
}
|
||||
|
||||
static FIXED_LENGTHS: Mutex<Huff> = Mutex::new(Huff {
|
||||
counts: [0_u16; 16],
|
||||
symbols: [0_u16; 288],
|
||||
});
|
||||
static FIXED_DISTS: Mutex<Huff> = Mutex::new(Huff {
|
||||
counts: [0_u16; 16],
|
||||
symbols: [0_u16; 288],
|
||||
});
|
||||
|
||||
struct HuffRing {
|
||||
pointer: usize,
|
||||
data: Vec<u8>,
|
||||
}
|
||||
|
||||
impl HuffRing {
|
||||
fn new() -> Self {
|
||||
let mut data = Vec::with_capacity(32 * 1024);
|
||||
unsafe {
|
||||
data.set_len(32 * 1024);
|
||||
};
|
||||
|
||||
return Self { pointer: 0, data };
|
||||
}
|
||||
}
|
||||
|
||||
struct InflateContext {
|
||||
input_buf: Vec<u8>,
|
||||
bit_index: usize,
|
||||
output_buf: alloc::vec::Vec<u8>,
|
||||
ring: HuffRing,
|
||||
}
|
||||
|
||||
impl InflateContext {
|
||||
fn new(bytes: &[u8]) -> Self {
|
||||
return Self {
|
||||
input_buf: bytes.to_vec(),
|
||||
bit_index: 0,
|
||||
output_buf: Vec::new(),
|
||||
ring: HuffRing::new(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn get_bit(&mut self) -> bool {
|
||||
if self.bit_index == 8 {
|
||||
self.input_buf.remove(0);
|
||||
if self.input_buf.len() == 0 {
|
||||
panic!("Not enough data! {:X?}", self.output_buf);
|
||||
}
|
||||
|
||||
self.bit_index = 0;
|
||||
}
|
||||
|
||||
let byte = self.input_buf[0] & (1 << self.bit_index) != 0;
|
||||
self.bit_index += 1;
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
pub fn get_bits(&mut self, num_bits: usize) -> u32 {
|
||||
let mut byte = 0_u32;
|
||||
|
||||
for bit in 0..num_bits {
|
||||
byte |= (self.get_bit() as u32) << bit;
|
||||
}
|
||||
|
||||
return byte;
|
||||
}
|
||||
|
||||
fn get_bits_base(&mut self, num: usize, base: usize) -> u32 {
|
||||
return (base + if num != 0 { self.get_bits(num) } else { 0 } as usize) as u32;
|
||||
}
|
||||
|
||||
pub fn decompress(&mut self) -> Result<Arc<[u8]>, ()> {
|
||||
build_fixed();
|
||||
|
||||
loop {
|
||||
let is_final = self.get_bit();
|
||||
let block_type = self.get_bits(2);
|
||||
|
||||
match block_type {
|
||||
0x00 => {
|
||||
self.uncompressed()?;
|
||||
}
|
||||
0x01 => {
|
||||
self.inflate(FIXED_LENGTHS.lock().write(), FIXED_DISTS.lock().write())?;
|
||||
}
|
||||
0x02 => {
|
||||
self.decode_huffman()?;
|
||||
}
|
||||
_ => {
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
||||
if is_final {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(Arc::from(self.output_buf.clone()));
|
||||
}
|
||||
|
||||
fn decode(&mut self, huff: &mut Huff) -> u32 {
|
||||
let mut base: i32 = 0;
|
||||
let mut offs: i32 = 0;
|
||||
|
||||
let mut i = 1;
|
||||
loop {
|
||||
offs = 2 * offs + self.get_bit() as i32;
|
||||
|
||||
assert!(i <= 15);
|
||||
|
||||
if offs < huff.counts[i] as i32 {
|
||||
break;
|
||||
}
|
||||
|
||||
base += huff.counts[i] as i32;
|
||||
offs -= huff.counts[i] as i32;
|
||||
i += 1;
|
||||
}
|
||||
|
||||
assert!(base + offs >= 0 && base + offs < 288);
|
||||
|
||||
return huff.symbols[(base + offs) as usize] as u32;
|
||||
}
|
||||
|
||||
fn emit(&mut self, byte: u8) {
|
||||
if self.ring.pointer == 32768 {
|
||||
self.ring.pointer = 0;
|
||||
}
|
||||
|
||||
self.ring.data[self.ring.pointer] = byte;
|
||||
self.output_buf.push(byte);
|
||||
self.ring.pointer += 1;
|
||||
}
|
||||
|
||||
fn peek(&mut self, offset: usize) -> u8 {
|
||||
self.ring.data[(self.ring.pointer - offset) % 32768]
|
||||
}
|
||||
|
||||
fn uncompressed(&mut self) -> Result<(), ()> {
|
||||
let len = u16::from_le(self.get_bits(16).try_into().unwrap());
|
||||
let nlen = u16::from_le(self.get_bits(16).try_into().unwrap());
|
||||
|
||||
if nlen != !len {
|
||||
return Err(());
|
||||
}
|
||||
|
||||
for _ in 0..len {
|
||||
// TODO: is this right?
|
||||
let byte = self.get_bits(8) as u8;
|
||||
self.emit(byte);
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn inflate(&mut self, huff_len: &mut Huff, huff_dist: &mut Huff) -> Result<(), ()> {
|
||||
let length_bits = [
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0,
|
||||
127,
|
||||
];
|
||||
let length_base = [
|
||||
3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, 35, 43, 51, 59, 67, 83, 99,
|
||||
115, 131, 163, 195, 227, 258, 0,
|
||||
];
|
||||
|
||||
let dist_bits = [
|
||||
0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12,
|
||||
12, 13, 13,
|
||||
];
|
||||
let dist_base = [
|
||||
1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025,
|
||||
1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577,
|
||||
];
|
||||
|
||||
loop {
|
||||
let mut symbol = self.decode(huff_len);
|
||||
|
||||
if symbol < 256 {
|
||||
self.emit(symbol as u8);
|
||||
} else {
|
||||
if symbol == 256 {
|
||||
break;
|
||||
}
|
||||
|
||||
symbol -= 257;
|
||||
|
||||
let length =
|
||||
self.get_bits_base(length_bits[symbol as usize], length_base[symbol as usize]);
|
||||
let distance = self.decode(huff_dist);
|
||||
let offset =
|
||||
self.get_bits_base(dist_bits[distance as usize], dist_base[distance as usize]);
|
||||
|
||||
for _ in 0..length {
|
||||
let b = self.peek(offset as usize);
|
||||
self.emit(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
fn decode_huffman(&mut self) -> Result<(), ()> {
|
||||
let clens = [
|
||||
16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15,
|
||||
];
|
||||
|
||||
let mut lengths = [0_u8; 320];
|
||||
|
||||
let literals = self.get_bits_base(5, 257);
|
||||
let distances = self.get_bits_base(5, 1);
|
||||
let clengths = self.get_bits_base(4, 4);
|
||||
|
||||
for i in 0..clengths {
|
||||
lengths[clens[i as usize] as usize] = self.get_bits(3) as u8;
|
||||
}
|
||||
|
||||
let mut codes = Huff {
|
||||
counts: [0_u16; 16],
|
||||
symbols: [0_u16; 288],
|
||||
};
|
||||
build_huffman(&lengths, 19, &mut codes);
|
||||
|
||||
let mut count = 0_u32;
|
||||
while count < literals + distances {
|
||||
let symbol = self.decode(&mut codes);
|
||||
|
||||
if symbol < 16 {
|
||||
lengths[count as usize] = symbol as u8;
|
||||
count += 1;
|
||||
} else if symbol < 19 {
|
||||
let mut rep = 0_u32;
|
||||
let mut length;
|
||||
|
||||
if symbol == 16 {
|
||||
rep = lengths[count as usize - 1] as u32;
|
||||
length = self.get_bits_base(2, 3);
|
||||
} else if symbol == 17 {
|
||||
length = self.get_bits_base(3, 3);
|
||||
} else {
|
||||
length = self.get_bits_base(7, 11);
|
||||
}
|
||||
|
||||
while length != 0 {
|
||||
lengths[count as usize] = rep as u8;
|
||||
count += 1;
|
||||
length -= 1;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
let mut huff_len = Huff {
|
||||
counts: [0_u16; 16],
|
||||
symbols: [0_u16; 288],
|
||||
};
|
||||
build_huffman(&lengths, literals as usize, &mut huff_len);
|
||||
let mut huff_dist = Huff {
|
||||
counts: [0_u16; 16],
|
||||
symbols: [0_u16; 288],
|
||||
};
|
||||
build_huffman(
|
||||
&lengths[literals as usize..],
|
||||
distances as usize,
|
||||
&mut huff_dist,
|
||||
);
|
||||
|
||||
self.inflate(&mut huff_len, &mut huff_dist)?;
|
||||
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
fn build_huffman(lengths: &[u8], size: usize, out: &mut Huff) {
|
||||
let mut offsets = [0_u32; 16];
|
||||
let mut count: u32 = 0;
|
||||
|
||||
assert!(size <= 288);
|
||||
|
||||
for i in 0..16 {
|
||||
out.counts[i] = 0;
|
||||
}
|
||||
|
||||
for i in 0..size {
|
||||
assert!(lengths[i] <= 15);
|
||||
|
||||
out.counts[lengths[i] as usize] += 1;
|
||||
}
|
||||
|
||||
out.counts[0] = 0;
|
||||
|
||||
for i in 0..16 {
|
||||
offsets[i] = count;
|
||||
count += out.counts[i] as u32;
|
||||
}
|
||||
|
||||
for i in 0..size {
|
||||
if lengths[i] != 0 {
|
||||
out.symbols[offsets[lengths[i] as usize] as usize] = i.try_into().unwrap();
|
||||
offsets[lengths[i] as usize] += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_fixed() {
|
||||
let mut lengths = [0_u8; 288];
|
||||
for i in 0..144 {
|
||||
lengths[i] = 8;
|
||||
}
|
||||
for i in 144..256 {
|
||||
lengths[i] = 9;
|
||||
}
|
||||
for i in 256..280 {
|
||||
lengths[i] = 7;
|
||||
}
|
||||
for i in 280..288 {
|
||||
lengths[i] = 8;
|
||||
}
|
||||
build_huffman(&lengths, 288, FIXED_LENGTHS.lock().write());
|
||||
|
||||
for i in 0..30 {
|
||||
lengths[i] = 5;
|
||||
}
|
||||
build_huffman(&lengths, 30, FIXED_DISTS.lock().write());
|
||||
}
|
||||
|
||||
@@ -43,15 +43,6 @@ pub fn init() {
|
||||
}
|
||||
let initramfs = initramfs.unwrap();
|
||||
|
||||
crate::println!("Initramfs is located at: {:#018X?}", unsafe {
|
||||
initramfs.base.as_ptr().unwrap()
|
||||
..initramfs
|
||||
.base
|
||||
.as_ptr()
|
||||
.unwrap()
|
||||
.add(initramfs.length as usize)
|
||||
});
|
||||
|
||||
let squashfs = Squashfs::new(initramfs.base.as_ptr().unwrap());
|
||||
|
||||
if squashfs.is_err() {
|
||||
@@ -61,15 +52,15 @@ pub fn init() {
|
||||
|
||||
let squashfs = squashfs.unwrap();
|
||||
|
||||
crate::println!("{:X?}", squashfs);
|
||||
crate::println!("{:?}", squashfs.superblock.features());
|
||||
|
||||
crate::println!(
|
||||
"{:X?}",
|
||||
squashfs
|
||||
.open("/firstdir/seconddirbutlonger/yeah.txt")
|
||||
.unwrap()
|
||||
.read()
|
||||
"\033[92m{:?}",
|
||||
core::str::from_utf8(
|
||||
&squashfs
|
||||
.open("/firstdir/seconddirbutlonger/yeah.txt")
|
||||
.unwrap()
|
||||
.read()
|
||||
.unwrap()
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
@@ -88,9 +79,9 @@ struct Squashfs<'a> {
|
||||
|
||||
impl Squashfs<'_> {
|
||||
fn new(ptr: *mut u8) -> Result<Squashfs<'static>, ()> {
|
||||
crate::println!("Parsing initramfs fs at {:p}", ptr);
|
||||
crate::log_info!("Parsing initramfs at {:p}", ptr);
|
||||
|
||||
// bytes used from superblock
|
||||
// 40 is the offset for bytes used by the archive in the superblock
|
||||
let length = unsafe { u64::from_le(*(ptr.add(40) as *const u64)) as usize };
|
||||
|
||||
let squashfs_data: &[u8] = unsafe { core::slice::from_raw_parts(ptr, length) };
|
||||
@@ -100,36 +91,81 @@ impl Squashfs<'_> {
|
||||
let data_table = &squashfs_data
|
||||
[core::mem::size_of::<SquashfsSuperblock>()..superblock.inode_table as usize];
|
||||
|
||||
let inode_table =
|
||||
&squashfs_data[superblock.inode_table as usize..superblock.dir_table as usize];
|
||||
macro_rules! get_metadata_table {
|
||||
($table_type:ident) => {{
|
||||
// get table size minus the top bit (indicates compression) plus 2, because
|
||||
// The table size is minus the size of the size header (two bytes)
|
||||
let table_size = (u16::from_le_bytes(
|
||||
squashfs_data
|
||||
[superblock.$table_type as usize..superblock.$table_type as usize + 2]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
) & 0x7FFF)
|
||||
+ 2;
|
||||
|
||||
let directory_table =
|
||||
&squashfs_data[superblock.dir_table as usize..superblock.frag_table as usize];
|
||||
|
||||
let mut fragment_table: Option<&[u8]> = None;
|
||||
|
||||
if superblock.frag_table != u64::MAX {
|
||||
fragment_table = Some(
|
||||
&squashfs_data[superblock.frag_table as usize..superblock.export_table as usize],
|
||||
);
|
||||
&squashfs_data[superblock.$table_type as usize
|
||||
..superblock.$table_type as usize + table_size as usize]
|
||||
}};
|
||||
}
|
||||
|
||||
let mut export_table: Option<&[u8]> = None;
|
||||
let inode_table = get_metadata_table!(inode_table);
|
||||
|
||||
if superblock.export_table != u64::MAX {
|
||||
export_table = Some(
|
||||
&squashfs_data[superblock.export_table as usize..superblock.id_table as usize],
|
||||
);
|
||||
}
|
||||
let directory_table = get_metadata_table!(dir_table);
|
||||
|
||||
let mut id_table: &[u8] = &squashfs_data[superblock.id_table as usize..];
|
||||
let mut xattr_table: Option<&[u8]> = None;
|
||||
let fragment_table: Option<&[u8]> = {
|
||||
if superblock.frag_table == u64::MAX {
|
||||
None
|
||||
} else {
|
||||
if superblock.export_table != u64::MAX {
|
||||
Some(
|
||||
&squashfs_data
|
||||
[superblock.frag_table as usize..superblock.export_table as usize],
|
||||
)
|
||||
} else if superblock.xattr_table != u64::MAX {
|
||||
Some(
|
||||
&squashfs_data
|
||||
[superblock.frag_table as usize..superblock.xattr_table as usize],
|
||||
)
|
||||
} else {
|
||||
Some(
|
||||
&squashfs_data
|
||||
[superblock.frag_table as usize..superblock.id_table as usize],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if superblock.xattr_table != u64::MAX {
|
||||
id_table =
|
||||
&squashfs_data[superblock.id_table as usize..superblock.xattr_table as usize];
|
||||
xattr_table = Some(&squashfs_data[superblock.xattr_table as usize..]);
|
||||
}
|
||||
let export_table: Option<&[u8]> = {
|
||||
if superblock.export_table == u64::MAX {
|
||||
None
|
||||
} else {
|
||||
if superblock.xattr_table != u64::MAX {
|
||||
Some(
|
||||
&squashfs_data
|
||||
[superblock.export_table as usize..superblock.xattr_table as usize],
|
||||
)
|
||||
} else {
|
||||
Some(
|
||||
&squashfs_data
|
||||
[superblock.export_table as usize..superblock.id_table as usize],
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
let id_table: &[u8] = if superblock.xattr_table != u64::MAX {
|
||||
&squashfs_data[superblock.id_table as usize..superblock.xattr_table as usize]
|
||||
} else {
|
||||
&squashfs_data[superblock.id_table as usize..]
|
||||
};
|
||||
|
||||
let xattr_table: Option<&[u8]> = {
|
||||
if superblock.xattr_table == u64::MAX {
|
||||
None
|
||||
} else {
|
||||
Some(&squashfs_data[superblock.xattr_table as usize..])
|
||||
}
|
||||
};
|
||||
|
||||
return Ok(Squashfs {
|
||||
superblock,
|
||||
@@ -212,7 +248,7 @@ impl Squashfs<'_> {
|
||||
|
||||
match self.superblock.compressor {
|
||||
SquashfsCompressionType::Gzip => {
|
||||
buffer.extend_from_slice(compressors::gzip::uncompress_data(bytes));
|
||||
buffer.extend_from_slice(&compressors::gzip::uncompress_data(bytes).unwrap());
|
||||
}
|
||||
_ => {
|
||||
crate::println!("Unsupported compression type")
|
||||
@@ -239,13 +275,7 @@ impl<'a> VfsFileSystem for Squashfs<'a> {
|
||||
let path_components: Vec<&str> = path.trim_start_matches('/').split('/').collect();
|
||||
let mut current_dir = self.read_root_dir();
|
||||
|
||||
crate::println!("\033[94m{}\033[0m", "-".repeat(40));
|
||||
for (i, &part) in path_components.iter().enumerate() {
|
||||
crate::println!(
|
||||
"{}\ncur: {current_dir:?}\n{part}\n{path}\n{0}",
|
||||
"-".repeat(20)
|
||||
);
|
||||
|
||||
let file = current_dir.find(part).ok_or(())?;
|
||||
|
||||
match file {
|
||||
@@ -261,7 +291,6 @@ impl<'a> VfsFileSystem for Squashfs<'a> {
|
||||
}
|
||||
}
|
||||
}
|
||||
crate::println!("\033[94m{}\033[0m", "-".repeat(40));
|
||||
|
||||
return Err(());
|
||||
}
|
||||
@@ -370,20 +399,10 @@ impl<'a> BasicDirectoryInode<'a> {
|
||||
fn entries(&self) -> Arc<[Inode]> {
|
||||
let mut entries: Vec<Inode> = Vec::new();
|
||||
|
||||
let directory_table = &self.header.squashfs.get_decompressed_table(
|
||||
self.header.squashfs.directory_table,
|
||||
(
|
||||
false,
|
||||
Some(
|
||||
!self
|
||||
.header
|
||||
.squashfs
|
||||
.superblock
|
||||
.features()
|
||||
.uncompressed_data_blocks,
|
||||
),
|
||||
),
|
||||
);
|
||||
let directory_table = &self
|
||||
.header
|
||||
.squashfs
|
||||
.get_decompressed_table(self.header.squashfs.directory_table, (true, None));
|
||||
|
||||
let directory_table_header =
|
||||
DirectoryTableHeader::from_bytes(&directory_table[self.block_offset as usize..]);
|
||||
@@ -408,22 +427,10 @@ impl<'a> BasicDirectoryInode<'a> {
|
||||
}
|
||||
|
||||
fn find(&self, name: &str) -> Option<Inode<'a>> {
|
||||
crate::println!("Searching for file {name} in directory");
|
||||
|
||||
let directory_table = &self.header.squashfs.get_decompressed_table(
|
||||
self.header.squashfs.directory_table,
|
||||
(
|
||||
false,
|
||||
Some(
|
||||
!self
|
||||
.header
|
||||
.squashfs
|
||||
.superblock
|
||||
.features()
|
||||
.uncompressed_data_blocks,
|
||||
),
|
||||
),
|
||||
);
|
||||
let directory_table = &self
|
||||
.header
|
||||
.squashfs
|
||||
.get_decompressed_table(self.header.squashfs.directory_table, (true, None));
|
||||
|
||||
let directory_table_header =
|
||||
DirectoryTableHeader::from_bytes(&directory_table[self.block_offset as usize..]);
|
||||
@@ -436,8 +443,6 @@ impl<'a> BasicDirectoryInode<'a> {
|
||||
|
||||
offset += 8 + directroy_table_entry.name.len();
|
||||
|
||||
crate::println!("{}", directroy_table_entry.name);
|
||||
|
||||
if directroy_table_entry.name == name {
|
||||
return Some(
|
||||
self.header
|
||||
@@ -490,12 +495,23 @@ impl<'a> VfsFile for BasicFileInode<'a> {
|
||||
let mut block_data: Vec<u8> = Vec::with_capacity(8192 * block_count);
|
||||
|
||||
unsafe {
|
||||
let data_table = self.header.squashfs.get_decompressed_table(
|
||||
self.header.squashfs.data_table,
|
||||
(
|
||||
false,
|
||||
Some(
|
||||
!self
|
||||
.header
|
||||
.squashfs
|
||||
.superblock
|
||||
.features()
|
||||
.uncompressed_data_blocks,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
core::ptr::copy_nonoverlapping(
|
||||
self.header
|
||||
.squashfs
|
||||
.data_table
|
||||
.as_ptr()
|
||||
.add(self.block_offset as usize),
|
||||
data_table.as_ptr().add(self.block_offset as usize),
|
||||
block_data.as_mut_ptr(),
|
||||
self.file_size as usize,
|
||||
);
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloc::{
|
||||
boxed::Box,
|
||||
string::{String, ToString},
|
||||
sync::Arc,
|
||||
vec::Vec,
|
||||
};
|
||||
|
||||
use crate::libs::mutex::Mutex;
|
||||
|
||||
@@ -18,12 +23,16 @@ pub trait VfsDirectory {
|
||||
pub static VFS_INSTANCES: Mutex<Vec<Vfs>> = Mutex::new(Vec::new());
|
||||
|
||||
pub struct Vfs {
|
||||
identifier: String,
|
||||
file_system: Box<dyn VfsFileSystem>,
|
||||
}
|
||||
|
||||
impl Vfs {
|
||||
pub fn new(file_system: Box<dyn VfsFileSystem>) -> Self {
|
||||
return Self { file_system };
|
||||
pub fn new(file_system: Box<dyn VfsFileSystem>, identifier: &str) -> Self {
|
||||
return Self {
|
||||
identifier: identifier.to_string(),
|
||||
file_system,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn open(&self, path: &str) -> Result<Box<dyn VfsFile + '_>, ()> {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
use crate::arch::interrupts::{idt_set_gate, InterruptIndex};
|
||||
// Shitty keyboard driver
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
@@ -39,6 +40,7 @@ pub enum KBDError {
|
||||
TestFailed,
|
||||
}
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub fn init() -> Result<(), KBDError> {
|
||||
// flush output buffer
|
||||
while (inb(KBD_COMMAND_AND_STATUS_PORT) & 1) != 0 {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
pub mod acpi;
|
||||
pub mod fs;
|
||||
pub mod keyboard;
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub mod pci;
|
||||
pub mod serial;
|
||||
pub mod storage;
|
||||
|
||||
@@ -5,6 +5,7 @@ use crate::arch::io::{inb, outb};
|
||||
|
||||
// COM1
|
||||
pub static PORT: u16 = 0x3f8;
|
||||
pub const UART: *mut char = 0x10000000 as *mut char;
|
||||
|
||||
pub static POISONED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
@@ -56,3 +57,17 @@ pub fn write_serial(character: char) {
|
||||
while is_transmit_empty() {}
|
||||
outb(PORT, character as u8);
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
pub fn is_transmit_empty() -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
pub fn write_serial(character: char) {
|
||||
unsafe {
|
||||
*UART = character;
|
||||
};
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,17 @@ use alloc::sync::Arc;
|
||||
pub trait BlockDevice {
|
||||
fn sector_count(&self) -> u64;
|
||||
fn read(&self, sector: u64, sector_count: usize) -> Result<Arc<[u8]>, ()>;
|
||||
fn write(&self, sector: u64, data: &mut [u8]) -> Result<(), ()>;
|
||||
fn write(&self, sector: u64, data: &[u8]) -> Result<(), ()>;
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Default)]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct GPTPartitionEntry {
|
||||
pub partition_type_guid: [u8; 16],
|
||||
pub unique_partition_guid: [u8; 16],
|
||||
pub start_sector: u64,
|
||||
pub end_sector: u64,
|
||||
pub attributes: u64,
|
||||
pub partition_name: [u8; 72],
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
use core::mem::size_of;
|
||||
|
||||
use alloc::{boxed::Box, sync::Arc, vec::Vec};
|
||||
use alloc::{boxed::Box, format, sync::Arc, vec::Vec};
|
||||
|
||||
use crate::{
|
||||
arch::io::{inb, insw, inw, outb, outsw, outw},
|
||||
arch::io::{inb, inw, outb, outw},
|
||||
drivers::{
|
||||
fs::fat,
|
||||
storage::drive::{GPTBlock, GPTPartitionEntry},
|
||||
@@ -296,7 +294,7 @@ impl ATABus {
|
||||
sector,
|
||||
sector_count,
|
||||
ATADriveDirection::Read,
|
||||
buffer.as_mut_slice(),
|
||||
(buffer.as_mut_ptr(), buffer.len()),
|
||||
)?;
|
||||
|
||||
return Ok(Arc::from(buffer));
|
||||
@@ -307,7 +305,7 @@ impl ATABus {
|
||||
drive: ATADriveType,
|
||||
sector: u64,
|
||||
sector_count: usize,
|
||||
buffer: &mut [u8],
|
||||
buffer: &[u8],
|
||||
) -> Result<(), ()> {
|
||||
if buffer.len() < ATA_SECTOR_SIZE * sector_count {
|
||||
return Err(());
|
||||
@@ -318,7 +316,7 @@ impl ATABus {
|
||||
sector,
|
||||
sector_count,
|
||||
ATADriveDirection::Write,
|
||||
buffer,
|
||||
(buffer.as_ptr() as *mut u8, buffer.len()),
|
||||
)?;
|
||||
|
||||
return Ok(());
|
||||
@@ -330,8 +328,10 @@ impl ATABus {
|
||||
sector: u64,
|
||||
sector_count: usize,
|
||||
direction: ATADriveDirection,
|
||||
buffer: &mut [u8],
|
||||
buffer: (*mut u8, usize),
|
||||
) -> Result<(), ()> {
|
||||
let buffer = unsafe { core::slice::from_raw_parts_mut(buffer.0, buffer.1) };
|
||||
|
||||
self.await_busy();
|
||||
|
||||
let using_lba48 = sector >= (1 << 28) - 1;
|
||||
@@ -413,30 +413,10 @@ impl ATABus {
|
||||
|
||||
// Allocate memory for the array that stores the sector data
|
||||
let mut buffer_offset = 0;
|
||||
for i in 0..sector_count {
|
||||
for _ in 0..sector_count {
|
||||
self.wait_for_drive_ready()
|
||||
.map_err(|_| crate::log_error!("Error reading IDE Device"))?;
|
||||
|
||||
// # Safety
|
||||
//
|
||||
// We know that buffer is at least sector_count * ATA_SECTOR_SIZE, so it will never panic:tm:
|
||||
// unsafe {
|
||||
// match direction {
|
||||
// ATADriveDirection::Read => insw(
|
||||
// self.io_bar + ATADriveDataRegister::Data as u16,
|
||||
// (buffer.as_mut_ptr() as *mut u16)
|
||||
// .add((i * ATA_SECTOR_SIZE) / size_of::<u16>()),
|
||||
// ATA_SECTOR_SIZE / size_of::<u16>(),
|
||||
// ),
|
||||
// ATADriveDirection::Write => outsw(
|
||||
// self.io_bar + ATADriveDataRegister::Data as u16,
|
||||
// (buffer.as_mut_ptr() as *mut u16)
|
||||
// .add((i * ATA_SECTOR_SIZE) / size_of::<u16>()),
|
||||
// ATA_SECTOR_SIZE / size_of::<u16>(),
|
||||
// ),
|
||||
// }
|
||||
// }
|
||||
|
||||
for chunk in
|
||||
buffer[buffer_offset..(buffer_offset + ATA_SECTOR_SIZE)].chunks_exact_mut(2)
|
||||
{
|
||||
@@ -525,7 +505,7 @@ impl BlockDevice for ATADrive {
|
||||
return unsafe { *(sectors as *const u32) } as u64;
|
||||
}
|
||||
|
||||
fn write(&self, sector: u64, buffer: &mut [u8]) -> Result<(), ()> {
|
||||
fn write(&self, sector: u64, buffer: &[u8]) -> Result<(), ()> {
|
||||
let sector_count = buffer.len() / 512;
|
||||
|
||||
self.bus.software_reset();
|
||||
@@ -612,10 +592,11 @@ fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
|
||||
.expect("Failed to read partition table");
|
||||
|
||||
crate::println!(
|
||||
"{}, {}, {}",
|
||||
"{}, {}, {}, {:X?}",
|
||||
(gpt.partition_entry_count * gpt.partition_entry_size) as usize / ATA_SECTOR_SIZE,
|
||||
gpt.partition_entry_count,
|
||||
gpt.partition_entry_size
|
||||
gpt.partition_entry_size,
|
||||
gpt.guid
|
||||
);
|
||||
|
||||
for i in 0..gpt.partition_entry_count {
|
||||
@@ -637,6 +618,11 @@ fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let unique_partition_guid: [u8; 16] = partition_sector
|
||||
[entry_offset + 16..entry_offset + 32]
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
let start_sector = u64::from_le_bytes(
|
||||
partition_sector[entry_offset + 32..entry_offset + 40]
|
||||
.try_into()
|
||||
@@ -648,11 +634,24 @@ fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let attributes = u64::from_le_bytes(
|
||||
partition_sector[entry_offset + 48..entry_offset + 56]
|
||||
.try_into()
|
||||
.unwrap(),
|
||||
);
|
||||
|
||||
let partition_name = partition_sector[entry_offset + 56..entry_offset + 128]
|
||||
.try_into()
|
||||
.unwrap();
|
||||
|
||||
// Store the parsed information in the partition_entries array
|
||||
partitions.push(GPTPartitionEntry {
|
||||
partition_type_guid,
|
||||
unique_partition_guid,
|
||||
start_sector,
|
||||
end_sector,
|
||||
attributes,
|
||||
partition_name,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -665,7 +664,10 @@ fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
|
||||
|
||||
let fat_fs = fat_fs.unwrap();
|
||||
|
||||
let vfs = crate::drivers::fs::vfs::Vfs::new(Box::new(fat_fs));
|
||||
let vfs = crate::drivers::fs::vfs::Vfs::new(
|
||||
Box::new(fat_fs),
|
||||
&format!("{:?}", partition.partition_type_guid),
|
||||
);
|
||||
|
||||
crate::drivers::fs::vfs::VFS_INSTANCES
|
||||
.lock()
|
||||
@@ -685,16 +687,6 @@ fn ide_initialize(bar0: u32, bar1: u32, _bar2: u32, _bar3: u32, _bar4: u32) {
|
||||
);
|
||||
}
|
||||
|
||||
let mut buffer = Vec::with_capacity(4096);
|
||||
unsafe { buffer.set_len(buffer.capacity()) };
|
||||
buffer.fill(0x69_u8);
|
||||
|
||||
let _ = drive.write(0, &mut buffer);
|
||||
|
||||
let data = drive.read(0, 1);
|
||||
|
||||
crate::println!("{:X?}", data);
|
||||
|
||||
crate::println!("{:?}", partitions);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
mod font;
|
||||
|
||||
use crate::libs::mutex::Mutex;
|
||||
use alloc::vec;
|
||||
use limine::FramebufferRequest;
|
||||
|
||||
pub static FRAMEBUFFER_REQUEST: FramebufferRequest = FramebufferRequest::new(0);
|
||||
@@ -35,6 +36,8 @@ pub fn put_char(
|
||||
mirror_buffer: Option<Framebuffer>,
|
||||
) {
|
||||
let font = font::G_8X16_FONT;
|
||||
let font_width = 8;
|
||||
let font_height = 16;
|
||||
|
||||
if character as usize > u8::MAX as usize {
|
||||
character = '\x00'; // rounded question mark
|
||||
@@ -46,13 +49,13 @@ pub fn put_char(
|
||||
let framebuffer =
|
||||
get_framebuffer().expect("Tried to use framebuffer, but framebuffer was not found");
|
||||
|
||||
let start_x = cx * 8;
|
||||
let start_y = cy * 16;
|
||||
let start_x = cx * font_width as u16;
|
||||
let start_y = cy * font_height as u16;
|
||||
|
||||
for (row_idx, &character_byte) in character_array.iter().enumerate() {
|
||||
let mut byte = [bg; 8];
|
||||
let mut byte = vec![bg; font_width];
|
||||
for (i, bit) in byte.iter_mut().enumerate() {
|
||||
*bit = [bg, fg][((character_byte >> (7 - i)) & 0b00000001) as usize]
|
||||
*bit = [bg, fg][((character_byte >> ((font_width - 1) - i)) & 0b00000001) as usize]
|
||||
}
|
||||
|
||||
let row_start_offset = (start_y as usize + row_idx) * framebuffer.pitch
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
pub unsafe fn memset32(dst: *mut u32, val: u32, count: usize) {
|
||||
if cfg!(not(any(target_arch = "x86", target_arch = "x86_64"))) {
|
||||
#[cfg(not(any(target_arch = "x86", target_arch = "x86_64")))]
|
||||
{
|
||||
let mut buf = dst;
|
||||
unsafe {
|
||||
while buf < dst.add(count) {
|
||||
@@ -11,12 +11,15 @@ pub unsafe fn memset32(dst: *mut u32, val: u32, count: usize) {
|
||||
return;
|
||||
}
|
||||
|
||||
core::arch::asm!(
|
||||
"rep stosd",
|
||||
inout("ecx") count => _,
|
||||
inout("edi") dst => _,
|
||||
inout("eax") val => _
|
||||
);
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
core::arch::asm!(
|
||||
"rep stosd",
|
||||
inout("ecx") count => _,
|
||||
inout("edi") dst => _,
|
||||
inout("eax") val => _
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn hcf() -> ! {
|
||||
@@ -25,7 +28,7 @@ pub fn hcf() -> ! {
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
core::arch::asm!("hlt");
|
||||
|
||||
#[cfg(target_arch = "aarch64")]
|
||||
#[cfg(any(target_arch = "aarch64", target_arch = "riscv64"))]
|
||||
core::arch::asm!("wfi");
|
||||
}
|
||||
}
|
||||
|
||||
22
src/main.rs
22
src/main.rs
@@ -1,5 +1,4 @@
|
||||
#![feature(abi_x86_interrupt)]
|
||||
#![feature(naked_functions)]
|
||||
#![feature(abi_x86_interrupt, naked_functions)]
|
||||
#![no_std]
|
||||
#![no_main]
|
||||
|
||||
@@ -35,10 +34,15 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||
// drivers::acpi::init_acpi();
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
drivers::pci::enumerate_pci_bus();
|
||||
|
||||
drivers::fs::vfs::init();
|
||||
|
||||
if let Some(kernel) = KERNEL_REQUEST.get_response().get() {
|
||||
crate::println!("{:X?}", kernel.kernel_file.get().unwrap().gpt_disk_uuid);
|
||||
}
|
||||
|
||||
crate::println!(
|
||||
"Total memory: {}",
|
||||
crate::mem::PHYSICAL_MEMORY_MANAGER
|
||||
@@ -58,6 +62,7 @@ pub struct KernelFeatures {
|
||||
|
||||
impl KernelFeatures {
|
||||
fn update_option(&mut self, option: &str, value: &str) {
|
||||
#[allow(clippy::single_match)]
|
||||
match option {
|
||||
"fat_in_mem" => self.fat_in_mem = value == "true",
|
||||
_ => {}
|
||||
@@ -113,11 +118,14 @@ fn parse_kernel_cmdline() -> KernelFeatures {
|
||||
#[panic_handler]
|
||||
fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||
crate::log_error!("{}", info);
|
||||
let rbp: u64;
|
||||
unsafe {
|
||||
core::arch::asm!("mov {0:r}, rbp", out(reg) rbp);
|
||||
};
|
||||
crate::arch::stack_trace::print_stack_trace(6, rbp);
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
let rbp: u64;
|
||||
unsafe {
|
||||
core::arch::asm!("mov {0:r}, rbp", out(reg) rbp);
|
||||
};
|
||||
crate::arch::stack_trace::print_stack_trace(6, rbp);
|
||||
}
|
||||
|
||||
hcf();
|
||||
}
|
||||
|
||||
@@ -110,10 +110,12 @@ pub fn init_shell() {
|
||||
prompt();
|
||||
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
let kbd_result = crate::drivers::keyboard::init();
|
||||
{
|
||||
let kbd_result = crate::drivers::keyboard::init();
|
||||
|
||||
if kbd_result.is_err() {
|
||||
crate::log_error!("Unable to initialize keyboard! {:?}", kbd_result);
|
||||
if kbd_result.is_err() {
|
||||
crate::log_error!("Unable to initialize keyboard! {:?}", kbd_result);
|
||||
}
|
||||
}
|
||||
|
||||
// #[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
|
||||
@@ -25,6 +25,7 @@ pub struct Console {
|
||||
pub cursor: Cursor,
|
||||
feature_bits: AtomicU8,
|
||||
pub second_buffer: Mutex<Option<crate::drivers::video::Framebuffer>>,
|
||||
// pub lines: Mutex<Vec<String>>,
|
||||
}
|
||||
|
||||
pub struct ConsoleFeatures {
|
||||
@@ -43,6 +44,7 @@ impl Console {
|
||||
cursor: Cursor::new(),
|
||||
feature_bits: AtomicU8::new(0b00000000),
|
||||
second_buffer: Mutex::new(None),
|
||||
// lines: Mutex::new(Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,6 +100,13 @@ impl Console {
|
||||
let rows = framebuffer.height / 16;
|
||||
self.columns.store(columns as u16, Ordering::SeqCst);
|
||||
self.rows.store(rows as u16, Ordering::SeqCst);
|
||||
|
||||
// let mut lines_lock = self.lines.lock();
|
||||
// lines_lock.write().reserve_exact(rows);
|
||||
// for _ in 0..rows {
|
||||
// let string = String::with_capacity(columns);
|
||||
// lines_lock.write().push(string);
|
||||
// }
|
||||
}
|
||||
|
||||
pub fn get_features(&self) -> ConsoleFeatures {
|
||||
@@ -115,6 +124,54 @@ impl Console {
|
||||
};
|
||||
}
|
||||
|
||||
// pub fn puts(&self, string: &str) {
|
||||
// let mut lines = Vec::new();
|
||||
|
||||
// // let mut text_lock = self.text.lock();
|
||||
// // let text = text_lock.write();
|
||||
|
||||
// let mut col_idx = 0_usize;
|
||||
// let mut line_string = String::new();
|
||||
// for ch in string.chars() {
|
||||
// if col_idx > self.columns.load(Ordering::SeqCst) as usize - 1 {
|
||||
// col_idx = 0;
|
||||
// lines.push(line_string.clone());
|
||||
// line_string.clear();
|
||||
// }
|
||||
|
||||
// if ch == '\n' {
|
||||
// col_idx = 0;
|
||||
// lines.push(line_string.clone());
|
||||
// line_string.clear();
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// line_string.push(ch);
|
||||
// col_idx += 1;
|
||||
// }
|
||||
|
||||
// let mut new_lines = [self.lines.lock().read().as_slice(), lines.as_slice()].concat();
|
||||
// if new_lines.len() > self.rows.load(Ordering::SeqCst) as usize {
|
||||
// new_lines.reverse();
|
||||
// new_lines.resize(self.rows.load(Ordering::SeqCst) as usize - 1, String::new());
|
||||
// new_lines.reverse();
|
||||
// }
|
||||
|
||||
// (*self.lines.lock().write()) = new_lines;
|
||||
|
||||
// crate::drivers::video::fill_screen(0x000000, *self.second_buffer.lock().read());
|
||||
// self.cursor.set_pos(0, 0);
|
||||
// for line in self.lines.lock().read() {
|
||||
// self._puts(line);
|
||||
// self.cursor
|
||||
// .set_pos(0, self.cursor.cy.load(Ordering::SeqCst) + 1);
|
||||
// crate::drivers::serial::write_serial('\r');
|
||||
// crate::drivers::serial::write_serial('\n')
|
||||
// }
|
||||
|
||||
// // self._puts(string);
|
||||
// }
|
||||
|
||||
// Uses a stripped down version of ANSI color codes:
|
||||
// \033[FG;BGm
|
||||
pub fn puts(&self, string: &str) {
|
||||
@@ -191,6 +248,7 @@ impl Console {
|
||||
self.cursor.bg.load(Ordering::SeqCst),
|
||||
*self.second_buffer.lock().read(),
|
||||
);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -227,6 +285,15 @@ impl Console {
|
||||
self.cursor.set_color(0xbababa, 0x000000);
|
||||
}
|
||||
|
||||
// pub fn reblit(&self) {
|
||||
// self.cursor.set_pos(0, 0);
|
||||
// self.reblit.store(true, Ordering::SeqCst);
|
||||
|
||||
// self.puts(&self.text.lock().read().trim_end_matches('\n'));
|
||||
|
||||
// self.reblit.store(false, Ordering::SeqCst);
|
||||
// }
|
||||
|
||||
pub fn scroll_console(&self) {
|
||||
let framebuffer_attributes = crate::drivers::video::get_framebuffer()
|
||||
.expect("Tried to scroll console but we have no framebuffer.");
|
||||
@@ -695,17 +762,20 @@ pub fn exec(command: &str) {
|
||||
}
|
||||
|
||||
if command == "test" {
|
||||
let message = "Hello from syscall!\n";
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov rdi, 0x01", // write syscall
|
||||
"mov rsi, 0x01", // stdio (but it doesnt matter)
|
||||
"mov rdx, {0:r}", // pointer
|
||||
"mov rcx, {1:r}", // count
|
||||
"int 0x80",
|
||||
in(reg) message.as_ptr(),
|
||||
in(reg) message.len()
|
||||
);
|
||||
#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
|
||||
{
|
||||
let message = "Hello from syscall!\n";
|
||||
unsafe {
|
||||
core::arch::asm!(
|
||||
"mov rdi, 0x01", // write syscall
|
||||
"mov rsi, 0x01", // stdio (but it doesnt matter)
|
||||
"mov rdx, {0:r}", // pointer
|
||||
"mov rcx, {1:r}", // count
|
||||
"int 0x80",
|
||||
in(reg) message.as_ptr(),
|
||||
in(reg) message.len()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
43
test.log
43
test.log
@@ -1,43 +0,0 @@
|
||||
[ * ] Serial Driver successfully initialized
|
||||
[ ? ] Initialized heap with 4MiB of memory at 0x4E8000
|
||||
[ ? ] Initialized framebuffer mirroring with 3MiB at 0x100000
|
||||
====== MEMORY MAP ======
|
||||
[ 0x0000000000001000..0x0000000000052000 ] Type: BootloaderReclaimable Size: 324KiB
|
||||
[ 0x0000000000053000..0x000000000009F000 ] Type: Usable Size: 304KiB
|
||||
[ 0x000000000009FC00..0x00000000000A0000 ] Type: Reserved Size: 1KiB
|
||||
[ 0x00000000000F0000..0x0000000000100000 ] Type: Reserved Size: 64KiB
|
||||
[ 0x0000000000100000..0x0000000007AC3000 ] Type: Usable Size: 121MiB
|
||||
[ 0x0000000007AC3000..0x0000000007AC4000 ] Type: KernelAndModules Size: 4KiB
|
||||
[ 0x0000000007AC4000..0x0000000007AC5000 ] Type: BootloaderReclaimable Size: 4KiB
|
||||
[ 0x0000000007AC5000..0x0000000007AC8000 ] Type: Usable Size: 12KiB
|
||||
[ 0x0000000007AC8000..0x0000000007AD0000 ] Type: BootloaderReclaimable Size: 32KiB
|
||||
[ 0x0000000007AD0000..0x0000000007AFE000 ] Type: KernelAndModules Size: 184KiB
|
||||
[ 0x0000000007AFE000..0x0000000007B3C000 ] Type: BootloaderReclaimable Size: 248KiB
|
||||
[ 0x0000000007B3C000..0x0000000007F26000 ] Type: Usable Size: 3MiB
|
||||
[ 0x0000000007F26000..0x0000000007FE0000 ] Type: BootloaderReclaimable Size: 744KiB
|
||||
[ 0x0000000007FE0000..0x0000000008000000 ] Type: Reserved Size: 128KiB
|
||||
[ 0x00000000FD000000..0x00000000FD3E8000 ] Type: Framebuffer Size: 3MiB
|
||||
[ 0x00000000FFFC0000..0x0000000100000000 ] Type: Reserved Size: 256KiB
|
||||
[ 0x000000FD00000000..0x0000010000000000 ] Type: Reserved Size: 12GiB
|
||||
Initramfs is located at: 0xffff800007ac3000..0xffff800007ac4000
|
||||
Parsing initramfs fs at 0xffff800007ac3000
|
||||
Squashfs { ptr: 0xffff800007ac3060, superblock: SquashfsSuperblock { magic: 73717368, inode_count: 2, mod_time: 6566B6A1, block_size: 20000, frag_count: 1, compressor: GZIP, block_log: 11, flags: 1CB, id_count: 1, ver_major: 4, ver_minor: 0, root_inode_offset: 20, root_inode_block: 0, _reserved: 0, bytes_used: 120, id_table: 118, xattr_table: FFFFFFFFFFFFFFFF, inode_table: 7B, dir_table: BD, frag_table: F0, export_table: 10A }, data_table: [48, 65, 6C, 6C, 6F, 20, 57, 6F, 72, 6C, 64, 20, 66, 72, 6F, 6D, 20, 49, 6E, 69, 74, 72, 61, 6D, 66, 73, A], inode_table: [40, 80, 2, 0, A4, 1, 0, 0, 0, 0, A1, B6, 66, 65, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1B, 0, 0, 0, 1, 0, ED, 1, 0, 0, 0, 0, A1, B6, 66, 65, 2, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 22, 0, 0, 0, 3, 0, 0, 0], directory_table: [1F, 80, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 2, 0, A, 0, 65, 78, 61, 6D, 70, 6C, 65, 2E, 74, 78, 74, 10, 80, 60, 0, 0, 0, 0, 0, 0, 0, 1B, 0, 0, 1, 0, 0, 0, 0], fragment_table: Some([DE, 0, 0, 0, 0, 0, 0, 0, 10, 80, 0, 0, 0, 0, 0, 0, 0, 0, 20, 0, 0, 0, 0, 0, 0, 0]), export_table: Some([F8, 0, 0, 0, 0, 0, 0, 0, 4, 80, E8, 3, 0, 0]), id_table: [12, 1, 0, 0, 0, 0, 0, 0], xattr_table: None }
|
||||
SquashfsFeatures { uncompressed_inodes: true, uncompressed_data_blocks: true, _reserved: false, uncompressed_fragments: true, unused_fragments: false, fragments_always_present: false, deduplicated_data: true, nfs_table_present: true, uncompressed_xattrs: true, no_xattrs: false, compressor_options_present: false, uncompressed_id_table: false }
|
||||
[1, 0, ED, 1, 0, 0, 0, 0, A1, B6, 66, 65, 2, 0, 0, 0]
|
||||
InodeHeader { file_type: BasicDirectory, _reserved: [0, 0, 0], mtime: 6566B6A1, inode_num: 2 }
|
||||
root_inode: DirectoryInode { block_index: 0, link_count: 2, file_size: 22, block_offset: 0, parent_inode: 3 }
|
||||
====== PCI DEVICES ======
|
||||
Entry 0: Bus: 0 Device: 0 Function: 0 VendorID: 0x8086 DeviceID: 0x1237 ClassCode: 0x06 SubclassCode: 0x00 ProgIF: 0x00
|
||||
Entry 1: Bus: 0 Device: 1 Function: 0 VendorID: 0x8086 DeviceID: 0x7000 ClassCode: 0x06 SubclassCode: 0x01 ProgIF: 0x00
|
||||
Entry 2: Bus: 0 Device: 1 Function: 1 VendorID: 0x8086 DeviceID: 0x7010 ClassCode: 0x01 SubclassCode: 0x01 ProgIF: 0x80
|
||||
Entry 3: Bus: 0 Device: 1 Function: 3 VendorID: 0x8086 DeviceID: 0x7113 ClassCode: 0x06 SubclassCode: 0x80 ProgIF: 0x00
|
||||
Entry 4: Bus: 0 Device: 2 Function: 0 VendorID: 0x1234 DeviceID: 0x1111 ClassCode: 0x03 SubclassCode: 0x00 ProgIF: 0x00
|
||||
Entry 5: Bus: 0 Device: 3 Function: 0 VendorID: 0x8086 DeviceID: 0x100E ClassCode: 0x02 SubclassCode: 0x00 ProgIF: 0x00
|
||||
[ ? ] ATA: Detected 1 drive
|
||||
[ ? ] ATA: Drive 0 has 131072 sectors (64 MB)
|
||||
11, 44, 128
|
||||
[ ? ] WARNING: FAT is not being stored in memory, this feature is experimental and file reads are expected to be slower.
|
||||
Found Fat32 FS
|
||||
[GPTPartitionEntry { partition_type_guid: [40, 115, 42, 193, 31, 248, 210, 17, 186, 75, 0, 160, 201, 62, 201, 59], start_sector: 2048, end_sector: 34815 }, GPTPartitionEntry { partition_type_guid: [175, 61, 198, 15, 131, 132, 114, 71, 142, 121, 61, 105, 216, 71, 125, 228], start_sector: 34816, end_sector: 131038 }]
|
||||
Total memory: 125MiB
|
||||
>
|
||||
Reference in New Issue
Block a user