Skip to content

Instantly share code, notes, and snippets.

@dak180
Created December 5, 2021 06:21
Show Gist options
  • Save dak180/cd44e9957e1c4180e7eb6eb000716ee2 to your computer and use it in GitHub Desktop.
Save dak180/cd44e9957e1c4180e7eb6eb000716ee2 to your computer and use it in GitHub Desktop.
/*
* Broadcom/Avago/LSI HBA/RAID cards are made to be used in servers where there's plenty of airflow (minimum of 200 linear feet per minute (LFM)).
* These conditions are not always attainable in Desktop PCs without generating a considerable amount of noise.
* In this scenario, there's a need of constantly monitoring the IOC core temperature to dynamically regulate fan speed.
*
* Unfortunately, those idiotic devs at Broadcom couldn't be bothered to add this feature to "sas3ircu" so a DIY approach had to be followed.
*
* The IOC has internal sensors that measure the temperature of each core.
*
* This very simple PoC displays the IOC core temperature (MAX(Sensor[0->N-1])) for Broadcom/Avago/LSI HBA cards. No kernel mods required.
*
* Tested against:
*
* Broadcom/Avago/LSI 9305-16i 12Gb/s HBA
*
* Tested on:
*
* FreeBSD 13.0-RELEASE
*
* Compile with:
*
* gcc lsi_temp.c -o lsi_temp
*
* Output:
*
* ./lsi_temp /dev/mpr0
* IOC Temp: 43 (C)
* Board Temp: 0 (N/A)
*
* tami@ombra.org.uk
*/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/ioctl.h>
#include <fcntl.h>
#define MPI2_IOCSTATUS_MASK (0x7FFF)
#define MPI2_IOCSTATUS_SUCCESS (0x0000)
#define MPRIO_READ_CFG_PAGE _IOWR('M', 201, struct mpr_cfg_page_req)
#define MPRIO_READ_CFG_HEADER _IOWR('M', 200, struct mpr_cfg_page_req)
#define MPI2_CONFIG_PAGETYPE_IO_UNIT (0x00)
#define MPI2_IOUNITPAGE7_PAGEVERSION (0x05)
#define MPI2_IOUNITPAGE7_IOC_TEMP_NOT_PRESENT (0x00)
#define MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT (0x01)
#define MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS (0x02)
typedef struct _MPI2_CONFIG_PAGE_HEADER
{
uint8_t PageVersion;
uint8_t PageLength;
uint8_t PageNumber;
uint8_t PageType;
} MPI2_CONFIG_PAGE_HEADER, Mpi2ConfigPageHeader_t;
struct mpr_cfg_page_req
{
MPI2_CONFIG_PAGE_HEADER header;
uint32_t page_address;
void *buf;
int len;
uint16_t ioc_status;
};
typedef struct _MPI2_CONFIG_PAGE_IO_UNIT_7
{
MPI2_CONFIG_PAGE_HEADER Header;
uint8_t CurrentPowerMode;
uint8_t PreviousPowerMode;
uint8_t PCIeWidth;
uint8_t PCIeSpeed;
uint32_t ProcessorState;
uint32_t PowerManagementCapabilities;
uint16_t IOCTemperature;
uint8_t IOCTemperatureUnits;
uint8_t IOCSpeed;
uint16_t BoardTemperature;
uint8_t BoardTemperatureUnits;
uint8_t Reserved3;
uint32_t BoardPowerRequirement;
uint32_t PCISlotPowerAllocation;
uint8_t Flags;
uint8_t Reserved6;
uint16_t Reserved7;
uint32_t Reserved8;
} MPI2_CONFIG_PAGE_IO_UNIT_7, Mpi2IOUnitPage7_t;
/* Application specific methods */
char *utos (uint8_t unit)
{
switch (unit)
{
case MPI2_IOUNITPAGE7_IOC_TEMP_FAHRENHEIT:
return "F";
case MPI2_IOUNITPAGE7_IOC_TEMP_CELSIUS:
return "C";
default:
return "N/A";
}
}
int main (int argc, char **argv)
{
int fd = 0, ret = 0;
struct mpr_cfg_page_req req;
Mpi2IOUnitPage7_t pg7;
if (argc != 2)
{
printf("%s: </dev/mpr0>\n", argv[0]);
return 1;
}
if(geteuid() != 0)
{
printf("Error: Not root!!\n");
return 1;
}
fd = open(argv[1], O_RDWR);
if(fd == 0)
{
printf("Error: Could not open device file!!\n");
return 1;
}
/* Initialize structs */
memset(&req, 0x00, sizeof(struct mpr_cfg_page_req));
memset(&pg7, 0x00, sizeof(Mpi2IOUnitPage7_t));
/* Get config page 7 header */
req.header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
req.header.PageNumber = 7;
req.header.PageVersion = MPI2_IOUNITPAGE7_PAGEVERSION;
req.buf = (Mpi2IOUnitPage7_t *) & pg7;
req.len = sizeof (Mpi2IOUnitPage7_t);
if(req.buf == NULL)
{
printf("Error: Out of memory!!\n");
close(fd);
return 1;
}
pg7.Header.PageType = MPI2_CONFIG_PAGETYPE_IO_UNIT;
pg7.Header.PageNumber = 7;
pg7.Header.PageVersion = MPI2_IOUNITPAGE7_PAGEVERSION;
ret = ioctl(fd, MPRIO_READ_CFG_HEADER, &req);
if(ret != 0)
{
printf("Error: MPRIO_READ_CFG_HEADER: IOCTL request failed with error num %d!!\n", ret);
close (fd);
return 1;
}
if((req.ioc_status & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
{
printf("Error: MPRIO_READ_CFG_HEADER: Reply ioc_status != 0x0000 (%x)!!\n", req.ioc_status);
close(fd);
return 1;
}
/* Get config page 7 content */
memcpy(req.buf, &req.header, sizeof(MPI2_CONFIG_PAGE_HEADER));
ret = ioctl(fd, MPRIO_READ_CFG_PAGE, &req);
if(ret != 0)
{
printf("Error: MPRIO_READ_CFG_PAGE: IOCTL request failed with error num %d!!\n", ret);
close(fd);
return 1;
}
if((req.ioc_status & MPI2_IOCSTATUS_MASK) != MPI2_IOCSTATUS_SUCCESS)
{
printf("Error: MPRIO_READ_CFG_PAGE: Reply ioc_status != 0x0000 (%x)!!\n", req.ioc_status);
close(fd);
return 1;
}
/* Display info */
printf("IOC Temp: %hu (%s)\n", pg7.IOCTemperature, utos(pg7.IOCTemperatureUnits));
printf("Board Temp: %hu (%s)\n", pg7.BoardTemperature, utos(pg7.BoardTemperatureUnits));
/* Exit */
close(fd);
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment