iPXE
bios_mp.c
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2024 Michael Brown <mbrown@fensystems.co.uk>.
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License as
6  * published by the Free Software Foundation; either version 2 of the
7  * License, or any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
17  * 02110-1301, USA.
18  *
19  * You can also choose to distribute this program under the terms of
20  * the Unmodified Binary Distribution Licence (as given in the file
21  * COPYING.UBDL), provided that you have satisfied its requirements.
22  */
23 
24 FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25 
26 /** @file
27  *
28  * BIOS multiprocessor API implementation
29  *
30  */
31 
32 #include <registers.h>
33 #include <ipxe/uaccess.h>
34 #include <ipxe/timer.h>
35 #include <ipxe/msr.h>
36 #include <ipxe/mp.h>
37 
38 /** Local APIC base address MSR */
39 #define MSR_APIC_BASE 0x0000001b
40 
41 /** Local APIC is in x2APIC mode */
42 #define MSR_APIC_BASE_X2APIC 0x400
43 
44 /** Local APIC base address mask */
45 #define MSR_APIC_BASE_MASK ( ~0xfffULL )
46 
47 /** Interrupt command register */
48 #define APIC_ICR 0x0300
49 
50 /** Interrupt command register (x2APIC) */
51 #define MSR_X2APIC_ICR 0x830
52 
53 /** Interrupt command register: send to all excluding self */
54 #define APIC_ICR_ALL_NOT_SELF 0x000c0000
55 
56 /** Interrupt command register: level mode */
57 #define APIC_ICR_LEVEL 0x00008000
58 
59 /** Interrupt command register: level asserted */
60 #define APIC_ICR_LEVEL_ASSERT 0x00004000
61 
62 /** Interrupt command register: INIT */
63 #define APIC_ICR_INIT 0x00000500
64 
65 /** Interrupt command register: SIPI */
66 #define APIC_ICR_SIPI( vector ) ( 0x00000600 | (vector) )
67 
68 /** Time to wait for an IPI to complete */
69 #define IPI_WAIT_MS 10
70 
71 /**
72  * Startup IPI vector
73  *
74  * The real-mode startup IPI code must be copied to a page boundary in
75  * base memory. We fairly arbitrarily choose to place this at 0x8000.
76  */
77 #define SIPI_VECTOR 0x08
78 
79 /** Protected-mode startup IPI handler */
80 extern void __asmcall mp_jump ( mp_addr_t func, mp_addr_t opaque );
81 
82 /**
83  * Execute a multiprocessor function on the boot processor
84  *
85  * @v func Multiprocessor function
86  * @v opaque Opaque data pointer
87  */
88 static void bios_mp_exec_boot ( mp_func_t func, void *opaque ) {
89 
90  /* Call multiprocessor function with physical addressing */
91  __asm__ __volatile__ ( PHYS_CODE ( "pushl %k2\n\t"
92  "pushl %k1\n\t"
93  "call *%k0\n\t"
94  "addl $8, %%esp\n\t" )
95  : : "r" ( mp_address ( mp_call ) ),
96  "r" ( mp_address ( func ) ),
97  "r" ( mp_address ( opaque ) ) );
98 }
99 
100 /**
101  * Send an interprocessor interrupt
102  *
103  * @v apic APIC base address
104  * @v x2apic x2APIC mode enabled
105  * @v icr Interrupt control register value
106  */
107 static void bios_mp_ipi ( void *apic, int x2apic, uint32_t icr ) {
108 
109  /* Write ICR according to APIC/x2APIC mode */
110  DBGC ( MSR_APIC_BASE, "BIOSMP sending IPI %#08x\n", icr );
111  if ( x2apic ) {
112  wrmsr ( MSR_X2APIC_ICR, icr );
113  } else {
114  writel ( icr, ( apic + APIC_ICR ) );
115  }
116 
117  /* Allow plenty of time for delivery to complete */
118  mdelay ( IPI_WAIT_MS );
119 }
120 
121 /**
122  * Start a multiprocessor function on all application processors
123  *
124  * @v func Multiprocessor function
125  * @v opaque Opaque data pointer
126  */
127 static void bios_mp_start_all ( mp_func_t func, void *opaque ) {
128  struct i386_regs regs;
129  uint64_t base;
130  uint32_t ipi;
131  void *apic;
132  int x2apic;
133 
134  /* Prepare SIPI handler */
135  regs.eax = mp_address ( func );
136  regs.edx = mp_address ( opaque );
138 
139  /* Get local APIC base address and mode */
140  base = rdmsr ( MSR_APIC_BASE );
141  x2apic = ( base & MSR_APIC_BASE_X2APIC );
142  DBGC ( MSR_APIC_BASE, "BIOSMP local %sAPIC base %#llx\n",
143  ( x2apic ? "x2" : "" ), ( ( unsigned long long ) base ) );
144 
145  /* Map local APIC */
146  apic = ioremap ( ( base & MSR_APIC_BASE_MASK ), PAGE_SIZE );
147  if ( ! apic )
148  goto err_ioremap;
149 
150  /* Assert INIT IPI */
153  bios_mp_ipi ( apic, x2apic, ipi );
154 
155  /* Clear INIT IPI */
156  ipi &= ~APIC_ICR_LEVEL_ASSERT;
157  bios_mp_ipi ( apic, x2apic, ipi );
158 
159  /* Send SIPI */
161  bios_mp_ipi ( apic, x2apic, ipi );
162 
163  iounmap ( apic );
164  err_ioremap:
165  /* No way to handle errors: caller must check that
166  * multiprocessor function executed as expected.
167  */
168  return;
169 }
170 
171 PROVIDE_MPAPI_INLINE ( pcbios, mp_address );
#define IPI_WAIT_MS
Time to wait for an IPI to complete.
Definition: bios_mp.c:69
#define PHYS_CODE(asm_code_str)
Definition: librm.h:281
#define APIC_ICR
Interrupt command register.
Definition: bios_mp.c:48
PROVIDE_MPAPI_INLINE(pcbios, mp_address)
#define DBGC(...)
Definition: compiler.h:505
static mp_address(void *address)
Calculate address as seen by a multiprocessor function.
Definition: efi_mp.h:25
i386 registers.
unsigned long long uint64_t
Definition: stdint.h:13
iPXE timers
#define SIPI_VECTOR
Startup IPI vector.
Definition: bios_mp.c:77
#define PAGE_SIZE
Page size.
Definition: io.h:27
uint32_t eax
Definition: registers.h:109
#define MSR_APIC_BASE_MASK
Local APIC base address mask.
Definition: bios_mp.c:45
static const void * base
Base address.
Definition: crypto.h:335
static __always_inline unsigned long virt_to_phys(volatile const void *addr)
Convert virtual address to a physical address.
Definition: uaccess.h:287
Access to external ("user") memory.
void mp_start_all(mp_func_t func, void *opaque)
Start a multiprocessor function on all application processors.
void __asmcall mp_call(mp_addr_t func, mp_addr_t opaque)
Call a multiprocessor function from C code on the current CPU.
#define __asmcall
Declare a function with standard calling conventions.
Definition: compiler.h:15
#define APIC_ICR_INIT
Interrupt command register: INIT.
Definition: bios_mp.c:63
void writel(uint32_t data, volatile uint32_t *io_addr)
Write 32-bit dword to memory-mapped device.
void() mp_func_t(mp_addr_t opaque, unsigned int cpuid)
A multiprocessor function.
Definition: mp.h:51
static void bios_mp_exec_boot(mp_func_t func, void *opaque)
Execute a multiprocessor function on the boot processor.
Definition: bios_mp.c:88
FILE_LICENCE(GPL2_OR_LATER_OR_UBDL)
unsigned long mp_addr_t
An address within the address space for a multiprocessor function.
Definition: mp.h:24
static void bios_mp_start_all(mp_func_t func, void *opaque)
Start a multiprocessor function on all application processors.
Definition: bios_mp.c:127
#define APIC_ICR_LEVEL
Interrupt command register: level mode.
Definition: bios_mp.c:57
#define APIC_ICR_SIPI(vector)
Interrupt command register: SIPI.
Definition: bios_mp.c:66
__asm__ __volatile__("\n1:\n\t" "movb -1(%3,%1), %%al\n\t" "stosb\n\t" "loop 1b\n\t" "xorl %%eax, %%eax\n\t" "mov %4, %1\n\t" "rep stosb\n\t" :"=&D"(discard_D), "=&c"(discard_c), "+m"(*value) :"r"(data), "g"(pad_len), "0"(value0), "1"(len) :"eax")
unsigned int uint32_t
Definition: stdint.h:12
struct i386_regs regs
Definition: registers.h:15
Multiprocessor functions.
void setup_sipi(unsigned int vector, uint32_t handler, struct i386_regs *regs)
Set up startup IPI handler.
Definition: librm_mgmt.c:415
#define APIC_ICR_ALL_NOT_SELF
Interrupt command register: send to all excluding self.
Definition: bios_mp.c:54
A 32-bit general register dump.
Definition: registers.h:62
#define MSR_APIC_BASE
Local APIC base address MSR.
Definition: bios_mp.c:39
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition: timer.c:78
void __asmcall mp_jump(mp_addr_t func, mp_addr_t opaque)
Protected-mode startup IPI handler.
__asm__(".section \".rodata\", \"a\", " PROGBITS "\n\t" "\nprivate_key_data:\n\t" ".size private_key_data, ( . - private_key_data )\n\t" ".equ private_key_len, ( . - private_key_data )\n\t" ".previous\n\t")
#define MSR_X2APIC_ICR
Interrupt command register (x2APIC)
Definition: bios_mp.c:51
#define MSR_APIC_BASE_X2APIC
Local APIC is in x2APIC mode.
Definition: bios_mp.c:42
PROVIDE_MPAPI(pcbios, mp_exec_boot, bios_mp_exec_boot)
#define APIC_ICR_LEVEL_ASSERT
Interrupt command register: level asserted.
Definition: bios_mp.c:60
void iounmap(volatile const void *io_addr)
Unmap I/O address.
uint32_t edx
Definition: registers.h:93
void mp_exec_boot(mp_func_t func, void *opaque)
Execute a multiprocessor function on the boot processor.
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
static void bios_mp_ipi(void *apic, int x2apic, uint32_t icr)
Send an interprocessor interrupt.
Definition: bios_mp.c:107
Model-specific registers.