mirror of
https://github.com/marcan/lsirec
synced 2024-12-26 21:22:03 +00:00
Initial commit
This commit is contained in:
commit
d51f4643e3
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
*.bin
|
||||
lsirec
|
23
LICENSE
Normal file
23
LICENSE
Normal file
@ -0,0 +1,23 @@
|
||||
Copyright (c) 2018 Hector Martin <marcan@marcan.st>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
78
README.md
Normal file
78
README.md
Normal file
@ -0,0 +1,78 @@
|
||||
## lsirec - LSI SAS2008 HBA low-level recovery tool for Linux
|
||||
|
||||
Currently supports reading and writing the SBR. In the future it may support
|
||||
cold-booting the adapter to recover bricked adapters or crossflash between
|
||||
firmwares directly from Linux.
|
||||
|
||||
Use [lsiutil](https://github.com/exactassembly/meta-xa-stm/blob/master/recipes-support/lsiutil/files/)
|
||||
to crossflash between IT/IR firmwares from Linux, without vendor/product ID
|
||||
restrictions.
|
||||
|
||||
## Quick guide to cleanly crossflash between IT/IR firmwares
|
||||
|
||||
`# lsiutil -e`
|
||||
|
||||
Select your adapter.
|
||||
|
||||
`46. Upload FLASH section` → `5. Complete (all sections)`
|
||||
|
||||
Make a complete Flash backup to be safe.
|
||||
|
||||
```
|
||||
67. Dump all port state
|
||||
68. Show port state summary
|
||||
```
|
||||
|
||||
Copy and paste these somewhere safe. Take special note of the SAS WWID.
|
||||
|
||||
`33. Erase non-volatile adapter storage` → `3. FLASH`, then also
|
||||
`8. Persistent manufacturing config pages`
|
||||
|
||||
Wipe the whole Flash. This will take a while. Option number 3 excludes the
|
||||
manufacturing config pages, so you need both.
|
||||
|
||||
`2. Download firmware (update the FLASH)`
|
||||
|
||||
Flash the new firmware. Optionally, use
|
||||
`4. Download/erase BIOS and/or FCode (update the FLASH)` to flash the BIOS/EFI
|
||||
module (not necessary if you're not booting from the adapter).
|
||||
|
||||
Exit lsiutil.
|
||||
|
||||
`# ./lsirec 0000:01:00.0 readsbr sbr_backup.bin`
|
||||
|
||||
Where 0000:01:00.0 is your PCI device ID.
|
||||
|
||||
`# python3 sbrtool.py parse sbr_backup.bin sbr.cfg`
|
||||
|
||||
Edit sbr.cfg with your favorite text editor. You may want to add
|
||||
`SASAddr = 0xYOUR_SAS_WWID` to make the SAS WWID is persist in the SBR (I'm not
|
||||
sure how/when this is used, but I've seen it in some SBRs). You may want to
|
||||
change the Subsystem VID/PID, or use another SBR as a template.
|
||||
|
||||
```
|
||||
# python3 sbrtool.py build sbr.cfg sbr_new.bin
|
||||
# ./lsirec 0000:01:00.0 writesbr sbr_new.bin
|
||||
```
|
||||
|
||||
Reboot and cross your fingers.
|
||||
|
||||
When the system comes back up, if all went well, launch `lsiutil -e` again and
|
||||
use `18. Change SAS WWID` to update the WWID if necessary, then reboot again
|
||||
(this writes it to the config section in Flash, not to the SBR).
|
||||
|
||||
## Disclaimer
|
||||
|
||||
This has barely been tested on one card. Don't blame me if this bricks or
|
||||
smokes your HBA. MegaRAID mode has not been tested yet, and you'll still need
|
||||
to flash from DOS/UEFI for now since lsiutil does not work with non-operational
|
||||
adapters under Linux.
|
||||
|
||||
DO NOT attempt to use this tool on non-SAS2008 chipsets. It probably won't work
|
||||
and may do horrible things. This tool deliberately does not check the VID/PID
|
||||
so it can be used on cards with wacky SBRs, but that means it will happily
|
||||
try to write the SBR into any random PCI device too.
|
||||
|
||||
## License
|
||||
|
||||
2-clause BSD. See the LICENSE file.
|
505
lsirec.c
Normal file
505
lsirec.c
Normal file
@ -0,0 +1,505 @@
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdint.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MPI2_DOORBELL 0x00
|
||||
|
||||
#define MPI2_WRSEQ 0x04
|
||||
|
||||
#define MPI2_DIAG 0x08
|
||||
#define MPI2_DIAG_WRITE_ENABLE 0x0080
|
||||
#define MPI2_DIAG_RW_ENABLE 0x0010
|
||||
#define MPI2_DIAG_RESET_ADAPTER 0x0004
|
||||
#define MPI2_DIAG_HOLD_IOC_RESET 0x0002
|
||||
|
||||
#define MPI2_DIAG_RW_DATA 0x10
|
||||
#define MPI2_DIAG_RW_ADDRESS_LOW 0x14
|
||||
#define MPI2_DIAG_RW_ADDRESS_HIGH 0x18
|
||||
|
||||
#define MPI2_DCR_DATA 0x38
|
||||
#define MPI2_DCR_ADDRESS 0x3c
|
||||
|
||||
#define MR_DIAG_RW_DATA 0x24
|
||||
#define MR_DIAG_RW_ADDRESS_LOW 0x28
|
||||
#define MR_DIAG_RW_ADDRESS_HIGH 0x2c
|
||||
#define MR_DIAG 0xf8
|
||||
#define MR_WRSEQ 0xfc
|
||||
|
||||
#define DCR_I2C_SELECT 0x307
|
||||
#define DCR_SBR_CONFIG 0x340
|
||||
|
||||
#define CHIP_I2C_BASE 0xC2100000
|
||||
#define CHIP_I2C_PINS (CHIP_I2C_BASE + 0x20)
|
||||
#define CHIP_I2C_SCL_RD 0x01
|
||||
#define CHIP_I2C_SDA_RD 0x02
|
||||
#define CHIP_I2C_SCL_DRV 0x04
|
||||
#define CHIP_I2C_SDA_DRV 0x08
|
||||
#define CHIP_I2C_RESET (CHIP_I2C_BASE + 0x24)
|
||||
|
||||
#define EEPROM_TYPE_16BIT 0x01
|
||||
#define EEPROM_TYPE_8BIT 0x02
|
||||
|
||||
typedef struct {
|
||||
void *bar1;
|
||||
|
||||
uint8_t sbr_addr;
|
||||
int eep_type;
|
||||
|
||||
uint32_t r_diag;
|
||||
uint32_t r_wrseq;
|
||||
uint32_t r_rw_data;
|
||||
uint32_t r_rw_addr_low;
|
||||
uint32_t r_rw_addr_high;
|
||||
} lsi_dev_t;
|
||||
|
||||
static uint32_t read32(lsi_dev_t *d, uint32_t offset)
|
||||
{
|
||||
return *(volatile uint32_t *)(d->bar1 + offset);
|
||||
}
|
||||
|
||||
static void write32(lsi_dev_t *d, uint32_t offset, uint32_t data)
|
||||
{
|
||||
*(volatile uint32_t *)(d->bar1 + offset) = data;
|
||||
}
|
||||
|
||||
static uint32_t chip_read32(lsi_dev_t *d, uint32_t offset)
|
||||
{
|
||||
write32(d, d->r_rw_addr_high, 0);
|
||||
write32(d, d->r_rw_addr_low, offset);
|
||||
return read32(d, d->r_rw_data);
|
||||
}
|
||||
|
||||
static void chip_write32(lsi_dev_t *d, uint32_t offset, uint32_t data)
|
||||
{
|
||||
write32(d, d->r_rw_addr_high, 0);
|
||||
write32(d, d->r_rw_addr_low, offset);
|
||||
write32(d, d->r_rw_data, data);
|
||||
}
|
||||
|
||||
static uint32_t dcr_read32(lsi_dev_t *d, uint32_t offset)
|
||||
{
|
||||
write32(d, MPI2_DCR_ADDRESS, offset);
|
||||
return read32(d, MPI2_DCR_DATA);
|
||||
}
|
||||
|
||||
static void dcr_write32(lsi_dev_t *d, uint32_t offset, uint32_t data)
|
||||
{
|
||||
write32(d, MPI2_DCR_ADDRESS, offset);
|
||||
write32(d, MPI2_DCR_DATA, data);
|
||||
}
|
||||
|
||||
static void lsi_unlock(lsi_dev_t *d)
|
||||
{
|
||||
write32(d, d->r_wrseq, 0x00);
|
||||
write32(d, d->r_wrseq, 0x04);
|
||||
write32(d, d->r_wrseq, 0x0b);
|
||||
write32(d, d->r_wrseq, 0x02);
|
||||
write32(d, d->r_wrseq, 0x07);
|
||||
write32(d, d->r_wrseq, 0x0d);
|
||||
}
|
||||
|
||||
static int lsi_open(const char *pci_id, lsi_dev_t *d)
|
||||
{
|
||||
char path[128];
|
||||
|
||||
memset(d, 0, sizeof(*d));
|
||||
|
||||
if (strlen(pci_id) > 16)
|
||||
return -1;
|
||||
|
||||
sprintf(path, "/sys/bus/pci/devices/%s/resource1", pci_id);
|
||||
|
||||
int fd = open(path, O_RDWR);
|
||||
if (fd < 0) {
|
||||
perror("open bar1");
|
||||
return fd;
|
||||
}
|
||||
|
||||
d->bar1 = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
|
||||
if (d->bar1 == MAP_FAILED) {
|
||||
perror("mmap bar1");
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(fd);
|
||||
|
||||
uint32_t val;
|
||||
|
||||
d->r_diag = MPI2_DIAG;
|
||||
d->r_wrseq = MPI2_WRSEQ;
|
||||
d->r_rw_addr_high = MPI2_DIAG_RW_ADDRESS_HIGH;
|
||||
d->r_rw_addr_low = MPI2_DIAG_RW_ADDRESS_LOW;
|
||||
d->r_rw_data = MPI2_DIAG_RW_DATA;
|
||||
|
||||
val = read32(d, d->r_diag);
|
||||
if (val & MPI2_DIAG_WRITE_ENABLE) {
|
||||
write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE);
|
||||
printf("Device in MPT mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Trying unlock in MPT mode...\n");
|
||||
lsi_unlock(d);
|
||||
|
||||
val = read32(d, d->r_diag);
|
||||
if (val & MPI2_DIAG_WRITE_ENABLE) {
|
||||
write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE);
|
||||
printf("Device in MPT mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
d->r_diag = MR_DIAG;
|
||||
d->r_wrseq = MR_WRSEQ;
|
||||
d->r_rw_addr_high = MR_DIAG_RW_ADDRESS_HIGH;
|
||||
d->r_rw_addr_low = MR_DIAG_RW_ADDRESS_LOW;
|
||||
d->r_rw_data = MR_DIAG_RW_DATA;
|
||||
|
||||
val = read32(d, d->r_diag);
|
||||
if (val & MPI2_DIAG_WRITE_ENABLE) {
|
||||
write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE);
|
||||
printf("Device in MEGARAID mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
printf("Trying unlock in MEGARAID mode...\n");
|
||||
lsi_unlock(d);
|
||||
|
||||
val = read32(d, d->r_diag);
|
||||
if (val & MPI2_DIAG_WRITE_ENABLE) {
|
||||
write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE);
|
||||
printf("Device in MEGARAID mode\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Failed to unlock device\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void i2c_delay(lsi_dev_t *d)
|
||||
{
|
||||
usleep(5);
|
||||
}
|
||||
|
||||
static void set_sda(lsi_dev_t *d, int sda)
|
||||
{
|
||||
uint32_t val = chip_read32(d, CHIP_I2C_PINS);
|
||||
if (sda)
|
||||
val &= ~CHIP_I2C_SDA_DRV;
|
||||
else
|
||||
val |= CHIP_I2C_SDA_DRV;
|
||||
chip_write32(d, CHIP_I2C_PINS, val);
|
||||
}
|
||||
|
||||
static void set_scl(lsi_dev_t *d, int scl)
|
||||
{
|
||||
uint32_t val = chip_read32(d, CHIP_I2C_PINS);
|
||||
if (scl)
|
||||
val &= ~CHIP_I2C_SCL_DRV;
|
||||
else
|
||||
val |= CHIP_I2C_SCL_DRV;
|
||||
chip_write32(d, CHIP_I2C_PINS, val);
|
||||
}
|
||||
|
||||
static int get_sda(lsi_dev_t *d)
|
||||
{
|
||||
return !!(chip_read32(d, CHIP_I2C_PINS) & CHIP_I2C_SDA_RD);
|
||||
}
|
||||
|
||||
static int wait_scl(lsi_dev_t *d)
|
||||
{
|
||||
for (int i = 0; i < 100; i++)
|
||||
{
|
||||
if (chip_read32(d, CHIP_I2C_PINS) & CHIP_I2C_SCL_RD)
|
||||
return 0;
|
||||
i2c_delay(d);
|
||||
}
|
||||
fprintf(stderr, "I2C: SCL timeout!\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
void i2c_stop(lsi_dev_t *d)
|
||||
{
|
||||
i2c_delay(d);
|
||||
set_sda(d, 0);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 1);
|
||||
i2c_delay(d);
|
||||
set_sda(d, 1);
|
||||
i2c_delay(d);
|
||||
}
|
||||
|
||||
void i2c_start(lsi_dev_t *d)
|
||||
{
|
||||
i2c_delay(d);
|
||||
set_sda(d, 1);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 1);
|
||||
i2c_delay(d);
|
||||
wait_scl(d);
|
||||
set_sda(d, 0);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 0);
|
||||
i2c_delay(d);
|
||||
}
|
||||
|
||||
void i2c_sendbit(lsi_dev_t *d, int bit)
|
||||
{
|
||||
set_sda(d, bit);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 1);
|
||||
wait_scl(d);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 0);
|
||||
i2c_delay(d);
|
||||
}
|
||||
|
||||
int i2c_getbit(lsi_dev_t *d)
|
||||
{
|
||||
set_sda(d, 1);
|
||||
i2c_delay(d);
|
||||
set_scl(d, 1);
|
||||
wait_scl(d);
|
||||
i2c_delay(d);
|
||||
int val = get_sda(d);
|
||||
set_scl(d, 0);
|
||||
i2c_delay(d);
|
||||
return val;
|
||||
}
|
||||
|
||||
void i2c_sendbyte(lsi_dev_t *d, uint8_t byte)
|
||||
{
|
||||
for (int i = 0x80; i ; i >>= 1)
|
||||
i2c_sendbit(d, byte & i);
|
||||
}
|
||||
|
||||
uint8_t i2c_getbyte(lsi_dev_t *d)
|
||||
{
|
||||
uint8_t val = 0;
|
||||
for (int i = 0x80; i ; i >>= 1)
|
||||
if (i2c_getbit(d))
|
||||
val |= i;
|
||||
return val;
|
||||
}
|
||||
|
||||
static int lsi_i2c_init(lsi_dev_t *d)
|
||||
{
|
||||
uint32_t val;
|
||||
|
||||
val = dcr_read32(d, DCR_SBR_CONFIG);
|
||||
if (val & 2) {
|
||||
d->sbr_addr = 0x54;
|
||||
} else {
|
||||
d->sbr_addr = 0x50;
|
||||
}
|
||||
printf("Using I2C address 0x%02x\n", d->sbr_addr);
|
||||
if (val & 8) {
|
||||
d->eep_type = EEPROM_TYPE_16BIT;
|
||||
} else {
|
||||
d->eep_type = EEPROM_TYPE_8BIT;
|
||||
}
|
||||
printf("Using EEPROM type %d\n", d->eep_type);
|
||||
|
||||
chip_write32(d, CHIP_I2C_RESET, 1);
|
||||
do {
|
||||
i2c_delay(d);
|
||||
} while (chip_read32(d, CHIP_I2C_RESET) & 1);
|
||||
|
||||
val = dcr_read32(d, DCR_I2C_SELECT);
|
||||
val |= 0x800000;
|
||||
dcr_write32(d, DCR_I2C_SELECT, val);
|
||||
|
||||
// Make sure things are reset
|
||||
for (int i = 0; i < 9; i++)
|
||||
i2c_sendbit(d, 1);
|
||||
i2c_stop(d);
|
||||
i2c_start(d);
|
||||
i2c_stop(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsi_i2c_read_sbr(lsi_dev_t *d, int offset, int len, uint8_t *buf)
|
||||
{
|
||||
i2c_start(d);
|
||||
i2c_sendbyte(d, (d->sbr_addr << 1) | 0);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR read failed: EEPROM did not ACK address W\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d->eep_type == EEPROM_TYPE_16BIT) {
|
||||
i2c_sendbyte(d, offset >> 8);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR read failed: EEPROM did not ACK offset1\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_sendbyte(d, offset & 0xff);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR read failed: EEPROM did not ACK offset0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_start(d);
|
||||
i2c_sendbyte(d, (d->sbr_addr << 1) | 1);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR read failed: EEPROM did not ACK address R\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
buf[i] = i2c_getbyte(d);
|
||||
i2c_sendbit(d, i == (len - 1));
|
||||
}
|
||||
|
||||
i2c_stop(d);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int lsi_i2c_write_sbr(lsi_dev_t *d, int offset, int len, uint8_t *buf)
|
||||
{
|
||||
for (int i = offset; i < (len + offset); i++)
|
||||
{
|
||||
i2c_start(d);
|
||||
i2c_sendbyte(d, (d->sbr_addr << 1) | 0);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR write failed: EEPROM did not ACK address W\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (d->eep_type == EEPROM_TYPE_16BIT) {
|
||||
i2c_sendbyte(d, i >> 8);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR write failed: EEPROM did not ACK offset1\n");
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
i2c_sendbyte(d, i & 0xff);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR write failed: EEPROM did not ACK offset0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
i2c_sendbyte(d, buf[i]);
|
||||
if (i2c_getbit(d)) {
|
||||
fprintf(stderr, "SBR write failed: EEPROM did not ACK data\n");
|
||||
return -1;
|
||||
}
|
||||
i2c_stop(d);
|
||||
|
||||
usleep(5000);
|
||||
}
|
||||
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_info(lsi_dev_t *d) {
|
||||
printf("Registers:\n");
|
||||
printf(" DOORBELL: 0x%08x\n", read32(d, MPI2_DOORBELL));
|
||||
printf(" DIAG: 0x%08x\n", read32(d, d->r_diag));
|
||||
printf(" DCR_I2C_SELECT: 0x%08x\n", dcr_read32(d, DCR_I2C_SELECT));
|
||||
printf(" DCR_SBR_SELECT: 0x%08x\n", dcr_read32(d, DCR_SBR_CONFIG));
|
||||
printf(" CHIP_I2C_PINS: 0x%08x\n", chip_read32(d, CHIP_I2C_PINS));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_readsbr(lsi_dev_t *d, const char *filename) {
|
||||
uint8_t sbr[256];
|
||||
int ret;
|
||||
|
||||
ret = lsi_i2c_init(d);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
printf("Reading SBR...\n");
|
||||
ret = lsi_i2c_read_sbr(d, 0, sizeof(sbr), sbr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666);
|
||||
ret = write(fd, sbr, sizeof(sbr));
|
||||
if (ret != sizeof(sbr)) {
|
||||
perror("write");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
printf("SBR saved to %s\n", filename);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int do_writesbr(lsi_dev_t *d, const char *filename) {
|
||||
uint8_t sbr[256];
|
||||
int ret;
|
||||
|
||||
ret = lsi_i2c_init(d);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
int fd = open(filename, O_RDONLY);
|
||||
ret = read(fd, sbr, sizeof(sbr));
|
||||
if (ret != sizeof(sbr)) {
|
||||
perror("read");
|
||||
return -1;
|
||||
}
|
||||
close(fd);
|
||||
|
||||
printf("Writing SBR...\n");
|
||||
ret = lsi_i2c_write_sbr(d, 0, sizeof(sbr), sbr);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
printf("SBR written from %s\n", filename);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void usage(char *argv0)
|
||||
{
|
||||
fprintf(stderr, "Usage: %s <PCI ID> <operation> [args...]\n", argv0);
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "PCI ID example: 0000:01:00.0\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Supported operations:\n");
|
||||
fprintf(stderr, " info\n");
|
||||
fprintf(stderr, " readsbr <sbr.bin>\n");
|
||||
fprintf(stderr, " writesbr <sbr.bin>\n");
|
||||
fprintf(stderr, "\n");
|
||||
fprintf(stderr, "Example: %s 0000:01:00.0 readsbr sbr.bin\n", argv0);
|
||||
fprintf(stderr, "\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
if (argc < 3) {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
|
||||
lsi_dev_t dev;
|
||||
|
||||
if (lsi_open(argv[1], &dev))
|
||||
return 1;
|
||||
|
||||
if (!strcmp(argv[2], "info")) {
|
||||
return !!do_info(&dev);
|
||||
} else if (!strcmp(argv[2], "readsbr") && argc == 4) {
|
||||
return !!do_readsbr(&dev, argv[3]);
|
||||
} else if (!strcmp(argv[2], "writesbr") && argc == 4) {
|
||||
return !!do_writesbr(&dev, argv[3]);
|
||||
} else {
|
||||
usage(argv[0]);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
25
sample_sbr/sbr_dell_h200e_itir.cfg
Normal file
25
sample_sbr/sbr_dell_h200e_itir.cfg
Normal file
@ -0,0 +1,25 @@
|
||||
Unk00 = 0x0022f661
|
||||
Unk04 = 0xb34f2000
|
||||
Unk08 = 0x91d802f8
|
||||
PCIVID = 0x1000
|
||||
PCIPID = 0x0072
|
||||
Unk10 = 0x0000
|
||||
HwConfig = 0x0107
|
||||
SubsysVID = 0x1028
|
||||
SubsysPID = 0x1f1c
|
||||
Unk18 = 0x00000000
|
||||
Unk1c = 0x00000000
|
||||
Unk20 = 0x00000000
|
||||
Unk24 = 0x00000000
|
||||
Unk28 = 0x00000000
|
||||
Unk2c = 0x00000000
|
||||
Unk30 = 0x00000000
|
||||
Unk34 = 0x00000000
|
||||
Unk38 = 0x00000000
|
||||
Unk3c = 0x00000000
|
||||
Interface = 0x00
|
||||
Unk41 = 0x0c
|
||||
Unk42 = 0x005d
|
||||
Unk44 = 0x145a305c
|
||||
Unk48 = 0x0575
|
||||
Unk4a = 0x00
|
25
sample_sbr/sbr_fujitsu_d2607_itir.cfg
Normal file
25
sample_sbr/sbr_fujitsu_d2607_itir.cfg
Normal file
@ -0,0 +1,25 @@
|
||||
Unk00 = 0x6122f661
|
||||
Unk04 = 0xb34f36f7
|
||||
Unk08 = 0x91d700f8
|
||||
PCIVID = 0x1000
|
||||
PCIPID = 0x0072
|
||||
Unk10 = 0x0000
|
||||
HwConfig = 0x0104
|
||||
SubsysVID = 0x1734
|
||||
SubsysPID = 0x1177
|
||||
Unk18 = 0x00000000
|
||||
Unk1c = 0x00000000
|
||||
Unk20 = 0x00000000
|
||||
Unk24 = 0x00000000
|
||||
Unk28 = 0x00000000
|
||||
Unk2c = 0x00000000
|
||||
Unk30 = 0x00000000
|
||||
Unk34 = 0x00000000
|
||||
Unk38 = 0x00000000
|
||||
Unk3c = 0x00000000
|
||||
Interface = 0x00
|
||||
Unk41 = 0x0c
|
||||
Unk42 = 0x005d
|
||||
Unk44 = 0x145a305c
|
||||
Unk48 = 0x0575
|
||||
Unk4a = 0x10
|
25
sample_sbr/sbr_sas9211-8i_itir.cfg
Normal file
25
sample_sbr/sbr_sas9211-8i_itir.cfg
Normal file
@ -0,0 +1,25 @@
|
||||
Unk00 = 0x6122f661
|
||||
Unk04 = 0xb34f36f7
|
||||
Unk08 = 0x91d700f8
|
||||
PCIVID = 0x1000
|
||||
PCIPID = 0x0072
|
||||
Unk10 = 0x0000
|
||||
HwConfig = 0x0107
|
||||
SubsysVID = 0x1000
|
||||
SubsysPID = 0x3020
|
||||
Unk18 = 0x00000000
|
||||
Unk1c = 0x00000000
|
||||
Unk20 = 0x00000000
|
||||
Unk24 = 0x00000000
|
||||
Unk28 = 0x00000000
|
||||
Unk2c = 0x00000000
|
||||
Unk30 = 0x00000000
|
||||
Unk34 = 0x00000000
|
||||
Unk38 = 0x00000000
|
||||
Unk3c = 0x00000000
|
||||
Interface = 0x00
|
||||
Unk41 = 0x0c
|
||||
Unk42 = 0x005d
|
||||
Unk44 = 0x145a305c
|
||||
Unk48 = 0x0575
|
||||
Unk4a = 0x10
|
105
sbrtool.py
Normal file
105
sbrtool.py
Normal file
@ -0,0 +1,105 @@
|
||||
#!/usr/bin/python3
|
||||
import sys, struct
|
||||
|
||||
MFG_FIELDS = [
|
||||
("Unk00", "I", "0x%08x"),
|
||||
("Unk04", "I", "0x%08x"),
|
||||
("Unk08", "I", "0x%08x"),
|
||||
("PCIVID", "H", "0x%04x"),
|
||||
("PCIPID", "H", "0x%04x"),
|
||||
("Unk10", "H", "0x%04x"),
|
||||
("HwConfig", "H", "0x%04x"),
|
||||
("SubsysVID", "H", "0x%04x"),
|
||||
("SubsysPID", "H", "0x%04x"),
|
||||
("Unk18", "I", "0x%08x"),
|
||||
("Unk1c", "I", "0x%08x"),
|
||||
("Unk20", "I", "0x%08x"),
|
||||
("Unk24", "I", "0x%08x"),
|
||||
("Unk28", "I", "0x%08x"),
|
||||
("Unk2c", "I", "0x%08x"),
|
||||
("Unk30", "I", "0x%08x"),
|
||||
("Unk34", "I", "0x%08x"),
|
||||
("Unk38", "I", "0x%08x"),
|
||||
("Unk3c", "I", "0x%08x"),
|
||||
("Interface", "B", "0x%02x"),
|
||||
("Unk41", "B", "0x%02x"),
|
||||
("Unk42", "H", "0x%04x"),
|
||||
("Unk44", "I", "0x%08x"),
|
||||
("Unk48", "H", "0x%04x"),
|
||||
("Unk4a", "B", "0x%02x"),
|
||||
]
|
||||
MFG_FORMAT = "<" + "".join(i[1] for i in MFG_FIELDS)
|
||||
MFG_KEYS = {k: i for i, (k, _, _) in enumerate(MFG_FIELDS)}
|
||||
|
||||
def checksum(b):
|
||||
return (0x5b - sum(b)) & 0xff
|
||||
|
||||
def do_parse(fbin, fcfg):
|
||||
sbr = open(fbin, "rb").read()
|
||||
|
||||
mfg = sbr[0:0x4c]
|
||||
mfg_2 = sbr[0x4c:0x98]
|
||||
|
||||
if mfg != mfg_2:
|
||||
print("WARNING: Mfg data copies differ, using first")
|
||||
|
||||
if mfg[-1] != checksum(mfg[:-1]):
|
||||
print("WARNING: Mfg data checksum error")
|
||||
|
||||
fd = open(fcfg, "w")
|
||||
|
||||
for (name, _, fmt), val in zip(MFG_FIELDS, struct.unpack(MFG_FORMAT, mfg[:-1])):
|
||||
fd.write("%s = %s\n" % (name, fmt % val))
|
||||
|
||||
sas_addr = sbr[0xd8:0xe0]
|
||||
if sas_addr != b"\x00" * 8:
|
||||
if sbr[0xef] != checksum(sas_addr):
|
||||
print("WARNING: SAS address checksum error")
|
||||
fd.write("SASAddr = 0x%016x\n" % struct.unpack(">Q", sas_addr))
|
||||
|
||||
fd.close()
|
||||
|
||||
def do_build(fcfg, fbin):
|
||||
fields = [0] * len(MFG_FIELDS)
|
||||
|
||||
sas_addr = None
|
||||
|
||||
for line in open(fcfg, "r"):
|
||||
line = line.strip()
|
||||
k, v = line.split("=", 1)
|
||||
k = k.strip()
|
||||
v = v.strip()
|
||||
if k == "SASAddr":
|
||||
sas_addr = struct.pack(">Q", int(v, 0))
|
||||
elif k in MFG_KEYS:
|
||||
fields[MFG_KEYS[k]] = int(v, 0)
|
||||
else:
|
||||
print("Unknown key %s" % k)
|
||||
sys.exit(1)
|
||||
|
||||
mfg = struct.pack(MFG_FORMAT, *fields)
|
||||
mfg = mfg + bytes([checksum(mfg)])
|
||||
|
||||
sbr = mfg + mfg + b"\x00" * 0x40
|
||||
|
||||
if not sas_addr:
|
||||
sbr += b"\x00" * 0x18
|
||||
else:
|
||||
sbr += sas_addr + b"\x00" * 0xf + bytes([checksum(sas_addr)])
|
||||
|
||||
sbr += b"\x00"* 16
|
||||
|
||||
assert len(sbr) == 256
|
||||
with open(fbin, "wb") as fd:
|
||||
fd.write(sbr)
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 4 or sys.argv[1] not in ("parse", "build"):
|
||||
print("Usage:")
|
||||
print(" %s parse sbr.bin sbr.cfg" % sys.argv[0])
|
||||
print(" %s build sbr.cfg sbr.bin" % sys.argv[0])
|
||||
sys.exit(1)
|
||||
elif sys.argv[1] == "parse":
|
||||
do_parse(sys.argv[2], sys.argv[3])
|
||||
elif sys.argv[1] == "build":
|
||||
do_build(sys.argv[2], sys.argv[3])
|
Loading…
Reference in New Issue
Block a user