Hacking Hacking the WiiU gamepad

  • Thread starter Thread starter Arisotura
  • Start date Start date
  • Views Views 101,210
  • Replies Replies 161
  • Likes Likes 46
Well, some better news.

I managed to get the gamepad to connect to regular 5GHz networks. It also seems to support 802.11ac fine, unlike what I had observed.

I patched Nintendo's wifi firmware to get there. Most notably:

* The function at 0x24B78 is responsible for pairwise key expansion (basically, calculating the PTK from the PMK). The end of this function does the funny 3-byte rotate that makes it incompatible with regular WPA, so I patched that out.

* At 0x297F2 you find the bytes A4:C0:E1 (Nintendo OUI). When connecting to a WPA2 access point, the RSN information element's data is checked for this value, which means it will refuse to connect to a regular WPA2 access point. Changing it to the standard 00:0F:AC allows the gamepad to atleast attempt to connect.

* At 0x2AFEB you find a pattern for the RSN information element the gamepad sends to the access point. Same issue as above: all 3 instances of A4:C0:E1 need to be changed to 00:0F:AC. Then, and only then, will the gamepad connect to a WPA2 access point.

* The firmware has a CRC32 at 0x3D300. The wifi card will refuse to start if that CRC32 doesn't match the actual firmware data (from beginning up to 0x3D300).

With this in mind I guess I can implement some networking features.
Seriously amazing work! Keep it up and take breaks of course!
 
Not been working on this a whole lot lately... bad mental health.

Picked it up lately though, felt like trying to figure out some of the registers. Ended up figuring out some video registers and a whole bunch of the clock system, so that's not too shabby.

As far as actual reverse-engineering is concerned, what is left to do is:

* finishing work on the audio output system
* audio input (microphone)
* video input (camera)
* h264 codec
* UART
* NFC
* the fun input devices (accel/gyro/compass), basically just figuring out how the readings work

I also worked on my soft reset code. I had the idea I could use the soft reset feature to load a different program without messing with UIC states at all.

I had made an attempt at soft reset, just using it to reset my test program. Except it didn't work right, hanging and sometimes spewing garbage through the FPGA debug output. I thought the reset was causing the SPI hardware to go haywire for some reason...

I had the idea to test it in my experimental gamepad emulator, and it ran into the same issue. I traced it back and figured it out: since I wasn't reloading my program and only resetting it, the data section wasn't getting reset. Anything in BSS would be zeroed out on init, but the data section was uninitialized, which didn't bode well.

So I put together a simple loader: you load it in RAM at 0x3F0000, then invoke it with a Flash address, and it will load a binary from that address. I tested it by reloading my program and loading a different test program, and it worked fine in both cases.

However I realized something when I tested it with a stock firmware. The stock firmware relies on the byte at 0xF000 to figure out where it is in Flash, and where to load its assets from. If that byte points to something else, it breaks.

This means that we'll need to install a custom bootloader. I'd rather have avoided that, but, oh well.

I will also need to iron out some of the kinks in this whole process. I still need to figure out how to properly shut down the wifi card before a reset/shutdown, for example.

I also figured that the loader thing could support other ways of loading code to the gamepad. IR would be a possibility. Wifi too, if I can fit a basic wifi driver and TCP/IP stack into 64K of memory.
 
I'm trying, but my brain pretty much decides what to focus on :P

Lately I've been working on the FPGA itself - the SPI FLASH emulator. Something I noticed was that when I tried to run a stock firmware off it, I would get random graphical corruption. Interestingly, it either happened or didn't happen, but when it did, it was always the same pattern. I tracked down the issue and found that it tended to happen when a SPI read started on the last two bytes of a 8-byte block.

(I think it was the same bug that killed one of my motherboards by uploading corrupted data to the UIC)

Reason for that is that my FLASH emulator uses burst mode to access SDRAM, reading 8 bytes of data at once. I did it this way because, when receiving a SPI read command, it allowed me to initiate a SDRAM read earlier, and according to my theory, I would have just enough time to receive the entire 8-byte burst of data before having to answer the read command.

(if you know how SPI FLASH works, you know that the moment you receive the last address bit, you have to be able to send out data on the next clock cycle, which is why it's tricky to emulate)

In practice, it seemed that there might not be quite enough time in the worst case, that is, when the first byte to be sent out is at the end of the 8-byte burst, which could cause bit 7 of that first byte to get corrupted.

I thought about it and realized - when we receive a SPI read command, the requested address may be anywhere within a given 8-byte block, but the first bit we need to send out is always bit 7 of a given byte.

So I changed the way I store data in SDRAM to take advantage of that fact, by interleaving the data in such a way that the bit 7's of all bytes come first, followed by all the bit 6's, all the bit 5's, and so on. This way, no matter which address is requested, we are guaranteed to receive the data on time.

This funny little interleaving did fix the problem I was having, so that's great.

-

I'm now trying to implement erase and page program commands, which I will need for the next steps of my little plan. In fact, I already had made an attempt at implementing them, but they were unreliable -- randomly altering the wrong area of memory or not working at all.

My brain couldn't let go until I had it figured out, but I eventually did. It turned out to be because I wasn't buffering the SPI chipselect line. Meaning that it could be sampled at a level between 0 and 1, which would propagate through the logic and fuck things up. What joy.

I guess now I can actually attempt to implement them and have them work reliably. I already have most of the logic down, but I'll probably have to do testing, bugfixing, the usual shit. And figuring out ways to simplify my FPGA logic so it fits within the timing constraints... Joy. I have developed a love-hate relationship to FPGA stuff.
Post automatically merged:

Erase and page program commands are in. Tested, they work absolutely fine. I did also simplify the logic so it works better.

Page program works in a kinda stupid way, as in it does read-modify-write over an entire 256-byte page even if only one byte of data was sent. No big deal though, erase and page program commands aren't timing-critical. The way they work is that you issue the command and then keep checking the status register to see if it's done. So it might as well take 3 months to complete, it would suck massively but it wouldn't break anything.

There was also an issue with it that I realized after implementing the funny interleaving trick. The SDRAM chip has masking inputs that you can use to restrict reads and writes to specific byte portions. I had simply used these to implement page program, so that any part of a page that didn't receive data would be left unchanged. But that only worked as far as I was storing data linearly. Interleaving made it impossible to rely on this. So I had to go for the less elegant read-modify-write approach.

FPGA codebase is here btw: https://github.com/Arisotura/spi_flash -- if that's of any interest.

With the latest fixes, it could probably support higher than 48MHz SPI clock with some revisions to spi_trx, but for now it will do the job.

Diamond does report some timing errors, but from what I've seen they don't matter given the way this is designed...

Well, I think that's it for the FPGA programming.
 
Last edited by Arisotura,
I'm trying, but my brain pretty much decides what to focus on :P

Lately I've been working on the FPGA itself - the SPI FLASH emulator. Something I noticed was that when I tried to run a stock firmware off it, I would get random graphical corruption. Interestingly, it either happened or didn't happen, but when it did, it was always the same pattern. I tracked down the issue and found that it tended to happen when a SPI read started on the last two bytes of a 8-byte block.

(I think it was the same bug that killed one of my motherboards by uploading corrupted data to the UIC)

Reason for that is that my FLASH emulator uses burst mode to access SDRAM, reading 8 bytes of data at once. I did it this way because, when receiving a SPI read command, it allowed me to initiate a SDRAM read earlier, and according to my theory, I would have just enough time to receive the entire 8-byte burst of data before having to answer the read command.

(if you know how SPI FLASH works, you know that the moment you receive the last address bit, you have to be able to send out data on the next clock cycle, which is why it's tricky to emulate)

In practice, it seemed that there might not be quite enough time in the worst case, that is, when the first byte to be sent out is at the end of the 8-byte burst, which could cause bit 7 of that first byte to get corrupted.

I thought about it and realized - when we receive a SPI read command, the requested address may be anywhere within a given 8-byte block, but the first bit we need to send out is always bit 7 of a given byte.

So I changed the way I store data in SDRAM to take advantage of that fact, by interleaving the data in such a way that the bit 7's of all bytes come first, followed by all the bit 6's, all the bit 5's, and so on. This way, no matter which address is requested, we are guaranteed to receive the data on time.

This funny little interleaving did fix the problem I was having, so that's great.

-

I'm now trying to implement erase and page program commands, which I will need for the next steps of my little plan. In fact, I already had made an attempt at implementing them, but they were unreliable -- randomly altering the wrong area of memory or not working at all.

My brain couldn't let go until I had it figured out, but I eventually did. It turned out to be because I wasn't buffering the SPI chipselect line. Meaning that it could be sampled at a level between 0 and 1, which would propagate through the logic and fuck things up. What joy.

I guess now I can actually attempt to implement them and have them work reliably. I already have most of the logic down, but I'll probably have to do testing, bugfixing, the usual shit. And figuring out ways to simplify my FPGA logic so it fits within the timing constraints... Joy. I have developed a love-hate relationship to FPGA stuff.
Post automatically merged:

Erase and page program commands are in. Tested, they work absolutely fine. I did also simplify the logic so it works better.

Page program works in a kinda stupid way, as in it does read-modify-write over an entire 256-byte page even if only one byte of data was sent. No big deal though, erase and page program commands aren't timing-critical. The way they work is that you issue the command and then keep checking the status register to see if it's done. So it might as well take 3 months to complete, it would suck massively but it wouldn't break anything.

There was also an issue with it that I realized after implementing the funny interleaving trick. The SDRAM chip has masking inputs that you can use to restrict reads and writes to specific byte portions. I had simply used these to implement page program, so that any part of a page that didn't receive data would be left unchanged. But that only worked as far as I was storing data linearly. Interleaving made it impossible to rely on this. So I had to go for the less elegant read-modify-write approach.

FPGA codebase is here btw: https://github.com/Arisotura/spi_flash -- if that's of any interest.

With the latest fixes, it could probably support higher than 48MHz SPI clock with some revisions to spi_trx, but for now it will do the job.

Diamond does report some timing errors, but from what I've seen they don't matter given the way this is designed...

Well, I think that's it for the FPGA programming.
Impressive. You're really into it. Keep going!
 
No reason it couldn't do the colors. It can do 256 colors (with 8-bit palette) or 16-bit color, so there's plenty.

Speaking of chiptune...




Just a silly audio streaming test, because I was bored. I don't yet know how to 'properly' repeat a sound buffer, but it seems there's no real proper way -- stock firmware seems to just calculate how long to wait before queuing the buffer again. Not very elegant, but the sound hardware is kinda dumb, so, uh.

Aside from this distraction, I'm in the process of cleaning up my codebase and making it into an actual boot menu thing. I ported lwIP and got some networking going -- gamepad is able to connect to the network, obtain an IP address, etc.

My idea for uploading binaries to it was to have it run a small TFTP server. This way it wouldn't need a custom app. You'd just use a TFTP client, connect it to the gamepad and send your file over to it. I'm trying to think of how to support wifi boot...

Maybe the TFTP stuff could also work the other way around, ie. provide a handy way to dump the entire FLASH or whatever.

Would be interesting to see other people take some interest in reverse-engineering the rest of the hardware. Notably:

* the h265 video decoder (might be useful for video demos? :P )
* the camera and associated hardware
* microphone input
* UART (might be handy as a debug output)
* NFC
 
No reason it couldn't do the colors. It can do 256 colors (with 8-bit palette) or 16-bit color, so there's plenty.

Speaking of chiptune...


View attachment 491880

Just a silly audio streaming test, because I was bored. I don't yet know how to 'properly' repeat a sound buffer, but it seems there's no real proper way -- stock firmware seems to just calculate how long to wait before queuing the buffer again. Not very elegant, but the sound hardware is kinda dumb, so, uh.

Aside from this distraction, I'm in the process of cleaning up my codebase and making it into an actual boot menu thing. I ported lwIP and got some networking going -- gamepad is able to connect to the network, obtain an IP address, etc.

My idea for uploading binaries to it was to have it run a small TFTP server. This way it wouldn't need a custom app. You'd just use a TFTP client, connect it to the gamepad and send your file over to it. I'm trying to think of how to support wifi boot...

Maybe the TFTP stuff could also work the other way around, ie. provide a handy way to dump the entire FLASH or whatever.

Would be interesting to see other people take some interest in reverse-engineering the rest of the hardware. Notably:

* the h265 video decoder (might be useful for video demos? :P )
* the camera and associated hardware
* microphone input
* UART (might be handy as a debug output)
* NFC
Excellent work! The music sounds pretty cool.
̶G̶a̶m̶e̶P̶a̶d̶ ̶M̶P̶3̶ ̶P̶l̶a̶y̶e̶r̶ ̶W̶h̶e̶n̶
 
  • Like
Reactions: lordelan
The full song is here:



I love it too! I find it has quite a magical vibe.

Still playing around with the audio hardware. It's weird... one of the control bits (bit 19 in 0xF0005400) mutes the audio output but only if it's set at a certain point, and if it's set later it doesn't matter at all.

I did figure out a way to play a buffer on repeat, so there's that. The downside is that I don't know how to gracefully stop it. I know of a way to stop playback, but then I can no longer get any audio output again. So there's definitely something odd with the audio output part.

Both situations also seem to only affect the audio output itself, while the decoding/playback logic is still operational, so that's weird.
 
  • Like
Reactions: awesomeee
Well, I just finished reverse-engineering most of the audio hardware (including the mic input part, couldn't resist) and putting together a basic audio API.

It's still pretty crude (for example the callbacks are called straight from IRQ handlers, which limits what you can do), but it allows for spotless audio streaming, so that's nice.

Fun side note: the hardware supports A-law and µ-law PCM8 encodings. If I convert Tsukema Tsukeru to one of these formats, it does fit in the FLASH memory :P

I guess now that I'm done with this little distraction, I can finish putting together my boot menu :P
 
Tsukema Tsukeru in A-law encoding is still 24MB, for 4:20 worth of music. The FLASH memory is 32MB, so you guess where this is going -- the gamepad isn't going to be very useful as a music player.

Unless a storage expansion for it is made, using the expansion connector or whatever.
 
  • Like
Reactions: I pwned U!
I figured :P

I'm still curious as to what people will do with the gamepad. I had some ideas for a storage expansion add-on, but it would take some work to actually make it.
Could the expansion port at the bottom be used? It might have simple data transferring possibility, not sure though

My old gamepad has a chip stuck in the port LMAO
 
  • Like
Reactions: Arisotura
It's an I2C interface. From what I read, it runs at 1MHz, so it wouldn't be super fast. Maybe it can be made to run faster.

The challenge would be designing something that can plug into that connector, because unlike USB, the socket has no springs -- they're supposed to be inside the plug, which complicates things a bit. They say that something like the weird 5-pin USB connector used for the wifi board in some Xbox models works, but it'd be nice to have a source for these without needing to sacrifice Xbox wifi cards.

Another challenge is that it would require code to support such a device on the UIC, since the expansion connector is connected to the UIC. The stock UIC firmware has no support for expansion devices beyond merely checking if something is connected.
 
I hope this could be used to add more features to the TV remote. It is missing several useful features that my physical remotes have:
  • a mute button
  • a button that takes you back to the channel you just switched from
  • a menu button
  • pause, play, fast forward, and rewind buttons
  • a button for toggling subtitles on and off
 

Site & Scene News

Popular threads in this forum