Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > alt.os.development > #18740

Re: [OSDev] How to switch to long mode in x86 CPUs?

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

Show all headers | View raw


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 | NextPrevious in thread | Find similar | Unroll thread


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