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
24FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
25
26/** @file
27 *
28 * BIOS multiprocessor API implementation
29 *
30 */
31
32#include <registers.h>
33#include <librm.h>
34#include <ipxe/uaccess.h>
35#include <ipxe/timer.h>
36#include <ipxe/msr.h>
37#include <ipxe/mp.h>
38
39/** Local APIC base address MSR */
40#define MSR_APIC_BASE 0x0000001b
41
42/** Local APIC is in x2APIC mode */
43#define MSR_APIC_BASE_X2APIC 0x400
44
45/** Local APIC base address mask */
46#define MSR_APIC_BASE_MASK ( ~0xfffULL )
47
48/** Interrupt command register */
49#define APIC_ICR 0x0300
50
51/** Interrupt command register (x2APIC) */
52#define MSR_X2APIC_ICR 0x830
53
54/** Interrupt command register: send to all excluding self */
55#define APIC_ICR_ALL_NOT_SELF 0x000c0000
56
57/** Interrupt command register: level mode */
58#define APIC_ICR_LEVEL 0x00008000
59
60/** Interrupt command register: level asserted */
61#define APIC_ICR_LEVEL_ASSERT 0x00004000
62
63/** Interrupt command register: INIT */
64#define APIC_ICR_INIT 0x00000500
65
66/** Interrupt command register: SIPI */
67#define APIC_ICR_SIPI( vector ) ( 0x00000600 | (vector) )
68
69/** Time to wait for an IPI to complete */
70#define IPI_WAIT_MS 10
71
72/**
73 * Startup IPI vector
74 *
75 * The real-mode startup IPI code must be copied to a page boundary in
76 * base memory. We fairly arbitrarily choose to place this at 0x8000.
77 */
78#define SIPI_VECTOR 0x08
79
80/** Protected-mode startup IPI handler */
81extern void __asmcall mp_jump ( mp_addr_t func, mp_addr_t opaque );
82
83/**
84 * Execute a multiprocessor function on the boot processor
85 *
86 * @v func Multiprocessor function
87 * @v opaque Opaque data pointer
88 */
89static void bios_mp_exec_boot ( mp_func_t func, void *opaque ) {
90
91 /* Call multiprocessor function with physical addressing */
92 __asm__ __volatile__ ( PHYS_CODE ( "pushl %k2\n\t"
93 "pushl %k1\n\t"
94 "call *%k0\n\t"
95 "addl $8, %%esp\n\t" )
96 : : "R" ( mp_address ( mp_call ) ),
97 "R" ( mp_address ( func ) ),
98 "R" ( mp_address ( opaque ) ) );
99}
100
101/**
102 * Send an interprocessor interrupt
103 *
104 * @v apic APIC base address
105 * @v x2apic x2APIC mode enabled
106 * @v icr Interrupt control register value
107 */
108static void bios_mp_ipi ( void *apic, int x2apic, uint32_t icr ) {
109
110 /* Write ICR according to APIC/x2APIC mode */
111 DBGC ( MSR_APIC_BASE, "BIOSMP sending IPI %#08x\n", icr );
112 if ( x2apic ) {
113 wrmsr ( MSR_X2APIC_ICR, icr );
114 } else {
115 writel ( icr, ( apic + APIC_ICR ) );
116 }
117
118 /* Allow plenty of time for delivery to complete */
120}
121
122/**
123 * Start a multiprocessor function on all application processors
124 *
125 * @v func Multiprocessor function
126 * @v opaque Opaque data pointer
127 */
128static void bios_mp_start_all ( mp_func_t func, void *opaque ) {
129 struct i386_regs regs;
131 uint32_t ipi;
132 void *apic;
133 int x2apic;
134
135 /* Prepare SIPI handler */
136 regs.eax = mp_address ( func );
137 regs.edx = mp_address ( opaque );
138 setup_sipi ( SIPI_VECTOR, virt_to_phys ( mp_jump ), &regs );
139
140 /* Get local APIC base address and mode */
141 base = rdmsr ( MSR_APIC_BASE );
142 x2apic = ( base & MSR_APIC_BASE_X2APIC );
143 DBGC ( MSR_APIC_BASE, "BIOSMP local %sAPIC base %#llx\n",
144 ( x2apic ? "x2" : "" ), ( ( unsigned long long ) base ) );
145
146 /* Map local APIC */
147 apic = ioremap ( ( base & MSR_APIC_BASE_MASK ), PAGE_SIZE );
148 if ( ! apic )
149 goto err_ioremap;
150
151 /* Assert INIT IPI */
154 bios_mp_ipi ( apic, x2apic, ipi );
155
156 /* Clear INIT IPI */
158 bios_mp_ipi ( apic, x2apic, ipi );
159
160 /* Send SIPI */
162 bios_mp_ipi ( apic, x2apic, ipi );
163
164 iounmap ( apic );
165 err_ioremap:
166 /* No way to handle errors: caller must check that
167 * multiprocessor function executed as expected.
168 */
169 return;
170}
171
#define __asmcall
Declare a function with standard calling conventions.
Definition compiler.h:15
__asm__ __volatile__("call *%9" :"=a"(result), "=c"(discard_ecx), "=d"(discard_edx) :"d"(0), "a"(code), "b"(0), "c"(in_phys), "D"(0), "S"(out_phys), "m"(hypercall))
unsigned int uint32_t
Definition stdint.h:12
unsigned long long uint64_t
Definition stdint.h:13
#define MSR_APIC_BASE
Local APIC base address MSR.
Definition bios_mp.c:40
#define APIC_ICR_LEVEL
Interrupt command register: level mode.
Definition bios_mp.c:58
#define APIC_ICR_ALL_NOT_SELF
Interrupt command register: send to all excluding self.
Definition bios_mp.c:55
#define IPI_WAIT_MS
Time to wait for an IPI to complete.
Definition bios_mp.c:70
#define MSR_X2APIC_ICR
Interrupt command register (x2APIC)
Definition bios_mp.c:52
void __asmcall mp_jump(mp_addr_t func, mp_addr_t opaque)
Protected-mode startup IPI handler.
static void bios_mp_start_all(mp_func_t func, void *opaque)
Start a multiprocessor function on all application processors.
Definition bios_mp.c:128
static void bios_mp_ipi(void *apic, int x2apic, uint32_t icr)
Send an interprocessor interrupt.
Definition bios_mp.c:108
static void bios_mp_exec_boot(mp_func_t func, void *opaque)
Execute a multiprocessor function on the boot processor.
Definition bios_mp.c:89
#define SIPI_VECTOR
Startup IPI vector.
Definition bios_mp.c:78
#define APIC_ICR_INIT
Interrupt command register: INIT.
Definition bios_mp.c:64
#define MSR_APIC_BASE_X2APIC
Local APIC is in x2APIC mode.
Definition bios_mp.c:43
#define APIC_ICR_LEVEL_ASSERT
Interrupt command register: level asserted.
Definition bios_mp.c:61
#define MSR_APIC_BASE_MASK
Local APIC base address mask.
Definition bios_mp.c:46
#define APIC_ICR_SIPI(vector)
Interrupt command register: SIPI.
Definition bios_mp.c:67
#define APIC_ICR
Interrupt command register.
Definition bios_mp.c:49
static mp_address(void *address)
Calculate address as seen by a multiprocessor function.
Definition bios_mp.h:27
#define DBGC(...)
Definition compiler.h:505
#define FILE_LICENCE(_licence)
Declare a particular licence as applying to a file.
Definition compiler.h:896
#define PAGE_SIZE
Page size.
Definition io.h:28
void * ioremap(unsigned long bus_addr, size_t len)
Map bus address as an I/O address.
void iounmap(volatile const void *io_addr)
Unmap I/O address.
Multiprocessor functions.
void mp_exec_boot(mp_func_t func, void *opaque)
Execute a multiprocessor function on the boot processor.
#define PROVIDE_MPAPI(_subsys, _api_func, _func)
Provide a multiprocessor API implementation.
Definition mp.h:91
void mp_start_all(mp_func_t func, void *opaque)
Start a multiprocessor function on all application processors.
void mp_func_t(mp_addr_t opaque, unsigned int cpuid)
A multiprocessor function.
Definition mp.h:51
#define PROVIDE_MPAPI_INLINE(_subsys, _api_func)
Provide a static inline multiprocessor API implementation.
Definition mp.h:100
unsigned long mp_addr_t
An address within the address space for a multiprocessor function.
Definition mp.h:24
void __asmcall mp_call(mp_addr_t func, mp_addr_t opaque)
Call a multiprocessor function from C code on the current CPU.
iPXE timers
Access to external ("user") memory.
#define PHYS_CODE(asm_code_str)
Definition librm.h:167
uint32_t base
Base.
Definition librm.h:3
void setup_sipi(unsigned int vector, uint32_t handler, struct i386_regs *regs)
Set up startup IPI handler.
Definition librm_mgmt.c:417
Model-specific registers.
__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")
i386 registers.
struct i386_regs regs
Definition registers.h:1
A 32-bit general register dump.
Definition registers.h:62
void mdelay(unsigned long msecs)
Delay for a fixed number of milliseconds.
Definition timer.c:79
#define writel
Definition w89c840.c:160