⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 x86.c

📁 开源的BIOS启动软件
💻 C
字号:
/* * init/x86.c * * Copyright (C) 2000 Russell King. * * This file provides the glue for the X86 emulator. */#include <bios/types.h>#include <bios/stdio.h>#include <bios/string.h>#include <bios/malloc.h>#include <bios/pci.h>#include <bios/x86.h>#include <x86emu.h>/* * The x86 memory map consists of 16 64K "pages".  This * allows us to point x86 addresses 0xa0000 - 0xbf000 * at the PCI memory space. */#define NR_PAGES	16static unsigned char *pages[NR_PAGES];/* * The image of the 0xCF8 register: *   31		1 *   16-23	bus *    8-15	devfn *    2-7	address * OR: *   31		0 *   16-23	bus *    4-7	1111b *    1-3	func *    0		0 */static u32 reg_cf8;/* * This is a bit array of port addresses we forward to * the PCI bus.  Note that we do this on a granularity * of 32-bits. */static unsigned char forward_to_pci[65536/32];#define TEST_FWD(port)	(forward_to_pci[(port)>>5] & (1 << ((port >> 2) & 7)))#define SET_FWD(port)	(forward_to_pci[(port)>>5] |= (1 << ((port >> 2) & 7)))/* * i8253 emulation.  We may not have a PCI Southbridge.  In fact, if we * do have one, we don't really want to touch it.  Instead, we emulate * this beast. */struct i8253_timer {	u32 latch;	u32 ctr;	u8  div_low, div_high;	u8  mode, high;};static struct i8253_timer i8253_timer[3];static unsigned int i8253_read(unsigned int ctr){	struct i8253_timer *t = i8253_timer + ctr;	unsigned int mode = t->mode & 0x30;	if (mode) {		unsigned int val, high;		val = t->latch;		high = t->high;		if (high)			val >>= 8;		if (mode == 0x30)			t->high = high ^ 1;		return val;	}	return 0;}static void i8253_write(unsigned int ctr, unsigned int val){	struct i8253_timer *t = i8253_timer + ctr;	unsigned int mode = t->mode & 0x30;	if (mode) {		unsigned int high;		high = t->high;		if (high)			t->div_high = val;		else			t->div_low = val;		if (mode == 0x30)			t->high = high ^ 1;		t->ctr = t->div_low | val << 16;	}}static void i8253_write_ctrl(unsigned int val){	if ((val & 0xc0) != 0xc0) {		struct i8253_timer *t = i8253_timer + (val >> 6);		unsigned int mode = val & 0x30;		t->high = mode == 0x10 ? 1 : 0;		if (mode)			t->mode = val;		else			t->latch = t->ctr;	} else {		debug_printf("x86: unsupported timer command %02x\n", val);	}}/* * Emulate one tick of the counter */static void i8253_cycle(void){	int i;	for (i = 0; i < 3; i++) {		struct i8253_timer *t = i8253_timer + i;		unsigned int dec, msk;		if ((t->mode & 0x0e) == 0x06) {			dec = 2;			msk = ~1;		} else {			dec = 1;			msk = ~0;		}		t->ctr -= dec;		if ((t->ctr & msk) == 0)			t->ctr = t->div_low | t->div_high << 8;	}}/* * For each x86 cycle, there is one i8253 cycle */static void x86_cycle(int nr){	while (nr--)		i8253_cycle();}/* * PCI Config access port emulation (needed for the 3DLabs cards) * *  Should we validate this against the device we're initialising? */static void pci_cfg_write(unsigned int addr, unsigned int val, int sz){	if (addr < 4) {		unsigned int off = (addr & 3);		switch (sz) {		case 1:       ((u8 *)reg_cf8)[off] = val; break;		case 2: ((u16 *)reg_cf8)[off & ~1] = val; break;		case 4:                    reg_cf8 = val; break;		}		return;	}	debug_printf("x86: pci_cfg_write: cf8 = 0x%08x, off = %d, val = 0x%08x, sz = %d\n",		reg_cf8, addr & 3, val, sz);	/*	 * Construct the footbridge address from the CF8 register	 */	addr &= 3;		/* word offset */	addr += reg_cf8 & 0xfc;	/* address offset */	if ((reg_cf8 >> 16) == 0x8000) {		addr += pci_config_addr((reg_cf8 >> 11) & 0x1f, (reg_cf8 >> 8) & 3);		switch (sz) {		case 1:	write_byte(val, addr); break;		case 2: write_word(val, addr); break;		case 4: write_long(val, addr); break;		}	}}static u32 pci_cfg_read(unsigned int addr, int sz){	u32 val = 0;	if (addr < 4)		return reg_cf8 >> (8 * (addr & 3));	/*	 * Construct the footbridge address from the CF8 register	 */	addr &= 3;		/* word offset */	addr += reg_cf8 & 0xfc;	/* address offset */	if ((reg_cf8 >> 16) == 0x8000) {		addr += pci_config_addr((reg_cf8 >> 11) & 0x1f, (reg_cf8 >> 8) & 3);		switch (sz) {		case 1:	val = read_byte(addr); break;		case 2: val = read_word(addr); break;		case 4: val = read_long(addr); break;		}	}	debug_printf("x86: pci_cfg_read: cf8 = 0x%08x, off = %d, sz = %d -> 0x%x\n",		reg_cf8, addr & 3, sz, val);	return val;}/* * x86 port read/write support */static u32 x86_in(unsigned int addr, int sz){	u32 val;	if (addr >= 0xcf8 && addr <= 0xcff) {		val = pci_cfg_read(addr - 0xcf8, sz);		goto out;	}	if (sz == 1 && addr >= 0x40 && addr < 0x43) {	/* 8253 timer */		val = i8253_read(addr - 0x40);		goto out;	}	val = 0;	if (addr == 0x61 || addr == 0x80)		goto out;	debug_printf("x86: in: size %d unknown port 0x%04x\n", sz, addr);out:	return val;}static void x86_out(unsigned int addr, unsigned int val, int sz){	if (addr >= 0xcf8 && addr <= 0xcff) {		pci_cfg_write(addr - 0xcf8, val, sz);		return;	}	switch (addr) {	case 0x61:		break;	case 0x43:		/* 8253 timer */		if (sz == 1)			i8253_write_ctrl(val);		break;	case 0x40 ... 0x42:	/* 8253 timer */		if (sz == 1)			i8253_write(addr - 0x40, val);		break;	default:		debug_printf("x86: out size %d to unknown port %04x\n", sz, addr);	}}static u8 x86_inb(u16 addr){	x86_cycle(1);	if (TEST_FWD(addr))		return pci_io_read_byte(addr);	return x86_in(addr, 1);}static u16 x86_inw(u16 addr){	x86_cycle(2);	if (TEST_FWD(addr))		return pci_io_read_word(addr);	return x86_in(addr, 2);}static u32 x86_inl(u16 addr){	x86_cycle(4);	if (TEST_FWD(addr))		return pci_io_read_long(addr);	return x86_in(addr, 4);}static void x86_outb(u16 addr, u8 val){	x86_cycle(1);	if (TEST_FWD(addr)) {		pci_io_write_byte(val, addr);		return;	}	x86_out(addr, val, 1);}static void x86_outw(u16 addr, u16 val){	x86_cycle(2);	if (TEST_FWD(addr)) {		pci_io_write_word(val, addr);		return;	}	x86_out(addr, val, 2);}static void x86_outl(u16 addr, u32 val){	x86_cycle(4);	if (TEST_FWD(addr)) {		pci_io_write_long(val, addr);		return;	}	x86_out(addr, val, 4);}static const X86EMU_pioFuncs piofuncs = {	inb:	x86_inb,	inw:	x86_inw,	inl:	x86_inl,	outb:	x86_outb,	outw:	x86_outw,	outl:	x86_outl,};/* * x86 memory read/write support */static void *x86_mem_bad(u32 addr, const char *fn){	debug_printf("x86: %s: address 0x%x out of range\n", fn, addr);	HALT_SYS();	return NULL;}static void *x86_mem(u32 addr, const char *fn){	u32 page = addr >> 16;	if (page < NR_PAGES && pages[page])		return pages[page] + addr - (page << 16);	return x86_mem_bad(addr, fn);}static u8 x86_rdb(u32 addr){	void *mem;	u32 val;	x86_cycle(1);	mem = x86_mem(addr, "rdb");	__asm__(	"teq	%1, #0	ldrneb	%0, [%1]" :	"=r" (val) : "0" (mem));	return val;}static u16 x86_rdw(u32 addr){	void *mem;	u32 val1, val2;	x86_cycle(1 + (addr & 1));	mem  = x86_mem(addr, "rdw");	__asm__(	"teq	%2, #0	beq	1f	tst	%2, #1	ldrneb	%1, [%2, #1]	ldrneb	%0, [%2]	ldreqh	%0, [%2]	orrne	%0, %0, %1, lsl #81:	" :	"=r" (val1), "=r" (val2) : "0" (mem) : "cc");	return val1;}static u32 x86_rdl(u32 addr){	void *mem;	u32 val1, val2, val3;	x86_cycle(2 + (addr & 1));	mem = x86_mem(addr, "rdl");	__asm__(	"teq	%3, #0	beq	1f	tst	%3, #3	ldreq	%0, [%3]	beq	1f	ldrb	%1, [%3]	ldrb	%2, [%3, #1]	orr	%1, %1, %2, lsl #8	ldrb	%2, [%3, #2]	orr	%1, %1, %2, lsl #16	ldrb	%2, [%3, #3]	orr	%0, %1, %2, lsl #241:	" :	"=r" (val1), "=r" (val2), "=r" (val3) : "0" (mem) : "cc");	return val1;}static void x86_wrb(u32 addr, u8 val){	void *mem = x86_mem(addr, "wrb");	x86_cycle(1);	if (mem)		*(u8 *)mem = val;	/*	 * If we are writing into the interrupt area,	 * then clear out our "catch all" version.	 */	if (mem && addr < 0x400)		_X86EMU_intrTab[addr >> 2] = NULL;}static void x86_wrw(u32 addr, u16 val){	void *mem = x86_mem(addr, "wrw");	x86_cycle(1 + (addr & 1));	if (mem) {		if (addr & 1) {			((u8 *)mem)[0] = val;			((u8 *)mem)[1] = val >> 8;		} else {			__asm__("str%?h\t%0,[%1]" : : "r" (val), "r" (mem));		}	}	/*	 * If we are writing into the interrupt area,	 * then clear out our "catch all" version.	 */	if (mem && addr < 0x400)		_X86EMU_intrTab[addr >> 2] = NULL;}static void x86_wrl(u32 addr, u32 val){	void *mem = x86_mem(addr, "wrl");	x86_cycle(2 + (addr & 1));	if (mem) {		if (addr & 3) {			((u8 *)mem)[0] = val;			((u8 *)mem)[1] = val >> 8;			((u8 *)mem)[2] = val >> 16;			((u8 *)mem)[3] = val >> 24;		} else {			*(u32 *)mem = val;		}	}	/*	 * If we are writing into the interrupt area,	 * then clear out our "catch all" version.	 */	if (mem && addr < 0x400)		_X86EMU_intrTab[addr >> 2] = NULL;}static const X86EMU_memFuncs memfuncs = {	rdb:	x86_rdb,	rdw:	x86_rdw,	rdl:	x86_rdl,	wrb:	x86_wrb,	wrw:	x86_wrw,	wrl:	x86_wrl,};/* * Don't fault on unknown interrupts - flag them! */static void x86_dummy_intr(int nr){	debug_printf("x86: unknown interrupt 0x%02x, AX=%04x BX=%04x CX=%04x\n",		nr, M.x86.R_AX, M.x86.R_BX, M.x86.R_CX);}/* * Copy into the x86 BIOS area */void x86_copy_in(unsigned int addr, void *data, int sz){	void *ptr;	if (addr < 0xc0000 || addr + sz >= 0xdffff) {		debug_printf("x86: copy_in: bad address 0x%x\n", addr);		return;	}	ptr = pages[12] + (addr - 0xc0000);	memcpy(ptr, data, sz);}/* * Copy out of the x86 BIOS area */void x86_copy_out(void *data, unsigned int addr, int sz){	void *ptr;	if (addr < 0xc0000 || addr + sz >= 0xdffff) {		debug_printf("x86: copy_out: bad address 0x%x\n", addr);		return;	}	ptr = pages[12] + (addr - 0xc0000);	memcpy(data, ptr, sz);}/* * Execute some x86 code */void x86_call(unsigned int eip, unsigned int ax){	/*	 * Initialise the stack and data segment	 */	_X86EMU_env.x86.R_SS = 0x0030;	_X86EMU_env.x86.R_DS = 0x0040;	_X86EMU_env.x86.R_SP = 0xfffe;	/*	 * Setup the RETF return address (SS:0xfffc) to point at	 * a hlt instruction.  This means that we will return	 * after execution of the function.	 */	push_word(0xf4f4);	/* hlt; hlt */	push_word(_X86EMU_env.x86.R_SS);	push_word(_X86EMU_env.x86.R_SP + 2);	_X86EMU_env.x86.R_AX = ax;	_X86EMU_env.x86.R_IP = eip & 15;	_X86EMU_env.x86.R_CS = eip >> 4;	i8253_timer[0].div_low = 0xff;	i8253_timer[0].div_high = 0xff;	i8253_timer[0].mode = 0x30;	X86EMU_exec();}/* * Initialise the X86 core */void x86_init(void){	int i;	/*	 * Setup our memory I/O and port I/O functions	 */	X86EMU_setupPioFuncs((X86EMU_pioFuncs *)&piofuncs);	X86EMU_setupMemFuncs((X86EMU_memFuncs *)&memfuncs);	/*	 * Make all interrupts point at our dummy int routine.	 * As the bioses initialise, they will register their	 * own, and the entry in this table will be NULL'd out.	 */	for (i = 0; i < 256; i++)		_X86EMU_intrTab[i] = x86_dummy_intr;	/*	 * Make pages 10 and 11 point at the PCI bus	 */	pages[10] = (unsigned char *)0x800a0000;	pages[11] = (unsigned char *)0x800b0000;	/*	 * We want some memory for the first page.  This contains	 * the interrupt table and stack.	 */	pages[0] = malloc(4 * 65536);	pages[1] = pages[0] + 65536;	/*	 * BIOSes are copied to pages 12 and 13.  We want these	 * two pages to be contiguous.	 */	pages[12] = pages[1] + 65536;	pages[13] = pages[12] + 65536;	memset(pages[0], 0, 2 * 65536);	memset(pages[12], 0xff, 2 * 65536);	/*	 * Set up the IO ports that we forward directly to PCI space.	 * Really, the PCI IO layer ought to know about these so that	 * we don't accidentally allocate another device in these	 * regions.	 */	/*	 * First, the standard VGA ports	 */	for (i = 0x3b0; i < 0x3e0; i += 4)		SET_FWD(i);	/*	 * And some S3 Diamond Stealth (Trio32) specials	 */	for (i = 0x8180; i < 0x8200; i += 4)	/* Streams processor */		SET_FWD(i);	for (i = 0xff00; i < 0xff44; i += 4)	/* LPB bus */		SET_FWD(i);	/*	 * Enhanced S3 registers	 */	SET_FWD(0x42e8);	SET_FWD(0x46e8);	SET_FWD(0x4ae8);	SET_FWD(0x82e8);	SET_FWD(0x86e8);	SET_FWD(0x8ae8);	SET_FWD(0x8ee8);	SET_FWD(0x92e8);	SET_FWD(0x96e8);	SET_FWD(0x9ae8);	SET_FWD(0x9ee8);	SET_FWD(0xa2e8);	SET_FWD(0xa6e8);	SET_FWD(0xaae8);	SET_FWD(0xaee8);	SET_FWD(0xb2e8);	SET_FWD(0xb6e8);	SET_FWD(0xbae8);	SET_FWD(0xbee8);	SET_FWD(0xe2e8);	SET_FWD(0x100);	/*	 * The following is for Trident TGUI9440 cards	 */	SET_FWD(0x2120);	SET_FWD(0x43c4);}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -