📄 bios32.cxx
字号:
/* * Copyright (C) 1998, 1999, Jonathan S. Shapiro. * * This file is part of the EROS Operating System. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2, * or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *//* X86 implementation of the PCI BIOS interface. Mostly, we just cop * out and pass the buck to the system BIOS. */#include <kerninc/kernel.hxx>#include <kerninc/util.h>#include <kerninc/PCI.hxx>#include <kerninc/IRQ.hxx>#include "Segment.hxx"#define dbg_bios32 0x1 /* steps in taking snapshot */#define dbg_pcibios 0x2 /* migration state machine *//* Following should be an OR of some of the above */#define dbg_flags ( 0u )#define DBCOND(x) (dbg_##x & dbg_flags)#define DEBUG(x) if DBCOND(x)#define DEBUG2(x,y) if ((dbg_##x|dbg_##y) & dbg_flags)/* * Different versions of GAS implement different syntax for lcall. The * newer ones bitch about the old syntax, while the older ones barf on * the new syntax. Lovely. Really. */#ifdef __GNUC__#if (__GNUC__ >= 2) && (__GNUC_MINOR__ >= 96)#define LCALL(reg) "lcall *(" #reg ")"#else#define LCALL(reg) "lcall (" #reg ")"#endif#endif/* Issue: the PCI bios is called during hardware init, when CPL == 0, * and also at driver init, by which point CPL is 1. For this reason * the descriptor table has TWO bios32 segments that are identical * except for CPL. Every procedure in here must take care to set the * right one using the following macro */#define SET_SELECTOR(farptr) \ farptr.selector = \ (GetCPL() ? Selector::KProcBios32 : Selector::KernelBios32)/* These are derived from MindShare's PCI System Architecture: */#define PCIBIOSFUN(x) (0xb100 | (x))struct PciFn { enum { BiosPresent = PCIBIOSFUN(0x1), FindDevice = PCIBIOSFUN(0x2), FindClass = PCIBIOSFUN(0x3), SpecialCycle = PCIBIOSFUN(0x6), RdConfByte = PCIBIOSFUN(0x8), RdConfHalfWord = PCIBIOSFUN(0x9), RdConfWord = PCIBIOSFUN(0xa), WrConfByte = PCIBIOSFUN(0xb), WrConfHalfWord = PCIBIOSFUN(0xc), WrConfWord = PCIBIOSFUN(0xd), GetIrqRoute = PCIBIOSFUN(0xe), SetPciInterrupt = PCIBIOSFUN(0xf), };} ;/* Various memory signatures we need to look for: */struct Signature { enum { bios32 = ('_' + ('3' << 8) + ('2' << 16) + ('_' << 24)), pci = ('P' + ('C' << 8) + ('I' << 16) + (' ' << 24)), pcisvc = ('$' + ('P' << 8) + ('C' << 16) + ('I' << 24)), };};union Bios32Entry { struct { uint32_t signature; /* Signature::bios32 */ kpa_t entry; /* physical address */ uint8_t revision; /* revision level */ uint8_t length; /* in paragraphs -- currently 1 */ uint8_t checksum; /* bytewise checksum */ uint8_t reserved[5]; /* reserved for future use */ } fields; uint8_t bytes[16];} ;/* Virtual address of the 32-bit BIOS service directory. Some BIOS's * appear to have more than one. We use only the first one for now. */static bool Bios32IsInit = false;static klva_t Bios32EntryPt = 0;static struct { uint32_t offset; uint16_t selector;} Bios32FarPtr = { 0, Selector::Null };inline uint32_tGetCPL(){ uint32_t cpl; __asm__ ("movw %%cs,%w0\n\t" "andl $0x3,%0" : "=r" (cpl) /* output */ : /* no input */); return cpl;}/* Try to hunt down a 32 bit BIOS services directory: */static voidbios32_init(){ DEBUG(bios32) dprintf(true, "Initializing BIOS32\n"); if (Bios32IsInit) return; Bios32Entry *b32bottom = (Bios32Entry *) 0xe0000; Bios32Entry *b32top = (Bios32Entry *) 0xffff0; Bios32Entry *entry = 0; for (entry = b32bottom; entry < b32top; entry++) { if (entry->fields.signature != Signature::bios32) continue; uint32_t length = entry->fields.length * 16; if (length == 0) { printf("BIOS32: WARNING! bogus bios32 structure at 0x%08x\n", entry); continue; } assert(length); uint8_t sum = 0; for (uint32_t i = 0; i < length; i++) sum += entry->bytes[i]; if (sum != 0) continue; DEBUG(bios32) dprintf(true, "BIOS32: structure found at 0x%08x\n", entry); if (entry->fields.revision != 0x0) fatal("Unsupported BIOS32 revision %d at 0x%08x\n", entry->fields.revision, entry); if (Bios32EntryPt == 0) { if (entry->fields.entry > 0x100000) fatal("BIOS32 entry point in high memory at 0x%08x\n", entry->fields.entry); Bios32EntryPt = Bios32FarPtr.offset = entry->fields.entry; printf("BIOS32 Services Directory found at 0x%08x\n", entry->fields.entry); } } Bios32IsInit = true; DEBUG(bios32) dprintf(true, "BIOS32 initialization completed: %s\n", Bios32IsInit ? "good" : "not present");}/* * Returns the entry point for the given service, NULL on error */static uint32_tBios32FindService(uint32_t service){ uint8_t returnCode; /* %al */ uint32_t address; /* %ebx */ uint32_t length; /* %ecx */ uint32_t entry; /* %edx */ SET_SELECTOR(Bios32FarPtr); IRQ::DISABLE(); __asm__(LCALL(%%edi) : "=a" (returnCode), "=b" (address), "=c" (length), "=d" (entry) : "0" (service), "1" (0), "D" (&Bios32FarPtr)); IRQ::ENABLE(); switch (returnCode) { case 0: return address + entry; case 0x80: /* Not present */ printf("Bios32FindService(%ld) : not present\n", service); return 0; default: /* Shouldn't happen */ fatal("Bios32FindService(%ld) : returned 0x%x\n", service, returnCode); return 0; }}bool PciBios::isInit = false;static klva_t PciBiosEntryPt = 0;static struct { uint32_t offset; uint16_t selector;} PciFarPtr = { 0, Selector::Null };voidPciBios::Init(){ if (isInit) return; DEBUG(pcibios) dprintf(true,"Initializing PCI Bios\n"); bios32_init(); if (Bios32EntryPt == 0) { printf("PciBios::Init(): no BIOS32 entry point\n"); return; } DEBUG(pcibios) dprintf(true,"First call to Bios32FindService()\n"); PciBiosEntryPt = Bios32FindService(Signature::pcisvc); DEBUG(pcibios) dprintf(true,"First call to Bios32FindService() -- made it\n"); if (PciBiosEntryPt) { uint32_t signature; uint8_t status; uint8_t majorRevision; uint8_t minorRevision; uint32_t pack; SET_SELECTOR(PciFarPtr); PciFarPtr.offset = PciBiosEntryPt; IRQ::DISABLE(); __asm__(LCALL(%%edi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:\tshl $8, %%eax\n\t" "movw %%bx, %%ax" : "=d" (signature), "=a" (pack) : "1" (PciFn::BiosPresent), "D" (&PciFarPtr) : "bx", "cx"); IRQ::ENABLE(); status = (pack >> 16) & 0xff; majorRevision = (pack >> 8) & 0xff; minorRevision = pack & 0xff;#if 0 printf("Found PCI BIOS with signature 0x%08x (\"%c%c%c%c\")\n", signature, signature & 0xff, (signature >> 8) & 0xff, (signature >> 16) & 0xff, (signature >> 24) & 0xff); printf(" status=%d\n", status); printf(" majorRev=0x%x\n", majorRevision); printf(" minorRev=0x%x\n", minorRevision);#endif if (signature != Signature::pci) { PciBiosEntryPt = 0; printf("PCI BIOS signature is bad\n"); halt('B'); } if (status) printf("Unexpected status %d from PciFn::BiosPresent\n", status); if (PciBiosEntryPt) { printf("PCI BIOS revision %x.%02x entry at 0x%x\n", majorRevision, minorRevision, PciBiosEntryPt); } } isInit = true; return;}boolPciBios::Present(){ PciBios::Init(); if (PciBiosEntryPt) return true; return false; /* just for now */}/* Patch brain-damaged card entries: */voidPciBios::Fixup(){}uint32_tPciBios::FindDevice (uint16_t vendor, uint16_t device_id, uint16_t index, uint8_t& bus, uint8_t& device_fn){ uint16_t bx = 0; uint16_t ret = 0; DEBUG(pcibios) dprintf(true,"Call to PciBios::FindDevice()\n"); SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%edi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=b" (bx), "=a" (ret) : "1" (PciFn::FindDevice), "c" (device_id), "d" (vendor), "S" ((uint32_t) index), "D" (&PciFarPtr)); IRQ::ENABLE(); DEBUG(pcibios) dprintf(true,"Call to PciBios::FindDevice() -- done\n"); bus = (bx >> 8) & 0xff; device_fn = bx & 0xff; return (uint32_t) (ret & 0xff00) >> 8;}uint32_tPciBios::FindClass (uint32_t devClass, uint16_t index, uint8_t &bus, uint8_t& device_fn){ uint16_t bx; uint16_t ret; SET_SELECTOR(PciFarPtr); DEBUG(pcibios) dprintf(true,"Call to PciBios::FindClass()\n"); IRQ::DISABLE(); __asm__ (LCALL(%%edi)"\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=b" (bx), "=a" (ret) : "1" (PciFn::FindClass), "c" (devClass), "S" ((uint32_t) index), "D" (&PciFarPtr)); IRQ::ENABLE(); DEBUG(pcibios) dprintf(true,"Call to PciBios::FindClass() -- done\n"); bus = (bx >> 8) & 0xff; device_fn = bx & 0xff; return (int) (ret & 0xff00) >> 8;}uint32_tPciBios::ReadConfig8 (uint8_t bus, uint8_t device_fn, uint8_t where, uint8_t& value){ uint32_t ret = 0; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (value), "=a" (ret) : "1" (PciFn::RdConfByte), "b" (bx), "D" ((uint32_t) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (uint32_t) (ret & 0xff00) >> 8;}uint32_tPciBios::ReadConfig16 (uint8_t bus, uint8_t device_fn, uint8_t where, uint16_t& value){ uint32_t ret = 0; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (value), "=a" (ret) : "1" (PciFn::RdConfHalfWord), "b" (bx), "D" ((uint32_t) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (uint32_t) (ret & 0xff00) >> 8;}uint32_tPciBios::ReadConfig32 (uint8_t bus, uint8_t device_fn, uint8_t where, uint32_t& value){ uint32_t ret = 0; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=c" (value), "=a" (ret) : "1" (PciFn::RdConfWord), "b" (bx), "D" ((uint32_t) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (uint32_t) (ret & 0xff00) >> 8;}uint32_tPciBios::WriteConfig8 (uint8_t bus, uint8_t device_fn, uint8_t where, uint8_t value){ uint32_t ret; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=a" (ret) : "0" (PciFn::WrConfByte), "c" (value), "b" (bx), "D" ((long) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (int) (ret & 0xff00) >> 8;}uint32_tPciBios::WriteConfig16 (uint8_t bus, uint8_t device_fn, uint8_t where, uint16_t value){ uint32_t ret = 0; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=a" (ret) : "0" (PciFn::WrConfHalfWord), "c" (value), "b" (bx), "D" ((long) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (int) (ret & 0xff00) >> 8;}uint32_tPciBios::WriteConfig32 (uint8_t bus, uint8_t device_fn, uint8_t where, uint32_t value){ uint32_t ret = 0; uint32_t bx = (bus << 8) | device_fn; SET_SELECTOR(PciFarPtr); IRQ::DISABLE(); __asm__(LCALL(%%esi) "\n\t" "jc 1f\n\t" "xor %%ah, %%ah\n" "1:" : "=a" (ret) : "0" (PciFn::WrConfWord), "c" (value), "b" (bx), "D" ((uint32_t) where), "S" (&PciFarPtr)); IRQ::ENABLE(); return (uint32_t) (ret & 0xff00) >> 8;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -