]> git.ipfire.org Git - thirdparty/ipxe.git/commitdiff
[librm] Add support for installing a startup IPI handler
authorMichael Brown <mcb30@ipxe.org>
Fri, 15 Mar 2024 13:12:22 +0000 (13:12 +0000)
committerMichael Brown <mcb30@ipxe.org>
Fri, 15 Mar 2024 13:27:06 +0000 (13:27 +0000)
Application processors are started via INIT and SIPI interprocessor
interrupts: the INIT places the processor into a "wait for SIPI"
state, and the SIPI then starts the processor in real mode at a
page-aligned address derived from the SIPI vector number.

Add support for installing a real-mode SIPI handler that will switch
the CPU into protected mode with flat physical addressing, load
initial register contents, and then jump to the address of a
protected-mode SIPI handler.  No stack pointer is set up, to avoid the
need to allocate stack space for each available processor.

We use 32-bit physical addressing in order to minimise the changes
required for a 64-bit build.  The existing long mode transition code
relies on the existence of the stack, so we cannot easily switch the
application processor into long mode.  We could use 32-bit virtual
addressing, but this runtime environment does not currently exist
outside of librm.S itself in a 64-bit build, and using it would
complicate the implementation of the protected-mode SIPI handler.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
src/arch/x86/include/librm.h
src/arch/x86/transitions/librm.S
src/arch/x86/transitions/librm_mgmt.c

index 40f075439a26aee3896351fbd2a8f76c1318ea36..84b345d3aad69420e057a83d895f8d764db07175 100644 (file)
@@ -474,6 +474,26 @@ extern struct page_table io_pages;
  */
 #define IO_BASE ( ( void * ) 0x100000000ULL )
 
+/** Startup IPI real-mode handler */
+extern char __text16_array ( sipi, [] );
+#define sipi __use_text16 ( sipi )
+
+/** Length of startup IPI real-mode handler */
+extern char sipi_len[];
+
+/** Startup IPI real-mode handler copy of real-mode data segment */
+extern uint16_t __text16 ( sipi_ds );
+#define sipi_ds __use_text16 ( sipi_ds )
+
+/** Startup IPI protected-mode handler (physical address) */
+extern uint32_t sipi_handler;
+
+/** Startup IPI register state */
+extern struct i386_regs sipi_regs;
+
+extern void setup_sipi ( unsigned int vector, uint32_t handler,
+                        struct i386_regs *regs );
+
 #endif /* ASSEMBLY */
 
 #endif /* LIBRM_H */
index 3943132466a8c4cbb3f1785c4b2de9107f8b4a80..a93b0251ff7f193e06c093aba626ef47e825d7c0 100644 (file)
@@ -1632,3 +1632,70 @@ init_pages:
 
        /* Return */
        ret
+
+/****************************************************************************
+ * sipi (real-mode jump)
+ *
+ * Handle Startup IPI
+ *
+ * This code must be copied to a page-aligned boundary in base memory.
+ * It will be entered with %cs:0000 pointing to the start of the code.
+ * The stack pointer is undefined and so no stack space can be used.
+ *
+ ****************************************************************************
+ */
+       .section ".text16.sipi", "ax", @progbits
+       .code16
+       .globl  sipi
+sipi:
+       /* Retrieve rm_ds from copy */
+       movw    %cs:( sipi_ds - sipi ), %ax
+       movw    %ax, %ds
+
+       /* Load GDT and switch to protected mode */
+       data32 lgdt gdtr
+       movl    %cr0, %eax
+       orb     $CR0_PE, %al
+       movl    %eax, %cr0
+       data32 ljmp $VIRTUAL_CS, $VIRTUAL(1f)
+
+       /* Copy of rm_ds required to access GDT */
+       .globl  sipi_ds
+sipi_ds:
+       .word   0
+
+       /* Length of real-mode SIPI handler to be copied */
+       .globl  sipi_len
+       .equ    sipi_len, . - sipi
+
+       .section ".text.sipi", "ax", @progbits
+       .code32
+1:     /* Set up protected-mode segment registers (with no stack) */
+       movw    $VIRTUAL_DS, %ax
+       movw    %ax, %ds
+       movw    %ax, %ss
+       movw    $PHYSICAL_DS, %ax
+       movw    %ax, %es
+       movw    %ax, %fs
+       movw    %ax, %gs
+
+       /* Load register state and clear stack pointer */
+       movl    $VIRTUAL(sipi_regs), %esp
+       popal
+
+       /* Switch to flat physical addressing */
+       movw    $PHYSICAL_DS, %sp
+       movw    %sp, %ds
+       movw    %sp, %ss
+
+       /* Clear stack pointer */
+       xorl    %esp, %esp
+
+       /* Jump to protected-mode SIPI handler */
+       ljmp    %cs:*VIRTUAL(sipi_handler)
+
+       /* Protected-mode SIPI handler vector */
+       .section ".data.sipi_handler", "aw", @progbits
+       .globl  sipi_handler
+sipi_handler:
+       .long   0, PHYSICAL_CS
index da221e8b17c4dbc0ec9ee57f873231de5d691887..b3820589c4137d7e41bc087100994451b57154b4 100644 (file)
@@ -45,6 +45,9 @@ struct idtr64 idtr64 = {
        .limit = ( sizeof ( idt64 ) - 1 ),
 };
 
+/** Startup IPI register state */
+struct i386_regs sipi_regs;
+
 /** Length of stack dump */
 #define STACK_DUMP_LEN 128
 
@@ -402,6 +405,29 @@ __asmcall void check_fxsr ( struct i386_all_regs *regs ) {
               ( ( regs->flags & CF ) ? " not" : "" ) );
 }
 
+/**
+ * Set up startup IPI handler
+ *
+ * @v vector           Startup IPI vector
+ * @v handler          Protected-mode startup IPI handler physical address
+ * @v regs             Initial register state
+ */
+void setup_sipi ( unsigned int vector, uint32_t handler,
+                 struct i386_regs *regs ) {
+
+       /* Record protected-mode handler */
+       sipi_handler = handler;
+
+       /* Update copy of rm_ds */
+       sipi_ds = rm_ds;
+
+       /* Save register state */
+       memcpy ( &sipi_regs, regs, sizeof ( sipi_regs ) );
+
+       /* Copy real-mode handler */
+       copy_to_real ( ( vector << 8 ), 0, sipi, ( ( size_t ) sipi_len ) );
+}
+
 PROVIDE_UACCESS_INLINE ( librm, phys_to_user );
 PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
 PROVIDE_UACCESS_INLINE ( librm, virt_to_user );