[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>
pull/1174/head
Michael Brown 2024-03-15 13:12:22 +00:00
parent 89bb926a04
commit a67f913d66
3 changed files with 113 additions and 0 deletions

View 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 */

View 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

View 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 );