Page 1 of 1

[SOLVED] How do I construct a pointer to a virtual address?

Posted: Sat Jan 04, 2020 10:53 pm
by peachsmith
My current goal is to create a pointer to a contiguous sequence of bytes.
I've enabled paging and have identity mapped the first page table.
As far as I can tell, it looks like I can build a pointer to a single char, but not an array of char.

Here is my attempt at enabling paging, based on the wiki article:

Code: Select all

uint32_t page_directory[1024] __attribute__((aligned(4096)));
uint32_t first_page_table[1024] __attribute__((aligned(4096)));

extern void load_page_directory(uint32_t*);
extern void enable_paging();

void init_paging()
{
	uint32_t i;

	for (i = 0; i < 1024; i++)
	{
		page_directory[i] = 0x00000002;
	}

	// Identity map the first page table
	for (i = 0; i < 1024; i++)
	{
		first_page_table[i] = (i * 0x1000) | 3;
	}

	page_directory[0] = ((uint32_t)first_page_table) | 3;

	load_page_directory(page_directory);
	enable_paging();
}
It was my understanding that on x86 in 32-bit protected mode without PAE enabled, a virtual address is basically a 32-bit number where the 10 most significant bits are the index within a page directory, the next 10 most significant bits are the index within a page table, and the 12 least significant bits are the offset within a physical frame.
If that's the case, then as long as the page table and directory are present, shouldn't the construction of a pointer just be a matter of combining the page directory index, page table index, and frame offset like so?

Code: Select all

uint32_t dir_i = 0;   // directory index [0, 1023]
uint32_t tab_i = 255; // table index     [0, 1023]
uint32_t offset = 0;  // frame offset    [0, 4095]

/**
 * The goal of this function is to construct a valid pointer to virtual memory.
 * There is currently only one page table with all of its 1024 entries
 * populated with identity mapping.
 * So the physical addresses 0x00 - 0x3FFFFF are mapped to the virtual
 * addresses 0x00 - 0x3FFFFF.
 */
void* build_pointer(size_t n)
{	
	uint32_t v_addr; // a virtual address

	// shift the 10 bit directory index to the left by 22	
	v_addr = dir_i << 22;
	
	// OR the address with the 10-bit table index shifted to the left by 12
	v_addr |= (tab_i << 12);
	
	// for now, we're only allowing allocation within one frame
	// and not worrying about freeing memory
	if (offset + n < 0xFFF)
	{
		v_addr |= offset;
		offset += n;
		return (void*)v_addr;
	}
	
	return NULL;
}
So basically, when I build a pointer to a char near the beginning of a frame, I can set the value pointed to by that pointer to be a single character, but I can't treat the pointer as an array.

Code: Select all

	char* my_char = (char*)build_pointer(3);
	
	my_char[0] = 'A';
	my_char[1] = 'B';
	my_char[2] = 'C';
	
	vga_putchar(my_char[0]); // This prints 'A'
	vga_putchar('\n');
	
	vga_putchar(my_char[1]); // This prints a garbage character
	vga_putchar('\n');
	
	vga_putchar(my_char[2]); // This prints a garbage character
	vga_putchar('\n');
What am I doing wrong?
Is it my alignment?

Re: How do I construct a pointer to a virtual address?

Posted: Sun Jan 05, 2020 12:48 am
by nullplan
No, as far as I can see, you've done most of it right. Your problem is the underlying physical memory. In your case, you are identity mapping everything, and the first pointer returned from build_pointer() is 0x000ff000. That is smack in the middle of BIOS ROM, so you cannot write there, or else you destroy the BIOS ROM copy. Use a different page for your experiments, like 0x00001000, which is past the IVT and the BDA and way below the EBDA.

Re: How do I construct a pointer to a virtual address?

Posted: Sun Jan 05, 2020 11:24 am
by peachsmith
Thanks! That was the problem.
Looks like I have some reading to do on the x86 physical memory map and detecting memory before I can reliably map frames on the fly.

Re: How do I construct a pointer to a virtual address?

Posted: Sun Jan 05, 2020 11:29 am
by bzt
Hi,

@nullplan is right. I'd just like to add that you don't need to construct a pointer. All the translations are done for you by the MMU. See the memory as a contiguous big array of bytes, and don't care which physical page it's mapped to (as long as there's a mapping, present bit set you won't get an exception).

So to "build" a pointer, just simply use

Code: Select all

ptr = 0x00001000;
Because at the early stage it can be very confusing when you're using a physical address or a virtual address, I'd recommend to create two typedefs:

Code: Select all

 typedef phys_t uint32_t;
typedef virt_t uint32_t;
and use it like this to make your code more readable:

Code: Select all

ptr = (virt_t)0x00001000;
Cheers,
bzt