├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── lsirec.c ├── sample_sbr ├── sbr_dell_h200e_itir.cfg ├── sbr_fujitsu_d2607_itir.cfg └── sbr_sas9211-8i_itir.cfg └── sbrtool.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.bin 2 | lsirec 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2018 Hector Martin 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 2. Redistributions in binary form must reproduce the above copyright 10 | notice, this list of conditions and the following disclaimer in the 11 | documentation and/or other materials provided with the distribution. 12 | 13 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 14 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 15 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 16 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 17 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 23 | POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CFLAGS = -Wall -O2 -std=gnu99 2 | 3 | all: lsirec 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## lsirec - LSI SAS2008/2108 HBA low-level recovery tool for Linux 2 | 3 | Currently supports reading and writing the SBR and booting the card in host 4 | boot mode. 5 | 6 | Use [lsiutil](https://github.com/exactassembly/meta-xa-stm/blob/master/recipes-support/lsiutil/files/) 7 | to crossflash between IT/IR firmwares from Linux, without vendor/product ID 8 | restrictions. 9 | 10 | ## Quick guide to cleanly crossflash between IT/IR firmwares 11 | 12 | `# lsiutil -e` 13 | 14 | Select your adapter. 15 | 16 | `46. Upload FLASH section` → `5. Complete (all sections)` 17 | 18 | Make a complete Flash backup to be safe. 19 | 20 | `67. Dump all port state` 21 | 22 | `68. Show port state summary` 23 | 24 | Copy and paste these somewhere safe. Take special note of the SAS WWID. 25 | 26 | `33. Erase non-volatile adapter storage` → `3. FLASH`, then also 27 | `8. Persistent manufacturing config pages` 28 | 29 | Wipe the whole Flash. This will take a while. Option number 3 excludes the 30 | manufacturing config pages, so you need both. 31 | 32 | `2. Download firmware (update the FLASH)` 33 | 34 | Flash the new firmware. Optionally, use 35 | `4. Download/erase BIOS and/or FCode (update the FLASH)` to flash the BIOS/EFI 36 | module (not necessary if you're not booting from the adapter). 37 | 38 | Exit lsiutil. 39 | 40 | `# ./lsirec 0000:01:00.0 readsbr sbr_backup.bin` 41 | 42 | Where 0000:01:00.0 is your PCI device ID. 43 | 44 | `# python3 sbrtool.py parse sbr_backup.bin sbr.cfg` 45 | 46 | Edit sbr.cfg with your favorite text editor. You may want to add 47 | `SASAddr = 0xYOUR_SAS_WWID` to make the SAS WWID persist in the SBR (I'm not 48 | sure which firmwares use this, but I've seen it in some SBRs). You may want to 49 | change the Subsystem VID/PID, or use another SBR as a template. 50 | 51 | `# python3 sbrtool.py build sbr.cfg sbr_new.bin` 52 | 53 | `# ./lsirec 0000:01:00.0 writesbr sbr_new.bin` 54 | 55 | Reboot and cross your fingers. 56 | 57 | When the system comes back up, if all went well, launch `lsiutil -e` again and 58 | use `18. Change SAS WWID` to update the WWID if necessary, then reboot again 59 | (this writes it to the config section in Flash, not to the SBR). 60 | 61 | *NEW*: instead of rebooting, you can use: 62 | 63 | `# ./lsirec 0000:01:00.0 reset` 64 | 65 | `# ./lsirec 0000:01:00.0 rescan` 66 | 67 | Make sure your disks are not in use if you do this. `reset` might fail if you 68 | have just flashed a new firmware. This is normal, as the adapter takes a while 69 | to copy the firmware to the backup area on first boot. Wait a few seconds and 70 | use `# ./lsirec 0000:01:00.0 info` until it returns `IOC is READY`. 71 | 72 | ## UNTESTED procedure to convert from MegaRAID to IT/IR firmware or recover a bricked card 73 | 74 | WARNING: this is completely untested. Host boot support has only been tested 75 | so far on a card that was already IT/IR. Please report back if you try this. 76 | This process initially boots the IT/IR firwmare without touching Flash, and I'm 77 | not sure if it might balk at whatever MegaRAID stuff was left there before we 78 | have a chance to wipe it. 79 | 80 | This procedure (obviously) resets the adapter, so make sure your disks are not 81 | in use and any dm/md/lvm mappings have been removed! 82 | 83 | This mode requires HugeTLB support enabled in your kernel: 84 | 85 | `# echo 16 > /proc/sys/vm/nr_hugepages` 86 | 87 | This process is also incompatible with IOMMUs. If you have one, make sure it 88 | is not active (e.g. check that `/sys/kernel/iommu_groups` is an empty 89 | directory). 90 | 91 | Make note of your SAS WWID (e.g. using MegaCLI or the kernel interfaces). 92 | 93 | `# ./lsirec 0000:01:00.0 unbind` 94 | 95 | Where 0000:01:00.0 is your PCI device ID. Unbind the kernel driver (if any). 96 | 97 | `# ./lsirec 0000:01:00.0 halt` 98 | 99 | Halt the IOP, so that the firmware will not interfere with subsequent 100 | operations. 101 | 102 | `# ./lsirec 0000:01:00.0 readsbr sbr_backup.bin` 103 | 104 | Back up your MegaRAID SBR. 105 | 106 | `# python3 sbrtool.py parse sbr_backup.bin sbr.cfg` 107 | 108 | Edit sbr.cfg with your favorite text editor. You'll probably want to use an IT 109 | SBR as a template, such as 110 | [sbr_sas9211-8i_itir.cfg](sample_sbr/sbr_sas9211-8i_itir.cfg). At the very 111 | least you need to set `PCIPID` properly (`0x0072` for SAS2008-based cards) and 112 | set `Interface` to `0x00` for IT/IR mode. You may want to add 113 | `SASAddr = 0xYOUR_SAS_WWID` to make the SAS WWID persist in the SBR (I'm not 114 | sure which firmwares use this, but I've seen it in some SBRs). 115 | 116 | `# python3 sbrtool.py build sbr.cfg sbr_new.bin` 117 | 118 | `# ./lsirec 0000:01:00.0 writesbr sbr_new.bin` 119 | 120 | Write the new IT/IR mode SBR. This does not immediately take effect. 121 | 122 | `# ./lsirec 0000:01:00.0 hostboot 2118it.bin` 123 | 124 | Where 2118it.bin is your desired firmware. If all went well, you should see 125 | something like this: 126 | 127 | ``` 128 | # ./lsirec 0000:01:00.0 hostboot 2118it.bin 129 | Device in MPT mode 130 | Resetting adapter in HCB mode... 131 | Trying unlock in MPT mode... 132 | Device in MPT mode 133 | IOC is RESET 134 | Setting up HCB... 135 | HCDW virtual: 0x7fca79e00000 136 | HCDW physical: 0x439a00000 137 | Loading firmware... 138 | Loaded 722708 bytes 139 | Booting IOC... 140 | IOC is READY 141 | IOC Host Boot successful. 142 | ``` 143 | 144 | At this point the PCI VID/PID should've changed, but the kernel will not have 145 | noticed. Check with lspci: 146 | 147 | `# lspci -vns 0000:01:00.0 -A linux-sysfs | head -n 2` 148 | 149 | `# lspci -vns 0000:01:00.0 -A intel-conf1 | head -n 2` 150 | 151 | The first command should still show the old VID/PID, but the second one should 152 | show the new (MPT mode) ones. To make the kernel notice: 153 | 154 | `# ./lsirec 0000:01:00.0 rescan` 155 | 156 | This removes the PCI device from the kernel and requests a rescan. At this point 157 | the mpt3sas kernel driver should load. Check `dmesg` for any errors. 158 | 159 | If all went well, you can use lsiutil to wipe Flash and flash your new firmware: 160 | 161 | `# lsiutil -e` 162 | 163 | Select your adapter. 164 | 165 | `46. Upload FLASH section` → `5. Complete (all sections)` 166 | 167 | Make a complete Flash backup to be safe. 168 | 169 | `33. Erase non-volatile adapter storage` → `3. FLASH`, then also 170 | `8. Persistent manufacturing config pages` 171 | 172 | Wipe the whole Flash. This will take a while. Option number 3 excludes the 173 | manufacturing config pages, so you need both. 174 | 175 | `2. Download firmware (update the FLASH)` 176 | 177 | Flash the new firmware. Optionally, use 178 | `4. Download/erase BIOS and/or FCode (update the FLASH)` to flash the BIOS/EFI 179 | module (not necessary if you're not booting from the adapter). 180 | 181 | Exit lsiutil. 182 | 183 | Finally, if all went well, reset into normal mode: 184 | 185 | `# ./lsirec 0000:01:00.0 reset` 186 | 187 | This might complain about IOC not becoming ready, but this is normal, as the 188 | first boot takes longer. Use `./lsirec 0000:01:00.0 info` after a few seconds 189 | and check for `IOC is READY`. 190 | 191 | `# ./lsirec 0000:01:00.0 rescan` 192 | 193 | If all went well, `dmesg` should show the driver loading again successfully. 194 | Launch `lsiutil -e` again and use `18. Change SAS WWID` to update the WWID 195 | if necessary, then `reset` and `rescan` again to make sure the kernel sees the 196 | new WWID. 197 | 198 | Enjoy your shiny new IT/IR-mode HBA. 199 | 200 | ## Disclaimer 201 | 202 | This has barely been tested a couple of cards. Don't blame me if this bricks or 203 | smokes your HBA. 204 | 205 | DO NOT attempt to use this tool on non-SAS2x08 chipsets. It probably won't work 206 | and may do horrible things. This tool deliberately does not check the VID/PID 207 | so it can be used on cards with wacky SBRs, but that means it will happily 208 | try to write the SBR into any random PCI device too. 209 | 210 | I have tested this on an LSI SAS2108-based MegaRAID card (Fujitsu D2616) with 211 | MegaRAID firmware and it successfully backed up the SBR, but the action 212 | triggered a PCI error in syslog (though the controller kept working). Your 213 | mileage may vary. I have not yet tried crossflashing it live to IT/IR mode. 214 | 215 | ## License 216 | 217 | 2-clause BSD. See the LICENSE file. 218 | -------------------------------------------------------------------------------- /lsirec.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #define MPI2_DOORBELL 0x00 13 | 14 | #define MPI2_DOORBELL_STATE_MASK 0xF0000000 15 | #define MPI2_DOORBELL_READY 0x10000000 16 | #define MPI2_DOORBELL_OPERATIONAL 0x20000000 17 | #define MPI2_DOORBELL_FAULT 0x40000000 18 | 19 | #define MPI2_WRSEQ 0x04 20 | 21 | #define MPI2_DIAG 0x08 22 | #define MPI2_DIAG_SBR_RELOAD 0x2000 23 | #define MPI2_DIAG_BOOTDEVICE_MASK 0x1800 24 | #define MPI2_DIAG_BOOTDEVICE_DEF 0x0000 25 | #define MPI2_DIAG_BOOTDEVICE_HCDW 0x0800 26 | #define MPI2_DIAG_CLR_FLASH_BAD_SIG 0x0400 27 | #define MPI2_DIAG_FORCE_HCB 0x0200 28 | #define MPI2_DIAG_HCB_MODE 0x0100 29 | #define MPI2_DIAG_WRITE_ENABLE 0x0080 30 | #define MPI2_DIAG_FLASH_BAD_SIG 0x0040 31 | #define MPI2_DIAG_RESET_HISTORY 0x0020 32 | #define MPI2_DIAG_RW_ENABLE 0x0010 33 | #define MPI2_DIAG_RESET_ADAPTER 0x0004 34 | #define MPI2_DIAG_HOLD_IOC_RESET 0x0002 35 | 36 | #define MPI2_DIAG_RW_DATA 0x10 37 | #define MPI2_DIAG_RW_ADDRESS_LOW 0x14 38 | #define MPI2_DIAG_RW_ADDRESS_HIGH 0x18 39 | 40 | #define MPI2_DCR_DATA 0x38 41 | #define MPI2_DCR_ADDRESS 0x3c 42 | 43 | #define MPI2_HCDW_SIZE 0x74 44 | #define MPI2_HCDW_SIZE_SIZE_MASK 0xFFFFF000 45 | #define MPI2_HCDW_SIZE_HCB_ENABLE 0x00000001 46 | 47 | #define MPI2_HCDW_ADDR_LOW 0x78 48 | #define MPI2_HCDW_ADDR_HIGH 0x7C 49 | 50 | #define MR_DIAG_RW_DATA 0x24 51 | #define MR_DIAG_RW_ADDRESS_LOW 0x28 52 | #define MR_DIAG_RW_ADDRESS_HIGH 0x2c 53 | #define MR_DIAG 0xf8 54 | #define MR_WRSEQ 0xfc 55 | 56 | #define DCR_I2C_SELECT 0x307 57 | #define DCR_SBR_CONFIG 0x340 58 | 59 | #define CHIP_I2C_BASE 0xC2100000 60 | #define CHIP_I2C_PINS (CHIP_I2C_BASE + 0x20) 61 | #define CHIP_I2C_SCL_RD 0x01 62 | #define CHIP_I2C_SDA_RD 0x02 63 | #define CHIP_I2C_SCL_DRV 0x04 64 | #define CHIP_I2C_SDA_DRV 0x08 65 | #define CHIP_I2C_RESET (CHIP_I2C_BASE + 0x24) 66 | 67 | #define EEPROM_TYPE_16BIT 0x01 68 | #define EEPROM_TYPE_8BIT 0x02 69 | 70 | #define HCDW_SIZE 0x200000 71 | 72 | typedef struct { 73 | char pci_id[16]; 74 | 75 | void *bar1; 76 | 77 | void *hcdw; 78 | 79 | uint8_t sbr_addr; 80 | int eep_type; 81 | 82 | uint32_t r_diag; 83 | uint32_t r_wrseq; 84 | uint32_t r_rw_data; 85 | uint32_t r_rw_addr_low; 86 | uint32_t r_rw_addr_high; 87 | } lsi_dev_t; 88 | 89 | static uint32_t read32(lsi_dev_t *d, uint32_t offset) 90 | { 91 | return *(volatile uint32_t *)(d->bar1 + offset); 92 | } 93 | 94 | static void write32(lsi_dev_t *d, uint32_t offset, uint32_t data) 95 | { 96 | *(volatile uint32_t *)(d->bar1 + offset) = data; 97 | } 98 | 99 | static uint32_t chip_read32(lsi_dev_t *d, uint32_t offset) 100 | { 101 | write32(d, d->r_rw_addr_high, 0); 102 | write32(d, d->r_rw_addr_low, offset); 103 | return read32(d, d->r_rw_data); 104 | } 105 | 106 | static void chip_write32(lsi_dev_t *d, uint32_t offset, uint32_t data) 107 | { 108 | write32(d, d->r_rw_addr_high, 0); 109 | write32(d, d->r_rw_addr_low, offset); 110 | write32(d, d->r_rw_data, data); 111 | } 112 | 113 | static uint32_t dcr_read32(lsi_dev_t *d, uint32_t offset) 114 | { 115 | write32(d, MPI2_DCR_ADDRESS, offset); 116 | return read32(d, MPI2_DCR_DATA); 117 | } 118 | 119 | static void dcr_write32(lsi_dev_t *d, uint32_t offset, uint32_t data) 120 | { 121 | write32(d, MPI2_DCR_ADDRESS, offset); 122 | write32(d, MPI2_DCR_DATA, data); 123 | } 124 | 125 | static void lsi_unlock(lsi_dev_t *d) 126 | { 127 | write32(d, d->r_wrseq, 0x00); 128 | write32(d, d->r_wrseq, 0x04); 129 | write32(d, d->r_wrseq, 0x0b); 130 | write32(d, d->r_wrseq, 0x02); 131 | write32(d, d->r_wrseq, 0x07); 132 | write32(d, d->r_wrseq, 0x0d); 133 | } 134 | 135 | static int lsi_reopen(lsi_dev_t *d) 136 | { 137 | uint32_t val; 138 | 139 | d->r_diag = MPI2_DIAG; 140 | d->r_wrseq = MPI2_WRSEQ; 141 | d->r_rw_addr_high = MPI2_DIAG_RW_ADDRESS_HIGH; 142 | d->r_rw_addr_low = MPI2_DIAG_RW_ADDRESS_LOW; 143 | d->r_rw_data = MPI2_DIAG_RW_DATA; 144 | 145 | val = read32(d, d->r_diag); 146 | if (val & MPI2_DIAG_WRITE_ENABLE) { 147 | write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE); 148 | printf("Device in MPT mode\n"); 149 | return 0; 150 | } 151 | 152 | if (read32(d, MR_DIAG) & MPI2_DIAG_WRITE_ENABLE) 153 | goto megaraid; 154 | 155 | printf("Trying unlock in MPT mode...\n"); 156 | lsi_unlock(d); 157 | 158 | val = read32(d, d->r_diag); 159 | if (val & MPI2_DIAG_WRITE_ENABLE) { 160 | write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE); 161 | printf("Device in MPT mode\n"); 162 | return 0; 163 | } 164 | 165 | megaraid: 166 | d->r_diag = MR_DIAG; 167 | d->r_wrseq = MR_WRSEQ; 168 | d->r_rw_addr_high = MR_DIAG_RW_ADDRESS_HIGH; 169 | d->r_rw_addr_low = MR_DIAG_RW_ADDRESS_LOW; 170 | d->r_rw_data = MR_DIAG_RW_DATA; 171 | 172 | val = read32(d, d->r_diag); 173 | if (val & MPI2_DIAG_WRITE_ENABLE) { 174 | write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE); 175 | printf("Device in MEGARAID mode\n"); 176 | return 0; 177 | } 178 | 179 | printf("Trying unlock in MEGARAID mode...\n"); 180 | lsi_unlock(d); 181 | 182 | val = read32(d, d->r_diag); 183 | if (val & MPI2_DIAG_WRITE_ENABLE) { 184 | write32(d, d->r_diag, val | MPI2_DIAG_RW_ENABLE); 185 | printf("Device in MEGARAID mode\n"); 186 | return 0; 187 | } 188 | 189 | fprintf(stderr, "Failed to unlock device\n"); 190 | 191 | return -1; 192 | } 193 | 194 | static int lsi_open(const char *pci_id, lsi_dev_t *d) 195 | { 196 | char path[128]; 197 | 198 | memset(d, 0, sizeof(*d)); 199 | 200 | if (strlen(pci_id) > 15) 201 | return -1; 202 | 203 | strcpy(d->pci_id, pci_id); 204 | 205 | sprintf(path, "/sys/bus/pci/devices/%s/resource1", pci_id); 206 | 207 | int fd = open(path, O_RDWR); 208 | if (fd < 0) { 209 | perror("open bar1"); 210 | return fd; 211 | } 212 | 213 | d->bar1 = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0); 214 | 215 | if (d->bar1 == MAP_FAILED) { 216 | perror("mmap bar1"); 217 | return -1; 218 | } 219 | 220 | close(fd); 221 | 222 | return lsi_reopen(d); 223 | } 224 | 225 | static int lsi_setup_hcdw(lsi_dev_t *d) 226 | { 227 | char path[128]; 228 | int fd; 229 | 230 | printf("Setting up HCB...\n"); 231 | 232 | // Enable bus mastering 233 | sprintf(path, "/sys/bus/pci/devices/%s/config", d->pci_id); 234 | 235 | fd = open(path, O_RDWR); 236 | if (fd < 0) { 237 | perror("open config"); 238 | return fd; 239 | } 240 | if (lseek(fd, 4, SEEK_SET) < 0) { 241 | perror("lseek cmd"); 242 | return -1; 243 | } 244 | uint16_t cmd; 245 | if (read(fd, &cmd, 2) != 2) { 246 | perror("read cmd"); 247 | return -1; 248 | } 249 | cmd |= 0x4; // bus master 250 | if (lseek(fd, 4, SEEK_SET) < 0) { 251 | perror("lseek cmd"); 252 | return -1; 253 | } 254 | if (write(fd, &cmd, 2) != 2) { 255 | perror("write cmd"); 256 | return -1; 257 | } 258 | close(fd); 259 | 260 | d->hcdw = mmap(NULL, HCDW_SIZE, PROT_READ|PROT_WRITE, 261 | MAP_PRIVATE|MAP_ANONYMOUS|MAP_HUGETLB|MAP_LOCKED, 0, 0); 262 | 263 | if (d->hcdw == MAP_FAILED) { 264 | perror("mmap hcdw"); 265 | fprintf(stderr, "Do you have hugepages enabled?\n"); 266 | fprintf(stderr, "Try: echo 16 > /proc/sys/vm/nr_hugepages\n"); 267 | return -1; 268 | } 269 | 270 | printf("HCDW virtual: %p\n", d->hcdw); 271 | 272 | fd = open("/proc/self/pagemap", O_RDONLY); 273 | if (fd < 0) { 274 | perror("open /proc/self/pagemap"); 275 | return fd; 276 | } 277 | if (lseek(fd, ((uintptr_t)d->hcdw) >> (PAGE_SHIFT - 3), SEEK_SET) < 0) { 278 | perror("lseek /proc/self/pagemap"); 279 | return -1; 280 | } 281 | uint64_t phys; 282 | if (read(fd, &phys, 8) != 8) { 283 | perror("read /proc/self/pagemap"); 284 | return -1; 285 | } 286 | close(fd); 287 | 288 | phys = (phys & ((1ULL<<55) - 1)) << PAGE_SHIFT; 289 | 290 | printf("HCDW physical: 0x%lx\n", phys); 291 | 292 | write32(d, MPI2_HCDW_ADDR_LOW, phys & 0xffffffff); 293 | write32(d, MPI2_HCDW_ADDR_HIGH, phys >> 32); 294 | write32(d, MPI2_HCDW_SIZE, (0xfffff000 & ~(HCDW_SIZE-1)) | 1); 295 | 296 | return 0; 297 | } 298 | 299 | static int lsi_disable_hcdw(lsi_dev_t *d) 300 | { 301 | write32(d, MPI2_HCDW_SIZE, 0); 302 | write32(d, MPI2_HCDW_ADDR_LOW, 0); 303 | write32(d, MPI2_HCDW_ADDR_HIGH, 0); 304 | 305 | int ret = munmap(d->hcdw, HCDW_SIZE); 306 | if (ret < 0) { 307 | perror("munmap hcdw"); 308 | return ret; 309 | } 310 | 311 | return 0; 312 | } 313 | 314 | static int lsi_unbind_driver(lsi_dev_t *d) 315 | { 316 | char path[128]; 317 | int ret; 318 | 319 | sprintf(path, "/sys/bus/pci/devices/%s/driver/unbind", d->pci_id); 320 | 321 | int fd = open(path, O_WRONLY|O_TRUNC); 322 | if (fd < 0) { 323 | if (errno == ENOENT) 324 | return 0; 325 | perror("open unbind"); 326 | return fd; 327 | } 328 | 329 | ret = write(fd, d->pci_id, strlen(d->pci_id)); 330 | if (ret != strlen(d->pci_id)) { 331 | perror("write unbind"); 332 | close(fd); 333 | return ret; 334 | } 335 | 336 | printf("Kernel driver unbound from device\n"); 337 | 338 | close(fd); 339 | return 1; 340 | } 341 | 342 | static int lsi_rescan(lsi_dev_t *d) 343 | { 344 | char path[128]; 345 | int ret; 346 | 347 | printf("Removing PCI device...\n"); 348 | 349 | sprintf(path, "/sys/bus/pci/devices/%s/remove", d->pci_id); 350 | 351 | int fd = open(path, O_WRONLY|O_TRUNC); 352 | if (fd < 0) { 353 | if (errno == ENOENT) 354 | return 0; 355 | perror("open remove"); 356 | return fd; 357 | } 358 | 359 | ret = write(fd, "1", 1); 360 | if (ret != 1) { 361 | perror("write remove"); 362 | close(fd); 363 | return ret; 364 | } 365 | 366 | close(fd); 367 | 368 | printf("Rescanning PCI bus...\n"); 369 | 370 | fd = open("/sys/bus/pci/rescan", O_WRONLY|O_TRUNC); 371 | if (fd < 0) { 372 | if (errno == ENOENT) 373 | return 0; 374 | perror("open rescan"); 375 | return fd; 376 | } 377 | 378 | ret = write(fd, "1", 1); 379 | if (ret != 1) { 380 | perror("write rescan"); 381 | close(fd); 382 | return ret; 383 | } 384 | 385 | close(fd); 386 | 387 | printf("PCI bus rescan complete.\n"); 388 | 389 | return 0; 390 | } 391 | 392 | static void i2c_delay(lsi_dev_t *d) 393 | { 394 | usleep(5); 395 | } 396 | 397 | static void set_sda(lsi_dev_t *d, int sda) 398 | { 399 | uint32_t val = chip_read32(d, CHIP_I2C_PINS); 400 | if (sda) 401 | val &= ~CHIP_I2C_SDA_DRV; 402 | else 403 | val |= CHIP_I2C_SDA_DRV; 404 | chip_write32(d, CHIP_I2C_PINS, val); 405 | } 406 | 407 | static void set_scl(lsi_dev_t *d, int scl) 408 | { 409 | uint32_t val = chip_read32(d, CHIP_I2C_PINS); 410 | if (scl) 411 | val &= ~CHIP_I2C_SCL_DRV; 412 | else 413 | val |= CHIP_I2C_SCL_DRV; 414 | chip_write32(d, CHIP_I2C_PINS, val); 415 | } 416 | 417 | static int get_sda(lsi_dev_t *d) 418 | { 419 | return !!(chip_read32(d, CHIP_I2C_PINS) & CHIP_I2C_SDA_RD); 420 | } 421 | 422 | static int wait_scl(lsi_dev_t *d) 423 | { 424 | for (int i = 0; i < 100; i++) 425 | { 426 | if (chip_read32(d, CHIP_I2C_PINS) & CHIP_I2C_SCL_RD) 427 | return 0; 428 | i2c_delay(d); 429 | } 430 | fprintf(stderr, "I2C: SCL timeout!\n"); 431 | return -1; 432 | } 433 | 434 | static void i2c_stop(lsi_dev_t *d) 435 | { 436 | i2c_delay(d); 437 | set_sda(d, 0); 438 | i2c_delay(d); 439 | set_scl(d, 1); 440 | i2c_delay(d); 441 | set_sda(d, 1); 442 | i2c_delay(d); 443 | } 444 | 445 | static void i2c_start(lsi_dev_t *d) 446 | { 447 | i2c_delay(d); 448 | set_sda(d, 1); 449 | i2c_delay(d); 450 | set_scl(d, 1); 451 | i2c_delay(d); 452 | wait_scl(d); 453 | set_sda(d, 0); 454 | i2c_delay(d); 455 | set_scl(d, 0); 456 | i2c_delay(d); 457 | } 458 | 459 | static void i2c_sendbit(lsi_dev_t *d, int bit) 460 | { 461 | set_sda(d, bit); 462 | i2c_delay(d); 463 | set_scl(d, 1); 464 | wait_scl(d); 465 | i2c_delay(d); 466 | set_scl(d, 0); 467 | i2c_delay(d); 468 | } 469 | 470 | static int i2c_getbit(lsi_dev_t *d) 471 | { 472 | set_sda(d, 1); 473 | i2c_delay(d); 474 | set_scl(d, 1); 475 | wait_scl(d); 476 | i2c_delay(d); 477 | int val = get_sda(d); 478 | set_scl(d, 0); 479 | i2c_delay(d); 480 | return val; 481 | } 482 | 483 | static void i2c_sendbyte(lsi_dev_t *d, uint8_t byte) 484 | { 485 | for (int i = 0x80; i ; i >>= 1) 486 | i2c_sendbit(d, byte & i); 487 | } 488 | 489 | static uint8_t i2c_getbyte(lsi_dev_t *d) 490 | { 491 | uint8_t val = 0; 492 | for (int i = 0x80; i ; i >>= 1) 493 | if (i2c_getbit(d)) 494 | val |= i; 495 | return val; 496 | } 497 | 498 | static int lsi_i2c_init(lsi_dev_t *d) 499 | { 500 | uint32_t val; 501 | 502 | val = dcr_read32(d, DCR_SBR_CONFIG); 503 | if (val & 2) { 504 | d->sbr_addr = 0x54; 505 | } else { 506 | d->sbr_addr = 0x50; 507 | } 508 | printf("Using I2C address 0x%02x\n", d->sbr_addr); 509 | if (val & 8) { 510 | d->eep_type = EEPROM_TYPE_16BIT; 511 | } else { 512 | d->eep_type = EEPROM_TYPE_8BIT; 513 | } 514 | printf("Using EEPROM type %d\n", d->eep_type); 515 | 516 | chip_write32(d, CHIP_I2C_RESET, 1); 517 | do { 518 | i2c_delay(d); 519 | } while (chip_read32(d, CHIP_I2C_RESET) & 1); 520 | 521 | val = dcr_read32(d, DCR_I2C_SELECT); 522 | val |= 0x800000; 523 | dcr_write32(d, DCR_I2C_SELECT, val); 524 | 525 | // Make sure things are reset 526 | for (int i = 0; i < 9; i++) 527 | i2c_sendbit(d, 1); 528 | i2c_stop(d); 529 | i2c_start(d); 530 | i2c_stop(d); 531 | 532 | return 0; 533 | } 534 | 535 | static int lsi_i2c_close(lsi_dev_t *d) 536 | { 537 | uint32_t val; 538 | 539 | chip_write32(d, CHIP_I2C_RESET, 1); 540 | do { 541 | i2c_delay(d); 542 | } while (chip_read32(d, CHIP_I2C_RESET) & 1); 543 | 544 | val = dcr_read32(d, DCR_I2C_SELECT); 545 | val &= ~0x800000; 546 | dcr_write32(d, DCR_I2C_SELECT, val); 547 | 548 | return 0; 549 | } 550 | 551 | static int lsi_i2c_read_sbr(lsi_dev_t *d, int offset, int len, uint8_t *buf) 552 | { 553 | i2c_start(d); 554 | i2c_sendbyte(d, (d->sbr_addr << 1) | 0); 555 | if (i2c_getbit(d)) { 556 | fprintf(stderr, "SBR read failed: EEPROM did not ACK address W\n"); 557 | return -1; 558 | } 559 | 560 | if (d->eep_type == EEPROM_TYPE_16BIT) { 561 | i2c_sendbyte(d, offset >> 8); 562 | if (i2c_getbit(d)) { 563 | fprintf(stderr, "SBR read failed: EEPROM did not ACK offset1\n"); 564 | return -1; 565 | } 566 | } 567 | 568 | i2c_sendbyte(d, offset & 0xff); 569 | if (i2c_getbit(d)) { 570 | fprintf(stderr, "SBR read failed: EEPROM did not ACK offset0\n"); 571 | return -1; 572 | } 573 | 574 | i2c_start(d); 575 | i2c_sendbyte(d, (d->sbr_addr << 1) | 1); 576 | if (i2c_getbit(d)) { 577 | fprintf(stderr, "SBR read failed: EEPROM did not ACK address R\n"); 578 | return -1; 579 | } 580 | 581 | for (int i = 0; i < len; i++) 582 | { 583 | buf[i] = i2c_getbyte(d); 584 | i2c_sendbit(d, i == (len - 1)); 585 | } 586 | 587 | i2c_stop(d); 588 | 589 | return 0; 590 | } 591 | 592 | static int lsi_i2c_write_sbr(lsi_dev_t *d, int offset, int len, uint8_t *buf) 593 | { 594 | for (int i = offset; i < (len + offset); i++) 595 | { 596 | i2c_start(d); 597 | i2c_sendbyte(d, (d->sbr_addr << 1) | 0); 598 | if (i2c_getbit(d)) { 599 | fprintf(stderr, "SBR write failed: EEPROM did not ACK address W\n"); 600 | return -1; 601 | } 602 | 603 | if (d->eep_type == EEPROM_TYPE_16BIT) { 604 | i2c_sendbyte(d, i >> 8); 605 | if (i2c_getbit(d)) { 606 | fprintf(stderr, "SBR write failed: EEPROM did not ACK offset1\n"); 607 | return -1; 608 | } 609 | } 610 | 611 | i2c_sendbyte(d, i & 0xff); 612 | if (i2c_getbit(d)) { 613 | fprintf(stderr, "SBR write failed: EEPROM did not ACK offset0\n"); 614 | return -1; 615 | } 616 | 617 | i2c_sendbyte(d, buf[i]); 618 | if (i2c_getbit(d)) { 619 | fprintf(stderr, "SBR write failed: EEPROM did not ACK data\n"); 620 | return -1; 621 | } 622 | i2c_stop(d); 623 | 624 | usleep(5000); 625 | } 626 | 627 | 628 | return 0; 629 | } 630 | 631 | static void print_ioc_state(lsi_dev_t *d) 632 | { 633 | uint32_t doorbell = read32(d, MPI2_DOORBELL); 634 | 635 | printf("IOC is "); 636 | if (!(doorbell & MPI2_DOORBELL_STATE_MASK)) { 637 | printf("RESET "); 638 | } 639 | if (doorbell & MPI2_DOORBELL_READY) { 640 | printf("READY "); 641 | } 642 | if (doorbell & MPI2_DOORBELL_OPERATIONAL) { 643 | printf("OPERATIONAL "); 644 | } 645 | if (doorbell & MPI2_DOORBELL_FAULT) { 646 | printf("FAULT "); 647 | } 648 | printf("\n"); 649 | } 650 | 651 | static int do_info(lsi_dev_t *d) 652 | { 653 | printf("Registers:\n"); 654 | printf(" DOORBELL: 0x%08x\n", read32(d, MPI2_DOORBELL)); 655 | printf(" DIAG: 0x%08x\n", read32(d, d->r_diag)); 656 | printf(" DCR_I2C_SELECT: 0x%08x\n", dcr_read32(d, DCR_I2C_SELECT)); 657 | printf(" DCR_SBR_SELECT: 0x%08x\n", dcr_read32(d, DCR_SBR_CONFIG)); 658 | printf(" CHIP_I2C_PINS: 0x%08x\n", chip_read32(d, CHIP_I2C_PINS)); 659 | 660 | print_ioc_state(d); 661 | 662 | return 0; 663 | } 664 | 665 | static int do_readsbr(lsi_dev_t *d, const char *filename) 666 | { 667 | uint8_t sbr[256]; 668 | int ret; 669 | 670 | ret = lsi_i2c_init(d); 671 | if (ret < 0) 672 | return ret; 673 | 674 | printf("Reading SBR...\n"); 675 | ret = lsi_i2c_read_sbr(d, 0, sizeof(sbr), sbr); 676 | if (ret < 0) 677 | return ret; 678 | 679 | int fd = open(filename, O_CREAT|O_WRONLY|O_TRUNC, 0666); 680 | ret = write(fd, sbr, sizeof(sbr)); 681 | if (ret != sizeof(sbr)) { 682 | perror("write"); 683 | return -1; 684 | } 685 | close(fd); 686 | printf("SBR saved to %s\n", filename); 687 | 688 | lsi_i2c_close(d); 689 | return 0; 690 | } 691 | 692 | static int do_writesbr(lsi_dev_t *d, const char *filename) 693 | { 694 | uint8_t sbr[256]; 695 | int ret; 696 | 697 | ret = lsi_i2c_init(d); 698 | if (ret < 0) 699 | return ret; 700 | 701 | int fd = open(filename, O_RDONLY); 702 | ret = read(fd, sbr, sizeof(sbr)); 703 | if (ret != sizeof(sbr)) { 704 | perror("read"); 705 | return -1; 706 | } 707 | close(fd); 708 | 709 | printf("Writing SBR...\n"); 710 | ret = lsi_i2c_write_sbr(d, 0, sizeof(sbr), sbr); 711 | if (ret < 0) 712 | return ret; 713 | printf("SBR written from %s\n", filename); 714 | 715 | lsi_i2c_close(d); 716 | return 0; 717 | } 718 | 719 | static int do_reset(lsi_dev_t *d) 720 | { 721 | int ret; 722 | uint32_t val; 723 | 724 | ret = lsi_unbind_driver(d); 725 | if (ret < 0) 726 | return ret; 727 | 728 | printf("Resetting adapter...\n"); 729 | val = read32(d, d->r_diag); 730 | val &= ~MPI2_DIAG_BOOTDEVICE_MASK; 731 | val &= ~MPI2_DIAG_FORCE_HCB; 732 | val &= ~MPI2_DIAG_HCB_MODE; 733 | write32(d, d->r_diag, val); 734 | 735 | usleep(100000); 736 | 737 | val = read32(d, d->r_diag); 738 | val |= MPI2_DIAG_RESET_ADAPTER; 739 | write32(d, d->r_diag, val); 740 | 741 | usleep(100000); 742 | print_ioc_state(d); 743 | 744 | for (int i = 0; i < 200; i++) { 745 | if (read32(d, MPI2_DOORBELL) & MPI2_DOORBELL_READY) 746 | break; 747 | usleep(10000); 748 | } 749 | 750 | print_ioc_state(d); 751 | 752 | if (!(read32(d, MPI2_DOORBELL) & MPI2_DOORBELL_READY)) { 753 | printf("IOC failed to become ready\n"); 754 | return -1; 755 | } 756 | 757 | return 0; 758 | } 759 | 760 | static int do_halt(lsi_dev_t *d) 761 | { 762 | int ret; 763 | uint32_t val; 764 | 765 | ret = lsi_unbind_driver(d); 766 | if (ret < 0) 767 | return ret; 768 | 769 | printf("Resetting adapter in HCB mode...\n"); 770 | val = read32(d, d->r_diag); 771 | val |= MPI2_DIAG_FORCE_HCB; 772 | write32(d, d->r_diag, val); 773 | val |= MPI2_DIAG_RESET_ADAPTER; 774 | write32(d, d->r_diag, val); 775 | 776 | usleep(1000000); 777 | 778 | lsi_reopen(d); 779 | 780 | val = read32(d, d->r_diag); 781 | val &= ~MPI2_DIAG_FORCE_HCB; 782 | write32(d, d->r_diag, val); 783 | 784 | print_ioc_state(d); 785 | 786 | if (read32(d, MPI2_DOORBELL) & MPI2_DOORBELL_STATE_MASK) { 787 | printf("IOC failed to stay in reset\n"); 788 | return -1; 789 | } 790 | 791 | return 0; 792 | } 793 | 794 | static int do_hostboot(lsi_dev_t *d, const char *filename) 795 | { 796 | int ret; 797 | uint32_t val; 798 | 799 | ret = do_halt(d); 800 | if (ret < 0) 801 | return ret; 802 | 803 | val = read32(d, d->r_diag); 804 | val |= MPI2_DIAG_CLR_FLASH_BAD_SIG; 805 | val &= ~MPI2_DIAG_BOOTDEVICE_MASK; 806 | val &= ~MPI2_DIAG_FORCE_HCB; 807 | val &= ~MPI2_DIAG_RESET_HISTORY; 808 | write32(d, d->r_diag, val); 809 | 810 | ret = lsi_setup_hcdw(d); 811 | if (ret < 0) { 812 | return ret; 813 | } 814 | 815 | printf("Loading firmware...\n"); 816 | memset(d->hcdw, 0x42, HCDW_SIZE); 817 | int fd = open(filename, O_RDONLY); 818 | if (fd < 0) { 819 | perror("open firmware"); 820 | return fd; 821 | } 822 | ssize_t length = read(fd, d->hcdw, HCDW_SIZE); 823 | if (length < 0) { 824 | perror("read firmware"); 825 | return length; 826 | } 827 | memmove(d->hcdw + HCDW_SIZE - length, d->hcdw, length); 828 | printf("Loaded %ld bytes\n", length); 829 | 830 | val = read32(d, d->r_diag); 831 | val |= MPI2_DIAG_BOOTDEVICE_HCDW; 832 | write32(d, d->r_diag, val); 833 | 834 | printf("Booting IOC...\n"); 835 | 836 | val &= ~MPI2_DIAG_HOLD_IOC_RESET; 837 | val &= ~MPI2_DIAG_FORCE_HCB; 838 | write32(d, d->r_diag, val); 839 | 840 | for (int i = 0; i < 200; i++) { 841 | if (read32(d, MPI2_DOORBELL) & MPI2_DOORBELL_READY) 842 | break; 843 | usleep(10000); 844 | } 845 | 846 | print_ioc_state(d); 847 | 848 | if (!(read32(d, MPI2_DOORBELL) & MPI2_DOORBELL_READY)) { 849 | printf("IOC failed to become ready\n"); 850 | return -1; 851 | } 852 | 853 | usleep(500000); 854 | 855 | printf("IOC Host Boot successful.\n"); 856 | 857 | lsi_disable_hcdw(d); 858 | 859 | return 0; 860 | } 861 | 862 | static int do_unbind(lsi_dev_t *d) 863 | { 864 | return lsi_unbind_driver(d); 865 | } 866 | 867 | static int do_rescan(lsi_dev_t *d) 868 | { 869 | int ret; 870 | 871 | ret = lsi_unbind_driver(d); 872 | if (ret < 0) 873 | return ret; 874 | 875 | return lsi_rescan(d); 876 | } 877 | 878 | static void usage(char *argv0) 879 | { 880 | fprintf(stderr, "Usage: %s [args...]\n", argv0); 881 | fprintf(stderr, "\n"); 882 | fprintf(stderr, "PCI ID example: 0000:01:00.0\n"); 883 | fprintf(stderr, "\n"); 884 | fprintf(stderr, "Supported operations:\n"); 885 | fprintf(stderr, " info\n"); 886 | fprintf(stderr, " Print the device state and registers\n"); 887 | fprintf(stderr, " readsbr \n"); 888 | fprintf(stderr, " Read the SBR.\n"); 889 | fprintf(stderr, " writesbr \n"); 890 | fprintf(stderr, " Write the SBR.\n"); 891 | fprintf(stderr, " *reset\n"); 892 | fprintf(stderr, " Perform a normal adapter reset. This also reloads\n"); 893 | fprintf(stderr, " the SBR.\n"); 894 | fprintf(stderr, " *halt\n"); 895 | fprintf(stderr, " Perform an HCB reset, not allowing the IOC to boot.\n"); 896 | fprintf(stderr, " *hostboot \n"); 897 | fprintf(stderr, " Reset the adapter and boot the specified firmware\n"); 898 | fprintf(stderr, " directly from host memory. Note: requires HugeTLB\n"); 899 | fprintf(stderr, " and is not compatible with IOMMUs.\n"); 900 | fprintf(stderr, " *unbind\n"); 901 | fprintf(stderr, " Unbind the kernel driver from the PCI device.\n"); 902 | fprintf(stderr, " *rescan\n"); 903 | fprintf(stderr, " Tell the kernel to remove and rescan the PCI device.\n"); 904 | fprintf(stderr, " This automatically picks up VID/PID changes and\n"); 905 | fprintf(stderr, " rebinds the driver.\n"); 906 | fprintf(stderr, "\n"); 907 | fprintf(stderr, "* Operation forcefully unbinds the kernel driver. Make\n"); 908 | fprintf(stderr, " sure your disks are not in use!\n"); 909 | fprintf(stderr, "\n"); 910 | fprintf(stderr, "Example: %s 0000:01:00.0 readsbr sbr.bin\n", argv0); 911 | fprintf(stderr, "\n"); 912 | } 913 | 914 | int main(int argc, char **argv) 915 | { 916 | if (argc < 3) { 917 | usage(argv[0]); 918 | return 1; 919 | } 920 | 921 | lsi_dev_t dev; 922 | 923 | if (lsi_open(argv[1], &dev)) 924 | return 1; 925 | 926 | if (!strcmp(argv[2], "info")) { 927 | return !!do_info(&dev); 928 | } else if (!strcmp(argv[2], "readsbr") && argc == 4) { 929 | return !!do_readsbr(&dev, argv[3]); 930 | } else if (!strcmp(argv[2], "writesbr") && argc == 4) { 931 | return !!do_writesbr(&dev, argv[3]); 932 | } else if (!strcmp(argv[2], "reset")) { 933 | return !!do_reset(&dev); 934 | } else if (!strcmp(argv[2], "halt")) { 935 | return !!do_halt(&dev); 936 | } else if (!strcmp(argv[2], "hostboot") && argc == 4) { 937 | return !!do_hostboot(&dev, argv[3]); 938 | } else if (!strcmp(argv[2], "unbind")) { 939 | return !!do_unbind(&dev); 940 | } else if (!strcmp(argv[2], "rescan")) { 941 | return !!do_rescan(&dev); 942 | } else { 943 | usage(argv[0]); 944 | return 1; 945 | } 946 | return 0; 947 | } 948 | -------------------------------------------------------------------------------- /sample_sbr/sbr_dell_h200e_itir.cfg: -------------------------------------------------------------------------------- 1 | Unk00 = 0x0022f661 2 | Unk04 = 0xb34f2000 3 | Unk08 = 0x91d802f8 4 | PCIVID = 0x1000 5 | PCIPID = 0x0072 6 | Unk10 = 0x0000 7 | HwConfig = 0x0107 8 | SubsysVID = 0x1028 9 | SubsysPID = 0x1f1c 10 | Unk18 = 0x00000000 11 | Unk1c = 0x00000000 12 | Unk20 = 0x00000000 13 | Unk24 = 0x00000000 14 | Unk28 = 0x00000000 15 | Unk2c = 0x00000000 16 | Unk30 = 0x00000000 17 | Unk34 = 0x00000000 18 | Unk38 = 0x00000000 19 | Unk3c = 0x00000000 20 | Interface = 0x00 21 | Unk41 = 0x0c 22 | Unk42 = 0x005d 23 | Unk44 = 0x145a305c 24 | Unk48 = 0x0575 25 | Unk4a = 0x00 26 | -------------------------------------------------------------------------------- /sample_sbr/sbr_fujitsu_d2607_itir.cfg: -------------------------------------------------------------------------------- 1 | Unk00 = 0x6122f661 2 | Unk04 = 0xb34f36f7 3 | Unk08 = 0x91d700f8 4 | PCIVID = 0x1000 5 | PCIPID = 0x0072 6 | Unk10 = 0x0000 7 | HwConfig = 0x0104 8 | SubsysVID = 0x1734 9 | SubsysPID = 0x1177 10 | Unk18 = 0x00000000 11 | Unk1c = 0x00000000 12 | Unk20 = 0x00000000 13 | Unk24 = 0x00000000 14 | Unk28 = 0x00000000 15 | Unk2c = 0x00000000 16 | Unk30 = 0x00000000 17 | Unk34 = 0x00000000 18 | Unk38 = 0x00000000 19 | Unk3c = 0x00000000 20 | Interface = 0x00 21 | Unk41 = 0x0c 22 | Unk42 = 0x005d 23 | Unk44 = 0x145a305c 24 | Unk48 = 0x0575 25 | Unk4a = 0x10 26 | -------------------------------------------------------------------------------- /sample_sbr/sbr_sas9211-8i_itir.cfg: -------------------------------------------------------------------------------- 1 | Unk00 = 0x6122f661 2 | Unk04 = 0xb34f36f7 3 | Unk08 = 0x91d700f8 4 | PCIVID = 0x1000 5 | PCIPID = 0x0072 6 | Unk10 = 0x0000 7 | HwConfig = 0x0107 8 | SubsysVID = 0x1000 9 | SubsysPID = 0x3020 10 | Unk18 = 0x00000000 11 | Unk1c = 0x00000000 12 | Unk20 = 0x00000000 13 | Unk24 = 0x00000000 14 | Unk28 = 0x00000000 15 | Unk2c = 0x00000000 16 | Unk30 = 0x00000000 17 | Unk34 = 0x00000000 18 | Unk38 = 0x00000000 19 | Unk3c = 0x00000000 20 | Interface = 0x00 21 | Unk41 = 0x0c 22 | Unk42 = 0x005d 23 | Unk44 = 0x145a305c 24 | Unk48 = 0x0575 25 | Unk4a = 0x10 26 | -------------------------------------------------------------------------------- /sbrtool.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python3 2 | import sys, struct 3 | 4 | MFG_FIELDS = [ 5 | ("Unk00", "I", "0x%08x"), 6 | ("Unk04", "I", "0x%08x"), 7 | ("Unk08", "I", "0x%08x"), 8 | ("PCIVID", "H", "0x%04x"), 9 | ("PCIPID", "H", "0x%04x"), 10 | ("Unk10", "H", "0x%04x"), 11 | ("HwConfig", "H", "0x%04x"), 12 | ("SubsysVID", "H", "0x%04x"), 13 | ("SubsysPID", "H", "0x%04x"), 14 | ("Unk18", "I", "0x%08x"), 15 | ("Unk1c", "I", "0x%08x"), 16 | ("Unk20", "I", "0x%08x"), 17 | ("Unk24", "I", "0x%08x"), 18 | ("Unk28", "I", "0x%08x"), 19 | ("Unk2c", "I", "0x%08x"), 20 | ("Unk30", "I", "0x%08x"), 21 | ("Unk34", "I", "0x%08x"), 22 | ("Unk38", "I", "0x%08x"), 23 | ("Unk3c", "I", "0x%08x"), 24 | ("Interface", "B", "0x%02x"), 25 | ("Unk41", "B", "0x%02x"), 26 | ("Unk42", "H", "0x%04x"), 27 | ("Unk44", "I", "0x%08x"), 28 | ("Unk48", "H", "0x%04x"), 29 | ("Unk4a", "B", "0x%02x"), 30 | ] 31 | MFG_FORMAT = "<" + "".join(i[1] for i in MFG_FIELDS) 32 | MFG_KEYS = {k: i for i, (k, _, _) in enumerate(MFG_FIELDS)} 33 | 34 | def checksum(b): 35 | return (0x5b - sum(b)) & 0xff 36 | 37 | def do_parse(fbin, fcfg): 38 | sbr = open(fbin, "rb").read() 39 | 40 | mfg = sbr[0:0x4c] 41 | mfg_2 = sbr[0x4c:0x98] 42 | 43 | if mfg != mfg_2: 44 | print("WARNING: Mfg data copies differ, using first") 45 | 46 | if mfg[-1] != checksum(mfg[:-1]): 47 | print("WARNING: Mfg data checksum error") 48 | 49 | fd = open(fcfg, "w") 50 | 51 | for (name, _, fmt), val in zip(MFG_FIELDS, struct.unpack(MFG_FORMAT, mfg[:-1])): 52 | fd.write("%s = %s\n" % (name, fmt % val)) 53 | 54 | sas_addr = sbr[0xd8:0xe0] 55 | if sas_addr != b"\x00" * 8: 56 | if sbr[0xef] != checksum(sas_addr): 57 | print("WARNING: SAS address checksum error") 58 | fd.write("SASAddr = 0x%016x\n" % struct.unpack(">Q", sas_addr)) 59 | 60 | fd.close() 61 | 62 | def do_build(fcfg, fbin): 63 | fields = [0] * len(MFG_FIELDS) 64 | 65 | sas_addr = None 66 | 67 | for line in open(fcfg, "r"): 68 | line = line.strip() 69 | k, v = line.split("=", 1) 70 | k = k.strip() 71 | v = v.strip() 72 | if k == "SASAddr": 73 | sas_addr = struct.pack(">Q", int(v, 0)) 74 | elif k in MFG_KEYS: 75 | fields[MFG_KEYS[k]] = int(v, 0) 76 | else: 77 | print("Unknown key %s" % k) 78 | sys.exit(1) 79 | 80 | mfg = struct.pack(MFG_FORMAT, *fields) 81 | mfg = mfg + bytes([checksum(mfg)]) 82 | 83 | sbr = mfg + mfg + b"\x00" * 0x40 84 | 85 | if not sas_addr: 86 | sbr += b"\x00" * 0x18 87 | else: 88 | sbr += sas_addr + b"\x00" * 0xf + bytes([checksum(sas_addr)]) 89 | 90 | sbr += b"\x00"* 16 91 | 92 | assert len(sbr) == 256 93 | with open(fbin, "wb") as fd: 94 | fd.write(sbr) 95 | 96 | if __name__ == "__main__": 97 | if len(sys.argv) != 4 or sys.argv[1] not in ("parse", "build"): 98 | print("Usage:") 99 | print(" %s parse sbr.bin sbr.cfg" % sys.argv[0]) 100 | print(" %s build sbr.cfg sbr.bin" % sys.argv[0]) 101 | sys.exit(1) 102 | elif sys.argv[1] == "parse": 103 | do_parse(sys.argv[2], sys.argv[3]) 104 | elif sys.argv[1] == "build": 105 | do_build(sys.argv[2], sys.argv[3]) 106 | --------------------------------------------------------------------------------