Commit | Line | Data |
---|---|---|
920dae64 AT |
1 | // ========== Copyright Header Begin ========================================== |
2 | // | |
3 | // OpenSPARC T2 Processor File: pcie_dev.cc | |
4 | // Copyright (c) 2006 Sun Microsystems, Inc. All Rights Reserved. | |
5 | // DO NOT ALTER OR REMOVE COPYRIGHT NOTICES. | |
6 | // | |
7 | // The above named program is free software; you can redistribute it and/or | |
8 | // modify it under the terms of the GNU General Public | |
9 | // License version 2 as published by the Free Software Foundation. | |
10 | // | |
11 | // The above named program is distributed in the hope that it will be | |
12 | // useful, but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | // General Public License for more details. | |
15 | // | |
16 | // You should have received a copy of the GNU General Public | |
17 | // License along with this work; if not, write to the Free Software | |
18 | // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. | |
19 | // | |
20 | // ========== Copyright Header End ============================================ | |
21 | #include "pcie.h" | |
22 | #include "arg.h" | |
23 | ||
24 | ||
25 | genericPcieDev::genericPcieDev(confHeaderType htype){ | |
26 | confSpace = new pciConfSpace(0x1000); | |
27 | //initConf(htype); | |
28 | ||
29 | busName = 0; | |
30 | busIf = 0; | |
31 | device = -1; | |
32 | function = -1; | |
33 | msiCapOffset = -1; | |
34 | pciExpCapOffset = -1; | |
35 | aerCapOffset = -1; | |
36 | aerCapable = false; | |
37 | msgDB = new pciExpMsgUtility(); | |
38 | errDB = new pciExpErrUtility(); | |
39 | } | |
40 | ||
41 | genericPcieDev::genericPcieDev(){ | |
42 | confSpace = new pciConfSpace(0x1000); | |
43 | ||
44 | busName = 0; | |
45 | busIf = 0; | |
46 | device = -1; | |
47 | function = -1; | |
48 | msiCapOffset = -1; | |
49 | pciExpCapOffset = -1; | |
50 | aerCapOffset = -1; | |
51 | aerCapable = false; | |
52 | msgDB = new pciExpMsgUtility(); | |
53 | errDB = new pciExpErrUtility(); | |
54 | } | |
55 | ||
56 | bool genericPcieDev::mapSpace(pcie_space space, uint64_t base, uint64_t size){ | |
57 | assert(busIf); | |
58 | return busIf->busif_map(devif_getName(),space, base, size); | |
59 | } | |
60 | bool genericPcieDev::unmapSpace(pcie_space space,uint64_t base){ | |
61 | assert(busIf); | |
62 | return busIf->busif_unmap(devif_getName(),space, base); | |
63 | } | |
64 | ||
65 | pcieCompleter genericPcieDev::devif_memAccess(bool wr, uint64_t addr, addrMd_xactnType mode, void * data,\ | |
66 | uint16_t length, uint8_t be, uint16_t reqId, tlp_X args, SAM_DeviceId *id){ | |
67 | if(id) | |
68 | *id = (dynamic_cast<Module*>(this))->samId; | |
69 | return pcieCompleter(UR,getId()); | |
70 | } | |
71 | ||
72 | pcieCompleter genericPcieDev::devif_ioAccess(bool wr, uint32_t addr, void * data, uint8_t be, \ | |
73 | uint16_t reqId, uint16_t length, tlp_X args, SAM_DeviceId *id){ | |
74 | if(id) | |
75 | *id = (dynamic_cast<Module*>(this))->samId; | |
76 | return pcieCompleter(UR,getId()); | |
77 | } | |
78 | ||
79 | pcieCompleter genericPcieDev::devif_msgAccess(uint8_t messageCode, msgRouting route, uint64_t tarIdOrAddr, uint16_t reqId, \ | |
80 | void * data, uint16_t length, \ | |
81 | uint16_t vendorId, uint32_t vendor_data, tlp_X args, SAM_DeviceId *id){ | |
82 | if(id) | |
83 | *id = (dynamic_cast<Module*>(this))->samId; | |
84 | return pcieCompleter(UR,getId()); | |
85 | } | |
86 | ||
87 | pcieCompleter genericPcieDev::devif_confAccess(bool wr, uint32_t offset, void * data, \ | |
88 | uint8_t be,uint16_t reqId, addrMd_xactnType tType, uint16_t length, tlp_X args, SAM_DeviceId *id){ | |
89 | ||
90 | assert(offset <= 0xfff); | |
91 | assert((be & 0xf0) == 0); | |
92 | assert(length == 1); | |
93 | ||
94 | if(id) | |
95 | *id = (dynamic_cast<Module*>(this))->samId; | |
96 | ||
97 | confSpace->confAccessByteE(offset, wr, (uint64_t *)data, be); | |
98 | devif_confAccessCb(wr,offset,be); | |
99 | return pcieCompleter(SC,getId()); | |
100 | } | |
101 | ||
102 | ||
103 | // parse the busname, device, functions args | |
104 | ||
105 | ||
106 | bool genericPcieDev::dev_parse_arg(const char *arg){ | |
107 | if (argval("bus", arg, &busName)) { | |
108 | Debug_more("%s: bus=<%s>\n", Here, busName); | |
109 | } else if (argval("dev", arg, &device)) { | |
110 | Debug_more("%s: dev=%d\n", Here, device); | |
111 | } else if (argval("fun", arg, &function)) { | |
112 | Debug_more("%s: fun=%d\n", Here, function); | |
113 | }else{ | |
114 | Debug_err("%s: WARNING: unrecognized arg '%s'\n", Here, arg); | |
115 | return false; | |
116 | } | |
117 | return true; | |
118 | } | |
119 | ||
120 | ||
121 | bool | |
122 | genericPcieDev::dev_check_args(){ | |
123 | if (!busName) { | |
124 | Debug_err("%s: ERROR: must specify a PCI bus\n",devif_getName()); | |
125 | return false; | |
126 | }else if(device == -1){ | |
127 | Debug_err("%s: ERROR: must specify device number\n", devif_getName()); | |
128 | return false; | |
129 | }else if(function == -1){ | |
130 | Debug_err("%s: ERROR: must specify function number\n", devif_getName()); | |
131 | return false; | |
132 | } | |
133 | return true; | |
134 | } | |
135 | ||
136 | void | |
137 | genericPcieDev::dev_init_done(int dl){ | |
138 | confSpace->set_debug_level(dl); | |
139 | pciDebug::set_debug_level(dl); | |
140 | if(aerCapOffset != -1) | |
141 | aerCapable = true; | |
142 | } | |
143 | ||
144 | ||
145 | void genericPcieDev::pcieDevInfo(){ | |
146 | printf("bus=<%s> dev=%d fun=%d\n",busName,device,function); | |
147 | printf("configuration space reg info:\n"); | |
148 | confSpace->print(); | |
149 | } | |
150 | ||
151 | ||
152 | ||
153 | // XXX the functions below "may" need some changes depending upon specific | |
154 | // PCIE needs. Need to do more research to be sure. | |
155 | bool genericPcieDev::dump(const char * dirname,const char *file){ | |
156 | char *filename; | |
157 | FILE *fp; | |
158 | ||
159 | if(file){ | |
160 | filename = new char[strlen(dirname) + strlen(file) + 6]; | |
161 | sprintf(filename,"%s/%s.pcie",dirname,file); | |
162 | ||
163 | fp = fopen(filename,"w"); | |
164 | if(!fp){ | |
165 | Debug_err("%s pci dump. fopen failed for %s\n",devif_getName(), filename); | |
166 | return false; | |
167 | } | |
168 | }else | |
169 | fp = stderr; | |
170 | ||
171 | pciConfReg * cr; | |
172 | ||
173 | for(int offset = 0; offset < 0x1000;){ | |
174 | if(offset == PCI_CONF_COMM){ | |
175 | offset += 2; | |
176 | continue; | |
177 | } | |
178 | cr = confSpace->getConfReg(offset); | |
179 | if(!cr) // hole | |
180 | offset++; | |
181 | else{ | |
182 | if (cr->size == 8) { | |
183 | assert(offset >= PCI_CONF_BASE0 && offset <= PCI_CONF_BASE5); | |
184 | ||
185 | pcieBaseAddrReg *bar = (pcieBaseAddrReg *)cr; | |
186 | fprintf(fp,"0x%08lx 0x%08lx 0x%08lx 0x%x %s %s\n",\ | |
187 | offset,bar->getVal(),bar->getMask(),4,bar->name, "Low"); | |
188 | fprintf(fp,"0x%08lx 0x%08lx 0x%08lx 0x%x %s %s\n",\ | |
189 | offset+4,bar->val_32To63,bar->mask_32To63,4,bar->name, "High"); | |
190 | } | |
191 | else { | |
192 | fprintf(fp,"0x%08lx 0x%08lx 0x%08lx 0x%x %s\n",\ | |
193 | offset,cr->getVal(),cr->getMask(),cr->size,cr->name); | |
194 | } | |
195 | offset += cr->size; | |
196 | } | |
197 | } | |
198 | ||
199 | cr = confSpace->getConfReg(PCI_CONF_COMM); | |
200 | fprintf(fp,"0x%08lx 0x%08lx 0x%08lx 0x%x %s\n",\ | |
201 | PCI_CONF_COMM,cr->getVal(),cr->getMask(),cr->size,cr->name); | |
202 | ||
203 | if(fp != stderr) | |
204 | fclose(fp); | |
205 | ||
206 | return true; | |
207 | } | |
208 | ||
209 | bool | |
210 | genericPcieDev::dev_module_added(const char *target_name){ | |
211 | if (busName && !strcmp(busName, target_name)) { | |
212 | busName = target_name; | |
213 | mmi_instance_t mod = mmi_get_instance(busName); | |
214 | busIf = (pcieBusIf*)mmi_get_interface(mod, PCIE_BUS_INTERFACE); | |
215 | if (!busIf) { | |
216 | Debug_err("%s: ERROR: can't find interface for PCIE bus <%s>\n", Here, busName); | |
217 | return false; | |
218 | }else if(!busIf->busif_addDevice(devif_getName(), device, function)) { | |
219 | Debug_err("%s: ERROR: could not add tp PCIE bus <%s>\n", Here, busName); | |
220 | return false; | |
221 | }else{ | |
222 | Debug_info("%s: bus <%s> connected: busif=%p\n", Here, busName, busIf); | |
223 | } | |
224 | } | |
225 | return true; | |
226 | } | |
227 | ||
228 | ||
229 | bool | |
230 | genericPcieDev::dev_module_deleted(const char *target_name){ | |
231 | if (busName && !strcmp(busName, target_name)) { | |
232 | busName = 0; | |
233 | busIf = 0; | |
234 | Debug_info("%s: bus <%s> disconnected\n", Here, busName); | |
235 | } | |
236 | return true; | |
237 | } | |
238 | ||
239 | ||
240 | ||
241 | // to be called after initPci() has been called | |
242 | bool genericPcieDev::restore(const char * dirname,const char * file){ | |
243 | char *filename = new char[strlen(dirname) + strlen(file) + 6]; | |
244 | sprintf(filename,"%s/%s.pcie",dirname,file); | |
245 | char name[128]; | |
246 | ||
247 | char buf[1024]; | |
248 | ||
249 | int off, size; | |
250 | uint32_t val; | |
251 | ||
252 | FILE *fp = fopen(filename,"r"); | |
253 | ||
254 | if(!fp){ | |
255 | Debug_err("%s pci restore. fopen failed for %s\n",devif_getBusName(), filename); | |
256 | return false; | |
257 | } | |
258 | ||
259 | pciConfReg * cr; | |
260 | char str[100]; | |
261 | bool is_commd = false; | |
262 | ||
263 | for(uint32_t offset = 0; offset < 0x1000;){ | |
264 | if(offset == PCI_CONF_COMM){ | |
265 | offset += 2; | |
266 | continue; | |
267 | } | |
268 | cr = confSpace->getConfReg(offset); | |
269 | if(!cr) // hole | |
270 | offset++; | |
271 | else{ | |
272 | uint8_t byte_offset = 0; | |
273 | do { | |
274 | fgets(buf,1024,fp); | |
275 | char * csr_offset = strtok(buf," "); | |
276 | off = strtoul(csr_offset,0,0); | |
277 | char * csr_value = strtok(0," "); | |
278 | val = strtoul(csr_value,0,0); | |
279 | ||
280 | if (off != offset) { | |
281 | // if mis-match, it must be restoring from an old chechpoint, | |
282 | // and this must be the command register, which always comes last. | |
283 | assert(off == PCI_CONF_COMM); | |
284 | printf("can't find PCIE register (offset = 0x%u) in %s.pcie, default value is used\n", offset, file); | |
285 | is_commd = true; | |
286 | break; | |
287 | } | |
288 | ||
289 | char * csr_mask = strtok(0," "); | |
290 | char * csr_size = strtok(0," "); | |
291 | size = strtoul(csr_size,0,0); | |
292 | cr->write(val,byte_offset,size); | |
293 | devif_confAccessCb(true,offset,size); | |
294 | offset += size; | |
295 | byte_offset += size; | |
296 | } while (byte_offset < cr->size); | |
297 | ||
298 | if (is_commd) | |
299 | break; | |
300 | } | |
301 | } | |
302 | ||
303 | // restore the command register | |
304 | cr = confSpace->getConfReg(PCI_CONF_COMM); | |
305 | if (!is_commd) | |
306 | fscanf(fp,"0x%x\t0x%x\t%s\n",&off,&val,name); | |
307 | cr->write(val,0,cr->size); | |
308 | devif_confAccessCb(true,PCI_CONF_COMM,cr->size); | |
309 | fclose(fp); | |
310 | ||
311 | return true; | |
312 | } | |
313 | ||
314 | ||
315 | ||
316 | ||
317 | void genericPcieDev::addMsiCap(uint8_t next_ptr, uint16_t offset, uint16_t reset_val){ | |
318 | //if(msiCapOffset != -1){ | |
319 | // printf("can;t have more than 1 MSI capability/function"); | |
320 | // return; | |
321 | //} | |
322 | msiCapOffset = offset; | |
323 | confSpace->addConfReg(new pciConfReg(" msi cap id ", 1, msiCapId, 0x0, 0x0), offset); | |
324 | confSpace->addConfReg(new pciConfReg(" msi next ptr ", 1, next_ptr, 0x0,0x0), offset + MSI_NEXTPTR_OFFSET); | |
325 | confSpace->addConfReg(new msiCapMsgCntrl(reset_val),offset + MSI_MSGCNTRL_OFFSET); | |
326 | confSpace->addConfReg(new pciConfReg(" msi addr0 ", 4, 0x0, 0xfffffffc, 0), offset + MSI_MSGADDR0_OFFSET); | |
327 | ||
328 | bool is64bit = (reset_val >> msiCapMsgCntrl::is64bitAddrCapable_bp) & 1; | |
329 | if(is64bit){ | |
330 | confSpace->addConfReg(new pciConfReg(" msi addr1 ", 4, 0x0, 0xffffffff), offset + MSI_MSGADDR1_OFFSET); | |
331 | confSpace->addConfReg(new pciConfReg(" msi data ", 2, 0x0, 0xffff), offset + MSI_MSGDATA64_OFFSET); | |
332 | }else | |
333 | confSpace->addConfReg(new pciConfReg(" msi data ", 2, 0x0, 0xffff), offset + MSI_MSGDATA32_OFFSET); | |
334 | return; | |
335 | } | |
336 | ||
337 | void genericPcieDev::addAERCap(uint16_t capOffset, uint16_t nexCapPtr){ | |
338 | char reg_descr[100]; | |
339 | aerCapOffset = capOffset; | |
340 | snprintf(reg_descr,100,"%s pciExpEnhncdCapHdr",devif_getName()); | |
341 | confSpace->addConfReg(new pciExpEnhncdCapHdr(0x10001 | nexCapPtr << pciExpEnhncdCapHdr::nxtCapOffset_rbp,0x0,0x0,reg_descr),capOffset);// AER capability, next ptr = 0, version = 1; | |
342 | ||
343 | ||
344 | confSpace->addConfReg(new pciExpAER_UnCorrErrStatReg(),capOffset + UNCORR_ERR_STAT_OFFSET); | |
345 | confSpace->addConfReg(new pciConfReg("pciExpAER_UnCorrErrMask",4,0x0,aerUncorrErrBitMask,0x0), capOffset + UNCORR_ERR_MASK_OFFSET); | |
346 | ||
347 | confSpace->addConfReg(new pciConfReg("pciExpAER_UnCorrErrSev",4,0x0,aerUncorrErrBitMask,0x0), capOffset + UNCORR_ERR_SEVERITY_OFFSET); | |
348 | ||
349 | confSpace->addConfReg(new pciExpAER_CorrErrStatReg(), capOffset + CORR_ERR_STAT_OFFSET); | |
350 | confSpace->addConfReg(new pciConfReg("pciExpAER_CorrErrMask",4, 0x0, aerCorrErrBitMask,0x0),capOffset + CORR_ERR_MASK_OFFSET); | |
351 | ||
352 | confSpace->addConfReg(new pciExpAER_ErrCapCntrlReg(0x1e0), capOffset + ADVANCED_ERR_CAPCNTRL_OFFSET); | |
353 | ||
354 | ||
355 | confSpace->addConfReg(new pciConfReg("header log0",4,0x0,0xffffffff,0x0),capOffset + HDR_LOG_OFFSET); | |
356 | confSpace->addConfReg(new pciConfReg("header log1",4,0x0,0xffffffff,0x0),capOffset + HDR_LOG_OFFSET + 4); | |
357 | confSpace->addConfReg(new pciConfReg("header log2",4,0x0,0xffffffff,0x0),capOffset + HDR_LOG_OFFSET + 8); | |
358 | confSpace->addConfReg(new pciConfReg("header log3",4,0x0,0xffffffff,0x0),capOffset + HDR_LOG_OFFSET + 12); | |
359 | } | |
360 | ||
361 | ||
362 | pcieCompleter genericPcieDev::routeMsg(pciExpMsgCode code){ | |
363 | msgRouting r = msgDB->msgCode2Routing(code); | |
364 | assert( r != routed_by_addr); | |
365 | assert( r != routed_by_id); | |
366 | ||
367 | // bridges should not use this function anymore. Only leaf devices are | |
368 | // allowed. | |
369 | SAM_DeviceId myId = (dynamic_cast<Module*>(this))->samId; | |
370 | return busIf->busif_msgAccess((uint8_t)code, r, getId(),&myId); | |
371 | } | |
372 | ||
373 | // handle an error condition from the perspective of a receiver/completer | |
374 | // of a TLP or as the detecting agent( eg trainingErr) of an | |
375 | // error condition. the routine sets the error bits and sends error messages | |
376 | // as applicable. This routine also takes care of AER regs if implemented | |
377 | // it is assumed that this device is not a root port | |
378 | ||
379 | pcieCompleter genericPcieDev::processErrorRcvrCmpltr(pciExp_Error error, uint32_t * header){ | |
380 | // implement the flow chart section 6.2.5 of rev 1.0a | |
381 | pciExpErrorType errType = errDB->error2DfltErrType(error); | |
382 | ||
383 | pciConfReg * devStatReg = confSpace->getConfReg(pciExpCapOffset + PCI_EXP_DEV_STAT_OFFSET); | |
384 | pciConfReg * devCntrlReg = confSpace->getConfReg(pciExpCapOffset + PCI_EXP_DEV_CTRL_OFFSET); | |
385 | ||
386 | pciConfReg * cmdReg = confSpace->getConfReg(PCI_CONF_COMM); | |
387 | pciConfReg * statReg = confSpace->getConfReg(PCI_CONF_STAT); | |
388 | ||
389 | if(errType == errCorrectable){ | |
390 | // correctable errors | |
391 | devStatReg->setBit(pciExpDevStatReg::corrErrDet_bp); | |
392 | if(aerCapable){ | |
393 | pciConfReg * corrErrStatReg = confSpace->getConfReg(aerCapOffset + CORR_ERR_STAT_OFFSET); | |
394 | corrErrStatReg->setBit(errDB->error2RegBitPos(error)); | |
395 | pciConfReg * corrErrMaskReg = confSpace->getConfReg(aerCapOffset + CORR_ERR_MASK_OFFSET); | |
396 | bool errorIsMasked = corrErrMaskReg->getBit(errDB->error2RegBitPos(error)); | |
397 | if(errorIsMasked) | |
398 | return pcieCompleter(SIM_OK); | |
399 | } | |
400 | ||
401 | if(devCntrlReg->getBit(pciExpDevCntrlReg::corrErrRepEn_bp)) | |
402 | return routeMsg(MSG_ERR_COR); | |
403 | else | |
404 | return pcieCompleter(SIM_OK); | |
405 | }else{ | |
406 | // un correctable errors | |
407 | // get the severity from severity register if aer is supported | |
408 | // else get the defualt severity for this error. | |
409 | uint8_t severityFatal; | |
410 | if(aerCapable){ | |
411 | pciConfReg * unCorrSevReg = confSpace->getConfReg(aerCapOffset + UNCORR_ERR_SEVERITY_OFFSET); | |
412 | severityFatal = unCorrSevReg->getBit(errDB->error2RegBitPos(error)); | |
413 | }else | |
414 | severityFatal = errType == errUnCorrectable_fatal ? true:false; | |
415 | if(severityFatal) | |
416 | devStatReg->setBit(pciExpDevStatReg::ftlErrDet_bp); | |
417 | else | |
418 | devStatReg->setBit(pciExpDevStatReg::nonFtlErrDet_bp); | |
419 | // set the bits in dev stat reg in case its a UR | |
420 | if(error == unsupportedRequest) | |
421 | devStatReg->setBit(pciExpDevStatReg::unSprtdReqDet_bp); | |
422 | ||
423 | if(error == poisonedTLP) | |
424 | statReg->setBit(pcieStatusReg::DETECTED_PARITY_ERROR); | |
425 | ||
426 | if(error == completerAbort) | |
427 | statReg->setBit(pcieStatusReg::SIGNALED_TARGET_ABORT); | |
428 | ||
429 | if(aerCapable){ | |
430 | pciConfReg * unCorrErrStatReg = confSpace->getConfReg(aerCapOffset + UNCORR_ERR_STAT_OFFSET); | |
431 | uint32_t unCorrErrStatOldVal = unCorrErrStatReg->getVal(); | |
432 | unCorrErrStatReg->setBit(errDB->error2RegBitPos(error)); | |
433 | pciConfReg * unCorrErrMaskReg = confSpace->getConfReg(aerCapOffset + UNCORR_ERR_MASK_OFFSET); | |
434 | bool errorIsMasked = unCorrErrMaskReg->getBit(errDB->error2RegBitPos(error)); | |
435 | ||
436 | if(errorIsMasked) | |
437 | return pcieCompleter(SIM_OK); | |
438 | ||
439 | if(unCorrErrStatOldVal == 0){ | |
440 | // error is unmasked and this is the first error, set the | |
441 | // first error pointer bit field | |
442 | pciConfReg * capCtrlReg = confSpace->getConfReg(aerCapOffset + ADVANCED_ERR_CAPCNTRL_OFFSET); | |
443 | capCtrlReg->setRange(pciExpAER_ErrCapCntrlReg::firstErrPointer_lbp, \ | |
444 | pciExpAER_ErrCapCntrlReg::firstErrPointer_rbp,errDB->error2RegBitPos(error)); | |
445 | // need to log the header as well | |
446 | assert(header); | |
447 | for(int i = 0; i < 4; i++) | |
448 | confSpace->getConfReg(aerCapOffset + HDR_LOG_OFFSET + i * 4)->setVal(*(header + i)); | |
449 | } | |
450 | } | |
451 | ||
452 | // handle UR seperately | |
453 | if(error == unsupportedRequest){ | |
454 | if(devCntrlReg->getBit(pciExpDevCntrlReg::unsprtdReqRepEn_bp) == 0) | |
455 | return pcieCompleter(SIM_OK); | |
456 | else if(severityFatal) | |
457 | return routeMsg(MSG_ERR_FATAL); | |
458 | else | |
459 | return routeMsg(MSG_ERR_NONFATAL); | |
460 | } | |
461 | ||
462 | int serrEnable = cmdReg->getBit(pcieCommandReg::SERR_ENABLE); | |
463 | if(serrEnable){ | |
464 | statReg->setBit(pcieStatusReg::SIGNALED_SYSTEM_ERROR); | |
465 | } | |
466 | if(serrEnable || devCntrlReg->getBit(pciExpDevCntrlReg::fatalErrRepEn_bp)) | |
467 | if(severityFatal) | |
468 | return routeMsg(MSG_ERR_FATAL); | |
469 | ||
470 | if(serrEnable || devCntrlReg->getBit(pciExpDevCntrlReg::nonFatalErrRepEn_bp )) | |
471 | if(!severityFatal) | |
472 | return routeMsg(MSG_ERR_NONFATAL); | |
473 | return pcieCompleter(SIM_OK); | |
474 | } | |
475 | } | |
476 | ||
477 | // return 0 on success, -1 on error | |
478 | int genericPcieDev::handleCompletion(pcieCompleter C){ | |
479 | pciConfReg * statReg = confSpace->getConfReg(PCI_CONF_STAT); | |
480 | ||
481 | if(C.status == SIM_OK)// non posted requests | |
482 | return 0; | |
483 | else if(C.status == SC) | |
484 | return 0; | |
485 | else if(C.status == UR) | |
486 | statReg->setBit(pcieStatusReg::RECEIVED_MASTER_ABORT); | |
487 | else if(C.status == CA) | |
488 | statReg->setBit(pcieStatusReg::RECEIVED_TARGET_ABORT); | |
489 | ||
490 | // the next two are not part of specs. the target returns it | |
491 | // so as to aid functional simulation (eg there is no way to timeout | |
492 | // in the initiator in this kind of model) | |
493 | else if(C.status == CMPL_TO) | |
494 | processErrorRcvrCmpltr(completionTimeout); | |
495 | else if(C.status == CMPL_POISONED){ | |
496 | pciConfReg * cmdReg = confSpace->getConfReg(PCI_CONF_COMM); | |
497 | if(cmdReg->getBit(pcieCommandReg::PARITY_ERR_EN)) | |
498 | statReg->setBit(pcieStatusReg::MASTER_DATA_PARITY_ERROR); | |
499 | } | |
500 | else if(C.status == SIM_FAIL){ | |
501 | printf("ERROR: internal error in the simulator. returned SIM_FAIL\n"); | |
502 | return -1; | |
503 | }else{ | |
504 | printf("ERROR: unknow completion <%d >received\n", C.status); | |
505 | return -1; | |
506 | } | |
507 | ||
508 | return 0; | |
509 | } | |
510 | ||
511 | ||
512 | ||
513 | ||
514 | ||
515 | /////////////////////////////////////////////////////////////////////////////// | |
516 | ||
517 | ||
518 | ||
519 | /*****************base address register*********/ | |
520 | pcieBaseAddrReg::pcieBaseAddrReg(const char * name, genericPcieDev * d,uint64_t spaceSize,pcie_space sp , bool ismem32, bool isPrefetch): \ | |
521 | pciConfReg(name,4,0,0 ,(void*)d){ | |
522 | ||
523 | if(d == 0){ | |
524 | printf("baseAddrReg constructor error: must provide pointer to genericPciDev\n"); | |
525 | return; | |
526 | } | |
527 | ||
528 | if(spaceSize < 16){ | |
529 | printf("baseAddrReg constructor error: size must be greater than or equal 16\n"); | |
530 | return; | |
531 | } | |
532 | ||
533 | int first_set_bit = ffs((uint32_t)spaceSize); | |
534 | if(first_set_bit == 0){ | |
535 | first_set_bit = ffs((uint32_t) (spaceSize >> 32)); | |
536 | mask = 0; | |
537 | mask_32To63 = (uint32_t)-1 << (first_set_bit - 1); | |
538 | }else{ | |
539 | mask_32To63 = 0xffffffff; | |
540 | mask = (uint32_t)-1 << (first_set_bit - 1); | |
541 | } | |
542 | ||
543 | this->space = sp; | |
544 | this->isPrefetchable = isPrefetch; | |
545 | this->mapSize = spaceSize; | |
546 | isMapped = false; | |
547 | val_32To63 = 0; | |
548 | mask_32To63 = 0xffffffff; | |
549 | ||
550 | if(space == PCIE_MEM){ | |
551 | if(ismem32) | |
552 | bartype = MEM32; | |
553 | else{ | |
554 | bartype = MEM64; | |
555 | size = 8; | |
556 | } | |
557 | }else | |
558 | bartype = IO; | |
559 | switch(space){ | |
560 | case PCIE_MEM: | |
561 | switch(isPrefetchable){ | |
562 | case true: | |
563 | val = bartype | PREFETCH; | |
564 | break; | |
565 | case false: | |
566 | val = bartype; | |
567 | break; | |
568 | } | |
569 | break; | |
570 | case PCIE_IO: | |
571 | val = IO; | |
572 | break; | |
573 | } | |
574 | ||
575 | return; | |
576 | } | |
577 | ||
578 | uint8_t pcieBaseAddrReg::write(uint32_t buf, uint8_t byte_offset, uint8_t bytes_to_write){ | |
579 | // simpler assumption for now of whole reg reads. | |
580 | // if assert ever fails then add support for (bytes_to_write <= 4 == true) etc. | |
581 | assert(bytes_to_write == 4); | |
582 | assert(byte_offset == 0 or byte_offset == 4); | |
583 | ||
584 | oldVal = val | (uint64_t)val_32To63 << 32; | |
585 | if(byte_offset > 3){ | |
586 | if(debug_level >= 2) | |
587 | printf("Name<%s>,size<0x%x>offset<0x4>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name,size,mask,val_32To63,buf & mask_32To63); | |
588 | val_32To63 = buf & mask_32To63; | |
589 | }else | |
590 | pciConfReg::write(buf, byte_offset, bytes_to_write); | |
591 | ||
592 | reMap(); | |
593 | return 4; | |
594 | } | |
595 | ||
596 | void pcieBaseAddrReg::reMap(){ | |
597 | //read the command register | |
598 | uint32_t commandRegVal; | |
599 | pciConfReg * confReg; | |
600 | confReg = ((genericPcieDev*)data)->confSpace->getConfReg(PCI_CONF_COMM); | |
601 | if(confReg){ | |
602 | confReg->read(&commandRegVal,confReg->size); | |
603 | if(space == PCIE_MEM){ | |
604 | bool space_enabled = commandRegVal & 1 << pcieCommandReg::MEM_SPACE; | |
605 | if(!space_enabled) | |
606 | return; | |
607 | if(isMapped) | |
608 | ((genericPcieDev*)data)->unmapSpace(PCIE_MEM,oldVal & ~0xf); | |
609 | ||
610 | if(bartype == MEM32) | |
611 | ((genericPcieDev*)data)->mapSpace(PCIE_MEM,val&~0xf,mapSize); | |
612 | else | |
613 | ((genericPcieDev*)data)->mapSpace(PCIE_MEM,(val|(uint64_t)val_32To63 << 32) &~0xf ,mapSize); | |
614 | }else if(space == PCIE_IO){ | |
615 | bool space_enabled = (commandRegVal & 1 << pcieCommandReg::IO_SPACE); | |
616 | if(!space_enabled) | |
617 | return; | |
618 | if(isMapped) | |
619 | ((genericPcieDev*)data)->unmapSpace(PCIE_IO,oldVal & ~0xf); | |
620 | ((genericPcieDev*)data)->mapSpace(PCIE_IO,val & ~0xf , mapSize); | |
621 | } | |
622 | } | |
623 | } | |
624 | ||
625 | bool pcieBaseAddrReg::map(){ | |
626 | assert(!isMapped); | |
627 | //isMapped = true; | |
628 | ||
629 | if(bartype == MEM64) | |
630 | isMapped = ((genericPcieDev*)data)->mapSpace(space,(val|(uint64_t)val_32To63 << 32) & ~0xf , mapSize); | |
631 | else | |
632 | isMapped = ((genericPcieDev*)data)->mapSpace(space,val & ~0xf , mapSize); | |
633 | return isMapped; | |
634 | } | |
635 | ||
636 | bool pcieBaseAddrReg::unmap(){ | |
637 | assert(isMapped); | |
638 | //isMapped = false; | |
639 | if(bartype == MEM64) | |
640 | isMapped = ((genericPcieDev*)data)->unmapSpace(space,(val|(uint64_t)val_32To63 << 32) & ~0xf); | |
641 | else | |
642 | isMapped = ((genericPcieDev*)data)->unmapSpace(space,val & ~0xf); | |
643 | return isMapped; | |
644 | } | |
645 | ||
646 | ||
647 | /********************Command reg************************/ | |
648 | uint8_t pcieCommandReg::write(uint32_t buf, uint8_t byte_offset, uint8_t bytes_to_write){ | |
649 | assert(byte_offset == 0); | |
650 | ||
651 | uint8_t ret; | |
652 | if(bytes_to_write <= 2) | |
653 | ret = bytes_to_write; | |
654 | else | |
655 | ret = 2; | |
656 | ||
657 | buf &= ~((int32_t)-1 << ret * 8); | |
658 | ||
659 | //uint32_t changed_bits = val ^ (mask & buf); | |
660 | uint32_t new_val = val | (mask & buf); | |
661 | uint32_t changed_bits = new_val ^ val; | |
662 | ||
663 | if(debug_level >= 2) | |
664 | printf("Name<%s>,size<0x%x>mask<0x%lx>value<0x%lx -> 0x%lx>\n",name,size,mask,val,new_val); | |
665 | ||
666 | val = new_val; | |
667 | ||
668 | if(changed_bits & 1 << IO_SPACE){ //I/O space map bit has changed | |
669 | if(val & 1 << IO_SPACE) //bit set to 1 | |
670 | mapSpace(PCIE_IO); | |
671 | else | |
672 | unMapSpace(PCIE_IO); //bit set to 0 | |
673 | } | |
674 | ||
675 | if(changed_bits & 1 << MEM_SPACE){ | |
676 | if(val & 1 << MEM_SPACE){ //bit set to 1 | |
677 | mapSpace(PCIE_MEM); | |
678 | }else{ | |
679 | unMapSpace(PCIE_MEM); | |
680 | } | |
681 | } | |
682 | ||
683 | return ret; | |
684 | } | |
685 | ||
686 | void pcieCommandReg::mapSpace(pcie_space spc){ | |
687 | //base address regs range from 0x10 to 0x24 | |
688 | pcieBaseAddrReg * baseAddr; | |
689 | for(int i = 0x10; i < 0x28; i+=4){ | |
690 | baseAddr = dynamic_cast<pcieBaseAddrReg*>(((genericPcieDev*)data)->confSpace->getConfReg(i)); | |
691 | if(baseAddr && baseAddr->space == spc){ | |
692 | baseAddr->map(); | |
693 | if(baseAddr->bartype == pcieBaseAddrReg::MEM64) | |
694 | i+=4; | |
695 | } | |
696 | } | |
697 | } | |
698 | ||
699 | void pcieCommandReg::unMapSpace(pcie_space spc){ | |
700 | //base address regs range from 0x10 to 0x24 | |
701 | pcieBaseAddrReg * baseAddr; | |
702 | for(int i = 0x10; i < 0x28; i+=4){ | |
703 | baseAddr = dynamic_cast<pcieBaseAddrReg*>(((genericPcieDev*)data)->confSpace->getConfReg(i)); | |
704 | if(baseAddr && baseAddr->space == spc){ | |
705 | baseAddr->unmap(); | |
706 | if(baseAddr->bartype == pcieBaseAddrReg::MEM64) | |
707 | i+=4; | |
708 | } | |
709 | } | |
710 | } | |
711 | ||
712 |