Initial commit

This commit is contained in:
Hector Martin 2018-03-28 02:18:47 +09:00
commit d51f4643e3
9 changed files with 791 additions and 0 deletions

2
.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*.bin
lsirec

23
LICENSE Normal file
View 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.

3
Makefile Normal file
View File

@ -0,0 +1,3 @@
CFLAGS = -Wall -O2
all: lsirec

78
README.md Normal file
View 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
View 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;
}

View 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

View 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

View 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
View 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])