Page 1 of 1

Using global variable causes page fault

Posted: Sun Dec 10, 2006 9:06 am
by oscoder
Hi,
In my OS I am currently writing the memory manager, which is being run as a seperate module (and process!) from the kernel, and is loaded using the GRUB module command. It is mapped into address space at 0x8000,0000 (upper two gigabytes), by using the multiboot information that grub gives about the memory locations to which it was loaded. All works perfectly (including task switching to it), until I try to access a global variable (an array for allocating memory), which causes a page fault. Can anyone suggest what I am doing wrong? There's not really much I can think of checking - any point in the array seems to do this, so I haven't defined it too small!

I'll post more code later if need be, for now here's the linker script and module loading code:

Linker Script:

Code: Select all

OUTPUT_FORMAT("binary")
ENTRY (_loader)

SECTIONS
{
    . = 0x80000000;

    .text :
    {
        *(.text)
    }

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

    .bss ALIGN (0x1000):
    {
        _sbss = .;
        *(COMMON)
        *(.bss)
        _ebss = .;
    }
}
Module mapping:

Code: Select all

ushort modules_init(ulong mods_addr)
{
 ulong module_size;
 ulong module_start;
 ulong module_eip;
 ulong i;			/*counter*/
 module_t *mod;

 /*Work out MM's properties*/
	mod = (module_t*)mods_addr;
	module_size = mod->mod_end - mod->mod_start + 1;
	module_start = mod->mod_start;
	module_eip = V_USERSPACE;
	_print("MM Properties evaluated...",0x01,4);

 /*Map memory manager to 0x80000000 (2Gb)*/
	change_page_table(	0, _USERSPACE_FIRST_TABLE,	/*set up the MM's page table*/
						_MM_PAGE_TABLE | PAGE_WRITEABLE | PAGE_PRESENT);

 	for(i=0; i<=(module_size/PAGE_SIZE); i+=PAGE_SIZE)	/*set up MM's pages to point to it's physical address*/
	 change_page(	0, _USERSPACE_FIRST_TABLE, i,
         			((module_start+i)&0xFFFFF000) | PAGE_WRITEABLE | PAGE_PRESENT);
	_print("MM mapped in...",0x01,4);
I've left out stack, etc mapping code to make it more readable, and to the best of my knowledge the page table manipulation functions work fine.

Thanks,
OScoder

Posted: Mon Dec 11, 2006 12:59 am
by samueldotj
Is the page fault value/address(CR2) matches with your global variable address?

Posted: Mon Dec 11, 2006 2:36 am
by Solar
Add .rodata to your linker script...

Posted: Mon Dec 11, 2006 3:58 am
by hendric
Solar wrote:Add .rodata to your linker script...
I know .rodata is necessary but I wanna know How does it work ? Hmm.....

Posted: Mon Dec 11, 2006 4:15 am
by Solar
How does what work?

Code: Select all

OUTPUT_FORMAT("binary") 
ENTRY (_loader) 

SECTIONS 
{ 
    . = 0x80000000; 

    .text : 
    { 
        *(.text) 
    } 

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

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

    .bss ALIGN (0x1000): 
    { 
        _sbss = .; 
        *(COMMON) 
        *(.bss) 
        _ebss = .; 
    } 
} 
Some parts of the static data are placed in .rodata, not .data. Strings, for example, which are char arrays actually. I saw the keyword "array" in your post, and a missing .rodata section in your linker script, and just tossed it in as a "this might help".

Posted: Mon Dec 11, 2006 9:14 am
by smbogan
It might be helpful to see where you declare it and how you use it.

Posted: Mon Dec 11, 2006 11:16 am
by oscoder
Hi. The code still page faults, though I've done some testing about found a few suspisious things.

Windows notes the size of the memory manager binary as 4.01 kilobytes, yet the address in cr2 is 0x80002000. Since the memory manager is loaded at 0x80000000, and 4kB = 0x1000, there must be a problem here! The kernel just maps in what it knows of the file. Is there any way to get the array to be allocated within its own binary file?

Here's the code I use (there is also a large #define constant directly above the array's definition, but it would make things look untidy to put it here!):

Code: Select all

#include "..\include\config.h"
#include "..\include\defs.h"
#include "..\lib\ring0\include\objects.h"
#include ".\include\main.h"
#include ".\include\tmp_scrn.h"

unsigned long alloc_stack[256];
unsigned char alloc_stack_ptr=255;

void parse_request(void *place);

void mm_main()
{
 ushort *instream;
	alloc_stack[0]=321;
	asm("cli");
	asm("hlt");
...
Here also is a CPU dump from when the page fault happened (in bochs):

Code: Select all

protected mode
CS.d_b = 32 bit
SS.d_b = 32 bit
| EAX=00000008  EBX=80000000  ECX=00107000  EDX=00107000
| ESP=80306ff3  EBP=80306ff7  ESI=00026382  EDI=0002638a
| IOPL=0 NV UP EI NG NZ NA PE NC
| SEG selector     base    limit G D
| SEG sltr(index|ti|rpl)     base    limit G D
|   DS:0010( 0002| 0|  0) 00000000 000fffff 1 1
|   ES:0010( 0002| 0|  0) 00000000 000fffff 1 1
|   FS:0010( 0002| 0|  0) 00000000 000fffff 1 1
|   GS:0010( 0002| 0|  0) 00000000 000fffff 1 1
|   SS:0010( 0002| 0|  0) 00000000 000fffff 1 1
|   CS:0008( 0001| 0|  0) 00000000 000fffff 1 1
| EIP=80000046 (80000046)
| CR0=0xe0000011 CR1=0x00000000 CR2=0x80002000
| CR3=0x00110000 CR4=0x00000000
Thanks for your help so far,
OScoder

Posted: Sat Dec 16, 2006 4:41 am
by oscoder
Ok. It seems no-one can see what my problem is. In that case can anyone suggest an OS I could look through for help? Just one that loaded modules via grub (preffereably binary!), would be a real help!

Thanks,
OScoder

Posted: Sat Dec 16, 2006 5:11 am
by Brendan
Hi,
oscoder wrote:Windows notes the size of the memory manager binary as 4.01 kilobytes, yet the address in cr2 is 0x80002000. Since the memory manager is loaded at 0x80000000, and 4kB = 0x1000, there must be a problem here!
Sure - 4 KB in the .text section (due to alignment), plus some bytes in the .data section, plus memory accesses to the .bss section which aren't in the file. My guess is you're using at least 3 pages (if you still don't have a .rodata section), and that this will grow as you write more code and use more data.

GRUB will give you the size of the module/file, but not the size of the .bss section that the module uses. I'd guess you need to put the size of the .bss section in the file somewhere and allocate extra pages for it (and fill them with zero before the module is started).

Just for fun, in your modules_init() function, change the line that calculates the module's size to "module_size = mod->mod_end - mod->mod_start + 1 + 8192;" and see if it helps...


Cheers,

Brendan

Posted: Wed Dec 20, 2006 3:47 pm
by oscoder
the .bss section which aren't in the file
Ok, thanks. Is there any way to get the .bss section put into the file by the linker? (since in a binary file there is no header for such things as the length of a bss section). Btw, I have put a .rodata in now.

Thanks,
OScoder