mirror of https://github.com/ipxe/ipxe.git
[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
parent
89bb926a04
commit
a67f913d66
|
@ -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 */
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue