Symmetric Multiprocessing: Difference between revisions

[unchecked revision][unchecked revision]
Content deleted Content added
m added code example
Line 90:
===Sending IPIs===
IPIs are sent through the BSP's LAPIC. Find the LAPIC base address from the MP tables or ACPI tables, then you can write 32-bit words to base + 0x300 and base + 0x310 to send IPIs. For a init IPI or startup IPI, you must first write the target LAPIC ID into bits 24-27 of base + 0x310. Then, for an init IPI, write 0x00004500 to base + 0x300. For a SIPI, write 0x00004600, ored with the page number at which you want to AP to start executing, to base + 0x300. For more information, see http://wiki.osdev.org/APIC#Local_APIC_registers.
 
==Example Code==
When you start testing your SMP code on real machines, you'll realize that they do not keep the standard. You must not do Broadcast INIT IPIs nor Broadcast SIPIs. The following example has a decant amount of error checking to be used on real hardware. It needs three variables
* ''numcores'' the number of valid cores
* ''lapic_ids'' an array of Local APIC IDs, numcores element
* ''lapic_ptr'' the pointer to the Local APIC registers
You can get these from the PCMP table above, or see the example code on the [[MADT]] page.
 
=== BSP Initialization Code ===
<source lang="c">
// get the BSP's Local APIC ID
uint8_t bspid;
__asm__ __volatile__ ("mov $1, %%eax; cpuid; shrl $24, %%ebx;": "=b"(bspid) : : );
 
// copy the AP trampoline code to a fixed address in low conventional memory (to address 0x0800:0x0000)
memcpy((void*)0x8000, &ap_trampoline, 4096);
 
// for each Local APIC ID we do...
for(i = 0; i < 256; i++) {
// do not start BSP, that's already running this code
if(lapic_ids[i] == bspid) continue;
// send INIT IPI
*((volatile uint32_t*)(lapic_ptr + 0x280)) = 0; // clear APIC errors
*((volatile uint32_t*)(lapic_ptr + 0x310)) = (*((volatile uint32_t*)(lapic_ptr + 0x310)) & 0x00ffffff) | (i << 24); // select AP
*((volatile uint32_t*)(lapic_ptr + 0x300)) = (*((volatile uint32_t*)(lapic_ptr + 0x300)) & 0xfff00000) | 0x00C500; // trigger INIT IPI
do { __asm__ __volatile__ ("pause" : : : "memory"); }while(*((volatile uint32_t*)(lapic_ptr + 0x300)) & (1 << 12)); // wait for delivery
*((volatile uint32_t*)(lapic_ptr + 0x310)) = (*((volatile uint32_t*)(lapic_ptr + 0x310)) & 0x00ffffff) | (i << 24); // select AP
*((volatile uint32_t*)(lapic_ptr + 0x300)) = (*((volatile uint32_t*)(lapic_ptr + 0x300)) & 0xfff00000) | 0x008500; // deassert
do { __asm__ __volatile__ ("pause" : : : "memory"); }while(*((volatile uint32_t*)(lapic_ptr + 0x300)) & (1 << 12)); // wait for delivery
mdelay(10); // wait 10 msec
// send STARTUP IPI (twice)
for(j = 0; j < 2; j++) {
*((volatile uint32_t*)(lapic_ptr + 0x280)) = 0; // clear APIC errors
*((volatile uint32_t*)(lapic_ptr + 0x310)) = (*((volatile uint32_t*)(lapic_ptr + 0x310)) & 0x00ffffff) | (i << 24); // select AP
*((volatile uint32_t*)(lapic_ptr + 0x300)) = (*((volatile uint32_t*)(lapic_ptr + 0x300)) & 0xfff0f800) | 0x000608; // trigger STARTUP IPI for 0800:0000
udelay(200); // wait 200 usec
do { __asm__ __volatile__ ("pause" : : : "memory"); }while(*((volatile uint32_t*)(lapic_ptr + 0x300)) & (1 << 12)); // wait for delivery
}
}
</source>
 
=== AP Initialization Code ===
As the application processors start up in real mode, a little Assembly is needed to enter protected mode. Modify this example to your kernel's needs.
<source lang="asm">
; this code will be relocated to 0x8000, sets up environment for calling a C function
.code16
ap_trampoline:
cli
cld
ljmp $0, $0x8040
.align 16
_L8010_GDT_table:
.long 0, 0
.long 0x0000FFFF, 0x00CF9A00 ; flat code
.long 0x0000FFFF, 0x008F9200 ; flat data
.long 0x00000068, 0x00CF8900 ; tss
_L8030_GDT_value:
.word _L8030_GDT_value - _L8010_GDT_table - 1
.long 0x8010
.long 0, 0
.align 64
_L8040:
xorw %ax, %ax
movw %ax, %ds
lgdtl 0x8030
movl %cr0, %eax
orl $1, %eax
movl %eax, %cr0
ljmp $8, $0x8060
.align 32
.code32
_L8060:
movw $16, %ax
movw %ax, %ds
movw %ax, %ss
; get our Local APIC ID
mov $1, %eax
cpuid
shrl $24, %ebx
movl %ebx, %edi
; set up 32k stack, one for each core. It is important that all core must have its own stack
shll $15, %ebx
movl stack_top, %rsp
subl %ebx, %rsp
pushl %edi
; jump into C code (should never return)
ljmp $8, $ap_startup
</source>
<source lang="C">
// this C code can be anywhere you want it, no relocation needed
void ap_startup(int apicid) {
// do what you want to do on the AP
while(1);
}
</source>
 
==See Also==