blob: 6aef7a8bebc07dd671eea1aecdf3ddb79ec952cd [file] [log] [blame]
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001/*
2// Copyright (c) 2019 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#include <errno.h>
17#include <fcntl.h>
18#include <peci.h>
19#include <string.h>
20#include <sys/ioctl.h>
21#include <syslog.h>
22#include <time.h>
23#include <unistd.h>
24
25EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd);
26
27/*-------------------------------------------------------------------------
28 * This function unlocks the peci interface
29 *------------------------------------------------------------------------*/
30void peci_Unlock(int peci_fd)
31{
32 if (close(peci_fd) != 0)
33 {
34 syslog(LOG_ERR, "PECI device failed to unlock.\n");
35 }
36}
37
38#define PECI_DEVICE "/dev/peci-0"
39/*-------------------------------------------------------------------------
40 * This function attempts to lock the peci interface with the specified
41 * timeout and returns a file descriptor if successful.
42 *------------------------------------------------------------------------*/
43EPECIStatus peci_Lock(int* peci_fd, int timeout_ms)
44{
45 struct timespec sRequest;
46 sRequest.tv_sec = 0;
47 sRequest.tv_nsec = PECI_TIMEOUT_RESOLUTION_MS * 1000 * 1000;
48 int timeout_count = 0;
49
50 if (NULL == peci_fd)
51 {
52 return PECI_CC_INVALID_REQ;
53 }
54
55 // Open the PECI driver with the specified timeout
56 *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
57 switch (timeout_ms)
58 {
59 case PECI_NO_WAIT:
60 break;
61 case PECI_WAIT_FOREVER:
62 while (-1 == *peci_fd)
63 {
64 nanosleep(&sRequest, NULL);
65 *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
66 }
67 default:
68 while (-1 == *peci_fd && timeout_count < timeout_ms)
69 {
70 nanosleep(&sRequest, NULL);
71 timeout_count += PECI_TIMEOUT_RESOLUTION_MS;
72 *peci_fd = open(PECI_DEVICE, O_RDWR | O_CLOEXEC);
73 }
74 }
75 if (-1 == *peci_fd)
76 {
77 syslog(LOG_ERR, " >>> PECI Device Busy <<< \n");
78 return PECI_CC_DRIVER_ERR;
79 }
80 return PECI_CC_SUCCESS;
81}
82
83/*-------------------------------------------------------------------------
84 * This function closes the peci interface
85 *------------------------------------------------------------------------*/
86static void peci_Close(int peci_fd)
87{
88 peci_Unlock(peci_fd);
89}
90
91/*-------------------------------------------------------------------------
92 * This function opens the peci interface and returns a file descriptor
93 *------------------------------------------------------------------------*/
94static EPECIStatus peci_Open(int* peci_fd)
95{
96 if (NULL == peci_fd)
97 {
98 return PECI_CC_INVALID_REQ;
99 }
100
101 // Lock the PECI driver with a default timeout
102 return peci_Lock(peci_fd, PECI_TIMEOUT_MS);
103}
104
105/*-------------------------------------------------------------------------
106 * This function issues peci commands to peci driver
107 *------------------------------------------------------------------------*/
108static EPECIStatus HW_peci_issue_cmd(unsigned int cmd, char* cmdPtr,
109 int peci_fd)
110{
111 if (cmdPtr == NULL)
112 {
113 return PECI_CC_INVALID_REQ;
114 }
115
116 if (ioctl(peci_fd, cmd, cmdPtr) != 0)
117 {
118 if (errno == ETIMEDOUT)
119 {
120 return PECI_CC_TIMEOUT;
121 }
122 return PECI_CC_DRIVER_ERR;
123 }
124
125 return PECI_CC_SUCCESS;
126}
127
128/*-------------------------------------------------------------------------
129 * Find the specified PCI bus number value
130 *------------------------------------------------------------------------*/
131EPECIStatus FindBusNumber(uint8_t u8Bus, uint8_t u8Cpu, uint8_t* pu8BusValue)
132{
133 uint8_t u8CpuBus0[] = {
134 PECI_PCI_BUS0_CPU0,
135 PECI_PCI_BUS0_CPU1,
136 };
137 uint8_t u8Bus0 = 0;
138 uint8_t u8Offset = 0;
139 EPECIStatus ret;
140 uint8_t u8Reg[4];
141 uint8_t cc = 0;
142
143 // First check for valid inputs
144 // Check cpu and bus numbers, only support buses [5:0]
145 if ((u8Bus > 5) || (u8Cpu >= (sizeof(u8CpuBus0) / sizeof(uint8_t))) ||
146 (pu8BusValue == NULL))
147 {
148 return PECI_CC_INVALID_REQ;
149 }
150
151 // Get the Bus 0 value for the requested CPU
152 u8Bus0 = u8CpuBus0[u8Cpu];
153
154 // Next check that the bus numbers are valid
155 // CPUBUSNO_VALID register - Above registers valid? - B(0) D5 F0 offset
156 // D4h
157 ret = peci_RdPCIConfig(u8Cpu, u8Bus0, PECI_PCI_CPUBUSNO_DEV,
158 PECI_PCI_CPUBUSNO_FUNC, PECI_PCI_CPUBUSNO_VALID,
159 u8Reg, &cc);
160 if (ret != PECI_CC_SUCCESS)
161 {
162 return ret;
163 }
164 // BIOS will set bit 31 of CPUBUSNO_VALID when the bus numbers are valid
165 if ((u8Reg[3] & 0x80) == 0)
166 {
167 return PECI_CC_HW_ERR;
168 }
169
170 // Bus numbers are valid so read the correct offset for the requested
171 // bus CPUBUSNO register - CPU Internal Bus Numbers [3:0] - B(0) D5 F0
172 // offset CCh CPUBUSNO_1 register - CPU Internal Bus Numbers [5:4] -
173 // B(0) D5 F0 offset D0h
174 u8Offset = u8Bus <= 3 ? PECI_PCI_CPUBUSNO : PECI_PCI_CPUBUSNO_1;
175 ret = peci_RdPCIConfig(u8Cpu, u8Bus0, PECI_PCI_CPUBUSNO_DEV,
176 PECI_PCI_CPUBUSNO_FUNC, u8Offset, u8Reg, &cc);
177 if (ret != PECI_CC_SUCCESS)
178 {
179 return ret;
180 }
181
182 // Now return the bus value for the requested bus
183 *pu8BusValue = u8Reg[u8Bus % 4];
184
185 // Unused bus numbers are set to zero which is only valid for bus 0
186 // so, return an error for any other bus set to zero
187 if (*pu8BusValue == 0 && u8Bus != 0)
188 {
189 return PECI_CC_CPU_NOT_PRESENT;
190 }
191
192 return PECI_CC_SUCCESS;
193}
194
195/*-------------------------------------------------------------------------
196 * This function checks the CPU PECI interface
197 *------------------------------------------------------------------------*/
198EPECIStatus peci_Ping(uint8_t target)
199{
200 int peci_fd = -1;
201 EPECIStatus ret;
202
203 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
204 {
205 return PECI_CC_DRIVER_ERR;
206 }
207 ret = peci_Ping_seq(target, peci_fd);
208
209 peci_Close(peci_fd);
210 return ret;
211}
212
213/*-------------------------------------------------------------------------
214 * This function allows sequential Ping with the provided
215 * peci file descriptor.
216 *------------------------------------------------------------------------*/
217EPECIStatus peci_Ping_seq(uint8_t target, int peci_fd)
218{
219 EPECIStatus ret;
220 struct peci_ping_msg cmd;
221
222 cmd.addr = target;
223 ret = HW_peci_issue_cmd(PECI_IOC_PING, (char*)&cmd, peci_fd);
224
225 return ret;
226}
227
228/*-------------------------------------------------------------------------
229 * This function gets PECI device information
230 *------------------------------------------------------------------------*/
231EPECIStatus peci_GetDIB(uint8_t target, uint64_t* dib)
232{
233 int peci_fd = -1;
234 EPECIStatus ret;
235
236 if (dib == NULL)
237 {
238 return PECI_CC_INVALID_REQ;
239 }
240
241 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
242 {
243 return PECI_CC_DRIVER_ERR;
244 }
245 ret = peci_GetDIB_seq(target, dib, peci_fd);
246
247 peci_Close(peci_fd);
248 return ret;
249}
250
251/*-------------------------------------------------------------------------
252 * This function allows sequential GetDIB with the provided
253 * peci file descriptor.
254 *------------------------------------------------------------------------*/
255EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd)
256{
257 struct peci_get_dib_msg cmd;
258 EPECIStatus ret;
259 cmd.addr = target;
260
261 if (dib == NULL)
262 {
263 return PECI_CC_INVALID_REQ;
264 }
265
266 ret = HW_peci_issue_cmd(PECI_IOC_GET_DIB, (char*)&cmd, peci_fd);
267
268 if (ret == PECI_CC_SUCCESS)
269 {
270 *dib = cmd.dib;
271 }
272
273 return ret;
274}
275
276/*-------------------------------------------------------------------------
277 * This function get PECI Thermal temperature
278 * Expressed in signed fixed point value of 1/64 degrees celsius
279 *------------------------------------------------------------------------*/
280EPECIStatus peci_GetTemp(uint8_t target, int16_t* temperature)
281{
282 int peci_fd = -1;
283 struct peci_get_temp_msg cmd;
284
285 if (temperature == NULL)
286 {
287 return PECI_CC_INVALID_REQ;
288 }
289
290 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
291 {
292 return PECI_CC_DRIVER_ERR;
293 }
294
295 cmd.addr = target;
296
297 EPECIStatus ret =
298 HW_peci_issue_cmd(PECI_IOC_GET_TEMP, (char*)&cmd, peci_fd);
299
300 if (ret == PECI_CC_SUCCESS)
301 {
302 *temperature = cmd.temp_raw;
303 }
304
305 peci_Close(peci_fd);
306
307 return ret;
308}
309
310/*-------------------------------------------------------------------------
311 * This function provides read access to the package configuration
312 * space within the processor.
313 *------------------------------------------------------------------------*/
314EPECIStatus peci_RdPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Value,
315 uint8_t u8ReadLen, uint8_t* pPkgConfig,
316 uint8_t* cc)
317{
318 int peci_fd = -1;
319 EPECIStatus ret;
320
321 if (pPkgConfig == NULL || cc == NULL)
322 {
323 return PECI_CC_INVALID_REQ;
324 }
325
326 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
327 {
328 return PECI_CC_DRIVER_ERR;
329 }
330 ret = peci_RdPkgConfig_seq(target, u8Index, u16Value, u8ReadLen, pPkgConfig,
331 peci_fd, cc);
332
333 peci_Close(peci_fd);
334 return ret;
335}
336
337/*-------------------------------------------------------------------------
338 * This function allows sequential RdPkgConfig with the provided
339 * peci file descriptor.
340 *------------------------------------------------------------------------*/
341EPECIStatus peci_RdPkgConfig_seq(uint8_t target, uint8_t u8Index,
342 uint16_t u16Value, uint8_t u8ReadLen,
343 uint8_t* pPkgConfig, int peci_fd, uint8_t* cc)
344{
345 struct peci_rd_pkg_cfg_msg cmd;
346 EPECIStatus ret;
347
348 if (pPkgConfig == NULL || cc == NULL)
349 {
350 return PECI_CC_INVALID_REQ;
351 }
352
353 // Per the PECI spec, the write length must be a byte, word, or dword
354 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
355 {
356 return PECI_CC_INVALID_REQ;
357 }
358
359 // The PECI buffer must be large enough to hold the requested data
360 if (sizeof(cmd.pkg_config) < u8ReadLen)
361 {
362 return PECI_CC_INVALID_REQ;
363 }
364
365 cmd.addr = target;
366 cmd.index = u8Index; // RdPkgConfig index
367 cmd.param = u16Value; // Config parameter value
368 cmd.rx_len = u8ReadLen;
369
370 ret = HW_peci_issue_cmd(PECI_IOC_RD_PKG_CFG, (char*)&cmd, peci_fd);
371 *cc = cmd.cc;
372 if (ret == PECI_CC_SUCCESS)
373 {
374 memcpy(pPkgConfig, cmd.pkg_config, u8ReadLen);
375 }
376
377 return ret;
378}
379
380/*-------------------------------------------------------------------------
381 * This function provides write access to the package configuration
382 * space within the processor
383 *------------------------------------------------------------------------*/
384EPECIStatus peci_WrPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Param,
385 uint32_t u32Value, uint8_t u8WriteLen, uint8_t* cc)
386{
387 int peci_fd = -1;
388 EPECIStatus ret;
389
390 if (cc == NULL)
391 {
392 return PECI_CC_INVALID_REQ;
393 }
394
395 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
396 {
397 return PECI_CC_DRIVER_ERR;
398 }
399 ret = peci_WrPkgConfig_seq(target, u8Index, u16Param, u32Value, u8WriteLen,
400 peci_fd, cc);
401
402 peci_Close(peci_fd);
403 return ret;
404}
405
406/*-------------------------------------------------------------------------
407 * This function allows sequential WrPkgConfig with the provided
408 * peci file descriptor.
409 *------------------------------------------------------------------------*/
410EPECIStatus peci_WrPkgConfig_seq(uint8_t target, uint8_t u8Index,
411 uint16_t u16Param, uint32_t u32Value,
412 uint8_t u8WriteLen, int peci_fd, uint8_t* cc)
413{
414 struct peci_wr_pkg_cfg_msg cmd;
415 EPECIStatus ret;
416
417 if (cc == NULL)
418 {
419 return PECI_CC_INVALID_REQ;
420 }
421
422 // Per the PECI spec, the write length must be a byte, word, or dword
423 if ((u8WriteLen != 1) && (u8WriteLen != 2) && (u8WriteLen != 4))
424 {
425 return PECI_CC_INVALID_REQ;
426 }
427
428 cmd.addr = target;
429 cmd.index = u8Index; // RdPkgConfig index
430 cmd.param = u16Param; // parameter value
431 cmd.tx_len = u8WriteLen;
432 cmd.value = u32Value;
433
434 ret = HW_peci_issue_cmd(PECI_IOC_WR_PKG_CFG, (char*)&cmd, peci_fd);
435 *cc = cmd.cc;
436
437 return ret;
438}
439
440/*-------------------------------------------------------------------------
441 * This function provides read access to Model Specific Registers
442 * defined in the processor doc.
443 *------------------------------------------------------------------------*/
444EPECIStatus peci_RdIAMSR(uint8_t target, uint8_t threadID, uint16_t MSRAddress,
445 uint64_t* u64MsrVal, uint8_t* cc)
446{
447 int peci_fd = -1;
448 struct peci_rd_ia_msr_msg cmd;
449 EPECIStatus ret;
450
451 if (u64MsrVal == NULL || cc == NULL)
452 {
453 return PECI_CC_INVALID_REQ;
454 }
455
456 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
457 {
458 return PECI_CC_DRIVER_ERR;
459 }
460
461 cmd.addr = target;
462 cmd.thread_id = threadID; // request byte for thread ID
463 cmd.address = MSRAddress; // MSR Address
464
465 ret = HW_peci_issue_cmd(PECI_IOC_RD_IA_MSR, (char*)&cmd, peci_fd);
466 *cc = cmd.cc;
467 if (ret == PECI_CC_SUCCESS)
468 {
469 *u64MsrVal = cmd.value;
470 }
471
472 peci_Close(peci_fd);
473 return ret;
474}
475
476/*-------------------------------------------------------------------------
477 * This function provides read access to the PCI configuration space at
478 * the requested PCI configuration address.
479 *------------------------------------------------------------------------*/
480EPECIStatus peci_RdPCIConfig(uint8_t target, uint8_t u8Bus, uint8_t u8Device,
481 uint8_t u8Fcn, uint16_t u16Reg, uint8_t* pPCIData,
482 uint8_t* cc)
483{
484 int peci_fd = -1;
485 EPECIStatus ret;
486
487 if (pPCIData == NULL || cc == NULL)
488 {
489 return PECI_CC_INVALID_REQ;
490 }
491
492 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
493 {
494 return PECI_CC_DRIVER_ERR;
495 }
496 ret = peci_RdPCIConfig_seq(target, u8Bus, u8Device, u8Fcn, u16Reg, pPCIData,
497 peci_fd, cc);
498
499 peci_Close(peci_fd);
500 return ret;
501}
502
503/*-------------------------------------------------------------------------
504 * This function allows sequential RdPCIConfig with the provided
505 * peci file descriptor.
506 *------------------------------------------------------------------------*/
507EPECIStatus peci_RdPCIConfig_seq(uint8_t target, uint8_t u8Bus,
508 uint8_t u8Device, uint8_t u8Fcn,
509 uint16_t u16Reg, uint8_t* pPCIData,
510 int peci_fd, uint8_t* cc)
511{
512 struct peci_rd_pci_cfg_msg cmd;
513 EPECIStatus ret;
514
515 if (pPCIData == NULL || cc == NULL)
516 {
517 return PECI_CC_INVALID_REQ;
518 }
519
520 // The PECI buffer must be large enough to hold the PCI data
521 if (sizeof(cmd.pci_config) < 4)
522 {
523 return PECI_CC_INVALID_REQ;
524 }
525
526 cmd.addr = target;
527 cmd.bus = u8Bus;
528 cmd.device = u8Device;
529 cmd.function = u8Fcn;
530 cmd.reg = u16Reg;
531
532 ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG, (char*)&cmd, peci_fd);
533 *cc = cmd.cc;
534
535 if (ret == PECI_CC_SUCCESS)
536 {
537 memcpy(pPCIData, cmd.pci_config, 4);
538 }
539
540 return ret;
541}
542
543/*-------------------------------------------------------------------------
544 * This function provides read access to the local PCI configuration space
545 *------------------------------------------------------------------------*/
546EPECIStatus peci_RdPCIConfigLocal(uint8_t target, uint8_t u8Bus,
547 uint8_t u8Device, uint8_t u8Fcn,
548 uint16_t u16Reg, uint8_t u8ReadLen,
549 uint8_t* pPCIReg, uint8_t* cc)
550{
551 int peci_fd = -1;
552 EPECIStatus ret;
553
554 if (pPCIReg == NULL || cc == NULL)
555 {
556 return PECI_CC_INVALID_REQ;
557 }
558
559 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
560 {
561 return PECI_CC_DRIVER_ERR;
562 }
563 ret = peci_RdPCIConfigLocal_seq(target, u8Bus, u8Device, u8Fcn, u16Reg,
564 u8ReadLen, pPCIReg, peci_fd, cc);
565
566 peci_Close(peci_fd);
567 return ret;
568}
569
570/*-------------------------------------------------------------------------
571 * This function allows sequential RdPCIConfigLocal with the provided
572 * peci file descriptor.
573 *------------------------------------------------------------------------*/
574EPECIStatus peci_RdPCIConfigLocal_seq(uint8_t target, uint8_t u8Bus,
575 uint8_t u8Device, uint8_t u8Fcn,
576 uint16_t u16Reg, uint8_t u8ReadLen,
577 uint8_t* pPCIReg, int peci_fd,
578 uint8_t* cc)
579{
580 struct peci_rd_pci_cfg_local_msg cmd;
581 EPECIStatus ret;
582
583 if (pPCIReg == NULL || cc == NULL)
584 {
585 return PECI_CC_INVALID_REQ;
586 }
587
588 // Per the PECI spec, the read length must be a byte, word, or dword
589 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
590 {
591 return PECI_CC_INVALID_REQ;
592 }
593
594 // The PECI buffer must be large enough to hold the requested data
595 if (sizeof(cmd.pci_config) < u8ReadLen)
596 {
597 return PECI_CC_INVALID_REQ;
598 }
599
600 cmd.addr = target;
601 cmd.bus = u8Bus;
602 cmd.device = u8Device;
603 cmd.function = u8Fcn;
604 cmd.reg = u16Reg;
605 cmd.rx_len = u8ReadLen;
606
607 ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
608 *cc = cmd.cc;
609
610 if (ret == PECI_CC_SUCCESS)
611 {
612 memcpy(pPCIReg, cmd.pci_config, u8ReadLen);
613 }
614
615 return ret;
616}
617
618/*-------------------------------------------------------------------------
619 * This function provides write access to the local PCI configuration space
620 *------------------------------------------------------------------------*/
621EPECIStatus peci_WrPCIConfigLocal(uint8_t target, uint8_t u8Bus,
622 uint8_t u8Device, uint8_t u8Fcn,
623 uint16_t u16Reg, uint8_t DataLen,
624 uint32_t DataVal, uint8_t* cc)
625{
626 int peci_fd = -1;
627 struct peci_wr_pci_cfg_local_msg cmd;
628 EPECIStatus ret;
629
630 if (cc == NULL)
631 {
632 return PECI_CC_INVALID_REQ;
633 }
634
635 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
636 {
637 return PECI_CC_DRIVER_ERR;
638 }
639
640 // Per the PECI spec, the write length must be a byte, word, or dword
641 if (DataLen != 1 && DataLen != 2 && DataLen != 4)
642 {
643 peci_Close(peci_fd);
644 return PECI_CC_INVALID_REQ;
645 }
646
647 cmd.addr = target;
648 cmd.bus = u8Bus;
649 cmd.device = u8Device;
650 cmd.function = u8Fcn;
651 cmd.reg = u16Reg;
652 cmd.tx_len = DataLen;
653 cmd.value = DataVal;
654
655 ret = HW_peci_issue_cmd(PECI_IOC_WR_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
656 *cc = cmd.cc;
657
658 peci_Close(peci_fd);
659 return ret;
660}
661
662/*-------------------------------------------------------------------------
663 * This internal function is the common interface for RdEndPointConfig to PCI
664 *------------------------------------------------------------------------*/
665static EPECIStatus peci_RdEndPointConfigPciCommon(
666 uint8_t target, uint8_t u8MsgType, uint8_t u8Seg, uint8_t u8Bus,
667 uint8_t u8Device, uint8_t u8Fcn, uint16_t u16Reg, uint8_t u8ReadLen,
668 uint8_t* pPCIData, int peci_fd, uint8_t* cc)
669{
670 struct peci_rd_end_pt_cfg_msg cmd;
671 EPECIStatus ret;
672
673 if (pPCIData == NULL || cc == NULL)
674 {
675 return PECI_CC_INVALID_REQ;
676 }
677
678 // The PECI buffer must be large enough to hold the requested data
679 if (sizeof(cmd.data) < u8ReadLen)
680 {
681 return PECI_CC_INVALID_REQ;
682 }
683
684 cmd.addr = target;
685 cmd.msg_type = u8MsgType;
686 cmd.params.pci_cfg.seg = u8Seg;
687 cmd.params.pci_cfg.bus = u8Bus;
688 cmd.params.pci_cfg.device = u8Device;
689 cmd.params.pci_cfg.function = u8Fcn;
690 cmd.params.pci_cfg.reg = u16Reg;
691 cmd.rx_len = u8ReadLen;
692
693 ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
694 *cc = cmd.cc;
695
696 if (ret == PECI_CC_SUCCESS)
697 {
698 memcpy(pPCIData, cmd.data, u8ReadLen);
699 }
700 else
701 {
702 ret = PECI_CC_DRIVER_ERR;
703 }
704
705 return ret;
706}
707
708/*-------------------------------------------------------------------------
709 * This function provides read access to the PCI configuration space at
710 * the requested PCI configuration address.
711 *------------------------------------------------------------------------*/
712EPECIStatus peci_RdEndPointConfigPci(uint8_t target, uint8_t u8Seg,
713 uint8_t u8Bus, uint8_t u8Device,
714 uint8_t u8Fcn, uint16_t u16Reg,
715 uint8_t u8ReadLen, uint8_t* pPCIData,
716 uint8_t* cc)
717{
718 int peci_fd = -1;
719 EPECIStatus ret;
720
721 if (pPCIData == NULL || cc == NULL)
722 {
723 return PECI_CC_INVALID_REQ;
724 }
725
726 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
727 {
728 return PECI_CC_DRIVER_ERR;
729 }
730 ret =
731 peci_RdEndPointConfigPci_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
732 u16Reg, u8ReadLen, pPCIData, peci_fd, cc);
733 peci_Close(peci_fd);
734 return ret;
735}
736
737/*-------------------------------------------------------------------------
738 * This function allows sequential RdEndPointConfig to PCI with the provided
739 * peci file descriptor.
740 *------------------------------------------------------------------------*/
741EPECIStatus peci_RdEndPointConfigPci_seq(uint8_t target, uint8_t u8Seg,
742 uint8_t u8Bus, uint8_t u8Device,
743 uint8_t u8Fcn, uint16_t u16Reg,
744 uint8_t u8ReadLen, uint8_t* pPCIData,
745 int peci_fd, uint8_t* cc)
746{
747 if (pPCIData == NULL || cc == NULL)
748 {
749 return PECI_CC_INVALID_REQ;
750 }
751
752 // Per the PECI spec, the read length must be a byte, word, or dword
753 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
754 {
755 return PECI_CC_INVALID_REQ;
756 }
757
758 return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
759 u8Bus, u8Device, u8Fcn, u16Reg,
760 u8ReadLen, pPCIData, peci_fd, cc);
761}
762
763/*-------------------------------------------------------------------------
764 * This function provides read access to the Local PCI configuration space at
765 * the requested PCI configuration address.
766 *------------------------------------------------------------------------*/
767EPECIStatus peci_RdEndPointConfigPciLocal(uint8_t target, uint8_t u8Seg,
768 uint8_t u8Bus, uint8_t u8Device,
769 uint8_t u8Fcn, uint16_t u16Reg,
770 uint8_t u8ReadLen, uint8_t* pPCIData,
771 uint8_t* cc)
772{
773 int peci_fd = -1;
774 EPECIStatus ret;
775
776 if (pPCIData == NULL || cc == NULL)
777 {
778 return PECI_CC_INVALID_REQ;
779 }
780
781 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
782 {
783 return PECI_CC_DRIVER_ERR;
784 }
785 ret = peci_RdEndPointConfigPciLocal_seq(target, u8Seg, u8Bus, u8Device,
786 u8Fcn, u16Reg, u8ReadLen, pPCIData,
787 peci_fd, cc);
788 peci_Close(peci_fd);
789 return ret;
790}
791
792/*-------------------------------------------------------------------------
793 * This function allows sequential RdEndPointConfig to PCI Local with the
794 *provided peci file descriptor.
795 *------------------------------------------------------------------------*/
796EPECIStatus peci_RdEndPointConfigPciLocal_seq(uint8_t target, uint8_t u8Seg,
797 uint8_t u8Bus, uint8_t u8Device,
798 uint8_t u8Fcn, uint16_t u16Reg,
799 uint8_t u8ReadLen,
800 uint8_t* pPCIData, int peci_fd,
801 uint8_t* cc)
802{
803 if (pPCIData == NULL || cc == NULL)
804 {
805 return PECI_CC_INVALID_REQ;
806 }
807
808 // Per the PECI spec, the read length must be a byte, word, or dword
809 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
810 {
811 return PECI_CC_INVALID_REQ;
812 }
813
814 return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI,
815 u8Seg, u8Bus, u8Device, u8Fcn, u16Reg,
816 u8ReadLen, pPCIData, peci_fd, cc);
817}
818
819/*-------------------------------------------------------------------------
820 * This function provides read access to PCI MMIO space at
821 * the requested PCI configuration address.
822 *------------------------------------------------------------------------*/
823EPECIStatus peci_RdEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
824 uint8_t u8Bus, uint8_t u8Device,
825 uint8_t u8Fcn, uint8_t u8Bar,
826 uint8_t u8AddrType, uint64_t u64Offset,
827 uint8_t u8ReadLen, uint8_t* pMmioData,
828 uint8_t* cc)
829{
830 int peci_fd = -1;
831 EPECIStatus ret;
832
833 if (pMmioData == NULL || cc == NULL)
834 {
835 return PECI_CC_INVALID_REQ;
836 }
837
838 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
839 {
840 return PECI_CC_DRIVER_ERR;
841 }
842 ret = peci_RdEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
843 u8Bar, u8AddrType, u64Offset, u8ReadLen,
844 pMmioData, peci_fd, cc);
845 peci_Close(peci_fd);
846 return ret;
847}
848
849/*-------------------------------------------------------------------------
850 * This function allows sequential RdEndPointConfig to PCI MMIO with the
851 *provided peci file descriptor.
852 *------------------------------------------------------------------------*/
853EPECIStatus peci_RdEndPointConfigMmio_seq(
854 uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
855 uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
856 uint8_t u8ReadLen, uint8_t* pMmioData, int peci_fd, uint8_t* cc)
857{
858 struct peci_rd_end_pt_cfg_msg cmd;
859 EPECIStatus ret;
860
861 if (pMmioData == NULL || cc == NULL)
862 {
863 return PECI_CC_INVALID_REQ;
864 }
865
866 // Per the PECI spec, the read length must be a byte, word, dword, or qword
867 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4 && u8ReadLen != 8)
868 {
869 return PECI_CC_INVALID_REQ;
870 }
871
872 // The PECI buffer must be large enough to hold the requested data
873 if (sizeof(cmd.data) < u8ReadLen)
874 {
875 return PECI_CC_INVALID_REQ;
876 }
877
878 cmd.addr = target;
879 cmd.msg_type = PECI_ENDPTCFG_TYPE_MMIO;
880 cmd.params.mmio.seg = u8Seg;
881 cmd.params.mmio.bus = u8Bus;
882 cmd.params.mmio.device = u8Device;
883 cmd.params.mmio.function = u8Fcn;
884 cmd.params.mmio.bar = u8Bar;
885 cmd.params.mmio.addr_type = u8AddrType;
886 cmd.params.mmio.offset = u64Offset;
887 cmd.rx_len = u8ReadLen;
888
889 ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
890 *cc = cmd.cc;
891
892 if (ret == PECI_CC_SUCCESS)
893 {
894 memcpy(pMmioData, cmd.data, u8ReadLen);
895 }
896 else
897 {
898 ret = PECI_CC_DRIVER_ERR;
899 }
900
901 return ret;
902}
903
904/*-------------------------------------------------------------------------
905 * This function allows sequential peci_WrEndPointConfig to PCI EndPoint with
906 *the provided peci file descriptor.
907 *------------------------------------------------------------------------*/
908EPECIStatus peci_WrEndPointConfig_seq(uint8_t target, uint8_t u8MsgType,
909 uint8_t u8Seg, uint8_t u8Bus,
910 uint8_t u8Device, uint8_t u8Fcn,
911 uint16_t u16Reg, uint8_t DataLen,
912 uint32_t DataVal, int peci_fd,
913 uint8_t* cc)
914{
915 struct peci_wr_end_pt_cfg_msg cmd;
916 EPECIStatus ret;
917
918 if (cc == NULL)
919 {
920 return PECI_CC_INVALID_REQ;
921 }
922
923 // Per the PECI spec, the write length must be a byte, word, or dword
924 if (DataLen != 1 && DataLen != 2 && DataLen != 4)
925 {
926 return PECI_CC_INVALID_REQ;
927 }
928
929 cmd.addr = target;
930 cmd.msg_type = u8MsgType;
931 cmd.params.pci_cfg.seg = u8Seg;
932 cmd.params.pci_cfg.bus = u8Bus;
933 cmd.params.pci_cfg.device = u8Device;
934 cmd.params.pci_cfg.function = u8Fcn;
935 cmd.params.pci_cfg.reg = u16Reg;
936 cmd.tx_len = DataLen;
937 cmd.value = DataVal;
938
939 ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
940 *cc = cmd.cc;
941
942 return ret;
943}
944
945/*-------------------------------------------------------------------------
946 * This function provides write access to the EP local PCI configuration space
947 *------------------------------------------------------------------------*/
948EPECIStatus peci_WrEndPointPCIConfigLocal(uint8_t target, uint8_t u8Seg,
949 uint8_t u8Bus, uint8_t u8Device,
950 uint8_t u8Fcn, uint16_t u16Reg,
951 uint8_t DataLen, uint32_t DataVal,
952 uint8_t* cc)
953{
954 int peci_fd = -1;
955 EPECIStatus ret;
956
957 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
958 {
959 return PECI_CC_DRIVER_ERR;
960 }
961
962 ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8Seg,
963 u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
964 DataVal, peci_fd, cc);
965 peci_Close(peci_fd);
966 return ret;
967}
968
969/*-------------------------------------------------------------------------
970 * This function provides write access to the EP local PCI configuration space
971 *------------------------------------------------------------------------*/
972EPECIStatus peci_WrEndPointPCIConfig(uint8_t target, uint8_t u8Seg,
973 uint8_t u8Bus, uint8_t u8Device,
974 uint8_t u8Fcn, uint16_t u16Reg,
975 uint8_t DataLen, uint32_t DataVal,
976 uint8_t* cc)
977{
978 int peci_fd = -1;
979 EPECIStatus ret;
980
981 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
982 {
983 return PECI_CC_DRIVER_ERR;
984 }
985 ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
986 u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
987 DataVal, peci_fd, cc);
988 peci_Close(peci_fd);
989 return ret;
990}
991
992/*-------------------------------------------------------------------------
993 * This function provides write access to PCI MMIO space at
994 * the requested PCI configuration address.
995 *------------------------------------------------------------------------*/
996EPECIStatus peci_WrEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
997 uint8_t u8Bus, uint8_t u8Device,
998 uint8_t u8Fcn, uint8_t u8Bar,
999 uint8_t u8AddrType, uint64_t u64Offset,
1000 uint8_t u8DataLen, uint64_t u64DataVal,
1001 uint8_t* cc)
1002{
1003 int peci_fd = -1;
1004 EPECIStatus ret;
1005
1006 if (cc == NULL)
1007 {
1008 return PECI_CC_INVALID_REQ;
1009 }
1010
1011 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1012 {
1013 return PECI_CC_DRIVER_ERR;
1014 }
1015 ret = peci_WrEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
1016 u8Bar, u8AddrType, u64Offset, u8DataLen,
1017 u64DataVal, peci_fd, cc);
1018 peci_Close(peci_fd);
1019 return ret;
1020}
1021
1022/*-------------------------------------------------------------------------
1023 * This function allows sequential WrEndPointConfig to PCI MMIO with the
1024 * provided peci file descriptor.
1025 *------------------------------------------------------------------------*/
1026EPECIStatus peci_WrEndPointConfigMmio_seq(
1027 uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
1028 uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
1029 uint8_t u8DataLen, uint64_t u64DataVal, int peci_fd, uint8_t* cc)
1030{
1031 struct peci_wr_end_pt_cfg_msg cmd;
1032 EPECIStatus ret;
1033
1034 if (cc == NULL)
1035 {
1036 return PECI_CC_INVALID_REQ;
1037 }
1038
1039 // Per the PECI spec, the read length must be a byte, word, dword, or qword
1040 if (u8DataLen != 1 && u8DataLen != 2 && u8DataLen != 4 && u8DataLen != 8)
1041 {
1042 return PECI_CC_INVALID_REQ;
1043 }
1044
1045 cmd.addr = target;
1046 cmd.msg_type = PECI_ENDPTCFG_TYPE_MMIO;
1047 cmd.params.mmio.seg = u8Seg;
1048 cmd.params.mmio.bus = u8Bus;
1049 cmd.params.mmio.device = u8Device;
1050 cmd.params.mmio.function = u8Fcn;
1051 cmd.params.mmio.bar = u8Bar;
1052 cmd.params.mmio.addr_type = u8AddrType;
1053 cmd.params.mmio.offset = u64Offset;
1054 cmd.tx_len = u8DataLen;
1055 cmd.value = u64DataVal;
1056
1057 ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
1058 *cc = cmd.cc;
1059
1060 return ret;
1061}
1062
1063/*-------------------------------------------------------------------------
1064 * This function provides crashdump discovery data over PECI
1065 *------------------------------------------------------------------------*/
1066EPECIStatus peci_CrashDump_Discovery(uint8_t target, uint8_t subopcode,
1067 uint8_t param0, uint16_t param1,
1068 uint8_t param2, uint8_t u8ReadLen,
1069 uint8_t* pData, uint8_t* cc)
1070{
1071 int peci_fd = -1;
1072 struct peci_crashdump_disc_msg cmd;
1073 EPECIStatus ret;
1074
1075 if (pData == NULL || cc == NULL)
1076 {
1077 return PECI_CC_INVALID_REQ;
1078 }
1079
1080 // Per the PECI spec, the read length must be a byte, word, or qword
1081 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 8)
1082 {
1083 return PECI_CC_INVALID_REQ;
1084 }
1085
1086 // The PECI buffer must be large enough to hold the requested data
1087 if (sizeof(cmd.data) < u8ReadLen)
1088 {
1089 return PECI_CC_INVALID_REQ;
1090 }
1091
1092 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1093 {
1094 return PECI_CC_DRIVER_ERR;
1095 }
1096
1097 cmd.addr = target;
1098 cmd.subopcode = subopcode;
1099 cmd.param0 = param0;
1100 cmd.param1 = param1;
1101 cmd.param2 = param2;
1102 cmd.rx_len = u8ReadLen;
1103
1104 ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_DISC, (char*)&cmd, peci_fd);
1105 *cc = cmd.cc;
1106 if (ret == PECI_CC_SUCCESS)
1107 {
1108 memcpy(pData, cmd.data, u8ReadLen);
1109 }
1110 else
1111 {
1112 ret = PECI_CC_DRIVER_ERR;
1113 }
1114
1115 peci_Close(peci_fd);
1116 return ret;
1117}
1118
1119/*-------------------------------------------------------------------------
1120 * This function provides crashdump GetFrame data over PECI
1121 *------------------------------------------------------------------------*/
1122EPECIStatus peci_CrashDump_GetFrame(uint8_t target, uint16_t param0,
1123 uint16_t param1, uint16_t param2,
1124 uint8_t u8ReadLen, uint8_t* pData,
1125 uint8_t* cc)
1126{
1127 int peci_fd = -1;
1128 struct peci_crashdump_get_frame_msg cmd;
1129 EPECIStatus ret;
1130
1131 if (pData == NULL || cc == NULL)
1132 {
1133 return PECI_CC_INVALID_REQ;
1134 }
1135
1136 // Per the PECI spec, the read length must be a qword or dqword
1137 if (u8ReadLen != 8 && u8ReadLen != 16)
1138 {
1139 return PECI_CC_INVALID_REQ;
1140 }
1141
1142 // The PECI buffer must be large enough to hold the requested data
1143 if (sizeof(cmd.data) < u8ReadLen)
1144 {
1145 return PECI_CC_INVALID_REQ;
1146 }
1147
1148 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1149 {
1150 return PECI_CC_DRIVER_ERR;
1151 }
1152
1153 cmd.addr = target;
1154 cmd.param0 = param0;
1155 cmd.param1 = param1;
1156 cmd.param2 = param2;
1157 cmd.rx_len = u8ReadLen;
1158
1159 ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_GET_FRAME, (char*)&cmd, peci_fd);
1160 *cc = cmd.cc;
1161 if (ret == PECI_CC_SUCCESS)
1162 {
1163 memcpy(pData, cmd.data, u8ReadLen);
1164 }
1165 else
1166 {
1167 ret = PECI_CC_DRIVER_ERR;
1168 }
1169
1170 peci_Close(peci_fd);
1171 return ret;
1172}
1173
1174/*-------------------------------------------------------------------------
1175 * This function provides raw PECI command access
1176 *------------------------------------------------------------------------*/
1177EPECIStatus peci_raw(uint8_t target, uint8_t u8ReadLen, const uint8_t* pRawCmd,
1178 const uint32_t cmdSize, uint8_t* pRawResp,
1179 uint32_t respSize)
1180{
1181 int peci_fd = -1;
1182 struct peci_xfer_msg cmd;
1183 uint8_t u8TxBuf[PECI_BUFFER_SIZE];
1184 uint8_t u8RxBuf[PECI_BUFFER_SIZE];
1185 EPECIStatus ret;
1186
1187 if (u8ReadLen && pRawResp == NULL)
1188 {
1189 return PECI_CC_INVALID_REQ;
1190 }
1191
1192 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1193 {
1194 return PECI_CC_DRIVER_ERR;
1195 }
1196
1197 // Check for valid buffer sizes
1198 if (cmdSize > PECI_BUFFER_SIZE || respSize < u8ReadLen ||
1199 u8ReadLen >
1200 (PECI_BUFFER_SIZE - 1)) // response buffer is data + 1 status byte
1201 {
1202 peci_Close(peci_fd);
1203 return PECI_CC_INVALID_REQ;
1204 }
1205
1206 cmd.addr = target;
1207 cmd.tx_len = (uint8_t)cmdSize;
1208 cmd.rx_len = u8ReadLen;
1209
1210 memcpy(u8TxBuf, pRawCmd, cmdSize);
1211
1212 cmd.tx_buf = u8TxBuf;
1213 cmd.rx_buf = u8RxBuf;
1214 ret = HW_peci_issue_cmd(PECI_IOC_XFER, (char*)&cmd, peci_fd);
1215
1216 if (ret == PECI_CC_SUCCESS || ret == PECI_CC_TIMEOUT)
1217 {
1218 memcpy(pRawResp, u8RxBuf, u8ReadLen);
1219 }
1220
1221 peci_Close(peci_fd);
1222 return ret;
1223}
1224
1225/*-------------------------------------------------------------------------
1226 * This function returns the CPUID (Model and stepping) for the given PECI
1227 * client address
1228 *------------------------------------------------------------------------*/
1229EPECIStatus peci_GetCPUID(const uint8_t clientAddr, CPUModel* cpuModel,
1230 uint8_t* stepping, uint8_t* cc)
1231{
1232 EPECIStatus ret = PECI_CC_SUCCESS;
1233 uint32_t cpuid = 0;
1234
1235 if (cpuModel == NULL || stepping == NULL || cc == NULL)
1236 {
1237 return PECI_CC_INVALID_REQ;
1238 }
1239
1240 if (peci_Ping(clientAddr) != PECI_CC_SUCCESS)
1241 {
1242 return PECI_CC_CPU_NOT_PRESENT;
1243 }
1244
1245 ret =
1246 peci_RdPkgConfig(clientAddr, PECI_MBX_INDEX_CPU_ID, PECI_PKG_ID_CPU_ID,
1247 sizeof(uint32_t), (uint8_t*)&cpuid, cc);
1248
1249 // Separate out the model and stepping (bits 3:0) from the CPUID
1250 *cpuModel = cpuid & 0xFFFFFFF0;
1251 *stepping = (uint8_t)(cpuid & 0x0000000F);
1252 return ret;
1253}