OpenBSD/sgi on octane2 - clock割り込み

cpu1でclock割り込みを起こしてみた。

cpu_hatchでcpu_initclocks()を呼び、statusレジスタで割り込み有効にする。

void 
prom_cpu_hatch(struct cpu_info *ci)
{
	char *cp;
	void *gp, *sp;
	asm volatile(
		"move %0, $gp\n"
		"move %1, $sp\n"
		: "=r"(gp), "=r"(sp));

	int cpuid = cpu_number();

	combinit();
	combprintf("comb initialized by cpu%d\n", cpuid);
	combprintf("ci:%p\n", ci);
	combprintf("gp:%p\n", gp);
	combprintf("sp:%p\n", sp);


	/*
	 * Make sure we can access the extended address space.
	 * Note that r10k and later do not allow XUSEG accesses
	 * from kernel mode unless SR_UX is set.
	 */
	setsr(getsr() | SR_KX | SR_UX);

	combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	/*
	 * Determine system type and set up configuration record data.
	 */
	sys_config.cpu[cpuid].clock = 175000000;  /* Reasonable default */
	cp = Bios_GetEnvironmentVariable("cpufreq");
	if (cp && atoi(cp, 10, NULL) > 100)
		sys_config.cpu[cpuid].clock = atoi(cp, 10, NULL) * 1000000;
	combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	sys_config.cpu[cpuid].type = (cp0_get_prid() >> 8) & 0xff;
	sys_config.cpu[cpuid].vers_maj = (cp0_get_prid() >> 4) & 0x0f;
	sys_config.cpu[cpuid].vers_min = cp0_get_prid() & 0x0f;
	sys_config.cpu[cpuid].fptype = (cp1_get_prid() >> 8) & 0xff;
	sys_config.cpu[cpuid].fpvers_maj = (cp1_get_prid() >> 4) & 0x0f;
	sys_config.cpu[cpuid].fpvers_min = cp1_get_prid() & 0x0f;
	sys_config.cpu[cpuid].tlbsize = 64;
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	Mips10k_ConfigCache();
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	/*
	 * Last chance to call the BIOS. Wiping the TLB means the BIOS' data
	 * areas are demapped on most systems. O2s are okay as they do not have 
	 * mapped BIOS text or data.
	 */
	delay(20*1000);		/* Let any UART FIFO drain... */
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	sys_config.cpu[cpuid].tlbwired = UPAGES / 2;
	tlb_set_wired(0);
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	tlb_flush(sys_config.cpu[cpuid].tlbsize);
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	tlb_set_wired(sys_config.cpu[cpuid].tlbwired);
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);

	/* XXX does it correct? */
	tlb_set_pid(1);
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);

	/*
	 * Turn off bootstrap exception vectors.
	 */
	setsr(getsr() & ~SR_BOOT_EXC_VEC);
        combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);
	/*
	 * Clear out the I and D caches.
	 */
	Mips_SyncCache();
	combprintf("[%s:%s:%d]\n", __FILE__, __func__, __LINE__);

	cpu_initclocks();

	setsr(getsr() | SR_INT_ENAB | SR_INT_MASK);
	while(1)
		;
}

cpu_initclocks()では初期化に必要な処理をひとしきり行い、最後にcp0_set_compare()を実行する。ここで設定されたカウンタ値に応じて次の割り込みが発生する。

void
cpu_initclocks()
{
	struct cpu_info *ci = curcpu();
	struct tod_desc *cd = &sys_tod;
	struct tod_time ct;
	u_int first_cp0, second_cp0, cycles_per_sec;
	int first_sec;
	int s;

	hz = 100;
	profhz = 100;
	stathz = 0;	/* XXX no stat clock yet */

	/*
	 * Calibrate the cycle counter frequency.
	 */
	if (cd->tod_get != NULL) {
		(*cd->tod_get)(cd->tod_cookie, 0, &ct);
		first_sec = ct.sec;

		/* Let the clock tick one second. */
		do {
			first_cp0 = cp0_get_count();
			(*cd->tod_get)(cd->tod_cookie, 0, &ct);
		} while (ct.sec == first_sec);
		first_sec = ct.sec;
		/* Let the clock tick one more second. */
		do {
			second_cp0 = cp0_get_count();
			(*cd->tod_get)(cd->tod_cookie, 0, &ct);
		} while (ct.sec == first_sec);

		cycles_per_sec = second_cp0 - first_cp0;
		sys_config.cpu[0].clock = cycles_per_sec * 2;
	}

	tick = 1000000 / hz;	/* number of micro-seconds between interrupts */
	tickadj = 240000 / (60 * hz);		/* can adjust 240ms in 60s */

	cp0_timecounter.tc_frequency = sys_config.cpu[0].clock / 2;
	tc_init(&cp0_timecounter);

	/* Start the clock. */
	s = splclock();
	ci->ci_cpu_counter_interval = cp0_timecounter.tc_frequency / hz;
	ci->ci_cpu_counter_last = cp0_get_count() + ci->ci_cpu_counter_interval;
	cp0_set_compare(ci->ci_cpu_counter_last);
	ci->ci_clock_started++;
	splx(s);
}

割り込みハンドラルーチンに、combprintf()を仕込んでcpu1へ割り込みが着たらinterruptと表示させる。

extern int
combprintf(const char *fmt, ...);
void
interrupt(struct trap_frame *trapframe)
{
	struct cpu_info *ci = curcpu();
	u_int32_t pending;
	u_int32_t cause;
	int i;
	intrmask_t xcpl;

	/*
	 *  Paranoic? Perhaps. But if we got here with the enable
	 *  bit reset a mtc0 COP_0_STATUS_REG may have been interrupted.
	 *  If this was a disable and the pipeline had advanced long
	 *  enough... i don't know but better safe than sorry...
	 *  The main effect is not the interrupts but the spl mechanism.
	 */
	if (!(trapframe->sr & SR_INT_ENAB)) {
		return;
	}

	if (cpu_number())
		combprintf("interrupt\n");

かくして、com1の出力はinterruptという文字だらけになったのであった。
めでたしめでたし。

これでIPIのテストにとりかかれるな。