Calculate the address spaces my kernel is occupying?

Discussions on more advanced topics such as monolithic vs micro-kernels, transactional memory models, and paging vs segmentation should go here. Use this forum to expand and improve the wiki!

Moderators: JAAman, klange, Octocontrabass, sortie, kmcguire, chase, thepowersgang, Owen, Combuster, AJ, 01000101, carbonBased, Candy, pcmattman

nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

I have set the starting address of my linker to be at 16 bytes using linker.ld:

Code: Select all

ENTRY(_start)
SECTIONS
{
   . = 16;
}



I'm not sure if this config makes all the .data and .text sections to be right next to each other?
If it does then is there a way to see how much memory the kernel itself is occupying after grub loads it into memory? i.e how can the kernel tell where the safe and free address space starts which wont overwrite the kernel itself?

For the sake of simplicity don't worry about stack at the moment.
nexos
Member
Member
Posts: 1071
Joined: Tue Feb 18, 2020 3:29 pm
Freenode IRC: nexos

Re: Calculate the address spaces my kernel is occupying?

Post by nexos »

Yes, change your linker script to this

Code: Select all

ENTRY(_start)
SECTIONS
{
       . = 16;
       .text : ALIGN(4K)
   {
      *(.text)
   }
 
   /* Read-only data. */
   .rodata : ALIGN(4K)
   {
      *(.rodata)
   }
 
   /* Read-write data (initialized) */
   .data : ALIGN(4K)
   {
      *(.data)
   }
 
   /* Read-write data (uninitialized) and stack */
   .bss : ALIGN(4K)
   {
      *(COMMON)
      *(.bss)
   }
        end = .;
}

Then in you C code do:

Code: Select all

extern uint32_t end[];
uint32_t ksize = end - 16;

ksize contains the size of you kernel in bytes.

I included sections in the above linker script because it's a good idea to be explicit about alignment. One more thing: putting your kernel's base at address 0x16 is not a good idea for a number of reasons. That region of memory is reserved by the BIOS. I recommend putting your kernel at 0x100000, as that is not used by the BIOS for anything.

Look at the memory map page on the wiki for more info on this.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

Thank you, can a pointer be used instead of the array? i.e something like this
```
extern uint32_t *end;
```
or an array must be used?
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

Looks like it has to be an array, compiler needs to do it's trick where it makes an array look like a pointer. It's not allocating an actual pointer variable, so arrays are needed.
davmac314
Member
Member
Posts: 118
Joined: Mon Jul 05, 2021 6:57 pm

Re: Calculate the address spaces my kernel is occupying?

Post by davmac314 »

nexos wrote:Yes, change your linker script to this

Code: Select all

ENTRY(_start)
SECTIONS
{
       . = 16;
       .text : ALIGN(4K)

That seems a little whack. Since you've just set ". = 16" won't the next line put ".text" right on the 4k mark? Which means ". = 16" is effectively ". = 4K".

Then in you C code do:

Code: Select all

extern uint32_t end[];
uint32_t ksize = end - 16;

ksize contains the size of you kernel in bytes.


No, it won't, due to the kernel really starting at 4k and due to the fact that subtracting 16 from a uint32_t pointer will effectively subtract 64 (i.e. 16 * sizeof(uint32_t)) before the conversion back to integer. And you really should use a cast (from pointer type back to uint32_t). (And if this is for a 64-bit kernel you'll want to use a uint64_t instead, or you'll get a compilation error).

I suggest using an opaque, never-defined struct type instead of an array, and casting to char * for the arithmetic:

Code: Select all

extern struct opaque end;
uint32_t ksize = (uint32_t)((char *)&end - 32);


(Though as I said, subtracting 32 probably won't give you the right answer here anyway).

Using an incomplete struct type prevents you from accidentally using the *value* instead of the *address*.

One more thing: putting your kernel's base at address 0x16 is not a good idea for a number of reasons. That region of memory is reserved by the BIOS. I recommend putting your kernel at 0x100000, as that is not used by the BIOS for anything.

The address is virtual, it doesn't need to correspond to the physical address where the kernel is loaded.
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Calculate the address spaces my kernel is occupying?

Post by kzinti »

Building on the previous reply, why not simply define a symbol at the start of the kernel as well. This way you don't need to do any arithmetics using magic numbers. Another suggestion would be to just declare the symbols as char[] and get rid of the casts. Finally use size_t to hold the kernel size, don't hardcode a uint32_t or uint64_t there...

Code: Select all

extern char _kernel_start[];
extern char _kernel_end[];
...
size_t kernel_size = _kernel_end - kernel_start;
Last edited by kzinti on Fri Jul 29, 2022 4:48 am, edited 3 times in total.
nexos
Member
Member
Posts: 1071
Joined: Tue Feb 18, 2020 3:29 pm
Freenode IRC: nexos

Re: Calculate the address spaces my kernel is occupying?

Post by nexos »

davmac314 wrote:That seems a little whack. Since you've just set ". = 16" won't the next line put ".text" right on the 4k mark? Which means ". = 16" is effectively ". = 4K".

It depends on if you count alignment space a part of the kernel. But it would make more sense to just align the entire base on a 4K aligned area.
davmac314 wrote:No, it won't, due to the kernel really starting at 4k and due to the fact that subtracting 16 from a uint32_t pointer will effectively subtract 64 (i.e. 16 * sizeof(uint32_t)) before the conversion back to integer. And you really should use a cast (from pointer type back to uint32_t). (And if this is for a 64-bit kernel you'll want to use a uint64_t instead, or you'll get a compilation error).

I suggest using an opaque, never-defined struct type instead of an array, and casting to char * for the arithmetic:

I admit that part of my post was erroneous. I would recommend this then:

Code: Select all

extern uint8_t end;
uint32_t ksize = (uint32_t)&end - 16;

Then you avoid the the more confusing casts.
davmac314 wrote:The address is virtual, it doesn't need to correspond to the physical address where the kernel is loaded.

The OP hasn't specified if they have set up paging, so I don't know.
Thank you, can a pointer be used instead of the array? i.e something like this
```
extern uint32_t *end;
```
or an array must be used?

It could be a pointer, using would just need to take the address of the pointer, e.g.:

Code: Select all

extern uint32_t* end;
uint32_t ksize = (uint32_t)&end - 16;

kzinti wrote:Building on the previous reply, why not simply define a symbol at the start of the kernel as well.

That's a good idea. I would definitely recommend doing that.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

Is `ALIGN(4K)` really needed here? Can it be omitted or the value changed? Also I don't have paging enabled.
nexos
Member
Member
Posts: 1071
Joined: Tue Feb 18, 2020 3:29 pm
Freenode IRC: nexos

Re: Calculate the address spaces my kernel is occupying?

Post by nexos »

nightcrawler wrote:Is `ALIGN(4K)` really needed here? Can it be omitted or the value changed? Also I don't have paging enabled.

If paging is not used, than it is not needed.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

So my original method

Code: Select all

ENTRY(_start)
SECTIONS
{
   . = 16;
}

is ok to use? or we still need sections without alignment?
nexos
Member
Member
Posts: 1071
Joined: Tue Feb 18, 2020 3:29 pm
Freenode IRC: nexos

Re: Calculate the address spaces my kernel is occupying?

Post by nexos »

Yes, as long as you define the start and end symbols as kzinti suggested. Also, remember that with paging disabled, you should probably put your kernel above 0x100000, as 0x16 physical is reserved.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

Thanks, so in its current form, would it look like this?

Code: Select all

ENTRY(_start)
SECTIONS
{
       . = 16;
        start = .;
        end = .;
}

i.e `start` is put before `16` or after it?
kzinti
Member
Member
Posts: 898
Joined: Mon Feb 02, 2015 7:11 pm

Re: Calculate the address spaces my kernel is occupying?

Post by kzinti »

Why do you want that 16 there at all? What is it doing for you?
nightcrawler
Posts: 17
Joined: Thu Aug 08, 2019 8:21 am

Re: Calculate the address spaces my kernel is occupying?

Post by nightcrawler »

Assume its any other number, like 1M, would this work?

Code: Select all

        . = 1M;
        start = .;
        end = .;
nexos
Member
Member
Posts: 1071
Joined: Tue Feb 18, 2020 3:29 pm
Freenode IRC: nexos

Re: Calculate the address spaces my kernel is occupying?

Post by nexos »

You need to explicitly put the sections between start and end. As it currently stands, the sections will probably be after the end symbol.
"How did you do this?"
"It's very simple — you read the protocol and write the code." - Bill Joy
Projects: NexNix | libnex | nnpkg
Post Reply