Page 1 of 1

Running code in regular memory when linked to higher-half?

Posted: Sat Jul 01, 2023 3:12 pm
by minater247
I've been working my way through getting different address spaces working, but I've come to a point where I want to copy one physical frame to another. I am able to get the physical addresses, so now I've tried to write an assembly function that just turns off paging, copies the data, and turns it back on - but I'm running into a problem. All my labels are offset by 3GB (0xC0000000, accessible as $VIRTUAL_BASE), which I've been working around by just subtracting that value, but I've now found that i686-elf-as doesn't allow conditional jumps to addresses in registers (ex. jmp *%edx works, jz *%edx does not). Also, it doesn't allow looping to addresses in registers with the loop command, so I'm not really sure what else to do.

I tried to make a specific section that would be linked to the lower half, but only after the multiboot header, which I wasn't able to get working - so the only option I saw left was rep movsb, but I still don't think that's working properly as the function never returns to the C from which it was called, and I think just ends up running garbage somewhere in lower memory (it never gets to re-enabling paging). I believe the best option here would be to just link to the lower half, but since I couldn't get that to work, I'm not sure what to do. Does anyone know how to make one specific section link normally without the virtual memory offset (and force it to come after the mulriboot header), or have any other way to copy one section of memory to another?

Here is what I've made so far:

Code: Select all

.global copy_page_physical
copy_page_physical:
	pushl %eax
	pushfl
	cli

	# get the source and destination addresses, as pushed from C
	movl 12(%esp), %esi
	movl 16(%esp), %edi

	lea (lower_half_cpp), %edx
	subl $VIRTUAL_BASE, %edx

	# disable paging
	movl %cr0, %ecx
	andl $0x7FFFFFFF, %ecx
	movl %ecx, %cr0
   # we shouldn't need to move the stack, unless we need to pop something before re-enabling paging

	jmp *%edx

lower_half_cpp: # "lower half CopyPagePhysical"
	# movsb needs the number of bytes to copy in ecx, so since we're not storing anything necessary there, that works out
	movl $1024, %ecx
	rep movsb

	# re-enable paging
	lea (higher_half_cpp), %edx

	movl %cr0, %eax
	orl $0x80000000, %eax
	movl %eax, %cr0

	jmp *%edx

higher_half_cpp:

	# magic breakpoint, which *is* triggered, somehow?
	xchgw %bx, %bx

	popfl
	popl %eax
	sti
	ret #either jumps to nonsense, or is never reached
And the linkerfile, since we're dealing with virtual memory:

Code: Select all

/* The bootloader will look at this image and start execution at the symbol
   designated as the entry point. */
ENTRY(_start)

/* A few definitions the assembly code can use for physical addresses. */
PHYSICAL_BASE = 0x00100000;
VIRTUAL_BASE = 0xC0000000;

SECTIONS
{

	/* Begin putting sections at 1 MiB, a conventional place for kernels to be
	   loaded at by the bootloader, and add C0000000 (3GB) for paging. */
	. = 0xC0100000;
 
	/* Multiboot comes first for GRUB to find, then stack*/
	.text ALIGN (0x1000) : AT(ADDR(.text)-0xC0000000)
	{
		*(.multiboot*)
		*(.text*)
	}
 
	/* Read-only data. */
	.rodata ALIGN (0x1000) : AT(ADDR(.rodata)-0xC0000000)
	{
		*(.rodata*)
	}
 
	/* Read-write data (initialized) */
	.data ALIGN (0x1000) : AT(ADDR(.data)-0xC0000000)
	{
		*(.data*)
	}
 
	/* Read-write data (uninitialized) and stack */
	.bss ALIGN (0x1000) : AT(ADDR(.bss)-0xC0000000)
	{
		*(COMMON*)
		*(.bss*)
	}

	KERNEL_END = .;
}

Re: Running code in regular memory when linked to higher-hal

Posted: Sat Jul 01, 2023 4:06 pm
by Octocontrabass
minater247 wrote:Does anyone [...] have any other way to copy one section of memory to another?
Don't turn off paging. Update your page tables to map both regions of memory, if they're not both already mapped. Copy your data. Update your page tables again to unmap any memory you no longer want to have mapped.

Do you need to copy anything in the first place? Paging allows you to map the data at your choice of virtual address.

Re: Running code in regular memory when linked to higher-hal

Posted: Sat Jul 01, 2023 4:07 pm
by Gigasoft
which I've been working around by just subtracting that value, but I've now found that i686-elf-as doesn't allow conditional jumps to addresses in registers
That's because those instructions don't exist. What's wrong with simply "jmp lower_half_cpp-$VIRTUAL_BASE"?

As for why it crashes, it's because you turned off paging while EIP still points to a high address. Instead of doing this dance every time you want to copy a page, you should probably just map the new page somewhere.

Re: Running code in regular memory when linked to higher-hal

Posted: Sat Jul 01, 2023 5:09 pm
by minater247
Don't turn off paging. Update your page tables to map both regions of memory, if they're not both already mapped.
I guess that makes sense too! I had thought it would be easier to just write a snippet of assembly like any other non-paged program would, but just setting up a couple of reserved frames for this purpose could work too.
Do you need to copy anything in the first place?
I had thought cloning address spaces requires copying any non-kernel pages? All the kernel entries I just map to their original places, but I copy everything else to make sure two processes don't collide in memory. Is this not what you're supposed to do?
What's wrong with simply "jmp lower_half_cpp-$VIRTUAL_BASE"?
I was just using that as an example - a better one might be:

Code: Select all

movl $1024, %ecx
lower_half_cpp_loop:
   movb (%esi), %edx
   movb %edx, (%edi)
   incl %edi
   incl %esi
   decl %ecx
   jnz lower_half_cpp_loop
Where I'd tried doing as I had been, since the physical address was in edx:

Code: Select all

jnz *%edx
Which, apparently, doesn't exist. Not exactly sure if jnz or any other conditional jump works with the syntax you showed - not at the same computer right now but I'll check later. First I'll try just using paging to map those addresses elsewhere.


So I guess this whole bit was a tad useless, then - I wonder why the instruction set lets that syntax work, but only for plain jmp commands... who knows. Thanks for all the help, I'll go give this a shot when I get back home!

Re: Running code in regular memory when linked to higher-hal

Posted: Sat Jul 01, 2023 6:45 pm
by Octocontrabass
minater247 wrote:I had thought cloning address spaces requires copying any non-kernel pages?
Only if those pages need to contain different data.
minater247 wrote:All the kernel entries I just map to their original places, but I copy everything else to make sure two processes don't collide in memory. Is this not what you're supposed to do?
That's one way to do it.

Another way is to make the pages read-only and copy them when the new process causes a #PF trying to write to them (copy-on-write). Make sure you set CR0.WP appropriately for your implementation. On Unix-like OSes, where calls to fork() are usually followed by calls to exec(), this is a big optimization.
minater247 wrote:So I guess this whole bit was a tad useless, then - I wonder why the instruction set lets that syntax work, but only for plain jmp commands...
Only Intel could tell you that. I suggest you refer to the Intel (or AMD) manuals if you want to know which instructions support which address modes.

Re: Running code in regular memory when linked to higher-hal

Posted: Sat Jul 01, 2023 7:20 pm
by minater247
That did it, it can now copy data from physical frames! Although I was noticing it was too slow for actual use, that sounds like a really good way to tell if they need to access the data there, I'll implement that once I get kernel/non-kernel program interaction working properly, thanks for showing me that! And I'd been using an online reference that bunched them together (the manual takes ages to load on my computer), so it appears the actual manual serves as a better reference, so I'll go there from now on. Anyways, thank you so much for the help!

Re: Running code in regular memory when linked to higher-hal

Posted: Sun Jul 02, 2023 5:21 am
by Gigasoft
Not exactly sure if jnz or any other conditional jump works with the syntax you showed
Direct near jump instructions are relative, so there is nothing to add or subtract in this case. I highly suggest learning the basic IA-32 instruction set before starting on an operating system instead of guessing. I think the individual volumes of the Intel programmer's manual are available as separate files, so you don't have to load the entire thing.