Using global variable causes page fault

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
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

Using global variable causes page fault

Post 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
User avatar
samueldotj
Member
Member
Posts: 32
Joined: Mon Nov 13, 2006 12:24 am

Post by samueldotj »

Is the page fault value/address(CR2) matches with your global variable address?
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post by Solar »

Add .rodata to your linker script...
Every good solution is obvious once you've found it.
hendric
Member
Member
Posts: 38
Joined: Sat Oct 21, 2006 10:56 am
Location: China

Post by hendric »

Solar wrote:Add .rodata to your linker script...
I know .rodata is necessary but I wanna know How does it work ? Hmm.....
Just Lazy Writing Anything...
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Post 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".
Every good solution is obvious once you've found it.
smbogan
Member
Member
Posts: 29
Joined: Tue Nov 21, 2006 3:17 pm

Post by smbogan »

It might be helpful to see where you declare it and how you use it.
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

Post 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
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

Post 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
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Post 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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
oscoder
Member
Member
Posts: 59
Joined: Mon Mar 27, 2006 12:00 am
Location: UK

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