DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/hardware/pci_bus.cpp
00001 /*
00002  *  Copyright (C) 2002-2020  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License along
00015  *  with this program; if not, write to the Free Software Foundation, Inc.,
00016  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  */
00018 
00019 
00020 #include <string.h>
00021 #include "dosbox.h"
00022 #include "inout.h"
00023 #include "paging.h"
00024 #include "mem.h"
00025 #include "pci_bus.h"
00026 #include "setup.h"
00027 #include "debug.h"
00028 #include "callback.h"
00029 #include "regs.h"
00030 #include "../ints/int10.h"
00031 #include "voodoo.h"
00032 #include "control.h"
00033 
00034 bool pcibus_enable = false;
00035 bool log_pci = false;
00036 
00037 bool has_pcibus_enable(void) {
00038     return pcibus_enable;
00039 }
00040 
00041 static Bit32u pci_caddress=0;                   // current PCI addressing
00042 
00043 static PCI_Device* pci_devices[PCI_MAX_PCIBUSSES][PCI_MAX_PCIDEVICES]={{NULL}};         // registered PCI devices
00044 
00045 // PCI address
00046 // 31    - set for a PCI access
00047 // 30-24 - 0
00048 // 23-16 - bus number                   (0x00ff0000)
00049 // 15-11 - device number (slot) (0x0000f800)
00050 // 10- 8 - subfunction number   (0x00000700)
00051 //  7- 2 - config register #    (0x000000fc)
00052 
00053 static void write_pci_addr(Bitu port,Bitu val,Bitu iolen) {
00054     (void)iolen;//UNUSED
00055     (void)port;//UNUSED
00056     if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("Write PCI address :=%x",(int)val);
00057         pci_caddress=(Bit32u)val;
00058 }
00059 
00060 static void write_pci(Bitu port,Bitu val,Bitu iolen) {
00061         if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("Write PCI data port %x :=%x (len %d)",(int)port,(int)val,(int)iolen);
00062 
00063         if (pci_caddress & 0x80000000) {
00064                 Bit8u busnum = (Bit8u)((pci_caddress >> 16) & 0xff);
00065                 Bit8u devnum = (Bit8u)((pci_caddress >> 11) & 0x1f);
00066                 Bit8u fctnum = (Bit8u)((pci_caddress >> 8) & 0x7);
00067                 Bit8u regnum = (Bit8u)((pci_caddress & 0xfc) + (port & 0x03));
00068                 if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("  Write to device %x register %x (function %x) (:=%x)",(int)devnum,(int)regnum,(int)fctnum,(int)val);
00069 
00070                 if (busnum >= PCI_MAX_PCIBUSSES) return;
00071                 if (devnum >= PCI_MAX_PCIDEVICES) return;
00072 
00073                 PCI_Device* dev=pci_devices[busnum][devnum];
00074                 if (dev == NULL) return;
00075                 dev->config_write(regnum,iolen,(Bit32u)val);
00076         }
00077 }
00078 
00079 
00080 static Bitu read_pci_addr(Bitu port,Bitu iolen) {
00081     (void)port;//UNUSED
00082     (void)iolen;//UNUSED
00083         if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("Read PCI address -> %x",pci_caddress);
00084         return pci_caddress;
00085 }
00086 
00087 static Bitu read_pci(Bitu port,Bitu iolen) {
00088         if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("Read PCI data -> %x",pci_caddress);
00089 
00090         if (pci_caddress & 0x80000000UL) {
00091                 Bit8u busnum = (Bit8u)((pci_caddress >> 16U) & 0xffU);
00092                 Bit8u devnum = (Bit8u)((pci_caddress >> 11U) & 0x1fU);
00093                 Bit8u fctnum = (Bit8u)((pci_caddress >> 8U) & 0x7U);
00094                 Bit8u regnum = (Bit8u)((pci_caddress & 0xfcu) + (port & 0x03U));
00095                 if (log_pci) LOG(LOG_PCI,LOG_DEBUG)("  Read from device %x register %x (function %x)",(int)devnum,(int)regnum,(int)fctnum);
00096 
00097                 if (busnum >= PCI_MAX_PCIBUSSES) return ~0UL;
00098                 if (devnum >= PCI_MAX_PCIDEVICES) return ~0UL;
00099 
00100                 PCI_Device* dev=pci_devices[busnum][devnum];
00101                 if (dev == NULL) return ~0UL;
00102                 return dev->config_read(regnum,iolen);
00103         }
00104 
00105         return ~0UL;
00106 }
00107 
00108 
00109 static Bitu PCI_PM_Handler() {
00110         LOG(LOG_PCI,LOG_WARN)("PCI PMode handler, unhandled function %x",reg_ax);
00111         return CBRET_NONE;
00112 }
00113 
00114 PCI_Device::~PCI_Device() {
00115 }
00116 
00117 PCI_Device::PCI_Device(Bit16u vendor, Bit16u device) {
00118         memset(config,0,256);           /* zero config space */
00119         memset(config_writemask,0,256); /* none of it is writeable */
00120         setVendorID(vendor);
00121         setDeviceID(device);
00122 
00123         /* default: allow setting/clearing some bits in the Command register */
00124         host_writew(config_writemask+0x04,0x0403);      /* allow changing mem/io enable and interrupt disable */
00125 }
00126 
00127 class PCI_VGADevice:public PCI_Device {
00128 private:
00129         static const Bit16u vendor=0x5333;              // S3
00130         static const Bit16u device=0x8811;              // trio64
00131 //      static const Bit16u device=0x8810;              // trio32
00132 public:
00133         PCI_VGADevice():PCI_Device(vendor,device) {
00134                 // init (S3 graphics card)
00135                 config[0x08] = 0x00;    // revision ID
00136                 config[0x09] = 0x00;    // interface
00137                 config[0x0a] = 0x00;    // subclass type (vga compatible)
00138                 config[0x0b] = 0x03;    // class type (display controller)
00139                 config[0x0c] = 0x00;    // cache line size
00140                 config[0x0d] = 0x00;    // latency timer
00141                 config[0x0e] = 0x00;    // header type (other)
00142 
00143                 config[0x3c] = 0xff;    // no irq
00144 
00145                 // reset
00146                 config[0x04] = 0x23;    // command register (vga palette snoop, ports enabled, memory space enabled)
00147                 config[0x05] = 0x00;
00148                 config[0x06] = 0x80;    // status register (medium timing, fast back-to-back)
00149                 config[0x07] = 0x02;
00150 
00151                 host_writew(config_writemask+0x04,0x0023);      /* allow changing mem/io enable and VGA palette snoop */
00152 
00153                 host_writed(config_writemask+0x10,0xFF000000);  /* BAR0: memory resource 16MB aligned */
00154                 host_writed(config+0x10,(((Bit32u)S3_LFB_BASE)&0xfffffff0) | 0x8);
00155 
00156                 host_writed(config_writemask+0x14,0xFFFF0000);  /* BAR1: memory resource 64KB aligned */
00157                 host_writed(config+0x14,(((Bit32u)(S3_LFB_BASE+0x1000000))&0xfffffff0));
00158         }
00159 };
00160 
00161 
00162 class PCI_SSTDevice:public PCI_Device {
00163 private:
00164         static const Bit16u vendor=0x121a;      // 3dfx
00165         Bit16u oscillator_ctr;
00166         Bit16u pci_ctr;
00167 public:
00168         PCI_SSTDevice(Bitu type):PCI_Device(vendor,(type==2)?0x0002:0x0001) {
00169                 oscillator_ctr=0;
00170                 pci_ctr=0;
00171 
00172                 // init (3dfx voodoo)
00173                 config[0x08] = 0x02;    // revision
00174                 config[0x09] = 0x00;    // interface
00175                 config[0x0a] = 0x00;    // subclass code (video/graphics controller)
00176                 config[0x0b] = 0x04;    // class code (multimedia device)
00177                 config[0x0e] = 0x00;    // header type (other)
00178 
00179                 // reset
00180                 config[0x04] = 0x02;    // command register (memory space enabled)
00181                 config[0x05] = 0x00;
00182                 config[0x06] = 0x80;    // status register (fast back-to-back)
00183                 config[0x07] = 0x00;
00184 
00185                 config[0x3c] = 0xff;    // no irq
00186 
00187                 host_writew(config_writemask+0x04,0x0123);      /* allow changing mem/io enable, B2B enable, and VGA palette snoop */
00188 
00189                 host_writed(config_writemask+0x10,0xFF000000);  /* BAR0: memory resource 16MB aligned */
00190                 host_writed(config+0x10,(((Bit32u)VOODOO_INITIAL_LFB)&0xfffffff0) | 0x8);
00191 
00192                 if (getDeviceID() >= 2) {
00193                         config[0x40] = 0x00;
00194                         config[0x41] = 0x40;    // voodoo2 revision ID (rev4)
00195                         config[0x42] = 0x01;
00196                         config[0x43] = 0x00;
00197                 }
00198         }
00199 
00200         virtual void config_write(Bit8u regnum,Bitu iolen,Bit32u value) {
00201                 if (iolen == 1) {
00202             const unsigned char mask = config_writemask[regnum];
00203             const unsigned char nmask = ~mask;
00204 
00205                         /* configuration write masks apply here as well */
00206                         config[regnum] =
00207                 ((unsigned char)value & mask) +
00208                                 (config[regnum] & nmask);
00209 
00210                         switch (regnum) { /* FIXME: I hope I ported this right --J.C. */
00211                                 case 0x10:
00212                                 case 0x11:
00213                                 case 0x12:
00214                                 case 0x13:
00215                                         VOODOO_PCI_SetLFB(host_readd(config+0x10)&0xfffffff0UL); /* need to act on the new (masked off) value */
00216                                         break;
00217                                 case 0x40:
00218                                         VOODOO_PCI_InitEnable(value&7);
00219                                         break;
00220                                 case 0xc0:
00221                                         VOODOO_PCI_Enable(true);
00222                                         break;
00223                                 case 0xe0:
00224                                         VOODOO_PCI_Enable(false);
00225                                         break;
00226                                 default:
00227                                         break;
00228                         }
00229                 }
00230                 else {
00231                         PCI_Device::config_write(regnum,iolen,value); /* which will break down I/O into 8-bit */
00232                 }
00233         }
00234         virtual Bit32u config_read(Bit8u regnum,Bitu iolen) {
00235                 if (iolen == 1) {
00236                         switch (regnum) {
00237                                 case 0x4c: /* FIXME: I hope I ported this right --J.C. */
00238                                 case 0x4d:
00239                                 case 0x4e:
00240                                 case 0x4f:
00241                                         LOG(LOG_PCI,LOG_DEBUG)("SST ParseReadRegister STATUS %x",regnum);
00242                                         break;
00243 
00244                                 case 0x54: /* FIXME: I hope I ported this right --J.C. */
00245                                         if (getDeviceID() >= 2) {
00246                                                 oscillator_ctr++;
00247                                                 pci_ctr--;
00248                                                 return (oscillator_ctr | (((unsigned long)pci_ctr<<16ul) & 0x0fff0000ul)) & 0xffu;
00249                                         }
00250                                         break;
00251                                 case 0x55:
00252                                         if (getDeviceID() >= 2)
00253                                                 return ((oscillator_ctr | (((unsigned long)pci_ctr<<16ul) & 0x0fff0000ul)) >> 8ul) & 0xffu;
00254                                         break;
00255                                 case 0x56:
00256                                         if (getDeviceID() >= 2)
00257                                                 return ((oscillator_ctr | (((unsigned long)pci_ctr<<16ul) & 0x0fff0000ul)) >> 16ul) & 0xffu;
00258                                         break;
00259                                 case 0x57:
00260                                         if (getDeviceID() >= 2)
00261                                                 return ((oscillator_ctr | (((unsigned long)pci_ctr<<16ul) & 0x0fff0000ul)) >> 24ul) & 0xffu;
00262                                         break;
00263                                 default:
00264                                         break;
00265                         }
00266 
00267                         return config[regnum];
00268                 }
00269                 else {
00270                         return PCI_Device::config_read(regnum,iolen); /* which will break down I/O into 8-bit */
00271                 }
00272         }
00273 };
00274 
00275 static bool initialized = false;
00276 
00277 static IO_WriteHandleObject PCI_WriteHandler[5];
00278 static IO_ReadHandleObject PCI_ReadHandler[5];
00279 
00280 static CALLBACK_HandlerObject callback_pci;
00281 
00282 static PhysPt GetPModeCallbackPointer(void) {
00283         return Real2Phys(callback_pci.Get_RealPointer());
00284 }
00285 
00286 static bool IsInitialized(void) {
00287         return initialized;
00288 }
00289 
00290 // set up port handlers and configuration data
00291 static void InitializePCI(void) {
00292         // log
00293         LOG(LOG_MISC,LOG_DEBUG)("InitializePCI(): reinitializing PCI bus emulation");
00294 
00295         // install PCI-addressing ports
00296         PCI_WriteHandler[0].Install(0xcf8,write_pci_addr,IO_MD);
00297         PCI_ReadHandler[0].Install(0xcf8,read_pci_addr,IO_MD);
00298         // install PCI-register read/write handlers
00299         for (Bitu ct=0;ct<4;ct++) {
00300                 PCI_WriteHandler[1+ct].Install(0xcfc+ct,write_pci,IO_MB);
00301                 PCI_ReadHandler[1+ct].Install(0xcfc+ct,read_pci,IO_MB);
00302         }
00303 
00304         callback_pci.Install(&PCI_PM_Handler,CB_IRETD,"PCI PM");
00305 
00306         initialized=true;
00307 }
00308 
00309 bool UnregisterPCIDevice(PCI_Device* device) {
00310         unsigned int bus,dev;
00311 
00312         for (bus=0;bus < PCI_MAX_PCIBUSSES;bus++) {
00313                 for (dev=0;dev < PCI_MAX_PCIDEVICES;dev++) {
00314                         if (pci_devices[bus][dev] == device) {
00315                                 pci_devices[bus][dev] = NULL;
00316                                 return true;
00317                         }
00318                 }
00319         }
00320 
00321         return false;
00322 }
00323 
00324 // register PCI device to bus and setup data
00325 Bits RegisterPCIDevice(PCI_Device* device, Bits bus=-1, Bits slot=-1) {
00326         if (device == NULL) return -1;
00327         if (bus >= PCI_MAX_PCIBUSSES) return -1;
00328         if (slot >= PCI_MAX_PCIDEVICES) return -1;
00329 
00330         if (!initialized) InitializePCI();
00331 
00332         if (bus < 0 || slot < 0) {
00333                 Bits try_bus,try_slot;
00334 
00335                 try_bus = (bus < 0) ? 0 : bus;
00336                 try_slot = (slot < 0) ? 0 : slot; /* NTS: Most PCI implementations have a motherboard chipset or PCI bus device at slot 0 */
00337                 while (pci_devices[try_bus][try_slot] != NULL) {
00338                         if (slot >= 0 || (try_slot+1) >= PCI_MAX_PCIDEVICES) {
00339                                 if (slot < 0) try_slot = 0;
00340                                 try_bus++;
00341 
00342                                 if (bus >= 0 || try_bus >= PCI_MAX_PCIBUSSES)
00343                                         break;
00344                         }
00345                         else if (slot < 0) {
00346                                 try_slot++;
00347                         }
00348                 }
00349 
00350                 bus = try_bus;
00351                 slot = try_slot;
00352         }
00353 
00354         if (bus >= PCI_MAX_PCIBUSSES || slot >= PCI_MAX_PCIDEVICES)
00355                 return -1;
00356 
00357         if (pci_devices[bus][slot] != NULL) E_Exit("PCI interface error: attempted to fill slot already taken");
00358         pci_devices[bus][slot]=device;
00359         return slot;
00360 }
00361 
00362 static void Deinitialize(void) {
00363         initialized=false;
00364         pci_caddress=0;
00365 
00366         // install PCI-addressing ports
00367         PCI_WriteHandler[0].Uninstall();
00368         PCI_ReadHandler[0].Uninstall();
00369         // install PCI-register read/write handlers
00370         for (Bitu ct=0;ct<4;ct++) {
00371                 PCI_WriteHandler[1+ct].Uninstall();
00372                 PCI_ReadHandler[1+ct].Uninstall();
00373         }
00374 
00375         // remove callback
00376         callback_pci.Uninstall();
00377 
00378         // disconnect all PCI devices
00379         for (Bitu bus=0;bus<PCI_MAX_PCIBUSSES;bus++) {
00380                 for (Bitu i=0;i < PCI_MAX_PCIDEVICES;i++) {
00381                         if (pci_devices[bus][i] != NULL) {
00382                                 delete pci_devices[bus][i];
00383                                 pci_devices[bus][i] = NULL;
00384                         }
00385                 }
00386         }
00387 }
00388 
00389 static PCI_Device *S3_PCI=NULL;
00390 static PCI_Device *SST_PCI=NULL;
00391 
00392 extern bool enable_pci_vga;
00393 
00394 void PCI_AddSVGAS3_Device(void) {
00395         if (!pcibus_enable || !enable_pci_vga) return;
00396 
00397         if (S3_PCI == NULL) {
00398                 if ((S3_PCI=new PCI_VGADevice()) == NULL)
00399                         return;
00400 
00401                 RegisterPCIDevice(S3_PCI);
00402         }
00403 }
00404 
00405 void PCI_RemoveSVGAS3_Device(void) {
00406         if (S3_PCI != NULL) {
00407                 UnregisterPCIDevice(S3_PCI);
00408                 S3_PCI = NULL;
00409         }
00410 }
00411 
00412 void PCI_AddSST_Device(Bitu type) {
00413         if (!pcibus_enable) return;
00414 
00415         if (SST_PCI == NULL) {
00416                 Bitu ctype = 1;
00417 
00418                 switch (type) {
00419                         case 1:
00420                         case 2:
00421                                 ctype = type;
00422                                 break;
00423                         default:
00424                                 LOG(LOG_PCI,LOG_WARN)("PCI:SST: Invalid board type %x specified",(int)type);
00425                                 break;
00426                 }
00427 
00428                 LOG(LOG_MISC,LOG_DEBUG)("Initializing Voodoo/3DFX PCI device");
00429                 if ((SST_PCI=new PCI_SSTDevice(ctype)) == NULL)
00430                         return;
00431 
00432                 RegisterPCIDevice(SST_PCI);
00433         }
00434 }
00435 
00436 void PCI_RemoveSST_Device(void) {
00437         if (SST_PCI != NULL) {
00438                 UnregisterPCIDevice(SST_PCI);
00439                 delete SST_PCI;
00440                 SST_PCI = NULL;
00441         }
00442 }
00443 
00444 PhysPt PCI_GetPModeInterface(void) {
00445         if (!pcibus_enable) return 0;
00446         return GetPModeCallbackPointer();
00447 }
00448 
00449 bool PCI_IsInitialized() {
00450         return IsInitialized();
00451 }
00452 
00453 void PCI_OnPowerOn(Section *sec) {
00454     (void)sec;//UNUSED
00455         Section_prop * secprop=static_cast<Section_prop *>(control->GetSection("dosbox"));
00456         assert(secprop != NULL);
00457 
00458         Deinitialize();
00459 
00460         pcibus_enable = secprop->Get_bool("enable pci bus");
00461         if (pcibus_enable) InitializePCI();
00462 }
00463 
00464 void PCI_ShutDown(Section* sec) {
00465     (void)sec;//UNUSED
00466         Deinitialize();
00467 }
00468 
00469 void PCIBUS_Init() {
00470         Section_prop * secprop=static_cast<Section_prop *>(control->GetSection("dosbox"));
00471         assert(secprop != NULL);
00472 
00473         LOG(LOG_MISC,LOG_DEBUG)("Initializing PCI bus emulation");
00474 
00475         initialized=false;
00476         for (Bitu bus=0;bus<PCI_MAX_PCIBUSSES;bus++)
00477                 for (Bitu devct=0;devct<PCI_MAX_PCIDEVICES;devct++)
00478                         pci_devices[bus][devct]=NULL;
00479 
00480         AddExitFunction(AddExitFunctionFuncPair(PCI_ShutDown),false);
00481         AddVMEventFunction(VM_EVENT_POWERON,AddVMEventFunctionFuncPair(PCI_OnPowerOn));
00482 
00483     /* NTS: PCI emulation does not have to change anything when entering into PC-98 mode.
00484      *      I/O ports for PCI bus control are the SAME on both platforms (0xCF8-0xCFF). */
00485 }
00486