INT 10h AH=0Eh(VGAへの文字出力)のエミュレーション

まずはコンソール出力が無いと始まらないので、INT 10hから手を付ける事にした。
と言っても、結構色々な機能があるので、まずはFreeBSDMBRで使われているAH=0Ehだけサポートしてみる。

fbsdrun.c

vmexit_hypercall(struct vmctx *ctx, struct vm_exit *vmexit, int *pvcpu)
{
        int intno = (vmexit->rip - 0x400) / 0x4;

        if (!bios_mode) {
                fprintf(stderr, "Failed to handle hypercall at 0x%lx\n", 
                        vmexit->rip);
                return (VMEXIT_ABORT);
        }
                
        if (emulate_bios_call(ctx, *pvcpu, intno) != 0) {
                fprintf(stderr, "Failed to emulate INT %x at 0x%lx\n", 
                        intno, vmexit->rip);
                return (VMEXIT_ABORT);
        }
                
        return (VMEXIT_CONTINUE);
}


bios_call.c

int
emulate_bios_call(struct vmctx *ctx, int vcpu, int intno)
{
	bios_call_func_t handler;

	assert(intno < MAX_INTRS);

	handler = bios_call_handlers[intno].handler;

	if (handler == default_bios_call)
		return (-1);

	return ((*handler)(ctx, vcpu, intno));
}


bios_int10.c

static int
int10_handler(struct vmctx *ctx, int vcpu, int intno)
{
	static int opened;
	uint64_t rax, rbx;
	uint8_t al, ah, bl, bh;
	int error;

	if (!opened) {
		ttyopen();
		opened = 1;
	}

	if ((error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RAX, &rax)) != 0)
		goto done;

	if ((error = vm_get_register(ctx, vcpu, VM_REG_GUEST_RBX, &rbx)) != 0)
		goto done;

	al = (uint8_t)rax;
	ah = (uint8_t)(rax >> 8);
	bl = (uint8_t)rbx;
	bh = (uint8_t)(rbx >> 8);

	switch (ah) {
	case 0x0e:
		ttywrite(al);
		break;
	default:
        	fprintf(stderr, "Not implemented BIOS call int=%x ah=%x\n",
			intno, ah);
	}

done:
	return (error);

}

詳しくは
svn diff -r238910:238930
https://socsvn.freebsd.org/socsvn/soc2012/syuu/bhyve-bios