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