OpenBSD/sgi on octane2 - セカンダリCPUの起動とMPCONFについて
Linux(+RACER対応パッチ)のソースコードを読むと、セカンダリプロセッサ起動のルーチンは以下のように実装されている:
/* arch/mips/sgi-ip30/ip30-smp.c */ void prom_boot_secondary(int cpu, struct task_struct *idle) { int pcpu=cpu_logical_map(cpu); MP_STACKADDR(pcpu)=__KSTK_TOS(idle); MP_LPARM(pcpu)=(unsigned long)idle->thread_info; MP_LAUNCH(pcpu)=ip30_smp_bootstrap; }
この変なマクロはなんやねん。と思ってたどってみると、こんなヘッダファイルがある:
/* include/asm-mips/mach-ip30/racermp.h */ #ifndef _RACERMP_H #define _RACERMP_H #define MPCONF_MAGIC 0xBADDEED2 #define MPCONF_ADDR 0xA800000000000600L #define MPCONF_SIZE 0x80 #define MPCONF(x) (MPCONF_ADDR+(x)*MPCONF_SIZE) #define MP_NCPU 4 #define MP_MAGIC(x) (*(volatile unsigned int *)(MPCONF(x)+0x00)) #define MP_PRID(x) (*(volatile unsigned int *)(MPCONF(x)+0x04)) #define MP_PHYSID(x) (*(volatile unsigned int *)(MPCONF(x)+0x08)) #define MP_VIRTID(x) (*(volatile unsigned int *)(MPCONF(x)+0x0c)) #define MP_SCACHESZ(x) (*(volatile unsigned int *)(MPCONF(x)+0x10)) #define MP_FANLOADS(x) (*(volatile unsigned short *)(MPCONF(x)+0x14)) #define MP_LAUNCH(x) (*(volatile void **)(MPCONF(x)+0x18)) #define MP_RNDVZ(x) (*(volatile void **)(MPCONF(x)+0x20)) #define MP_STACKADDR(x) (*(volatile unsigned long *)(MPCONF(x)+0x40)) #define MP_LPARM(x) (*(volatile unsigned long *)(MPCONF(x)+0x48)) #define MP_RPARM(x) (*(volatile unsigned long *)(MPCONF(x)+0x50)) #define MP_IDLEFLAG(x) (*(volatile unsigned int *)(MPCONF(x)+0x58)) #endif */
どうやら、物理アドレス0x0000000000000600にMPCONFというハードウェアレジスタが有るらしい。
CPU4つ分のレジスタが用意されてて、それぞれ色々なパラメータが読めたり書けたりする。
意味は多分こんな感じだ。
MAGIC: マジック 正しいトコよんでりゃ0xbaddeed2が返るはず
PRID: プロセッサのレビジョン
PHYSID: フィジカル CPU ID
VIRTID: バーチャル CPU ID 仮想CPUと物理CPUの数が一致しない事があるのか???
MP_SCACHESZ: セカンダリキャッシュサイズ
FANLOADS: よーわからん
LAUNCH: エントリポイント
RNDVZ: ランデブー関数 起動後にこの関数を叩く為にあんのかな?
STACKADDR: スタックアドレス
LPARAM: LAUNCHへの引数
RPARAM: RNDVZへの引数
IDLEFLAG: よーわからん
で、早速こんなコードを実装してみたのだが、どーもCPU2が起きてる気配がない。
paddr_t mpconf = PHYS_TO_XKPHYS(MPCONF_BASE, CCA_NC); *(volatile uint64_t *)(mpconf + MPCONF_STACKADDR(ci->ci_cpuid)) = (uint64_t)(kstack + USPACE); *(volatile uint64_t *)(mpconf + MPCONF_LPARAM(ci->ci_cpuid)) = (uint64_t)ci; *(volatile uint64_t *)(mpconf + MPCONF_LAUNCH(ci->ci_cpuid)) = (uint64_t)cpu_spinup_trampoline;
うーん・・・?
なんでじゃろうのう。
追記:
もしかして、MPCONFの0番目ってcpu0じゃなくてセカンダリCPUの0番目なんじゃね?
と思い、ちょっとまずはレジスタを画面に表示してみるテスト。
id:1 magic:0 prid:0 physid:0 virtid:0 scachesz:0 fanloads:0 launch:0 rndvz:0 stackaddr:0 lparam:0 rparam:0 idleflag:0
なんかCPUありそうにないよね。magic == 0だし。
id:0 magic:baddeed2 prid:e35 physid:0 virtid:0 scachesz:21 fanloads:1 launch:0 rndvz:0 stackaddr:0 lparam:0 rparam:0 idleflag:0
なんかとってもコレっぽい気がする。
って事なので、こんな風に書き換えてみた:
void prom_boot_secondary(struct cpu_info *ci) { int cpuid = ci->ci_cpuid - 1; paddr_t mpconf = PHYS_TO_XKPHYS(MPCONF_BASE, CCA_NC); uint32_t magic = *(volatile uint32_t *)(mpconf + MPCONF_MAGIC(cpuid)); uint32_t prid = *(volatile uint32_t *)(mpconf + MPCONF_PRID(cpuid)); uint32_t physid = *(volatile uint32_t *)(mpconf + MPCONF_PHYSID(cpuid)); uint32_t virtid = *(volatile uint32_t *)(mpconf + MPCONF_VIRTID(cpuid)); uint32_t scachesz = *(volatile uint32_t *)(mpconf + MPCONF_SCACHESZ(cpuid)); uint16_t fanloads = *(volatile uint16_t *)(mpconf + MPCONF_FANLOADS(cpuid)); uint64_t launch = *(volatile uint64_t *)(mpconf + MPCONF_LAUNCH(cpuid)); uint64_t rndvz = *(volatile uint64_t *)(mpconf + MPCONF_RNDVZ(cpuid)); uint64_t stackaddr = *(volatile uint64_t *)(mpconf + MPCONF_STACKADDR(cpuid)); uint64_t lparam = *(volatile uint64_t *)(mpconf + MPCONF_LPARAM(cpuid)); uint64_t rparam = *(volatile uint64_t *)(mpconf + MPCONF_RPARAM(cpuid)); uint32_t idleflag = *(volatile uint32_t *)(mpconf + MPCONF_IDLEFLAG(cpuid)); printf("id:%d magic:%x prid:%x physid:%x virtid:%x\n" "scachesz:%u fanloads:%x launch:%llx rndvz:%llx\n" "stackaddr:%llx lparam:%llx rparam:%llx idleflag:%x\n", cpuid, magic, prid, physid, virtid, scachesz, fanloads, launch, rndvz, stackaddr, lparam, rparam, idleflag); vaddr_t kstack; kstack = uvm_km_alloc(kernel_map, USPACE); if (kstack == 0) { panic("prom_boot_secondary: unable to allocate idle stack"); return; } *(volatile uint64_t *)(mpconf + MPCONF_STACKADDR(cpuid)) = (uint64_t)(kstack + USPACE); *(volatile uint64_t *)(mpconf + MPCONF_LPARAM(cpuid)) = (uint64_t)ci; *(volatile uint64_t *)(mpconf + MPCONF_LAUNCH(cpuid)) = (uint64_t)cpu_spinup_trampoline; }
こんな風に呼んでみる:
struct cpu_info cpu2; cpu2.ci_cpuid = 1; prom_boot_secondary(&cpu2); while(mp_launched == 0) asm volatile("sync");
cpu1がmp_launchedを変更してくれる事を期待して待ち続けるテスト。
cpu_spinup_trampolineの方は、mp_launchedへ書き込むアセンブリコードを実装してみた。
LEAF(cpu_spinup_trampoline, 0) mfc0 v0, COP_0_STATUS_REG li v1, ~SR_INT_ENAB and v0, v1 mtc0 v0, COP_0_STATUS_REG # disable all interrupts mtc0 zero, COP_0_CAUSE_REG # Clear soft interrupts LA gp, _gp LA t0, mp_launched li t1, 1 sw t1, 0(t0) sync nop 1: j 1b nop /* jal cpu_hatch */ nop END(cpu_spinup_trampoline)
・・・うーん、起きてくる気配なし。とまってしまう。
はて・・・・?