Skip to content

LoongArch: Fixed a MP wake up bug and enabled IPI vectors. #10984

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,12 @@
#ifndef EXCEPTION_COMMON_H_
#define EXCEPTION_COMMON_H_

#include <Base.h>

#define MAX_DEBUG_MESSAGE_LENGTH 0x100
#define SMP_BOOT_CPU BIT0
#define SMP_RESCHEDULE BIT1
#define SMP_CALL_FUNCTION BIT2

extern INTN mExceptionKnownNameNum;
extern INTN mInterruptKnownNameNum;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -226,6 +226,11 @@ DumpImageAndCpuContent (
/**
IPI Interrupt Handler.

Generally, the IPI interrupt uses three vector:
SMP_BOOT_CPU BSP boots AP. The BSP may reside in OS or other non-UEFI environment.
SMP_RESCHEDULE BSP calls AP via UEFI MpInitLib.
SMP_CALL_FUNCTION BSP calls AP to jump specified pointer, which allows with one parameter.

@param InterruptType The type of interrupt that occurred
@param SystemContext A pointer to the system context when the interrupt occurred
**/
Expand All @@ -236,43 +241,74 @@ IpiInterruptHandler (
IN EFI_SYSTEM_CONTEXT SystemContext
)
{
UINTN ResumeVector;
UINTN Parameter;
UINTN ResumeVector = 0;
UINTN Parameter = 0;
UINTN IpiStatus = 0;

IpiStatus = IoCsrRead32 (LOONGARCH_IOCSR_IPI_STATUS);

//
// Clear interrupt.
//
IoCsrWrite32 (LOONGARCH_IOCSR_IPI_CLEAR, IoCsrRead32 (LOONGARCH_IOCSR_IPI_STATUS));
IoCsrWrite32 (LOONGARCH_IOCSR_IPI_CLEAR, IpiStatus);

//
// Get the resume vector and parameter if populated.
//
ResumeVector = IoCsrRead64 (LOONGARCH_IOCSR_MBUF0);
Parameter = IoCsrRead64 (LOONGARCH_IOCSR_MBUF3);
if ((IpiStatus & SMP_RESCHEDULE) != 0) {
MemoryFence ();
return;
} else {
if (((IpiStatus & SMP_BOOT_CPU) != 0) || ((IpiStatus & SMP_CALL_FUNCTION) != 0)) {
//
// Confirm that the mail box message has arrived.
//
do {
ResumeVector = IoCsrRead32 (LOONGARCH_IOCSR_MBUF0);
} while (!ResumeVector);

//
// Clean up current processor mailbox 0 and mailbox 3.
//
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
//
// Get the resume vector if populated.
//
ResumeVector = IoCsrRead64 (LOONGARCH_IOCSR_MBUF0);

if ((IpiStatus & SMP_BOOT_CPU) != 0) {
SystemContext.SystemContextLoongArch64->PRMD &= ~((UINT64)BIT2); // Clean PIE
} else if ((IpiStatus & SMP_CALL_FUNCTION) != 0) {
//
// Confirm that the mail box message has arrived.
//
do {
Parameter = IoCsrRead32 (LOONGARCH_IOCSR_MBUF3);
} while (!Parameter);

//
// Get the parameter if populated.
//
Parameter = IoCsrRead64 (LOONGARCH_IOCSR_MBUF3);

//
// Set $a0 as APIC ID and $a1 as parameter value.
//
SystemContext.SystemContextLoongArch64->R4 = CsrRead (LOONGARCH_CSR_CPUID);
SystemContext.SystemContextLoongArch64->R5 = Parameter;
}
} else {
InternalPrintMessage (
"Core %d: Should never be here, IPI Status = %d.\n",
CsrRead (LOONGARCH_CSR_CPUID),
IpiStatus
);
DefaultExceptionHandler (EXCEPT_LOONGARCH_INT, SystemContext);
}

//
// If mailbox 0 is non-NULL, it means that the BSP or other cores called the IPI to wake
// up the current core and let it use the resume vector stored in mailbox 0.
//
// If both the resume vector and parameter are non-NULL, it means that the IPI was
// called in the BIOS.
//
// The situation where the resume vector is non-NULL and the parameter is NULL has been
// processed after the exception entry is pushed onto the stack.
//
if ((ResumeVector != 0) && (Parameter != 0)) {
SystemContext.SystemContextLoongArch64->ERA = ResumeVector;
//
// Set $a0 as APIC ID and $a1 as parameter value.
// Clean up current processor mailbox 0 and mailbox 3.
//
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);

//
SystemContext.SystemContextLoongArch64->R4 = CsrRead (LOONGARCH_CSR_CPUID);
SystemContext.SystemContextLoongArch64->R5 = Parameter;
// Set the ERA to the resume vector sent by caller.
//
SystemContext.SystemContextLoongArch64->ERA = ResumeVector;
}

MemoryFence ();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -250,7 +250,7 @@ ASM_PFX(ExceptionEntryStart):

csrrd $t0, LOONGARCH_CSR_EUEN
andi $t0, $t0, CSR_EUEN_FPEN
beqz $t0, PushRegDone
beqz $t0, EntryConmmonHanlder

fst.d $fa0, $sp, 0 * RSIZE
fst.d $fa1, $sp, 1 * RSIZE
Expand Down Expand Up @@ -312,52 +312,6 @@ ASM_PFX(ExceptionEntryStart):
// Push exception context down
//

PushRegDone:
//
// Process IPI only when mailbox3 is NULL and mailbox0 is no-NULL.
//
li.d $t0, LOONGARCH_IOCSR_MBUF0
iocsrrd.d $a0, $t0
beqz $a0, EntryConmmonHanlder

li.d $t0, LOONGARCH_IOCSR_MBUF3
iocsrrd.d $t1, $t0
bnez $t1, EntryConmmonHanlder

csrrd $t0, LOONGARCH_CSR_ESTAT
srli.d $t0, $t0, 12
andi $t0, $t0, 0x1
beqz $t0, EntryConmmonHanlder

//
// Clean up current processor mailbox 0 and mailbox 3.
//
li.d $t0, LOONGARCH_IOCSR_MBUF0
iocsrwr.d $zero, $t0
li.d $t0, LOONGARCH_IOCSR_MBUF3
iocsrwr.d $zero, $t0

//
// Clear IPI interrupt.
//
li.d $t0, LOONGARCH_IOCSR_IPI_STATUS
iocsrrd.w $t1, $t0
li.d $t0, LOONGARCH_IOCSR_IPI_CLEAR
iocsrwr.w $t1, $t0

//
// Only kernel stage BSP calls IPI without parameters. Clean up the PIE and make sure
// global interrupts are turned off for the current processor when jumping to the kernel.
//
csrwr $a0, LOONGARCH_CSR_ERA // Update ERA
li.w $t0, BIT2 // IE
csrxchg $zero, $t0, LOONGARCH_CSR_PRMD // Clean PIE

//
// Return this exception and jump to kernel using ERA.
//
ertn

EntryConmmonHanlder:
addi.d $sp, $sp, -(GP_REG_CONTEXT_SIZE + CSR_REG_CONTEXT_SIZE)
move $a0, $sp
Expand Down
69 changes: 34 additions & 35 deletions UefiCpuPkg/Library/MpInitLib/LoongArch64/MpLib.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@

#define INVALID_APIC_ID 0xFFFFFFFF

EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
EFI_GUID mCpuInitMpLibHobGuid = CPU_INIT_MP_LIB_HOB_GUID;
PROCESSOR_RESOURCE_DATA *mProcessorResourceData = NULL;

/**
Get the Application Processors state.
Expand Down Expand Up @@ -223,12 +224,6 @@ CollectProcessorCount (
IN CPU_MP_DATA *CpuMpData
)
{
PROCESSOR_RESOURCE_DATA *ProcessorResourceData;
CPU_INFO_IN_HOB *CpuInfoInHob;
UINTN Index;

ProcessorResourceData = NULL;

//
// Set the default loop mode for APs.
//
Expand All @@ -240,16 +235,11 @@ CollectProcessorCount (
// as the first broadcast method to wake up all APs, and all of APs will read NODE0
// Core0 Mailbox0 in an infinit loop.
//
ProcessorResourceData = GetProcessorResourceDataFromGuidedHob ();
mProcessorResourceData = GetProcessorResourceDataFromGuidedHob ();

if (ProcessorResourceData != NULL) {
if (mProcessorResourceData != NULL) {
CpuMpData->ApLoopMode = ApInHltLoop;
CpuMpData->CpuCount = ProcessorResourceData->NumberOfProcessor;
CpuInfoInHob = (CPU_INFO_IN_HOB *)(UINTN)(CpuMpData->CpuInfoInHob);

for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
CpuInfoInHob[Index].ApicId = ProcessorResourceData->ApicId[Index];
}
CpuMpData->CpuCount = mProcessorResourceData->NumberOfProcessor;
}

//
Expand Down Expand Up @@ -380,15 +370,15 @@ ApWakeupFunction (
//
InterlockedIncrement ((UINT32 *)&CpuMpData->FinishedCount);

while (TRUE) {
//
// Clean per-core mail box registers.
//
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF1, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF2, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);
//
// Clean per-core mail box registers.
//
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF0, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF1, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF2, 0x0);
IoCsrWrite64 (LOONGARCH_IOCSR_MBUF3, 0x0);

while (TRUE) {
//
// Enable IPI interrupt and global interrupt
//
Expand Down Expand Up @@ -699,34 +689,34 @@ WakeUpAP (
DEBUG ((DEBUG_INFO, "%a: func 0x%llx, ExchangeInfo 0x%llx\n", __func__, ApWakeupFunction, (UINTN)ExchangeInfo));
if (CpuMpData->ApLoopMode == ApInHltLoop) {
for (Index = 0; Index < CpuMpData->CpuCount; Index++) {
if (Index != CpuMpData->BspNumber) {
if (mProcessorResourceData->ApicId[Index] != CpuMpData->BspNumber) {
IoCsrWrite64 (
LOONGARCH_IOCSR_MBUF_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(IOCSR_MBUF_SEND_BOX_HI (0x3) << IOCSR_MBUF_SEND_BOX_SHIFT) |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
(mProcessorResourceData->ApicId[Index] << IOCSR_MBUF_SEND_CPU_SHIFT) |
((UINTN)(ExchangeInfo) & IOCSR_MBUF_SEND_H32_MASK))
);
IoCsrWrite64 (
LOONGARCH_IOCSR_MBUF_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(IOCSR_MBUF_SEND_BOX_LO (0x3) << IOCSR_MBUF_SEND_BOX_SHIFT) |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
(mProcessorResourceData->ApicId[Index] << IOCSR_MBUF_SEND_CPU_SHIFT) |
((UINTN)ExchangeInfo) << IOCSR_MBUF_SEND_BUF_SHIFT)
);

IoCsrWrite64 (
LOONGARCH_IOCSR_MBUF_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(IOCSR_MBUF_SEND_BOX_HI (0x0) << IOCSR_MBUF_SEND_BOX_SHIFT) |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
(mProcessorResourceData->ApicId[Index] << IOCSR_MBUF_SEND_CPU_SHIFT) |
((UINTN)(ApWakeupFunction) & IOCSR_MBUF_SEND_H32_MASK))
);
IoCsrWrite64 (
LOONGARCH_IOCSR_MBUF_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(IOCSR_MBUF_SEND_BOX_LO (0x0) << IOCSR_MBUF_SEND_BOX_SHIFT) |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
(mProcessorResourceData->ApicId[Index] << IOCSR_MBUF_SEND_CPU_SHIFT) |
((UINTN)ApWakeupFunction) << IOCSR_MBUF_SEND_BUF_SHIFT)
);

Expand All @@ -736,10 +726,13 @@ WakeUpAP (
IoCsrWrite64 (
LOONGARCH_IOCSR_IPI_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
0x2 // Bit 2
(mProcessorResourceData->ApicId[Index] << IOCSR_MBUF_SEND_CPU_SHIFT) |
0x2 // SMP_CALL_FUNCTION
)
);

MemoryFence ();
MicroSecondDelay (100); // Delay 100ms after send IPI.
}
}
} else {
Expand Down Expand Up @@ -767,15 +760,18 @@ WakeUpAP (
*(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;

//
// Send IPI 4 interrupt to wake up APs.
// Send IPI 2 interrupt to wake up APs.
//
IoCsrWrite64 (
LOONGARCH_IOCSR_IPI_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(CpuInfoInHob[Index].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
0x2 // Bit 2
0x1 // SMP_RESCHEDULE
)
);

MemoryFence ();
MicroSecondDelay (100); // Delay 100ms after send IPI.
}
}

Expand All @@ -799,16 +795,19 @@ WakeUpAP (
*(UINT32 *)CpuData->StartupApSignal = WAKEUP_AP_SIGNAL;

//
// Send IPI 4 interrupt to wake up APs.
// Send IPI 2 interrupt to wake up APs.
//
IoCsrWrite64 (
LOONGARCH_IOCSR_IPI_SEND,
(IOCSR_MBUF_SEND_BLOCKING |
(CpuInfoInHob[ProcessorNumber].ApicId << IOCSR_MBUF_SEND_CPU_SHIFT) |
0x2 // Bit 2
0x1 // SMP_RESCHEDULE
)
);

MemoryFence ();
MicroSecondDelay (100); // Delay 100ms after send IPI.

//
// Wait specified AP waken up
//
Expand Down Expand Up @@ -1360,7 +1359,7 @@ MpInitLibInitialize (
//
// Set BSP basic information
//
InitializeApData (CpuMpData, 0, 0);
InitializeApData (CpuMpData, CpuMpData->BspNumber, 0);

//
// Set up APs wakeup signal buffer and initialization APs ApicId status.
Expand Down
Loading