Zámek velikosti sektoru sedí v datovém záznamu, ne v kódu firmwaru
Osm SAS SSD z vyřazeného pole hlásí v Linuxu nulovou velikost. Než jsem začal obcházet firmware v ovladači, rozebral jsem tři dumpy SPI flash a našel místo, které o velikosti sektoru rozhoduje. Je to datový záznam, ne kus kódu.
Osm SAS SSD z vyřazeného diskového pole, všechny zdravé, a v Linuxu z nich neteče ani bajt. Mají 528bajtové sektory: 512 bajtů dat a 16 bajtů metadat, ze kterých si pole počítá vlastní kontrolu integrity. Blokové vrstvě jádra to nestačí, protože po velikosti bloku chce mocninu dvou, takže disk skončí v systému s nulovou velikostí. A firmware velikost sektoru změnit nedovolí.
Jak jsem se do téhle situace dostal a co všechno na ty disky nefunguje, je v článku Velikost sektoru zamkl výrobce disku, ne značka na štítku. Tenhle text je o jednom kroku z něj, o rozboru firmwaru.
Než jsem začal stavět obchvat v ovladači, chtěl jsem vědět, s čím vlastně bojuju. Jestli je ten zámek kus kódu, nebo jen položka v konfiguraci. Vyšlo to druhé, a je to podstatný rozdíl: kód bez podpisového klíče nepřepíšu, kdežto datový záznam s kontrolním součtem, který umím dopočítat, ano.
Tři dumpy a proč jeden nestačí
Disk se sám k obsahu své SPI flash nepustí. Jediná cesta k němu vede přes odpájení paměti a programátor, a to se mi u vlastního kusu zatím nechtělo. Na fóru Serve The Home ale visí ve vlákně o reformátování disků na 512 B tři dumpy, které stačí.
- Uživatel Arslan109 odpájel v září 2025 čip z disku ST1600FM0013 s firmwarem 6214, tedy z přesně toho modelu, který mám v serveru.
- Uživatel leromarinvit koupil o dva měsíce později nejlevnější disk téže rodiny s pravým Seagate firmwarem, dumpnul ho ve verzi 0007, upgradoval na 000A a dumpnul znovu. Obojí ověřil dvojím čtením.
Sám o sobě by mi jeden dump neřekl nic. Nevím, kde v tom binárním balíku o čtyřech megabajtech hledat, a hledat naslepo se nedá. Zajímavý je až rozdíl mezi zamčeným a nezamčeným kusem, a k tomu je potřeba mít oba.
Všechny tři soubory mají shodně 4 194 304 bajtů, tedy plnou kapacitu čipu W25Q32. Využito je z toho kolem desetiny, zbytek je výplň nulami a jedničkami. Prvních dvaatřicet bajtů je u všech tří naprosto stejných, takže formát je týž. Zajímavější je míra podobnosti:
IBM 6214 vs Seagate 000A -> 89,7 % shodných bajtů
Seagate 0007 vs Seagate 000A -> 80,0 % shodných bajtů
IBM firmware je tedy Seagate verzi podobnější, než jsou si dvě verze Seagate firmwaru navzájem. Z toho jsem odhadl, že rozdíly nebudou v jádru kódu, ale v konfiguraci. Ukázalo se, že ten odhad seděl.
Nejdřív ověřit, že formátu vůbec rozumím
Nechtěl jsem stavět na dojmu. Existuje oficiální balíček firmwaru od Seagate pro tuhle řadu a v něm soubory LOD, které umí rozebrat parser od skupiny EURECOM S3. Když se obsah LOD najde v dumpu, znamená to, že mapování mezi balíčkem a fyzickou pamětí chápu správně.
Artifact 3 type 0x22 0x9f000 B Flash address = 0xfa0 <- hlavni kod
Artifact 5 type 0x9026 0xd0028 B Flash address = 0x30
Artifact 9 type 0x1a 0x180 B <- podpis (384 B)
Obsah Artifactu 3 se v dumpech opravdu našel, a to na téže adrese:
seagate_000A -> posun 0xe0fcc
ibm_6214 -> posun 0xe0fcc stejny layout
seagate_0007 -> posun 0x10fcc starsi verze, jiny layout
Novější Seagate dump a IBM dump mají hlavní kód na stejném místě. Ten starší ne, což je jen připomínka, že layout flash se mezi verzemi firmwaru posouvá.
Konfigurace není zašifrovaná, je to prostě text
Kus, kterého jsem si všiml jako prvního, sedí na adrese 0x0e1140. Je to odpověď na příkaz INQUIRY, uložená tak, jak ji disk pošle na sběrnici:
IBM 9f 00 10 02 "IBM-SSG IBM-SSGSSVJ1P6 6214" "ZAL... 216214"
Seagate 8b 01 10 02 "SEAGATE ST200FM0133 000A" "ZAJ..."
Stejná struktura, jiný obsah. To je moment, kdy se hledání změní z luštění na čtení. Kolem té adresy jsou další záznamy a mají pravidelný tvar:
[id:1][len:2][00 00][id:1][len-4:2][namelen:1][jmeno][data]
Klasické TLV, jen s tím zvláštním rysem, že se identifikátor i délka opakují podruhé. Toho jsem nakonec využil při psaní parseru jako kontroly: záznam, kde se druhý výskyt neshoduje s prvním, je špatně zarovnaný a posunu se o bajt dál.
688 bajtů, které Seagate nemá vůbec
Pak už je to jednoduché. Vypsal jsem místa, kde má IBM data a Seagate nepopsanou flash:
0x0e1590 - 0x0e1820 656 B hlavni blok
0x0e1c70 - 0x0e1c80 16 B
0x0e1f40 - 0x0e1f50 16 B
688 B celkem
V tom hlavním bloku jsou čtyři pojmenované záznamy, které v Seagate verzi nejsou:
| Offset | ID | Délka | Jméno |
|---|---|---|---|
| 0x0e15b8 | 0xc4 | 0x28 | (samé mezery) |
| 0x0e15e4 | 0xc7 | 0xa0 | SCDD |
| 0x0e1688 | 0xc8 | 0xd8 | AIX |
| 0x0e1760 | 0xc9 | 0xac | AIX |
Jméno AIX mě zarazilo. Je to unixový systém od IBM a v dumpu
disku vypadá jako věc z jiného světa. Vysvětlení je nudné a zároveň pěkné: IBM
si do konfigurace ukládá, jak se disk má chovat ve svých polích, a pojmenovává
si ty záznamy po svých platformách.
Uvnitř záznamu 0xc8 leží block descriptor jako holá data, přesně v tom tvaru, jaký chodí po sběrnici v odpovědi na MODE SENSE:
0e1690 09 41 49 58 20 20 20 20 20 20 00 00 00 08 b4 a8
0e16a0 0a 58 00 00 02 10 ...
"AIX " b4a80a58 000210
3030911576 528
Počet logických bloků a délka bloku. Tohle je to místo. Ne kus kódu, který by kontroloval, co po disku chci, ale záznam v datech, ze kterého si firmware při startu bere výchozí geometrii.
Katalog záznamů to potvrzuje
Aby to nebyla náhoda, hledal jsem druhé doložení. Konfigurační blok má index, seznam identifikátorů záznamů, které v něm mají být:
IBM ... c0 c1 c3 [c4 c7 c8 c9] d1 d2 00 21 polozek
Seagate ... c0 c1 c3 d1 d2 00 17 polozek
Přesně ta čtyři ID navíc, o která jde. Rozdíl tedy není v tom, že by Seagate ty záznamy měl a nechal je prázdné. Nezná je vůbec.
Sedí i třetí věc. Hlavička bloku začíná magickým řetězcem yqnI
a hned za ním je délka:
0x0e1130 79 71 6e 49 | f0 06 | 79 9a | ff 00 a4 00 00 00 06 32 IBM
0x0e1130 79 71 6e 49 | 60 04 | ab 93 | ff 00 90 00 00 00 06 12 Seagate
magic "yqnI" delka soucet
1776 B
1120 B
Rozdíl obou délek je 656 bajtů, tedy přesně velikost hlavního bloku, který má IBM navíc. Tím jsem si potvrdil, jak se to pole čte, a mimochodem i to, že se při zápisu bude muset přepočítat.
Kontrolní součet: součet slov přes celý blok musí dát nulu
Zbývalo pole 79 9a, respektive ab 93. Že je to
kontrolní součet, se dá odhadnout z polohy. Horší je uhodnout algoritmus, a
zkoušet CRC naslepo je ztráta času. Našel jsem to jinde: na
fóru hddguru
někdo před lety rozebíral formát LOD a otiskl tam funkci GETSUMM
napsanou v REXXu.
Sečti šestnáctibitová slova v pořadí little endian přes celý blok včetně samotného pole kontrolního součtu. Výsledek musí být nula.
Sedí to všude, kam jsem se podíval:
IBM konfig blok @0x0e1130, delka 0x06f0 -> soucet 0x0000 OK
SGA konfig blok @0x0e1130, delka 0x0460 -> soucet 0x0000 OK
vsechny LOD hlavicky -> soucet 0x0000 OK
Když je to takhle jednoduché, dá se součet po úpravě dat dopočítat jedním odečtením. Žádná tabulka, žádný polynom.
Patch má sedm bajtů
Zbytek je mechanika. Skript najde magic yqnI, projde záznamy,
v tom pojmenovaném AIX přepíše block descriptor a dopočítá součet:
python tools/patch_ibm_lock.py vlastni_dump.bin vystup.bin --mode blocksize --blocks 3125627568
Změní se sedm bajtů:
0x0e1136-0x0e1137 kontrolni soucet 0x9a79 -> 0xad33
0x0e169e-0x0e16a1 pocet bloku b4a80a58 -> ba47d3b0 (3 125 627 568)
0x0e16a5 velikost sektoru 0x10 -> 0x00 (528 -> 512)
Nový počet bloků není odhad, je to hodnota z tabulky formátů v produktovém manuálu Seagate pro tenhle model. Kapacita zůstává na 1600,32 GB, protože těch 16 bajtů metadat si disk bere z interní rezervy, ne z uživatelského prostoru.
Dump musí být z vlastního disku
Jedna věc, na kterou při čtení fór narazíte pozdě, a mohla by stát disk. Konfigurační blok obsahuje sériové číslo a kalibrační data konkrétního kusu. Nahrát cizí dump na svůj disk znamená přepsat mu identitu identitou někoho jiného, a co udělá kalibrace z jiné NAND, netuším.
Postup je proto takový, že se ten cizí dump použije jedině jako předloha k pochopení formátu:
- odpájet paměť z vlastního disku (WSON-8, 8 krát 6 mm, u konektoru SAS),
- vyčíst ji programátorem, a to dvakrát, se srovnáním otisků,
- pustit patcher na vlastním dumpu,
- zapsat zpět a ověřit čtením.
Co jsem neudělal
Nezapsal jsem to. Nemám programátor ani horkovzduch a rozebírat kvůli tomu zdravý disk mi zatím za to nestojí. Všechno výše je tedy rozbor a nástroj, ne provedená oprava. Ukázkové výstupy, které mám, vznikly z cizího dumpu a slouží k ověření patcheru, ne k nahrání do disku.
Zůstává i jedna věcná neznámá, kterou se nedá zjistit jinak než zápisem: jestli si firmware konfigurační blok ověřuje ještě něčím kromě toho kontrolního součtu. Podpis by byl konec cesty. Že by tam byl, nic nenaznačuje, ale nenaznačuje ani opak, a to je rozdíl, který se v textu o vlastní práci má napsat rovnou.
Co z toho beru jako přenositelné: u zamčené konfigurace se vyplatí napřed hledat rozdíl proti nezamčenému kusu, ne se hrabat v kódu. Konfigurace bývá uložená čitelně, protože ji firmware musí umět rychle přečíst. A kontrolní součet, který nad ní stojí, se skoro nikdy nedělá složitě.
Kód a podklady
Všechno, na čem tenhle text stojí, je ve veřejném repozitáři st1600fm0013-528b-sectors – skripty, zálohy firmwaru i rozbory, které se do článku nevešly.
tools/dump_config_block.py– vytáhne konfigurační blok z dumputools/diff_config_records.py– porovná TLV záznamy dvou diskůtools/patch_ibm_lock.py– přepíše těch sedm bajtů a dopočítá kontrolní součetREADME_CZ.md– celý postup od dumpu po patch
Zdroje
- Serve The Home, vlákno 4968, strana 28 – dumpy SPI flash a jejich popis od lidí, kteří je pořídili
- hddguru, rozbor formátu LOD – funkce
GETSUMM - eurecom-s3/hdd_firmware_tools – parser LOD souborů
- Seagate 1200.2 SAS SSD Product Manual 100773817 Rev. D – tabulka podporovaných formátů
Dál k tématu
-
Velikost sektoru zamkl výrobce disku, ne značka na štítku
Osm SAS SSD z vyřazeného pole hlásí v Linuxu nulovou velikost. Rozebral jsem firmware, našel sedm bajtů, které za to můžou, a doportoval patch do jádra. Pak jsem pustil jednu nedestruktivní sondu a zjistil, že u jiného disku stačí jeden příkaz.
-
Nový kernel jsem vyzkoušel proti fyzickému disku bez restartu serveru
Cizí patch do ovladače sd bez hlavičky a bez stopy na internetu cílil na jádro, které nikdy neexistovalo. Popisuju, jak jsem ho posoudil, doportoval na 6.8 a vyzkoušel proti fyzickému disku v QEMU, aniž bych restartoval server.
-
Linux load average – Chápeme hodnoty příkazu uptime
Řešili jste někdy co vlastně znamenají 3 čísla na konci příkazu uptime ? load average: 0,10, 0,09, 0,11 Tato čísla obecně vyjadřují průměrné zatížení linuxového systému za období 1, 5 a 15 minut a nezahrnují žádné procesy, čekající doby i/o wait na discích nebo síti.…
-
Test rychlosti internetu (speedtest) z linuxového bashe
Řešili jste někdy otázku, jak změřit rychlost na linuxovém serveru bez GUI ? Dříve jsem to řešil stahováním 1GB souboru , ale to se pro větší rychlosti nehodí hlavně kvůli nedostatečné šířce pásma cílového serveru, tudíž přesné změření rychlosti nepřipadá v úvahu. Proto jsem…
Diskuse
Zatím tu nikdo nediskutuje.
Napsat příspěvek
Diskutovat můžete i bez účtu. S registrací se ale příspěvek zveřejní hned a nemusíte pokaždé vyplňovat jméno. Účet už máte? Přihlaste se.