How to set ss and sp registers in i386 correctly

I am currently writing a loader and started to run out of space in my 512B, so I started writing more code outside of 512B and intended to use the loader to read it into memory. I added at 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 a stack to the end of my OS code, which will be unknown.

I believe that I understand how to configure them - similar to using es , I use ss to expand the addressing, but I can not find anything that explains this well for my level of knowledge. I am also not sure how to properly install it to put my stack in the end. I used:

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

And did not find errors; however, I would like to know if this is correct, or if I really just allocated space for the stack when filling the memory with a higher address.

How does ss 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 verify that the stack is in the right place will also be useful, if at all possible.

+5
source share
3 answers

I know this is scary for a beginner, but there is no good or wrong way to install a stack if it meets the requirements and it does not contain errors.
Before you have a fully working memory manager, you (the person) must be a memory manager.
You need to know how you use memory.
The whole concept of "memory allocation" does not yet exist! You do not have a memory manager, you just have many RW RAM addresses.

Document your assumptions

Start with the required minimum amount of memory that your bootloader can safely accept.
Since the initial program loader (loader) is 0x7c00, it is reasonable to assume that the system has 31.5KiB of memory. You can also assume that there is no memory and rely on the cache, but this is also an extended topic.

Making assumptions is vital when things go wrong.

Take into account the disadvantages

Then you should know about the reserved and used areas, this is achieved using a standard memory card .

Exposure:

 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 respect for Michael Petch .

Make a fully informed decision

Create a minimal environment by setting up a temporary stack.
In the fragment above, the area 00530 - 07bff free, you can use it as a ~ 29KiB stack.
Since the stack is fully descending, you can put the ss:sp stack pointer in 07c00 .
07c00 is a physical address that translates it to any suitable logical address ( 0000:7c00 , 0001:7bf0 , 0002:7be0 , 0003:7bd0 , ..., 07c0:0000 ), anyone will get the one you like most) and set ss:sp it in any atomic way wrt interrupts that you know / how.

EDIT Using a logical address with a small offset part leads to problems when offset overflow correction from 0 to fffe as Ped7g is correctly noted.
Although checking for static behavior (the starting address is the same) dynamic behavior fails, so it’s better to use 0000:7c00 and, of course, nothing with a segment above 0053 .

Any other area will work; setting the stack pointer to a0000 (end of normal memory) is another choice.
No, it's better to just be aware of where you are investing.

EDIT : as Michael Patch noted in the comments , the address a0000 also dangerous. A more secure address would be 9c000 .

Update memory card

Update the memory card with blocks that suit your needs.
Write down where your kernel begins and ends, where your dynamic data is located, etc.

for instance

 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 

So far, you could leave using static blocks on a memory card, but if you want to use more than 1 megabyte of memory, you need a query BIOS to get an available memory card.
This is internally dynamic, as each system has a different amount of memory.

This map is nothing but very minimal metadata for the memory manager, so the time has come ...

Implement a simple memory manager

In the base environment that you have installed so far, enter a simple memory manager in which blocks of memory are stored.

Pitfall: The memory manager needs some “meta-memory” to store its book of allocated memory, this call is for speculation.

As soon as you can allocate and free memory, you can move the stack to a large area, load other data from the disk or its equivalent, etc.
The idea is that you can now manage memory dynamically, as in C, with malloc and mfree freeing you from the burden of mental handling of a memory card.

More advanced memory manager

A more advanced memory manager is usually written in a high-level language where data manipulation is easier (especially when working with topics such as paging).

+3
source

Instead of putting your stack at the end of your code, put it at the beginning. This is a common practice for bootloaders. In other words:

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

Now you go down from the place where your boatman code is loaded. It also makes things easier, since one of the following things you are going to do is download the second step. This makes it easy to create the second stage in the same assembly file, load it and go to it without worrying about rewriting the stack code.

+1
source

One of the things I do in my startup code is to put the stack as high as possible in regular memory.

  SYS_MEM equ 12H StkSize equ 44 ; # of 1024 byte blocks to reserve for stack 
  int SYS_MEM ; 1K blocks to EBDA sub ax, StkSize ; 1K blocks for stack shl ax, 6 ; * 64 = Base segment of stack ; Align stack on 4k boundary for PLM4 mapping. xor al, al mov es, ax xor cx, cx ; Guarantees MSB of CX will be NULL mov di, cx ; ES:DI = Destination mov ch, StkSize*2 ; Block size * 512 = 16 bit words or ax, -1 ; Fill pattern FFFF rep stosw ; DI points to TOS, bump it back for a 96 byte scratch area and 14 ; values that were preserved in prolog. sub di, 124 mov si, sp mov cl, 14 ; Registers saved in prolog push di rep ss movsw ; Copy to new stack frame mov bp, di ; BP = Base of 96 byte scratch buffer. ; This may look a little odd, but retrieving the value that was saved ; three instructions ago, does work. cli pop sp push es pop ss ; SS:SP = TOS sti 

There are a few things that are relevant to my requirements, but essentially I grab 44k at the top of the memory, and this is where the stack is located until I fall into the 64-bit version.

Then the code that the kernel reads is transferred to 540H, and the kernel (1181 sector) is read from 1000H to 94600H in my case. I found this to be very practical, in real and protected modes.

+1
source

Source: https://habr.com/ru/post/1270159/


All Articles