blob: ba38e00dd9c36ec39b9d5a00ed1df5d1bee7ffd8 [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
Jason M. Bills6ca31642020-08-06 10:26:19 -0700208 // The target address must be in the valid range
209 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
210 {
211 return PECI_CC_INVALID_REQ;
212 }
213
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700214 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
215 {
216 return PECI_CC_DRIVER_ERR;
217 }
218 ret = peci_Ping_seq(target, peci_fd);
219
220 peci_Close(peci_fd);
221 return ret;
222}
223
224/*-------------------------------------------------------------------------
225 * This function allows sequential Ping with the provided
226 * peci file descriptor.
227 *------------------------------------------------------------------------*/
228EPECIStatus peci_Ping_seq(uint8_t target, int peci_fd)
229{
230 EPECIStatus ret;
231 struct peci_ping_msg cmd;
232
Jason M. Bills6ca31642020-08-06 10:26:19 -0700233 // The target address must be in the valid range
234 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
235 {
236 return PECI_CC_INVALID_REQ;
237 }
238
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700239 cmd.addr = target;
240 ret = HW_peci_issue_cmd(PECI_IOC_PING, (char*)&cmd, peci_fd);
241
242 return ret;
243}
244
245/*-------------------------------------------------------------------------
246 * This function gets PECI device information
247 *------------------------------------------------------------------------*/
248EPECIStatus peci_GetDIB(uint8_t target, uint64_t* dib)
249{
250 int peci_fd = -1;
251 EPECIStatus ret;
252
253 if (dib == NULL)
254 {
255 return PECI_CC_INVALID_REQ;
256 }
257
Jason M. Bills6ca31642020-08-06 10:26:19 -0700258 // The target address must be in the valid range
259 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
260 {
261 return PECI_CC_INVALID_REQ;
262 }
263
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700264 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
265 {
266 return PECI_CC_DRIVER_ERR;
267 }
268 ret = peci_GetDIB_seq(target, dib, peci_fd);
269
270 peci_Close(peci_fd);
271 return ret;
272}
273
274/*-------------------------------------------------------------------------
275 * This function allows sequential GetDIB with the provided
276 * peci file descriptor.
277 *------------------------------------------------------------------------*/
278EPECIStatus peci_GetDIB_seq(uint8_t target, uint64_t* dib, int peci_fd)
279{
280 struct peci_get_dib_msg cmd;
281 EPECIStatus ret;
282 cmd.addr = target;
283
284 if (dib == NULL)
285 {
286 return PECI_CC_INVALID_REQ;
287 }
288
Jason M. Bills6ca31642020-08-06 10:26:19 -0700289 // The target address must be in the valid range
290 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
291 {
292 return PECI_CC_INVALID_REQ;
293 }
294
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700295 ret = HW_peci_issue_cmd(PECI_IOC_GET_DIB, (char*)&cmd, peci_fd);
296
297 if (ret == PECI_CC_SUCCESS)
298 {
299 *dib = cmd.dib;
300 }
301
302 return ret;
303}
304
305/*-------------------------------------------------------------------------
306 * This function get PECI Thermal temperature
307 * Expressed in signed fixed point value of 1/64 degrees celsius
308 *------------------------------------------------------------------------*/
309EPECIStatus peci_GetTemp(uint8_t target, int16_t* temperature)
310{
311 int peci_fd = -1;
312 struct peci_get_temp_msg cmd;
313
314 if (temperature == NULL)
315 {
316 return PECI_CC_INVALID_REQ;
317 }
318
Jason M. Bills6ca31642020-08-06 10:26:19 -0700319 // The target address must be in the valid range
320 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
321 {
322 return PECI_CC_INVALID_REQ;
323 }
324
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700325 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
326 {
327 return PECI_CC_DRIVER_ERR;
328 }
329
330 cmd.addr = target;
331
332 EPECIStatus ret =
333 HW_peci_issue_cmd(PECI_IOC_GET_TEMP, (char*)&cmd, peci_fd);
334
335 if (ret == PECI_CC_SUCCESS)
336 {
337 *temperature = cmd.temp_raw;
338 }
339
340 peci_Close(peci_fd);
341
342 return ret;
343}
344
345/*-------------------------------------------------------------------------
346 * This function provides read access to the package configuration
347 * space within the processor.
348 *------------------------------------------------------------------------*/
349EPECIStatus peci_RdPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Value,
350 uint8_t u8ReadLen, uint8_t* pPkgConfig,
351 uint8_t* cc)
352{
353 int peci_fd = -1;
354 EPECIStatus ret;
355
356 if (pPkgConfig == NULL || cc == NULL)
357 {
358 return PECI_CC_INVALID_REQ;
359 }
360
Jason M. Bills6ca31642020-08-06 10:26:19 -0700361 // The target address must be in the valid range
362 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
363 {
364 return PECI_CC_INVALID_REQ;
365 }
366
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700367 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
368 {
369 return PECI_CC_DRIVER_ERR;
370 }
371 ret = peci_RdPkgConfig_seq(target, u8Index, u16Value, u8ReadLen, pPkgConfig,
372 peci_fd, cc);
373
374 peci_Close(peci_fd);
375 return ret;
376}
377
378/*-------------------------------------------------------------------------
379 * This function allows sequential RdPkgConfig with the provided
380 * peci file descriptor.
381 *------------------------------------------------------------------------*/
382EPECIStatus peci_RdPkgConfig_seq(uint8_t target, uint8_t u8Index,
383 uint16_t u16Value, uint8_t u8ReadLen,
384 uint8_t* pPkgConfig, int peci_fd, uint8_t* cc)
385{
386 struct peci_rd_pkg_cfg_msg cmd;
387 EPECIStatus ret;
388
389 if (pPkgConfig == NULL || cc == NULL)
390 {
391 return PECI_CC_INVALID_REQ;
392 }
393
Jason M. Bills6ca31642020-08-06 10:26:19 -0700394 // The target address must be in the valid range
395 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
396 {
397 return PECI_CC_INVALID_REQ;
398 }
399
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700400 // Per the PECI spec, the write length must be a byte, word, or dword
401 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
402 {
403 return PECI_CC_INVALID_REQ;
404 }
405
406 // The PECI buffer must be large enough to hold the requested data
407 if (sizeof(cmd.pkg_config) < u8ReadLen)
408 {
409 return PECI_CC_INVALID_REQ;
410 }
411
412 cmd.addr = target;
413 cmd.index = u8Index; // RdPkgConfig index
414 cmd.param = u16Value; // Config parameter value
415 cmd.rx_len = u8ReadLen;
416
417 ret = HW_peci_issue_cmd(PECI_IOC_RD_PKG_CFG, (char*)&cmd, peci_fd);
418 *cc = cmd.cc;
419 if (ret == PECI_CC_SUCCESS)
420 {
421 memcpy(pPkgConfig, cmd.pkg_config, u8ReadLen);
422 }
423
424 return ret;
425}
426
427/*-------------------------------------------------------------------------
428 * This function provides write access to the package configuration
429 * space within the processor
430 *------------------------------------------------------------------------*/
431EPECIStatus peci_WrPkgConfig(uint8_t target, uint8_t u8Index, uint16_t u16Param,
432 uint32_t u32Value, uint8_t u8WriteLen, uint8_t* cc)
433{
434 int peci_fd = -1;
435 EPECIStatus ret;
436
437 if (cc == NULL)
438 {
439 return PECI_CC_INVALID_REQ;
440 }
441
Jason M. Bills6ca31642020-08-06 10:26:19 -0700442 // The target address must be in the valid range
443 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
444 {
445 return PECI_CC_INVALID_REQ;
446 }
447
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700448 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
449 {
450 return PECI_CC_DRIVER_ERR;
451 }
452 ret = peci_WrPkgConfig_seq(target, u8Index, u16Param, u32Value, u8WriteLen,
453 peci_fd, cc);
454
455 peci_Close(peci_fd);
456 return ret;
457}
458
459/*-------------------------------------------------------------------------
460 * This function allows sequential WrPkgConfig with the provided
461 * peci file descriptor.
462 *------------------------------------------------------------------------*/
463EPECIStatus peci_WrPkgConfig_seq(uint8_t target, uint8_t u8Index,
464 uint16_t u16Param, uint32_t u32Value,
465 uint8_t u8WriteLen, int peci_fd, uint8_t* cc)
466{
467 struct peci_wr_pkg_cfg_msg cmd;
468 EPECIStatus ret;
469
470 if (cc == NULL)
471 {
472 return PECI_CC_INVALID_REQ;
473 }
474
Jason M. Bills6ca31642020-08-06 10:26:19 -0700475 // The target address must be in the valid range
476 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
477 {
478 return PECI_CC_INVALID_REQ;
479 }
480
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700481 // Per the PECI spec, the write length must be a byte, word, or dword
482 if ((u8WriteLen != 1) && (u8WriteLen != 2) && (u8WriteLen != 4))
483 {
484 return PECI_CC_INVALID_REQ;
485 }
486
487 cmd.addr = target;
488 cmd.index = u8Index; // RdPkgConfig index
489 cmd.param = u16Param; // parameter value
490 cmd.tx_len = u8WriteLen;
491 cmd.value = u32Value;
492
493 ret = HW_peci_issue_cmd(PECI_IOC_WR_PKG_CFG, (char*)&cmd, peci_fd);
494 *cc = cmd.cc;
495
496 return ret;
497}
498
499/*-------------------------------------------------------------------------
500 * This function provides read access to Model Specific Registers
501 * defined in the processor doc.
502 *------------------------------------------------------------------------*/
503EPECIStatus peci_RdIAMSR(uint8_t target, uint8_t threadID, uint16_t MSRAddress,
504 uint64_t* u64MsrVal, uint8_t* cc)
505{
506 int peci_fd = -1;
507 struct peci_rd_ia_msr_msg cmd;
508 EPECIStatus ret;
509
510 if (u64MsrVal == NULL || cc == NULL)
511 {
512 return PECI_CC_INVALID_REQ;
513 }
514
Jason M. Bills6ca31642020-08-06 10:26:19 -0700515 // The target address must be in the valid range
516 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
517 {
518 return PECI_CC_INVALID_REQ;
519 }
520
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700521 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
522 {
523 return PECI_CC_DRIVER_ERR;
524 }
525
526 cmd.addr = target;
527 cmd.thread_id = threadID; // request byte for thread ID
528 cmd.address = MSRAddress; // MSR Address
529
530 ret = HW_peci_issue_cmd(PECI_IOC_RD_IA_MSR, (char*)&cmd, peci_fd);
531 *cc = cmd.cc;
532 if (ret == PECI_CC_SUCCESS)
533 {
534 *u64MsrVal = cmd.value;
535 }
536
537 peci_Close(peci_fd);
538 return ret;
539}
540
541/*-------------------------------------------------------------------------
542 * This function provides read access to the PCI configuration space at
543 * the requested PCI configuration address.
544 *------------------------------------------------------------------------*/
545EPECIStatus peci_RdPCIConfig(uint8_t target, uint8_t u8Bus, uint8_t u8Device,
546 uint8_t u8Fcn, uint16_t u16Reg, uint8_t* pPCIData,
547 uint8_t* cc)
548{
549 int peci_fd = -1;
550 EPECIStatus ret;
551
552 if (pPCIData == NULL || cc == NULL)
553 {
554 return PECI_CC_INVALID_REQ;
555 }
556
Jason M. Bills6ca31642020-08-06 10:26:19 -0700557 // The target address must be in the valid range
558 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
559 {
560 return PECI_CC_INVALID_REQ;
561 }
562
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700563 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
564 {
565 return PECI_CC_DRIVER_ERR;
566 }
567 ret = peci_RdPCIConfig_seq(target, u8Bus, u8Device, u8Fcn, u16Reg, pPCIData,
568 peci_fd, cc);
569
570 peci_Close(peci_fd);
571 return ret;
572}
573
574/*-------------------------------------------------------------------------
575 * This function allows sequential RdPCIConfig with the provided
576 * peci file descriptor.
577 *------------------------------------------------------------------------*/
578EPECIStatus peci_RdPCIConfig_seq(uint8_t target, uint8_t u8Bus,
579 uint8_t u8Device, uint8_t u8Fcn,
580 uint16_t u16Reg, uint8_t* pPCIData,
581 int peci_fd, uint8_t* cc)
582{
583 struct peci_rd_pci_cfg_msg cmd;
584 EPECIStatus ret;
585
586 if (pPCIData == NULL || cc == NULL)
587 {
588 return PECI_CC_INVALID_REQ;
589 }
590
Jason M. Bills6ca31642020-08-06 10:26:19 -0700591 // The target address must be in the valid range
592 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
593 {
594 return PECI_CC_INVALID_REQ;
595 }
596
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700597 // The PECI buffer must be large enough to hold the PCI data
598 if (sizeof(cmd.pci_config) < 4)
599 {
600 return PECI_CC_INVALID_REQ;
601 }
602
603 cmd.addr = target;
604 cmd.bus = u8Bus;
605 cmd.device = u8Device;
606 cmd.function = u8Fcn;
607 cmd.reg = u16Reg;
608
609 ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG, (char*)&cmd, peci_fd);
610 *cc = cmd.cc;
611
612 if (ret == PECI_CC_SUCCESS)
613 {
614 memcpy(pPCIData, cmd.pci_config, 4);
615 }
616
617 return ret;
618}
619
620/*-------------------------------------------------------------------------
621 * This function provides read access to the local PCI configuration space
622 *------------------------------------------------------------------------*/
623EPECIStatus peci_RdPCIConfigLocal(uint8_t target, uint8_t u8Bus,
624 uint8_t u8Device, uint8_t u8Fcn,
625 uint16_t u16Reg, uint8_t u8ReadLen,
626 uint8_t* pPCIReg, uint8_t* cc)
627{
628 int peci_fd = -1;
629 EPECIStatus ret;
630
631 if (pPCIReg == NULL || cc == NULL)
632 {
633 return PECI_CC_INVALID_REQ;
634 }
635
Jason M. Bills6ca31642020-08-06 10:26:19 -0700636 // The target address must be in the valid range
637 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
638 {
639 return PECI_CC_INVALID_REQ;
640 }
641
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700642 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
643 {
644 return PECI_CC_DRIVER_ERR;
645 }
646 ret = peci_RdPCIConfigLocal_seq(target, u8Bus, u8Device, u8Fcn, u16Reg,
647 u8ReadLen, pPCIReg, peci_fd, cc);
648
649 peci_Close(peci_fd);
650 return ret;
651}
652
653/*-------------------------------------------------------------------------
654 * This function allows sequential RdPCIConfigLocal with the provided
655 * peci file descriptor.
656 *------------------------------------------------------------------------*/
657EPECIStatus peci_RdPCIConfigLocal_seq(uint8_t target, uint8_t u8Bus,
658 uint8_t u8Device, uint8_t u8Fcn,
659 uint16_t u16Reg, uint8_t u8ReadLen,
660 uint8_t* pPCIReg, int peci_fd,
661 uint8_t* cc)
662{
663 struct peci_rd_pci_cfg_local_msg cmd;
664 EPECIStatus ret;
665
666 if (pPCIReg == NULL || cc == NULL)
667 {
668 return PECI_CC_INVALID_REQ;
669 }
670
Jason M. Bills6ca31642020-08-06 10:26:19 -0700671 // The target address must be in the valid range
672 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
673 {
674 return PECI_CC_INVALID_REQ;
675 }
676
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700677 // Per the PECI spec, the read length must be a byte, word, or dword
678 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
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.pci_config) < u8ReadLen)
685 {
686 return PECI_CC_INVALID_REQ;
687 }
688
689 cmd.addr = target;
690 cmd.bus = u8Bus;
691 cmd.device = u8Device;
692 cmd.function = u8Fcn;
693 cmd.reg = u16Reg;
694 cmd.rx_len = u8ReadLen;
695
696 ret = HW_peci_issue_cmd(PECI_IOC_RD_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
697 *cc = cmd.cc;
698
699 if (ret == PECI_CC_SUCCESS)
700 {
701 memcpy(pPCIReg, cmd.pci_config, u8ReadLen);
702 }
703
704 return ret;
705}
706
707/*-------------------------------------------------------------------------
708 * This function provides write access to the local PCI configuration space
709 *------------------------------------------------------------------------*/
710EPECIStatus peci_WrPCIConfigLocal(uint8_t target, uint8_t u8Bus,
711 uint8_t u8Device, uint8_t u8Fcn,
712 uint16_t u16Reg, uint8_t DataLen,
713 uint32_t DataVal, uint8_t* cc)
714{
715 int peci_fd = -1;
716 struct peci_wr_pci_cfg_local_msg cmd;
717 EPECIStatus ret;
718
719 if (cc == NULL)
720 {
721 return PECI_CC_INVALID_REQ;
722 }
723
Jason M. Bills6ca31642020-08-06 10:26:19 -0700724 // The target address must be in the valid range
725 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
726 {
727 return PECI_CC_INVALID_REQ;
728 }
729
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700730 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
731 {
732 return PECI_CC_DRIVER_ERR;
733 }
734
735 // Per the PECI spec, the write length must be a byte, word, or dword
736 if (DataLen != 1 && DataLen != 2 && DataLen != 4)
737 {
738 peci_Close(peci_fd);
739 return PECI_CC_INVALID_REQ;
740 }
741
742 cmd.addr = target;
743 cmd.bus = u8Bus;
744 cmd.device = u8Device;
745 cmd.function = u8Fcn;
746 cmd.reg = u16Reg;
747 cmd.tx_len = DataLen;
748 cmd.value = DataVal;
749
750 ret = HW_peci_issue_cmd(PECI_IOC_WR_PCI_CFG_LOCAL, (char*)&cmd, peci_fd);
751 *cc = cmd.cc;
752
753 peci_Close(peci_fd);
754 return ret;
755}
756
757/*-------------------------------------------------------------------------
758 * This internal function is the common interface for RdEndPointConfig to PCI
759 *------------------------------------------------------------------------*/
760static EPECIStatus peci_RdEndPointConfigPciCommon(
761 uint8_t target, uint8_t u8MsgType, uint8_t u8Seg, uint8_t u8Bus,
762 uint8_t u8Device, uint8_t u8Fcn, uint16_t u16Reg, uint8_t u8ReadLen,
763 uint8_t* pPCIData, int peci_fd, uint8_t* cc)
764{
765 struct peci_rd_end_pt_cfg_msg cmd;
766 EPECIStatus ret;
767
768 if (pPCIData == NULL || cc == NULL)
769 {
770 return PECI_CC_INVALID_REQ;
771 }
772
Jason M. Bills6ca31642020-08-06 10:26:19 -0700773 // The target address must be in the valid range
774 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
775 {
776 return PECI_CC_INVALID_REQ;
777 }
778
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700779 // The PECI buffer must be large enough to hold the requested data
780 if (sizeof(cmd.data) < u8ReadLen)
781 {
782 return PECI_CC_INVALID_REQ;
783 }
784
785 cmd.addr = target;
786 cmd.msg_type = u8MsgType;
787 cmd.params.pci_cfg.seg = u8Seg;
788 cmd.params.pci_cfg.bus = u8Bus;
789 cmd.params.pci_cfg.device = u8Device;
790 cmd.params.pci_cfg.function = u8Fcn;
791 cmd.params.pci_cfg.reg = u16Reg;
792 cmd.rx_len = u8ReadLen;
793
794 ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
795 *cc = cmd.cc;
796
797 if (ret == PECI_CC_SUCCESS)
798 {
799 memcpy(pPCIData, cmd.data, u8ReadLen);
800 }
801 else
802 {
803 ret = PECI_CC_DRIVER_ERR;
804 }
805
806 return ret;
807}
808
809/*-------------------------------------------------------------------------
810 * This function provides read access to the PCI configuration space at
811 * the requested PCI configuration address.
812 *------------------------------------------------------------------------*/
813EPECIStatus peci_RdEndPointConfigPci(uint8_t target, uint8_t u8Seg,
814 uint8_t u8Bus, uint8_t u8Device,
815 uint8_t u8Fcn, uint16_t u16Reg,
816 uint8_t u8ReadLen, uint8_t* pPCIData,
817 uint8_t* cc)
818{
819 int peci_fd = -1;
820 EPECIStatus ret;
821
822 if (pPCIData == NULL || cc == NULL)
823 {
824 return PECI_CC_INVALID_REQ;
825 }
826
Jason M. Bills6ca31642020-08-06 10:26:19 -0700827 // The target address must be in the valid range
828 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
829 {
830 return PECI_CC_INVALID_REQ;
831 }
832
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700833 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
834 {
835 return PECI_CC_DRIVER_ERR;
836 }
837 ret =
838 peci_RdEndPointConfigPci_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
839 u16Reg, u8ReadLen, pPCIData, peci_fd, cc);
840 peci_Close(peci_fd);
841 return ret;
842}
843
844/*-------------------------------------------------------------------------
845 * This function allows sequential RdEndPointConfig to PCI with the provided
846 * peci file descriptor.
847 *------------------------------------------------------------------------*/
848EPECIStatus peci_RdEndPointConfigPci_seq(uint8_t target, uint8_t u8Seg,
849 uint8_t u8Bus, uint8_t u8Device,
850 uint8_t u8Fcn, uint16_t u16Reg,
851 uint8_t u8ReadLen, uint8_t* pPCIData,
852 int peci_fd, uint8_t* cc)
853{
854 if (pPCIData == NULL || cc == NULL)
855 {
856 return PECI_CC_INVALID_REQ;
857 }
858
Jason M. Bills6ca31642020-08-06 10:26:19 -0700859 // The target address must be in the valid range
860 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
861 {
862 return PECI_CC_INVALID_REQ;
863 }
864
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700865 // Per the PECI spec, the read length must be a byte, word, or dword
866 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
867 {
868 return PECI_CC_INVALID_REQ;
869 }
870
871 return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
872 u8Bus, u8Device, u8Fcn, u16Reg,
873 u8ReadLen, pPCIData, peci_fd, cc);
874}
875
876/*-------------------------------------------------------------------------
877 * This function provides read access to the Local PCI configuration space at
878 * the requested PCI configuration address.
879 *------------------------------------------------------------------------*/
880EPECIStatus peci_RdEndPointConfigPciLocal(uint8_t target, uint8_t u8Seg,
881 uint8_t u8Bus, uint8_t u8Device,
882 uint8_t u8Fcn, uint16_t u16Reg,
883 uint8_t u8ReadLen, uint8_t* pPCIData,
884 uint8_t* cc)
885{
886 int peci_fd = -1;
887 EPECIStatus ret;
888
889 if (pPCIData == NULL || cc == NULL)
890 {
891 return PECI_CC_INVALID_REQ;
892 }
893
Jason M. Bills6ca31642020-08-06 10:26:19 -0700894 // The target address must be in the valid range
895 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
896 {
897 return PECI_CC_INVALID_REQ;
898 }
899
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700900 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
901 {
902 return PECI_CC_DRIVER_ERR;
903 }
904 ret = peci_RdEndPointConfigPciLocal_seq(target, u8Seg, u8Bus, u8Device,
905 u8Fcn, u16Reg, u8ReadLen, pPCIData,
906 peci_fd, cc);
907 peci_Close(peci_fd);
908 return ret;
909}
910
911/*-------------------------------------------------------------------------
912 * This function allows sequential RdEndPointConfig to PCI Local with the
913 *provided peci file descriptor.
914 *------------------------------------------------------------------------*/
915EPECIStatus peci_RdEndPointConfigPciLocal_seq(uint8_t target, uint8_t u8Seg,
916 uint8_t u8Bus, uint8_t u8Device,
917 uint8_t u8Fcn, uint16_t u16Reg,
918 uint8_t u8ReadLen,
919 uint8_t* pPCIData, int peci_fd,
920 uint8_t* cc)
921{
922 if (pPCIData == NULL || cc == NULL)
923 {
924 return PECI_CC_INVALID_REQ;
925 }
926
Jason M. Bills6ca31642020-08-06 10:26:19 -0700927 // The target address must be in the valid range
928 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
929 {
930 return PECI_CC_INVALID_REQ;
931 }
932
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700933 // Per the PECI spec, the read length must be a byte, word, or dword
934 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4)
935 {
936 return PECI_CC_INVALID_REQ;
937 }
938
939 return peci_RdEndPointConfigPciCommon(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI,
940 u8Seg, u8Bus, u8Device, u8Fcn, u16Reg,
941 u8ReadLen, pPCIData, peci_fd, cc);
942}
943
944/*-------------------------------------------------------------------------
945 * This function provides read access to PCI MMIO space at
946 * the requested PCI configuration address.
947 *------------------------------------------------------------------------*/
948EPECIStatus peci_RdEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
949 uint8_t u8Bus, uint8_t u8Device,
950 uint8_t u8Fcn, uint8_t u8Bar,
951 uint8_t u8AddrType, uint64_t u64Offset,
952 uint8_t u8ReadLen, uint8_t* pMmioData,
953 uint8_t* cc)
954{
955 int peci_fd = -1;
956 EPECIStatus ret;
957
958 if (pMmioData == NULL || cc == NULL)
959 {
960 return PECI_CC_INVALID_REQ;
961 }
962
Jason M. Bills6ca31642020-08-06 10:26:19 -0700963 // The target address must be in the valid range
964 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
965 {
966 return PECI_CC_INVALID_REQ;
967 }
968
Jason M. Bills7ef5a552020-04-06 14:58:44 -0700969 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
970 {
971 return PECI_CC_DRIVER_ERR;
972 }
973 ret = peci_RdEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
974 u8Bar, u8AddrType, u64Offset, u8ReadLen,
975 pMmioData, peci_fd, cc);
976 peci_Close(peci_fd);
977 return ret;
978}
979
980/*-------------------------------------------------------------------------
981 * This function allows sequential RdEndPointConfig to PCI MMIO with the
982 *provided peci file descriptor.
983 *------------------------------------------------------------------------*/
984EPECIStatus peci_RdEndPointConfigMmio_seq(
985 uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
986 uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
987 uint8_t u8ReadLen, uint8_t* pMmioData, int peci_fd, uint8_t* cc)
988{
989 struct peci_rd_end_pt_cfg_msg cmd;
990 EPECIStatus ret;
991
992 if (pMmioData == NULL || cc == NULL)
993 {
994 return PECI_CC_INVALID_REQ;
995 }
996
Jason M. Bills6ca31642020-08-06 10:26:19 -0700997 // The target address must be in the valid range
998 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
999 {
1000 return PECI_CC_INVALID_REQ;
1001 }
1002
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001003 // Per the PECI spec, the read length must be a byte, word, dword, or qword
1004 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 4 && u8ReadLen != 8)
1005 {
1006 return PECI_CC_INVALID_REQ;
1007 }
1008
1009 // The PECI buffer must be large enough to hold the requested data
1010 if (sizeof(cmd.data) < u8ReadLen)
1011 {
1012 return PECI_CC_INVALID_REQ;
1013 }
1014
1015 cmd.addr = target;
1016 cmd.msg_type = PECI_ENDPTCFG_TYPE_MMIO;
1017 cmd.params.mmio.seg = u8Seg;
1018 cmd.params.mmio.bus = u8Bus;
1019 cmd.params.mmio.device = u8Device;
1020 cmd.params.mmio.function = u8Fcn;
1021 cmd.params.mmio.bar = u8Bar;
1022 cmd.params.mmio.addr_type = u8AddrType;
1023 cmd.params.mmio.offset = u64Offset;
1024 cmd.rx_len = u8ReadLen;
1025
1026 ret = HW_peci_issue_cmd(PECI_IOC_RD_END_PT_CFG, (char*)&cmd, peci_fd);
1027 *cc = cmd.cc;
1028
1029 if (ret == PECI_CC_SUCCESS)
1030 {
1031 memcpy(pMmioData, cmd.data, u8ReadLen);
1032 }
1033 else
1034 {
1035 ret = PECI_CC_DRIVER_ERR;
1036 }
1037
1038 return ret;
1039}
1040
1041/*-------------------------------------------------------------------------
1042 * This function allows sequential peci_WrEndPointConfig to PCI EndPoint with
1043 *the provided peci file descriptor.
1044 *------------------------------------------------------------------------*/
1045EPECIStatus peci_WrEndPointConfig_seq(uint8_t target, uint8_t u8MsgType,
1046 uint8_t u8Seg, uint8_t u8Bus,
1047 uint8_t u8Device, uint8_t u8Fcn,
1048 uint16_t u16Reg, uint8_t DataLen,
1049 uint32_t DataVal, int peci_fd,
1050 uint8_t* cc)
1051{
1052 struct peci_wr_end_pt_cfg_msg cmd;
1053 EPECIStatus ret;
1054
1055 if (cc == NULL)
1056 {
1057 return PECI_CC_INVALID_REQ;
1058 }
1059
Jason M. Bills6ca31642020-08-06 10:26:19 -07001060 // The target address must be in the valid range
1061 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1062 {
1063 return PECI_CC_INVALID_REQ;
1064 }
1065
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001066 // Per the PECI spec, the write length must be a byte, word, or dword
1067 if (DataLen != 1 && DataLen != 2 && DataLen != 4)
1068 {
1069 return PECI_CC_INVALID_REQ;
1070 }
1071
1072 cmd.addr = target;
1073 cmd.msg_type = u8MsgType;
1074 cmd.params.pci_cfg.seg = u8Seg;
1075 cmd.params.pci_cfg.bus = u8Bus;
1076 cmd.params.pci_cfg.device = u8Device;
1077 cmd.params.pci_cfg.function = u8Fcn;
1078 cmd.params.pci_cfg.reg = u16Reg;
1079 cmd.tx_len = DataLen;
1080 cmd.value = DataVal;
1081
1082 ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
1083 *cc = cmd.cc;
1084
1085 return ret;
1086}
1087
1088/*-------------------------------------------------------------------------
1089 * This function provides write access to the EP local PCI configuration space
1090 *------------------------------------------------------------------------*/
1091EPECIStatus peci_WrEndPointPCIConfigLocal(uint8_t target, uint8_t u8Seg,
1092 uint8_t u8Bus, uint8_t u8Device,
1093 uint8_t u8Fcn, uint16_t u16Reg,
1094 uint8_t DataLen, uint32_t DataVal,
1095 uint8_t* cc)
1096{
1097 int peci_fd = -1;
1098 EPECIStatus ret;
1099
1100 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1101 {
1102 return PECI_CC_DRIVER_ERR;
1103 }
1104
1105 ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_LOCAL_PCI, u8Seg,
1106 u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
1107 DataVal, peci_fd, cc);
1108 peci_Close(peci_fd);
1109 return ret;
1110}
1111
1112/*-------------------------------------------------------------------------
1113 * This function provides write access to the EP local PCI configuration space
1114 *------------------------------------------------------------------------*/
1115EPECIStatus peci_WrEndPointPCIConfig(uint8_t target, uint8_t u8Seg,
1116 uint8_t u8Bus, uint8_t u8Device,
1117 uint8_t u8Fcn, uint16_t u16Reg,
1118 uint8_t DataLen, uint32_t DataVal,
1119 uint8_t* cc)
1120{
1121 int peci_fd = -1;
1122 EPECIStatus ret;
1123
1124 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1125 {
1126 return PECI_CC_DRIVER_ERR;
1127 }
1128 ret = peci_WrEndPointConfig_seq(target, PECI_ENDPTCFG_TYPE_PCI, u8Seg,
1129 u8Bus, u8Device, u8Fcn, u16Reg, DataLen,
1130 DataVal, peci_fd, cc);
1131 peci_Close(peci_fd);
1132 return ret;
1133}
1134
1135/*-------------------------------------------------------------------------
1136 * This function provides write access to PCI MMIO space at
1137 * the requested PCI configuration address.
1138 *------------------------------------------------------------------------*/
1139EPECIStatus peci_WrEndPointConfigMmio(uint8_t target, uint8_t u8Seg,
1140 uint8_t u8Bus, uint8_t u8Device,
1141 uint8_t u8Fcn, uint8_t u8Bar,
1142 uint8_t u8AddrType, uint64_t u64Offset,
1143 uint8_t u8DataLen, uint64_t u64DataVal,
1144 uint8_t* cc)
1145{
1146 int peci_fd = -1;
1147 EPECIStatus ret;
1148
1149 if (cc == NULL)
1150 {
1151 return PECI_CC_INVALID_REQ;
1152 }
1153
Jason M. Bills6ca31642020-08-06 10:26:19 -07001154 // The target address must be in the valid range
1155 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1156 {
1157 return PECI_CC_INVALID_REQ;
1158 }
1159
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001160 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1161 {
1162 return PECI_CC_DRIVER_ERR;
1163 }
1164 ret = peci_WrEndPointConfigMmio_seq(target, u8Seg, u8Bus, u8Device, u8Fcn,
1165 u8Bar, u8AddrType, u64Offset, u8DataLen,
1166 u64DataVal, peci_fd, cc);
1167 peci_Close(peci_fd);
1168 return ret;
1169}
1170
1171/*-------------------------------------------------------------------------
1172 * This function allows sequential WrEndPointConfig to PCI MMIO with the
1173 * provided peci file descriptor.
1174 *------------------------------------------------------------------------*/
1175EPECIStatus peci_WrEndPointConfigMmio_seq(
1176 uint8_t target, uint8_t u8Seg, uint8_t u8Bus, uint8_t u8Device,
1177 uint8_t u8Fcn, uint8_t u8Bar, uint8_t u8AddrType, uint64_t u64Offset,
1178 uint8_t u8DataLen, uint64_t u64DataVal, int peci_fd, uint8_t* cc)
1179{
1180 struct peci_wr_end_pt_cfg_msg cmd;
1181 EPECIStatus ret;
1182
1183 if (cc == NULL)
1184 {
1185 return PECI_CC_INVALID_REQ;
1186 }
1187
Jason M. Bills6ca31642020-08-06 10:26:19 -07001188 // The target address must be in the valid range
1189 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1190 {
1191 return PECI_CC_INVALID_REQ;
1192 }
1193
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001194 // Per the PECI spec, the read length must be a byte, word, dword, or qword
1195 if (u8DataLen != 1 && u8DataLen != 2 && u8DataLen != 4 && u8DataLen != 8)
1196 {
1197 return PECI_CC_INVALID_REQ;
1198 }
1199
1200 cmd.addr = target;
1201 cmd.msg_type = PECI_ENDPTCFG_TYPE_MMIO;
1202 cmd.params.mmio.seg = u8Seg;
1203 cmd.params.mmio.bus = u8Bus;
1204 cmd.params.mmio.device = u8Device;
1205 cmd.params.mmio.function = u8Fcn;
1206 cmd.params.mmio.bar = u8Bar;
1207 cmd.params.mmio.addr_type = u8AddrType;
1208 cmd.params.mmio.offset = u64Offset;
1209 cmd.tx_len = u8DataLen;
1210 cmd.value = u64DataVal;
1211
1212 ret = HW_peci_issue_cmd(PECI_IOC_WR_END_PT_CFG, (char*)&cmd, peci_fd);
1213 *cc = cmd.cc;
1214
1215 return ret;
1216}
1217
1218/*-------------------------------------------------------------------------
1219 * This function provides crashdump discovery data over PECI
1220 *------------------------------------------------------------------------*/
1221EPECIStatus peci_CrashDump_Discovery(uint8_t target, uint8_t subopcode,
1222 uint8_t param0, uint16_t param1,
1223 uint8_t param2, uint8_t u8ReadLen,
1224 uint8_t* pData, uint8_t* cc)
1225{
1226 int peci_fd = -1;
1227 struct peci_crashdump_disc_msg cmd;
1228 EPECIStatus ret;
1229
1230 if (pData == NULL || cc == NULL)
1231 {
1232 return PECI_CC_INVALID_REQ;
1233 }
1234
Jason M. Bills6ca31642020-08-06 10:26:19 -07001235 // The target address must be in the valid range
1236 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1237 {
1238 return PECI_CC_INVALID_REQ;
1239 }
1240
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001241 // Per the PECI spec, the read length must be a byte, word, or qword
1242 if (u8ReadLen != 1 && u8ReadLen != 2 && u8ReadLen != 8)
1243 {
1244 return PECI_CC_INVALID_REQ;
1245 }
1246
1247 // The PECI buffer must be large enough to hold the requested data
1248 if (sizeof(cmd.data) < u8ReadLen)
1249 {
1250 return PECI_CC_INVALID_REQ;
1251 }
1252
1253 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1254 {
1255 return PECI_CC_DRIVER_ERR;
1256 }
1257
1258 cmd.addr = target;
1259 cmd.subopcode = subopcode;
1260 cmd.param0 = param0;
1261 cmd.param1 = param1;
1262 cmd.param2 = param2;
1263 cmd.rx_len = u8ReadLen;
1264
1265 ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_DISC, (char*)&cmd, peci_fd);
1266 *cc = cmd.cc;
1267 if (ret == PECI_CC_SUCCESS)
1268 {
1269 memcpy(pData, cmd.data, u8ReadLen);
1270 }
1271 else
1272 {
1273 ret = PECI_CC_DRIVER_ERR;
1274 }
1275
1276 peci_Close(peci_fd);
1277 return ret;
1278}
1279
1280/*-------------------------------------------------------------------------
1281 * This function provides crashdump GetFrame data over PECI
1282 *------------------------------------------------------------------------*/
1283EPECIStatus peci_CrashDump_GetFrame(uint8_t target, uint16_t param0,
1284 uint16_t param1, uint16_t param2,
1285 uint8_t u8ReadLen, uint8_t* pData,
1286 uint8_t* cc)
1287{
1288 int peci_fd = -1;
1289 struct peci_crashdump_get_frame_msg cmd;
1290 EPECIStatus ret;
1291
1292 if (pData == NULL || cc == NULL)
1293 {
1294 return PECI_CC_INVALID_REQ;
1295 }
1296
Jason M. Bills6ca31642020-08-06 10:26:19 -07001297 // The target address must be in the valid range
1298 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1299 {
1300 return PECI_CC_INVALID_REQ;
1301 }
1302
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001303 // Per the PECI spec, the read length must be a qword or dqword
1304 if (u8ReadLen != 8 && u8ReadLen != 16)
1305 {
1306 return PECI_CC_INVALID_REQ;
1307 }
1308
1309 // The PECI buffer must be large enough to hold the requested data
1310 if (sizeof(cmd.data) < u8ReadLen)
1311 {
1312 return PECI_CC_INVALID_REQ;
1313 }
1314
1315 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1316 {
1317 return PECI_CC_DRIVER_ERR;
1318 }
1319
1320 cmd.addr = target;
1321 cmd.param0 = param0;
1322 cmd.param1 = param1;
1323 cmd.param2 = param2;
1324 cmd.rx_len = u8ReadLen;
1325
1326 ret = HW_peci_issue_cmd(PECI_IOC_CRASHDUMP_GET_FRAME, (char*)&cmd, peci_fd);
1327 *cc = cmd.cc;
1328 if (ret == PECI_CC_SUCCESS)
1329 {
1330 memcpy(pData, cmd.data, u8ReadLen);
1331 }
1332 else
1333 {
1334 ret = PECI_CC_DRIVER_ERR;
1335 }
1336
1337 peci_Close(peci_fd);
1338 return ret;
1339}
1340
1341/*-------------------------------------------------------------------------
1342 * This function provides raw PECI command access
1343 *------------------------------------------------------------------------*/
1344EPECIStatus peci_raw(uint8_t target, uint8_t u8ReadLen, const uint8_t* pRawCmd,
1345 const uint32_t cmdSize, uint8_t* pRawResp,
1346 uint32_t respSize)
1347{
1348 int peci_fd = -1;
1349 struct peci_xfer_msg cmd;
1350 uint8_t u8TxBuf[PECI_BUFFER_SIZE];
1351 uint8_t u8RxBuf[PECI_BUFFER_SIZE];
1352 EPECIStatus ret;
1353
1354 if (u8ReadLen && pRawResp == NULL)
1355 {
1356 return PECI_CC_INVALID_REQ;
1357 }
1358
Jason M. Bills6ca31642020-08-06 10:26:19 -07001359 // The target address must be in the valid range
1360 if (target < MIN_CLIENT_ADDR || target > MAX_CLIENT_ADDR)
1361 {
1362 return PECI_CC_INVALID_REQ;
1363 }
1364
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001365 if (peci_Open(&peci_fd) != PECI_CC_SUCCESS)
1366 {
1367 return PECI_CC_DRIVER_ERR;
1368 }
1369
1370 // Check for valid buffer sizes
1371 if (cmdSize > PECI_BUFFER_SIZE || respSize < u8ReadLen ||
1372 u8ReadLen >
1373 (PECI_BUFFER_SIZE - 1)) // response buffer is data + 1 status byte
1374 {
1375 peci_Close(peci_fd);
1376 return PECI_CC_INVALID_REQ;
1377 }
1378
1379 cmd.addr = target;
1380 cmd.tx_len = (uint8_t)cmdSize;
1381 cmd.rx_len = u8ReadLen;
1382
1383 memcpy(u8TxBuf, pRawCmd, cmdSize);
1384
1385 cmd.tx_buf = u8TxBuf;
1386 cmd.rx_buf = u8RxBuf;
1387 ret = HW_peci_issue_cmd(PECI_IOC_XFER, (char*)&cmd, peci_fd);
1388
1389 if (ret == PECI_CC_SUCCESS || ret == PECI_CC_TIMEOUT)
1390 {
1391 memcpy(pRawResp, u8RxBuf, u8ReadLen);
1392 }
1393
1394 peci_Close(peci_fd);
1395 return ret;
1396}
1397
1398/*-------------------------------------------------------------------------
1399 * This function returns the CPUID (Model and stepping) for the given PECI
1400 * client address
1401 *------------------------------------------------------------------------*/
1402EPECIStatus peci_GetCPUID(const uint8_t clientAddr, CPUModel* cpuModel,
1403 uint8_t* stepping, uint8_t* cc)
1404{
1405 EPECIStatus ret = PECI_CC_SUCCESS;
1406 uint32_t cpuid = 0;
1407
1408 if (cpuModel == NULL || stepping == NULL || cc == NULL)
1409 {
1410 return PECI_CC_INVALID_REQ;
1411 }
1412
Jason M. Bills6ca31642020-08-06 10:26:19 -07001413 // The client address must be in the valid range
1414 if (clientAddr < MIN_CLIENT_ADDR || clientAddr > MAX_CLIENT_ADDR)
1415 {
1416 return PECI_CC_INVALID_REQ;
1417 }
1418
Jason M. Bills7ef5a552020-04-06 14:58:44 -07001419 if (peci_Ping(clientAddr) != PECI_CC_SUCCESS)
1420 {
1421 return PECI_CC_CPU_NOT_PRESENT;
1422 }
1423
1424 ret =
1425 peci_RdPkgConfig(clientAddr, PECI_MBX_INDEX_CPU_ID, PECI_PKG_ID_CPU_ID,
1426 sizeof(uint32_t), (uint8_t*)&cpuid, cc);
1427
1428 // Separate out the model and stepping (bits 3:0) from the CPUID
1429 *cpuModel = cpuid & 0xFFFFFFF0;
1430 *stepping = (uint8_t)(cpuid & 0x0000000F);
1431 return ret;
1432}