mirror of
https://github.com/ipxe/ipxe
synced 2026-02-14 02:31:26 +03:00
[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>
This commit is contained in:
@@ -474,6 +474,26 @@ extern struct page_table io_pages;
|
|||||||
*/
|
*/
|
||||||
#define IO_BASE ( ( void * ) 0x100000000ULL )
|
#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 /* ASSEMBLY */
|
||||||
|
|
||||||
#endif /* LIBRM_H */
|
#endif /* LIBRM_H */
|
||||||
|
|||||||
@@ -1632,3 +1632,70 @@ init_pages:
|
|||||||
|
|
||||||
/* Return */
|
/* Return */
|
||||||
ret
|
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 ),
|
.limit = ( sizeof ( idt64 ) - 1 ),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Startup IPI register state */
|
||||||
|
struct i386_regs sipi_regs;
|
||||||
|
|
||||||
/** Length of stack dump */
|
/** Length of stack dump */
|
||||||
#define STACK_DUMP_LEN 128
|
#define STACK_DUMP_LEN 128
|
||||||
|
|
||||||
@@ -402,6 +405,29 @@ __asmcall void check_fxsr ( struct i386_all_regs *regs ) {
|
|||||||
( ( regs->flags & CF ) ? " not" : "" ) );
|
( ( 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, phys_to_user );
|
||||||
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
|
PROVIDE_UACCESS_INLINE ( librm, user_to_phys );
|
||||||
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
|
PROVIDE_UACCESS_INLINE ( librm, virt_to_user );
|
||||||
|
|||||||
Reference in New Issue
Block a user