読者です 読者をやめる 読者になる 読者になる

mp_start()もそろそろ大詰め。

minix

IPIの初期化が終わったらsend_startup_ipi()をコール。
引数のtrampoline_addrは初期化の時と同じくトランポリンのアドレス、2個目の引数はどのcpuをスタートさせるかを表す。

void send_startup_ipi(u32_t trampoline_addr, int which_cpu) {
/* Send STARTUP IPI to a processor in order to start execution in it */

   u32_t icr_h,icr_l;

   if (DELIVERY_STATUS) {
      ADDS_MP_STATUS("PANIC: APIC is busy, no startup IPI has been sent!!!\n");
      return;
   } 
   /* put addr in 8 bit lower */
   trampoline_addr = (trampoline_addr >> 12) & 0xFF;

   /* prepare to send STARTUP IPI */
   icr_h = LOCAL_APIC_READ(LOCAL_APIC_ICR_HIGH);
   icr_l = LOCAL_APIC_READ(LOCAL_APIC_ICR_LOW);
 
   icr_l &=~0xCDFFF;  /* clear */
   icr_h &= 0xF0FFFFFF;  /* clear */

   icr_l |= (DELIVERY_STARTUP<<DELIVERY_SHIFT); /* stablish STARTUP */
   icr_l |= (1 << LEVEL_SHIFT);                 /* level = assert */
   icr_l &= ~(1 << TRIGGER_SHIFT);              /* trigger = edge */
   icr_l |= (PHYSICAL_DEST << DEST_MODE_SHIFT); /* destination = physical */
   icr_l |= (DEST_FIELD << DEST_SHORT_SHIFT);   /* destination by field */

   icr_l |= trampoline_addr;                    /* vector field */
   icr_h |= (which_cpu << DEST_FIELD_SHIFT);    /* cpu to interrupt */

   /* send the IPI */
   apic_error_status();
   LOCAL_APIC_WRITE(LOCAL_APIC_ICR_HIGH, icr_h);
   LOCAL_APIC_WRITE(LOCAL_APIC_ICR_LOW,  icr_l);
   wait_for_ipi_completion();
}

この中でやっているのは、変数に値を設定していって、最後にその値を書き込むだけなので至って普通の処理。
ビットをセットしている。
忘れがちになるけどLOCAL_APIC_XXXXで読み書きしているメモリのアドレスは0x0FEE00000がベースアドレスで、READ、WRITEともに1番目の引数の値分足したところにアクセスする。
この関数の場合だと、オフセットにしているのはこちら。

#define LOCAL_APIC_ICR_LOW      0x0300  /* offset for IRC low regiter */
#define LOCAL_APIC_ICR_HIGH     0x0310  /* offset for IRC high register */

コメントでIRCってなっているのはICRのtypoかな?

IPIの初期化、起動が終わったらAPがちゃんと動いているか確認している。

               if (! AP_running()) {
                  ADDS_MP_STATUS("\n\n*** WARNING! AP#");
                  ADDN_MP_STATUS(cpu,10,1);
                  ADDS_MP_STATUS(" is not running ***\n\n");
               }

そして、全cpuの起動が終わったら、最後に以下の関数をコールしてトランポリンコードに使っていたメモリ領域に0を書き込んでおく。

void free_trampoline(u32_t addr) {
/* Restore with 0's the memory zone used for trampoline */

   char dummy=0;
   u32_t tramp_len=(u32_t)end_init_ap-(u32_t)init_ap; 
   while (tramp_len--) phys_copy((u32_t)&dummy,addr++,1);
}

これでkernel/main.cのmain()関数からMP環境を設定する流れは終わりになるんだけど、
c言語のソースからは呼ばれなくてトランポリンコードの中から呼ばれる関数達もあるので、こちらもちゃんと見ないといけないお。
例えば、xmp.sにいるtrampoline_pm()とかmp.cにいるap_main()みたいな関数。

実際の流れだと、トランポリンコードの最後の方で_trampoline_pmという処理に飛んで、

        ! Far jmp to start with 32 bit execution.
        ! Jump to a .text CS-addressed point
        C16     jmpf    CS_SELECTOR:_trampoline_pm

_trampoline_pmの中で色々処理を行ってから、xmp.sにいる_enable_cacheのコールと、mp.cにいるc関数のap_main()を呼ぶ流れがある。

        ! Enable AP cache
                call    _enable_cache
        ! Continue in C code
                call    _ap_main

APを有効にしてもこれらの処理がないと多分というか間違いなくちゃんと動かないので、次回はこれらの処理を読んでいくことにする。