// Copyright (c) 2006-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of the License "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// e32\nkernsmp\x86\nccpu.cpp
//
//
#include <x86.h>
#include <apic.h>
const TLinAddr KWarmResetTrampolineAddr = 0x467;
TLinAddr ApTrampolinePage = 0; // overridden in multiple memory model with linear address
extern "C" void nanowait(TUint32 aNanoseconds);
void cmos_write(TUint32 val, TUint32 addr);
void SetupApInitInfo(volatile SApInitInfo&);
void _ApMain();
TInt WakeAP(TInt aAPICID)
{
__KTRACE_OPT(KBOOT,DEBUGPRINT("WakeAP %d", aAPICID));
read_apic_reg(SIVR);
write_apic_reg(ESR, 0);
read_apic_reg(ESR);
__KTRACE_OPT(KBOOT,DEBUGPRINT("Asserting INIT"));
//Turn INIT on target chip
write_apic_reg(ICRH, aAPICID<<24);
// Send IPI
write_apic_reg(ICRL, 0xC500);
__KTRACE_OPT(KBOOT,DEBUGPRINT("Waiting for send to finish..."));
TInt timeout = 0;
TUint32 send_status;
TUint32 accept_status;
do {
__KTRACE_OPT(KBOOT,DEBUGPRINT("+"));
nanowait(100000);
send_status = read_apic_reg(ICRL) & 0x1000;
} while (send_status && (++timeout < 1000));
nanowait(10000000);
__KTRACE_OPT(KBOOT,DEBUGPRINT("Deasserting INIT"));
// Target chip
write_apic_reg(ICRH, aAPICID<<24);
// Send IPI
write_apic_reg(ICRL, 0x8500);
__KTRACE_OPT(KBOOT,DEBUGPRINT("Waiting for send to finish..."));
timeout = 0;
do {
__KTRACE_OPT(KBOOT,DEBUGPRINT("+"));
nanowait(100000);
send_status = read_apic_reg(ICRL) & 0x1000;
} while (send_status && (++timeout < 1000));
/*
* Should we send STARTUP IPIs ?
*
* Determine this based on the APIC version.
* If we don't have an integrated APIC, don't send the STARTUP IPIs.
*/
// if (APIC_INTEGRATED(apic_version[phys_apicid]))
TInt num_starts = 2;
// else
// num_starts = 0;
// Run STARTUP IPI loop.
// maxlvt = get_maxlvt();
TInt j;
for (j = 1; j <= num_starts; j++)
{
__KTRACE_OPT(KBOOT,DEBUGPRINT("Sending STARTUP %d",j));
read_apic_reg(SIVR);
write_apic_reg(ESR, 0);
read_apic_reg(ESR);
// target chip
write_apic_reg(ICRH, aAPICID<<24);
// send startup IPI
write_apic_reg(ICRL, (0x600 | (KApBootPage>>12)));
// give other CPU time to accept it
nanowait(300000);
__KTRACE_OPT(KBOOT,DEBUGPRINT("Waiting for send to finish..."));
timeout = 0;
do {
__KTRACE_OPT(KBOOT,DEBUGPRINT("+"));
nanowait(100000);
send_status = read_apic_reg(ICRL) & 0x1000;
} while (send_status && (++timeout < 1000));
// give other CPU time to accept it
nanowait(300000);
/*
* Due to the Pentium erratum 3AP.
*/
// if (maxlvt > 3) {
// read_apic_reg(APIC_SPIV);
// write_apic_reg(APIC_ESR, 0);
// }
accept_status = (read_apic_reg(ESR) & 0xEF);
if (send_status || accept_status)
break;
}
__KTRACE_OPT(KBOOT,DEBUGPRINT("After startup"));
if (send_status)
__KTRACE_OPT(KBOOT,DEBUGPRINT("APIC never delivered???"));
if (accept_status)
__KTRACE_OPT(KBOOT,DEBUGPRINT("APIC delivery error %x", accept_status));
return (send_status | accept_status);
}
TInt NKern::BootAP(volatile SAPBootInfo* aInfo)
{
__KTRACE_OPT(KBOOT,DEBUGPRINT("NKern::BootAP %08x %08x+%x", aInfo->iCpu, aInfo->iInitStackBase, aInfo->iInitStackSize));
cmos_write(0xa, 0xf);
TUint8* t = (TUint8*)(KWarmResetTrampolineAddr + ApTrampolinePage);
TUint cs = KApBootPage>>4;
*t++ = 0; // IP low
*t++ = 0; // IP high
*t++ = (TUint8)cs;
*t++ = cs>>8;
volatile SApInitInfo& a = *(volatile SApInitInfo*)KApBootPage;
TCpuPages& cp=X86::CpuPage();
SetupApInitInfo(a);
memcpy((TAny*)a.iTempGdt, cp.iGdt, sizeof(cp.iGdt));
a.iTempGdtr = TUint64(KApBootPage + _FOFF(SApInitInfo,iTempGdt))<<16 | TUint64(KSmpGdtSize*sizeof(SX86Des)-1);
a.iRgs.iCs = RING0_CS;
a.iRgs.iEip = (TLinAddr)&_ApMain;
a.iBootFlag = 0;
a.iBootFlag2 = 0;
a.iLinAddr = (TLinAddr)&a;
a.iStackBase = aInfo->iInitStackBase;
a.iStackSize = aInfo->iInitStackSize;
a.iRgs.iEsp = a.iStackBase + a.iStackSize;
a.iExtra = (TAny*)aInfo;
TInt r = WakeAP(aInfo->iCpu);
if (r!=0)
return KErrGeneral;
TInt timeout = 500;
while (--timeout)
{
nanowait(1000000);
if (a.iBootFlag == KBootFlagMagic-1)
break;
__chill();
}
__KTRACE_OPT(KBOOT, DEBUGPRINT("iBootFlag=%08x",a.iBootFlag));
if (timeout==0)
return KErrTimedOut;
__e32_atomic_add_ord32(&a.iBootFlag, TUint32(-1));
NKern::DisableAllInterrupts();
while (a.iBootFlag2==0)
{}
__e32_io_completion_barrier();
a.iBootFlag2 = 2;
__e32_io_completion_barrier();
a.iBPTimestamp = X86::Timestamp();
__e32_io_completion_barrier();
while (a.iBootFlag2==2)
{}
__e32_io_completion_barrier();
NKern::EnableAllInterrupts();
return KErrNone;
}
void InitAPTimestamp(SNThreadCreateInfo&)
{
volatile SApInitInfo& a = *(volatile SApInitInfo*)KApBootPage;
NKern::DisableAllInterrupts();
a.iBootFlag2 = 1;
__e32_io_completion_barrier();
while (a.iBootFlag2==1)
{}
__e32_io_completion_barrier();
a.iAPTimestamp = X86::Timestamp();
__e32_io_completion_barrier();
TUint64 bpt = a.iBPTimestamp;
TUint64 apt = a.iAPTimestamp;
TUint64 delta = bpt - apt;
TSubScheduler& ss = SubScheduler();
ss.iLastTimestamp64 += delta;
*(TUint64*)&ss.i_TimestampOffset = delta;
__KTRACE_OPT(KBOOT,DEBUGPRINT("APT=0x%lx BPT=0x%lx Delta=0x%lx", apt, bpt, delta));
__e32_io_completion_barrier();
a.iBootFlag2 = 3;
NKern::EnableAllInterrupts();
}