Trivial Solutions
                        Happy Programmers Make Us Happy

The LoseThos 64-bit PC Operating System
File:/LT/OSMain/MultiProc.CPZ
This, and all LoseThos files, are public domain.  Do whatever you like.
God is watching; God is just; God is never out-done in generosity.  He talks to me!

LoseThos code compiles with the 64-bit LoseThos compiler/assembler I wrote.
Boot the LoseThos CD and you can compile it.  Not in Kansas anymore!

Click here for preliminary compiler information you need.

Project: OSMain
asm {
        ALIGN   16,OC_NOP
USE16
//See LoseThos MultiCore.

//This code gets copied to MP_VECTOR_ADDRESS.
//See MemCpy(MP_VECTOR_ADDRESS.
SYS_MP_INIT_START::
        JMP     MP2_START2
        ALIGN   4,OC_NOP
MP2_SYS_TEMP_PTR:       DU32    0,0;

MP2_START2:
        CLI

        WBINVD

        MOV     AX,MP_VECTOR_ADDRESS/16
        MOV     DS,AX

//See mp->sys_temp_ptr
#assert MP2_SYS_TEMP_PTR-SYS_MP_INIT_START==MP_SYS_TEMP_PTR
        LGDT    U32 [MP_SYS_TEMP_PTR]

        MOV     EAX,SYS_START_CR0
        MOV_CR0_EAX

        DU8     0x66,0xEA;               //JMP SYS_CS_SEL:MP_INIT_OS
        DU32    MP_INIT_OS;
        DU16    SYS_CS_SEL;

SYS_MP_INIT_END::
USE32
MP_INIT_OS:
        MOV     AX,ZERO_DS_SEL
        MOV     DS,AX
        MOV     ES,AX
        MOV     FS,AX
        MOV     GS,AX
        MOV     SS,AX

@@1:    LOCK
        BTS     U32 [SYS_MP_CNT_LOCK],0
        JC      @@1

        MOV     ESI,U32 [SYS_MP_CNT]
        LOCK
        INC     U32 [SYS_MP_CNT]
        LOCK
        BTR     U32 [SYS_MP_CNT_LOCK],0


        CMP     ESI,MP_MAX_PROCESSORS
        JAE     I32 _SYS_HLT

        IMUL2   ESI,CPU_STRUCT_SIZE
        ADD     ESI,U32 [SYS_CPU_STRUCTS]

        LEA     EAX,U32 CPU_START_STK_TOP[ESI]
        MOV     ESP,EAX
        PUSH    U32 SYS_START_RFLAGS
        POPFD
        PUSH    U32 0
        CALL    INIT_EM64T
USE64
        FNINIT

        PUSH    RSI
        PUSH    RSI
        CALL    _SET_GS_BASE
        POP     RSI
@@2:    MOV     RBX,U64 CPU_SETH_TASK[RSI]
        OR      RBX,RBX
        JZ      @@2
        MOV     U64 TASK_GS[RBX],RSI
        MOV     RAX,RBX
        CALL    SET_FS_BASE

        JMP     I32 SYS_RESTORE_CONTEXT
}

TssStruct *TssNew(I64 cpu_num)
{
  U32 *d,*d1;
  TssStruct *tss=CAlloc(sizeof(TssStruct));
  tss->io_map_offset=offset(TssStruct.io_map);
  MemSet(tss->io_map,0xFF,0x10000/8);

  tss->st0=MAlloc(INTERRUPT_STK);
  tss->rsp0=tss->st0><(U8 *)+MSize(tss->st0);
  tss->st1=MAlloc(INTERRUPT_STK);
  tss->rsp1=tss->st1><(U8 *)+MSize(tss->st1);
  tss->st2=MAlloc(INTERRUPT_STK);
  tss->rsp2=tss->st2><(U8 *)+MSize(tss->st2);

  tss->tr=CPU_TR_SELS+cpu_num*sizeof(GdtTabStruct);

  d=sys_gdttab><(U8 *)+tss->tr;
  d1=d><(U8 *)+4;
  *d =0x0000FFFF;
  *d1=0x008F8900;
  d><(U8 *)+=2;
  *d|=tss & 0x00FFFFFF;
  *d1++|=tss & 0xFF000000;
  *d1++=0;
  *d1=0;

  return tss;
}

U0 MPCmdCtrlInit(MPCmdCtrl *ctrl)
{
  MemSet(ctrl,0,sizeof(MPCmdCtrl));
  ctrl->next_waiting=ctrl->last_waiting=ctrl;
  ctrl->next_done=ctrl->last_done=&ctrl->next_done;
}

CPUStruct *CPUStructInit(U64 num,CPUStruct *c)
{
  MemSet(c,0,sizeof(CPUStruct));
  c->address=c;
  c->num=num;
  c->idle_factor=0.01;
  MPCmdCtrlInit(&c->cmd_ctrl);
  if (num)
    c->idle_task=Spawn(0,NULL,"Idle Task",Fs,NULL,IDLE_STK,0);
  else
    c->timer_irq_chain=&IntSimpleTimer;
  return c;
}

U0 MPInt(U8 num,I64 cpu_num=1)
{
  U32 *dl=LAPIC_ICR_LOW,*dh=LAPIC_ICR_HIGH;
  if (cpu_num>=mp_cnt) throw (EXCEPT_MULTICORE,0);
  PushFD;
  Cli; //multitasking safe because each core has a local apic and IRQ's are off
  while (*dl&0x1000)
    Pause;
  *dh=mp_apic_ids[cpu_num]<<24;
  *dl=0x4000+num;
  PopFD;
}

U0 IntEOI()
{
  U32 *dd=LAPIC_EOI;
  *dd=0;
}

U0 MPIntAll(U8 num)
{ //(All but self)
  U32 *dl=LAPIC_ICR_LOW;
  PushFD;
  Cli; //multitasking safe because each core has a local apic and IRQ's are off
  while (*dl&0x1000)
    Pause;
  *dl=0xC4800+num;
  PopFD;
}

U0 MPNMInt()
{
  U32 *dl=LAPIC_ICR_LOW;
  *dl=0xC4400;
}

U0 MPHalt()
{
  mp_cnt=1;
  MPNMInt; //Hlt All other processors
  BusyWait(10000);
}

U0 MPWake(U64 target_cpu_mask)
{
  I64 i;
  target_cpu_mask&=1<<mp_cnt-1;
  Btr(&target_cpu_mask,Gs->num);
  PushFD;
  Cli;
  for (i=Bsf(target_cpu_mask);target_cpu_mask;i++)
    if (Btr(&target_cpu_mask,i)) {
      if (Bt(&cpu_structs[i].cpu_flags,CPUf_SETH_SLEEP))
        cpu_structs[i].seth_task->wake_jiffy=0;
      MPInt(I_WAKE,i);
      if (Bt(&cpu_structs[i].cpu_flags,CPUf_SETH_SLEEP)) //In case went back to sleep
        cpu_structs[i].seth_task->wake_jiffy=0;
    }
  PopFD;
}

U0 MPAPICInit()
{ //Called by adam during start-up
  //and other cores during initialization
  //after MPStart().
  U32 *d;

  d=LAPIC_SVR;
  *d|=LAPICF_APIC_ENABLED;
  d=LAPIC_APIC_ID;
  mp_apic_ids[Gs->num]=*d>>24;
  d=LAPIC_LDR;
  *d=mp_apic_ids[Gs->num]<<24;
  d=LAPIC_DFR;
  *d=0xF0000000;
/*
  MemSet(LAPIC_IRR,0,0x20);
  MemSet(LAPIC_ISR,0,0x20);
  MemSet(LAPIC_TMR,0,0x20);
*/

  SetRAX(Gs->tss->tr);
  asm {
        LTR     AX
  }
  if (Gs->num)
    IDTInit;
}

#assert !offset(MPCmdCtrl.next_waiting)

BoolI64 MPCmdCtrlDo(MPCmdCtrl *ctrl)
{ //Called while locked
  MPCmdStruct *tempm=ctrl->next_waiting;
  while (tempm!=ctrl && !Bt(&tempm->target_cpu_mask,Gs->num))
    tempm=tempm->next;
  if (tempm!=ctrl) {
    RemQue(tempm);
    LBts(&tempm->flags,MPCf_DISPATCHED);
    tempm->handler_cpu=Gs->num;
    LBtr(&ctrl->flags,MPCCf_LOCKED);
    switch (tempm->cmd_code) {
      case MPCT_CALL:
        tempm->result=tempm->add(tempm->data);
        Preempt(OFF);
        Sti;
        break;
      case MPCT_SPAWN_TASK:
        if (tempm->desc)
          tempm->task=Spawn(tempm->add,tempm->data,tempm->desc);
        else
          tempm->task=Spawn(tempm->add,tempm->data,"MP Job");
        break;
    }
    if (Bt(&tempm->flags,MPCf_FREE_ON_COMPLETE)) {
      Free(tempm->desc);
      Free(tempm);
    } else {
      while (LBts(&ctrl->flags,MPCCf_LOCKED))
        Yield;
      InsQue(tempm,ctrl->last_done);
      LBts(&tempm->flags,MPCf_DONE);
      LBtr(&ctrl->flags,MPCCf_LOCKED);
    }
    return TRUE;
  } else {
    LBtr(&ctrl->flags,MPCCf_LOCKED);
    return FALSE;
  }
}

U0 MPSethServerTask()
{
  MPCmdCtrl *my_ctrl=&Gs->cmd_ctrl;
  Sti;
  while (TRUE) {
    TaskFinishOffDying;
    if (!LBts(&my_ctrl->flags,MPCCf_LOCKED) &&
        !MPCmdCtrlDo(my_ctrl) && //Jobs for this core are highest priority.
        !LBts(&mp_ctrl->flags,MPCCf_LOCKED))
        MPCmdCtrlDo(mp_ctrl);
    if (my_ctrl->next_waiting==my_ctrl &&
        mp_ctrl->next_waiting==mp_ctrl) {
      LBts(&Gs->cpu_flags,CPUf_SETH_SLEEP);
      Sleep(20); //We don't want this too long because of need for TaskFinishOffDying().
      LBtr(&Gs->cpu_flags,CPUf_SETH_SLEEP);
    }
  }
}

MPCmdStruct *MPQueueJob(I64 (*fp_add)(U0 *data),U0 *data=NULL,
       U8 *desc=NULL,
       U64 flags=1<<MPCf_FREE_ON_COMPLETE,
       BoolI8 spawn=FALSE,U64 target_cpu_mask=ALL_MASK)
{
  I64 i=Bsf(target_cpu_mask);
  MPCmdCtrl *ctrl;
  MPCmdStruct *tempm;
  if (mp_cnt<2 || i<0) throw(EXCEPT_MULTICORE,1);
  tempm=ACAlloc(sizeof(MPCmdStruct));
  if (desc)
    tempm->desc=AStrNew(desc);
  if (spawn)
    tempm->cmd_code=MPCT_SPAWN_TASK;
  else
    tempm->cmd_code=MPCT_CALL;
  tempm->add=fp_add;
  tempm->data=data;
  tempm->target_cpu_mask=target_cpu_mask;
  tempm->flags=flags;
  if (i==Bsr(target_cpu_mask))
    tempm->ctrl=ctrl=&cpu_structs[i].cmd_ctrl;
  else
    tempm->ctrl=ctrl=mp_ctrl;
  PushFD;
  Cli;
  while (LBts(&ctrl->flags,MPCCf_LOCKED))
    Yield;
  InsQue(tempm,ctrl->last_waiting);
  LBtr(&ctrl->flags,MPCCf_LOCKED);
  PopFD;
  MPWake(target_cpu_mask);
  return tempm;
}

MPCmdStruct *MPJob(I64 (*fp_add)(U0 *data),U0 *data=NULL,
       U64 flags=1<<MPCf_FREE_ON_COMPLETE,
       U64 target_cpu_mask=ALL_MASK)
//Set flags to zero if you wish to
//get the result.
{
  return MPQueueJob(fp_add,data,NULL,flags,FALSE,target_cpu_mask);
}

I64 MPJobResult(MPCmdStruct *tempm)
{
  I64 result;
  MPCmdCtrl *ctrl=tempm->ctrl;
  if (mp_cnt<2) throw(EXCEPT_MULTICORE,2);
  while (!Bt(&tempm->flags,MPCf_DONE))
    Yield;
  PushFD;
  Cli;
  while (LBts(&ctrl->flags,MPCCf_LOCKED))
    Yield;
  RemQue(tempm);
  LBtr(&ctrl->flags,MPCCf_LOCKED);
  PopFD;
  result=tempm->result;
  Free(tempm->desc);
  Free(tempm);
  return result;
}

TaskStruct *MPSpawn(U0 (*fp_add)(U0 *data),U0 *data=NULL,U8 *name=NULL,U64 target_cpu=ALL_MASK)
{
  TaskStruct *result;
  MPCmdStruct *tempm=MPQueueJob(fp_add,data,name,0,TRUE,target_cpu);
  MPCmdCtrl *ctrl;
  while (!Bt(&tempm->flags,MPCf_DONE))
    Yield;
  result=tempm->task;
  ctrl=tempm->ctrl;
  PushFD;
  Cli;
  while (LBts(&ctrl->flags,MPCCf_LOCKED))
    Yield;
  RemQue(tempm);
  LBtr(&ctrl->flags,MPCCf_LOCKED);
  PopFD;
  Free(tempm->desc);
  Free(tempm);
  return result;
}

U0 MPCPUTaskInit()
{ //Called by multicore's seth task after MPStart()
  //as the first thing a CPU does before waiting for jobs
  MPAPICInit;
  Fs->rip=&MPSethServerTask;
  Fs->time_slice_start=GetTimeStamp;
  RestoreContext;
}

U0 MPCmdQueDel(MPCmdStruct *root)
{
  MPCmdStruct *tempm=root->next,*tempm1;
  while (tempm!=root) {
    tempm1=tempm->next;
    RemQue(tempm);
    Free(tempm->desc);
    Free(tempm);
    tempm=tempm1;
  }
}

U0 MPStart()
{ //Called by adam during start-up
  //and can be called again to reset multicores.
  //Doesn't always work when resetting.  Kill all tasks, first.
  //Must be called from core#0.
  TaskStruct *task;
  U8 buf[128];
  U32 *d;
  MPMainStruct *mp=MP_VECTOR_ADDRESS;
  CPUStruct *c;
  BlkPool *code_bp,*data_bp,*saved_code_bp,*saved_data_bp;
  I64 shared_blks,i,old_mp_cnt;
  RaxRbxRcxRdx ee;

  CpuId(0x1,&ee);
  if (!Bt(&ee.rdx,9))
    return;

  PushFD;
  Cli;
  if (mp_cnt>1) {
    old_mp_cnt=mp_cnt;
    MPHalt; //sets mp_cnt to 1

    for (i=1;i<old_mp_cnt;i++) {
      c=&cpu_structs[i];
      MPCmdQueDel(&c->cmd_ctrl.next_waiting);
      MPCmdQueDel(&c->cmd_ctrl.next_done);

      data_bp=c->data_bp;
      code_bp=c->code_bp;
      if (data_bp && data_bp!=code_bp) {
        if (sys_data_bp)
          Free2MegMemBlks(data_bp,data_bp->size>>12,sys_data_bp);
        else
          Free2MegMemBlks(data_bp,data_bp->size>>12,sys_code_bp);
      }
      if (code_bp)
        Free2MegMemBlks(code_bp,code_bp->size>>12,sys_code_bp);
    }
 
    MPCmdQueDel(&mp_ctrl->next_waiting);
    MPCmdQueDel(&mp_ctrl->next_done);
  }
  MemSet(&cpu_structs[1],0,sizeof(CPUStruct)*(MP_MAX_PROCESSORS-1));

//When you start-up other cores, they jump to an address
//specified by a byte vector number, MPN_VECTOR which corresponds
//to a location 4096*vector number, MP_VECTOR_ADDRESS.  I use vector 0x0F.
//This copies the start-up code downto 0x0F000.
//OSMain is at 0x10000, so it's safe.
  MemCpy(mp,SYS_MP_INIT_START,SYS_MP_INIT_END-SYS_MP_INIT_START);

#assert offset(MPMainStruct.sys_temp_ptr)==MP_SYS_TEMP_PTR
  mp->sys_temp_ptr=MAXGDT*16-1+sys_gdttab><(U8 *)<<16;
  mp_cnt=1;
  mp_cnt_lock=0;

  d=LAPIC_LVT_ERR;
  *d=*d&0xFFFFFF00+MPN_VECTOR;
  WbInvd;

  d=LAPIC_ICR_LOW;
  *d=0xC4500; //assert init IPI
  BusyWait(10000);

  *d=0xC4600+MPN_VECTOR; //start-up
  BusyWait(200);
  *d=0xC4600+MPN_VECTOR;

  BusyWait(100000);
  if (mp_cnt>MP_MAX_PROCESSORS)
    mp_cnt=MP_MAX_PROCESSORS;

  for (i=1;i<mp_cnt;i++) {
    c =&cpu_structs[i];
    //SPrintF not available yet.
    StrCpy(buf,"Seth Task CPU#0");
    buf[14]+=i;

    shared_blks=MP_DATA_HEAP_SIZE/0x200000; //2Meg blks for core data heaps
    if (sys_data_bp && sys_data_bp!=sys_code_bp)
      data_bp=Alloc2MegMemBlks(&shared_blks,sys_data_bp);
    else
      data_bp=Alloc2MegMemBlks(&shared_blks,sys_code_bp);
    BlkPoolInit(data_bp,shared_blks<<12);

    shared_blks=MP_CODE_HEAP_SIZE/0x200000; //2Meg blks for core code heaps
    code_bp=Alloc2MegMemBlks(&shared_blks,sys_code_bp);
    BlkPoolInit(code_bp,shared_blks<<12);

    saved_code_bp=Gs->code_bp;
    saved_data_bp=Gs->data_bp;
    Gs->code_bp=code_bp;
    Gs->data_bp=data_bp;

    task=Spawn(&MPCPUTaskInit,NULL,buf,NULL,NULL,SETH_STK,0);
    //TaskStruct allocated off this core's seth_task's heap (Which is Adam)

    task->task_in_queue_signature=TASK_IN_QUEUE_SIGNATURE_VAL;
    CPUStructInit(i,c);
    c->code_bp=code_bp;
    c->data_bp=data_bp;
    Gs->code_bp=saved_code_bp;
    Gs->data_bp=saved_data_bp;
    c->tss=TssNew(i);
    c->seth_task=task;
    WbInvd;
  }
  PopFD;
}

U0 MPInit()
{ //Called by adam during start-up
  RaxRbxRcxRdx ee;
  CPUStruct *c;
  CpuId(0x1,&ee);

  mp_cnt=1;
  mp_cnt_lock=0;

  mp_ctrl=AMAlloc(sizeof(MPCmdCtrl));
  MPCmdCtrlInit(mp_ctrl);

  mp_crash=ACAlloc(sizeof(MPCrashStruct));

//Must be in code heap because init code uses 32 bit address of cpu_struct
  c=cpu_structs=CAlloc(sizeof(CPUStruct)*MP_MAX_PROCESSORS,Fs->code_heap);
  MemCpy(c,&sys_temp_cpu0_struct,sizeof(CPUStruct));
  c->address=cpu_structs;

  SetGs(c);
  adam_task->gs=c;
  adam_task->time_slice_start=GetTimeStamp;
  c->seth_task=adam_task;
  c->code_bp=sys_code_bp;
  c->data_bp=sys_data_bp;
  c->tss=TssNew(0);

  c->idle_task=Spawn(0,NULL,"Idle Task",Fs,NULL,IDLE_STK,0);
  if (Bt(&ee.rdx,9)) {
//Unnecessary?
//    SetMSR(IA32_LAPIC_BASE,LAPIC_BASE+0x900);
    MPAPICInit;
  }
}

U0 MPCrash()
{ //Entering the debugger from another core causes an interrupt on core#0
  //Which calls this routine.
  IntEOI;
  mp_cnt=1;
  Raw(ON);
  sys_dc_flags|=DCF_SHOW_DOLLAR;
  coutln "MP Crash CPU#",mp_crash->cpu_num," Task:",mp_crash->task;
  Dbg(mp_crash->msg,mp_crash->msg_num);
}
Historical capture: 2010-08-25. Home | Power on LoseThos | Sources and changes