How can you map more than 256 TiB in long mode.

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
antoni
Member
Member
Posts: 61
Joined: Sun May 24, 2020 9:11 am
Location: /dev/null

How can you map more than 256 TiB in long mode.

Post by antoni »

I'm switching to BOOTBOOT. For a long time I developed my bottom half kernel but now I want to try a higher half kernel.

The problem is that it's code segment for example is mapped to 0xFFFFFFFFFFE02000. However, using 4-level paging you can only map up to 256 TiB (max. range 0x1000000000000) of memory!

For now I use this function to get physical address of some page:

Code: Select all

intptr_t vm_get_physaddr(struct vm_space *vm_space, void *virtualaddr)
{
	uint64_t pml4 = vm_space->pml4;
	uint64_t ptindex = (uint64_t) virtualaddr / 0x1000 % 512;
	uint64_t pdindex = ((uint64_t) virtualaddr / 0x200000) % 512;
	uint64_t pdpindex = ((uint64_t) virtualaddr / 0x40000000) % 512;
	uint64_t pml4index = ((uint64_t) virtualaddr / 0x8000000000);
	uint64_t *entry = (uint64_t*) pml4;

	if (!(entry[pml4index] & 1))
		return -1;
	entry = (uint64_t*) (entry[pml4index] & ~0xFFF);

	if (!(entry[pdpindex] & 1))
		return -1;
	entry = (uint64_t*) (entry[pdpindex] & ~0xFFF);

	if (!(entry[pdindex] & 1))
		return -1;
	entry = (uint64_t*) (entry[pdindex] & ~0xFFF);

	if (!(entry[ptindex] & 1))
		return -1;
	entry = (uint64_t*) (entry[ptindex] & ~0xFFF);

	return entry;
}
I was able to successfully get address of first 2GiB (or more if available) of memory which are identity mapped in BOOTBOOT protocol. However, obviously this method did not work with higher addresses. I wonder how they were even mapped.
User avatar
qookie
Member
Member
Posts: 72
Joined: Sun Apr 30, 2017 12:16 pm
Libera.chat IRC: qookie
Location: Poland

Re: How can you map more than 256 TiB in long mode.

Post by qookie »

The 48-bit address is sign extended into 64-bits, so bits 63-48 are supposed to have the same value as bit 47, which turns 0x800000000000 into 0xFFFF800000000000, and so on. If you try accessing an address where bits 63-48 don't have the same value as bit 47, you'll get a general protection fault with error code 0.
Working on managarm.
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How can you map more than 256 TiB in long mode.

Post by iansjack »

Check out "canonical address" in the Programmer's Manual.
antoni
Member
Member
Posts: 61
Joined: Sun May 24, 2020 9:11 am
Location: /dev/null

Re: How can you map more than 256 TiB in long mode.

Post by antoni »

I'm not sure how exactly this works... I can't access this same address in non-sign-extended form.

Code: Select all

(gdb) x/b 0xffffffffffe07d40
0xffffffffffe07d40 <buf>:	0x57
(gdb) x/b 0xffffffe07d40
0xffffffe07d40:	Cannot access memory at address 0xffffffe07d40
(gdb) 
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: How can you map more than 256 TiB in long mode.

Post by nullplan »

antoni wrote:I'm not sure how exactly this works... I can't access this same address in non-sign-extended form.
Yes, because those would be non-canonical. The CPU actually checks the sign extension and generates #GP(0) if it is wrong.
Carpe diem!
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How can you map more than 256 TiB in long mode.

Post by iansjack »

0xffffffe07d40 = 0000 0000 0000 0000 1111 1111 1111 1111 1111 1111 1110 0000 0111 1101 0010 0000 in binary.

Bit 47 is 1. This is not extended to bits 48 - 63, which are all 0, so the address is not canonical, and raises an error when you attempt to access it.

0xffffffffffe07d40 = 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1110 0000 0111 1101 0010 0000 in binary.

Bit 47 is 1. This is extended to bits 48 - 63, which are all 1, so the address is canonical and can be accessed without an error.
Ethin
Member
Member
Posts: 625
Joined: Sun Jun 23, 2019 5:36 pm
Location: North Dakota, United States

Re: How can you map more than 256 TiB in long mode.

Post by Ethin »

If you want to map 256-Tbytes, check for five-level paging and then set CR4.LA57. Then set up your PML5 table and fill it with 512 PML5Es.Then canonicalize your addresses by sign-extending bit 56 to bits 57-64 (if memory serves).
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: How can you map more than 256 TiB in long mode.

Post by iansjack »

Restricting your OS to the latest Xeon processors is probably not a good move for a hobby OS.
klange
Member
Member
Posts: 679
Joined: Wed Mar 30, 2011 12:31 am
Libera.chat IRC: klange
Discord: klange

Re: How can you map more than 256 TiB in long mode.

Post by klange »

It's also not what the OP is asking for...
I was able to successfully get address of first 2GiB (or more if available) of memory which are identity mapped in BOOTBOOT protocol. However, obviously this method did not work with higher addresses. I wonder how they were even mapped.
You need to walk the page tables, just like the MMU does, to translate a virtual address to a physical address.
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: How can you map more than 256 TiB in long mode.

Post by Octocontrabass »

antoni wrote:The problem is that it's code segment for example is mapped to 0xFFFFFFFFFFE02000. However, using 4-level paging you can only map up to 256 TiB (max. range 0x1000000000000) of memory!
You can map 256 TiB total, but it's split into two 128 TiB pieces. The lower 128TiB is addresses from 0 to 0x00007FFFFFFFFFFF, and the upper 128 TiB is addresses from 0xFFFF800000000000 to 0xFFFFFFFFFFFFFFFF. Your example address 0xFFFFFFFFFFE02000 is in the upper 128 TiB.

With 4-level paging, you can't map anything from 0x0000800000000000 to 0xFFFF7FFFFFFFFFFF. Addresses in that range will always be invalid.
Post Reply