多核初始化过程

时间:2022-05-07 19:36:48
平台介绍:

loongson3A-4核cpu(mips架构),vxWorks6.8

多核cpu启动时,刚开始时是cpu0在运行,后面才会初始化其他三个核,这里就说一些其他三个核的初始化过程。

函数调用关系:

usrRoot-->usrSmpInit-->usrEnableCpu-->kernelCpuEnableInternal-->vxCpuStateInit

kernelCpuEnableInternal-->sysCpuEnable-->sysCpuStart-->godson3_cpu_start-->sysCpuInit-->kernelCpuEntry-->idleTask

在这些函数调用完了之后呢,其他三个核就进入了一个可使用状态。

现在呢咱们一步一步分析一下这个初始化的过程:

void usrSmpInit (void)
{
/*这里挂接cpu调度的中断函数,并使能。主要用于核间通信*/
if ((vxIpiConnect (IPI_INTR_ID_SCHED,(IPI_HANDLER_FUNC) windRescheduleNotify,NULL) == ERROR) ||vxIpiEnable (IPI_INTR_ID_SCHED) == ERROR))
{
return;
}
/*使能cpu*/
usrEnableCpu();
}

LOCAL void usrEnableCpu (void)
{
UINT cpu;
UINT numCpus = vxCpuConfiguredGet (); /*获取配置的cpu的个数*/
cpuset_t enabledCpus;
ULONG timeoutTicks, timeoutStart, currentTickCount;

CPUSET_ZERO (enabledCpus);
/*cpu0已经run起来了,先记录cpu0*/
CPUSET_SET (enabledCpus, 0);
/*设置超时时间*/
vxCpuEnableTimeout = VX_ENABLE_CPU_TIMEOUT;
timeoutTicks = vxCpuEnableTimeout * sysClkRateGet ();
/*对多核cpu进行使能*/
for (cpu = 1; cpu < numCpus; cpu++)
{
if (kernelCpuEnableInternal (cpu) == ERROR)
{
}
else
CPUSET_SET (enabledCpus, cpu);
}

timeoutStart = vxAbsTicksGet ();
/*如果没有使能就等待*/
do
{
currentTickCount = vxAbsTicksGet ();

if ((currentTickCount - timeoutStart) > timeoutTicks)
{
for ( cpu = 1 ; cpu < numCpus ; cpu ++)
{
if (!CPUSET_ISSET (_WRS_CPU_ENABLED (),cpu))
}
break;
}
} while (_WRS_CPU_ENABLED () != enabledCpus);

/*如果定义了cpu使能的钩子函数呢,就在这里运行了,注意了这里呢就是给每个cpu挂接它自己的中断的地址*/
for ( cpu = 1 ; cpu < numCpus ; cpu ++)
{
if (CPUSET_ISSET (_WRS_CPU_ENABLED(), cpu))
{
usrSmpCpuEnableHook (cpu);
}
}
}
STATUS kernelCpuEnableInternal     (    unsigned int cpuToEnable/* logical index of CPU to enable */    )    {    char *pStackBase;    WIND_CPU_STATE cpuState;/*设置idle task的exception stack*/    pStackBase = (_WRS_KERNEL_CPU_GLOBAL_GET (cpuToEnable, idleTaskId))->pExcStackBase;/*初始化cpu的状态*/    vxCpuStateInit (cpuToEnable, &cpuState, pStackBase, kernelCpuEntry);    /*使能cpu*/    return sysCpuEnable (CPU_LOGICAL_TO_PHYS(cpuToEnable), &cpuState);    }

记录cpu状态的结构体:
typedef struct wind_cpu_state
{
REG_SETregs;
} WIND_CPU_STATE;

typedef struct/* REG_SET - MIPS architecture register set */
{
ULONG sr;/* process status register */
INSTR *pc;/* program counter - really epc! */
_RType lo;/* divide lo register */
_RType hi;/* divide hi register */
_RType gpreg[32];/* data registers */

ULONG cause; /* cause reg for branch bit in getNpc() */
ULONG fpcsr; /* fp cause reg for getNpc() */

ULONG intCtrl; /* extended interrupt control */
ULONG _ULextra1; /* extra */
_RType tlbhi;/* current address space storage */
_RType _RTextra2; /* extra */
_RType _RTextra3; /* extra */
_RType _RTextra4; /* extra */
_RType _RTextra5; /* extra */
_RType _RTextra6; /* extra */
_RType _RTextra7; /* extra */
_RType _RTextra8; /* extra */
_RType _RTextra9; /* extra */
_RType _RTextra10; /* extra */
} REG_SET;

typedef unsigned long _RType;/* registers are 32 bits */

STATUS vxCpuStateInit
(
unsigned int logicalCpuIndex, /* logical CPU index */
WIND_CPU_STATE * pCpuState, /* pointer CPU state structure */
char * pStackBase, /* stack base */
void (* entry) (void) /* kernel entry point */
)
{
/*清空要记录cpu状体的结构体*/
bzero ((char *)pCpuState, sizeof(WIND_CPU_STATE));
/*这里使用了一个定义好的默认的状态,但是需要关闭中断*/
pCpuState->regs.sr = taskSrDefault & ~SR_IE;
/*存储_gp值,记录栈地址,以及入口函数*/
vxSdaSet (&pCpuState->regs);
pCpuState->regs.spReg = (_RType)pStackBase;
pCpuState->regs.pc = (INSTR *)entry;
}
STATUS sysCpuEnable(unsigned int cpuNum, WIND_CPU_STATE *cpuState)    {/*这里定义了一个数组sysCpuInitTable,记录每一个cpu的初始化e完成后的入口函数的地址*/    sysCpuInitTable[cpuNum] = (FUNCPTR) cpuState->regs.pc;/*调用sysCpuStart,使用初始化好的cpuState取初始化cpu*/    return sysCpuStart(cpuNum, cpuState);    }/*这值是一个对godson3_cpu_start函数的一个封装*/STATUS sysCpuStart(int cpu, WIND_CPU_STATE *cpuState)    {godson3_cpu_start(cpu,  (void (*)(void))SYS_CPU_INIT,  (long)cpuState->regs.spReg,  (long)cpuState->regs.gpReg,  (long)cpuState->regs.a1Reg);return OK;    }


/*godson3_cpu_start是一段汇编,记录一些信息,为cpu初始化做准备*/
#define MAILBOX_BUF1 0x900000003ff01120
#define MAILBOX_BUF2 0x900000003ff01220
#define MAILBOX_BUF3 0x900000003ff01320
.globl godson3_cpu_start
.ent godson3_cpu_start
godson3_cpu_start:
/*对1号cpu进行的信息记录*/
lit0,0x1 /*t0=0x01*/
bne a0,t0,1f/*如果a0!=t0,就向前调转到标号1处*/
nop/*等待*/
dlit0,MAILBOX_BUF1/*加载MAILBOX_BUF1的地址到t0中*/
b2f/*调转到标号2处*/
1:
lit0,0x2
bnea0,t0,1f
nop
dlit0,MAILBOX_BUF2
b2f
1:
lit0,0x3
bnea0,t0,1f
nop
dlit0,MAILBOX_BUF3
b2f
2:
/*这里是把信息存储到MAILBOX_BUF中,1号cpu对应MAILBOX_BUF1,2号cpu对应MAILBOX_BUF2,以此类推*/
sd a1,0(t0) /*把sysCpuInit地址储存到MAILBOX_BUF偏移地址0处*/
sda2,8(t0) /*sp寄存器值存储到MAILBOX_BUF偏移8处*/
sda3,0x10(t0) /*gp寄存器值存储到MAILBOX_BUF偏移0x10处*/
sda4,0x18(t0) /*al寄存器值存储到MAILBOX_BUF偏移0x18处*/
1:
jrra
.end godson3_cpu_start


之后cpu就开始运行函数sysCpuInit:
.globl sysCpuInit
.ent sysCpuInit
sysCpuInit:
/*主要是把cpu的寄存器设置到一个可控状态*/
mtc0zero, C0_CAUSE /*cause寄存器清零*/

liv0, 1/*设置内部计数寄存器值为最大,延长时钟中断到来*/
mtc0v0, C0_COUNT
mtc0zero, C0_COMPARE

/*清除BEV位,因为cpu0已经设置好了中断以及异常的基地址*/
mfc0t0, C0_SR
HAZARD_CP_READ

andt0, ~SR_BEV
mtc0t0, C0_SR
HAZARD_CP_WRITE
/*设置tlb,下面会给出代码详细讲述*/
jalmipsTlbClear

#if defined (INCLUDE_MAPPED_KERNEL)

/*如果使能了mmu,那么设置sp位K0BASE的地址*/
andsp, ADDRESS_SPACE_MASK
orsp, K0BASE

/*读取EBase寄存器值,获取cpuNum的值*/
mfc0v0, $15, 1
sync
andiv0,0x3 /* !! changed by yinwx, 20100224 !! */
sllv0,15 /*by whh, from 13 to 15*/

/*为函数mmuMipsInitialMemoryMapSet设置参数*/
/*EXC_PAGE_PHYS_ADRS=0x8000,*/
lia0, EXC_PAGE_PHYS_ADRS
adda0, v0 /* adjust exception page start addr */
/*LOCAL_MEM_LOCAL_ADRS=0*/
lia1, LOCAL_MEM_LOCAL_ADRS
lia2, (LOCAL_MEM_LOCAL_ADRS + LOCAL_MEM_SIZE)
/*设置tlb映射,后面展示代码说明*/
jalmmuMipsInitialMemoryMapSet
beqzv0, 1f/*如果返回值为0,则运行mmuMipsExcpageInit*/
0:
/* memory mapping error #1 */
b0b
1:
jalmmuMipsExcpageInit/* set up values in -1 page */
beqzv0, 3f/*运行成功呢,就跳到标号处执行*/
2:
/* memory mapping error */
b2b

/* tlb setup in place, switch to mapped address */
3:
lat0, 0f
jt0
0:
#endif /* INCLUDE_MAPPED_KERNEL */
/*修改taskSrDefault并关闭中断使能,然后再写入到SR寄存器中*/
lwa0, taskSrDefault
jaltaskSRInit
lwa0, taskSrDefault
anda0, ~SR_IE
jalintSRSet
/*获取cpuNum号*/
mfc0v0, $15, 1
sync
andiv0,0x3 /* !! changed by yinwx, 20100224 !! */
sllv0,2
/*加载每个cpu初始化完后的入口函数地址,并调转到那个函数执行,这里的入口函数为kernelCpuEntry*/
lat0, sysCpuInitTable
addt0, t0, v0
lwt0, 0(t0)/* get table entry for this core */
beqzt0, 1f/* branch if no entry */
jalt0/* no return */

lat0, sysCpuLoopCount
addt0, v0
lit1, 1
2:
lwt2, 0(t1)
addt2, t1
swt2, 0(t1)
b2b

1:
/* "no kernel" */
b1b
.end sysCpuInit

_WRS_FUNC_NORETURN void kernelCpuEntry (void)
{
/*获取cpuIndex号*/
unsigned int currentCpu = _WRS_CPU_INDEX_GET ();
/*设置栈中的值*/
windIntStackSet (_WRS_KERNEL_CPU_GLOBAL_GET (currentCpu, vxIntStackBase));
/*把这个已经使能的cpu记录起来*/
CPUSET_ATOMICSET (_WRS_CPU_ENABLED (), currentCpu);
/*为这个核加载idle任务*/
windLoadContext ();
}

OK,到这里这个核就初始化完毕了。
看一下sysCpuInit函数中调用的几个函数:
/*除了tlb表项中的TLBHI,其他设置为0*/
/*这里把a3作为要写入的tlb号,a2为总数,循环执行,直到所有表项设置完毕*/
.entmipsTlbClear
.globlmipsTlbClear
FUNC_LABEL(mipsTlbClear)
movea3, ra/* save return address */
balmipsTlbSizeGet/*获取可设置的额tlb表项的数量*/
movera, a3/* restore return address */

lia2,TLB_4K_PAGE_SIZE_MASK
mtc0a2,C0_PAGEMASK/* setup page mask */
HAZARD_CP_WRITE

/* Setup loop vars */

lia3,0/* first TLB index */

movea2, v0/* use calculated TLB count */
lit2, K0BASE/* use KSEG 0 addresses for TLB */
mtc0zero, C0_TLBLO0/* clear entry lo0, set invalid */
mtc0zero, C0_TLBLO1/* clear entry lo1, set invalid */

1:

sllt1, a3, MMU_R4K_PAGE_SHIFT+1/* Valid bit set to zero */
/* +1 since VPN2 field maps 2 pages */
ort1, t2, t1/* generate unique tlbhi value */

/* store the entry */

MTC0t1, C0_TLBHI/* clear entry high */
mtc0a3, C0_INX/* set the index */
HAZARD_CP_WRITE

addiua3, 1
tlbwi/* write the TLB */
HAZARD_TLB

bnea3, a2, 1b

jrra
FUNC_END(mipsTlbClear)
.endmipsTlbClear

mmu的几个调用函数,没有看明白作用,回头清楚了在写。