Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to set ss and sp registers correctly in i386

I am currently writing a bootloader and have begun to run out of space in my 512B, so I have begun writing more code outside the 512B and intend to use the bootloader to read it into memory. I have added to the end of my code:

stack_start:
    resb 4096
stack_end:

So that I can allocate space for the stack at the end of the operating system code. Currently in my bootloader, I allocate 4KiB for the stack after the bootloader using the following which I took from here:

mov ax, 07c0h           ; 4K stack space after the bootloader -- code is running at 0x07c0
add ax, 288             ; (4096 + 512)/16 bytes per paragraph (288 paragraphs)
mov ss, ax
mov sp, 4096            ; moves the stack pointer

However now I need a way to allocate the stack to be at the end of my OS code, which will be an unknown size.

I believe I understand how these need to be set up -- similar to using es, I am using ss for extending the addressing space, but I can't find anything that explains it well for my level of knowledge. I am also not sure how to set it correctly for putting my stack at the end. I have used:

mov ax, stack_start
mov ss, ax
mov sp, 4096

And have not encountered an error; however I would like to know if this is correct, or whether I've actually just allocated some space for the stack while filling memory with a higher address.

How does ss actually work? And how can I use it and sp to allocate memory for the stack at the end of my code?

This is in i386 using nasm.

EDIT: Some way to validate that the stack is in the right place would be really useful, too, if that is at all possible.

like image 209
WORD_559 Avatar asked Jul 24 '17 14:07

WORD_559


2 Answers

I know this is scary for a beginner but there is no good or wrong way to setup the stack as long as it fulfils the requirements and it is bug-free.
Before you have a fully working memory manager, you (the human) have to be the memory manager.
You must be aware of how you use the memory.
The whole concept of "allocating memory" doesn't exist yet! You don't have a memory manager, you just have a bunch of RW RAM addresses.

Document your assumptions

Start with requiring a minimum amount of memory that your bootloader can safely assume.
Since the Initial Program Loader (the bootloader) is at 0x7c00 it is reasonable to believe the system has 31,5KiB of memory. You can also assume that no memory is present and rely on the cache but that's and advanced topic.

Stating assumptions is vital when things go wrong.

Mind the gaps

Then you must be aware of the reserved and used areas, this is achieved with a standard memory map.

An excerpt:

00000 - 003ff   IVT
00400 - 004ff   BDA
00500 - 0052f   Dangerous Zone (The Petch Zone :) )
00530 - 07bff   Free
07c00 - 07dff   IPL

The "Petch Zone" is an inside joke and homage to Michael Petch.

Make a fully conscious decision

Build up your minimal environment by setting up a temporary stack.
In the fragment above the area 00530 - 07bff is free you can use it as a ~29KiB stack.
Since the stack is full-descending you can put the stack pointer ss:sp at 07c00.
07c00 is a physical address, convert it to any suitable logical address (0000:7c00, 0001:7bf0, 0002:7be0, 0003:7bd0, ..., 07c0:0000, any will do pick up the one you like the most) and set SS:SP to it in any atomic way w.r.t. interrupts you know/like.

EDIT Using logical address with a small offset part leads to problems when the offset underflow wrapping from 0 to fffe as Ped7g correctly noted.
While the static behaviour checks (the starting address is the same) the dynamic behaviour fails, so it's better to use 0000:7c00 and surely not anything with segment above 0053.

Any other area will do, setting the stack pointer to a0000 (The end of the conventional memory) is another choice.
None is better, just be aware of where you put what.

EDIT: As Michael Petch pointed out in the comments, the address a0000 is also dangerous.
A safer address would be 9c000.

Update the memory map

Update the memory map with the blocks that fit your needs.
Write down where your kernel starts and ends, where your dynamic data lies and so on.

For example

00000 - 003ff   IVT
00400 - 004ff   BDA
00500 - 0052f   Dangerous Zone (The Petch Zone :) )
00530 - 07bff   Stack
07c00 - 07dff   IPL
07e00 - 08fff   Kernel
09000 - 10000   Other kernel stuff

Until now you could have gone away with using static blocks in the memory map but if you want to use more than 1 MiB of memory you need to query the BIOS to get the memory map of available memory.
This is intrinsically dynamic as each system comes with a different amount of memory.

That map is nothing but a very minimal meta-data for a memory manager, so it is time to...

Implement a simple memory manager

Within the basic environment you setup up to now, code a simple memory manager that book keeps blocks of memory.

Pitfall: A memory manager needs some "meta-memory" to hold its book of allocated memory, this call for an assumption.

Once you can allocate and free memory you can move the stack into a bigger area, load other data from disk or equivalent and so on.
The idea is that you can now manage memory dynamically like you do in C with malloc and mfree relieving you from the burden of mentally dealing with the memory map.

More advanced memory manager

A more advanced memory manager is usually written in an high-level language where data manipulation is easier (especially when dealing with topics like Paging).

like image 168
Margaret Bloom Avatar answered Oct 11 '22 23:10

Margaret Bloom


Rather than putting your stack at the end of your code, put it at the beginning. This is actually pretty common practice for boot loaders. In other words:

 cli
 mov ax,0x7c00
 mov sp, ax
 xor ax, ax
 mov ss, ax
 sti

You are now growing down from where your boatloader code loads. This also makes things easier since one of the very next things you are going to do is to load a second stage. This allows you to easily create the second stage in the same assembly file, load it in and jump to it, without having to worry about the stack overwriting code.

like image 26
David Hoelzer Avatar answered Oct 11 '22 23:10

David Hoelzer