flat assembler
Message board for the users of flat assembler.

Index > MenuetOS > How to boot MenuetOS under UEFI?

Author
Thread Post new topic Reply to topic
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 15 Jan 2024, 21:46
Hi,

I've created a simple and easy to use boot manager, Easyboot, which works on BIOS and UEFI machines as well (and on RPi, but that's not relevant here).

I've also implemented an Easyboot plugin to boot MenuetOS 32 / 64 with this loader. This works very well, but sadly it is BIOS only. I wish to add UEFI support to it, but since MenuetOS64 is closed source, I'm not sure how to proceed. Can somebody help me?

The most problematic part is, that the kernel's entry point is in real mode, and it calls some BIOS services before it switches to protected / long mode (which obviously doesn't exists on a UEFI system).

For the records, I've already managed to do this for KolibriOS (a MenuetOS 32 fork), but since Kolibri is Open Source and most of the work were already done, that was pretty easy. The main required steps were:
- adding the protected mode entry point in the kernel's header (so that loaders could skip the real mode part), but the kernel still starts with the real mode code for backward compatibility
- all initialization code moved before that entry point, so protmode part now only relies on a BOOT_LO structure nothing else
- in my loader's kolibri.c plugin I create exactly the same BOOT_LO struct that the real mode kernel part would, and I jump directly to the protected mode entry point
- since my loader can create that BOOT_LO struct on BIOS as well as on UEFI machines, and the kernel's protmode code does not use any BIOS services, this means that the OS can be loaded on UEFI too out-of-the-box (tested, works like a charm).

Is there a standardized way to boot MenuetOS under UEFI? And if not, is there any chance to apply similar changes to its kernel as described above?

And please don't go mad, but would it be possible to Open Source MenuetOS64 kernel? Not the entire thing, just the initialization part? That way I could help with implementing UEFI support (I don't care how long mode is initialized, I'm only interested in finding the protmode entry point and what environment it expects, because there are surely no BIOS calls after that point, so boot firmware shouldn't matter for the rest of the code.)

Cheers,
bzt
Post 15 Jan 2024, 21:46
View user's profile Send private message Reply with quote
Ville



Joined: 17 Jun 2003
Posts: 297
Ville 16 Jan 2024, 11:06
Hi!

I'll add the following header to next release, friday the latest.
Code:
    db  'UEFIHDR0'
    dd  protected_mode_entry_1
    dd  protected_mode_entry_2 with lgdt,cr0 init
    dd  boot_info_base
    dd  kernel.mnt_location
    dd  config.mnt_location
    dd  ramdisk_location
    dd  lgdt_load_address    

At first the loader must load kernel.mnt, config.mnt and ramdisk to the locations defined at the header.

When entering at protected_mode_entry_1, M64 expects that lgdt is loaded from lgdt_load_address and cr0 bit 0 is set and bits 29 and 30 are cleared.

When entering at protected_mode_entry_2, lgdt and cr0 are set at M64 code.

boot_info_base defines the address for following structure and the loader must set the varibles marked with *.
Code:
boot_info_base

*+0x00  qword  Bits per pixel
 +0x08  qword  Vesa video mode
 +0x10  qword  Mouse port
*+0x18  qword  Vesa 2.0 LFB address
*+0x20  qword  X resolution
*+0x28  qword  Y resolution
 +0x30  qword  Memory model
 +0x38  qword  MTRR graphics acceleration
*+0x40  qword  Scanline length in bytes
*+0x48  qword  Bytes per pixel
 +0x50  qword  Mouse packet size
 +0x58  qword  Bootup graphics driver (0/1=vesa/other driver)
 +0x60  qword  Bootup pixel count
*+0x68  qword  Bootup auto configuration (0/1=no/yes)    
Post 16 Jan 2024, 11:06
View user's profile Send private message Reply with quote
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 16 Jan 2024, 20:19
Ville wrote:
I'll add the following header to next release, friday the latest.
Great, thanks!

Ville wrote:
Code:
    db  'UEFIHDR0'    
Where is this header going to be located? At a fixed position in the beginning of kernel.mnt? That would be great! And are you sure it's okay to call it "UEFIHDR0"? I mean it can be (and with my loader will be) used on BIOS machines as well. Just a thought, any magic fine by me. Otherwise clean and good solution, I like this idea!
Ville wrote:
When entering at protected_mode_entry_1, M64 expects that lgdt is loaded from lgdt_load_address and cr0 bit 0 is set and bits 29 and 30 are cleared.
This is the one I'm going to use, because it's more bulletproof. Should the long mode requirements change in the future, I'll let the kernel set up those.
Is it okay if I don't set up the segment registers just the gdt? (The very first thing that Kolibri does at the protmode entry point is to set up segments, is this true to MenuetOS as well?)
Ville wrote:
boot_info_base defines the address for following structure and the loader must set the varibles marked with *.
Perfect, but are you sure this is all? Don't you need a memory map or an ACPI pointer? You can't get those two firmware-independently (on BIOS you need the E820 service and parse the EBDA, but on UEFI you have neither, there you must use GetMemoryMap and the Configuration system table). I can give you a memory map on UEFI which looks like E820 (I already convert it), and the RSD PTR (or just simply the 64-bit address of the root table RDST/XSDT, whichever you prefer).

On Kolibri, all detection code has been removed from protmode code in kernel.asm; now E820 and EBDA parsing is done in bootbios.asm. So the protmode code just uses what's in BOOT_LO. On UEFI these values come from the boot loader, and again, the protmode code just uses what's in the boot structure (with the converted E820 memory map and ACPI pointer). There are no more firmware dependent things, just these two (actually there are, but those are already part of the boot structure).

Cheers,
bzt
Post 16 Jan 2024, 20:19
View user's profile Send private message Reply with quote
Ville



Joined: 17 Jun 2003
Posts: 297
Ville 17 Jan 2024, 22:46
Great work, thanks! The header is now located at +8 of kernel.mnt file, but I recommend scanning for it. And no need to set the segment registers, since those are set at the protected mode entry. And boot_info_base does not have other parameters defined, so you can return the memory map pointer and both RSD PTR and RSDT/XSDT pointers to boot_info_base +0x70, +0x78 and +0x80.
Post 17 Jan 2024, 22:46
View user's profile Send private message Reply with quote
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 19 Jan 2024, 01:17
Great! I have a few more questions though.
Ville wrote:
you can return the memory map pointer
About memory map: what format should I use? Does Multiboot2 section 3.6.8 Memory map work for you?
Code:
qword base_address ; in bytes
qword length ; in bytes
dword type ; E820 type code
dword padding ; always 0 on BIOS, but I can put the UEFI type code here on UEFI    
Or should I leave out padding and have unaligned 20 bytes records? Also how do I pass the number of memory map entries? Should this memory map pointer point to a Multiboot2 tag instead (memory map records with a little header)?
Code:
dword magic ; always 6
dword size ; size of the block in bytes
dword reclen ; size of one record in bytes, always 24
dword padding ; always 0
; memory map records, (size - 16) / 24 times    
Or should I just start the list with the number of entries perhaps?
Code:
word numrec
; memory map records, numrec times    
(There'll be probably no more than 100 records ever, word is only to be future proof. Linux allows 128 records tops in its zero_page, and Kolibri limits it to 32, however I have seen UEFI machines with more than 32 records).

Third option is to always terminate the list with full zero record (base_address and type can be zero, but normally length can't be).

Fourth possible option is to add numrec directly to boot_info. Even adding it to the most significant 16 bits of memory map pointer makes sense, because it's unlikely that any boot loader would allocate this above 4G, much less above 256T. So +70 .. +75 pointer, +76 .. +77 number of entries. (This is tricky, but does not increase the size of boot_info and also keeps its fields 8-bytes aligned.)

Fifth option (this is what Kolibri does) that boot_info has the numrec as its very last field, there's no memory map pointer, rather the memory map records follow the boot_info struct directly (I personally think having a memory map pointer in boot_info is a more flexible and more future proof solution). It also has 20 bytes per records (so there's no padding field), which is more compact, but makes the records unaligned.

It really doesn't matter to me which one you prefer (you could also come up with a total different approach). For example, in the boot protocol that I've designed, length must be multiple of 16 bytes, and type is stored in its lowest 4 bits.

Ville wrote:
both RSD PTR and RSDT/XSDT pointers to boot_info_base +0x70, +0x78 and +0x80.
About this, I'm bit confused. There's two variant of RSD PTR, they only differ in the size of the address field (either 32 bit or 64). Both fields point to the same address, with either RSDT or an XSDT table. So there's not much sense to pass both. All that there is to RSD PTR is having a pointer to the latter (just in two fields, either 32 or 64 bit, but the same address). I guess it would be the best to pass a pointer to RSD PTR at +78, and you can then parse all the rest?

To sum it up, does this work for you?
Code:
+0x70 pword  memory_map_ptr ; with 24 bytes records (qword base, qword length, dword type, dword padding)
+0x76 word   num_memory_map ; number of records
+0x78 qword  rsdp_ptr ; pointer to RSD PTR (with a pointer to RSDT/XSDT inside)    


Cheers,
bzt
Post 19 Jan 2024, 01:17
View user's profile Send private message Reply with quote
Ville



Joined: 17 Jun 2003
Posts: 297
Ville 19 Jan 2024, 16:53
Multiboot2 section 3.6.8 memory map (8+8+4+4) is fine by me. And I would also include the number of entries to boot_info and use qword alignment for all parameters.

The header is now labeled "PMENTRY0".

Included is a test image with "PMENTRY0" struct at +8 of kernel.mnt. Version has multiprocessor support disabled for now, since some of the initialization is done at the 16bit code. Also the test image does not yet have memory map tests. M64 uses memory from 0x8000-0x58000, 1MB-13.99MB and 16MB - amount_of_ram defined at config.mnt.
Code:
+0x70 qword  memory_map_ptr ; with 24 bytes records (qword base, qword length, dword type, dword padding)
+0x78 qword  num_memory_map ; number of records
+0x80 qword  rsdp_ptr ; pointer to RSD PTR (with a pointer to RSDT/XSDT inside)    


Description:
Download
Filename: M6414960pre1.zip
Filesize: 977.15 KB
Downloaded: 59 Time(s)

Post 19 Jan 2024, 16:53
View user's profile Send private message Reply with quote
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 22 Jan 2024, 12:52
Hi Ville,

Thanks! Sorry for the delay, I'm rather busy with other projects.

I've checked the attachment, but now I have a problem with identifying the MenuetOS kernel. The KERNEL.MNT used to look like this:
Code:
00000000  e9 e6 07 4d 65 6e 75 65  74 4f 53 20 36 34 62 69  |...MenuetOS 64bi|
00000010  74 20 76 31 2e 34 39 0d  0a 0d 0a 00 00 00 00 00  |t v1.49.........|
00000020  00 00 00 00 4e 6f 20 32  34 20 6f 72 20 33 32 20  |....No 24 or 32 |    
This was great because there was a clear identification at offset 3. With this attachment, this has gone:
Code:
00000000  e9 0f 08 90 90 90 90 90  50 4d 45 4e 54 52 59 30  |........PMENTRY0|
00000010  78 18 01 00 40 18 01 00  00 90 00 00 00 00 01 00  |x...@...........|
00000020  00 40 05 00 00 00 10 00  20 1c 01 00 4d 65 6e 75  |.@...... ...Menu|    

My question is, would it be possible to keep the MenuetOS identification string and version as they were, at offset 3 and move PMENTRY0 instead? Something like:
Code:
00000000  e9 0f 08 4d 65 6e 75 65  74 4f 53 20 36 34 62 69  |...MenuetOS 64bi|
00000010  74 20 76 31 2e 34 39 0d  0a 0d 0a 00 00 00 00 00  |t v1.49.........|
00000020  50 4d 45 4e 54 52 59 30  78 18 01 00 40 18 01 00  |PMENTRY0x...@...|
00000030  00 90 00 00 00 00 01 00  00 40 05 00 00 00 10 00  |.........@......|
00000040  20 1c 01 00 4e 6f 20 32  34 20 6f 72 20 33 32 20  | ...No 24 or 32 |    
Actually any position for PMENTRY0 is good as long as it does not interfere with the MenuetOS version string's current position. Using 20h would require 4 bytes shorter version string, but I think that's okay and I picked that offset for this example because 24h wouldn't be 8 bytes aligned. Also 28h or 30h sounds equally good to me, it's up to you, just please keep the version string as it were.

What are your thoughts on this?
bzt
Post 22 Jan 2024, 12:52
View user's profile Send private message Reply with quote
Ville



Joined: 17 Jun 2003
Posts: 297
Ville 23 Jan 2024, 22:43
Sounds good! In the included version PMENTRY0 is located at +0x20 and kernel version at +0x3. Also SMP is now enabled when [boot_info_base+0x68] is set to 1. SMP code checks if "RSD PTR " is located at [rsdp_ptr] and if not, the code falls back to scanning for the header. I'll add the memory checks next.


Description:
Download
Filename: M6414960pre2.zip
Filesize: 976.69 KB
Downloaded: 60 Time(s)

Post 23 Jan 2024, 22:43
View user's profile Send private message Reply with quote
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 24 Jan 2024, 10:10
Hi Ville,

Ville wrote:
Sounds good! In the included version PMENTRY0 is located at +0x20 and kernel version at +0x3.
This is perfect!
Ville wrote:
Also SMP is now enabled when [boot_info_base+0x68] is set to 1.
I set this to 1 by default, but now I've added a "noauto" command line flag that turns this off.
Ville wrote:
SMP code checks if "RSD PTR " is located at [rsdp_ptr] and if not, the code falls back to scanning for the header.
Watch out! This won't work on UEFI! There's no BDA, so you'll have garbage at 40Eh where the EBDA address should be. Also EBDA area 9A000h to A0000h doesn't exists! It's garbage, or if you're lucky, then full of zeros. Regardless it makes sense to look for RSD PTR when a protmode aware BIOS loader loads MenuetOS, just be sure to add lots of extra checks so that you won't end up in an infinite scanning loop in a totally unrelated portion of memory.

And now about your attachment, I've tested it. It works in real mode on BIOS (as it did), but also using the protmode entry on BIOS! MenuetOS boots up nicely and quickly and it uses the framebuffer that the loader passes! So this part is a success.

On UEFI however, it hangs. Not sure what's going on, the kernel is booted as it should, it can find the config and the ramdisk, it even switches to long mode, but then nothing happens.

Nontheless I've uploaded the new plugin, because I believe it probably is okay and should work correctly (let me know if it doesn't!), and also so that you could play with it.

For debugging, your PMENTRY0 is loaded at 6F8h (so that pm_entry1 is at 700h). I've also put a "jmp $" in the plugin right before it would transfer control over and created a register and memory dump for you, both on BIOS and UEFI. I've also attached a memory dump when the kernel freeze. I hope these would help you with your debugging.

Edit: hmm, looks like the 64Mb memory dump was too big for this forum, I had to remove it. Anyway, here's a 4Mb dump right before control is transfered to the kernel, on both BIOS and UEFI, so that you can compare them.

Oh, one more thing: Easyboot sets up dummy exception handlers, so you could see if there were any, but no indication of that. Of course this works only up to the point where you set up your own IDT (which I'm not sure if happens before or after the hang), so I've also tried qemu with -d int, no exceptions. It just hangs at 22863c3h, very much above your kernel's binary, I don't know how execution got there, but maybe you do. Good luck with your debugging, and let me know if I can help with anything!

Cheers,
bzt


Description: UEFI dumps, this halts and freezes
Download
Filename: menuet_uefi.zip
Filesize: 1.09 MB
Downloaded: 67 Time(s)

Description: BIOS dumps, this works!
Download
Filename: menuet_bios.zip
Filesize: 1.19 MB
Downloaded: 61 Time(s)

Description: Easyboot config to test with
Download
Filename: menuet_easyboot.zip
Filesize: 127.52 KB
Downloaded: 62 Time(s)

Post 24 Jan 2024, 10:10
View user's profile Send private message Reply with quote
Ville



Joined: 17 Jun 2003
Posts: 297
Ville 25 Jan 2024, 16:20
Thats excellent news! I also have UEFI booting working with the included pre3 files (in qemu). M64 PM-entry simply clears unused variables at boot_info and both entry points now have the same target address. And transparency and SMP work fine when qemu is started with the following options. Memory map checks are not yet included.

qemu-system-x86_64 -bios uefi.bin -m 640 -drive file=disk.img,format=raw -serial stdio -smp 2


Description:
Download
Filename: M6414960pre3.zip
Filesize: 1.07 MB
Downloaded: 68 Time(s)



Last edited by Ville on 27 Jan 2024, 13:11; edited 1 time in total
Post 25 Jan 2024, 16:20
View user's profile Send private message Reply with quote
bzt



Joined: 09 Nov 2018
Posts: 77
bzt 27 Jan 2024, 01:05
Hi Ville,

I've tested this latest attachment and I can confirm that it works perfectly!

- BIOS with real mode: works!
- BIOS with protmode entry: works!
- UEFI with protmode entry: works!

Well done! This is excellent news!
Post 27 Jan 2024, 01:05
View user's profile Send private message Reply with quote
Display posts from previous:
Post new topic Reply to topic

Jump to:  


< Last Thread | Next Thread >
Forum Rules:
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You can attach files in this forum
You can download files in this forum


Copyright © 1999-2024, Tomasz Grysztar. Also on GitHub, YouTube.

Website powered by rwasa.