forked from AbleOS/ableos
Compare commits
62 commits
Author | SHA1 | Date | |
---|---|---|---|
Erin | 23b02b7284 | ||
Erin | e3ab94be87 | ||
Erin | cef19a8fe4 | ||
Erin | e3ce700295 | ||
Erin | 70601eb0c5 | ||
Erin | f8630f6471 | ||
Erin | ea7bbacc56 | ||
Erin | eb93deae25 | ||
Erin | 84d6cc49a1 | ||
Erin | fed31617a8 | ||
Erin | ebd3fe2248 | ||
Erin | 72c4aabbcc | ||
Erin | 6d2a9c74d4 | ||
Erin | 816204cd5b | ||
Erin | b5f75a13f4 | ||
Erin | c241d839f2 | ||
Erin | 7ac54e5ec8 | ||
Erin | d3d52c974a | ||
Erin | 7d21956b0a | ||
Erin | b802732acf | ||
Erin | 8d640b6a9b | ||
Erin | e33014d000 | ||
Erin | ec5b21292e | ||
Erin | eae5979921 | ||
Erin | 3414b40da8 | ||
Asya | 80fd1aaa41 | ||
Asya | 78a5422924 | ||
Asya | 558fc6da59 | ||
Erin | e796a05ddf | ||
Erin | 4c28bfd4d2 | ||
Erin | 0249353a6b | ||
Erin | b68984dc7f | ||
Erin | 58046c3c7b | ||
Erin | e85e5f2ed8 | ||
Erin | 51b084a176 | ||
Erin | 11b492d6ce | ||
Erin | cc714be94e | ||
Erin | 118c3bb3f0 | ||
Erin | a9aa31f087 | ||
Erin | b3954e3533 | ||
Erin | 8fbf7b5a5a | ||
Erin | 7652bbf402 | ||
Erin | eae4891071 | ||
Erin | fa82e49dc3 | ||
Erin | 87fdb2e0bf | ||
Erin | aaf840c5b4 | ||
Erin | 9c2b8573d9 | ||
Erin | b2cf3621b5 | ||
Able | 82a48f5c98 | ||
Erin | 403e410797 | ||
Able | 78a52f7102 | ||
Able | d94e50be39 | ||
Able | e8712d7c39 | ||
Able | 65bb92e02b | ||
Erin | 2114b9a346 | ||
Able | a2d6d7245a | ||
Able | 1e1d004240 | ||
Able | 6e20d33817 | ||
TheOddGarlic | 23c355762c | ||
TheOddGarlic | fe69e31b91 | ||
TheOddGarlic | 346d3a9166 | ||
TheOddGarlic | 643db212ef |
4
.gdbinit
Normal file
4
.gdbinit
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
set history save on
|
||||||
|
file target/x86_64-ableos/debug/ableos
|
||||||
|
target remote localhost:9000
|
||||||
|
tui enable
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1 @@
|
||||||
target/
|
target/
|
||||||
/.idea
|
|
6
.vscode/settings.json
vendored
6
.vscode/settings.json
vendored
|
@ -1,4 +1,6 @@
|
||||||
{
|
{
|
||||||
"rust-analyzer.checkOnSave.allTargets": false,
|
"files.associations": {
|
||||||
"rust-analyzer.showUnlinkedFileNotification": false
|
"stddef.h": "c"
|
||||||
|
},
|
||||||
|
"rust-analyzer.checkOnSave.allTargets": false
|
||||||
}
|
}
|
BIN
AAVMF_CODE.fd
BIN
AAVMF_CODE.fd
Binary file not shown.
|
@ -1,4 +0,0 @@
|
||||||
# Commit style
|
|
||||||
`[SCOPE]: [COMMENT]`
|
|
||||||
SCOPE is what you changed
|
|
||||||
COMMENT is why you changed that
|
|
1206
Cargo.lock
generated
1206
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,3 +1,2 @@
|
||||||
[workspace]
|
[workspace]
|
||||||
resolver = "2"
|
|
||||||
members = ["kernel", "repbuild"]
|
members = ["kernel", "repbuild"]
|
||||||
|
|
59
README.md
59
README.md
|
@ -1,43 +1,26 @@
|
||||||
```
|
# ableOS
|
||||||
TODO
|
![Discord](https://img.shields.io/discord/831368967385120810) ![Code Size](https://img.shields.io/github/languages/code-size/abletheabove/ableos)
|
||||||
- Integrate HBVM
|
## Set up
|
||||||
HBVM also needs full spec compliance
|
Install [Qemu](https://www.qemu.org/)
|
||||||
- Build out the object system
|
|
||||||
- Build or Find an acceptable IDL
|
|
||||||
Short List of potentials
|
|
||||||
- [comline](https://git.ablecorp.us/DOOME1M8Cover/comline)
|
|
||||||
- Work on a styleguide for commits
|
|
||||||
Maybe something allong the lines of
|
|
||||||
[relevant shorthand note] Explination
|
|
||||||
- Build a scheduler for HBVM
|
|
||||||
- Language support on HBVM
|
|
||||||
- HBVM assembler (with IDL support)
|
|
||||||
- HBVM Lisp/s-expr Compiler (Also (with (IDL (support))))
|
|
||||||
- Documentation
|
|
||||||
- Drivers
|
|
||||||
- serial driver
|
|
||||||
- PS/2 mouse driver
|
|
||||||
- PS/2 Keyboard driver
|
|
||||||
- VGA driver
|
|
||||||
- SVGA driver
|
|
||||||
- File system
|
|
||||||
TarFS
|
|
||||||
Pass in a tar file as an initrd and parse it with TarFS
|
|
||||||
- VFS
|
|
||||||
Being (written)[https://git.ablecorp.us/bee/ableos-vfs] by Bee
|
|
||||||
- Disk driver
|
|
||||||
- A ton more
|
|
||||||
```
|
|
||||||
|
|
||||||
# Community
|
> On Windows be sure to add `C:\Program Files\qemu` to your `PATH` variable
|
||||||
[Discord](https://discord.gg/JrKVukDtgs)
|
|
||||||
|
`rustup component add rust-src`
|
||||||
|
|
||||||
|
`rustup component add llvm-tools-preview`
|
||||||
|
|
||||||
|
`cargo install bootimage`
|
||||||
|
|
||||||
|
|
||||||
|
## Running
|
||||||
|
repbuild can be used to run and build docs for able os
|
||||||
|
|
||||||
# Compiling
|
`cargo repbuild doc`
|
||||||
Firstly, I would like to apologize. I am not capable of supporting building on any random machine with any random operating system.
|
`cargo repbuild run`
|
||||||
|
|
||||||
AbleOS very likely builds with `nix-shell` on your operating system.
|
## Testing on real hardware
|
||||||
|
I recommend using an old x86_64 computer
|
||||||
1. `git submodule update --init`
|
* `cargo run --release` to generate a binary image that is bootable
|
||||||
1. `cargo repbuild`
|
* flash it to a USB device via `dd` or balenaEtcher
|
||||||
|
* Remove said USB device and plug into test machine
|
||||||
|
* assure test machine boots from USB devices
|
||||||
|
|
11
arm.sh
11
arm.sh
|
@ -1,11 +0,0 @@
|
||||||
qemu-system-aarch64 -m 1024 -cpu cortex-a57 \
|
|
||||||
-M virt -pflash "AAVMF_CODE.fd" \
|
|
||||||
-device virtio-gpu-pci \
|
|
||||||
-device virtio-mouse-device \
|
|
||||||
-device virtio-keyboard-device \
|
|
||||||
-kernel limine/BOOTX64.EFI
|
|
||||||
# -device virtio-serial-pci \
|
|
||||||
# -drive if=none,file=target/disk.img,id=hd0 \
|
|
||||||
# -device virtio-blk-device,drive=hd0
|
|
||||||
|
|
||||||
# -kernel target/aarch64-virt-ableos/debug/kernel
|
|
50
design.dot
50
design.dot
|
@ -1,50 +0,0 @@
|
||||||
strict graph OS {
|
|
||||||
layout=dot;
|
|
||||||
ModelingSoftware -- GraphicsAPI;
|
|
||||||
ModelingSoftware -- HID;
|
|
||||||
ModelingSoftware -- VFS;
|
|
||||||
|
|
||||||
GameEngine3D -- GraphicsAPI;
|
|
||||||
GameEngine3D -- VFS;
|
|
||||||
GameEngine3D -- AudioSubsystem;
|
|
||||||
GameEngine3D -- HID;
|
|
||||||
GameEngine3D -- Networking;
|
|
||||||
|
|
||||||
Git -- VFS;
|
|
||||||
Git -- Networking;
|
|
||||||
|
|
||||||
ListFile -- VFS;
|
|
||||||
MakeFile -- VFS;
|
|
||||||
|
|
||||||
AudioSubsystem -- AbleOSInterface;
|
|
||||||
GraphicsAPI -- AbleOSInterface;
|
|
||||||
HID -- AbleOSInterface;
|
|
||||||
Networking -- AbleOSInterface;
|
|
||||||
|
|
||||||
FatFileSystemProc -- VFS;
|
|
||||||
NTFSFileSystemProc -- VFS;
|
|
||||||
EXT2 -- VFS;
|
|
||||||
|
|
||||||
FatFileSystemProc -- DriveSystemProc;
|
|
||||||
NTFSFileSystemProc -- DriveSystemProc;
|
|
||||||
EXT2 -- DriveSystemProc;
|
|
||||||
|
|
||||||
|
|
||||||
DriveSystemProc -- AbleOSInterface;
|
|
||||||
|
|
||||||
AbleOSInterface -- DriveSystemProc;
|
|
||||||
AbleOSInterface -- Kernel;
|
|
||||||
|
|
||||||
|
|
||||||
Kernel -- x86HAL;
|
|
||||||
Kernel -- riscvHAL;
|
|
||||||
Kernel -- aarch64HAL;
|
|
||||||
|
|
||||||
x86HAL -- GPU;
|
|
||||||
riscvHAL -- GPU;
|
|
||||||
aarch64HAL -- GPU;
|
|
||||||
|
|
||||||
x86HAL -- SoundCard;
|
|
||||||
riscvHAL -- SoundCard;
|
|
||||||
aarch64HAL -- SoundCard;
|
|
||||||
}
|
|
|
@ -3,27 +3,14 @@ edition = "2021"
|
||||||
name = "kernel"
|
name = "kernel"
|
||||||
version = "0.2.0"
|
version = "0.2.0"
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
limine = { version = "0.1", git = "https://github.com/limine-bootloader/limine-rs" }
|
elfloader = "0.16"
|
||||||
|
error-stack = { version = "0.3", default-features = false}
|
||||||
hbvm = { git = "https://git.ablecorp.us/ableos/holey-bytes" }
|
log = "0.4"
|
||||||
# hbasm = { git = "https://git.ablecorp.us/ableos/holey-bytes" }
|
spin = "0.9"
|
||||||
|
uart_16550 = "0.2"
|
||||||
embedded-graphics = "0.7.1"
|
slab = { version = "0.4", default-features = false }
|
||||||
|
versioning = { git = "https://git.ablecorp.us/able/aos_userland" }
|
||||||
|
|
||||||
error-stack = { version = "0.3", default-features = false }
|
|
||||||
log = "0.4"
|
|
||||||
spin = "0.9"
|
|
||||||
uart_16550 = "0.2"
|
|
||||||
slab = { version = "0.4", default-features = false }
|
|
||||||
xml = { git = "https://git.ablecorp.us/ableos/ableos_userland" }
|
|
||||||
|
|
||||||
clparse = { git = "https://git.ablecorp.us/ableos/ableos_userland", default-features = false }
|
|
||||||
versioning = { git = "https://git.ablecorp.us/ableos/ableos_userland" }
|
|
||||||
able_graphics_library = { git = "https://git.ablecorp.us/ableos/ableos_userland" }
|
|
||||||
hashbrown = "*"
|
|
||||||
|
|
||||||
[dependencies.crossbeam-queue]
|
[dependencies.crossbeam-queue]
|
||||||
version = "0.3"
|
version = "0.3"
|
||||||
|
@ -46,14 +33,10 @@ features = [
|
||||||
"sum",
|
"sum",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
[target.'cfg(target_arch = "x86_64")'.dependencies]
|
||||||
|
limine = { version = "0.1", git = "https://github.com/limine-bootloader/limine-rs" }
|
||||||
x86_64 = "0.14"
|
x86_64 = "0.14"
|
||||||
x2apic = "0.4"
|
x2apic = "0.4"
|
||||||
virtio-drivers = "0.4.0"
|
|
||||||
# rdrand = "*"
|
|
||||||
rdrand = { version = "0.8", default-features = false }
|
|
||||||
|
|
||||||
|
|
||||||
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
[target.'cfg(target_arch = "riscv64")'.dependencies]
|
||||||
sbi = "0.2.0"
|
sbi = "0.2.0"
|
||||||
|
|
33
kernel/data/⑨. バカ
Normal file
33
kernel/data/⑨. バカ
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
|
||||||
|
.//// *(####
|
||||||
|
(####((/, (########,
|
||||||
|
(##%##(###(( ,(#########%#
|
||||||
|
#%%%%###(###((. *((####%%&%%%%#
|
||||||
|
.#####%%%%%####((. *(####%&&&&&%%%%#
|
||||||
|
#(((((((##%%&&%#((/ /#(##%&&%##########
|
||||||
|
/######%%%%%%%(..........(#%%%%%%#####%%#
|
||||||
|
,#%%%%%%#/...... ..............,&&&%%###.
|
||||||
|
,%%#(#(...........................(#&&%%#
|
||||||
|
.###%...............................%%#(#.
|
||||||
|
#%&.................................%#
|
||||||
|
,%&&*.................................&&%(
|
||||||
|
%%#//........./............*/..,......&&&%#
|
||||||
|
.......,..............*,*.,,,,,,.
|
||||||
|
......./....,.........*,,,,.,,,,,,
|
||||||
|
,...../..(###%,.....&*&%###/..,.,,.
|
||||||
|
,,..../%(((#(,.....,%%%%###,..,..,.
|
||||||
|
.,,*.../..,#,.........,(&(,,**.,,,,,
|
||||||
|
,/((..,*..........,,.,,,,,**.,##((
|
||||||
|
/(((%%%,,,,,.,,,,,,,,,,,#/##(,(/
|
||||||
|
.***..(,.#%%%&&#/%,,(%*,,**#
|
||||||
|
**,...,(#%%%%&#%%%#%%%%,,.,,***.
|
||||||
|
.....,*,*#%%%%%###%##%%%&,,*,,....
|
||||||
|
......,*/. (############%%%&&&&#*,,....
|
||||||
|
...,*,..,(((((((((((#((((((((((((&&&#*,
|
||||||
|
,##///////(((((((((((((#///#///(##/.
|
||||||
|
/*/**//... ...**(((((((,. .,*......
|
||||||
|
....... ......,,/*,. ...........
|
||||||
|
,..,,,,...... .. .... ,,....... ..,,,
|
||||||
|
........,.,.,*//////*,......,.
|
||||||
|
...,,,, ,,,,,,
|
||||||
|
|
|
@ -1,15 +0,0 @@
|
||||||
ENTRY(_kernel_start)
|
|
||||||
|
|
||||||
SECTIONS
|
|
||||||
{
|
|
||||||
. = 0xffffffff80000000;
|
|
||||||
.text.boot : { *(.text.boot) }
|
|
||||||
.text : { *(.text) }
|
|
||||||
.data : { *(.data) }
|
|
||||||
.rodata : { *(.rodata) }
|
|
||||||
.bss : { *(.bss) }
|
|
||||||
|
|
||||||
. = ALIGN(8);
|
|
||||||
. = . + 0x4000;
|
|
||||||
LD_STACK_PTR = .;
|
|
||||||
}
|
|
|
@ -47,8 +47,7 @@ SECTIONS
|
||||||
/* Align initial kernel heap to page boundary */
|
/* Align initial kernel heap to page boundary */
|
||||||
. = ALIGN(4K);
|
. = ALIGN(4K);
|
||||||
PROVIDE(_initial_kernel_heap_start = .);
|
PROVIDE(_initial_kernel_heap_start = .);
|
||||||
/* PROVIDE(_initial_kernel_heap_size = 1024 * 1024); */
|
PROVIDE(_initial_kernel_heap_size = 1024 * 1024);
|
||||||
PROVIDE(_initial_kernel_heap_size = 1024 * 4096 * 100);
|
|
||||||
. += _initial_kernel_heap_size;
|
. += _initial_kernel_heap_size;
|
||||||
} :data
|
} :data
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,16 +27,14 @@
|
||||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
use {
|
use core::{
|
||||||
core::{
|
alloc::{GlobalAlloc, Layout},
|
||||||
alloc::{GlobalAlloc, Layout},
|
mem,
|
||||||
mem,
|
ptr::{self, NonNull},
|
||||||
ptr::{self, NonNull},
|
|
||||||
},
|
|
||||||
log::trace,
|
|
||||||
spin::Mutex,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use spin::Mutex;
|
||||||
|
|
||||||
struct Allocator(Mutex<Option<Heap>>);
|
struct Allocator(Mutex<Option<Heap>>);
|
||||||
|
|
||||||
unsafe impl GlobalAlloc for Allocator {
|
unsafe impl GlobalAlloc for Allocator {
|
||||||
|
@ -62,7 +60,7 @@ static ALLOCATOR: Allocator = Allocator(Mutex::new(None));
|
||||||
|
|
||||||
// FIXME: umm is `memory` VirtualAddress or PhysicalAddress? both?
|
// FIXME: umm is `memory` VirtualAddress or PhysicalAddress? both?
|
||||||
pub fn init(memory: *mut u8, memory_size: usize) {
|
pub fn init(memory: *mut u8, memory_size: usize) {
|
||||||
trace!("Initialising kernel heap allocator");
|
log::info!("Initialising kernel heap allocator");
|
||||||
*ALLOCATOR.0.lock() = Some(unsafe { Heap::new(memory, memory_size) });
|
*ALLOCATOR.0.lock() = Some(unsafe { Heap::new(memory, memory_size) });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,28 +144,19 @@ impl Heap {
|
||||||
self.allocated_chunks += chunks_needed;
|
self.allocated_chunks += chunks_needed;
|
||||||
|
|
||||||
let ptr: *mut u8 = unsafe { mem::transmute(header.add(1)) };
|
let ptr: *mut u8 = unsafe { mem::transmute(header.add(1)) };
|
||||||
{
|
// FIXME: zero or scrub memory?
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
trace!("Allocating {:?}", ptr);
|
|
||||||
}
|
|
||||||
// FIXME: zero out memory to prevent leaking data
|
|
||||||
|
|
||||||
assert!(ptr.is_aligned_to(alignment));
|
assert!(ptr.is_aligned_to(alignment));
|
||||||
NonNull::new(ptr)
|
NonNull::new(ptr)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn deallocate(&mut self, ptr: *mut u8) {
|
fn deallocate(&mut self, ptr: *mut u8) {
|
||||||
{
|
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
log::trace!("Deallocating {:?}", ptr);
|
|
||||||
}
|
|
||||||
let header = Self::allocation_header(ptr);
|
let header = Self::allocation_header(ptr);
|
||||||
let start = (header as usize - self.chunks as usize) / CHUNK_SIZE;
|
let start = (header as usize - self.chunks as usize) / CHUNK_SIZE;
|
||||||
assert!(self.bitmap_get(start));
|
assert!(self.bitmap_get(start));
|
||||||
let size = unsafe { (*header).size_in_chunks };
|
let size = unsafe { (*header).size_in_chunks };
|
||||||
self.bitmap_set_range(start, size, false);
|
self.bitmap_set_range(start, size, false);
|
||||||
self.allocated_chunks -= size;
|
self.allocated_chunks -= size;
|
||||||
// FIXME: zero out memory to prevent leaking data
|
// FIXME: zero or scrub memory?
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Finds first hole that can fit an allocation of `size` chunks, returns the start of the
|
/// Finds first hole that can fit an allocation of `size` chunks, returns the start of the
|
||||||
|
@ -249,10 +238,7 @@ impl Heap {
|
||||||
return Some(start_of_free_chunks);
|
return Some(start_of_free_chunks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
{
|
|
||||||
trace!("No first fit found");
|
|
||||||
}
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -339,7 +325,5 @@ unsafe impl Send for Heap {}
|
||||||
|
|
||||||
#[alloc_error_handler]
|
#[alloc_error_handler]
|
||||||
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
|
||||||
log::error!("allocation error: {:?}", layout);
|
crate::arch::sloop()
|
||||||
// Todo: Maybe panic here instead
|
|
||||||
crate::arch::spin_loop()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
use {core::fmt::Write, spin::Mutex};
|
|
||||||
|
|
||||||
const SERIAL_CONSOLE: Mutex<SerialConsole> = Mutex::new(SerialConsole {
|
|
||||||
uart: 0x09000000 as *mut u8,
|
|
||||||
});
|
|
||||||
|
|
||||||
struct SerialConsole {
|
|
||||||
uart: *mut u8,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl core::fmt::Write for SerialConsole {
|
|
||||||
fn write_str(&mut self, s: &str) -> core::fmt::Result {
|
|
||||||
for c in s.chars() {
|
|
||||||
unsafe { *self.uart = c as u8 }
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn log(args: core::fmt::Arguments<'_>) -> core::fmt::Result {
|
|
||||||
SERIAL_CONSOLE.lock().write_fmt(args)
|
|
||||||
}
|
|
|
@ -1,34 +1 @@
|
||||||
use {core::arch::asm, limine::FramebufferRequest};
|
//!
|
||||||
|
|
||||||
pub mod logging;
|
|
||||||
pub use logging::log;
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
|
||||||
|
|
||||||
#[no_mangle]
|
|
||||||
unsafe extern "C" fn _kernel_start() -> ! {
|
|
||||||
crate::logger::init().expect("failed to set logger");
|
|
||||||
log::info!("Initialising AKern {}", crate::VERSION);
|
|
||||||
|
|
||||||
static FB_REQ: FramebufferRequest = FramebufferRequest::new(0);
|
|
||||||
let fb1 = &FB_REQ.get_response().get().unwrap().framebuffers()[0];
|
|
||||||
|
|
||||||
for i in 0..100_usize {
|
|
||||||
let offset = i * fb1.pitch as usize + i * 4;
|
|
||||||
unsafe {
|
|
||||||
*(fb1.address.as_ptr().unwrap().offset(offset as isize) as *mut u32) = 0xFFFFFFFF;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
spin_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn spin_loop() -> ! {
|
|
||||||
loop {
|
|
||||||
unsafe { asm!("wfi") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hardware_random_u64() -> u64 {
|
|
||||||
0
|
|
||||||
}
|
|
|
@ -79,7 +79,7 @@ unsafe extern fn _kernel_start() -> ! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spin loop
|
/// Spin loop
|
||||||
pub fn spin_loop() -> ! {
|
pub fn sloop() -> ! {
|
||||||
loop {
|
loop {
|
||||||
unsafe { asm!("wfi") }
|
unsafe { asm!("wfi") }
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,934 +0,0 @@
|
||||||
use crate::cpu_features;
|
|
||||||
|
|
||||||
use {
|
|
||||||
alloc::vec::Vec,
|
|
||||||
core::{arch::asm, fmt, ops::Deref, slice, str},
|
|
||||||
};
|
|
||||||
#[repr(u32)]
|
|
||||||
pub enum RequestType {
|
|
||||||
BasicInformation = 0x0000_0000,
|
|
||||||
VersionInformation = 0x0000_0001,
|
|
||||||
ThermalPowerManagementInformation = 0x0000_0006,
|
|
||||||
StructuredExtendedInformation = 0x0000_0007,
|
|
||||||
ExtendedFunctionInformation = 0x8000_0000,
|
|
||||||
ExtendedProcessorSignature = 0x8000_0001,
|
|
||||||
BrandString1 = 0x8000_0002,
|
|
||||||
BrandString2 = 0x8000_0003,
|
|
||||||
BrandString3 = 0x8000_0004,
|
|
||||||
// reserved = 0x80000005,
|
|
||||||
CacheLine = 0x8000_0006,
|
|
||||||
TimeStampCounter = 0x8000_0007,
|
|
||||||
PhysicalAddressSize = 0x8000_0008,
|
|
||||||
}
|
|
||||||
#[allow(clippy::similar_names)]
|
|
||||||
pub fn cpuid(code: RequestType) -> (u32, u32, u32, u32) {
|
|
||||||
let eax;
|
|
||||||
let ebx;
|
|
||||||
let ecx;
|
|
||||||
let edx;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
asm!(
|
|
||||||
"movq %rbx, {0:r}",
|
|
||||||
"cpuid",
|
|
||||||
"xchgq %rbx, {0:r}",
|
|
||||||
lateout(reg) ebx,
|
|
||||||
inlateout("eax") code as u32 => eax,
|
|
||||||
inlateout("ecx") 0 => ecx,
|
|
||||||
lateout("edx") edx,
|
|
||||||
options(nostack, preserves_flags, att_syntax),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
(eax, ebx, ecx, edx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The main entrypoint to the CPU information
|
|
||||||
#[cfg(any(target_arch = "x86_64", target_arch = "x86"))]
|
|
||||||
pub fn master() -> Option<Master> {
|
|
||||||
Some(Master::new())
|
|
||||||
}
|
|
||||||
|
|
||||||
// This matches the Intel Architecture guide, with bits 31 -> 0.
|
|
||||||
// The bit positions are inclusive.
|
|
||||||
fn bits_of(val: u32, start_bit: u8, end_bit: u8) -> u32 {
|
|
||||||
let mut silly = 0;
|
|
||||||
|
|
||||||
for _ in start_bit..end_bit + 1 {
|
|
||||||
silly <<= 1;
|
|
||||||
silly |= 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
(val >> start_bit) & silly
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_bytes(v: &u32) -> &[u8] {
|
|
||||||
let start = v as *const u32 as *const u8;
|
|
||||||
// TODO: use u32::BYTES
|
|
||||||
unsafe { slice::from_raw_parts(start, 4) }
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! bit {
|
|
||||||
($reg:ident, {$($idx:expr => $name:ident),+}) => {
|
|
||||||
$(pub fn $name(self) -> bool {
|
|
||||||
((self.$reg >> $idx) & 1) != 0
|
|
||||||
})+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! dump {
|
|
||||||
($me:expr, $f: expr, $sname:expr, {$($name:ident),+}) => {
|
|
||||||
$f.debug_struct($sname)
|
|
||||||
$(.field(stringify!($name), &$me.$name()))+
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! delegate_flag {
|
|
||||||
($item:ident, {$($name:ident),+}) => {
|
|
||||||
$(pub fn $name(&self) -> bool {
|
|
||||||
self.$item.map(|i| i.$name()).unwrap_or(false)
|
|
||||||
})+
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
macro_rules! master_attr_reader {
|
|
||||||
($name:ident, $kind:ty) => {
|
|
||||||
pub fn $name(&self) -> Option<&$kind> {
|
|
||||||
self.$name.as_ref()
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct VersionInformation {
|
|
||||||
eax: u32,
|
|
||||||
ebx: u32,
|
|
||||||
ecx: u32,
|
|
||||||
edx: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl VersionInformation {
|
|
||||||
pub fn new() -> VersionInformation {
|
|
||||||
let (a, b, c, d) = cpuid(RequestType::VersionInformation);
|
|
||||||
VersionInformation {
|
|
||||||
eax: a,
|
|
||||||
ebx: b,
|
|
||||||
ecx: c,
|
|
||||||
edx: d,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn family_id(self) -> u32 {
|
|
||||||
let family_id = bits_of(self.eax, 8, 11);
|
|
||||||
let extended_family_id = bits_of(self.eax, 20, 27);
|
|
||||||
|
|
||||||
if family_id != 0x0F {
|
|
||||||
family_id
|
|
||||||
} else {
|
|
||||||
extended_family_id + family_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn model_id(self) -> u32 {
|
|
||||||
let family_id = self.family_id();
|
|
||||||
let model_id = bits_of(self.eax, 4, 7);
|
|
||||||
let extended_model_id = bits_of(self.eax, 16, 19);
|
|
||||||
|
|
||||||
if family_id == 0x06 || family_id == 0x0F {
|
|
||||||
(extended_model_id << 4) + model_id
|
|
||||||
} else {
|
|
||||||
model_id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn stepping(self) -> u32 {
|
|
||||||
bits_of(self.eax, 0, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn processor_signature(self) -> u32 {
|
|
||||||
self.eax
|
|
||||||
}
|
|
||||||
// TODO: Change return type and move this to a file that has the list
|
|
||||||
pub fn brand_string(self) -> Option<&'static str> {
|
|
||||||
let brand_index = bits_of(self.ebx, 0, 7);
|
|
||||||
let processor_signature = self.processor_signature();
|
|
||||||
|
|
||||||
match brand_index {
|
|
||||||
0x00 => None,
|
|
||||||
0x01 => Some("Intel(R) Celeron(R)"),
|
|
||||||
0x02 => Some("Intel(R) Pentium(R) III"),
|
|
||||||
0x03 => {
|
|
||||||
if processor_signature == 0x06B1 {
|
|
||||||
Some("Intel(R) Celeron(R)")
|
|
||||||
} else {
|
|
||||||
Some("Intel(R) Pentium(R) III Xeon(R)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x04 => Some("Intel(R) Pentium(R) III"),
|
|
||||||
0x06 => Some("Mobile Intel(R) Pentium(R) III-M"),
|
|
||||||
0x07 => Some("Mobile Intel(R) Celeron(R)"),
|
|
||||||
0x08 => Some("Intel(R) Pentium(R) 4"),
|
|
||||||
0x09 => Some("Intel(R) Pentium(R) 4"),
|
|
||||||
0x0A => Some("Intel(R) Celeron(R)"),
|
|
||||||
0x0B => {
|
|
||||||
if processor_signature == 0x0F13 {
|
|
||||||
Some("Intel(R) Xeon(R) MP")
|
|
||||||
} else {
|
|
||||||
Some("Intel(R) Xeon(R)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x0C => Some("Intel(R) Xeon(R) MP"),
|
|
||||||
0x0E => {
|
|
||||||
if processor_signature == 0x0F13 {
|
|
||||||
Some("Intel(R) Xeon(R)")
|
|
||||||
} else {
|
|
||||||
Some("Mobile Intel(R) Pentium(R) 4-M")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
0x0F => Some("Mobile Intel(R) Celeron(R)"),
|
|
||||||
0x11 => Some("Mobile Genuine Intel(R)"),
|
|
||||||
0x12 => Some("Intel(R) Celeron(R) M"),
|
|
||||||
0x13 => Some("Mobile Intel(R) Celeron(R)"),
|
|
||||||
0x14 => Some("Intel(R) Celeron(R)"),
|
|
||||||
0x15 => Some("Mobile Genuine Intel(R)"),
|
|
||||||
0x16 => Some("Intel(R) Pentium(R) M"),
|
|
||||||
0x17 => Some("Mobile Intel(R) Celeron(R)"),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(ecx, {
|
|
||||||
0 => sse3,
|
|
||||||
1 => pclmulqdq,
|
|
||||||
2 => dtes64,
|
|
||||||
3 => monitor,
|
|
||||||
4 => ds_cpl,
|
|
||||||
5 => vmx,
|
|
||||||
6 => smx,
|
|
||||||
7 => eist,
|
|
||||||
8 => tm2,
|
|
||||||
9 => ssse3,
|
|
||||||
10 => cnxt_id,
|
|
||||||
11 => sdbg,
|
|
||||||
12 => fma,
|
|
||||||
13 => cmpxchg16b,
|
|
||||||
14 => xtpr_update_control,
|
|
||||||
15 => pdcm,
|
|
||||||
// 16 - reserved
|
|
||||||
17 => pcid,
|
|
||||||
18 => dca,
|
|
||||||
19 => sse4_1,
|
|
||||||
20 => sse4_2,
|
|
||||||
21 => x2apic,
|
|
||||||
22 => movbe,
|
|
||||||
23 => popcnt,
|
|
||||||
24 => tsc_deadline,
|
|
||||||
25 => aesni,
|
|
||||||
26 => xsave,
|
|
||||||
27 => osxsave,
|
|
||||||
28 => avx,
|
|
||||||
29 => f16c,
|
|
||||||
30 => rdrand
|
|
||||||
// 31 - unused
|
|
||||||
});
|
|
||||||
|
|
||||||
bit!(edx, {
|
|
||||||
0 => fpu,
|
|
||||||
1 => vme,
|
|
||||||
2 => de,
|
|
||||||
3 => pse,
|
|
||||||
4 => tsc,
|
|
||||||
5 => msr,
|
|
||||||
6 => pae,
|
|
||||||
7 => mce,
|
|
||||||
8 => cx8,
|
|
||||||
9 => apic,
|
|
||||||
// 10 - reserved
|
|
||||||
11 => sep,
|
|
||||||
12 => mtrr,
|
|
||||||
13 => pge,
|
|
||||||
14 => mca,
|
|
||||||
15 => cmov,
|
|
||||||
16 => pat,
|
|
||||||
17 => pse_36,
|
|
||||||
18 => psn,
|
|
||||||
19 => clfsh,
|
|
||||||
// 20 - reserved
|
|
||||||
21 => ds,
|
|
||||||
22 => acpi,
|
|
||||||
23 => mmx,
|
|
||||||
24 => fxsr,
|
|
||||||
25 => sse,
|
|
||||||
26 => sse2,
|
|
||||||
27 => ss,
|
|
||||||
28 => htt,
|
|
||||||
29 => tm,
|
|
||||||
// 30 -reserved
|
|
||||||
31 => pbe
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for VersionInformation {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "VersionInformation", {
|
|
||||||
family_id,
|
|
||||||
model_id,
|
|
||||||
stepping,
|
|
||||||
brand_string,
|
|
||||||
sse3,
|
|
||||||
pclmulqdq,
|
|
||||||
dtes64,
|
|
||||||
monitor,
|
|
||||||
ds_cpl,
|
|
||||||
vmx,
|
|
||||||
smx,
|
|
||||||
eist,
|
|
||||||
tm2,
|
|
||||||
ssse3,
|
|
||||||
cnxt_id,
|
|
||||||
sdbg,
|
|
||||||
fma,
|
|
||||||
cmpxchg16b,
|
|
||||||
xtpr_update_control,
|
|
||||||
pdcm,
|
|
||||||
pcid,
|
|
||||||
dca,
|
|
||||||
sse4_1,
|
|
||||||
sse4_2,
|
|
||||||
x2apic,
|
|
||||||
movbe,
|
|
||||||
popcnt,
|
|
||||||
tsc_deadline,
|
|
||||||
aesni,
|
|
||||||
xsave,
|
|
||||||
osxsave,
|
|
||||||
avx,
|
|
||||||
f16c,
|
|
||||||
rdrand,
|
|
||||||
fpu,
|
|
||||||
vme,
|
|
||||||
de,
|
|
||||||
pse,
|
|
||||||
tsc,
|
|
||||||
msr,
|
|
||||||
pae,
|
|
||||||
mce,
|
|
||||||
cx8,
|
|
||||||
apic,
|
|
||||||
sep,
|
|
||||||
mtrr,
|
|
||||||
pge,
|
|
||||||
mca,
|
|
||||||
cmov,
|
|
||||||
pat,
|
|
||||||
pse_36,
|
|
||||||
psn,
|
|
||||||
clfsh,
|
|
||||||
ds,
|
|
||||||
acpi,
|
|
||||||
mmx,
|
|
||||||
fxsr,
|
|
||||||
sse,
|
|
||||||
sse2,
|
|
||||||
ss,
|
|
||||||
htt,
|
|
||||||
tm,
|
|
||||||
pbe
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ExtendedProcessorSignature {
|
|
||||||
ecx: u32,
|
|
||||||
edx: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ExtendedProcessorSignature {
|
|
||||||
fn new() -> ExtendedProcessorSignature {
|
|
||||||
let (_, _, c, d) = cpuid(RequestType::ExtendedProcessorSignature);
|
|
||||||
ExtendedProcessorSignature { ecx: c, edx: d }
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(ecx, {
|
|
||||||
0 => lahf_sahf_in_64_bit,
|
|
||||||
// 1-4 reserved
|
|
||||||
5 => lzcnt,
|
|
||||||
// 6-7 reserved
|
|
||||||
8 => prefetchw
|
|
||||||
// 9-31 reserved
|
|
||||||
});
|
|
||||||
|
|
||||||
bit!(edx, {
|
|
||||||
// 0-10 reserved
|
|
||||||
11 => syscall_sysret_in_64_bit,
|
|
||||||
// 12-19 reserved
|
|
||||||
20 => execute_disable,
|
|
||||||
// 21-25 reserved
|
|
||||||
26 => gigabyte_pages,
|
|
||||||
27 => rdtscp_and_ia32_tsc_aux,
|
|
||||||
// 28 reserved
|
|
||||||
29 => intel_64_bit_architecture
|
|
||||||
// 30-31 reserved
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ExtendedProcessorSignature {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "ThermalPowerManagementInformation", {
|
|
||||||
lahf_sahf_in_64_bit,
|
|
||||||
lzcnt,
|
|
||||||
prefetchw,
|
|
||||||
syscall_sysret_in_64_bit,
|
|
||||||
execute_disable,
|
|
||||||
gigabyte_pages,
|
|
||||||
rdtscp_and_ia32_tsc_aux,
|
|
||||||
intel_64_bit_architecture
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3 calls of 4 registers of 4 bytes
|
|
||||||
const BRAND_STRING_LENGTH: usize = 3 * 4 * 4;
|
|
||||||
|
|
||||||
pub struct BrandString {
|
|
||||||
bytes: [u8; BRAND_STRING_LENGTH],
|
|
||||||
}
|
|
||||||
|
|
||||||
impl BrandString {
|
|
||||||
fn new() -> BrandString {
|
|
||||||
fn append_bytes(a: RequestType, bytes: &mut [u8]) {
|
|
||||||
let (a, b, c, d) = cpuid(a);
|
|
||||||
|
|
||||||
let result_bytes = as_bytes(&a)
|
|
||||||
.iter()
|
|
||||||
.chain(as_bytes(&b).iter())
|
|
||||||
.chain(as_bytes(&c).iter())
|
|
||||||
.chain(as_bytes(&d).iter());
|
|
||||||
|
|
||||||
for (output, input) in bytes.iter_mut().zip(result_bytes) {
|
|
||||||
*output = *input
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut brand_string = BrandString {
|
|
||||||
bytes: [0; BRAND_STRING_LENGTH],
|
|
||||||
};
|
|
||||||
append_bytes(RequestType::BrandString1, &mut brand_string.bytes[0..]);
|
|
||||||
append_bytes(RequestType::BrandString2, &mut brand_string.bytes[16..]);
|
|
||||||
append_bytes(RequestType::BrandString3, &mut brand_string.bytes[32..]);
|
|
||||||
brand_string
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Clone for BrandString {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
let mut bytes = [0; BRAND_STRING_LENGTH];
|
|
||||||
for (d, s) in bytes.iter_mut().zip(self.bytes.iter()) {
|
|
||||||
*d = *s;
|
|
||||||
}
|
|
||||||
BrandString { bytes }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Deref for BrandString {
|
|
||||||
type Target = str;
|
|
||||||
|
|
||||||
fn deref(&self) -> &str {
|
|
||||||
let nul_terminator = self.bytes.iter().position(|&b| b == 0).unwrap_or(0);
|
|
||||||
let usable_bytes = &self.bytes[..nul_terminator];
|
|
||||||
unsafe { str::from_utf8_unchecked(usable_bytes) }.trim()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for BrandString {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
(self as &str).fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for BrandString {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
(self as &str).fmt(f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct ThermalPowerManagementInformation {
|
|
||||||
eax: u32,
|
|
||||||
ebx: u32,
|
|
||||||
ecx: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ThermalPowerManagementInformation {
|
|
||||||
fn new() -> ThermalPowerManagementInformation {
|
|
||||||
let (a, b, c, _) = cpuid(RequestType::ThermalPowerManagementInformation);
|
|
||||||
ThermalPowerManagementInformation {
|
|
||||||
eax: a,
|
|
||||||
ebx: b,
|
|
||||||
ecx: c,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(eax, {
|
|
||||||
0 => digital_temperature_sensor,
|
|
||||||
1 => intel_turbo_boost,
|
|
||||||
2 => arat,
|
|
||||||
// 3 - reserved
|
|
||||||
4 => pln,
|
|
||||||
5 => ecmd,
|
|
||||||
6 => ptm,
|
|
||||||
7 => hwp,
|
|
||||||
8 => hwp_notification,
|
|
||||||
9 => hwp_activity_window,
|
|
||||||
10 => hwp_energy_performance_preference,
|
|
||||||
// 12 - reserved
|
|
||||||
13 => hdc
|
|
||||||
});
|
|
||||||
|
|
||||||
pub fn number_of_interrupt_thresholds(self) -> u32 {
|
|
||||||
bits_of(self.ebx, 0, 3)
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(ecx, {
|
|
||||||
0 => hardware_coordination_feedback,
|
|
||||||
// 1-2 - reserved
|
|
||||||
3 => performance_energy_bias
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for ThermalPowerManagementInformation {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "ThermalPowerManagementInformation", {
|
|
||||||
digital_temperature_sensor,
|
|
||||||
intel_turbo_boost,
|
|
||||||
arat,
|
|
||||||
pln,
|
|
||||||
ecmd,
|
|
||||||
ptm,
|
|
||||||
hwp,
|
|
||||||
hwp_notification,
|
|
||||||
hwp_activity_window,
|
|
||||||
hwp_energy_performance_preference,
|
|
||||||
hdc,
|
|
||||||
|
|
||||||
number_of_interrupt_thresholds,
|
|
||||||
|
|
||||||
hardware_coordination_feedback,
|
|
||||||
performance_energy_bias
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct StructuredExtendedInformation {
|
|
||||||
ebx: u32,
|
|
||||||
ecx: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StructuredExtendedInformation {
|
|
||||||
fn new() -> StructuredExtendedInformation {
|
|
||||||
let (_, b, c, _) = cpuid(RequestType::StructuredExtendedInformation);
|
|
||||||
StructuredExtendedInformation { ebx: b, ecx: c }
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(ebx, {
|
|
||||||
0 => fsgsbase,
|
|
||||||
1 => ia32_tsc_adjust_msr,
|
|
||||||
// 2 - reserved
|
|
||||||
3 => bmi1,
|
|
||||||
4 => hle,
|
|
||||||
5 => avx2,
|
|
||||||
// 6 - reserved
|
|
||||||
7 => smep,
|
|
||||||
8 => bmi2,
|
|
||||||
9 => enhanced_rep_movsb_stosb,
|
|
||||||
10 => invpcid,
|
|
||||||
11 => rtm,
|
|
||||||
12 => pqm,
|
|
||||||
13 => deprecates_fpu_cs_ds,
|
|
||||||
// 14 - reserved
|
|
||||||
15 => pqe,
|
|
||||||
// 16-17 - reserved
|
|
||||||
18 => rdseed,
|
|
||||||
19 => adx,
|
|
||||||
20 => smap,
|
|
||||||
// 21-24 - reserved
|
|
||||||
25 => intel_processor_trace
|
|
||||||
// 26-31 - reserved
|
|
||||||
});
|
|
||||||
|
|
||||||
bit!(ecx, {
|
|
||||||
0 => prefetchwt1
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for StructuredExtendedInformation {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "StructuredExtendedInformation", {
|
|
||||||
fsgsbase,
|
|
||||||
ia32_tsc_adjust_msr,
|
|
||||||
bmi1,
|
|
||||||
hle,
|
|
||||||
avx2,
|
|
||||||
smep,
|
|
||||||
bmi2,
|
|
||||||
enhanced_rep_movsb_stosb,
|
|
||||||
invpcid,
|
|
||||||
rtm,
|
|
||||||
pqm,
|
|
||||||
deprecates_fpu_cs_ds,
|
|
||||||
pqe,
|
|
||||||
rdseed,
|
|
||||||
adx,
|
|
||||||
smap,
|
|
||||||
intel_processor_trace,
|
|
||||||
prefetchwt1
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
|
||||||
pub enum CacheLineAssociativity {
|
|
||||||
Disabled,
|
|
||||||
DirectMapped,
|
|
||||||
TwoWay,
|
|
||||||
FourWay,
|
|
||||||
EightWay,
|
|
||||||
SixteenWay,
|
|
||||||
Full,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct CacheLine(u32);
|
|
||||||
|
|
||||||
impl CacheLine {
|
|
||||||
fn new() -> CacheLine {
|
|
||||||
let (_, _, c, _) = cpuid(RequestType::CacheLine);
|
|
||||||
CacheLine(c)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cache_line_size(self) -> u32 {
|
|
||||||
bits_of(self.0, 0, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn l2_associativity(self) -> Option<CacheLineAssociativity> {
|
|
||||||
match bits_of(self.0, 12, 15) {
|
|
||||||
0x00 => Some(CacheLineAssociativity::Disabled),
|
|
||||||
0x01 => Some(CacheLineAssociativity::DirectMapped),
|
|
||||||
0x02 => Some(CacheLineAssociativity::TwoWay),
|
|
||||||
0x04 => Some(CacheLineAssociativity::FourWay),
|
|
||||||
0x06 => Some(CacheLineAssociativity::EightWay),
|
|
||||||
0x08 => Some(CacheLineAssociativity::SixteenWay),
|
|
||||||
0x0F => Some(CacheLineAssociativity::Full),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn cache_size(self) -> u32 {
|
|
||||||
bits_of(self.0, 16, 31)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for CacheLine {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "CacheLine", {
|
|
||||||
cache_line_size,
|
|
||||||
l2_associativity,
|
|
||||||
cache_size
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct TimeStampCounter {
|
|
||||||
edx: u32,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl TimeStampCounter {
|
|
||||||
fn new() -> TimeStampCounter {
|
|
||||||
let (_, _, _, d) = cpuid(RequestType::TimeStampCounter);
|
|
||||||
TimeStampCounter { edx: d }
|
|
||||||
}
|
|
||||||
|
|
||||||
bit!(edx, {
|
|
||||||
// 0-7 - reserved
|
|
||||||
8 => invariant_tsc
|
|
||||||
// 9-31 - reserved
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for TimeStampCounter {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "TimeStampCounter", { invariant_tsc })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone)]
|
|
||||||
pub struct PhysicalAddressSize(u32);
|
|
||||||
|
|
||||||
impl PhysicalAddressSize {
|
|
||||||
fn new() -> PhysicalAddressSize {
|
|
||||||
let (a, _, _, _) = cpuid(RequestType::PhysicalAddressSize);
|
|
||||||
PhysicalAddressSize(a)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn physical_address_bits(self) -> u32 {
|
|
||||||
bits_of(self.0, 0, 7)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn linear_address_bits(self) -> u32 {
|
|
||||||
bits_of(self.0, 8, 15)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Debug for PhysicalAddressSize {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
dump!(self, f, "PhysicalAddressSize", {
|
|
||||||
physical_address_bits,
|
|
||||||
linear_address_bits
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information about the currently running processor
|
|
||||||
///
|
|
||||||
/// Feature flags match the feature mnemonic listed in the Intel
|
|
||||||
/// Instruction Set Reference. This struct provides a facade for flags
|
|
||||||
/// so the consumer doesn't need to worry about which particular CPUID
|
|
||||||
/// leaf provides the information.
|
|
||||||
///
|
|
||||||
/// For data beyond simple feature flags, you will need to retrieve
|
|
||||||
/// the nested struct and call the appropriate methods on it.
|
|
||||||
#[derive(Debug, Clone)]
|
|
||||||
pub struct Master {
|
|
||||||
// TODO: Rename struct
|
|
||||||
version_information: Option<VersionInformation>,
|
|
||||||
thermal_power_management_information: Option<ThermalPowerManagementInformation>,
|
|
||||||
structured_extended_information: Option<StructuredExtendedInformation>,
|
|
||||||
extended_processor_signature: Option<ExtendedProcessorSignature>,
|
|
||||||
brand_string: Option<BrandString>,
|
|
||||||
cache_line: Option<CacheLine>,
|
|
||||||
time_stamp_counter: Option<TimeStampCounter>,
|
|
||||||
physical_address_size: Option<PhysicalAddressSize>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Master {
|
|
||||||
pub fn new() -> Master {
|
|
||||||
fn when_supported<F, T>(max: u32, kind: RequestType, then: F) -> Option<T>
|
|
||||||
where
|
|
||||||
F: FnOnce() -> T,
|
|
||||||
{
|
|
||||||
if max >= kind as u32 {
|
|
||||||
Some(then())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let (max_value, _, _, _) = cpuid(RequestType::BasicInformation);
|
|
||||||
|
|
||||||
let vi = when_supported(max_value, RequestType::VersionInformation, || {
|
|
||||||
VersionInformation::new()
|
|
||||||
});
|
|
||||||
let tpm = when_supported(
|
|
||||||
max_value,
|
|
||||||
RequestType::ThermalPowerManagementInformation,
|
|
||||||
|| ThermalPowerManagementInformation::new(),
|
|
||||||
);
|
|
||||||
let sei = when_supported(
|
|
||||||
max_value,
|
|
||||||
RequestType::StructuredExtendedInformation,
|
|
||||||
|| StructuredExtendedInformation::new(),
|
|
||||||
);
|
|
||||||
|
|
||||||
// Extended information
|
|
||||||
|
|
||||||
let (max_value, _, _, _) = cpuid(RequestType::ExtendedFunctionInformation);
|
|
||||||
|
|
||||||
let eps = when_supported(max_value, RequestType::ExtendedProcessorSignature, || {
|
|
||||||
ExtendedProcessorSignature::new()
|
|
||||||
});
|
|
||||||
let brand_string =
|
|
||||||
when_supported(max_value, RequestType::BrandString3, || BrandString::new());
|
|
||||||
let cache_line = when_supported(max_value, RequestType::CacheLine, || CacheLine::new());
|
|
||||||
let tsc = when_supported(max_value, RequestType::TimeStampCounter, || {
|
|
||||||
TimeStampCounter::new()
|
|
||||||
});
|
|
||||||
let pas = when_supported(max_value, RequestType::PhysicalAddressSize, || {
|
|
||||||
PhysicalAddressSize::new()
|
|
||||||
});
|
|
||||||
|
|
||||||
Master {
|
|
||||||
version_information: vi,
|
|
||||||
thermal_power_management_information: tpm,
|
|
||||||
structured_extended_information: sei,
|
|
||||||
extended_processor_signature: eps,
|
|
||||||
brand_string,
|
|
||||||
cache_line,
|
|
||||||
time_stamp_counter: tsc,
|
|
||||||
physical_address_size: pas,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: Macroify this and also include all of the cpu features from self
|
|
||||||
pub fn features(&self) -> Vec<(&str, bool)> {
|
|
||||||
let mut fv = Vec::new();
|
|
||||||
cpu_features!(self, fv);
|
|
||||||
return fv;
|
|
||||||
}
|
|
||||||
|
|
||||||
master_attr_reader!(version_information, VersionInformation);
|
|
||||||
master_attr_reader!(
|
|
||||||
thermal_power_management_information,
|
|
||||||
ThermalPowerManagementInformation
|
|
||||||
);
|
|
||||||
master_attr_reader!(
|
|
||||||
structured_extended_information,
|
|
||||||
StructuredExtendedInformation
|
|
||||||
);
|
|
||||||
master_attr_reader!(extended_processor_signature, ExtendedProcessorSignature);
|
|
||||||
master_attr_reader!(cache_line, CacheLine);
|
|
||||||
master_attr_reader!(time_stamp_counter, TimeStampCounter);
|
|
||||||
master_attr_reader!(physical_address_size, PhysicalAddressSize);
|
|
||||||
|
|
||||||
pub fn brand_string(&self) -> Option<&str> {
|
|
||||||
self.brand_string
|
|
||||||
.as_ref()
|
|
||||||
.map(|bs| bs as &str)
|
|
||||||
.or(self.version_information.and_then(|vi| vi.brand_string()))
|
|
||||||
}
|
|
||||||
|
|
||||||
delegate_flag!(version_information, {
|
|
||||||
sse3,
|
|
||||||
pclmulqdq,
|
|
||||||
dtes64,
|
|
||||||
monitor,
|
|
||||||
ds_cpl,
|
|
||||||
vmx,
|
|
||||||
smx,
|
|
||||||
eist,
|
|
||||||
tm2,
|
|
||||||
ssse3,
|
|
||||||
cnxt_id,
|
|
||||||
sdbg,
|
|
||||||
fma,
|
|
||||||
cmpxchg16b,
|
|
||||||
xtpr_update_control,
|
|
||||||
pdcm,
|
|
||||||
pcid,
|
|
||||||
dca,
|
|
||||||
sse4_1,
|
|
||||||
sse4_2,
|
|
||||||
x2apic,
|
|
||||||
movbe,
|
|
||||||
popcnt,
|
|
||||||
tsc_deadline,
|
|
||||||
aesni,
|
|
||||||
xsave,
|
|
||||||
osxsave,
|
|
||||||
avx,
|
|
||||||
f16c,
|
|
||||||
rdrand,
|
|
||||||
fpu,
|
|
||||||
vme,
|
|
||||||
de,
|
|
||||||
pse,
|
|
||||||
tsc,
|
|
||||||
msr,
|
|
||||||
pae,
|
|
||||||
mce,
|
|
||||||
cx8,
|
|
||||||
apic,
|
|
||||||
sep,
|
|
||||||
mtrr,
|
|
||||||
pge,
|
|
||||||
mca,
|
|
||||||
cmov,
|
|
||||||
pat,
|
|
||||||
pse_36,
|
|
||||||
psn,
|
|
||||||
clfsh,
|
|
||||||
ds,
|
|
||||||
acpi,
|
|
||||||
mmx,
|
|
||||||
fxsr,
|
|
||||||
sse,
|
|
||||||
sse2,
|
|
||||||
ss,
|
|
||||||
htt,
|
|
||||||
tm,
|
|
||||||
pbe
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate_flag!(thermal_power_management_information, {
|
|
||||||
digital_temperature_sensor,
|
|
||||||
intel_turbo_boost,
|
|
||||||
arat,
|
|
||||||
pln,
|
|
||||||
ecmd,
|
|
||||||
ptm,
|
|
||||||
hwp,
|
|
||||||
hwp_notification,
|
|
||||||
hwp_activity_window,
|
|
||||||
hwp_energy_performance_preference,
|
|
||||||
hdc,
|
|
||||||
hardware_coordination_feedback,
|
|
||||||
performance_energy_bias
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate_flag!(structured_extended_information, {
|
|
||||||
fsgsbase,
|
|
||||||
ia32_tsc_adjust_msr,
|
|
||||||
bmi1,
|
|
||||||
hle,
|
|
||||||
avx2,
|
|
||||||
smep,
|
|
||||||
bmi2,
|
|
||||||
enhanced_rep_movsb_stosb,
|
|
||||||
invpcid,
|
|
||||||
rtm,
|
|
||||||
pqm,
|
|
||||||
deprecates_fpu_cs_ds,
|
|
||||||
pqe,
|
|
||||||
rdseed,
|
|
||||||
adx,
|
|
||||||
smap,
|
|
||||||
intel_processor_trace,
|
|
||||||
prefetchwt1
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate_flag!(extended_processor_signature, {
|
|
||||||
lahf_sahf_in_64_bit,
|
|
||||||
lzcnt,
|
|
||||||
prefetchw,
|
|
||||||
syscall_sysret_in_64_bit,
|
|
||||||
execute_disable,
|
|
||||||
gigabyte_pages,
|
|
||||||
rdtscp_and_ia32_tsc_aux,
|
|
||||||
intel_64_bit_architecture
|
|
||||||
});
|
|
||||||
|
|
||||||
delegate_flag!(time_stamp_counter, { invariant_tsc });
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
cfg_if! {
|
|
||||||
if #[cfg(any(target_arch = "x86_64", target_arch = "x86"))] {
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn basic_genuine_intel() {
|
|
||||||
let (_, b, c, d) = cpuid(RequestType::BasicInformation);
|
|
||||||
|
|
||||||
assert_eq!(b"Genu", as_bytes(&b));
|
|
||||||
assert_eq!(b"ntel", as_bytes(&c));
|
|
||||||
assert_eq!(b"ineI", as_bytes(&d));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn brand_string_contains_intel() {
|
|
||||||
assert!(master().unwrap().brand_string().unwrap().contains("Intel(R)"))
|
|
||||||
}
|
|
||||||
|
|
||||||
} else {}
|
|
||||||
}
|
|
||||||
*/
|
|
|
@ -1,67 +0,0 @@
|
||||||
use {
|
|
||||||
crate::{
|
|
||||||
arch::{pci, x86_64::cpuid},
|
|
||||||
device_tree::DeviceTree,
|
|
||||||
kmain::DEVICE_TREE,
|
|
||||||
},
|
|
||||||
limine::SmpRequest,
|
|
||||||
xml::XMLElement,
|
|
||||||
};
|
|
||||||
|
|
||||||
fn collect_cpu_info(device_tree: &mut DeviceTree) {
|
|
||||||
use crate::alloc::string::ToString;
|
|
||||||
|
|
||||||
static SMP: SmpRequest = SmpRequest::new(0);
|
|
||||||
let smp_response = SMP.get_response().get().unwrap();
|
|
||||||
|
|
||||||
let mut cpu = XMLElement::new("cpu");
|
|
||||||
|
|
||||||
// Get the amount of cores on the CPU
|
|
||||||
let core_count = smp_response.cpu_count.to_string();
|
|
||||||
cpu.set_attribute("core count", core_count);
|
|
||||||
for x in 0..smp_response.cpu_count {
|
|
||||||
let core_name = alloc::format!("core_{}", x);
|
|
||||||
let core = XMLElement::new(core_name);
|
|
||||||
cpu.set_child(core);
|
|
||||||
}
|
|
||||||
|
|
||||||
let cpu_info = cpuid::master().unwrap();
|
|
||||||
|
|
||||||
let brand_string = cpu_info.brand_string().unwrap_or("Unknown").to_string();
|
|
||||||
cpu.set_attribute("brand string", brand_string);
|
|
||||||
|
|
||||||
cpu.set_attribute("speed", "unknown");
|
|
||||||
|
|
||||||
// Get CPU features and add them to the device tree entry.
|
|
||||||
let mut cpu_features = XMLElement::new("CPU Features");
|
|
||||||
for (feature_key, feature_enabled) in cpu_info.features() {
|
|
||||||
cpu_features.set_attribute(feature_key, feature_enabled.to_string());
|
|
||||||
}
|
|
||||||
cpu.set_child(cpu_features);
|
|
||||||
|
|
||||||
// CPU temperature
|
|
||||||
if cpu_info.digital_temperature_sensor() {
|
|
||||||
let mut temperature_child = XMLElement::new("Temperature");
|
|
||||||
|
|
||||||
temperature_child.set_attribute("degrees", "Unknown");
|
|
||||||
cpu.set_child(temperature_child);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add CPU to device tree
|
|
||||||
let cpus = device_tree.devices.get_mut("CPUs").unwrap();
|
|
||||||
cpus.push(cpu);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn collect_device_info() {
|
|
||||||
// Lock device tree
|
|
||||||
unsafe {
|
|
||||||
DEVICE_TREE.force_unlock();
|
|
||||||
}
|
|
||||||
let device_tree = &mut DEVICE_TREE.lock();
|
|
||||||
|
|
||||||
// Generate device tree from PCI enumeration.
|
|
||||||
pci::init(device_tree);
|
|
||||||
|
|
||||||
// Collect CPU info and add it to the device tree
|
|
||||||
collect_cpu_info(device_tree);
|
|
||||||
}
|
|
|
@ -1,33 +1,34 @@
|
||||||
use {
|
use spin::Lazy;
|
||||||
spin::Lazy,
|
use x86_64::{
|
||||||
x86_64::{
|
structures::{
|
||||||
structures::{
|
gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
|
||||||
gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector},
|
tss::TaskStateSegment,
|
||||||
tss::TaskStateSegment,
|
|
||||||
},
|
|
||||||
VirtAddr,
|
|
||||||
},
|
},
|
||||||
|
VirtAddr,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const DOUBLE_FAULT_IX: u16 = 0;
|
pub const DOUBLE_FAULT_IX: u16 = 0;
|
||||||
|
|
||||||
pub unsafe fn init() {
|
pub unsafe fn init() {
|
||||||
use x86_64::instructions::{
|
use x86_64::instructions::segmentation::{Segment, CS, DS, ES, SS};
|
||||||
segmentation::{Segment, CS, SS},
|
use x86_64::instructions::tables::load_tss;
|
||||||
tables::load_tss,
|
|
||||||
};
|
|
||||||
|
|
||||||
log::trace!("Initialising GDT");
|
log::info!("Initialising GDT");
|
||||||
GDT.0.load();
|
GDT.0.load();
|
||||||
CS::set_reg(GDT.1.kcode);
|
CS::set_reg(GDT.1.kcode);
|
||||||
|
DS::set_reg(GDT.1.kdata);
|
||||||
|
ES::set_reg(GDT.1.kdata);
|
||||||
SS::set_reg(GDT.1.kdata);
|
SS::set_reg(GDT.1.kdata);
|
||||||
load_tss(GDT.1.tss);
|
load_tss(GDT.1.tss);
|
||||||
|
init_syscalls(&GDT.1);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Selectors {
|
struct Selectors {
|
||||||
kcode: SegmentSelector,
|
kcode: SegmentSelector,
|
||||||
kdata: SegmentSelector,
|
kdata: SegmentSelector,
|
||||||
tss: SegmentSelector,
|
tss: SegmentSelector,
|
||||||
|
udata: SegmentSelector,
|
||||||
|
ucode: SegmentSelector,
|
||||||
}
|
}
|
||||||
|
|
||||||
static TSS: Lazy<TaskStateSegment> = Lazy::new(|| {
|
static TSS: Lazy<TaskStateSegment> = Lazy::new(|| {
|
||||||
|
@ -49,7 +50,27 @@ static GDT: Lazy<(GlobalDescriptorTable, Selectors)> = Lazy::new(|| {
|
||||||
let sels = Selectors {
|
let sels = Selectors {
|
||||||
kcode: gdt.add_entry(Descriptor::kernel_code_segment()),
|
kcode: gdt.add_entry(Descriptor::kernel_code_segment()),
|
||||||
kdata: gdt.add_entry(Descriptor::kernel_data_segment()),
|
kdata: gdt.add_entry(Descriptor::kernel_data_segment()),
|
||||||
tss: gdt.add_entry(Descriptor::tss_segment(&TSS)),
|
tss: gdt.add_entry(Descriptor::tss_segment(&TSS)),
|
||||||
|
udata: gdt.add_entry(Descriptor::user_data_segment()),
|
||||||
|
ucode: gdt.add_entry(Descriptor::user_code_segment()),
|
||||||
};
|
};
|
||||||
(gdt, sels)
|
(gdt, sels)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/// Initialize syscalls
|
||||||
|
unsafe fn init_syscalls(sls: &Selectors) {
|
||||||
|
use x86_64::registers::{
|
||||||
|
model_specific::{Efer, EferFlags, LStar, SFMask, Star},
|
||||||
|
rflags::RFlags,
|
||||||
|
};
|
||||||
|
|
||||||
|
Star::write(sls.ucode, sls.udata, sls.kcode, sls.kdata);
|
||||||
|
SFMask::write(RFlags::from_bits(0x200).expect("Invalid RFlags for SFMask"));
|
||||||
|
LStar::write(VirtAddr::from_ptr(handle_syscall as *const ()));
|
||||||
|
Efer::update(|x| *x |= EferFlags::SYSTEM_CALL_EXTENSIONS);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn handle_syscall(a: u64) -> ! {
|
||||||
|
log::info!("called syscall {a}");
|
||||||
|
todo!("Return from syscall");
|
||||||
|
}
|
||||||
|
|
|
@ -1,69 +0,0 @@
|
||||||
use {limine::NonNullPtr};
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{kmain::DEVICE_TREE},
|
|
||||||
able_graphics_library::raw_pixel::Display,
|
|
||||||
embedded_graphics::{pixelcolor::Rgb888, prelude::*},
|
|
||||||
limine::{Framebuffer, FramebufferRequest},
|
|
||||||
spin::{Lazy, Mutex},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static DISPLAY: Lazy<Mutex<Display>> = Lazy::new(|| {
|
|
||||||
static FB_REQ: FramebufferRequest = FramebufferRequest::new(0);
|
|
||||||
let fb1: &NonNullPtr<Framebuffer> = &FB_REQ.get_response().get().unwrap().framebuffers()[0];
|
|
||||||
|
|
||||||
{
|
|
||||||
use crate::alloc::string::ToString;
|
|
||||||
let mut dt = DEVICE_TREE.lock();
|
|
||||||
let mut disp = xml::XMLElement::new("display_0");
|
|
||||||
|
|
||||||
disp.set_attribute("width", fb1.width);
|
|
||||||
disp.set_attribute("height", fb1.height);
|
|
||||||
disp.set_attribute("bits per pixel", fb1.bpp);
|
|
||||||
dt.devices.insert("Displays".to_string(), alloc::vec![disp]);
|
|
||||||
}
|
|
||||||
let _size: usize = (fb1.width * fb1.height).try_into().unwrap();
|
|
||||||
let back_buffer: alloc::vec::Vec<u32> = alloc::vec![0; 800*600];
|
|
||||||
|
|
||||||
let m = Mutex::new(Display {
|
|
||||||
fb: fb1.address.as_ptr().unwrap().cast(),
|
|
||||||
// bb: fb1.address.as_ptr().unwrap().cast(),
|
|
||||||
bb: back_buffer.as_slice().as_ptr() as *mut u32,
|
|
||||||
size: Size::new(fb1.width as u32, fb1.height as u32),
|
|
||||||
color: Rgb888::WHITE,
|
|
||||||
});
|
|
||||||
log::info!("Graphics initialised");
|
|
||||||
m
|
|
||||||
});
|
|
||||||
|
|
||||||
pub fn init() {
|
|
||||||
Lazy::force(&DISPLAY);
|
|
||||||
}
|
|
||||||
// pub fn virtio_gpu<T: Transport>(transport: T) {
|
|
||||||
// let mut gpu = VirtIOGpu::<AbleosHal, T>::new(transport).expect("failed to create gpu driver");
|
|
||||||
// let (width, height) = gpu.resolution().expect("failed to get resolution");
|
|
||||||
// let width = width as usize;
|
|
||||||
// let height = height as usize;
|
|
||||||
// log::info!("GPU resolution is {}x{}", width, height);
|
|
||||||
// let fb = gpu.setup_framebuffer().expect("failed to get fb");
|
|
||||||
// for y in 0..height {
|
|
||||||
// for x in 0..width {
|
|
||||||
// let idx = (y * width + x) * 4;
|
|
||||||
// fb[idx] = x as u8;
|
|
||||||
// fb[idx + 1] = y as u8;
|
|
||||||
// fb[idx + 2] = (x + y) as u8;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// gpu.flush().expect("failed to flush");
|
|
||||||
// //delay some time
|
|
||||||
// log::info!("virtio-gpu show graphics....");
|
|
||||||
// for _ in 0..100000 {
|
|
||||||
// for _ in 0..100000 {
|
|
||||||
// unsafe {
|
|
||||||
// core::arch::asm!("nop");
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// log::info!("virtio-gpu test finished");
|
|
||||||
// }
|
|
|
@ -1,13 +1,9 @@
|
||||||
// TODO: Turn apic keyboard interrupt into a standard ipc message
|
use spin::{Lazy, Mutex};
|
||||||
use {
|
use x2apic::lapic::{LocalApic, LocalApicBuilder};
|
||||||
log::trace,
|
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode};
|
||||||
spin::{Lazy, Mutex},
|
|
||||||
x2apic::lapic::{LocalApic, LocalApicBuilder},
|
|
||||||
x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, PageFaultErrorCode},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub unsafe fn init() {
|
pub unsafe fn init() {
|
||||||
trace!("Initialising IDT");
|
log::info!("Initialising IDT");
|
||||||
IDT.load();
|
IDT.load();
|
||||||
Lazy::force(&LAPIC);
|
Lazy::force(&LAPIC);
|
||||||
x86_64::instructions::interrupts::enable();
|
x86_64::instructions::interrupts::enable();
|
||||||
|
@ -16,12 +12,11 @@ pub unsafe fn init() {
|
||||||
#[repr(u8)]
|
#[repr(u8)]
|
||||||
enum Interrupt {
|
enum Interrupt {
|
||||||
Timer = 32,
|
Timer = 32,
|
||||||
|
|
||||||
ApicErr = u8::MAX - 1,
|
ApicErr = u8::MAX - 1,
|
||||||
Spurious = u8::MAX,
|
Spurious = u8::MAX,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) static LAPIC: Lazy<Mutex<LocalApic>> = Lazy::new(|| {
|
static LAPIC: Lazy<Mutex<LocalApic>> = Lazy::new(|| {
|
||||||
let mut lapic = LocalApicBuilder::new()
|
let mut lapic = LocalApicBuilder::new()
|
||||||
.timer_vector(Interrupt::Timer as usize)
|
.timer_vector(Interrupt::Timer as usize)
|
||||||
.error_vector(Interrupt::ApicErr as usize)
|
.error_vector(Interrupt::ApicErr as usize)
|
||||||
|
@ -44,11 +39,7 @@ static IDT: Lazy<InterruptDescriptorTable> = Lazy::new(|| {
|
||||||
.set_stack_index(super::gdt::DOUBLE_FAULT_IX);
|
.set_stack_index(super::gdt::DOUBLE_FAULT_IX);
|
||||||
}
|
}
|
||||||
idt.page_fault.set_handler_fn(page_fault);
|
idt.page_fault.set_handler_fn(page_fault);
|
||||||
|
|
||||||
idt[Interrupt::ApicErr as usize].set_handler_fn(apic_err);
|
|
||||||
idt[Interrupt::Spurious as usize].set_handler_fn(spurious);
|
|
||||||
idt[Interrupt::Timer as usize].set_handler_fn(timer);
|
idt[Interrupt::Timer as usize].set_handler_fn(timer);
|
||||||
|
|
||||||
idt
|
idt
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -63,9 +54,7 @@ extern "x86-interrupt" fn page_fault(
|
||||||
panic!("Page fault ({error_code:?}): {stack_frame:?}")
|
panic!("Page fault ({error_code:?}): {stack_frame:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
extern "x86-interrupt" fn timer(_isf: InterruptStackFrame) {
|
extern "x86-interrupt" fn timer(_: InterruptStackFrame) {
|
||||||
// TODO: Pause the running program then schedule the next program
|
|
||||||
|
|
||||||
unsafe { LAPIC.lock().end_of_interrupt() };
|
unsafe { LAPIC.lock().end_of_interrupt() };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,11 @@
|
||||||
//! Logging (as in terms of console / serial output)
|
//! Logging (as in terms of console / serial output)
|
||||||
#![allow(deprecated)]
|
|
||||||
|
|
||||||
use {
|
use core::fmt::Write;
|
||||||
core::fmt::Write,
|
use limine::{LimineTerminalRequest, LimineTerminalResponse};
|
||||||
limine::{TerminalRequest, TerminalResponse},
|
use spin::{Lazy, Mutex};
|
||||||
spin::{Lazy, Mutex},
|
use uart_16550::SerialPort;
|
||||||
uart_16550::SerialPort,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub static SERIAL_CONSOLE: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3F8) });
|
static SERIAL_CONSOLE: Mutex<SerialPort> = Mutex::new(unsafe { SerialPort::new(0x3f8) });
|
||||||
static TERMINAL_LOGGER: Lazy<Mutex<TermLogger>> = Lazy::new(|| Mutex::new(TermLogger::new()));
|
static TERMINAL_LOGGER: Lazy<Mutex<TermLogger>> = Lazy::new(|| Mutex::new(TermLogger::new()));
|
||||||
|
|
||||||
pub fn init() {
|
pub fn init() {
|
||||||
|
@ -18,16 +15,16 @@ pub fn init() {
|
||||||
|
|
||||||
pub fn log(args: core::fmt::Arguments<'_>) -> core::fmt::Result {
|
pub fn log(args: core::fmt::Arguments<'_>) -> core::fmt::Result {
|
||||||
x86_64::instructions::interrupts::without_interrupts(|| {
|
x86_64::instructions::interrupts::without_interrupts(|| {
|
||||||
// TERMINAL_LOGGER.lock().write_fmt(args)?;
|
TERMINAL_LOGGER.lock().write_fmt(args)?;
|
||||||
SERIAL_CONSOLE.lock().write_fmt(args)
|
SERIAL_CONSOLE.lock().write_fmt(args)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TermLogger(&'static TerminalResponse);
|
struct TermLogger(&'static LimineTerminalResponse);
|
||||||
unsafe impl Send for TermLogger {}
|
unsafe impl Send for TermLogger {}
|
||||||
impl TermLogger {
|
impl TermLogger {
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
static TERM_REQ: TerminalRequest = TerminalRequest::new(0);
|
static TERM_REQ: LimineTerminalRequest = LimineTerminalRequest::new(0);
|
||||||
Self(
|
Self(
|
||||||
TERM_REQ
|
TERM_REQ
|
||||||
.get_response()
|
.get_response()
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
use {
|
use core::sync::atomic::AtomicU64;
|
||||||
crate::memory::{MemoryManager, MAX_ORDER},
|
use limine::{LimineMemmapEntry, LimineMemoryMapEntryType, NonNullPtr};
|
||||||
core::sync::atomic::AtomicU64,
|
use spin::{Mutex, Once};
|
||||||
limine::{MemmapEntry, MemoryMapEntryType, NonNullPtr},
|
use x86_64::{structures::paging::OffsetPageTable, VirtAddr};
|
||||||
spin::{Mutex, Once},
|
use crate::memory::{MemoryManager, MAX_ORDER};
|
||||||
x86_64::{structures::paging::OffsetPageTable, VirtAddr},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const PAGE_SIZE: usize = 4096;
|
pub const PAGE_SIZE: usize = 4096;
|
||||||
|
|
||||||
|
@ -14,7 +12,7 @@ static PAGE_TABLE: Once<Mutex<OffsetPageTable>> = Once::new();
|
||||||
|
|
||||||
/// Initialise page table
|
/// Initialise page table
|
||||||
pub unsafe fn init_pt(phys_base: VirtAddr) {
|
pub unsafe fn init_pt(phys_base: VirtAddr) {
|
||||||
log::debug!("Retrieving page table");
|
log::info!("Retrieving page table");
|
||||||
HHDM_OFFSET.store(phys_base.as_u64(), core::sync::atomic::Ordering::Relaxed);
|
HHDM_OFFSET.store(phys_base.as_u64(), core::sync::atomic::Ordering::Relaxed);
|
||||||
PAGE_TABLE.call_once(|| {
|
PAGE_TABLE.call_once(|| {
|
||||||
Mutex::new(OffsetPageTable::new(
|
Mutex::new(OffsetPageTable::new(
|
||||||
|
@ -30,11 +28,11 @@ pub unsafe fn init_pt(phys_base: VirtAddr) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Initialise memory manager
|
/// Initialise memory manager
|
||||||
pub fn initialize(mmap: &'static [NonNullPtr<MemmapEntry>]) {
|
pub fn initialize(mmap: &'static [NonNullPtr<LimineMemmapEntry>]) {
|
||||||
let mut memory_manager = MemoryManager::new();
|
let mut memory_manager = MemoryManager::new();
|
||||||
|
|
||||||
for entry in mmap {
|
for entry in mmap {
|
||||||
if entry.typ != MemoryMapEntryType::Usable {
|
if entry.typ != LimineMemoryMapEntryType::Usable {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,25 +1,17 @@
|
||||||
use embedded_graphics::pixelcolor::Rgb888;
|
|
||||||
|
|
||||||
use crate::{arch::x86_64::graphics::DISPLAY, bootmodules::BootModule};
|
|
||||||
|
|
||||||
pub mod memory;
|
pub mod memory;
|
||||||
|
|
||||||
mod cpuid;
|
|
||||||
mod gdt;
|
mod gdt;
|
||||||
pub mod graphics;
|
mod interrupts;
|
||||||
pub(crate) mod interrupts;
|
mod logging;
|
||||||
pub mod logging;
|
|
||||||
pub mod pci;
|
|
||||||
pub mod virtio;
|
|
||||||
mod device_info_collector;
|
|
||||||
|
|
||||||
pub use {logging::log, memory::PAGE_SIZE};
|
pub use logging::log;
|
||||||
|
pub use memory::PAGE_SIZE;
|
||||||
|
|
||||||
use {
|
use crate::allocator;
|
||||||
crate::allocator,
|
use memory::MEMORY_MANAGER;
|
||||||
limine::{HhdmRequest, KernelFileRequest, MemmapRequest, ModuleRequest},
|
use limine::{
|
||||||
x86_64::VirtAddr,
|
LimineHhdmRequest, LimineKernelFileRequest, LimineMemmapRequest, LimineModuleRequest,
|
||||||
};
|
};
|
||||||
|
use x86_64::VirtAddr;
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
fn _initial_kernel_heap_start();
|
fn _initial_kernel_heap_start();
|
||||||
|
@ -35,7 +27,7 @@ unsafe extern "C" fn _kernel_start() -> ! {
|
||||||
crate::logger::init().expect("failed to set logger");
|
crate::logger::init().expect("failed to set logger");
|
||||||
log::info!("Initialising AKern {}", crate::VERSION);
|
log::info!("Initialising AKern {}", crate::VERSION);
|
||||||
|
|
||||||
static HDHM_REQ: HhdmRequest = HhdmRequest::new(0);
|
static HDHM_REQ: LimineHhdmRequest = LimineHhdmRequest::new(0);
|
||||||
memory::init_pt(VirtAddr::new(
|
memory::init_pt(VirtAddr::new(
|
||||||
HDHM_REQ
|
HDHM_REQ
|
||||||
.get_response()
|
.get_response()
|
||||||
|
@ -46,7 +38,7 @@ unsafe extern "C" fn _kernel_start() -> ! {
|
||||||
|
|
||||||
allocator::init(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE as _);
|
allocator::init(INITIAL_KERNEL_HEAP_START, INITIAL_KERNEL_HEAP_SIZE as _);
|
||||||
|
|
||||||
static MMAP_REQ: MemmapRequest = MemmapRequest::new(0);
|
static MMAP_REQ: LimineMemmapRequest = LimineMemmapRequest::new(0);
|
||||||
memory::initialize(
|
memory::initialize(
|
||||||
MMAP_REQ
|
MMAP_REQ
|
||||||
.get_response()
|
.get_response()
|
||||||
|
@ -58,107 +50,24 @@ unsafe extern "C" fn _kernel_start() -> ! {
|
||||||
gdt::init();
|
gdt::init();
|
||||||
interrupts::init();
|
interrupts::init();
|
||||||
|
|
||||||
static KFILE_REQ: KernelFileRequest = KernelFileRequest::new(0);
|
|
||||||
static MOD_REQ: ModuleRequest = ModuleRequest::new(0);
|
|
||||||
|
|
||||||
device_info_collector::collect_device_info();
|
|
||||||
|
|
||||||
// Graphics test
|
|
||||||
{
|
{
|
||||||
graphics::init();
|
let mut mm = MEMORY_MANAGER.get().unwrap().lock();
|
||||||
let mut dis = DISPLAY.lock();
|
let alloc_0 = mm.allocate_pages(0).unwrap();
|
||||||
use embedded_graphics::prelude::RgbColor;
|
log::debug!("Addr: {alloc_0}");
|
||||||
|
let alloc_1 = mm.allocate_pages(0).unwrap();
|
||||||
let _ = dis.set_color(Rgb888::YELLOW);
|
log::debug!("Addr: {alloc_1}");
|
||||||
let thick = 6;
|
mm.deallocate_pages(alloc_0, 0);
|
||||||
let p1 = (400, 30);
|
let alloc_2 = mm.allocate_pages(1).unwrap();
|
||||||
let p2 = (200, 150);
|
log::debug!("Addr: {alloc_2}");
|
||||||
let p3 = (600, 150);
|
mm.deallocate_pages(alloc_1, 0);
|
||||||
let p4 = (200, 350);
|
mm.deallocate_pages(alloc_2, 1);
|
||||||
let p5 = (600, 350);
|
let alloc_3 = mm.allocate_pages(1).unwrap();
|
||||||
let p6 = (400, 470);
|
log::debug!("Addr: {alloc_3}");
|
||||||
|
mm.deallocate_pages(alloc_3, 1);
|
||||||
{
|
|
||||||
//HEXAGON
|
|
||||||
|
|
||||||
let _ = dis.line(p1.0, p1.1, p2.0, p2.1, thick);
|
|
||||||
let _ = dis.line(p1.0, p1.1, p3.0, p3.1, thick);
|
|
||||||
let _ = dis.line(p2.0, p2.1, p4.0, p4.1, thick);
|
|
||||||
let _ = dis.line(p3.0, p3.1, p5.0, p5.1, thick);
|
|
||||||
let _ = dis.line(p6.0, p6.1, p4.0, p4.1, thick);
|
|
||||||
let _ = dis.line(p6.0, p6.1, p5.0, p5.1, thick);
|
|
||||||
}
|
|
||||||
{
|
|
||||||
let _ = dis.line(600, 150, 200, 350, thick);
|
|
||||||
let _ = dis.line(600, 350, 400, 250, thick);
|
|
||||||
}
|
|
||||||
|
|
||||||
{
|
|
||||||
let _ = dis.set_color(Rgb888::WHITE);
|
|
||||||
let hp1 = (350, 150);
|
|
||||||
let hp2 = (350, 350);
|
|
||||||
let hp3 = (450, 250);
|
|
||||||
let hp4 = (350, 250);
|
|
||||||
let hp5 = (450, 150);
|
|
||||||
let hp6 = (450, 350);
|
|
||||||
|
|
||||||
let _ = dis.line(hp1.0, hp1.1, hp2.0, hp2.1, thick);
|
|
||||||
let _ = dis.line(hp3.0, hp3.1, hp4.0, hp4.1, thick);
|
|
||||||
let _ = dis.line(hp5.0, hp5.1, hp6.0, hp6.1, thick);
|
|
||||||
}
|
|
||||||
|
|
||||||
dis.swap_buffers();
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: Add in rdseed and rdrand as sources for randomness
|
|
||||||
let _rand = xml::XMLElement::new("Random");
|
|
||||||
|
|
||||||
let bm = MOD_REQ.get_response().get();
|
|
||||||
|
|
||||||
let mut bootmodules = alloc::vec::Vec::new();
|
|
||||||
|
|
||||||
if bm.is_some() {
|
|
||||||
let bm = bm.unwrap();
|
|
||||||
for x in 0..bm.module_count {
|
|
||||||
let file = bm.modules().get(x as usize);
|
|
||||||
if file.is_some() {
|
|
||||||
let file = file.unwrap();
|
|
||||||
let raw_bytes = core::slice::from_raw_parts(
|
|
||||||
file.base.as_ptr().expect("invalid initrd"),
|
|
||||||
file.length as usize,
|
|
||||||
)
|
|
||||||
.to_vec();
|
|
||||||
|
|
||||||
let file_path = alloc::string::String::from_utf8(
|
|
||||||
file.path.to_str().unwrap().to_bytes().to_vec(),
|
|
||||||
);
|
|
||||||
if file_path.is_err() {
|
|
||||||
panic!("invalid file path: {:?}", file_path);
|
|
||||||
}
|
|
||||||
let file_cmd = alloc::string::String::from_utf8(
|
|
||||||
file.cmdline.to_str().unwrap().to_bytes().to_vec(),
|
|
||||||
);
|
|
||||||
if file_cmd.is_err() {
|
|
||||||
panic!("invalid module cmd: {:?}", file_cmd);
|
|
||||||
}
|
|
||||||
|
|
||||||
log::trace!("module path: {:?}", file_path);
|
|
||||||
log::trace!("module cmd: {:?}", file_cmd);
|
|
||||||
|
|
||||||
bootmodules.push(BootModule::new(
|
|
||||||
file_path.unwrap(),
|
|
||||||
raw_bytes,
|
|
||||||
file_cmd.unwrap(),
|
|
||||||
));
|
|
||||||
} else {
|
|
||||||
log::error!("You should not be here");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log::info!("Boot module count: {:?}", bootmodules.len());
|
|
||||||
assert_eq!(bm.module_count, bootmodules.len() as u64);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static KFILE_REQ: LimineKernelFileRequest = LimineKernelFileRequest::new(0);
|
||||||
|
static MOD_REQ: LimineModuleRequest = LimineModuleRequest::new(0);
|
||||||
crate::kmain::kmain(
|
crate::kmain::kmain(
|
||||||
KFILE_REQ
|
KFILE_REQ
|
||||||
.get_response()
|
.get_response()
|
||||||
|
@ -171,24 +80,22 @@ unsafe extern "C" fn _kernel_start() -> ! {
|
||||||
.transpose()
|
.transpose()
|
||||||
.expect("expected valid cmdline string")
|
.expect("expected valid cmdline string")
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
bootmodules,
|
MOD_REQ
|
||||||
|
.get_response()
|
||||||
|
.get()
|
||||||
|
.and_then(|m| m.modules().get(0))
|
||||||
|
.map(|file| unsafe {
|
||||||
|
core::slice::from_raw_parts(
|
||||||
|
file.base.as_ptr().expect("invalid initrd"),
|
||||||
|
file.length as usize,
|
||||||
|
)
|
||||||
|
}),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Spin loop
|
/// Spin loop
|
||||||
pub fn spin_loop() -> ! {
|
pub fn sloop() -> ! {
|
||||||
loop {
|
loop {
|
||||||
x86_64::instructions::hlt();
|
x86_64::instructions::hlt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hardware_random_u64() -> u64 {
|
|
||||||
use {log::trace, rdrand::RdRand};
|
|
||||||
let gen = RdRand::new().unwrap();
|
|
||||||
let ret = gen.try_next_u64().unwrap();
|
|
||||||
trace!("Random {}", ret);
|
|
||||||
|
|
||||||
ret
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_edid() {}
|
|
||||||
|
|
|
@ -1,501 +0,0 @@
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
/// A struct containing info about a PCI device.
|
|
||||||
pub struct PciDeviceInfo {
|
|
||||||
pub header_type: u8,
|
|
||||||
pub device: u8,
|
|
||||||
pub bus: u8,
|
|
||||||
pub device_id: DeviceID,
|
|
||||||
pub full_class: PciFullClass,
|
|
||||||
pub rev_id: u8,
|
|
||||||
}
|
|
||||||
use crate::alloc::string::ToString;
|
|
||||||
|
|
||||||
/// Enumerate PCI devices and run initialisation routines on ones we support
|
|
||||||
pub fn init(device_tree: &mut DeviceTree) {
|
|
||||||
device_tree.devices
|
|
||||||
.insert("Unidentified PCI".to_string(), alloc::vec![]);
|
|
||||||
let mut devices = alloc::vec![];
|
|
||||||
|
|
||||||
for bus in 0..=255 {
|
|
||||||
for device in 0..32 {
|
|
||||||
if let Some(device_info) = check_device(bus, device) {
|
|
||||||
let vendor = device_info.device_id.vendor;
|
|
||||||
let id = device_info.device_id.id;
|
|
||||||
use Vendor::*;
|
|
||||||
let (dev_type, dev_name) = match (vendor, id) {
|
|
||||||
(Qemu, 4369) => ("GPUs", "QEMU VGA"),
|
|
||||||
(VirtIO, 4176) => ("GPUs", "VirtIO PCI GPU"),
|
|
||||||
(CirrusLogic, 184) => ("GPUs", "Cirrus SVGA"), //GD 5446?
|
|
||||||
(_, _) => ("Unidentified PCI", "UNKNOWN DEVICE"),
|
|
||||||
};
|
|
||||||
// let (dev_type, dev_name) = match device_info.full_class {
|
|
||||||
// PciFullClass::Unclassified_NonVgaCompatible => todo!(),
|
|
||||||
// PciFullClass::Unclassified_VgaCompatible => todo!(),
|
|
||||||
|
|
||||||
// PciFullClass::Display_VGA => ("GPUs", "VGA Device"),
|
|
||||||
// PciFullClass::Display_XGA => ("GPUs", "XGA Device"),
|
|
||||||
// PciFullClass::Display_3D => ("GPUs", "3D Device"),
|
|
||||||
// PciFullClass::Display_Other => ("GPUs", "Other"),
|
|
||||||
|
|
||||||
// _ => ("Unidentified PCI", "UNKNOWN DEVICE"),
|
|
||||||
// };
|
|
||||||
|
|
||||||
let mut dev = xml::XMLElement::new(dev_name);
|
|
||||||
let mut pci_info = xml::XMLElement::new("PCI Info");
|
|
||||||
pci_info.set_attribute("id", id);
|
|
||||||
pci_info.set_attribute("device", device_info.device);
|
|
||||||
pci_info.set_attribute("vendor", vendor);
|
|
||||||
pci_info.set_attribute("class", device_info.full_class.to_string());
|
|
||||||
dev.set_child(pci_info);
|
|
||||||
devices.push((dev_type, dev));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (dev_type, dev) in devices {
|
|
||||||
if let Some(abc) = device_tree.devices.get_mut(dev_type) {
|
|
||||||
abc.push(dev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
|
|
||||||
assert!(device < 32);
|
|
||||||
let (device_id, vendor_id) = get_ids(bus, device, 0);
|
|
||||||
if vendor_id == 0xFFFF {
|
|
||||||
// Device doesn't exist
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let reg2 = unsafe { pci_config_read(bus, device, 0, 0x8) };
|
|
||||||
let class = ((reg2 >> 16) & 0x0000_FFFF) as u16;
|
|
||||||
let pci_class = PciFullClass::from_u16(class);
|
|
||||||
let header_type = get_header_type(bus, device, 0);
|
|
||||||
|
|
||||||
Some(PciDeviceInfo {
|
|
||||||
header_type,
|
|
||||||
device,
|
|
||||||
bus,
|
|
||||||
device_id: DeviceID {
|
|
||||||
vendor: vendor_id.into(),
|
|
||||||
id: device_id,
|
|
||||||
},
|
|
||||||
full_class: pci_class,
|
|
||||||
rev_id: (reg2 & 0x0000_00FF) as u8,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
|
||||||
pub struct DeviceID {
|
|
||||||
pub vendor: Vendor,
|
|
||||||
pub id: u16,
|
|
||||||
}
|
|
||||||
impl DeviceID {
|
|
||||||
pub const fn new(vendor: Vendor, id: u16) -> Self {
|
|
||||||
Self { vendor, id }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Copy, Clone, Eq)]
|
|
||||||
#[repr(u16)]
|
|
||||||
pub enum Vendor {
|
|
||||||
ThreeDfxInteractiveInc = 0x121A,
|
|
||||||
ThreeDLabs = 0x3D3D,
|
|
||||||
AllianceSemiconductorCorp = 0x1142,
|
|
||||||
ARKLogicInc = 0xEDD8,
|
|
||||||
ATITechnologiesInc = 0x1002,
|
|
||||||
AvanceLogicIncALI = 0x1005,
|
|
||||||
ChipsandTechnologies = 0x102C,
|
|
||||||
CirrusLogic = 0x1013,
|
|
||||||
Compaq = 0x0E11,
|
|
||||||
CyrixCorp = 0x1078,
|
|
||||||
DiamondMultimediaSystems = 0x1092,
|
|
||||||
DigitalEquipmentCorp = 0x1011,
|
|
||||||
Iit = 0x1061,
|
|
||||||
IntegratedMicroSolutionsInc = 0x10E0,
|
|
||||||
IntelCorp = 0x8086,
|
|
||||||
IntergraphicsSystems = 0x10EA,
|
|
||||||
MacronixInc = 0x10D9,
|
|
||||||
MatroxGraphicsInc = 0x102B,
|
|
||||||
MiroComputersProductsAG = 0x1031,
|
|
||||||
NationalSemiconductorCorp = 0x100B,
|
|
||||||
NeoMagicCorp = 0x10C8,
|
|
||||||
Number9ComputerCompany = 0x105D,
|
|
||||||
NVidiaCorporation = 0x10DE,
|
|
||||||
NVidiaSgsthomson = 0x12D2,
|
|
||||||
OakTechnologyInc = 0x104E,
|
|
||||||
Qemu = 0x1234,
|
|
||||||
QuantumDesignsHKLtd = 0x1098,
|
|
||||||
Real3D = 0x003D,
|
|
||||||
Rendition = 0x1163,
|
|
||||||
S3Inc = 0x5333,
|
|
||||||
SierraSemiconductor = 0x10A8,
|
|
||||||
SiliconIntegratedSystemsSiS = 0x1039,
|
|
||||||
SiliconMotionInc = 0x126F,
|
|
||||||
STBSystemsInc = 0x10B4,
|
|
||||||
TexasInstruments = 0x104C,
|
|
||||||
ToshibaAmericaInfoSystems = 0x1179,
|
|
||||||
TridentMicrosystems = 0x1023,
|
|
||||||
TsengLabsInc = 0x100C,
|
|
||||||
TundraSemiconductorCorp = 0x10E3,
|
|
||||||
VIATechnologiesInc = 0x1106,
|
|
||||||
VirtIO = 0x1AF4,
|
|
||||||
VMWareInc = 0x15AD,
|
|
||||||
Weitek = 0x100E,
|
|
||||||
Unknown(u16),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for Vendor {
|
|
||||||
fn from(vendor_id: u16) -> Self {
|
|
||||||
use Vendor::*;
|
|
||||||
match vendor_id {
|
|
||||||
0x121A => ThreeDfxInteractiveInc,
|
|
||||||
0x3D3D => ThreeDLabs,
|
|
||||||
0x1142 => AllianceSemiconductorCorp,
|
|
||||||
0xEDD8 => ARKLogicInc,
|
|
||||||
0x1002 => ATITechnologiesInc,
|
|
||||||
0x1005 => AvanceLogicIncALI,
|
|
||||||
0x102C => ChipsandTechnologies,
|
|
||||||
0x1013 => CirrusLogic,
|
|
||||||
0x0E11 => Compaq,
|
|
||||||
0x1078 => CyrixCorp,
|
|
||||||
0x1092 => DiamondMultimediaSystems,
|
|
||||||
0x1011 => DigitalEquipmentCorp,
|
|
||||||
0x1061 => Iit,
|
|
||||||
0x10E0 => IntegratedMicroSolutionsInc,
|
|
||||||
0x8086 => IntelCorp,
|
|
||||||
0x10EA => IntergraphicsSystems,
|
|
||||||
0x10D9 => MacronixInc,
|
|
||||||
0x102B => MatroxGraphicsInc,
|
|
||||||
0x1031 => MiroComputersProductsAG,
|
|
||||||
0x100B => NationalSemiconductorCorp,
|
|
||||||
0x10C8 => NeoMagicCorp,
|
|
||||||
0x105D => Number9ComputerCompany,
|
|
||||||
0x10DE => NVidiaCorporation,
|
|
||||||
0x12D2 => NVidiaSgsthomson,
|
|
||||||
0x104E => OakTechnologyInc,
|
|
||||||
0x1234 => Qemu,
|
|
||||||
0x1098 => QuantumDesignsHKLtd,
|
|
||||||
0x003D => Real3D,
|
|
||||||
0x1163 => Rendition,
|
|
||||||
0x5333 => S3Inc,
|
|
||||||
0x10A8 => SierraSemiconductor,
|
|
||||||
0x1039 => SiliconIntegratedSystemsSiS,
|
|
||||||
0x126F => SiliconMotionInc,
|
|
||||||
0x10B4 => STBSystemsInc,
|
|
||||||
0x104C => TexasInstruments,
|
|
||||||
0x1179 => ToshibaAmericaInfoSystems,
|
|
||||||
0x1023 => TridentMicrosystems,
|
|
||||||
0x100C => TsengLabsInc,
|
|
||||||
0x10E3 => TundraSemiconductorCorp,
|
|
||||||
0x1106 => VIATechnologiesInc,
|
|
||||||
0x1AF4 => VirtIO,
|
|
||||||
0x15AD => VMWareInc,
|
|
||||||
0x100E => Weitek,
|
|
||||||
id => Unknown(id),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<u16> for Vendor {
|
|
||||||
fn into(self) -> u16 {
|
|
||||||
use Vendor::*;
|
|
||||||
match self {
|
|
||||||
ThreeDfxInteractiveInc => 0x121A,
|
|
||||||
ThreeDLabs => 0x3D3D,
|
|
||||||
AllianceSemiconductorCorp => 0x1142,
|
|
||||||
ARKLogicInc => 0xEDD8,
|
|
||||||
ATITechnologiesInc => 0x1002,
|
|
||||||
AvanceLogicIncALI => 0x1005,
|
|
||||||
ChipsandTechnologies => 0x102C,
|
|
||||||
CirrusLogic => 0x1013,
|
|
||||||
Compaq => 0x0E11,
|
|
||||||
CyrixCorp => 0x1078,
|
|
||||||
DiamondMultimediaSystems => 0x1092,
|
|
||||||
DigitalEquipmentCorp => 0x1011,
|
|
||||||
Iit => 0x1061,
|
|
||||||
IntegratedMicroSolutionsInc => 0x10E0,
|
|
||||||
IntelCorp => 0x8086,
|
|
||||||
IntergraphicsSystems => 0x10EA,
|
|
||||||
MacronixInc => 0x10D9,
|
|
||||||
MatroxGraphicsInc => 0x102B,
|
|
||||||
MiroComputersProductsAG => 0x1031,
|
|
||||||
NationalSemiconductorCorp => 0x100B,
|
|
||||||
NeoMagicCorp => 0x10C8,
|
|
||||||
Number9ComputerCompany => 0x105D,
|
|
||||||
NVidiaCorporation => 0x10DE,
|
|
||||||
NVidiaSgsthomson => 0x12D2,
|
|
||||||
OakTechnologyInc => 0x104E,
|
|
||||||
Qemu => 0x1234,
|
|
||||||
QuantumDesignsHKLtd => 0x1098,
|
|
||||||
Real3D => 0x003D,
|
|
||||||
Rendition => 0x1163,
|
|
||||||
S3Inc => 0x5333,
|
|
||||||
SierraSemiconductor => 0x10A8,
|
|
||||||
SiliconIntegratedSystemsSiS => 0x1039,
|
|
||||||
SiliconMotionInc => 0x126F,
|
|
||||||
STBSystemsInc => 0x10B4,
|
|
||||||
TexasInstruments => 0x104C,
|
|
||||||
ToshibaAmericaInfoSystems => 0x1179,
|
|
||||||
TridentMicrosystems => 0x1023,
|
|
||||||
TsengLabsInc => 0x100C,
|
|
||||||
TundraSemiconductorCorp => 0x10E3,
|
|
||||||
VIATechnologiesInc => 0x1106,
|
|
||||||
VirtIO => 0x1AF4,
|
|
||||||
VMWareInc => 0x15AD,
|
|
||||||
Weitek => 0x100E,
|
|
||||||
Unknown(id) => id,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Vendor {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
use Vendor::*;
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Qemu => write!(f, "QEMU (0x1234)"),
|
|
||||||
VirtIO => write!(f, "VirtIO (0x1AF4)"),
|
|
||||||
VMWareInc => write!(f, "VMWARE (0x15AD)"),
|
|
||||||
S3Inc => write!(f, "S3 Incorporated (0x5333)"),
|
|
||||||
IntelCorp => write!(f, "Intel Corp. (0x8086)"),
|
|
||||||
ATITechnologiesInc => write!(f, "ATI (0x1002)"),
|
|
||||||
Unknown(id) => write!(f, "Unknown ({:#6})", id),
|
|
||||||
other => write!(f, "{other:?}"),
|
|
||||||
}?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use core::fmt::Display;
|
|
||||||
|
|
||||||
use x86_64::instructions::port::Port;
|
|
||||||
use crate::device_tree::DeviceTree;
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
/// Class specification for a PCI device
|
|
||||||
pub enum PciClass {
|
|
||||||
Unclassified = 0x00,
|
|
||||||
MassStorage = 0x01,
|
|
||||||
Network = 0x02,
|
|
||||||
Display = 0x03,
|
|
||||||
Multimedia = 0x04,
|
|
||||||
Memory = 0x05,
|
|
||||||
Bridge = 0x06,
|
|
||||||
Unknown = 0xFF,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u8> for PciClass {
|
|
||||||
/// Convert a u8 into the corresponding PciClass
|
|
||||||
fn from(n: u8) -> Self {
|
|
||||||
use PciClass::*;
|
|
||||||
match n {
|
|
||||||
0x00 => Unclassified,
|
|
||||||
0x01 => MassStorage,
|
|
||||||
0x02 => Network,
|
|
||||||
0x03 => Display,
|
|
||||||
0x04 => Multimedia,
|
|
||||||
0x05 => Memory,
|
|
||||||
0x06 => Bridge,
|
|
||||||
_ => Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(non_camel_case_types, dead_code)]
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
|
||||||
#[repr(C)]
|
|
||||||
/// Full class specification (type and subtype) for a PCI device.
|
|
||||||
///
|
|
||||||
/// Uses non-camel-case types for readability.
|
|
||||||
pub enum PciFullClass {
|
|
||||||
Unclassified_NonVgaCompatible = 0x0000,
|
|
||||||
Unclassified_VgaCompatible = 0x0001,
|
|
||||||
|
|
||||||
MassStorage_ScsiBus = 0x0100,
|
|
||||||
MassStorage_IDE = 0x0101,
|
|
||||||
MassStorage_Floppy = 0x0102,
|
|
||||||
MassStorage_IpiBus = 0x0103,
|
|
||||||
MassStorage_RAID = 0x0104,
|
|
||||||
MassStorage_ATA = 0x0105,
|
|
||||||
MassStorage_SATA = 0x0106,
|
|
||||||
MassStorage_SerialSCSI = 0x0107,
|
|
||||||
MassStorage_NVM = 0x0108,
|
|
||||||
MassStorage_Other = 0x0180,
|
|
||||||
|
|
||||||
Network_Ethernet = 0x0200,
|
|
||||||
Network_TokenRing = 0x0201,
|
|
||||||
Network_FDDI = 0x0202,
|
|
||||||
Network_ATM = 0x0203,
|
|
||||||
Network_ISDN = 0x0204,
|
|
||||||
Network_WorldFlip = 0x0205,
|
|
||||||
Network_PICMG = 0x0206,
|
|
||||||
Network_Infiniband = 0x0207,
|
|
||||||
Network_Fabric = 0x0208,
|
|
||||||
Network_Other = 0x0280,
|
|
||||||
|
|
||||||
Display_VGA = 0x0300,
|
|
||||||
Display_XGA = 0x0301,
|
|
||||||
Display_3D = 0x0302,
|
|
||||||
Display_Other = 0x0380,
|
|
||||||
|
|
||||||
Multimedia_Video = 0x0400,
|
|
||||||
Multimedia_AudioController = 0x0401,
|
|
||||||
Multimedia_Telephony = 0x0402,
|
|
||||||
Multimedia_AudioDevice = 0x0403,
|
|
||||||
Multimedia_Other = 0x0480,
|
|
||||||
|
|
||||||
Memory_RAM = 0x0500,
|
|
||||||
Memory_Flash = 0x0501,
|
|
||||||
Memory_Other = 0x0580,
|
|
||||||
|
|
||||||
Bridge_Host = 0x0600,
|
|
||||||
Bridge_ISA = 0x0601,
|
|
||||||
Bridge_EISA = 0x0602,
|
|
||||||
Bridge_MCA = 0x0603,
|
|
||||||
Bridge_PciToPci = 0x0604,
|
|
||||||
Bridge_PCMCIA = 0x0605,
|
|
||||||
Bridge_NuBus = 0x0606,
|
|
||||||
Bridge_CardBus = 0x0607,
|
|
||||||
Bridge_RACEway = 0x0608,
|
|
||||||
Bridge_PciToPciSemiTransparent = 0x0609,
|
|
||||||
Bridge_InfinibandToPci = 0x060A,
|
|
||||||
Bridge_Other = 0x0680,
|
|
||||||
|
|
||||||
Unknown = 0xFFFF,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl PciFullClass {
|
|
||||||
// listen, i know this sucks, but i didn't want to include
|
|
||||||
// `num`, `num-traits` and `num-derive` as dependencies for
|
|
||||||
// this crate just for a convenience function
|
|
||||||
/// Convert a u16 into the corresponding PciFullClass
|
|
||||||
pub fn from_u16(n: u16) -> PciFullClass {
|
|
||||||
match n {
|
|
||||||
0x0000 => PciFullClass::Unclassified_NonVgaCompatible,
|
|
||||||
0x0001 => PciFullClass::Unclassified_VgaCompatible,
|
|
||||||
|
|
||||||
0x0100 => PciFullClass::MassStorage_ScsiBus,
|
|
||||||
0x0101 => PciFullClass::MassStorage_IDE,
|
|
||||||
0x0102 => PciFullClass::MassStorage_Floppy,
|
|
||||||
0x0103 => PciFullClass::MassStorage_IpiBus,
|
|
||||||
0x0104 => PciFullClass::MassStorage_RAID,
|
|
||||||
0x0105 => PciFullClass::MassStorage_ATA,
|
|
||||||
0x0106 => PciFullClass::MassStorage_SATA,
|
|
||||||
0x0107 => PciFullClass::MassStorage_SerialSCSI,
|
|
||||||
0x0108 => PciFullClass::MassStorage_NVM,
|
|
||||||
0x0180 => PciFullClass::MassStorage_Other,
|
|
||||||
|
|
||||||
0x0200 => PciFullClass::Network_Ethernet,
|
|
||||||
0x0201 => PciFullClass::Network_TokenRing,
|
|
||||||
0x0202 => PciFullClass::Network_FDDI,
|
|
||||||
0x0203 => PciFullClass::Network_ATM,
|
|
||||||
0x0204 => PciFullClass::Network_ISDN,
|
|
||||||
0x0205 => PciFullClass::Network_WorldFlip,
|
|
||||||
0x0206 => PciFullClass::Network_PICMG,
|
|
||||||
0x0207 => PciFullClass::Network_Infiniband,
|
|
||||||
0x0208 => PciFullClass::Network_Fabric,
|
|
||||||
0x0280 => PciFullClass::Network_Other,
|
|
||||||
|
|
||||||
0x0300 => PciFullClass::Display_VGA,
|
|
||||||
0x0301 => PciFullClass::Display_XGA,
|
|
||||||
0x0302 => PciFullClass::Display_3D,
|
|
||||||
0x0380 => PciFullClass::Display_Other,
|
|
||||||
|
|
||||||
0x0400 => PciFullClass::Multimedia_Video,
|
|
||||||
0x0401 => PciFullClass::Multimedia_AudioController,
|
|
||||||
0x0402 => PciFullClass::Multimedia_Telephony,
|
|
||||||
0x0403 => PciFullClass::Multimedia_AudioDevice,
|
|
||||||
0x0480 => PciFullClass::Multimedia_Other,
|
|
||||||
|
|
||||||
0x0500 => PciFullClass::Memory_RAM,
|
|
||||||
0x0501 => PciFullClass::Memory_Flash,
|
|
||||||
0x0580 => PciFullClass::Memory_Other,
|
|
||||||
|
|
||||||
0x0600 => PciFullClass::Bridge_Host,
|
|
||||||
0x0601 => PciFullClass::Bridge_ISA,
|
|
||||||
0x0602 => PciFullClass::Bridge_EISA,
|
|
||||||
0x0603 => PciFullClass::Bridge_MCA,
|
|
||||||
0x0604 => PciFullClass::Bridge_PciToPci,
|
|
||||||
0x0605 => PciFullClass::Bridge_PCMCIA,
|
|
||||||
0x0606 => PciFullClass::Bridge_NuBus,
|
|
||||||
0x0607 => PciFullClass::Bridge_CardBus,
|
|
||||||
0x0608 => PciFullClass::Bridge_RACEway,
|
|
||||||
0x0609 => PciFullClass::Bridge_PciToPciSemiTransparent,
|
|
||||||
0x060A => PciFullClass::Bridge_InfinibandToPci,
|
|
||||||
0x0680 => PciFullClass::Bridge_Other,
|
|
||||||
|
|
||||||
_ => PciFullClass::Unknown,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Convert a PciFullClass to its u16 representation
|
|
||||||
pub fn as_u16(&self) -> u16 {
|
|
||||||
*self as u16
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<u16> for PciFullClass {
|
|
||||||
/// Convert a u16 into the corresponding PciFullClass
|
|
||||||
fn from(n: u16) -> Self {
|
|
||||||
Self::from_u16(n)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for PciFullClass {
|
|
||||||
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
|
||||||
write!(f, "{:?} ({:#06X})", self, self.as_u16())?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn pci_config_read(bus: u8, device: u8, func: u8, offset: u8) -> u32 {
|
|
||||||
let bus = bus as u32;
|
|
||||||
let device = device as u32;
|
|
||||||
let func = func as u32;
|
|
||||||
let offset = offset as u32;
|
|
||||||
// construct address param
|
|
||||||
let address =
|
|
||||||
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xFC) | 0x8000_0000) as u32;
|
|
||||||
|
|
||||||
// write address
|
|
||||||
Port::new(0xCF8).write(address);
|
|
||||||
|
|
||||||
// read data
|
|
||||||
Port::new(0xCFC).read()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn pci_config_write(bus: u8, device: u8, func: u8, offset: u8, value: u32) {
|
|
||||||
let bus = bus as u32;
|
|
||||||
let device = device as u32;
|
|
||||||
let func = func as u32;
|
|
||||||
let offset = offset as u32;
|
|
||||||
// construct address param
|
|
||||||
let address =
|
|
||||||
((bus << 16) | (device << 11) | (func << 8) | (offset & 0xFC) | 0x8000_0000) as u32;
|
|
||||||
|
|
||||||
// write address
|
|
||||||
Port::new(0xCF8).write(address);
|
|
||||||
|
|
||||||
// write data
|
|
||||||
Port::new(0xCFC).write(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_header_type(bus: u8, device: u8, function: u8) -> u8 {
|
|
||||||
assert!(device < 32);
|
|
||||||
assert!(function < 8);
|
|
||||||
let res = unsafe { pci_config_read(bus, device, function, 0x0C) };
|
|
||||||
((res >> 16) & 0xFF) as u8
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_ids(bus: u8, device: u8, function: u8) -> (u16, u16) {
|
|
||||||
assert!(device < 32);
|
|
||||||
assert!(function < 8);
|
|
||||||
let res = unsafe { pci_config_read(bus, device, function, 0) };
|
|
||||||
let dev_id = ((res >> 16) & 0xFFFF) as u16;
|
|
||||||
let vnd_id = (res & 0xFFFF) as u16;
|
|
||||||
(dev_id, vnd_id)
|
|
||||||
}
|
|
|
@ -1,37 +0,0 @@
|
||||||
use {
|
|
||||||
core::{ptr::NonNull},
|
|
||||||
virtio_drivers::{BufferDirection, Hal, PhysAddr},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn test() {
|
|
||||||
let _ps = virtio_drivers::PAGE_SIZE;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct AbleosHal;
|
|
||||||
|
|
||||||
unsafe impl Hal for AbleosHal {
|
|
||||||
fn dma_alloc(_pages: usize, _direction: BufferDirection) -> (PhysAddr, NonNull<u8>) {
|
|
||||||
todo!();
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn dma_dealloc(_paddr: PhysAddr, _vaddr: NonNull<u8>, _pages: usize) -> i32 {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn mmio_phys_to_virt(paddr: PhysAddr, _size: usize) -> NonNull<u8> {
|
|
||||||
NonNull::new(paddr as _).unwrap()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn share(_buffer: NonNull<[u8]>, _direction: BufferDirection) -> PhysAddr {
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe fn unshare(_paddr: PhysAddr, _buffer: NonNull<[u8]>, _direction: BufferDirection) {
|
|
||||||
// Nothing to do, as the host already has access to all memory and we didn't copy the buffer
|
|
||||||
// anywhere else.
|
|
||||||
todo!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fn virt_to_phys(vaddr: usize) -> PhysAddr {
|
|
||||||
vaddr
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
use {
|
|
||||||
crate::alloc::string::ToString,
|
|
||||||
alloc::{string::String, vec::Vec},
|
|
||||||
clparse::Arguments,
|
|
||||||
core::fmt::{Debug, Display},
|
|
||||||
log::trace,
|
|
||||||
xml::XMLElement,
|
|
||||||
};
|
|
||||||
pub type BootModules = Vec<BootModule>;
|
|
||||||
|
|
||||||
pub struct BootModule {
|
|
||||||
pub path: String,
|
|
||||||
pub bytes: Vec<u8>,
|
|
||||||
pub cmd: String,
|
|
||||||
}
|
|
||||||
impl BootModule {
|
|
||||||
pub fn new(path: String, bytes: Vec<u8>, cmd: String) -> Self {
|
|
||||||
Self { path, bytes, cmd }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn build_cmd<T: Display + Debug>(name: T, cmdline: T) -> XMLElement {
|
|
||||||
let mut cmdline = cmdline.to_string();
|
|
||||||
cmdline.pop();
|
|
||||||
cmdline.remove(0);
|
|
||||||
|
|
||||||
let cmd = Arguments::parse(cmdline.to_string()).unwrap();
|
|
||||||
trace!("Cmdline: {cmd:?}");
|
|
||||||
|
|
||||||
let mut clo = XMLElement::new(name);
|
|
||||||
for (key, value) in cmd.arguments {
|
|
||||||
clo.set_attribute(key, value);
|
|
||||||
}
|
|
||||||
trace!("command line object: {:?}", clo);
|
|
||||||
clo
|
|
||||||
}
|
|
|
@ -1,186 +0,0 @@
|
||||||
//! AbleOS capability tree implementation
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::{tab, utils::TAB},
|
|
||||||
alloc::{
|
|
||||||
string::{String, ToString},
|
|
||||||
vec,
|
|
||||||
},
|
|
||||||
core::fmt,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Seperate
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
struct Argument {
|
|
||||||
name: String,
|
|
||||||
type_: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Function {
|
|
||||||
name: String,
|
|
||||||
args: Vec<Argument>,
|
|
||||||
ret: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Capability {
|
|
||||||
name: String,
|
|
||||||
functions: Vec<Function>,
|
|
||||||
sub_capabilities: Vec<Capability>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Capability {
|
|
||||||
fn new(name: String) -> Capability {
|
|
||||||
Capability {
|
|
||||||
name,
|
|
||||||
functions: Vec::new(),
|
|
||||||
sub_capabilities: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Capability {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(f, "Capability: {}\r\n", self.name)?;
|
|
||||||
for function in &self.functions {
|
|
||||||
write!(f, "{}Function: {}\r\n", tab!(1), function.name)?;
|
|
||||||
for arg in &function.args {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"{}Argument: {} (Type: {})\r\n",
|
|
||||||
tab!(2),
|
|
||||||
arg.name,
|
|
||||||
arg.type_
|
|
||||||
)?;
|
|
||||||
}
|
|
||||||
write!(f, "{}Return Type: {}\r\n", tab!(2), &function.ret)?;
|
|
||||||
}
|
|
||||||
for sub_capability in &self.sub_capabilities {
|
|
||||||
write!(f, "{}{}\r\n", tab!(1), sub_capability)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// impl Capability {
|
|
||||||
// fn to_string_with_indentation(&self, level: usize) -> String {
|
|
||||||
// let indent = tab!(level);
|
|
||||||
// let mut result = format!("{}Capability: {}\n", indent, self.name);
|
|
||||||
// for function in &self.functions {
|
|
||||||
// result.push_str(&format!(
|
|
||||||
// "{}Function: {}\n",
|
|
||||||
// tab!(indent + 1),
|
|
||||||
// function.name
|
|
||||||
// ));
|
|
||||||
// for arg in &function.args {
|
|
||||||
// result.push_str(&format!(
|
|
||||||
// "{}Argument: {} (Type: {})\n",
|
|
||||||
// tab!(indent + 2),
|
|
||||||
// arg.name,
|
|
||||||
// arg.type_
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// result.push_str(&format!(
|
|
||||||
// "{}Return Type: {}\n",
|
|
||||||
// tab!(indent + 2),
|
|
||||||
// &function.ret
|
|
||||||
// ));
|
|
||||||
// }
|
|
||||||
// for sub_capability in &self.sub_capabilities {
|
|
||||||
// result.push_str(&sub_capability.to_string_with_indentation(level + 1));
|
|
||||||
// }
|
|
||||||
// result
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
struct CapabilityTree {
|
|
||||||
capabilities: Vec<Capability>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl CapabilityTree {
|
|
||||||
fn new() -> CapabilityTree {
|
|
||||||
CapabilityTree {
|
|
||||||
capabilities: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A super simple capabilities example
|
|
||||||
pub fn example() {
|
|
||||||
let mut capability_tree = Capability::new("VFS".to_string());
|
|
||||||
|
|
||||||
let mut file_management_capability = Capability::new("File Management".to_string());
|
|
||||||
file_management_capability.functions.push(Function {
|
|
||||||
name: "OpenFile".to_string(),
|
|
||||||
args: vec![
|
|
||||||
Argument {
|
|
||||||
name: "path".to_string(),
|
|
||||||
type_: "String".to_string(),
|
|
||||||
},
|
|
||||||
Argument {
|
|
||||||
name: "mode".to_string(),
|
|
||||||
type_: "String".to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ret: "FileHandle".to_string(),
|
|
||||||
});
|
|
||||||
file_management_capability.functions.push(Function {
|
|
||||||
name: "ReadFile".to_string(),
|
|
||||||
args: vec![
|
|
||||||
Argument {
|
|
||||||
name: "file".to_string(),
|
|
||||||
type_: "FileHandle".to_string(),
|
|
||||||
},
|
|
||||||
Argument {
|
|
||||||
name: "buffer".to_string(),
|
|
||||||
type_: "&mut [u8]".to_string(),
|
|
||||||
},
|
|
||||||
Argument {
|
|
||||||
name: "length".to_string(),
|
|
||||||
type_: "usize".to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ret: "usize".to_string(),
|
|
||||||
});
|
|
||||||
file_management_capability.functions.push(Function {
|
|
||||||
name: "WriteFile".to_string(),
|
|
||||||
args: vec![
|
|
||||||
Argument {
|
|
||||||
name: "file".to_string(),
|
|
||||||
type_: "FileHandle".to_string(),
|
|
||||||
},
|
|
||||||
Argument {
|
|
||||||
name: "buffer".to_string(),
|
|
||||||
type_: "&[u8]".to_string(),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
ret: "None".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut directory_management_capability = Capability::new("Directory Management".to_string());
|
|
||||||
directory_management_capability.functions.push(Function {
|
|
||||||
name: "CreateDirectory".to_string(),
|
|
||||||
args: vec![Argument {
|
|
||||||
name: "path".to_string(),
|
|
||||||
type_: "String".to_string(),
|
|
||||||
}],
|
|
||||||
ret: "bool".to_string(),
|
|
||||||
});
|
|
||||||
directory_management_capability.functions.push(Function {
|
|
||||||
name: "ListDirectory".to_string(),
|
|
||||||
args: vec![Argument {
|
|
||||||
name: "path".to_string(),
|
|
||||||
type_: "String".to_string(),
|
|
||||||
}],
|
|
||||||
ret: "Vec<String>".to_string(),
|
|
||||||
});
|
|
||||||
|
|
||||||
capability_tree
|
|
||||||
.sub_capabilities
|
|
||||||
.push(file_management_capability);
|
|
||||||
capability_tree
|
|
||||||
.sub_capabilities
|
|
||||||
.push(directory_management_capability);
|
|
||||||
|
|
||||||
log::debug!("CapTree\r\n{}", capability_tree);
|
|
||||||
}
|
|
|
@ -1,73 +0,0 @@
|
||||||
//! A tree of hardware devices
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::alloc::string::ToString,
|
|
||||||
alloc::{string::String, vec::Vec},
|
|
||||||
core::fmt,
|
|
||||||
hashbrown::HashMap,
|
|
||||||
};
|
|
||||||
/// A device object.
|
|
||||||
/// TODO define device
|
|
||||||
pub type Device = xml::XMLElement;
|
|
||||||
|
|
||||||
/// A tree of devices
|
|
||||||
// TODO: alphabetize this list
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct DeviceTree {
|
|
||||||
/// The device tree
|
|
||||||
pub devices: HashMap<String, Vec<Device>>,
|
|
||||||
}
|
|
||||||
impl DeviceTree {
|
|
||||||
/// Build the device tree. Does not populate the device tree
|
|
||||||
pub fn new() -> Self {
|
|
||||||
let mut dt = Self {
|
|
||||||
devices: HashMap::new(),
|
|
||||||
};
|
|
||||||
device_tree!(dt, [
|
|
||||||
"Mice",
|
|
||||||
"Keyboards",
|
|
||||||
"Controllers",
|
|
||||||
"Generic HIDs",
|
|
||||||
"Disk Drives",
|
|
||||||
"CD Drives",
|
|
||||||
"Batteries",
|
|
||||||
"Monitors",
|
|
||||||
"GPUs",
|
|
||||||
"CPUs",
|
|
||||||
"USB",
|
|
||||||
"Serial Ports",
|
|
||||||
"Cameras",
|
|
||||||
"Biometric Devices",
|
|
||||||
]);
|
|
||||||
dt
|
|
||||||
}
|
|
||||||
}
|
|
||||||
use crate::{utils::TAB, device_tree};
|
|
||||||
use crate::tab;
|
|
||||||
impl fmt::Display for DeviceTree {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
writeln!(f)?;
|
|
||||||
for (device_type, devices) in &self.devices {
|
|
||||||
writeln!(f, "\r{}{}/\r", tab!(1), device_type)?;
|
|
||||||
for device in devices {
|
|
||||||
writeln!(f, "{}{}/\r", tab!(2), device.name)?;
|
|
||||||
for attr in &device.attributes {
|
|
||||||
writeln!(f, "{}{}\r", tab!(3), attr)?;
|
|
||||||
}
|
|
||||||
for child in &device.children {
|
|
||||||
writeln!(f, "{}{}\r", tab!(3), child.name)?;
|
|
||||||
for attr in &child.attributes {
|
|
||||||
writeln!(f, "{}{}\r", tab!(4), attr)?;
|
|
||||||
}
|
|
||||||
for child in &child.children {
|
|
||||||
writeln!(f, "{}{}\r", tab!(4), child.name)?;
|
|
||||||
for attr in &child.attributes {
|
|
||||||
writeln!(f, "{}{}\r", tab!(5), attr)?;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,66 +0,0 @@
|
||||||
//! A handle module
|
|
||||||
|
|
||||||
use {
|
|
||||||
crate::arch::hardware_random_u64,
|
|
||||||
core::fmt::{self, Formatter},
|
|
||||||
};
|
|
||||||
/// An operating system handle without permissions attached
|
|
||||||
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)]
|
|
||||||
pub struct OSHandle {
|
|
||||||
id: u64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl OSHandle {
|
|
||||||
/// turn a u64 into an OSHandle
|
|
||||||
pub fn new_from_u64(id: u64) -> Self {
|
|
||||||
Self { id }
|
|
||||||
}
|
|
||||||
/// Generate a new OSHandle using the HAL random function
|
|
||||||
pub fn random_new() -> Self {
|
|
||||||
Self {
|
|
||||||
id: hardware_random_u64(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// A handle for resources
|
|
||||||
#[derive(Debug, Eq, Hash, PartialEq, Clone, Copy)]
|
|
||||||
pub struct Handle {
|
|
||||||
id: OSHandle,
|
|
||||||
// TODO: Update this to be indexes into the caps
|
|
||||||
perms: Permissions,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Handle {
|
|
||||||
/// make a new handle
|
|
||||||
pub fn new() -> Handle {
|
|
||||||
Handle {
|
|
||||||
id: OSHandle::random_new(),
|
|
||||||
perms: Permissions::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// represent the os handle as a u64
|
|
||||||
pub fn as_u64(&self) -> u64 {
|
|
||||||
self.id.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Handle {
|
|
||||||
fn fmt(&self, w: &mut Formatter<'_>) -> Result<(), core::fmt::Error> {
|
|
||||||
write!(w, "{:?}", self.id)?;
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(PartialEq, Hash, Eq, Debug, Clone, Copy)]
|
|
||||||
struct Permissions {
|
|
||||||
edit_children: bool,
|
|
||||||
edit_attributes: bool,
|
|
||||||
}
|
|
||||||
impl Permissions {
|
|
||||||
fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
edit_children: true,
|
|
||||||
edit_attributes: true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
//! the system interface
|
|
||||||
|
|
||||||
// use {
|
|
||||||
// crate::ipc::message::Message,
|
|
||||||
// alloc::vec::Vec,
|
|
||||||
// crossbeam_queue::{ArrayQueue, SegQueue},
|
|
||||||
// // hbvm::engine::Engine,
|
|
||||||
// log::trace,
|
|
||||||
// HostError::MemoryError,
|
|
||||||
// };
|
|
||||||
/// Host errors
|
|
||||||
pub enum HostError {
|
|
||||||
/// A host memory error
|
|
||||||
MemoryError,
|
|
||||||
}
|
|
||||||
|
|
||||||
// / Check f0 register for the handle
|
|
||||||
// / check f1 for the message ptr
|
|
||||||
// / check f2 for the message length
|
|
||||||
// pub fn ipc_send(engine: &mut Engine) -> Result<(), HostError> {
|
|
||||||
// let _handle = engine.registers.f0;
|
|
||||||
// let message_start = engine.registers.f1;
|
|
||||||
// let message_length = engine.registers.f2;
|
|
||||||
|
|
||||||
// let mut ipc_msg: Vec<u8> = alloc::vec![];
|
|
||||||
|
|
||||||
// for x in message_start..message_start + message_length {
|
|
||||||
// let byte = engine.read_mem_addr_8(x);
|
|
||||||
// match byte {
|
|
||||||
// Ok(byte) => ipc_msg.push(byte),
|
|
||||||
// Err(_) => return Err(MemoryError),
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// log::trace!("Message bytes {:?}", ipc_msg);
|
|
||||||
// Ok(())
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // pub fn ipc_recv(_engine: &mut Engine) {}
|
|
||||||
|
|
||||||
// /// Check f0 for the buffer type
|
|
||||||
// /// 0 means an unbound buffer
|
|
||||||
// /// 1 means a bound buffer
|
|
||||||
// /// Check f1 if the buffer is bound
|
|
||||||
// ///
|
|
||||||
// /// f2 Return a handle to the sender
|
|
||||||
// /// f3 returns a handle the the reciever
|
|
||||||
// pub fn ipc_mkbuf(engine: &mut Engine) {
|
|
||||||
// match engine.registers.f0 as usize {
|
|
||||||
// 0 => {
|
|
||||||
// trace!("Making a new ipc unbound buffer");
|
|
||||||
// let _buf: SegQueue<Message> = SegQueue::new();
|
|
||||||
// }
|
|
||||||
// 1 => {
|
|
||||||
// let buf_len = engine.registers.f1 as usize;
|
|
||||||
// trace!("Making a new ipc buffer with capacity {}", buf_len);
|
|
||||||
// let _buf: ArrayQueue<Message> = ArrayQueue::new(buf_len);
|
|
||||||
// }
|
|
||||||
// _ => {}
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // pub fn rpc_discover(_engine: &mut Engine) {}
|
|
||||||
// // pub fn rpc_register(_engine: &mut Engine) {}
|
|
||||||
// // pub fn rpc_call(_engine: &mut Engine) {}
|
|
||||||
|
|
||||||
use {hbvm::vm::mem::HandlePageFault, log::error};
|
|
||||||
/// AbleOS HBVM traphandler
|
|
||||||
pub struct TrapHandler;
|
|
||||||
impl HandlePageFault for TrapHandler {
|
|
||||||
fn page_fault(
|
|
||||||
&mut self,
|
|
||||||
reason: hbvm::vm::mem::MemoryAccessReason,
|
|
||||||
memory: &mut hbvm::vm::mem::Memory,
|
|
||||||
vaddr: u64,
|
|
||||||
size: hbvm::vm::mem::PageSize,
|
|
||||||
dataptr: *mut u8,
|
|
||||||
) -> bool {
|
|
||||||
error!(
|
|
||||||
"REASON: {}
|
|
||||||
memory: {:?}
|
|
||||||
vaddr: {}
|
|
||||||
size: {:?}
|
|
||||||
Dataptr {:?}",
|
|
||||||
reason, memory, vaddr, size, dataptr
|
|
||||||
);
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
//!
|
|
||||||
|
|
||||||
use {
|
|
||||||
super::message::Message,
|
|
||||||
crossbeam_queue::{ArrayQueue, SegQueue},
|
|
||||||
};
|
|
||||||
|
|
||||||
enum BufferTypes {
|
|
||||||
Unbound(SegQueue<Message>),
|
|
||||||
Bound(ArrayQueue<Message>),
|
|
||||||
}
|
|
||||||
/// Interproccess buffer
|
|
||||||
pub struct IpcBuffer {
|
|
||||||
protocol: Protocol,
|
|
||||||
buffer: BufferTypes,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IpcBuffer {
|
|
||||||
/// Validate a message to match the `IPC.protocol`
|
|
||||||
pub fn validate_messages(&mut self) -> Result<(), IpcError> {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// Interprocess Communication Errors
|
|
||||||
pub enum IpcError {
|
|
||||||
/// An invalid message error returned to the sender
|
|
||||||
InvalidMessage,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// TODO: define this, possibly as the binary form of the IDL
|
|
||||||
/// DEPEND: This depends on an IDL
|
|
||||||
pub struct Protocol {
|
|
||||||
// TODO: add in settings
|
|
||||||
// like `invalid_message_handler` with some options similar to
|
|
||||||
// `Deny` Drops the message
|
|
||||||
// `Allow` Allows invalid messages (This disables validators IPC side and relies on programs to handle invalid messages)
|
|
||||||
// `CustomFunct` a callback
|
|
||||||
// and `report_invalid_messages_to_sender`
|
|
||||||
// `True`
|
|
||||||
// `False`
|
|
||||||
// settings: PSettings,
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
//! An ipc message structured in a nice convenient way
|
|
||||||
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
|
|
||||||
/// TODO: Extend this into a full structure
|
|
||||||
/// DEPEND: This depends on an IDL
|
|
||||||
pub type Message = Vec<u8>;
|
|
|
@ -1,3 +0,0 @@
|
||||||
//! Interprocess communication
|
|
||||||
pub mod buffer;
|
|
||||||
pub mod message;
|
|
|
@ -1,61 +1,15 @@
|
||||||
//! AbleOS Kernel Entrypoint
|
//! AbleOS Kernel Entrypoint
|
||||||
|
|
||||||
// use crate::arch::sloop;
|
pub fn kmain(cmdline: &str, bootstrap: Option<&'static [u8]>) -> ! {
|
||||||
use {
|
log::debug!("Entered kmain");
|
||||||
crate::{
|
|
||||||
bootmodules::{build_cmd, BootModules},
|
|
||||||
device_tree::DeviceTree,
|
|
||||||
scheduler::Scheduler,
|
|
||||||
},
|
|
||||||
alloc::format,
|
|
||||||
log::{debug, info, trace},
|
|
||||||
spin::{Lazy, Mutex},
|
|
||||||
xml::XMLElement,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub fn kmain(cmdline: &str, boot_modules: BootModules) -> ! {
|
// TODO: Actual cmdline parsing (Serde?)
|
||||||
debug!("Entered kmain");
|
if cmdline.contains("baka=9") {
|
||||||
|
let _ = crate::arch::log(format_args!(include_str!("../data/⑨. バカ")));
|
||||||
let kcmd = build_cmd("Kernel Command Line", &cmdline);
|
|
||||||
trace!("Cmdline: {kcmd:?}");
|
|
||||||
|
|
||||||
for (i, bm) in boot_modules.iter().enumerate() {
|
|
||||||
let name = format!("module-{}", i);
|
|
||||||
let _bmcmd: XMLElement;
|
|
||||||
if bm.cmd.len() >= 2 {
|
|
||||||
// TODO: pass into the program
|
|
||||||
_bmcmd = build_cmd(name, bm.cmd.clone());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let dt = DEVICE_TREE.lock();
|
log::info!("Cmdline: \"{cmdline}\"");
|
||||||
|
let bootstrap = bootstrap.expect("no bootstrap found");
|
||||||
|
|
||||||
info!("Device Tree: {}", dt);
|
crate::arch::sloop()
|
||||||
info!("Boot complete. Moving to init_system");
|
|
||||||
|
|
||||||
// TODO: schedule the disk driver from the initramfs
|
|
||||||
// TODO: schedule the filesystem driver from the initramfs
|
|
||||||
// TODO: schedule the init system from the initramfs
|
|
||||||
|
|
||||||
// capabilities::example();
|
|
||||||
|
|
||||||
let mut sched = Scheduler::new();
|
|
||||||
|
|
||||||
sched.new_process(boot_modules[0].bytes.clone());
|
|
||||||
sched.new_process(boot_modules[1].bytes.clone());
|
|
||||||
|
|
||||||
sched.run();
|
|
||||||
// spin_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub static DEVICE_TREE: Lazy<Mutex<DeviceTree>> = Lazy::new(|| {
|
|
||||||
let dt = DeviceTree::new();
|
|
||||||
Mutex::new(dt)
|
|
||||||
});
|
|
||||||
|
|
||||||
#[test_case]
|
|
||||||
fn trivial_assertion() {
|
|
||||||
trace!("trivial assertion... ");
|
|
||||||
assert_eq!(1, 1);
|
|
||||||
info!("[ok]");
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,5 @@
|
||||||
//! The ableOS kernel.
|
//! The ableOS kernel.
|
||||||
//! Named akern.
|
|
||||||
//! Akern is woefully undersupported at the moment but we are looking to add support improve hardware discovery and make our lives as kernel and operating system developers easier and better
|
|
||||||
|
|
||||||
#![no_std]
|
|
||||||
#![feature(
|
#![feature(
|
||||||
abi_x86_interrupt,
|
abi_x86_interrupt,
|
||||||
alloc_error_handler,
|
alloc_error_handler,
|
||||||
|
@ -10,28 +7,19 @@
|
||||||
panic_info_message,
|
panic_info_message,
|
||||||
pointer_is_aligned,
|
pointer_is_aligned,
|
||||||
prelude_import,
|
prelude_import,
|
||||||
ptr_sub_ptr,
|
ptr_sub_ptr
|
||||||
custom_test_frameworks
|
|
||||||
)]
|
)]
|
||||||
#![deny(clippy::pedantic, missing_docs, warnings)]
|
#![no_std]
|
||||||
#![allow(dead_code)]
|
|
||||||
#![test_runner(crate::test_runner)]
|
|
||||||
|
|
||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
|
|
||||||
mod allocator;
|
mod allocator;
|
||||||
mod arch;
|
mod arch;
|
||||||
mod bootmodules;
|
|
||||||
pub mod capabilities;
|
|
||||||
pub mod device_tree;
|
|
||||||
pub mod handle;
|
|
||||||
pub mod host;
|
|
||||||
pub mod ipc;
|
|
||||||
mod kmain;
|
mod kmain;
|
||||||
mod logger;
|
mod logger;
|
||||||
mod memory;
|
mod memory;
|
||||||
mod scheduler;
|
mod task;
|
||||||
pub mod utils;
|
mod ubloader;
|
||||||
|
|
||||||
use versioning::Version;
|
use versioning::Version;
|
||||||
|
|
||||||
|
@ -59,11 +47,3 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
|
||||||
|
|
||||||
loop {}
|
loop {}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
fn test_runner(tests: &[&dyn Fn()]) {
|
|
||||||
println!("Running {} tests", tests.len());
|
|
||||||
for test in tests {
|
|
||||||
test();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,32 +1,29 @@
|
||||||
// TODO: Add a logger api with logger levels and various outputs
|
|
||||||
|
|
||||||
use log::{Level, SetLoggerError};
|
use log::{Level, SetLoggerError};
|
||||||
|
|
||||||
pub fn init() -> Result<(), SetLoggerError> {
|
pub fn init() -> Result<(), SetLoggerError> {
|
||||||
log::set_logger(&crate::logger::Logger)?;
|
log::set_logger(&crate::logger::Logger)?;
|
||||||
log::set_max_level(log::LevelFilter::Debug);
|
log::set_max_level(log::LevelFilter::Trace);
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Logger;
|
struct Logger;
|
||||||
impl log::Log for Logger {
|
impl log::Log for Logger {
|
||||||
fn enabled(&self, _metadata: &log::Metadata) -> bool {
|
fn enabled(&self, metadata: &log::Metadata) -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
fn log(&self, record: &log::Record) {
|
fn log(&self, record: &log::Record) {
|
||||||
let lvl = record.level();
|
let lvl = record.level();
|
||||||
let lvl_color = match lvl {
|
|
||||||
Level::Error => "160",
|
|
||||||
Level::Warn => "172",
|
|
||||||
Level::Info => "47",
|
|
||||||
Level::Debug => "25",
|
|
||||||
Level::Trace => "103",
|
|
||||||
};
|
|
||||||
let module = record.module_path().unwrap_or_default();
|
|
||||||
let line = record.line().unwrap_or_default();
|
|
||||||
crate::arch::log(format_args!(
|
crate::arch::log(format_args!(
|
||||||
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
|
"\x1b[38;5;{}m{lvl}\x1b[0m [{}]: {}\r\n",
|
||||||
|
match lvl {
|
||||||
|
Level::Error => "160",
|
||||||
|
Level::Warn => "172",
|
||||||
|
Level::Info => "47",
|
||||||
|
Level::Debug => "25",
|
||||||
|
Level::Trace => "103",
|
||||||
|
},
|
||||||
|
record.module_path().unwrap_or_default(),
|
||||||
record.args(),
|
record.args(),
|
||||||
))
|
))
|
||||||
.expect("write to serial console");
|
.expect("write to serial console");
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
#![no_std]
|
#![no_std]
|
||||||
#![no_main]
|
#![no_main]
|
||||||
|
|
||||||
extern crate kernel;
|
extern crate kernel;
|
||||||
|
|
|
@ -1,76 +0,0 @@
|
||||||
use {
|
|
||||||
alloc::{collections::VecDeque, rc::Rc, slice, vec::Vec},
|
|
||||||
hbvm::validate::validate,
|
|
||||||
};
|
|
||||||
|
|
||||||
use {crate::host::TrapHandler, hbvm::vm::Vm};
|
|
||||||
const TIMER_QUOTIENT: usize = 100;
|
|
||||||
|
|
||||||
pub struct Scheduler<'a> {
|
|
||||||
data: VecDeque<Vm<'a, TrapHandler, TIMER_QUOTIENT>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: This is a very simple schduler and it sucks and should be replaced with a better one
|
|
||||||
// Written By Yours Truly: Munir
|
|
||||||
|
|
||||||
impl Scheduler<'_> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
Self {
|
|
||||||
data: VecDeque::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn new_process(&mut self, program: Vec<u8>) {
|
|
||||||
let prog = program.clone();
|
|
||||||
let prog_arc = Rc::new(prog);
|
|
||||||
|
|
||||||
let binding = Rc::try_unwrap(prog_arc).ok().unwrap();
|
|
||||||
|
|
||||||
#[allow(clippy::redundant_else)]
|
|
||||||
if let Err(e) = validate(&program.as_slice()) {
|
|
||||||
log::error!("Program validation error: {e:?}");
|
|
||||||
} else {
|
|
||||||
log::info!("valid program");
|
|
||||||
unsafe {
|
|
||||||
let slice = slice::from_raw_parts(binding.as_ptr(), binding.len());
|
|
||||||
let mut vm = Vm::new_unchecked(&*slice, TrapHandler);
|
|
||||||
vm.memory.insert_test_page();
|
|
||||||
self.data.push_front(vm);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn run(&mut self) -> ! {
|
|
||||||
loop {
|
|
||||||
// If there are no programs to run then sleep.
|
|
||||||
if self.data.is_empty() {
|
|
||||||
use crate::arch::spin_loop;
|
|
||||||
spin_loop();
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut prog = self.data.pop_front().unwrap();
|
|
||||||
let ret = prog.run();
|
|
||||||
match ret {
|
|
||||||
Ok(oki) => match oki {
|
|
||||||
hbvm::vm::VmRunOk::End => {
|
|
||||||
log::info!(
|
|
||||||
"Program ended. {} programs remaining.",
|
|
||||||
// Add one here because we pop a program
|
|
||||||
self.data.len() + 1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
hbvm::vm::VmRunOk::Timer => {
|
|
||||||
log::info!("Timer exhausted. Scheduled program");
|
|
||||||
self.data.push_back(prog);
|
|
||||||
}
|
|
||||||
hbvm::vm::VmRunOk::Ecall => {
|
|
||||||
// panic!();
|
|
||||||
log::info!("{:?}", prog.registers);
|
|
||||||
self.data.push_back(prog);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
Err(_) => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
149
kernel/src/task.rs
Normal file
149
kernel/src/task.rs
Normal file
|
@ -0,0 +1,149 @@
|
||||||
|
//! Async task and executor
|
||||||
|
|
||||||
|
use alloc::{boxed::Box, collections::BTreeMap, sync::Arc, task::Wake};
|
||||||
|
use core::{
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll, Waker},
|
||||||
|
};
|
||||||
|
use crossbeam_queue::SegQueue;
|
||||||
|
use slab::Slab;
|
||||||
|
use spin::RwLock;
|
||||||
|
|
||||||
|
type TaskQueue = Arc<SegQueue<TaskId>>;
|
||||||
|
type SpawnQueue = Arc<SegQueue<Task>>;
|
||||||
|
|
||||||
|
static SPAWN_QUEUE: RwLock<Option<SpawnQueue>> = RwLock::new(None);
|
||||||
|
|
||||||
|
/// Spawn a new task
|
||||||
|
pub fn spawn(future: impl Future<Output = ()> + Send + 'static) {
|
||||||
|
match &*SPAWN_QUEUE.read() {
|
||||||
|
Some(s) => s.push(Task::new(future)),
|
||||||
|
None => panic!("no task executor is running"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Forcibly yield a task
|
||||||
|
pub fn yield_now() -> impl Future<Output = ()> {
|
||||||
|
struct YieldNow(bool);
|
||||||
|
impl Future for YieldNow {
|
||||||
|
type Output = ();
|
||||||
|
|
||||||
|
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
|
||||||
|
if self.0 {
|
||||||
|
Poll::Ready(())
|
||||||
|
} else {
|
||||||
|
self.0 = true;
|
||||||
|
cx.waker().wake_by_ref();
|
||||||
|
Poll::Pending
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
YieldNow(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Tasks executor
|
||||||
|
#[derive(Default)]
|
||||||
|
pub struct Executor {
|
||||||
|
/// All spawned tasks' stash
|
||||||
|
tasks: Slab<Task>,
|
||||||
|
|
||||||
|
/// Awake tasks' queue
|
||||||
|
queue: TaskQueue,
|
||||||
|
|
||||||
|
/// Incoming tasks to enqueue
|
||||||
|
incoming: SpawnQueue,
|
||||||
|
|
||||||
|
/// Wakers
|
||||||
|
wakers: BTreeMap<TaskId, Waker>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Executor {
|
||||||
|
/// Spawn a task
|
||||||
|
pub fn spawn(&mut self, future: impl Future<Output = ()> + Send + 'static) {
|
||||||
|
self.queue
|
||||||
|
.push(TaskId(self.tasks.insert(Task::new(future))));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Spin poll loop until it runs out of tasks
|
||||||
|
pub fn run(&mut self) {
|
||||||
|
// Assign `self.incoming` to global spawn queue to spawn tasks
|
||||||
|
// from within
|
||||||
|
{
|
||||||
|
let mut spawner = SPAWN_QUEUE.write();
|
||||||
|
if spawner.is_some() {
|
||||||
|
panic!("task executor is already running");
|
||||||
|
}
|
||||||
|
|
||||||
|
*spawner = Some(Arc::clone(&self.incoming));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Try to get incoming task, if none available, poll
|
||||||
|
// enqueued one
|
||||||
|
while let Some(id) = self
|
||||||
|
.incoming
|
||||||
|
.pop()
|
||||||
|
.map(|t| TaskId(self.tasks.insert(t)))
|
||||||
|
.or_else(|| self.queue.pop())
|
||||||
|
{
|
||||||
|
let Some(task) = self.tasks.get_mut(id.0) else {
|
||||||
|
panic!("attempted to get non-extant task with id {}", id.0)
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut cx = Context::from_waker(self.wakers.entry(id).or_insert_with(|| {
|
||||||
|
Waker::from(Arc::new(TaskWaker {
|
||||||
|
id,
|
||||||
|
queue: Arc::clone(&self.queue),
|
||||||
|
}))
|
||||||
|
}));
|
||||||
|
|
||||||
|
match task.poll(&mut cx) {
|
||||||
|
Poll::Ready(()) => {
|
||||||
|
// Task done, unregister
|
||||||
|
self.tasks.remove(id.0);
|
||||||
|
self.wakers.remove(&id);
|
||||||
|
}
|
||||||
|
Poll::Pending => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*SPAWN_QUEUE.write() = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
|
struct TaskId(usize);
|
||||||
|
|
||||||
|
/// Async task
|
||||||
|
struct Task {
|
||||||
|
future: Pin<Box<dyn Future<Output = ()> + Send>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Task {
|
||||||
|
/// Create a new task from a future
|
||||||
|
fn new(future: impl Future<Output = ()> + Send + 'static) -> Self {
|
||||||
|
Self {
|
||||||
|
future: Box::pin(future),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn poll(&mut self, cx: &mut Context) -> Poll<()> {
|
||||||
|
self.future.as_mut().poll(cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TaskWaker {
|
||||||
|
id: TaskId,
|
||||||
|
queue: TaskQueue,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Wake for TaskWaker {
|
||||||
|
fn wake(self: Arc<Self>) {
|
||||||
|
self.wake_by_ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn wake_by_ref(self: &Arc<Self>) {
|
||||||
|
self.queue.push(self.id);
|
||||||
|
}
|
||||||
|
}
|
0
kernel/src/ubloader.rs
Normal file
0
kernel/src/ubloader.rs
Normal file
|
@ -1,53 +0,0 @@
|
||||||
//! A module for small utilities to be used kernel wide
|
|
||||||
//! Simple functions and constants
|
|
||||||
|
|
||||||
/// Used when tab `\t` in hardware is not known and we will default to two spaces
|
|
||||||
pub const TAB: &str = " ";
|
|
||||||
|
|
||||||
|
|
||||||
// NOTE: this only reduces the code duplication in source code not in generated code!
|
|
||||||
// Written by Yours Truly: Munir
|
|
||||||
/// A simple macro to reduce code duplication when we use TAB internally
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! tab {
|
|
||||||
($num:expr) => {
|
|
||||||
TAB.repeat($num)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: this only reduces the code duplication in source code not in generated code!
|
|
||||||
// Written by Yours Truly: Munir
|
|
||||||
/// A simple macro to reduce code duplication when we insert device types into the device tree
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! device_tree {
|
|
||||||
($devtree:expr, $dev_type_vec:expr) => {
|
|
||||||
for each_device_type in $dev_type_vec {
|
|
||||||
$devtree.devices.insert(each_device_type.to_string(), Vec::new());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE: this only reduces the code duplication in source code not in generated code!
|
|
||||||
// Written by Yours Truly: Munir
|
|
||||||
/// A simple macro to reduce code duplication when we insert cpu features into the array/vector
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! cpu_features {
|
|
||||||
($cpu_master_object:expr, $result_vec:expr) => {
|
|
||||||
// checks for cpu features and returns bool for each feature
|
|
||||||
let apic = $cpu_master_object.apic();
|
|
||||||
let avx = $cpu_master_object.avx();
|
|
||||||
let avx2 = $cpu_master_object.avx2();
|
|
||||||
let x2 = $cpu_master_object.x2apic();
|
|
||||||
let gb_pages = $cpu_master_object.gigabyte_pages();
|
|
||||||
let rdseed = $cpu_master_object.rdseed();
|
|
||||||
let rdrand = $cpu_master_object.rdrand();
|
|
||||||
|
|
||||||
$result_vec.push(("apic", apic));
|
|
||||||
$result_vec.push(("avx", avx));
|
|
||||||
$result_vec.push(("avx2", avx2));
|
|
||||||
$result_vec.push(("gigabyte pages", gb_pages));
|
|
||||||
$result_vec.push(("rdrand", rdrand));
|
|
||||||
$result_vec.push(("rdseed", rdseed));
|
|
||||||
$result_vec.push(("x2apic", x2));
|
|
||||||
};
|
|
||||||
}
|
|
|
@ -1,25 +0,0 @@
|
||||||
{
|
|
||||||
"arch": "aarch64",
|
|
||||||
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128",
|
|
||||||
"disable-redzone": true,
|
|
||||||
"env": "",
|
|
||||||
"executables": true,
|
|
||||||
"features": "+strict-align,+neon,+fp-armv8",
|
|
||||||
"linker": "rust-lld",
|
|
||||||
"linker-flavor": "ld.lld",
|
|
||||||
"linker-is-gnu": true,
|
|
||||||
"pre-link-args": {
|
|
||||||
"ld.lld": [
|
|
||||||
"-Tkernel/lds/aarch64-qemu.ld"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"llvm-target": "aarch64-unknown-none",
|
|
||||||
"max-atomic-width": 128,
|
|
||||||
"os": "none",
|
|
||||||
"panic-strategy": "abort",
|
|
||||||
"relocation-model": "static",
|
|
||||||
"target-c-int-width": "32",
|
|
||||||
"target-endian": "little",
|
|
||||||
"target-pointer-width": "64",
|
|
||||||
"vendor": ""
|
|
||||||
}
|
|
2
limine
2
limine
|
@ -1 +1 @@
|
||||||
Subproject commit 2d3d7b2633b5d95ce404a177ada0d5cbee802721
|
Subproject commit 83779119f658c8ce337c33492266ecf8300db429
|
|
@ -4,11 +4,7 @@ version = "0.2.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
derive_more = "0.99"
|
env_logger = "0.10"
|
||||||
error-stack = "0.2"
|
error-stack = "0.2"
|
||||||
fatfs = "0.3"
|
fatfs = "0.3"
|
||||||
|
log = "0.4"
|
||||||
[dependencies.reqwest]
|
|
||||||
version = "0.11"
|
|
||||||
default-features = false
|
|
||||||
features = ["rustls-tls", "blocking"]
|
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -2,7 +2,7 @@ ${ABLEOS_KERNEL}=boot:///kernel
|
||||||
# TODO: Make a boot background image for ableOS
|
# TODO: Make a boot background image for ableOS
|
||||||
|
|
||||||
DEFAULT_ENTRY=1
|
DEFAULT_ENTRY=1
|
||||||
TIMEOUT=0
|
TIMEOUT=3
|
||||||
VERBOSE=yes
|
VERBOSE=yes
|
||||||
INTERFACE_RESOLUTION=1024x768
|
INTERFACE_RESOLUTION=1024x768
|
||||||
# Terminal related settings
|
# Terminal related settings
|
||||||
|
@ -13,13 +13,9 @@ TERM_BACKDROP=008080
|
||||||
COMMENT=Default AbleOS boot entry.
|
COMMENT=Default AbleOS boot entry.
|
||||||
PROTOCOL=limine
|
PROTOCOL=limine
|
||||||
KERNEL_PATH=${ABLEOS_KERNEL}
|
KERNEL_PATH=${ABLEOS_KERNEL}
|
||||||
# execute is an array of boot modules to execute
|
KERNEL_CMDLINE=
|
||||||
KERNEL_CMDLINE="execute=[0,1]"
|
|
||||||
# Setting a default resolution for the framebuffer
|
# Setting a default resolution for the framebuffer
|
||||||
RESOLUTION=1024x768x24
|
RESOLUTION=1024x768x24
|
||||||
|
|
||||||
MODULE_PATH=boot:///inf_loop.hb
|
# MODULE_PATH=boot:///boot/initrd.tar
|
||||||
MODULE_CMDLINE="diskid=123456789"
|
# MODULE_CMDLINE=This is the first module.
|
||||||
|
|
||||||
MODULE_PATH=boot:///ecall.hb
|
|
||||||
MODULE_CMDLINE=""
|
|
||||||
|
|
|
@ -1,22 +1,12 @@
|
||||||
use {
|
use error_stack::{bail, report, Context, IntoReport, Result, ResultExt};
|
||||||
derive_more::Display,
|
use fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek};
|
||||||
error_stack::{bail, report, Context, IntoReport, Result, ResultExt},
|
use std::{fmt::Display, fs::File, io, path::Path, process::Command};
|
||||||
fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek},
|
|
||||||
std::{fmt::Display, fs::File, io, path::Path, process::Command},
|
|
||||||
};
|
|
||||||
|
|
||||||
fn main() -> Result<(), Error> {
|
fn main() -> Result<(), Error> {
|
||||||
|
env_logger::init();
|
||||||
let mut args = std::env::args();
|
let mut args = std::env::args();
|
||||||
args.next();
|
args.next();
|
||||||
|
|
||||||
// let disk_meta = fs::metadata("target/disk.img").unwrap();
|
|
||||||
// let config_meta = fs::metadata("system.toml").unwrap();
|
|
||||||
|
|
||||||
// if disk_meta.modified().unwrap() < config_meta.modified().unwrap() {
|
|
||||||
// // TODO: work on adding in system.toml support
|
|
||||||
// // TODO: rebuild the disk
|
|
||||||
// }
|
|
||||||
|
|
||||||
match args.next().as_deref() {
|
match args.next().as_deref() {
|
||||||
Some("build" | "b") => {
|
Some("build" | "b") => {
|
||||||
let mut release = false;
|
let mut release = false;
|
||||||
|
@ -24,12 +14,9 @@ fn main() -> Result<(), Error> {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if arg == "-r" || arg == "--release" {
|
if arg == "-r" || arg == "--release" {
|
||||||
release = true;
|
release = true;
|
||||||
} else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
|
}
|
||||||
|
if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
|
||||||
target = Target::Riscv64Virt;
|
target = Target::Riscv64Virt;
|
||||||
} else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" {
|
|
||||||
target = Target::Aarch64;
|
|
||||||
} else {
|
|
||||||
return Err(report!(Error::InvalidSubCom));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,12 +28,9 @@ fn main() -> Result<(), Error> {
|
||||||
for arg in args {
|
for arg in args {
|
||||||
if arg == "-r" || arg == "--release" {
|
if arg == "-r" || arg == "--release" {
|
||||||
release = true;
|
release = true;
|
||||||
} else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
|
}
|
||||||
|
if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
|
||||||
target = Target::Riscv64Virt;
|
target = Target::Riscv64Virt;
|
||||||
} else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" {
|
|
||||||
target = Target::Aarch64;
|
|
||||||
} else {
|
|
||||||
return Err(report!(Error::InvalidSubCom));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,11 +55,25 @@ fn main() -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
|
fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
|
||||||
|
let path = Path::new("target/disk.img");
|
||||||
|
|
||||||
|
match std::fs::metadata(path) {
|
||||||
|
Err(e) if e.kind() == io::ErrorKind::NotFound => (),
|
||||||
|
Err(e) => bail!(e),
|
||||||
|
Ok(_) => {
|
||||||
|
return FileSystem::new(
|
||||||
|
File::options().read(true).write(true).open(path)?,
|
||||||
|
FsOptions::new(),
|
||||||
|
)
|
||||||
|
.into_report()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let mut img = File::options()
|
let mut img = File::options()
|
||||||
.read(true)
|
.read(true)
|
||||||
.write(true)
|
.write(true)
|
||||||
.create(true)
|
.create(true)
|
||||||
.open(Path::new("target/disk.img"))?;
|
.open(path)?;
|
||||||
|
|
||||||
img.set_len(1024 * 1024 * 64)?;
|
img.set_len(1024 * 1024 * 64)?;
|
||||||
|
|
||||||
|
@ -91,15 +89,6 @@ fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
|
||||||
&mut bootdir.create_file("bootx64.efi")?,
|
&mut bootdir.create_file("bootx64.efi")?,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
io::copy(
|
|
||||||
&mut File::open("limine/BOOTAA64.EFI")
|
|
||||||
.into_report()
|
|
||||||
.attach_printable(
|
|
||||||
"copying Limine bootloader arm version (have you pulled the submodule?)",
|
|
||||||
)?,
|
|
||||||
&mut bootdir.create_file("bootaa64.efi")?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
io::copy(
|
io::copy(
|
||||||
&mut File::open("repbuild/limine.cfg")?,
|
&mut File::open("repbuild/limine.cfg")?,
|
||||||
&mut fs.root_dir().create_file("limine.cfg")?,
|
&mut fs.root_dir().create_file("limine.cfg")?,
|
||||||
|
@ -109,15 +98,6 @@ fn get_fs() -> Result<FileSystem<impl ReadWriteSeek>, io::Error> {
|
||||||
&mut File::open("repbuild/background.bmp")?,
|
&mut File::open("repbuild/background.bmp")?,
|
||||||
&mut fs.root_dir().create_file("background.bmp")?,
|
&mut fs.root_dir().create_file("background.bmp")?,
|
||||||
)?;
|
)?;
|
||||||
io::copy(
|
|
||||||
&mut File::open("repbuild/holeybytes/inf_loop.hb")?,
|
|
||||||
&mut fs.root_dir().create_file("inf_loop.hb")?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
io::copy(
|
|
||||||
&mut File::open("repbuild/holeybytes/ecall.hb")?,
|
|
||||||
&mut fs.root_dir().create_file("ecall.hb")?,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
drop(bootdir);
|
drop(bootdir);
|
||||||
Ok(fs)
|
Ok(fs)
|
||||||
|
@ -132,11 +112,11 @@ fn build(release: bool, target: Target) -> Result<(), Error> {
|
||||||
com.arg("-r");
|
com.arg("-r");
|
||||||
}
|
}
|
||||||
|
|
||||||
if target == Target::Riscv64Virt {
|
match target {
|
||||||
com.args(["--target", "targets/riscv64-virt-ableos.json"]);
|
Target::Riscv64Virt => {
|
||||||
}
|
com.args(["--target", "targets/riscv64-virt-ableos.json"]);
|
||||||
if target == Target::Aarch64 {
|
}
|
||||||
com.args(["--target", "targets/aarch64-virt-ableos.json"]);
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
match com.status() {
|
match com.status() {
|
||||||
|
@ -145,16 +125,14 @@ fn build(release: bool, target: Target) -> Result<(), Error> {
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
let kernel_dir = match target {
|
if target != Target::X86_64 {
|
||||||
Target::X86_64 => "target/x86_64-ableos",
|
return Ok(());
|
||||||
Target::Riscv64Virt => "target/riscv64-virt-ableos",
|
}
|
||||||
Target::Aarch64 => "target/aarch64-virt-ableos",
|
|
||||||
};
|
|
||||||
|
|
||||||
(|| -> std::io::Result<_> {
|
(|| -> std::io::Result<_> {
|
||||||
io::copy(
|
io::copy(
|
||||||
&mut File::open(
|
&mut File::open(
|
||||||
Path::new(kernel_dir)
|
Path::new("target/x86_64-ableos")
|
||||||
.join(if release { "release" } else { "debug" })
|
.join(if release { "release" } else { "debug" })
|
||||||
.join("kernel"),
|
.join("kernel"),
|
||||||
)?,
|
)?,
|
||||||
|
@ -170,49 +148,42 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
|
||||||
let mut com = match target {
|
let mut com = match target {
|
||||||
Target::X86_64 => Command::new("qemu-system-x86_64"),
|
Target::X86_64 => Command::new("qemu-system-x86_64"),
|
||||||
Target::Riscv64Virt => Command::new("qemu-system-riscv64"),
|
Target::Riscv64Virt => Command::new("qemu-system-riscv64"),
|
||||||
Target::Aarch64 => Command::new("qemu-system-aarch64"),
|
|
||||||
};
|
};
|
||||||
let ovmf_path = fetch_ovmf(target);
|
|
||||||
|
|
||||||
match target {
|
if target == Target::X86_64 {
|
||||||
Target::X86_64 => {
|
#[rustfmt::skip]
|
||||||
#[rustfmt::skip]
|
com.args([
|
||||||
com.args([
|
"-bios",
|
||||||
"-bios", &ovmf_path.change_context(Error::OvmfFetch)?,
|
std::env::var("REPBUILD_QEMU_FIRMWARE_PATH")
|
||||||
"-drive", "file=target/disk.img,format=raw",
|
.as_deref()
|
||||||
"-m", "4G",
|
.unwrap_or("/usr/share/OVMF/OVMF_CODE.fd"),
|
||||||
"-smp", "cores=4",
|
"-drive", "file=target/disk.img,format=raw",
|
||||||
]);
|
"-m", "4G",
|
||||||
}
|
"-serial", "stdio",
|
||||||
Target::Riscv64Virt => {
|
"-smp", "cores=2",
|
||||||
#[rustfmt::skip]
|
]);
|
||||||
com.args([
|
|
||||||
"-M", "virt",
|
#[cfg(target_os = "linux")]
|
||||||
"-m", "128M",
|
{
|
||||||
"-serial", "stdio",
|
com.args(["-enable-kvm", "-cpu", "host"]);
|
||||||
"-kernel",
|
|
||||||
if release {
|
|
||||||
"target/riscv64-virt-ableos/release/kernel"
|
|
||||||
} else {
|
|
||||||
"target/riscv64-virt-ableos/debug/kernel"
|
|
||||||
}
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
Target::Aarch64 => {
|
|
||||||
#[rustfmt::skip]
|
|
||||||
com.args([
|
|
||||||
"-M", "virt",
|
|
||||||
"-cpu", "cortex-a72",
|
|
||||||
"-device", "ramfb",
|
|
||||||
"-device", "qemu-xhci",
|
|
||||||
"-device", "usb-kbd",
|
|
||||||
"-m", "2G",
|
|
||||||
"-bios", &ovmf_path.change_context(Error::OvmfFetch)?,
|
|
||||||
"-drive", "file=target/disk.img,format=raw",
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if target == Target::Riscv64Virt {
|
||||||
|
#[rustfmt::skip]
|
||||||
|
com.args([
|
||||||
|
"-M", "virt",
|
||||||
|
"-m", "128M",
|
||||||
|
"-serial", "stdio",
|
||||||
|
"-kernel",
|
||||||
|
if release {
|
||||||
|
"target/riscv64-virt-ableos/release/kernel"
|
||||||
|
} else {
|
||||||
|
"target/riscv64-virt-ableos/debug/kernel"
|
||||||
|
}
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
match com
|
match com
|
||||||
.status()
|
.status()
|
||||||
.into_report()
|
.into_report()
|
||||||
|
@ -223,90 +194,33 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn fetch_ovmf(target: Target) -> Result<String, OvmfFetchError> {
|
|
||||||
let (ovmf_url, ovmf_path) = match target {
|
|
||||||
Target::X86_64 => (
|
|
||||||
"https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd",
|
|
||||||
"target/RELEASEX64_OVMF.fd",
|
|
||||||
),
|
|
||||||
Target::Riscv64Virt => return Err(OvmfFetchError::Empty.into()),
|
|
||||||
Target::Aarch64 => (
|
|
||||||
"https://retrage.github.io/edk2-nightly/bin/RELEASEAARCH64_QEMU_EFI.fd",
|
|
||||||
"target/RELEASEAARCH64_QEMU_EFI.fd",
|
|
||||||
),
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut file = match std::fs::metadata(ovmf_path) {
|
|
||||||
Err(e) if e.kind() == std::io::ErrorKind::NotFound => std::fs::OpenOptions::new()
|
|
||||||
.create(true)
|
|
||||||
.write(true)
|
|
||||||
.read(true)
|
|
||||||
.open(ovmf_path)
|
|
||||||
.into_report()
|
|
||||||
.change_context(OvmfFetchError::Io)?,
|
|
||||||
Ok(_) => return Ok(ovmf_path.to_owned()),
|
|
||||||
Err(e) => return Err(report!(e).change_context(OvmfFetchError::Io)),
|
|
||||||
};
|
|
||||||
let mut bytes = reqwest::blocking::get(ovmf_url)
|
|
||||||
.into_report()
|
|
||||||
.change_context(OvmfFetchError::Fetch)?;
|
|
||||||
|
|
||||||
bytes
|
|
||||||
.copy_to(&mut file)
|
|
||||||
.into_report()
|
|
||||||
.change_context(OvmfFetchError::Io)?;
|
|
||||||
|
|
||||||
Ok(ovmf_path.to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Display)]
|
|
||||||
enum OvmfFetchError {
|
|
||||||
#[display(fmt = "Failed to fetch OVMF package")]
|
|
||||||
Fetch,
|
|
||||||
#[display(fmt = "No OVMF package available")]
|
|
||||||
Empty,
|
|
||||||
#[display(fmt = "IO Error")]
|
|
||||||
Io,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Context for OvmfFetchError {}
|
|
||||||
|
|
||||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||||
enum Target {
|
enum Target {
|
||||||
X86_64,
|
X86_64,
|
||||||
Riscv64Virt,
|
Riscv64Virt,
|
||||||
Aarch64,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Display)]
|
#[derive(Debug)]
|
||||||
enum Error {
|
enum Error {
|
||||||
#[display(fmt = "Failed to build the kernel")]
|
|
||||||
Build,
|
Build,
|
||||||
#[display(fmt = "Missing or invalid subcommand (available: build, run)")]
|
|
||||||
InvalidSubCom,
|
InvalidSubCom,
|
||||||
#[display(fmt = "IO Error")]
|
|
||||||
Io,
|
Io,
|
||||||
#[display(fmt = "Failed to spawn a process")]
|
|
||||||
ProcessSpawn,
|
ProcessSpawn,
|
||||||
#[display(fmt = "Failed to fetch UEFI firmware")]
|
|
||||||
OvmfFetch,
|
|
||||||
#[display(fmt = "QEMU Error: {}", "fmt_qemu_err(*_0)")]
|
|
||||||
Qemu(Option<i32>),
|
Qemu(Option<i32>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Context for Error {}
|
impl Context for Error {}
|
||||||
|
impl Display for Error {
|
||||||
fn fmt_qemu_err(e: Option<i32>) -> impl Display {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
struct W(Option<i32>);
|
match self {
|
||||||
impl Display for W {
|
Self::Build => f.write_str("failed to build the kernel"),
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
Self::InvalidSubCom => {
|
||||||
if let Some(c) = self.0 {
|
f.write_str("missing or invalid subcommand (available: build, run)")
|
||||||
c.fmt(f)
|
|
||||||
} else {
|
|
||||||
f.write_str("Interrupted by signal")
|
|
||||||
}
|
}
|
||||||
|
Self::Io => f.write_str("IO error"),
|
||||||
|
Self::ProcessSpawn => f.write_str("failed to spawn a process"),
|
||||||
|
Self::Qemu(Some(c)) => write!(f, "QEMU Error: {c}"),
|
||||||
|
Self::Qemu(None) => write!(f, "QEMU Error: interrupted by signal"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
W(e)
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
hex_literal_case = "Upper"
|
|
||||||
imports_granularity = "One"
|
|
||||||
struct_field_align_threshold = 5
|
|
41
shell.nix
41
shell.nix
|
@ -1,41 +0,0 @@
|
||||||
{ pkgs ? import <nixpkgs> { } }:
|
|
||||||
pkgs.mkShell rec {
|
|
||||||
buildInputs = with pkgs; [
|
|
||||||
clang
|
|
||||||
llvmPackages.bintools
|
|
||||||
rustup
|
|
||||||
qemu_full
|
|
||||||
# OMVFFull
|
|
||||||
# OMVF
|
|
||||||
];
|
|
||||||
extraCmds = '''';
|
|
||||||
RUSTC_VERSION = pkgs.lib.readFile ./rust-toolchain.toml;
|
|
||||||
# https://github.com/rust-lang/rust-bindgen#environment-variables
|
|
||||||
LIBCLANG_PATH =
|
|
||||||
pkgs.lib.makeLibraryPath [ pkgs.llvmPackages_latest.libclang.lib ];
|
|
||||||
shellHook = ''
|
|
||||||
export REPBUILD_QEMU_FIRMWARE_PATH=${pkgs.OVMF.fd}/FV/OVMF.fd
|
|
||||||
|
|
||||||
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
|
||||||
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/$RUSTC_VERSION-x86_64-unknown-linux-gnu/bin/
|
|
||||||
'';
|
|
||||||
# Add precompiled library to rustc search path
|
|
||||||
RUSTFLAGS = (builtins.map (a: "-L ${a}/lib") [
|
|
||||||
# add libraries here (e.g. pkgs.libvmi)
|
|
||||||
]);
|
|
||||||
# Add glibc, clang, glib and other headers to bindgen search path
|
|
||||||
BINDGEN_EXTRA_CLANG_ARGS =
|
|
||||||
# Includes with normal include path
|
|
||||||
(builtins.map (a: ''-I"${a}/include"'') [
|
|
||||||
# add dev libraries here (e.g. pkgs.libvmi.dev)
|
|
||||||
pkgs.glibc.dev
|
|
||||||
])
|
|
||||||
# Includes with special directory paths
|
|
||||||
++ [
|
|
||||||
''
|
|
||||||
-I"${pkgs.llvmPackages_latest.libclang.lib}/lib/clang/${pkgs.llvmPackages_latest.libclang.version}/include"''
|
|
||||||
''-I"${pkgs.glib.dev}/include/glib-2.0"''
|
|
||||||
"-I${pkgs.glib.out}/lib/glib-2.0/include/"
|
|
||||||
];
|
|
||||||
|
|
||||||
}
|
|
31
system.toml
31
system.toml
|
@ -1,31 +0,0 @@
|
||||||
[boot]
|
|
||||||
# This package must be installed system wide
|
|
||||||
init = "init_server"
|
|
||||||
|
|
||||||
|
|
||||||
[boot.limine]
|
|
||||||
default_entry = 1
|
|
||||||
timeout = 0
|
|
||||||
verbose = true
|
|
||||||
interface_resolution = "1024x768x24"
|
|
||||||
# Terminal related settings
|
|
||||||
term_wallpaper = "boot:///background.bmp"
|
|
||||||
term_background = "008080"
|
|
||||||
|
|
||||||
[boot.limine.ableos]
|
|
||||||
comment = "Default AbleOS boot entry."
|
|
||||||
protocol = "limine"
|
|
||||||
kernel_path = "boot:///kernel"
|
|
||||||
kernel_cmdline = "baka=false foobles=true"
|
|
||||||
resolution = "1024x768x24"
|
|
||||||
|
|
||||||
[repositories]
|
|
||||||
core = "https://git.ablecorp.us/AbleOS/core"
|
|
||||||
userspace = "https://git.ablecorp.us/AbleOS/ableos_userland"
|
|
||||||
|
|
||||||
|
|
||||||
[packages]
|
|
||||||
[packages.init_server]
|
|
||||||
version = "1.0"
|
|
||||||
hash = ""
|
|
||||||
repo = "core"
|
|
Loading…
Reference in a new issue