paging problem

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.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

paging problem

Post by xyjamepa »

Hi...

I have read this tutorial about implementing simple paging
and it worked fine without any page faults or triple faults.
But I have a problem I want to make the first 4MB identity mapped,
so the physical addresses became equal to virtual addresses.
Could you please help me.

Code: Select all

void init_paging()
{
 printf("enabling paging...");
 
 unsigned long address=0;
 dword i;
 for(i=0;i<1024;i++)
  {
    page_table[i]= address | 3 ; //supervisor ,read/write ,present
    address= address+4096 ;
  }
 
 page_directory[0]= page_table;
 page_directory[0]= page_directory[0] | 3; 
 
 for(i=1;i<1024;i++)
  page_directory[i]= 0 | 2;

 
 write_cr3(page_directory);         // put that page directory address into CR3
 write_cr0(read_cr0() | 0x80000000); // set the paging bit in CR0 to 1
 
 printf("[Done]\n");

}
Thanx.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

That tutorial sets you up with the first 4 MB identity mapped...
User avatar
AndrewAPrice
Member
Member
Posts: 2311
Joined: Mon Jun 05, 2006 11:00 pm
Location: USA (and Australia)

Post by AndrewAPrice »

What's your problem?
My OS is Perception.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...
pcmattman wrote:That tutorial sets you up with the first 4 MB identity mapped...
Good I didn't know that,
Would you please explain to me how did that happen?
Also if I want to map a new page directory entry,
but without identity mapping how can I do that ?


Thanx.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

I have time...

I'll talk you through it. This is the exact same code I use to map my kernel (and I use other address spaces, so I know it works).

Code: Select all

unsigned long address = 0;
Three guesses, first 2 don't count. For this code alone, which is setting up the kernel address space, this'll hold the physical addresses.

Code: Select all

 dword i;
 for(i=0;i<1024;i++)
  {
    page_table[i]= address | 3 ; //supervisor ,read/write ,present
    address= address+4096 ;
  }
Each page table has 1024 4 byte entries (4 kb, 1 page). This basically maps each address 1:1 (so 0 == 0, 0x100000 == 0x100000).

By OR-ing it by 3, you are setting attributes. 3 is 11b, so the page is present (1b) and has read/write privileges (10b). It's currently set as supervisor, so ring3 tasks will page fault on access to this page. If you want it to be available to all, you could set the fourth bit (USER privileges).

If you read the intel manuals you can get better descriptions of these bits.

Notice how the address goes up 4 kb each time? That's because paging maps pages (funnily enough) and pages are generally 4 kb (you can get 2 mb and 4 mb pages as well). I'll come back to mapping other addresses later.

Code: Select all

 page_directory[0]= page_table;
 page_directory[0]= page_directory[0] | 3;
This just maps the first entry in the page directory (the one that maps 0-4 MB) to the page table and ORs it by 3 to have proper privileges.

Code: Select all

 for(i=1;i<1024;i++)
  page_directory[i]= 0 | 2;
This sets all other pages to non-present. You do this so that the CPU doesn't try to access random memory (you can never rely on what was there before you decided it was useful to you).

Code: Select all

 write_cr3(page_directory);         // put that page directory address into CR3
 write_cr0(read_cr0() | 0x80000000); // set the paging bit in CR0 to 1
This sets CR3 to your page directory, and then enables paging (bit 31 iirc). CR3 will always hold the current page directory. Never set it to NULL unless you disable paging.

Now that I've explained how that all works...
Also if I want to map a new page directory entry,
but without identity mapping how can I do that ?
Say we have the address 0x400000 (the 4 MB mark). Watch this:

Code: Select all

uint_t pagedir_offset = 0x400000 >> 22;
uint_t pagetab_offset = 0x400000 << 10 >> 10 >> 12;
Then, using such code as this:

Code: Select all

uint_t virt-addr = 0x400000;
((uint_t*) (pagedir[virt_addr >> 22] & ~0xFFF ))[virt_addr << 10 >> 10 >> 12] = phys_addr | flags;
You can set mapping exactly the way you want it. By the way, you AND with ~0xFFF because you want to remove the privilege bits (remember OR-ing by 3 earlier?).

I may as well provide some utility functions, but remember that these won't just integrate straight away. You'll need to make it allocate pages the way you need it to (pm_alloc should be replaced with whatever function you use to allocate pages).

Code: Select all

// gets the kernel page directory
uint_t GetKernelPageDir()
{
	return (uint_t) pagedir;
}

// allocates space for a page table
void AllocatePageTable( uint_t pdir, uint_t virt_addr, uint_t flags )
{
	// get a pointer to the page directory
	uint_t* local_pagedir = (uint_t*) pdir;
	
	// is there a page directory for it?
	// if not, then create it
	if( local_pagedir[virt_addr >> 22] == 0 )
	{
		local_pagedir[virt_addr >> 22] = ((uint_t) pm_alloc( 1 )) | flags;
		memset( (uint_t*) local_pagedir[virt_addr >> 22], 0, 4096 );
	}
}

// sets a physical address mapping
void MapPhysicalAddress( uint_t pdir, uint_t virt_addr, uint_t phys_addr, uint_t flags )
{
	// get a pointer to the page directory
	uint_t* local_pagedir = (uint_t*) pdir;
	
	// set the mapping
	if( local_pagedir[virt_addr >> 22] == 0 )
		AllocatePageTable( pdir, virt_addr, flags );
	((uint_t*) (local_pagedir[virt_addr >> 22] & ~0xFFF ))[virt_addr << 10 >> 10 >> 12] = phys_addr | flags;
}

// gets the physical address for a virtual address
uint_t GetPhysicalAddress( uint_t pdir, uint_t virt_addr )
{
	// get a pointer to the page directory
	uint_t* local_pagedir = (uint_t*) pdir;
	
	// get the physical address
	uint_t phys_addr = ((uint_t*) (local_pagedir[virt_addr >> 22] & ~0xFFF))[virt_addr << 10 >> 10 >> 12] & ~0xFFF;
	
	// return it
	return phys_addr;
}

// creates a new address space with a certain size, returning the page directory location
uint_t CreateAddressSpace( uint_t pagecount )
{
	// allocate a page directory for the address space
	uint_t* local_pagedir = (uint_t*) pm_alloc( pagecount );
	
	// zero it out so that no pages are present within it
	memset( local_pagedir, 0, 4096 );
	
	// and then set it up so we have the first 4 MB mapped into the directory
	// the page table is always at 0x9D000 so this will not fail
	memcpy( local_pagedir, pagedir, sizeof( unsigned int ) );
	
	// return the address
	return (uint_t) local_pagedir;
}
In the above code 'pagedir' is the equivalent of 'page_directory' in your code.

You call CreateAddressSpace to get a page directory with the kernel address space already mapped into it. Then you can do this to map addresses:

Code: Select all

MapPhysicalAddress( mypagedir, <virt_addr>, pm_alloc( 1 ), <flags> );
Remember that MapPhysicalAddress only maps 1 page. I'm sure you can figure out how to do it over more than 1 page 8)

I hope this helps. I also hope that some time in the future someone stumbles across this when they were as confused as I was and is able to understand how to get basic paging working.

Good luck.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...

First of all thank you pcmattman for your explain..
Okay now I'm having another problem,I've recently implemented
virtual mode monitor in my os and it worked successfuly,
But now when I enable paging I get page fault exception,I know
if I want to use paging with virtual mode the 1st miga must be
identity mapped,and my the first 4 migas are identity mapped...
any suggestions...


Thanx.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

When a page fault occurs, the address that caused the fault is put into CR2. What's your CR2 value at the time of the fault.

You can also find out the different fields of the error code if you read the intel manuals.

Edit: AJ's reply below is not related to this reply. This is correct.
Last edited by pcmattman on Mon Jul 23, 2007 1:48 am, edited 1 time in total.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Hi,

IIRC, that is not correct. I think the nice thing about v86 is that you can use it with paging - i.e. you do not need to reference physical memory below 1mb.

Sorry - I know this doesn't help the problem though. Are you sure that all your pages are the correct PL (i.e. have the u/s bit set?).

Cheers,
Adam

[edit - posted at the same time as pcmattman!]
Last edited by AJ on Mon Jul 23, 2007 6:31 am, edited 1 time in total.
pcmattman
Member
Member
Posts: 2566
Joined: Sun Jan 14, 2007 9:15 pm
Libera.chat IRC: miselin
Location: Sydney, Australia (I come from a land down under!)
Contact:

Post by pcmattman »

AJ wrote:IIRC, that is not correct. I think the nice thing about v86 is that you can use it with paging - i.e. you do not need to reference physical memory below 1mb.
According to what I read in the intel manuals, the data does not need to be physically at the 1 MB mark, just mapped below 1 MB. It all has to do with the way the processor translates addresses.

Correct me if I'm wrong.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

Yup - that's what I was trying to say in a round-about way :oops:
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...

my page directory at 0x4000 and my page table at 0x5000
my virtual task works in ring 0.
my page directory falgs supervisor,present read/write
I'm using hardware task switching,I have two tasks
main() and my virtual task ,my virtual task works at 0x1000
I gave cr3 field 0x4000 which is the address of my page directory.
my cr2 is 0x1000 which is the address of my virtual task,
after that I changed my virtual task addr to 0x2000 and
cr2 gave me 0x2000.


Thanx.
User avatar
AJ
Member
Member
Posts: 2646
Joined: Sun Oct 22, 2006 7:01 am
Location: Devon, UK
Contact:

Post by AJ »

If you are talking about a v86 task, I believe that has to run in ring 3.

Adam
frank
Member
Member
Posts: 729
Joined: Sat Dec 30, 2006 2:31 pm
Location: East Coast, USA

Post by frank »

You have to set the user bit(4th bit) on all of the pages that the VM86 task is expected to access.
User avatar
xyjamepa
Member
Member
Posts: 397
Joined: Fri Sep 29, 2006 8:59 am

Post by xyjamepa »

Hi...
frank wrote:You have to set the user bit(4th bit) on all of the pages that the VM86 task is expected to access.
I've done this

page_table= address | 7 ;

now the page table is user read/write present,also I've done the
same with the first page directory entry,after that my virtual task
worked fine but after executing BIOS int and when tring to go back to pm
using IRET bochs panic this message:

task_switch: CS not valid executable seg


Thanx.
User avatar
Kevin McGuire
Member
Member
Posts: 843
Joined: Tue Nov 09, 2004 12:00 am
Location: United States
Contact:

Post by Kevin McGuire »

It is some sort of problem with your task state segment. It is reading it and getting a invalid code segment descriptor. I have not had much experience using task state segments, but can you verify that it is correct?
Post Reply