Long/64-Bit Mode switch from protected mode questions

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Long/64-Bit Mode switch from protected mode questions

Post by rpio »

...
Last edited by rpio on Tue Aug 13, 2024 12:04 pm, edited 1 time in total.
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by Octocontrabass »

ngx wrote:1. Does this mean that I could only use assembly for bootstrap(as 32 bit C can't be linked to the 32+64bit assembly compiled as elf64 and 64-Bit C)?
No, but it does mean linking the 32-bit C with the rest of your kernel will be difficult. Setting up the initial page tables is very simple, so you can do it using only assembly, and it's not as difficult to link 32-bit assembly with the rest of your kernel. Another option is to link the bootstrap code into a separate executable, and implement a simple ELF loader to handle the 64-bit executable. Another option is to use a different bootloader that will put the CPU in 64-bit mode for you.
ngx wrote:2. Would it would work always or only depending on the assembler target(e.g. -f elf32), what should assembler output and compiler target be?
If you tell the assembler to output elf64, you'll be able to link your 32-bit assembly with the rest of your kernel. You may need to look out for certain types of relocations, since the linker is only prepared to handle 64-bit code.
ngx wrote:3. Should there be any changes made in the linker-script for it to work?
Probably, but the specifics depend on exactly how you map your kernel after you enable paging. There's an example here.
ngx wrote:4. Should something be changed in GRUB2 to load the 64-bit ELF?
Nope. GRUB2 can load it.
ngx wrote:5. This is not so related but for a day I was struggling because the clang code for --target=x86_64-elf was not working in grub and same was before with code with --target=xi386-elf, the fix was to change to x86-64-elf and i686-elf accordingly, what could the problem be?
Maybe you had a typo somewhere? There's no "xi386" or "x86-64" targets.
ngx wrote:5. Do the assembly routines and C functions and variables declared during protected mode operation still work/can be called/accessed in 64-Bit mode?
Assembly routines and C functions only work if you put the CPU in the correct mode before calling them, and you must use assembly to switch modes. Better stick to just one mode switch, when you enable 64-bit mode for the first time. (Or use a bootloader that can do it for you.)

Variables... may be accessible, if you link everything into a single executable, but I've never tried it and I'm sure there are ways it can break. Better avoid this too.
ngx wrote:6. How is it possible to check that red-zone was disabled and long mode was enabled?
The red zone is disabled when you tell the compiler to disable the red zone. The compiler may link against a runtime library, which must also be compiled without the red zone. (We have instructions on the wiki to set it up with GCC. I've never tried with Clang so I don't know if any special configuration is necessary.)

You can check if long mode is active by looking at EFER.LMA (bit 10).
ngx wrote:7. Long mode requires paging to be enabled, but I am not really professional in this topic - so could it happen that some data or function get's split between two pages(like few bytes on end of one and few bytes on start of another page), then how do I prevent it?
You don't need to prevent it.
ngx wrote:8. Where should the paging structures be stored as with their amount increasing, their storage requirements also increase?
Anywhere is fine, as long as it's memory not already in use for some other purpose. For switching to long mode, you might dedicate some space in the .bss section for enough page tables to get your kernel started, and then figure out where to put the rest once you're in 64-bit mode.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Long/64-Bit Mode switch from protected mode questions

Post by nullplan »

ngx wrote:1. Does this mean that I could only use assembly for bootstrap(as 32 bit C can't be linked to the 32+64bit assembly compiled as elf64 and 64-Bit C)?
2. Would it would work always or only depending on the assembler target(e.g. -f elf32), what should assembler output and compiler target be?
3. Should there be any changes made in the linker-script for it to work?
For all of these, I recommend getting a second loader kernel. I have two loader kernels, one for UEFI and one for GRUB. The one for GRUB is a 32-bit kernel using GRUB's module mechanism to load the main kernel. It is written mostly in C, with obvious assembler stubs. The UEFI one is all written in C, and is 64-bit mode. Both will setup the environment the main kernel expects, transfer whatever start parameters are needed to the new kernel's stack in a unified format, and then just transfer control to the main kernel.
ngx wrote:4. Should something be changed in GRUB2 to load the 64-bit ELF?
Better to make the loader kernel for GRUB 32-bit. That way it will also work with GRUB legacy.
ngx wrote:5. Do the assembly routines and C functions and variables declared during protected mode operation still work/can be called/accessed in 64-Bit mode?
In theory, you can access protected mode data from long mode; those are just addresses, after all. 32-bit code will not work in 64-bit mode.
ngx wrote:6. How is it possible to check that red-zone was disabled and long mode was enabled?
Red zone has to be disabled in the compiler. You can't really check, except to make sure that "-mno-red-zone" is part of the compiler command line for all source files. You could read through the disassembly of the kernel to check manually, but that is probably not feasible for a kernel of any complexity.

As for long mode being active, you can test the LMA bit in EFER. That code should be mode agnostic.
ngx wrote:7. Long mode requires paging to be enabled, but I am not really professional in this topic - so could it happen that some data or function get's split between two pages(like few bytes on end of one and few bytes on start of another page), then how do I prevent it?
You don't. It is not an issue. If you map the memory linearly, nothing bad will happen there. Most of the time you want to load the kernel as a blob to some place, and then map that place in one block to somewhere in virtual memory. Then it doesn't matter if a function crosses a page boundary, the addresses, both physical and virtual, will still be consecutive.
ngx wrote:8. Where should the paging structures be stored as with their amount increasing, their storage requirements also increase?
Wherever is free. Having a decent physical memory allocator is half the rent, here. I don't think having them contiguous in memory would aid anything. Just place them down wherever.
Carpe diem!
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

0
Last edited by rpio on Mon Feb 22, 2021 12:45 pm, edited 2 times in total.
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

...
Last edited by rpio on Tue Aug 13, 2024 12:16 pm, edited 2 times in total.
sj95126
Member
Member
Posts: 151
Joined: Tue Aug 11, 2020 12:14 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by sj95126 »

ngx wrote:And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?
I assume you mean a byte (or bit) map to track free frames? Unless you're doing something particularly fancy, you'll know at boot-time how many total frames the system has, and how many are usable (vs. reserved, ACPI, MMIO, etc.), and set the map accordingly. It shouldn't need to increase in size after that.

I went with a different approach. I track the free frames as a linked list, where the pointer to the next frame is stored in the frame itself. So, I don't need any extra memory to track them, and finding a free frame is fast, because there's no searching - just take the first frame off the list. With paging, there's no need for the physical frames to be contiguous, so allocating multiple frames as a block isn't necessary.
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

sj95126 wrote:
ngx wrote:And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?
I assume you mean a byte (or bit) map to track free frames? Unless you're doing something particularly fancy, you'll know at boot-time how many total frames the system has, and how many are usable (vs. reserved, ACPI, MMIO, etc.), and set the map accordingly. It shouldn't need to increase in size after that.

I went with a different approach. I track the free frames as a linked list, where the pointer to the next frame is stored in the frame itself. So, I don't need any extra memory to track them, and finding a free frame is fast, because there's no searching - just take the first frame off the list. With paging, there's no need for the physical frames to be contiguous, so allocating multiple frames as a block isn't necessary.
If i allocate the page frame byte/bit map at boot time it could then be too large if there is a lot of RAM - how can i solve this?
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

nullplan wrote:
ngx wrote:1. Does this mean that I could only use assembly for bootstrap(as 32 bit C can't be linked to the 32+64bit assembly compiled as elf64 and 64-Bit C)?
2. Would it would work always or only depending on the assembler target(e.g. -f elf32), what should assembler output and compiler target be?
3. Should there be any changes made in the linker-script for it to work?
For all of these, I recommend getting a second loader kernel. I have two loader kernels, one for UEFI and one for GRUB. The one for GRUB is a 32-bit kernel using GRUB's module mechanism to load the main kernel. It is written mostly in C, with obvious assembler stubs. The UEFI one is all written in C, and is 64-bit mode. Both will setup the environment the main kernel expects, transfer whatever start parameters are needed to the new kernel's stack in a unified format, and then just transfer control to the main kernel.
ngx wrote:4. Should something be changed in GRUB2 to load the 64-bit ELF?
Better to make the loader kernel for GRUB 32-bit. That way it will also work with GRUB legacy.
ngx wrote:5. Do the assembly routines and C functions and variables declared during protected mode operation still work/can be called/accessed in 64-Bit mode?
In theory, you can access protected mode data from long mode; those are just addresses, after all. 32-bit code will not work in 64-bit mode.
ngx wrote:6. How is it possible to check that red-zone was disabled and long mode was enabled?
Red zone has to be disabled in the compiler. You can't really check, except to make sure that "-mno-red-zone" is part of the compiler command line for all source files. You could read through the disassembly of the kernel to check manually, but that is probably not feasible for a kernel of any complexity.

As for long mode being active, you can test the LMA bit in EFER. That code should be mode agnostic.
ngx wrote:7. Long mode requires paging to be enabled, but I am not really professional in this topic - so could it happen that some data or function get's split between two pages(like few bytes on end of one and few bytes on start of another page), then how do I prevent it?
You don't. It is not an issue. If you map the memory linearly, nothing bad will happen there. Most of the time you want to load the kernel as a blob to some place, and then map that place in one block to somewhere in virtual memory. Then it doesn't matter if a function crosses a page boundary, the addresses, both physical and virtual, will still be consecutive.
ngx wrote:8. Where should the paging structures be stored as with their amount increasing, their storage requirements also increase?
Wherever is free. Having a decent physical memory allocator is half the rent, here. I don't think having them contiguous in memory would aid anything. Just place them down wherever.
Thanks for your reply, but what do you mean by placing them down - like in the physical or in the virtual memory and then what should i do if the table increases to high sizes, should i move other data forwards(as it increases) to fit it?

And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?

If the questions about the location of the data sounds simple, that is my first OS so it is hard for me to think of what could be done to solve certain problems because.

Note: I am writing a higher half kernel - it is not higher half just yet, but will be very soon
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

Octocontrabass wrote:
ngx wrote:1. Does this mean that I could only use assembly for bootstrap(as 32 bit C can't be linked to the 32+64bit assembly compiled as elf64 and 64-Bit C)?
No, but it does mean linking the 32-bit C with the rest of your kernel will be difficult. Setting up the initial page tables is very simple, so you can do it using only assembly, and it's not as difficult to link 32-bit assembly with the rest of your kernel. Another option is to link the bootstrap code into a separate executable, and implement a simple ELF loader to handle the 64-bit executable. Another option is to use a different bootloader that will put the CPU in 64-bit mode for you.
ngx wrote:2. Would it would work always or only depending on the assembler target(e.g. -f elf32), what should assembler output and compiler target be?
If you tell the assembler to output elf64, you'll be able to link your 32-bit assembly with the rest of your kernel. You may need to look out for certain types of relocations, since the linker is only prepared to handle 64-bit code.
ngx wrote:3. Should there be any changes made in the linker-script for it to work?
Probably, but the specifics depend on exactly how you map your kernel after you enable paging. There's an example here.
ngx wrote:4. Should something be changed in GRUB2 to load the 64-bit ELF?
Nope. GRUB2 can load it.
ngx wrote:5. This is not so related but for a day I was struggling because the clang code for --target=x86_64-elf was not working in grub and same was before with code with --target=xi386-elf, the fix was to change to x86-64-elf and i686-elf accordingly, what could the problem be?
Maybe you had a typo somewhere? There's no "xi386" or "x86-64" targets.
ngx wrote:5. Do the assembly routines and C functions and variables declared during protected mode operation still work/can be called/accessed in 64-Bit mode?
Assembly routines and C functions only work if you put the CPU in the correct mode before calling them, and you must use assembly to switch modes. Better stick to just one mode switch, when you enable 64-bit mode for the first time. (Or use a bootloader that can do it for you.)

Variables... may be accessible, if you link everything into a single executable, but I've never tried it and I'm sure there are ways it can break. Better avoid this too.
ngx wrote:6. How is it possible to check that red-zone was disabled and long mode was enabled?
The red zone is disabled when you tell the compiler to disable the red zone. The compiler may link against a runtime library, which must also be compiled without the red zone. (We have instructions on the wiki to set it up with GCC. I've never tried with Clang so I don't know if any special configuration is necessary.)

You can check if long mode is active by looking at EFER.LMA (bit 10).
ngx wrote:7. Long mode requires paging to be enabled, but I am not really professional in this topic - so could it happen that some data or function get's split between two pages(like few bytes on end of one and few bytes on start of another page), then how do I prevent it?
You don't need to prevent it.
ngx wrote:8. Where should the paging structures be stored as with their amount increasing, their storage requirements also increase?
Anywhere is fine, as long as it's memory not already in use for some other purpose. For switching to long mode, you might dedicate some space in the .bss section for enough page tables to get your kernel started, and then figure out where to put the rest once you're in 64-bit mode.
Thanks for answering,
I agree with the part about placing initial tables in .bss, I also wanted to do it like this. But as I am already in long mode I don't really know where I should start placing the page tables and how should I dynamically allocate more space for them and the same problem is with the bytemap of the page frame allocator I am developing, where are the tables placed in mostly(by other OSs)?

Note: I am doing a Higher Half Kernel
sj95126
Member
Member
Posts: 151
Joined: Tue Aug 11, 2020 12:14 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by sj95126 »

ngx wrote:
sj95126 wrote:
ngx wrote:And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?
I assume you mean a byte (or bit) map to track free frames? Unless you're doing something particularly fancy, you'll know at boot-time how many total frames the system has, and how many are usable (vs. reserved, ACPI, MMIO, etc.), and set the map accordingly. It shouldn't need to increase in size after that.

I went with a different approach. I track the free frames as a linked list, where the pointer to the next frame is stored in the frame itself. So, I don't need any extra memory to track them, and finding a free frame is fast, because there's no searching - just take the first frame off the list. With paging, there's no need for the physical frames to be contiguous, so allocating multiple frames as a block isn't necessary.
If i allocate the page frame byte/bit map at boot time it could then be too large if there is a lot of RAM - how can i solve this?
What's considered too large? If you use a page frame bit map, where each bit represents a 4K frame, then you'll only need a fairly small amount of memory to manage it. If your system has 32GB of RAM, you'd only need 1MB to hold the bitmaps to track whether frames are allocated or not.

My answer is tailored to systems that use paging, which you might not, but even so you probably want to track your memory allocation is reasonably sized blocks. You might as well make them 4K so you can transition to paging easier down the road.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: Long/64-Bit Mode switch from protected mode questions

Post by nullplan »

ngx wrote:Thanks for your reply, but what do you mean by placing them down - like in the physical or in the virtual memory and then what should i do if the table increases to high sizes, should i move other data forwards(as it increases) to fit it?
For physical memory: Your boot environment (i.e. BIOS or UEFI) will tell you about the layout of the physical memory. You can initialize your physical memory manager with the knowledge of these areas existing, then reserve the memory you need to preserve (so wherever your kernel is, and where your stack is, and whatever other memory you are already using). Whatever remains is physical memory free to use. So you can place your paging structures on an as-needed basis wherever is space when the time comes to allocate it. Paging structures never need to be allocated in any unit other than one page at a time.

For virtual memory: x86-64 has the advantage of having definitely more than 4 times as much virtual address space as physical address space. Therefore, it is feasible to devote half of the kernel half of virtual address space to a linear map of all physical memory. This makes address translation trivial and allows you to access all memory whenever you need to. Of course you can map the memory logically in other places as well. Nothing prevents you from mapping the same page twice.
ngx wrote:And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?
Why would the map need to dynamically increase? From the moment you boot, you know how much memory is in the system, so you know how big your byte-map is ever going to get, so you can just allocate the maximum size and never worry about it again.
Carpe diem!
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

sj95126 wrote:
ngx wrote:And also I have a question that is pretty close to this one - where should I place the byte-map of my page frame allocator and how can it dynamically increase?
I assume you mean a byte (or bit) map to track free frames? Unless you're doing something particularly fancy, you'll know at boot-time how many total frames the system has, and how many are usable (vs. reserved, ACPI, MMIO, etc.), and set the map accordingly. It shouldn't need to increase in size after that.

I went with a different approach. I track the free frames as a linked list, where the pointer to the next frame is stored in the frame itself. So, I don't need any extra memory to track them, and finding a free frame is fast, because there's no searching - just take the first frame off the list. With paging, there's no need for the physical frames to be contiguous, so allocating multiple frames as a block isn't necessary.
A bit of late reply, but I just remembered about what you said about linked list of frames while I was implementing my PMM. I wanted to ask how are you doing this because if you have the pointer to next frame inside another frame then there are only 4096-8 bytes available, how do you handle it so the stuff which uses the page does not overwrite the place where address is stored?
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by Octocontrabass »

You don't. When the frame is part of the list, it's not being used by anything else, so nothing will overwrite the pointer. When the frame isn't part of the list, there's no pointer.
rpio
Member
Member
Posts: 92
Joined: Sat Feb 20, 2021 3:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by rpio »

Octocontrabass wrote:You don't. When the frame is part of the list, it's not being used by anything else, so nothing will overwrite the pointer. When the frame isn't part of the list, there's no pointer.
I can't really understand the algorithm here, can you please explain how should the linked list of frames work:
1. Do you put the address of next frame into each frame when the OS starts?

1. How is the frame allocated?
2. Home is the frame freed?
3. How do I reserve some areas based on memory map and/or driver requests(like if a driver asks for area from 0xb8000 to something, or anything like that) using your algorithm?

Thanks in Advance
Gigasoft
Member
Member
Posts: 856
Joined: Sat Nov 21, 2009 5:11 pm

Re: Long/64-Bit Mode switch from protected mode questions

Post by Gigasoft »

Allocating means removing one from the list, and you free it put putting it back. Pages that do not correspond to usable RAM naturally won't be managed by the frame allocator or put on a free page list.
Post Reply