Virtual memory voodo behavior.

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
Sourcer
Member
Member
Posts: 58
Joined: Fri Jun 17, 2016 11:29 pm
Libera.chat IRC: WalterPinkman

Virtual memory voodo behavior.

Post by Sourcer »

This is so damn weird..
I'm implementing a virtual memory manager for "kernel space" (i.e. higher 1GB in my 4GB RAM) that i could use it for my 'kmalloc'.

I already have a physical memory manager, i already identity mapped the first 4MB of my RAM(kernel is in the first 2MB, but just in case..)
I already mapped the higher 1GB to my kernel physical memory addresses.

Now i'm implementing a 'virtual page alloactor' that uses a phyiscal page allocator in the background, and only maps it to the correct page directory entry, and page table index.

Now for the problem:

My physical memory alloactor returned an address of an available page:

Code: Select all

available_page_addr = allocate_physical_page();
make_present_read_write_supervisor(&available_page_addr);
available_page_addr has a value of 0x190000, and i'm OR'ing it with 0x3, so after calling the second function, the value will be:
0x190003.

This is a valid address, and since is within the first 4MB of my RAM, so i can access it even without mapping it to the higher 1GB, because of the identity map i've already done.
So, this is valid and prints the value that i expect:

Code: Select all

printf("%x\n", *(int*)physical_page_addr = 5);
Now for the complex part...
I have limited my kernel heap to 512MB size, so in fact, i have 512 / 4 page tables(since each page table maps 4MB).
So i declare my page tables statically:

Code: Select all

typedef unsigned int page_pointer_t;
page_pointer_t kernel_page_tables[512 / 4][1024] __attribute__((aligned(4096));
kernel_page_table is an array of 128 arrays of size (1024 * sizeof(int)), i.e. each array is 4096 bytes,
AND IS ALIGNED TO PAGE BOUNDARY!

I start my management from addresss 0xc0400000, entry 769 in my kernel page directory.
So one would say, in order to map the physical address 'available_page_addr', to virtual address 0xc0400000,
i shall do:

Code: Select all

//First map the page table for 0xc0400000 - +4MB
//And bitwise OR it with 0x3
kernel_page_directory[0xc0400000 >> 22] = kernel_page_tables | 0x3;
//Map the physical page address, it is already OR'd with 0x3
kernel_page_directory[0xc0400000 >> 22][(0xc0400000 >> 12) & 0x3ff] = (page_pointer_t)physical_page_addr;
Or, after calculating:

Code: Select all

kernel_page_directory[769][0] = (page_pointer_t)physical_page_addr;
So far so good?

Well, this code is suppose to work:

Code: Select all

*((char*)0xc0400000) = 0x5;
But a bloody triple fault is thrown by QEMU.

I'll refresh your memory again:

- The physical page address is page aligned: 0x190000
- The page table address is aligned(gcc attribute), and its addresss, just in case, is: 0x104000
- the value of the kernel page directory in entry 769 is 0x104003, i.e. the first page table, OR'd with 3.
- the value of the first page table in index 0 is 0x190003, i.e. the physical page OR'd with 3.

Any ideas why?
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: Virtual memory voodo behavior.

Post by linuxyne »

* Could you paste the datatype of kernel_page_directory?
The reason for this question: int a[][] is not quite the same as int *a[].

* What does qemu monitor's 'info tlb' command show?

* Is it possible to test on bochs?

Edit: If qemu is run with -d cpu_reset, it dumps the cpu state at the time of the fault, usually on the stderr. May at least help with the register values at the time of the fault.
Sourcer
Member
Member
Posts: 58
Joined: Fri Jun 17, 2016 11:29 pm
Libera.chat IRC: WalterPinkman

Re: Virtual memory voodo behavior.

Post by Sourcer »

linuxyne wrote:* Could you paste the datatype of kernel_page_directory?
The reason for this question: int a[][] is not quite the same as int *a[].

* What does qemu monitor's 'info tlb' command show?

* Is it possible to test on bochs?

Edit: If qemu is run with -d cpu_reset, it dumps the cpu state at the time of the fault, usually on the stderr. May at least help with the register values at the time of the fault.

Code: Select all

typedef unsigned int page_table_t
page_table_t* kernel_page_directory[1024] __attribute__((aligned(PAGE_SIZE)));
The highest address on 'info tlb' is 0xc03ff000, and i flush the TLB using x86 instruction just in case, after i update the page table.

Code: Select all

invlpg [0]
Couldn't boot my kernel on bochs, so i just stayed with QEMU for now.

output from QEMU:

Code: Select all

Triple fault
CPU Reset (CPU 0)
EAX=c0400000 EBX=00009500 ECX=000b8000 EDX=00000007
ESI=00000000 EDI=00190000 EBP=c018e028 ESP=c018e010
EIP=c0184be5 EFL=00000282 [--S----] CPL=0 II=0 A20=1 SMM=0 HLT=0
ES =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
CS =0008 00000000 ffffffff 00cf9a00 DPL=0 CS32 [-R-]
SS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
DS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
FS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
GS =0010 00000000 ffffffff 00cf9300 DPL=0 DS   [-WA]
LDT=0000 00000000 0000ffff 00008200 DPL=0 LDT
TR =0000 00000000 0000ffff 00008b00 DPL=0 TSS32-busy
GDT=     00101204 00000017
IDT=     c018e060 000007ff
CR0=80000011 CR2=c0400000 CR3=00102000 CR4=00000000
DR0=00000000 DR1=00000000 DR2=00000000 DR3=00000000 
DR6=ffff0ff0 DR7=00000400
CCS=00000010 CCD=c018e010 CCO=ADDL    
EFER=0000000000000000
FCW=037f FSW=0000 [ST=0] FTW=00 MXCSR=00001f80
FPR0=0000000000000000 0000 FPR1=0000000000000000 0000
FPR2=0000000000000000 0000 FPR3=0000000000000000 0000
FPR4=0000000000000000 0000 FPR5=0000000000000000 0000
FPR6=0000000000000000 0000 FPR7=0000000000000000 0000
XMM00=00000000000000000000000000000000 XMM01=00000000000000000000000000000000
XMM02=00000000000000000000000000000000 XMM03=00000000000000000000000000000000
XMM04=00000000000000000000000000000000 XMM05=00000000000000000000000000000000
XMM06=00000000000000000000000000000000 XMM07=00000000000000000000000000000000


CR3 looks valid, that is the page directory physical address. CR0 looks valid also.
They are all valid, because i can use pointers on identity map pages and other kernel pages that are already mapped.
CR2 contains the address i was reffering..

so i get it, page fault occures, i haven't implemented the page fault routine just yet, so things just go crazy. but why would a page fault occur when i just mapped that address?
linuxyne
Member
Member
Posts: 211
Joined: Sat Jul 02, 2016 7:02 am

Re: Virtual memory voodo behavior.

Post by linuxyne »

The effect of

Code: Select all

kernel_page_directory[0xc0400000 >> 22] = kernel_page_tables | 0x3;
kernel_page_directory[0xc0400000 >> 22][(0xc0400000 >> 12) & 0x3ff] = (page_pointer_t)physical_page_addr;
is not what is intended.

a[x][y] when used with int *a[] becomes:

Code: Select all

int *p = a[x];
p[y] = value;
In our case,

Code: Select all

int *p = KPD[769]; // we expect p to contain KPT, but it actually contains KPT  | 3
p[y] = value; // writes to index y into an array starting at an (unaligned) address KPT | 3. Not that the misalignment is the direct cause. The misaligned write must have gone through and the 'xp' command at the qemu monitor should show that.
This can be tried instead, with appropriate casting required:

Code: Select all

kernel_page_directory[0xc0400000 >> 22] = kernel_page_tables;
kernel_page_directory[0xc0400000 >> 22][(0xc0400000 >> 12) & 0x3ff] = (page_pointer_t)physical_page_addr;
kernel_page_directory[0xc0400000 >> 22] |= 0x3;
Edit: added the sample with a[x][y]
Sourcer
Member
Member
Posts: 58
Joined: Fri Jun 17, 2016 11:29 pm
Libera.chat IRC: WalterPinkman

Re: Virtual memory voodo behavior.

Post by Sourcer »

linuxyne wrote:The effect of

Code: Select all

kernel_page_directory[0xc0400000 >> 22] = kernel_page_tables | 0x3;
kernel_page_directory[0xc0400000 >> 22][(0xc0400000 >> 12) & 0x3ff] = (page_pointer_t)physical_page_addr;
is not what is intended.

a[x][y] when used with int *a[] becomes:

Code: Select all

int *p = a[x];
p[y] = value;
In our case,

Code: Select all

int *p = KPD[769]; // we expect p to contain KPT, but it actually contains KPT  | 3
p[y] = value; // writes to index y into an array starting at an (unaligned) address KPT | 3. Not that the misalignment is the direct cause. The misaligned write must have gone through and the 'xp' command at the qemu monitor should show that.
This can be tried instead, with appropriate casting required:

Code: Select all

kernel_page_directory[0xc0400000 >> 22] = kernel_page_tables;
kernel_page_directory[0xc0400000 >> 22][(0xc0400000 >> 12) & 0x3ff] = (page_pointer_t)physical_page_addr;
kernel_page_directory[0xc0400000 >> 22] |= 0x3;
Edit: added the sample with a[x][y]
Image

I can't believe it! I'M SO MAD, HOW DIDN'T I SEE THIS?
Post Reply