Unlock EFM32 debug access with OpenOCD :20160923

Most microcontrollers allow you to lock them so people can't read out your precious machine code, as though anyone would even bother trying to get their hands on your nasty hacked-together firmware. On Silicon Labs EFM32 "[a-zA-Z]+ Gecko" devices[1], this is done by disabling most of the SWD interface so that you can't even connect to the core, much less access memory. You trigger a lock by writing to registers on the memory-mapped Memory System Controller peripheral. You trigger an unlock by… uh…

OK, so according to Application Note 0062 "Programming Internal Flash Over the Serial Wire Debug Interface", you must write to registers on the SW-DP and the AHB-AP, which are some kind of esoteric ARM thing that I don't care about. Cool. How?

Simplicity Studio, the mediocre and often buggy Eclipse-based[2] IDE that Silicon Labs provides for their EFM32 stuff, has lock/unlock functions via a built-in flash programming tool. They also used to provide a standalone app called eACommander that could do it, but axed it in the update from Simplicity Studio v2 to the even buggier v3. Both of these methods require using an EFM32 development kit in "Out" mode, which debugs whatever's attached to the JTAG/SWD header instead of the onboard MCU.

The devkits have an onboard J-Link debugger IC, so you can also use the J-Link Commander utility. It's total garbage, but at least it's command-line, so you can script it. In theory, anyway. In practice you're going to have to get your hands dirty with expect or throw in a lot of sleep 1. Not really worth the effort.

Well, screw all that. Especially screw J-Link Commander. I use OpenOCD. It works great, once you figure out the right incantations:

# Lock
reset init
mww 0x400c0008 1
mww 0x400c0010 0x0fe041fc
mww 0x400c000c 1
mww 0x400c0018 0
mww 0x400c000c 8
reset_config srst_only
reset run
shutdown

# Unlock
dap apreg 0 0x4 0xcfacc118
dap apreg 0 0x0 1
dap apreg 0 0x8
sleep 1000
dap apreg 0 0x0 2
reset_config none
reset init
shutdown

OpenOCD had no dap apreg command when I originally tried to do this, so I had to write my own:

diff --git a/src/target/arm_adi_v5.c b/src/target/arm_adi_v5.c
index eb3392b..9d4c2bb 100644
--- a/src/target/arm_adi_v5.c
+++ b/src/target/arm_adi_v5.c
@@ -1582,6 +1582,124 @@ COMMAND_HANDLER(dap_ti_be_32_quirks_command)
    return 0;
 }

+COMMAND_HANDLER(dap_dpread_command)
+{
+	struct target *target = get_current_target(CMD_CTX);
+	struct arm *arm = target_to_arm(target);
+	struct adiv5_dap *dap = arm->dap;
+
+	int retval;
+	uint8_t regnum;
+	uint32_t regval;
+
+	switch (CMD_ARGC) {
+	case 1:
+		COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], regnum);
+		break;
+	default:
+		return ERROR_COMMAND_SYNTAX_ERROR;
+	}
+
+	retval = dap_queue_dp_read(dap, regnum, &regval);
+	if (retval != ERROR_OK)
+		return retval;
+	retval = dap_run(dap);
+	if (retval != ERROR_OK)
+		return retval;
+	command_print(CMD_CTX, "R DP reg 0x%02" PRIx8 " value 0x%08" PRIx32, regnum, regval);
+
+	return retval;
+}
+
+COMMAND_HANDLER(dap_dpwrite_command)
+{
+	struct target *target = get_current_target(CMD_CTX);
+	struct arm *arm = target_to_arm(target);
+	struct adiv5_dap *dap = arm->dap;
+
+	int retval;
+	uint8_t regnum;
+	uint32_t regval;
+
+	switch (CMD_ARGC) {
+	case 2:
+		COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], regnum);
+		COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], regval);
+		break;
+	default:
+		return ERROR_COMMAND_SYNTAX_ERROR;
+	}
+
+	retval = dap_queue_dp_write(dap, regnum, regval);
+	if (retval != ERROR_OK)
+		return retval;
+	retval = dap_run(dap);
+	if (retval != ERROR_OK)
+		return retval;
+	command_print(CMD_CTX, "W DP reg 0x%02" PRIx8 " value 0x%08" PRIx32, regnum, regval);
+
+	return retval;
+}
+
+COMMAND_HANDLER(dap_apread_command)
+{
+	struct target *target = get_current_target(CMD_CTX);
+	struct arm *arm = target_to_arm(target);
+	struct adiv5_dap *dap = arm->dap;
+
+	int retval;
+	uint8_t regnum;
+	uint32_t regval;
+
+	switch (CMD_ARGC) {
+	case 1:
+		COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], regnum);
+		break;
+	default:
+		return ERROR_COMMAND_SYNTAX_ERROR;
+	}
+
+	retval = dap_queue_ap_read(dap_ap(dap, dap->apsel), regnum, &regval);
+	if (retval != ERROR_OK)
+		return retval;
+	retval = dap_run(dap);
+	if (retval != ERROR_OK)
+		return retval;
+	command_print(CMD_CTX, "R AP reg 0x%02" PRIx8 " value 0x%08" PRIx32, regnum, regval);
+
+	return retval;
+}
+
+COMMAND_HANDLER(dap_apwrite_command)
+{
+	struct target *target = get_current_target(CMD_CTX);
+	struct arm *arm = target_to_arm(target);
+	struct adiv5_dap *dap = arm->dap;
+
+	int retval;
+	uint8_t regnum;
+	uint32_t regval;
+
+	switch (CMD_ARGC) {
+	case 2:
+		COMMAND_PARSE_NUMBER(u8, CMD_ARGV[0], regnum);
+		COMMAND_PARSE_NUMBER(u32, CMD_ARGV[1], regval);
+		break;
+	default:
+		return ERROR_COMMAND_SYNTAX_ERROR;
+	}
+
+	retval = dap_queue_ap_write(dap_ap(dap, dap->apsel), regnum, regval);
+	if (retval != ERROR_OK)
+		return retval;
+	retval = dap_run(dap);
+	if (retval != ERROR_OK)
+		return retval;
+	command_print(CMD_CTX, "W AP reg 0x%02" PRIx8 " value 0x%08" PRIx32, regnum, regval);
+
+	return retval;
+}
+
 static const struct command_registration dap_commands[] = {
    {
        .name = "info",
@@ -1638,6 +1756,34 @@ static const struct command_registration dap_commands[] = {
        .help = "set/get quirks mode for TI TMS450/TMS570 processors",
        .usage = "[enable]",
    },
+	{
+		.name = "dpread",
+		.handler = dap_dpread_command,
+		.mode = COMMAND_EXEC,
+		.help = "read DP register",
+		.usage = "[reg]",
+	},
+	{
+		.name = "dpwrite",
+		.handler = dap_dpwrite_command,
+		.mode = COMMAND_EXEC,
+		.help = "write DP register",
+		.usage = "[reg] [value]",
+	},
+	{
+		.name = "apread",
+		.handler = dap_apread_command,
+		.mode = COMMAND_EXEC,
+		.help = "read AP register",
+		.usage = "[reg]",
+	},
+	{
+		.name = "apwrite",
+		.handler = dap_apwrite_command,
+		.mode = COMMAND_EXEC,
+		.help = "write AP register",
+		.usage = "[reg] [value]",
+	},
    COMMAND_REGISTRATION_DONE
 };

Which you would use like so:

dap dpwrite 0x8 0 # Probably equivalent to dap apsel 0?
dap apwrite 0x4 0xcfacc118
dap apwrite 0x0 1
dap apread 0x8
sleep 1000
dap apwrite 0x0 2
reset_config none
reset init
shutdown

In retrospect, I didn't really need dpread and dpwrite. It doesn't matter now because someone went ahead and wrote the code for me. That almost never happens.

[1] Choose one of: Plain Gecko, Giant Gecko, Tiny Gecko, Wonder Gecko, Zero Gecko, Magic Gecko, Wang Chung Gecko, Gordon Gecko.

[2] As though these are not redundant. Oh, ha ha, it is to laugh.

Feed thyself :20151230

Despite what the average 20-something of my disposition would tell you, instant ramen for breakfast, Chinese takeout for lunch, and frozen pizza for dinner do not a good diet make. It's because the pizza is too hard, you see. Why did you think that would be a good idea? Pick up your teeth and see me after class.

Also, if you're going to eat instant ramen — at least use water. Dry instant ramen is not the same thing as bread. Trust me on this. For one thing, you can't spread butter on it for crap, and it doesn't taste like anything without the salt packet. (With the salt packet, it tastes like salt. Obviously.) Eat a salad.

But really, what you should be doing is cooking. You can look at recipes, but don't take them too seriously or you'll end up kneeling in front of the local store's spice rack, pawing through tiny glass bottles of weeds in search of something called "tarragon". What the hell is that? It sounds like a medieval weapon. You know, like those ten-foot poles with the spikes on the end. "Forsooth, I took up the tarragon and staved the foe's head in." Why not?

Some things you just don't need to bother with. Five pounds of peeled potatoes? Probably important. An entire side of beef? Sure. A sprig of cilantro? Eh, maybe leave that one out. A bay leaf? Who do you think you are? Put that away. Drop one in your worst enemy's soup so he chokes to death. Don't eat it.

You can feed yourself for two weeks with only a few hours' worth of effort. You need the following:

Prepare the ingredients:

  1. Chop the carrots into cross-sections. Don't peel them. Why would you peel them?
  2. Chop the sweet potatoes into roughly cubic pieces. Don't peel these either. Waste of time.
  3. Chop the onion. Experience pain. Curse your ancestors.
  4. Did you get another onion? If so, repeat.
  5. You should have about two gallons of vegetables. Put them in the first two containers.
  6. Pour all the dried stuff into the third container.
  7. If anything overflowed onto the counter, you have too much stuff.

Cook everything:

  1. Put the broth in the pot and heat it to a simmer. That means small bubbles and steam, but not really boiling. If you boil it, it will taste bad.
  2. Vigorously pour all the ingredients into the pot. Shield yourself so you don't get hot broth in your face. Stir.
  3. Go do something else for a while. Come back and stir the pot periodically.
  4. Stop cooking when none of the stuff is hard anymore, or the stew gets thick, whichever comes first.

If you did this right, the stew will last you a long time. You can freeze it, and when you thaw it the texture will totally suck. Who cares? It's stew. Have a big heaping bowl, every day, until you can't stand it anymore.

Then make some bread! Here's what you need:

And here's what you do with it:

  1. Pour some flour into a large bowl. Not a soup bowl, that's too small. Bigger.
  2. Pour water in.
  3. Mix it. You might as well use your bare hands. This will take a while.
  4. The dough is ready when it's dry enough to hold its shape, but not so dry that it has dry flakes in there. Too wet? More flour. Too dry? More water.
  5. Spread some cornmeal in a baking tray. This is to keep the bread from welding to the tray.
  6. Put the dough in the tray and form it into a roughly dome-like shape. It should be about two inches high; much higher and the outside will burn while the inside stays soggy.
  7. Stick it in the oven and bake for an hour at 350°F.

Makes a good chewy loaf. One slice of this is worth three of any other bread. Yeast? What's that?