The device implemented only the SMC read command (0x10). The write
command (0x11) fell through to the default case and returned
kSMCBadCommand, so every SMC key write a guest performs failed.

A macOS guest writes keys through this command in normal operation: at
boot it writes "NTOK", "MSDW", "QENA" and "HE0N", and retrying those
failed writes delays boot; at shutdown it writes "OSWD". All of them
showed up as a stream of kSMCBadCommand errors.

Accept the write command, collect the key, the declared length and the
payload, and return success. The payload is not interpreted yet, so the
writes are accepted and discarded, which is enough to remove the boot
delay and the shutdown errors; the following patch gives the watchdog
keys meaning.

Signed-off-by: Daniel Golle <[email protected]>
---
 hw/misc/applesmc.c   | 39 +++++++++++++++++++++++++++++++++++++++
 hw/misc/trace-events |  3 +++
 2 files changed, 42 insertions(+)

diff --git a/hw/misc/applesmc.c b/hw/misc/applesmc.c
index 6f9d0a590d..bc44b0f1d8 100644
--- a/hw/misc/applesmc.c
+++ b/hw/misc/applesmc.c
@@ -128,6 +128,7 @@ static void applesmc_io_cmd_write(void *opaque, hwaddr 
addr, uint64_t val,
     smc_debug("CMD received: 0x%02x\n", (uint8_t)val);
     switch (val) {
     case APPLESMC_READ_CMD:
+    case APPLESMC_WRITE_CMD:
         /* did last command run through OK? */
         if (status == APPLESMC_ST_CMD_DONE || status == APPLESMC_ST_NEW_CMD) {
             s->cmd = val;
@@ -200,6 +201,44 @@ static void applesmc_io_data_write(void *opaque, hwaddr 
addr, uint64_t val,
         }
         s->read_pos++;
         break;
+    case APPLESMC_WRITE_CMD:
+        if (s->read_pos < 4) {
+            s->key[s->read_pos] = val;
+            s->status = APPLESMC_ST_ACK;
+            if (s->read_pos == 3) {
+                trace_applesmc_key_selected(s->key[0], s->key[1],
+                                            s->key[2], s->key[3]);
+            }
+            s->read_pos++;
+        } else if (s->read_pos == 4) {
+            s->data_len = val;
+            s->data_pos = 0;
+            s->read_pos++;
+            s->status = APPLESMC_ST_ACK;
+            trace_applesmc_write_len(s->key[0], s->key[1], s->key[2],
+                                     s->key[3], s->data_len);
+            if (s->data_len == 0) {
+                s->status = APPLESMC_ST_CMD_DONE;
+                s->status_1e = APPLESMC_ST_CMD_DONE;
+            }
+        } else {
+            if (s->data_pos < s->data_len) {
+                s->data[s->data_pos] = val;
+                trace_applesmc_write_data(s->key[0], s->key[1], s->key[2],
+                                          s->key[3], s->data_pos, 
(uint8_t)val);
+                s->data_pos++;
+            }
+            if (s->data_pos >= s->data_len) {
+                trace_applesmc_write_complete(s->key[0], s->key[1],
+                                              s->key[2], s->key[3],
+                                              s->data_len);
+                s->status = APPLESMC_ST_CMD_DONE;
+                s->status_1e = APPLESMC_ST_CMD_DONE;
+            } else {
+                s->status = APPLESMC_ST_ACK;
+            }
+        }
+        break;
     default:
         s->status = APPLESMC_ST_CMD_DONE;
         s->status_1e = APPLESMC_ST_1E_STILL_BAD_CMD;
diff --git a/hw/misc/trace-events b/hw/misc/trace-events
index b924b4398e..fc01cc9b7d 100644
--- a/hw/misc/trace-events
+++ b/hw/misc/trace-events
@@ -45,6 +45,9 @@ applesmc_data_write(uint8_t cmd, uint8_t pos, uint8_t val) 
"cmd 0x%02x pos %u da
 applesmc_key_selected(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "key 
%c%c%c%c"
 applesmc_key_not_found(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3) "key 
%c%c%c%c not found"
 applesmc_data_read(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t 
pos, uint8_t val) "read key %c%c%c%c [%u] 0x%02x"
+applesmc_write_len(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t 
len) "write key %c%c%c%c len %u"
+applesmc_write_data(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, uint8_t 
pos, uint8_t val) "write key %c%c%c%c [%u] 0x%02x"
+applesmc_write_complete(uint8_t c0, uint8_t c1, uint8_t c2, uint8_t c3, 
uint8_t len) "write key %c%c%c%c complete (%u bytes)"
 
 # avr_power.c
 avr_power_read(uint8_t value) "power_reduc read value:%u"
-- 
2.55.0

Reply via email to