summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Brown2024-03-15 14:12:22 +0100
committerMichael Brown2024-03-15 14:27:06 +0100
commita67f913d6635772af746fc215066e3d510ae25d1 (patch)
tree8a398a6478b31fed595c77b31e700e2999fe91b1
parent[efi] Provide a multiprocessor API for EFI (diff)
downloadipxe-a67f913d6635772af746fc215066e3d510ae25d1.tar.gz
ipxe-a67f913d6635772af746fc215066e3d510ae25d1.tar.xz
ipxe-a67f913d6635772af746fc215066e3d510ae25d1.zip
[librm] Add support for installing a startup IPI handler
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>
-rw-r--r--src/arch/x86/include/librm.h20
-rw-r--r--src/arch/x86/transitions/librm.S67
-rw-r--r--src/arch/x86/transitions/librm_mgmt.c26
3 files changed, 113 insertions, 0 deletions
diff --git a/src/arch/x86/include/librm.h b/src/arch/x86/include/librm.h
index 40f07543..84b345d3 100644
--- a/src/arch/x86/include/librm.h
+++ b/src/arch/x86/include/librm.h
@@ -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 */
diff --git a/src/arch/x86/transitions/librm.S b/src/arch/x86/transitions/librm.S
index 39431324..a93b0251 100644
--- a/src/arch/x86/transitions/librm.S
+++ b/src/arch/x86/transitions/librm.S
@@ -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
diff --git a/src/arch/x86/transitions/librm_mgmt.c b/src/arch/x86/transitions/librm_mgmt.c
index da221e8b..b3820589 100644
--- a/src/arch/x86/transitions/librm_mgmt.c
+++ b/src/arch/x86/transitions/librm_mgmt.c
@@ -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 );