Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > alt.os.development > #18739 > unrolled thread
| Started by | cross@spitfire.i.gajendra.net (Dan Cross) |
|---|---|
| First post | 2025-03-01 13:34 +0000 |
| Last post | 2025-03-01 14:42 +0000 |
| Articles | 2 — 2 participants |
Back to article view | Back to alt.os.development
This discussion starts older than the indexed window; earlier articles aren't shown. The article labeled Started by
below is the oldest one visible, not the original post.
Re: [OSDev] How to switch to long mode in x86 CPUs? cross@spitfire.i.gajendra.net (Dan Cross) - 2025-03-01 13:34 +0000
Re: [OSDev] How to switch to long mode in x86 CPUs? scott@slp53.sl.home (Scott Lurndal) - 2025-03-01 14:42 +0000
| From | cross@spitfire.i.gajendra.net (Dan Cross) |
|---|---|
| Date | 2025-03-01 13:34 +0000 |
| Subject | Re: [OSDev] How to switch to long mode in x86 CPUs? |
| Message-ID | <vpv2di$5t5$1@reader1.panix.com> |
[Note: Followup-To: alt.os.development] In article <875xkughgw.fsf@onesoftnet.eu.org>, Ar Rakin <rakinar2@onesoftnet.eu.org> wrote: >On 28 Feb 2025 09:59, David Brown <david.brown@hesbynett.no> wrote: >[snip] >> On the other hand, if you are interested in learning the intricacies >> of the x86 world, you need to look elsewhere for information - as >> Keith says, it is not really C related when you are writing >> assembly. comp.arch might be a helpful group. > >Looks like comp.arch might be the group I needed to find! Thanks. Comp.arch is not a good choice here. It's for discussion of computer architecutre generally, specifics of how to boot an x86 core would be mostly tangential. alt.os.development is probably your best bet, but please try not to feed the troll there. >[snip] >Also just to clarify, from what I know, since the x86 processors start >in 16-bit real mode for the sake of compatibility, you'd have to start >writing 16-bit code first, then switch to 32-bit protected mode, then >finally switch to 64-bit long mode. However if you use an existing >bootloader such as GRUB, then you usually would not need to worry about >16-bit code since GRUB drops you in 32-bit protected mode. But still, >if you wish to switch to 64-bit long mode, you *will* have to mix 64-bit >and 32-bit code together. > >Either I don't know how to, or I just don't feel comfortable mixing >32-bit and 64-bit code together *when in C* because you will have a hard >time linking those together. Please correct me if I am wrong. Eh...It can be done, but I don't see the point. Even starting from the reset vector (e.g., in 16-bit 8086 mode) it's not that many instructions to get yourself into long mode with paging enabled. To see an example of how this is done, have a look at: https://github.com/oxidecomputer/phbl/blob/main/src/start.S It's sufficient straight-forward, and so low-level, that doing this in a high-level language doesn't seem worth it from a cost/benefit perspective. - Dan C.
[toc] | [next] | [standalone]
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Date | 2025-03-01 14:42 +0000 |
| Message-ID | <w1FwP.46245$SZca.33446@fx13.iad> |
| In reply to | #18739 |
scott@slp53.sl.home (Scott Lurndal) writes:
>cross@spitfire.i.gajendra.net (Dan Cross) writes:
>>[Note: Followup-To: alt.os.development]
>>
>>In article <875xkughgw.fsf@onesoftnet.eu.org>,
>>Ar Rakin <rakinar2@onesoftnet.eu.org> wrote:
>>>On 28 Feb 2025 09:59, David Brown <david.brown@hesbynett.no> wrote:
>>>[snip]
>>>> On the other hand, if you are interested in learning the intricacies
>>>> of the x86 world, you need to look elsewhere for information - as
>>>> Keith says, it is not really C related when you are writing
>>>> assembly. comp.arch might be a helpful group.
>>>
>>>Looks like comp.arch might be the group I needed to find! Thanks.
>>
>>Comp.arch is not a good choice here. It's for discussion of
>>computer architecutre generally, specifics of how to boot an x86
>>core would be mostly tangential.
>
>Here's some x86 bootstrap code from MBR (pre UEFI) days.
And here's the part that runs in long mode.
#include "dvmm_constants.h"
#include "core/asm_offsets.h"
/**
* \fn void ::dvmmstart(uint32 e820space, uint32 bspflag, uint32 rmseg)
* \brief The 64-bit code entry point
* \param e820space The number of bytes of e820 space remaining. Passed
* in the <b>%eax</b> register.
* \param bspflag Set to zero to indicate that the Bootstrap Processor
* (BSP) or one to indicate that an Application Processor (AP) has
* invoked this function. Passed in the <b>%ebx</b> register.
* \param rmseg The real-mode segment selector used by the second-stage
* bootstrap program. Passed in the <b>%esi</b> register.
* \remark This function never returns.
*
* This code is the long mode 64-bit code entry point. It is
* loaded by the PXE boot loader (or 3leaf floppy boot loader) to
* physical address 0x100000, virtual address 0xffff830000100000.
* It is responsible for setting up the 64 bit GDT and IDT and then
* invoking the C++ code. It executes based at 0x100000 until the
* call into ::dvmm_bsp_start, from whence we're running at 0xffff83...
*
* \note Note that this code is linked into the main hypervisor ELF image.
*
*/
SS_CODE=0x10
SS_DATA=0x18
.code64
.section inittext,"xa",@progbits
# .text
# On entry to dvmmstart:
# %eax contains the amount of e820 buffer space remaining
# %ebx contains 0 for the bootstrap processor (BSP), and 1 for application
# processors (AP).
# %esi contains the real-mode segment selector for the secondary bootstrap
#
.global dvmmstart
dvmmstart:
#
# Get processor into known state.
#
cld
cli
movl %esi, %r15d # Save real-mode segment base
xorq %rsi, %rsi # EAX contains 'memsize' initialized
movl %eax, %esi # before calling setup64.s
movabsq $PHYSMAP_BASE, %r12 # Base DVMM virtual address
#
# 64bit RSP points to the stack's linear address.
#
leaq init_stack_top(%rip), %rsp
addq %r12, %rsp # Rebase the stack
#
# Fill in the GDTR.
#
leaq gdt64(%rip), %rax # Load the address of the gdt
leaq gdtdesc64(%rip), %rcx # Load the address of the gdt descriptor
addq $2, %rcx # &gdt goes 2 bytes into the gdt descriptor
movq %r12, (%rcx) # Start with PHYSMAP_BASE
addq %rax, (%rcx) # offset to gdt
lgdtq gdtdesc64(%rip) # Load the GDT
#
# Load up the new selectors.
#
movl $SS_DATA, %eax
movl %eax, %ds
movl %eax, %ss
movl %eax, %es
movl %eax, %fs
movl %eax, %gs
#
# Fill in the IDTR.
#
leaq idt64(%rip), %rax # Load the address of the idt
leaq idtdesc64(%rip), %rcx # Load the address of the idt descriptor
addq $2, %rcx # &idt goes 2 bytes into the gdt descriptor
movq %r12, (%rcx) # Start with PHYSMAP_BASE
addq %rax, (%rcx) # add offset to &idt
#
# Fill in the IDT.
#
testl $1, %ebx # BSP or AP?
jnz 2f # AP, just load IDT
movabsq $handlerlist, %r11 # List of interrupt handlers
movl $((idt64_end - idt64) / 16), %ecx # Number of entries to copy
1:
movq (%r11), %rdx # Get handler address
addq $8, %r11 # Skip to next
movw %dx, (%rax) # Set the low 16 bits of the target offset
movw $SS_CODE, 2(%rax) # Set the target CS
movw $0x8e00, 4(%rax) # Set the gate flags
shrq $16, %rdx # Shift temp_int address down by 16
movw %dx, 6(%rax) # Set bits 31-16 of the target offset
shrq $16, %rdx # Shift temp_int address down by 16 more
movl %edx, 8(%rax) # Set bits 63-32 of the target offset
addq $16, %rax # Next IDT64 entry
decw %cx # Down by one
testw $0xffff, %cx # Is it zero yet?
jnz 1b # No, loop around
2:
lidt idtdesc64(%rip) # Load the IDT
#
# Clear BSS
#
testl $1, %ebx # BSP or AP?
jnz 1f # AP, skip clear BSS & constructors
xorq %rax, %rax
leaq _sbss(%rip), %rdi
movq $0, %rcx
leaq _ebss(%rip), %rcx
subq %rdi, %rcx
rep stosb
pushq %rsi # Save RSI, since we'll need it later.
#
# Invoke the debugger early initialization function
#
movabsq $debugger, %rdi # Set this
movabsq $_ZN10c_debugger10early_initEv, %rcx # Call ::early_init
call *%rcx
#
# Invoke global C++ constructors.
#
movabsq $__call_constructors, %rcx
call *%rcx
popq %rsi # Restore RSI.
#
# Invoke C++ code. Pass begin and end address of memory map.
#
1:
movq $512, %rax # Starting with 512 bytes
subq %rsi, %rax # Subtract remaining
addq $0x90000, %rax # e820 data map address
# Use x86_64 calling conventions
movq $0x90000, %rdi # Pass arg1 to main
addq %r12, %rdi # Rebase arg1
movq %rax, %rsi # Pass arg2 to main
addq %r12, %rsi # Rebase arg2
movq %r15, %rdx # Arg3 is real-mode segment base
xorq %rbp, %rbp # Indicate end of stack for -fframe-pointer
movabsq $dvmm_bsp_start, %rcx # We use an indirect jump to invoke main
testl $1, %ebx # BSP or AP?
jz 4f # BSP, use 'dvmm_bsp_start'
movabsq $dvmm_ap_start, %rcx # AP, use 'dvmm_ap_start'
4: call *%rcx # because it is more than 2GB away from here.
#
# Should never return.
#
1:
hlt
jmp 1b
#
# Global Descriptor Table
#
.align 16,0
gdt64:
.long 0, 0 # Entry 0 (NULL segment descriptor)
.long 0, 0 # Entry 1 (reserved)
.long 0x0000ffff, 0x00AF9A00 # Code read/exec long mode
.long 0x0000ffff, 0x00CF9200 # Data read/write.
gdt64_end:
gdtdesc64: # 64 bit GDT Descriptor
.word gdt64_end-gdt64-1 # limit
.quad PHYSMAP_BASE # 64 bit GDT base (overwritten above)
#
# Interrupt Descriptor Table
#
/* These are overwritten above in dvmmstart. */
#define IGATE .quad 0x0, 0x0
.align 16,0
idt64:
IGATE #DE
IGATE #DB
IGATE #NMI
IGATE #BP
IGATE #OF
IGATE #BR
IGATE #UD
IGATE #NM
IGATE #DF
IGATE #Reserved
IGATE #TS
IGATE #NP
IGATE #SS
IGATE #GP
IGATE #PF
IGATE #MF
IGATE #AC
IGATE #MC
IGATE #XF
idt64_end:
idtdesc64:
.word idt64_end-idt64-1 # limit
.quad PHYSMAP_BASE # 64 bit IDT base (overwritten above)
handlerlist:
.quad fault_divide
.quad trap_debug
.quad fault_nmi
.quad trap_breakpoint
.quad trap_overflow
.quad fault_bounds
.quad fault_ud
.quad fault_nm
.quad abort_df
.quad fault_invalid9
.quad fault_invtss
.quad fault_notpresent
.quad fault_stack
.quad fault_gp
.quad fault_page
.quad fault_invalid15
.quad fault_fpe
.quad fault_alignment
.quad machine_check
.quad fault_simd
.quad fault_invalid20
.quad fault_invalid21
.quad fault_invalid22
.quad fault_invalid23
.quad fault_invalid24
.quad fault_invalid25
.quad fault_invalid26
.quad fault_invalid27
.quad fault_invalid28
.quad fault_invalid29
.quad fault_security
.quad fault_invalid31
.align 4096,0
/*
* Application processor initialization code. This needs to be aligned on a
* page boundary, as we use the LAPIC startup interprocessor interrupt (IPI)
* to start the application processors (AP) from the bootstrap processor (BSP),
* and the startup API requires a page-aligned address within the first
* megabyte of DRAM as a start-vector.
*/
.global dvmm_ap_init
.global dvmm_ap_base
.code16
dvmm_ap_init:
jmp 1f
dvmm_ap_base:
.word 0x0000
1: wbinvd
mov %cs, %ax
mov %ax, %ds
cli
mov %cs:(0x2), %ax
mov %ax, %es
sub $0x20, %ax
/*
* We only need a few bytes (4, to be exact) on the stack used prior
* to invoking dvmmstart in long-mode. Use the bottom portion of the
* segment starting at $3000:$0, the first portion of the 512 bytes at
* that segment is only used by the bootstrap processor to hold the
* BIOS E820 table.
*/
mov %ax, %ss
mov $0x1f0, %sp
mov %es, %ax
mov %ax, %cs:(2f-dvmm_ap_init)
movw $1, %es:(0x20) # Mark AP startup
.byte 0xea # Long Jump
.word 0x0 # Offset from seg base == 0
2: .word 0x0 # Real-mode segment base
.global dvmm_ap_init_end
dvmm_ap_init_end:
.long 0
.global init_stack
.global init_stack_top
.align STACK_SIZE_ASM,0
init_stack:
.space STACK_SIZE_ASM, 0
init_stack_top:
/* vim: sw=4 sts=4 sta ts=8:
*/
[toc] | [prev] | [standalone]
Back to top | Article view | alt.os.development
csiph-web