PCIDevice.cpp

Go to the documentation of this file.
00001 /* ES40 emulator.
00002  * Copyright (C) 2007-2008 by the ES40 Emulator Project
00003  *
00004  * WWW    : http://sourceforge.net/projects/es40
00005  * E-mail : camiel@camicom.com
00006  * 
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  * 
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  * 
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
00020  * 
00021  * Although this is not required, the author would appreciate being notified of, 
00022  * and receiving any modifications you may make to the source code that might serve
00023  * the general public.
00024  */
00025 
00085 #include "StdAfx.h"
00086 #include "PCIDevice.h"
00087 #include "System.h"
00088 
00089 CPCIDevice::CPCIDevice(CConfigurator* cfg, CSystem* c, int pcibus, int pcidev) : CSystemComponent(cfg, c)
00090 {
00091   int i;
00092 
00093   int j;
00094 
00095   for(i = 0; i < 8; i++)
00096   {
00097     device_at[i] = false;
00098     for(j = 0; j < 8; j++)
00099       pci_range_is_io[i][j] = false;
00100   }
00101 
00102   for(i = 0; i < MAX_DEV_RANGES; i++)
00103     dev_range_is_io[i] = false;
00104 
00105   myPCIBus = pcibus;
00106   myPCIDev = pcidev;
00107 }
00108 
00109 CPCIDevice::~CPCIDevice(void)
00110 { }
00111 
00112 void CPCIDevice::add_function(int func, u32 data[64], u32 mask[64])
00113 {
00114   memcpy(std_config_data[func], data, 64 * sizeof(u32));
00115   memcpy(std_config_mask[func], mask, 64 * sizeof(u32));
00116 #if defined(ES40_BIG_ENDIAN)
00117   int i;
00118   for(i = 0; i < 64; i++)
00119   {
00120     std_config_data[func][i] = endian_32(std_config_data[func][i]);
00121     std_config_mask[func][i] = endian_32(std_config_mask[func][i]);
00122   }
00123 #endif
00124   device_at[func] = true;
00125 }
00126 
00127 void CPCIDevice::add_legacy_io(int id, u32 base, u32 length)
00128 {
00129   dev_range_is_io[id] = true;
00130   cSystem->RegisterMemory(this, id,
00131                           U64(0x00000801fc000000) + (U64(0x0000000200000000) * myPCIBus) + base,
00132                                 length);
00133 }
00134 
00135 void CPCIDevice::add_legacy_mem(int id, u32 base, u32 length)
00136 {
00137   dev_range_is_io[id] = false;
00138   cSystem->RegisterMemory(this, id,
00139                           U64(0x0000080000000000) + (U64(0x0000000200000000) * myPCIBus) + base,
00140                                 length);
00141 }
00142 
00143 u32 CPCIDevice::config_read(int func, u32 address, int dsize)
00144 {
00145   u8*   x;
00146 
00147   u32   data = 0;
00148 
00149   x = (u8*) pci_state.config_data[func];
00150   x += address;
00151 
00152   switch(dsize)
00153   {
00154   case 8:   data = endian_8(*x); break;
00155   case 16:  data = endian_16(*((u16*) x)); break;
00156   case 32:  data = endian_32(*((u32*) x)); break;
00157   }
00158 
00159   data = config_read_custom(func, address, dsize, data);
00160 
00161   //  printf("%s(%s).%d config read  %d bytes @ %x = %x\n",myCfg->get_myName(), myCfg->get_myValue(), func,dsize/8,address, data);
00162   return data;
00163 }
00164 
00165 void CPCIDevice::config_write(int func, u32 address, int dsize, u32 data)
00166 {
00167 
00168   //  printf("%s(%s).%d config write %d bytes @ %x = %x\n",myCfg->get_myName(), myCfg->get_myValue(), func,dsize/8,address, data);
00169   u8*   x;
00170   u8*   y;
00171 
00172   u32   mask = 0;
00173   u32   old_data = 0;
00174   u32   new_data = 0;
00175 
00176   x = (u8*) pci_state.config_data[func];
00177   x += address;
00178   y = (u8*) pci_state.config_mask[func];
00179   y += address;
00180 
00181 #if defined(DEBUG_PCI)
00182   if(address == 0x3c && (data & 0xff) != 0xff)
00183     printf("%s.%d PCI Interrupt set to %02x.\n", devid_string, func, data & 0xff);
00184 #endif
00185   switch(dsize)
00186   {
00187   case 8:
00188     data = endian_8(data);
00189     old_data = (*x) & 0xff;
00190     mask = (*y) & 0xff;
00191     new_data = (old_data &~mask) | data & mask;
00192     *x = (u8) new_data;
00193     break;
00194 
00195   case 16:
00196     data = endian_16(data);
00197     old_data = (*((u16*) x)) & 0xffff;
00198     mask = (*((u16*) y)) & 0xffff;
00199     new_data = (old_data &~mask) | data & mask;
00200     *((u16*) x) = (u16) new_data;
00201     break;
00202 
00203   case 32:
00204     data = endian_32(data);
00205     old_data = (*((u32*) x));
00206     mask = (*((u32*) y));
00207     new_data = (old_data &~mask) | data & mask;
00208     *((u32*) x) = new_data;
00209     break;
00210   }
00211 
00212   if(dsize == 32 && ((data & mask) != mask) && ((data & mask) != 0))
00213   {
00214     switch(address)
00215     {
00216     case 0x10:
00217     case 0x14:
00218     case 0x18:
00219     case 0x1c:
00220     case 0x20:
00221     case 0x24:
00222       register_bar(func, (address - 0x10) / 4, endian_32(new_data),
00223                    endian_32(mask));
00224       break;
00225 
00226     case 0x30:
00227       register_bar(func, 6, endian_32(new_data), endian_32(mask));
00228       break;
00229     }
00230   }
00231 
00232   config_write_custom(func, address, dsize, old_data, new_data, data);
00233 }
00234 
00235 void CPCIDevice::register_bar(int func, int bar, u32 data, u32 mask)
00236 {
00237   int id = PCI_RANGE_BASE + (func * 8) + bar;
00238   u32 length = ((~mask) | 1) + 1;
00239   u64 t;
00240 
00241   if((data & 1) && bar != 6)
00242   {
00243 
00244     // io space
00245     pci_range_is_io[func][bar] = true;
00246 
00247     cSystem->RegisterMemory(this, PCI_RANGE_BASE + (func * 8) + bar,
00248                             t = U64(0x00000801fc000000) + (U64(0x0000000200000000) * myPCIBus) +
00249                                     (data &~0x3), length);
00250 #if defined(DEBUG_PCI)
00251     printf("%s(%s).%d PCI BAR %d set to IO  % "LL "x, len %x.\n",
00252            myCfg->get_myName(), myCfg->get_myValue(), func, bar, t, length);
00253 #endif
00254   }
00255   else if((data & 1) || bar != 6)
00256   {
00257 
00258     // io space
00259     pci_range_is_io[func][bar] = true;
00260 
00261     cSystem->RegisterMemory(this, PCI_RANGE_BASE + (func * 8) + bar,
00262                             t = U64(0x0000080000000000) + (U64(0x0000000200000000) * myPCIBus) +
00263                                     (data &~0xf), length);
00264 #if defined(DEBUG_PCI)
00265     printf("%s(%s).%d PCI BAR %d set to MEM % "LL "x, len %x.\n",
00266            myCfg->get_myName(), myCfg->get_myValue(), func, bar, t, length);
00267 #endif
00268   }
00269   else
00270   {
00271 
00272     // disabled...
00273 #if defined(DEBUG_PCI)
00274     printf("%s(%s).%d PCI BAR %d should be disabled...\n", myCfg->get_myName(),
00275            myCfg->get_myValue(), func, bar);
00276 #endif
00277   }
00278 }
00279 
00280 void CPCIDevice::ResetPCI()
00281 {
00282   int i;
00283 
00284   for(i = 0; i < 8; i++)
00285   {
00286     if(device_at[i])
00287     {
00288       cSystem->RegisterMemory(this, PCI_RANGE_BASE + (i * 8) + 7,
00289                               U64(0x00000801fe000000) + (U64(0x0000000200000000) * myPCIBus) +
00290                                       (U64(0x0000000000000800) * myPCIDev) +
00291                                     (U64(0x0000000000000100) * i), 0x100);
00292       memcpy(pci_state.config_data[i], std_config_data[i], 64 * sizeof(u32));
00293       memcpy(pci_state.config_mask[i], std_config_mask[i], 64 * sizeof(u32));
00294 
00295       config_write(i, 0x10, 32, endian_32(pci_state.config_data[i][4]));
00296       config_write(i, 0x14, 32, endian_32(pci_state.config_data[i][5]));
00297       config_write(i, 0x18, 32, endian_32(pci_state.config_data[i][6]));
00298       config_write(i, 0x1c, 32, endian_32(pci_state.config_data[i][7]));
00299       config_write(i, 0x20, 32, endian_32(pci_state.config_data[i][8]));
00300       config_write(i, 0x24, 32, endian_32(pci_state.config_data[i][9]));
00301       config_write(i, 0x30, 32, endian_32(pci_state.config_data[i][12]));
00302     }
00303   }
00304 }
00305 
00306 u64 CPCIDevice::ReadMem(int index, u64 address, int dsize)
00307 {
00308   int func;
00309   int bar;
00310 
00311   if(dsize == 64)
00312     return ReadMem(index, address, 32) | (((u64) ReadMem(index, address + 4, 32)) << 32);
00313 
00314   if(dsize != 8 && dsize != 16 && dsize != 32)
00315   {
00316     FAILURE_5(InvalidArgument,
00317               "ReadMem: %s(%s) Unsupported dsize %d. (%d, %"LL "x)\n",
00318               myCfg->get_myName(), myCfg->get_myValue(), dsize, index, address);
00319   }
00320 
00321   if(index < PCI_RANGE_BASE)
00322   {
00323     if(dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(1)))
00324     {
00325       printf("%s(%s) Legacy IO access with IO disabled from PCI config.\n",
00326              myCfg->get_myName(), myCfg->get_myValue());
00327       return 0;
00328     }
00329 
00330     if(!dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(2)))
00331     {
00332       printf("%s(%s) Legacy memory access with memory disabled from PCI config.\n",
00333              myCfg->get_myName(), myCfg->get_myValue());
00334       return 0;
00335     }
00336 
00337     //    printf("%s(%s) Calling ReadMem_Legacy(%d).\n",myCfg->get_myName(), myCfg->get_myValue(), index);
00338     return ReadMem_Legacy(index, (u32) address, dsize);
00339   }
00340 
00341   index -= PCI_RANGE_BASE;
00342 
00343   bar = index & 7;
00344   func = (index / 8) & 7;
00345 
00346   if(bar == 7)
00347     return config_read(func, (u32) address, dsize);
00348 
00349   if(pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(1)))
00350   {
00351     printf("%s(%s).%d PCI IO access with IO disabled from PCI config.\n",
00352            myCfg->get_myName(), myCfg->get_myValue(), func);
00353     return 0;
00354   }
00355 
00356   if(!pci_range_is_io[func][bar]
00357    && !(pci_state.config_data[func][1] & endian_32(2)))
00358   {
00359     printf("%s(%s).%d PCI memory access with memory disabled from PCI config.\n",
00360            myCfg->get_myName(), myCfg->get_myValue(), func);
00361     return 0;
00362   }
00363 
00364   //  printf("%s(%s).%d Calling ReadMem_Bar(%d,%d).\n",myCfg->get_myName(), myCfg->get_myValue(), func,func,bar);
00365   return ReadMem_Bar(func, bar, (u32) address, dsize);
00366 }
00367 
00368 void CPCIDevice::WriteMem(int index, u64 address, int dsize, u64 data)
00369 {
00370   int func;
00371   int bar;
00372 
00373   if(dsize == 64)
00374   {
00375     WriteMem(index, address, 32, data & U64(0xffffffff));
00376     WriteMem(index, address + 4, 32, (data >> 32) & U64(0xffffffff));
00377     return;
00378   }
00379 
00380   if(dsize != 8 && dsize != 16 && dsize != 32)
00381   {
00382     FAILURE_6(InvalidArgument,
00383               "WriteMem: %s(%s) Unsupported dsize %d. (%d,%"LL "x,%"LL "x)\n",
00384               myCfg->get_myName(), myCfg->get_myValue(), dsize, index, address,
00385               data);
00386   }
00387 
00388   if(index < PCI_RANGE_BASE)
00389   {
00390     if(dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(1)))
00391     {
00392       printf("%s(%s) Legacy IO access with IO disabled from PCI config.\n",
00393              myCfg->get_myName(), myCfg->get_myValue());
00394       return;
00395     }
00396 
00397     if(!dev_range_is_io[index] && !(pci_state.config_data[0][1] & endian_32(2)))
00398     {
00399       printf("%s(%s) Legacy memory access with memory disabled from PCI config.\n",
00400              myCfg->get_myName(), myCfg->get_myValue());
00401       return;
00402     }
00403 
00404     WriteMem_Legacy(index, (u32) address, dsize, (u32) data);
00405     return;
00406   }
00407 
00408   index -= PCI_RANGE_BASE;
00409 
00410   bar = index & 7;
00411   func = (index / 8) & 7;
00412 
00413   if(bar == 7)
00414   {
00415     config_write(func, (u32) address, dsize, (u32) data);
00416     return;
00417   }
00418 
00419   if(pci_range_is_io[func][bar] && !(pci_state.config_data[func][1] & endian_32(1)))
00420   {
00421     printf("%s(%s).%d PCI IO access with IO disabled from PCI config.\n",
00422            myCfg->get_myName(), myCfg->get_myValue(), func);
00423     return;
00424   }
00425 
00426   if(!pci_range_is_io[func][bar]
00427    && !(pci_state.config_data[func][1] & endian_32(2)))
00428   {
00429     printf("%s(%s).%d PCI memory access with memory disabled from PCI config.\n",
00430            myCfg->get_myName(), myCfg->get_myValue(), func);
00431     return;
00432   }
00433 
00434   WriteMem_Bar(func, bar, (u32) address, dsize, (u32) data);
00435 }
00436 
00437 bool CPCIDevice::do_pci_interrupt(int func, bool asserted)
00438 {
00439   if((endian_32(pci_state.config_data[func][0x0f]) & 0xff) != 0xff)
00440   {
00441     cSystem->interrupt(endian_32(pci_state.config_data[func][0x0f]) & 0xff,
00442                        asserted);
00443     return true;
00444   }
00445   else
00446     return false;
00447 }
00448 
00449 static u32  pci_magic1 = 0xC1095A78;
00450 static u32  pci_magic2 = 0x87A5901C;
00451 
00455 int CPCIDevice::SaveState(FILE* f)
00456 {
00457   long  ss = sizeof(pci_state);
00458 
00459   fwrite(&pci_magic1, sizeof(u32), 1, f);
00460   fwrite(&ss, sizeof(long), 1, f);
00461   fwrite(&pci_state, sizeof(pci_state), 1, f);
00462   fwrite(&pci_magic2, sizeof(u32), 1, f);
00463   printf("%s: %d PCI bytes saved.\n", devid_string, (int) ss);
00464   return 0;
00465 }
00466 
00470 int CPCIDevice::RestoreState(FILE* f)
00471 {
00472   long    ss;
00473   u32     m1;
00474   u32     m2;
00475   size_t  r;
00476 
00477   r = fread(&m1, sizeof(u32), 1, f);
00478   if(r != 1)
00479   {
00480     printf("%s: unexpected end of file!\n", devid_string);
00481     return -1;
00482   }
00483 
00484   if(m1 != pci_magic1)
00485   {
00486     printf("%s: PCI MAGIC 1 does not match!\n", devid_string);
00487     return -1;
00488   }
00489 
00490   fread(&ss, sizeof(long), 1, f);
00491   if(r != 1)
00492   {
00493     printf("%s: unexpected end of file!\n", devid_string);
00494     return -1;
00495   }
00496 
00497   if(ss != sizeof(pci_state))
00498   {
00499     printf("%s: PCI STRUCT SIZE does not match!\n", devid_string);
00500     return -1;
00501   }
00502 
00503   fread(&pci_state, sizeof(pci_state), 1, f);
00504   if(r != 1)
00505   {
00506     printf("%s: unexpected end of file!\n", devid_string);
00507     return -1;
00508   }
00509 
00510   r = fread(&m2, sizeof(u32), 1, f);
00511   if(r != 1)
00512   {
00513     printf("%s: unexpected end of file!\n", devid_string);
00514     return -1;
00515   }
00516 
00517   if(m2 != pci_magic2)
00518   {
00519     printf("%s: PCI MAGIC 1 does not match!\n", devid_string);
00520     return -1;
00521   }
00522 
00523   printf("%s: %d PCI bytes restored.\n", devid_string, (int) ss);
00524   return 0;
00525 }
00526 
00527 u32 CPCIDevice::ReadMem_Legacy(int index, u32 address, int dsize)
00528 {
00529   FAILURE_2(NotImplemented, "%s(%s) No Legacy read handler installed",
00530             myCfg->get_myName(), myCfg->get_myValue());
00531 }
00532 
00533 void CPCIDevice::WriteMem_Legacy(int index, u32 address, int dsize, u32 data)
00534 {
00535   FAILURE_2(NotImplemented, "%s(%s) No Legacy write handler installed",
00536             myCfg->get_myName(), myCfg->get_myValue());
00537 }
00538 
00539 u32 CPCIDevice::ReadMem_Bar(int func, int bar, u32 address, int dsize)
00540 {
00541   FAILURE_3(NotImplemented, "%s(%s).%d No BAR read handler installed",
00542             myCfg->get_myName(), myCfg->get_myValue(), func);
00543 }
00544 
00545 void CPCIDevice::WriteMem_Bar(int func, int bar, u32 address, int dsize, u32 data)
00546 {
00547   FAILURE_3(NotImplemented, "%s(%s).%d No BAR write handler installed",
00548             myCfg->get_myName(), myCfg->get_myValue(), func);
00549 }
00550 
00558 void CPCIDevice::do_pci_read(u32 address, void*  dest, size_t element_size,
00559                              size_t element_count)
00560 {
00561   size_t  el;
00562   char*   dst = (char*) dest;
00563 
00564   if(element_count == 0)
00565     return;
00566 
00567   // get the 64-bit system wide address
00568   u64 phys_addr = cSystem->PCI_Phys(myPCIBus, address);
00569 
00570   // if there is only one element to read, this is a simple ReadMem operation.
00571   if(element_count == 1)
00572   {
00573     switch(element_size)
00574     {
00575     case 1:
00576       * (u8*) dest = (u8) cSystem->ReadMem(phys_addr, 8, this);
00577       break;
00578 
00579     case 2:
00580       * (u16*) dest = (u16) cSystem->ReadMem(phys_addr, 16, this);
00581       break;
00582 
00583     case 4:
00584       * (u32*) dest = (u32) cSystem->ReadMem(phys_addr, 32, this);
00585       break;
00586 
00587     default:
00588       FAILURE(InvalidArgument, "Strange element size");
00589     }
00590 
00591     return;
00592   }
00593 
00594 #if defined(ES40_BIG_ENDIAN)
00595 
00596   // if this is a big-endian host machine, the memcpy method is only valid
00597   // if we're transferring bytes. Otherwise, endian-conversions need to be done.
00598   if(element_size == 1)
00599   {
00600 #endif
00601 
00602     // get a pointer to system memory if the address is inside main memory
00603     char*   memptr = cSystem->PtrToMem(phys_addr);
00604 
00605     // if the address is inside system memory, a simple memcpy operation is
00606     // all that is needed.
00607     if(memptr)
00608     {
00609       memcpy(dest, memptr, element_size * element_count);
00610       return;
00611     }
00612 
00613 #if defined(ES40_BIG_ENDIAN)
00614   }
00615 #endif
00616 
00617   // outside main memory, or inside main memory with endian-conversion
00618   // required we need to do the transfer element-by-element.
00619   switch(element_size)
00620   {
00621   case 1:
00622     for(el = 0; el < element_count; el++)
00623     {
00624       *(u8*) dst = (u8) cSystem->ReadMem(phys_addr, 8, this);
00625       dst++;
00626       phys_addr++;
00627     }
00628     break;
00629 
00630   case 2:
00631     {
00632       *(u16*) dst = endian_16((u16) cSystem->ReadMem(phys_addr, 16, this));
00633       dst += 2;
00634       phys_addr += 2;
00635     }
00636     break;
00637 
00638   case 4:
00639     {
00640       *(u32*) dst = endian_32((u32) cSystem->ReadMem(phys_addr, 32, this));
00641       dst += 4;;
00642       phys_addr += 4;
00643     }
00644     break;
00645 
00646   default:
00647     FAILURE(InvalidArgument, "Strange element size");
00648   }
00649 }
00650 
00658 void CPCIDevice::do_pci_write(u32 address, void*  source, size_t element_size,
00659                               size_t element_count)
00660 {
00661   size_t  el;
00662   char*   src = (char*) source;
00663 
00664   if(element_count == 0)
00665     return;
00666 
00667   // get the 64-bit system wide address
00668   u64 phys_addr = cSystem->PCI_Phys(myPCIBus, address);
00669 
00670   // if there is only one element to read, this is a simple ReadMem operation.
00671   if(element_count == 1)
00672   {
00673     switch(element_size)
00674     {
00675     case 1:   cSystem->WriteMem(phys_addr, 8, *(u8*) source, this); break;
00676     case 2:   cSystem->WriteMem(phys_addr, 16, *(u16*) source, this); break;
00677     case 4:   cSystem->WriteMem(phys_addr, 32, *(u32*) source, this); break;
00678     default:  FAILURE(InvalidArgument, "Strange element size");
00679     }
00680 
00681     return;
00682   }
00683 
00684 #if defined(ES40_BIG_ENDIAN)
00685 
00686   // if this is a big-endian host machine, the memcpy method is only valid
00687   // if we're transferring bytes. Otherwise, endian-conversions need to be done.
00688   if(element_size == 1)
00689   {
00690 #endif
00691 
00692     // get a pointer to system memory if the address is inside main memory
00693     char*   memptr = cSystem->PtrToMem(phys_addr);
00694 
00695     // if the address is inside system memory, a simple memcpy operation is
00696     // all that is needed.
00697     if(memptr)
00698     {
00699       memcpy(memptr, source, element_size * element_count);
00700       return;
00701     }
00702 
00703 #if defined(ES40_BIG_ENDIAN)
00704   }
00705 #endif
00706 
00707   // outside main memory, or inside main memory with endian-conversion
00708   // required we need to do the transfer element-by-element.
00709   switch(element_size)
00710   {
00711   case 1:
00712     for(el = 0; el < element_count; el++)
00713     {
00714       cSystem->WriteMem(phys_addr, 8, *(u8*) src, this);
00715       src++;
00716       phys_addr++;
00717     }
00718     break;
00719 
00720   case 2:
00721     {
00722       cSystem->WriteMem(phys_addr, 16, endian_16(*(u16*) src), this);
00723       src += 2;
00724       phys_addr += 2;
00725     }
00726     break;
00727 
00728   case 4:
00729     {
00730       cSystem->WriteMem(phys_addr, 32, endian_32(*(u32*) src), this);
00731       src += 4;;
00732       phys_addr += 4;
00733     }
00734     break;
00735 
00736   default:
00737     FAILURE(InvalidArgument, "Strange element size");
00738   }
00739 }

SourceForge.net Logo
Project space on SourceForge.net