How to select a specific load address for kernel?

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.
Post Reply
Thunder

How to select a specific load address for kernel?

Post by Thunder »

How to select a specific load address for kernel?
And what it should be ?

Again the beginner in OS creating Thunder ;)
Spiner

Re:How to select a specific load address for kernel?

Post by Spiner »

Just add the following option to the linker command line:
-Ttext <load address>
For example: -Ttext 0x100000

Or use a linker script (the following is the one I use) with the -T <filename> option for ld:

OUTPUT_FORMAT("elf32-i386")
phys_load_address = 0x00100000;
ENTRY (entry)

SECTIONS
{
   . = phys_load_address;

   .text :
   {
      *(.text)
   }

   .rodata :
   {
      *(.rodata)
   }

   .data ALIGN (0x1000) :
   {
      *(.data)
   }

   .bss :
   {
      _sbss = .;
      *(COMMON)
      *(.bss)
      _ebss = .;
   }
}

Ciao.

/Spiner
Schol-R-LEA

Re:How to select a specific load address for kernel?

Post by Schol-R-LEA »

Thunder wrote: How to select a specific load address for kernel?
And what it should be ?
Based on you earlier posting, I'm assumng that you're coding in NASM at this point, and that the second stage of the mini-kernel is in real mode. If you're in p-mode, then issues are somewhat different, and even moreso if you're coding in C.

In any case, you are really asking three questions: where should the boot code load the second stage, where should the boot loader jump to in the loaded code, and how do you set up the second stage code itself to begin at the desired origin?

The first question is fairly easy to answer. At the stage where you are loading the second stage, the only areas you don't have free to use in memory are:

the first 256 bytes (the interrupt table);
the 512 bytes from 0000:7C00 to 0000:7D00, containing the bootstrap;
the segment where you defined the stack for the boot loader, if any;
and
the system areas starting at A000:0000.

In principle, you can load the second stage code anywhere else that you'd like; in a real OS, you'd probably be limited by the design as to where you can put it. You can also relocate the stack, or, if you really wanted to, the boot code itself; unless you have a compelling reason to do so, however, I'd recommend against it.

The easiest solution is: if the second stage is the main kernel, and fits within 31K, you can load it at 0010:0000 (absolute address 0x00100), just above interrupt table; otherwise, load it at some convienent point above the boot loader, such as 0100:0000 (absolute address 0x01000).

The next question is more troublesome, but still at least one fairly straightforward answer, which also provides the easiest solutio to question three. This is to simply assume that the entry point of the code is at offset 0x0000 in the segment it is loaded into. Given this, you can simply push the segment address, push zero, and perform a RETF to transfer to the entry point.

Ironically, the earlier version of your kernel had code to do this (in the subroutine 'begin'), but at this point, unless you are loading a third stage,it is not needed; this is should have occurred in the boot loader already.

To coninue, you now have to make sure that the second stage code entry point matches the address the boot code transfers to. In this case, the simple solution is to set the code as [org 0000], and place the entry point at *very* start of code. This means that it must come before anything else that anything that actually causes code or data to be emitted into the binary stream: op codes, subroutines, DBs, RESBs, etc. Comments, EQUates, %macro and %struct definitions *can* be before this in the source code, but you must be careful not do anything that will put a byte into the binary directly. You can, however, use a JMP to a farther point and then define you subroutines between the two areas, if you truly wish to.

Another interesting solution, used by Myke Predko in <i>PC PhD</i>, is to push the segment offset followed by 0x0100, then RETF; you then use [org 0100] instead of [org 0000], and proceed otherwise the same way. The advantage of this is that you can use it to load and run any DOS COM file from a self-booting floppy, providing it makes no DOS calls and the boot loader can load all of it correctly.

In point of fact, you can put the entry point more or less wherever you want within your code, but this is of only academic interest unless your design absolutely calls for it. In any case, the key is to make sure that the address which the boot strap transfers control to is the same as that of the actual entry point. Since this could involve some rather messy calculations on your part to get it right, it is best simply to leave it as simple as possible.
Thunder

Re:How to select a specific load address for kernel?

Post by Thunder »

Thank a lot, for your long answers :)
Thunder
P.S. yes i'm programming with NASM... ;)
User avatar
Pype.Clicker
Member
Member
Posts: 5964
Joined: Wed Oct 18, 2006 2:31 am
Location: In a galaxy, far, far away
Contact:

Re:How to select a specific load address for kernel?

Post by Pype.Clicker »

the first 256 bytes (the interrupt table);
the 512 bytes from 0000:7C00 to 0000:7D00, containing the bootstrap;
the segment where you defined the stack for the boot loader, if any;
and the system areas starting at A000:0000.
i've just gone quickly through it, but it seems to me that you've forgotten the Bios variables area (post, etc) where stuffs like INT16 keyboard buffer, INT13 disk parameters, boot status, etc. are stored. They are stored from 0040:0000 to 0040:0100 included.

Overwriting these will probably lead to bad behaviour (i guess you don't want to see your code changing at each keypress or every second after having re-enabled interrupts
8) )
Schol-R-LEA

Oops

Post by Schol-R-LEA »

You're absolutely right. Thank you for reminding me.

Also, the boot loader is from 07C00 to 07E00, not 07D00. Just thought I'd correct that.

BTW, I'd recommend that any constants be defined as EQUates, just to make future changes easier and make the code easier to read. This is just good coding practice regardless of what you're working on, of course; putting magic numbers in code directly is a Bad Thing in general.
Peter_Vigren

Re:How to select a specific load address for kernel?

Post by Peter_Vigren »

Schol-R-LEA wrote: the first 256 bytes (the interrupt table);
the 512 bytes from 0000:7C00 to 0000:7D00, containing the bootstrap;
the segment where you defined the stack for the boot loader, if any;
and
the system areas starting at A000:0000.
The Interrupt Table is 1024 bytes (that is, 4 bytes per interrupt) according to the information I got.
Post Reply