Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > alt.os.development > #18740
| From | scott@slp53.sl.home (Scott Lurndal) |
|---|---|
| Subject | Re: [OSDev] How to switch to long mode in x86 CPUs? |
| Newsgroups | alt.os.development |
| References | <871pvje5yq.fsf@onesoftnet.eu.org> <vprtt6$3jah9$1@dont-email.me> <875xkughgw.fsf@onesoftnet.eu.org> <vpv2di$5t5$1@reader1.panix.com> <cYEwP.46244$SZca.36012@fx13.iad> |
| Message-ID | <w1FwP.46245$SZca.33446@fx13.iad> (permalink) |
| Organization | UsenetServer - www.usenetserver.com |
| Date | 2025-03-01 14:42 +0000 |
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:
*/
Back to alt.os.development | Previous | Next — Previous in thread | Find similar | Unroll thread
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
csiph-web