Running code in regular memory when linked to higher-half?
Posted: Sat Jul 01, 2023 3:12 pm
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:
And the linkerfile, since we're dealing with virtual memory:
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
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 = .;
}