Calling 32-bit C from lower- of higher half 64-bit kernel

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
User avatar
teeuwen
Posts: 13
Joined: Thu May 04, 2017 2:03 am

Calling 32-bit C from lower- of higher half 64-bit kernel

Post by teeuwen »

My x86_64 kernel is loaded into the higher half, at 0xFFFFFFFF80000000. Before paging is set up and the jump to the higher half and long mode is made, I'm subtracting 0xFFFFFFFF80000000 from my addresses to get the lower half physical address. So far so good.

I would, however, like to call some 32-bit C code from assembly while we're still in the lower half (kernel starts at 1MB) to initialize paging, load the gdt. This is presents a problem because said code is linked into the higher half of the kernel which results in an error. I tried to solve this by using GCC's section attribute to relocate the static variables and functions to a section in the lower half (1). Linking now succeeds, but the machine just triple faults and Bochs reports this error:

Code: Select all

bx_dbg_read_linear: physical memory read error (phy=0x000080101170, lin=0x0000000080101170)

This happens if I just:

Code: Select all

call paging_init

Which makes sense. So I tried this:

Code: Select all

lea paging_init, %eax
call *%eax

Check the contents of eax again and now they correspond to the address of the function and the machine doesn't triple fault... But the function is simply not called! What?

My ld script looks like this:

Code: Select all

VM_ADDR = 0xFFFFFFFF80000000;

SECTIONS
{
   . = 0x100000;

   .mboot ALIGN(4096) : {
      *(.mboot)
      . = ALIGN(4096);
   }

   .init ALIGN(4096) : {
      *(.init)
      *(.gdt)
   }

   . += VM_ADDR;

   .text ALIGN(4096) : AT(ADDR(.text) - VM_ADDR) {
      *(.text)
   }

   .data ALIGN(4096) : AT(ADDR(.data) - VM_ADDR) {
      *(.data)
      *(.rodata)
   }

   .bss ALIGN(4096) : AT(ADDR(.bss) - VM_ADDR) {
      *(COMMON)
      *(.bss)
   }

   /DISCARD/ : {
      *(.eh_frame)
      *(.comment)
   }
}


The .init section contains .text compiled for i386 and .gdt is for 32-bit .bss data.

Moving the section is done by prefixing this to functions:

Code: Select all

__attribute__ ((section(".init")))


Maybe what I'm trying to do is just impossible (it shouldn't be) and I should just separate the binaries into a 32-bit loader and the 64-bit kernel. I would love to hear your thoughts on this and perhaps even a solution.

(1) I can move the functions and variables to another section but I haven't found a way to give debugging information a new section, for now I'm just omitting 32-bit debug info using -g0. If you have a solution for this issue as well, that'd be awesome!

P.S. I don't like how my tabs (and tabs are 8 columns, mind you) are displayed as a mere 3 spaces on this forum.

EDIT: I've decided to get rid of the C code and just implement it in assembly. This works for me, though it'd still be nice to know how to tackle this problem in the future if (read when) it comes up again...
"Writing a portable OS is not much harder than a nonportable one, and all systems should be written with portability in mind these days." — Andrew S. Tanenbaum

Elarix
Source: https://github.com/teeuwen/elarix
Post Reply