📄 x86.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 + -