X9SAE-V BIOS Reflash

My wife “returned” a PC I built for her office years ago, as she no longer needed it. It’d been in storage for some years, so I figured I’d check on it before breaking it down to parts and storing them, as I do.

Manufacturer photo of an X9SAE-V

Disappointingly, it failed to boot. The short version is that the BIOS was lightly corrupted, so I desoldered it, reflashed it, put it back on, and the board was fine. I’ll describe here the issues it presented with, and the minor hurdles encountered, so you can recognize the problem and have a smoother time of dealing with it than I did.

Symptoms

With the board on the bench, all of the extraneous widgets removed, and a selection of independently tested CPUs and RAM, we had the following behaviors on boot (with all combinations of CPU and RAM):

  1. Without RAM, it beeps.
  2. With RAM, it reboots a few times like it’s doing memory training.
  3. Never progresses further than that.
  4. Never produces the “no video adapter” beeps, even when there isn’t a video adapter.
  5. Definitely never displays anything.
  6. No amount of resetting the CMOS settings (with the little jumper) had any effect.

This suggested that the CPU and chipset were doing something. The board wasn’t totally dead. It helped that it was clean and I knew it had been in climate controlled storage. No leakage from the coin cell battery.

I figured that the possible issues were something like:

  1. Damage to the DIMM socket or traces.
  2. BIOS was corrupted.

Sockets and traces all looked fine, nobody had been in the case since me some years prior. Couldn’t do much about either of those anyways. BIOS corruption could be dealt with.

Hardware Process

Vicinity of the motherboard PCB around the flash chip

The BIOS is stored in a W25Q128 family 8-SOIC 208mil wide part. Surface mounted, no socket alas. (The ASUS I was testing components in had a nice socketed 8-DIP BIOS. Every time I looked at it, I sighed.) I tried using an 8-SOIC clip-on adapter to reflash it in-circuit, but had no luck. I assume other things were loading the signals. I moved on to the desoldering plan.

The motherboard is certainly multilayer, and was assembled in the RoHS era with what has to be lead-free solder. Having learned my lesson from some other boards, I built a little kapton and aluminum foil shield around neighboring parts, and hit the BIOS with hot air. So much hot air, for so long. And it had no effect at all.

Since replacement parts are easily acquired on DigiKey and Mouser, I figured I could be a little rougher. I took the soldering iron, liquified the solder on each pin and lifted it with tweezers. Did one side, and then as I recall I was able to sort of lay my iron across all four pins on the other side and get them all at once. This is a slightly fiddly procedure. You only need light pressure on the pins while lifting them; the goal is to get them out of the solder puddle, not to bend them 45 degrees. Same thing with lifting the chip while heating four pins at once. You’re pulling on something that weighs a fraction of a gram; any force at all will lift it from the board the instant the solder is all liquid. If you haven’t done this before, watch a bunch of YouTube soldering videos. An 8-SOIC is about as easy an introduction to surface mount soldering as you can ask for, but you don’t want to lift pads off the PCB and turn it into a challenging repair.

Chip off the board, I straightened up the pins, put some low temp leaded solder on the pads, and cleaned it back off with wick, to leave them clean and flat. A little heat on the pins and I sort of smeared the remaining solder around on them so the chip would fit into a socket. It read out just fine; most of the remaining real difficulty was in prepping the new BIOS. I’ll cover that in the next section and finish describing what I did with the hardware.

With the new image complete, and flashed onto the chip, I was concerned that I might’ve done it wrong, and thus need to iterate on the flashing/testing process, so I was afraid to just solder the chip back down. Instead I soldered 8 female dupont jumper wires onto the 8-SOIC pads and hooked them up to the socket. I plunked the chip in, and booted the system without problem. While you can skip this step, because you’ve got the next section and this whole thing to reassure you that it’ll be fine, at least you now know that the board will boot with the BIOS hanging off the board by 3-4 inches of loose wire.

Vicinity of the motherboard PCB around the flash chip

After I confirmed it booted, I removed the wires and soldered the chip straight down with the lowest temperature leaded solder I had on hand. Hopefully that will make it easier for hot air to remove in the future, if necessary. And of course, it still worked after that.

(I used a T48 and minipro for reading and writing to the flash. There are dozens or hundreds of ways of accomplishing this. A Raspberry Pi, or a Pico, and some wires, can do it. A 3.3V parallel port and some wires could probably do it, too, if you were patient enough.)

Software Process

The chip is 128 Mbit, or 16 MiB. You can get the latest BIOS for the board from Supermicro (2.3a, X9SAE1_106.zip), and find inside of it a 16MiB X9SAE1.106. ifdtool -d on your dumped BIOS and the one from Supermicro should be similar. My image’s descriptor table had read/write access enabled for all areas, whereas the Supermicro image has most access disabled. That controls the running system’s access to the various regions of flash. The old permissions had been fine, so I figured I’d retain them.

Using ifdtool -x I extracted the regions from my BIOS and the Supermicro BIOS into four files each.

[jkominek@kyatt x9sae]$ /usr/sbin/ifdtool -x dumped-x9saev.rom
Warning: No platform specified. Output may be incomplete
File dumped-x9saev.rom is 16777216 bytes
  Flash Region 0 (Flash Descriptor): 00000000 - 00000fff
  Flash Region 1 (BIOS): 00c00000 - 00ffffff
  Flash Region 2 (Intel ME): 00003000 - 00bfffff
  Flash Region 3 (GbE): 00001000 - 00002fff
  Flash Region 4 (Platform Data): 00fff000 - 00000fff (unused)
[jkominek@kyatt x9sae]$ ls -l flashregion*
total 5953
-rwx------+ 1 jkominek     4096 Oct 19 11:33 flashregion_0_flashdescriptor.bin
-rwx------+ 1 jkominek  4194304 Oct 19 11:33 flashregion_1_bios.bin
-rwx------+ 1 jkominek 12570624 Oct 19 11:33 flashregion_2_intel_me.bin
-rwx------+ 1 jkominek     8192 Oct 19 11:33 flashregion_3_gbe.bin

I wanted to keep my original flash descriptor, and my GbE data, since that had my MAC addresses. None of the checksums matched with the vendor BIOS, which isn’t surprising. I assumed I needed to keep the BIOS and Intel ME matching, and they’re the “meat” of the image, so I chose to inject my old descriptor and GbE data into the new vendor image, which worked fine for me:

cp X9SAE1.106 repaired.rom
ifdtool -i Descriptor:board-orig/flashregion_0_flashdescriptor.bin repaired.rom
mv repaired.rom.new repaired.rom
ifdtool -i gbe:board-orig/flashregion_3_gbe.bin repaired.rom
mv repaired.rom.new repaired.rom

repaired.rom will be 16MiB and requires no further preparation before writing to the chip.

What was corrupted?

This is bonus points: what had gone wrong? It’s possible that my particular failure mechanism is because of where the BIOS was corrupted. If the bit had been in some text used in the BIOS configuration menu, I’d have never noticed.

The descriptor and GbE sections didn’t matter, I was replacing those. ME Analyzer didn’t report any CRC errors on the Intel ME section.

uefiextract produced some errors, though:

[jkominek@kyatt x9sae]$ uefiextract dumped/flashregion_1_bios.bin
parseFileHeader: invalid data checksum D8h, should be 98h
parseFileHeader: file alignment 10h is greater than parent volume alignment 8h
parseCompressedSectionBody: decompression failed with error Customized decompression failed
parseFileHeader: file alignment 10h is greater than parent volume alignment 8h
parseFileHeader: invalid data checksum 83h, should be 82h
parseFileHeader: file alignment 1000h is greater than parent volume alignment 8h
parseFileHeader: invalid data checksum 5Ah, should be AAh
parseFileHeader: file alignment 1000h is greater than parent volume alignment 8h

Maybe uefiextract and Supermicro just don’t get along? Let’s check it against the BIOS in the fresh vendor file:

[jkominek@kyatt x9sae]$ uefiextract vendor/flashregion_1_bios.bin
parseFileHeader: file alignment 10h is greater than parent volume alignment 8h
parseFileHeader: file alignment 10h is greater than parent volume alignment 8h
parseFileHeader: file alignment 10h is greater than parent volume alignment 8h
parseFileHeader: file alignment 1000h is greater than parent volume alignment 8h
parseFileHeader: invalid data checksum 5Ah, should be AAh
parseFileHeader: file alignment 1000h is greater than parent volume alignment 8h

Not encouraging. But my file has at least two more checksum errors, and that decompression failure, over the vendor BIOS. So I figured the vendor version was better than what I had.

I pulled up UEFITool NE on the BIOS section and took a look around. At this point my enthusiasm for investigating was waning, as I had a solid plan for repair. But I found a FV_MAIN_NESTED (which contained a compression section) had a bad checksum. So that’s one checksum error and the decompression error. Next, CpuPei had a bad checksum. Finally, BiosAc also had a bad checksum, but that turned out to be bad in the vendor BIOS, too.

So I blamed everything on the CpuPei corruption and considered my investigation complete. Could it have been something else? Perhaps. My understanding of UEFI is minuscule, so there might have been some other ramification or result of the damaged compressed section.

Epilogue

I took the battery out and stuck the motherboard into an ESD bag and put it into storage.

Why didn’t I take the opportunity to install a socket for the BIOS? If it had been a DIP package soldered to the board, I would have. Those sockets are cheap, and I keep a stock of them. There are surface mount sockets for surface mount parts, and I think at the time I even found one for the 208mil 8-SOIC, but it was $5 or so, and I don’t anticipate having to remove the chip again.

The Last Line Effect and Extralinguistic Mechanisms

Since the release of the Racket Manifesto by Felleisen, et al. it has become a thesis of mine that normal programmers face, on a regular basis, real problems which could be alleviated by the sorts of macro mechanisms offered by Racket (and to a lesser extent, other Lisps). I’ve got in mind some in-depth investigations I’d like to perform to really back this argument up, but until I have time for that, a recent article will serve as a sort of teaser for what I’ve got in mind.

Andrey Karpov wrote in the “The Last Line Effect”:

When writing program code, programmers often have to write a series of similar constructs. Typing the same code several times is boring and inefficient. That’s why they use the Copy-Paste method: a code fragment is copied and pasted several times with further editing. Everyone knows what is bad about this method: you risk easily forgetting to change something in the pasted lines and thus giving birth to errors. Unfortunately, there is often no better alternative to be found.

Now let’s speak of the pattern I discovered. I figured out that mistakes are most often made in the last pasted block of code.

Andrey goes on to provide some evidence for his assertion. I think he’s on to something, and it makes sense. You do your pasting, then you set your hands in motion with some repeated delete delete delete type type type down arrow, repeat. By the time you’ve done that three or four times, your mind is numb, your eyes are glazed, and the muscles aren’t being supervised anymore.

I’d like to point out and discuss two of his examples:

Example, Chromium

if (access & FILE_WRITE_ATTRIBUTES)
  output.append(ASCIIToUTF16("\tFILE_WRITE_ATTRIBUTES\n"));
if (access & FILE_WRITE_DATA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_DATA\n"));
if (access & FILE_WRITE_EA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
if (access & FILE_WRITE_EA)
  output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n"));
break;

They’ve got some bitmasks that are referenced via variables (they’re probably #defines, but it doesn’t matter), and when the relevant bits are set, they’d like to write out the name of the bitmask that was set. Turns out this could actually be implemented as a function:

void check_and_append(access, mask, output, string)
{
  if (access & mask)
    output.append(ASCIIToUTF16(concat("\t", string, "\n")));
}
...
check_and_append(access, FILE_WRITE_ATTRIBUTES,
                 output, "FILE_WRITE_ATTRIBUTES");
...

Hmm. Not really an improvement. We’re still duplicating things. And the repetition of access and output is tedious. I’d expect most programmers to copy and paste the function call. Back where we started.

The C macro system happens to be powerful enough to address this case:

#define C_A(a,o,s) \
  if (a & s) \
      o.append(ASCIIToUTF16("\t" #s "\n"));

C_A(access, output, FILE_WRITE_ATTRIBUTES)

will expand to what we want.

This accomplishes our goal, because the macro is able to see the symbol FILE_WRITE_ATTRIBUTES, rather than just the value of 0x00100 or whatever it might happen to be.

We could shorten the uses of C_A by having the macro definition refer directly to access and output. But sooner or later, that will trip over standard macro hygiene problems.

Example, Multi Theft Auto

CWaterPoly* CWaterManagerSA::CreateQuad (....)
{
  ....
  pInterface->m_wVertexIDs [ 0 ] = pV1->GetID ();
  pInterface->m_wVertexIDs [ 1 ] = pV2->GetID ();
  pInterface->m_wVertexIDs [ 2 ] = pV3->GetID ();
  pInterface->m_wVertexIDs [ 3 ] = pV4->GetID ();
  ....
}

Ok, again, a C macro could help:

#define setter(i) \
  {pInterface->m_wVertexIDs [ (i-1) ] = (pV ## i)->GetID ();}
   ....
   setter(1);
   setter(2);
   setter(3);
   setter(4);
   ....

But, come on, this begs to be in a for loop:

#define setter(i) \
  {pInterface->m_wVertexIDs [ (i-1) ] = (pV ## i)->GetID ();}
   ....
   for(int i=1; i<=4; i++) {
     setter(i);
   }
   ....

And, unsurprisingly, that doesn’t work. Perhaps a more clever C preprocessor could notice that the for loop is static, and generate all of the appropriate code. Maybe there’s even some option I don’t remember floating around that would work with existing preprocessors. Frankly, I’m pleased I remember how to do the stringification and concatenation.

We could write a Racket macro that covers this:

(define-syntax (assign stx)
  (define (trailing-digits expr)
    (string->number
      (regexp-replace* #px"^.*?(\\d+)$"
                       (symbol->string (syntax->datum expr))
                       "\\1")))
  (syntax-case stx ()
    [(_ target source ...)
     (with-syntax ([(i ...)
                    (map trailing-digits
		         (syntax->list #'(source ...)))])
       #'(begin
           (vector-set! target (sub1 i) source)
           ...))]))

That’s not short. But if you’re going to be writing a lot of code where you want to pull values out of variables with numbers in their names, and stuff those values into corresponding positions in arrays, wouldn’t it be nice to have? How many times would you need to use it before it became worth the effort, in order to avoid the copying and pasting?

How much effort would it be worth to eliminate a class of errors?

Finally

Refer back to the bold portion of Andrey’s text: “there is often no better alternative to be found”. What? We’re programming! We’re providing instructions to a machine, which will be faithfully executed on our behalf, regardless of how tedious and repetitive they may be, and there’s no better alternative than copying and pasting?

When the Racket manifesto discusses Racket’s design principles, the authors write:

When programmers must resort to extra-linguistic mechanisms to solve a problem, the chosen language has failed them.

I assert that copying and pasting code is an extra-linguistic mechanism for expressing repetition the language is incapable of. When you are left with no choice but to copy and paste code, your language has failed you.

Musical Interlude 1

Sorry about all the fancy embedded things.

More SciCast-related CDF’s coming soon.

LMSR Made Interactive

The Logarithmic Market Scoring Rule (LMSR) is the rule which governs the price of trades on SciCast. SciCast’s predecessor, DAGGRE, produced a tutorial video on the LMSR.

I thought it might help if people could play with the LMSR directly, to get a feel for how it behaves. I looked around at my options for making interactive mathematical plots, and decided that the best option for the amount of time I was willing to put in was Mathematica CDF files. You’ll need the CDF player installed.

You should have two sliders, “start” and “tradeto”, as well as a graph with two curves on it, one red, the other green. If not, something has gone awry. Sorry.

The value of “start” represents where the trade is starting from; the current state of the market. “tradeto” is the value of the trade you’re considering making (this assumes power mode). “start” and “tradeto” are depicted visually on the graph by vertical lines labelled “S” and “T”. “start” controls a second line, labelled ▽.

Where T intersects with the Yes (green) and No (red) lines, there is a number which is the points you’ll gain (positive) or lose (negative), if the question resolves as Yes, or No, respectively. Between the S and ▽ lines, any trade you make has the potential to earn more than it risks. Everywhere between those lines has decimal odds of ≥2.0.

I have a number of other interactive graphs I’m planning to put up, but I’m still tweaking them. If you’ve got access to Mathematica, and you’d care to root around in my scicasting repo you can find my more-or-less in progress Mathematica notebook where I’m developing them.

On a programming note, these graphs are the most complicated things I’ve ever bothered to do in Mathematica. I’m pleased to discover that a lot of the horrifying looking Mathematica code I’ve seen on their blog, and elsewhere on the Internet over the years, was much worse than it had to be. Apparently nobody believes in adding whitespace to format things nicely. On the downside, it still hurts to read.

GitHub repository for my forecasting code

I’ve put up a GitHub repository of the code I’ve used to aid my forecasting.

Right now I’ve just got the previously discussed Bluefin-21 model, a model for the “Will NASA land a telepresence robot on Venus by 2020?” question, and some code that pulls down citation information from Google Scholar and crunches it for “Will there be at least one female winner of a Fields Medal in 2014?”.

venus launch windows

My Venus model presupposes there are three launch windows (2015, 2016 and 2018) for Venus. I also mention that in my comment on SciCast, though I don’t mention how I determined that.

I’m going to assume you have a reasonable mental model of planets, and their orbits around the Sun. Further, you believe that with current propulsion techniques, there are better and worse times to launch when you want to get to another planet.

Ok, first step, you hit up JPL’s HORIZONS web site. Set your Target Body to Venus (it’ll be 299; that’s the NAIF SPICE ID for Venus), the Time Span to run from 2014-7-1 to 2020-1-1 with a step size of 1 calendar month. Pull up the table settings and turn off everything except #20 Observer range & range-rate. Hit Generate Ephemeris. Bam, you’ve got a table of how far apart Venus and the Earth are, on a monthly basis, for the chunk of time under consideration in the question.

You could plot it, and you’d get something looking suspiciously like this:

Earth-Venus Distance 2014-06 to 2020-01

Hey look, three dips, almost in the three years I mention. Those correspond to the points when the planets are closest, and are roughly the best time to try and get from the Earth to Venus. Since Venus zips around the Sun quite a bit faster than the Earth, every close approach is Venus “catching up” with us.

The best plan for launching, then, is to kick it out of orbit into the direction of Venus, and let Venus catch up to it. The transit then just takes a few months. You can work out a good time to launch by shaving a few months off each of the minimums on the graph. That’s why the 2017 dip corresponds to a (late) 2016 launch window.