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)

・・・うーん、起きてくる気配なし。とまってしまう。
はて・・・・?