I'll look into it @ms140569 :)
@ all : Does anyone know some best practices for coping with nested locking in the logging- and panic infrastructure? In my (very basic!) kernel I have a logging facade that logs to serial and the frame buffer. The facade is behind a Mutex. A panic calls my logging facade (via the log crate). Hence, if inside my logging facade a panic occurs, I have a deadlock/nested locking problem. The system is not functional and I will never output an error message.
Are there any cool tips here? Anything except "don't use locking"?
no_panic
crate, but this apparently only works with unwinding.
Hi guys, im kinda new to os dev and gave myself the project to write a small shell in uefi interface and for that i'm using uefi-rs.
And I found myself struggling with reading an input, I get that I have to use stdin.read_key()
but it sounds like I have to use it with something like:
let mut events = [ stdin.wait_for_key_event()];
boot_services.wait_for_event(events); // didn't actually tried this bit of code
Like am I on the right way ? I dont really get how to use it with stdin.read_key()
. Could someone show me a little snippet about how I'm suppose to achieve this ?
Hi, I got my way with my previous question on my own but I have a new one which I really need your help : I'm trying to read a file which is inside the EFI partition, so I look up for the C way of doing it and it seems like I need to get an instance of SimpleFileSystem
to call open_volume()
.
But in C they instantiate it with a NULL pointer, which is impossible in Rust right ? So how could I call an instanceSimpleFileSystem
in Rust ?
I tried by looking at the source but I dont yet understand everything in this struct :
pub struct SimpleFileSystem {
revision: u64,
open_volume:
extern "efiapi" fn(this: &mut SimpleFileSystem, root: &mut *mut FileImpl) -> Status,
}
I would really appreciate your help please
let fs = uefi_boot_system_table
.boot_services()
.locate_protocol::<SimpleFileSystem>()
.unwrap()
.unwrap();
let fs = unsafe { &mut *fs.get() };
// TODO: not sure what will be inside buf; is there already a type for that in the UEFI crate?
let mut buf = [0; 4096];
let mut dir = fs.open_volume().unwrap().unwrap();
let res = dir.read_entry(&mut buf).unwrap().unwrap().unwrap();
log::debug!("{:#?}", res);
[DEBUG] src/main.rs@82: NamedFileProtocolInfo {
header: FileInfoHeader {
size: 88,
file_size: 8192,
physical_size: 8192,
create_time: 2022-01-24 22:46:34.000000000, Timezone=local, Daylight=(empty),
last_access_time: 2022-01-24 00:00:00.000000000, Timezone=local, Daylight=(empty),
modification_time: 2022-01-24 22:46:34.000000000, Timezone=local, Daylight=(empty),
attribute: DIRECTORY,
},
name: [
'E',
'F',
'I',
'\u{0}',
],
}
bootloader
crate: https://github.com/rust-osdev/bootloader/blob/9583eafafe7aaa5282d4739006fc7172d8d1552e/src/bin/uefi.rs#L123-L175 . The difference to your example is that I'm also using the LoadedImage and DevicePath protocols. I don't remember why I did it this way though :D
Hi, sorry I took so long to answer, I hadn't so much time to work on this since last time.
Anyway, big thanks to both of you, I'm inspired myself from your code to make it work !
I used @phip1611 's part to open the protocol and @phil-opp:matrix.org to read my file.
To provide a proper buffer to convert to str
after, I used this piece of code which was pretty neat :
let buf_layout = Layout::array::<u8>(file_size).unwrap();
let mut buf = allocate_buffer(buf_layout); // Return a 'Box<[u8]>'
So just so I understand, this is the way instantiate any protocol right ?
bootimage
together with the 0.10
release of bootloader
, is that right? The bootimage
crate only works on bootloader v0.9
so I recommend to use this older version for now.
Hi again guys, i'm trying to boot to windows from my uefi application, to achieve this I wrote this :
let windows_efi = match read_file(&system_table, "EFI\\Microsoft\\Boot\\bootmgfw.old.efi") {
Ok(t) => t, // This return an allocated buffer with the content of the file
Err(_) => panic!("Windows efi file not found")
};
let windows_handle = system_table
.boot_services()
.load_image_from_buffer(*image_handle, windows_efi.deref())
.unwrap_success();
match system_table.boot_services().start_image(windows_handle) {
Ok(_) => log::info!("OK"),
Err(e) => log::info!("KO : {:#?}", e) // I'm getting a INVALID_PARAMETER status here
}
I'm getting an INVALID_PARAMETER status and I don't really understand why.
Am doing this the right way ? I would really appreciate your help as I'm still learning the UEFI specification.
The bootmgfw.efi
file is started directly by the firmware yes, it has a dedicated boot entry linking to the image. Doesn't it mean that there aren't any previous initialization ?
I will try to play with the load options of the image from LoadedImage
's protocole, if that doesn't work I will be creating an issue on github.
Thanks for your help anyway !
Doesn't it mean that there aren't any previous initialization ?
Yes, probably. But maybe you're changing some state before starting the image, so that the windows efi image is started with an unexpected state? Just guessing, though...
bootmgfw.efi
happily works when started from other EFI boot managers (rEFInd etc) so I'd have a look at what state things like that leave the system in before launching another EFI executable and see if you can get it to work that way
bootmgfw.efi
happily works when started from other EFI boot managers (rEFInd etc) so I'd have a look at what state things like that leave the system in before launching another EFI executable and see if you can get it to work that way
You mean like call ExitBootServices before starting the image ? Could try that.
What's really odd is that it work with with other efi image. I've manage to load it from the EFI Shell so I guess it should be possible.
I'm not touching any NVRAM variables or anything, I'll try by clearing load options or something.
_DYNAMIC
's loaded address in a shared library without an interpreter? As far as I've tried, both gcc and ld.lld create a .got entry for that symbol, and whenever I try to get the address of it in Rust code, I always obtain the address of the entry in .got. I have checked the disassembled code generated by both linker and always found mov rax, [rel ..]
instead of lea rax, [rel ..]
which is what I wanted.
@js2xxx I'm not sure what you are trying to achieve. Do you want to know the runtime address where a bunch of code was loaded to?
On x86, you can try something like:
# Loads the link time address into rcx
# OFFSET in intel syntax is similar to $ in AT&T syntax
# This way we get the symbol address as immediate in assembly output
# https://stackoverflow.com/questions/39355188/
# This is different to nasm, where the symbol address would be used
# by default as immediate.
mov rcx, OFFSET start
# Loads the real address of start into rdx
# results in "-0x20(%rip)"
lea rdx, [rip - start]
# rdx, the relative runtime offset
# example: link addr = 8MiB
# real addr = 1MiB
# offset = 1MiB - 8MiB = -7MiB
# => we need to add "-7MiB" (the offset in rdx) to every static/old/invalid address next
sub rdx, rcx
RDX will contain the relative offset between the real address and the link time address. You just need the address of a well-known symbol of your library. In this case, it uses "start".
How could I get the Y and X axis while enumerating the framebuffer?
Well, usually you have a reference to a continuous memory region and meta information about the buffer, such as height, width, and pixel layout. When you're enumerating all pixels of the continuous memory region (usually pairs of 32-bit, for example (_rgb), you have to iterate over the 32-bit values at first. To get the corresponding index of the pixel on the screen, you calculate:
y_pos = i / width
x_pos = i % width
Does this help you?
Wondering if anyone has thoughts on this: I thought it might be nice to update my IDT with new interrupt handlers at runtime, if needed to do so. Right now I have it setup like in blog_os - I have a lazy_static which initializes an immutable static IDT. Obviously I can't mutate that. And I can't easily create a new one on demand (to then load into the CPU), because it needs to be static so it can't accidentally go away on the processor. The way I see it, there are a couple of options: a) have a static mut IDT (probably not a good idea if I want to avoid UB land), or create a new IDT on the heap, leak the memory, and load it (also not great cause now I'm leaking memory even if it is small). Wondering if there is a good way to do this.
The alternative option is just to have my interrupt handlers contain any branching logic needed so I would never need to write a new function into the IDT for some reason. Probably what I will do in the end, because it's safe and easy. But it did get me thinking about how one would properly go about updating their IDT if they wanted to do so.
@cdrzewiecki_gitlab I had to do this too, here's what worked for me.
I first got an initial IDT up and running using lazy_static:
lazy_static! {
pub static ref IDT: RwLock<InterruptDescriptorTable> = RwLock::new({
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
unsafe {
idt.double_fault.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt.non_maskable_interrupt.set_handler_fn(non_maskable_interrupt_handler);
// ..... lines omitted for brevity
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
// ..... lines omitted for brevity
idt
});
}
then I declared the following function to load the IDT:
pub fn init_idt() {
unsafe { IDT.read().load_unsafe(); }
}
so when the kernel starts, the first time the BSP will run init_idt().
Then later, when I need to make changes to the IDT I do this: (example taken from my VMware SVGA module)
// Set the interrupt handler and enable the IRQ
let mut idt = crate::interrupts::IDT.write();
idt[irq.into()].set_handler_fn(svga_interrupt_handler);
unsafe {
idt.load_unsafe();
(*crate::interrupts::PICS.lock()).set_mask(irq, true);
}
It may not be the best, cleanest solution but it works for me since I only have to mutate the handlers here. If I have to do that in the future I might consider a different approach but for now the reduced complexity and scope doesn't justify an overhaul. I want to have this be KISS.
Hope it helps! :)
your_idt_instance.load()
, not load(your_idt_instance)
).
This whole project is honestly nuts and pulling my damn brain cells apart XD Awesome stuff!
Just got Rust up and running for the second time ever (first time was a quick try out to see what the fuss was about), and umm... There is a lot of Rust I still don't understand, but honestly the guide has probably taught me more about how everything works and the general syntax used in more complex programs than anything else.
Finished the guide after running through it today, I'll be trying to get a game running! I can say for sure it doesn't feel like the OS is quite where I would want it to make a game, but ASCII text and magic keyboard input should let me make a main menu and a game of snake :)
Really wanting to keep doing more stuff, making my OS in Rust compared to straight NASM is so much easier, especially on Windows, so I just want to make something with more and more features while learning Rust too XD
What would be some good resources going forward with my OS? I'm more wanting to look into file systems, USB peripherals (assuming BIOS won't be helping me with the keyboard like QEMU), actually installing my OS, and deeper into graphics to allow finer grained control over color and, well, pixels... Maybe OpenGL software rendering or something? (Maybe ordered graphics, USB peripherals, file system, installing)