aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Cave-Ayland <mark.cave-ayland@ilande.co.uk>2025-07-11 21:46:36 +0100
committerPhilippe Mathieu-Daudé <philmd@linaro.org>2025-07-15 00:25:21 +0200
commit6f8ce26bb00db66e6cec632f16df3cd13e4df934 (patch)
tree7e91c12e348987038849220c7bf90a8d14dc974e
parentab1207401edc19d17fad6cb473cd6beae31b1dd1 (diff)
downloadqemu-6f8ce26bb00db66e6cec632f16df3cd13e4df934.zip
qemu-6f8ce26bb00db66e6cec632f16df3cd13e4df934.tar.gz
qemu-6f8ce26bb00db66e6cec632f16df3cd13e4df934.tar.bz2
esp.c: only allow ESP commands permitted in the current asc_mode
If an ESP command is issued in an incorrect mode then an illegal command interrupt should be generated. Add a new esp_cmd_is_valid() function to indicate whether the ESP command is valid for the current mode, and if not then raise the illegal command interrupt. This fixes WinNT MIPS which issues ICCS after a Chip Reset which is not permitted, but will fail with an INACCESSIBLE_BOOT_DEVICE error unless an interrupt is generated. Signed-off-by: Mark Cave-Ayland <mark.cave-ayland@ilande.co.uk> Fixes: 83428f7a97 ("esp.c: move write_response() non-DMA logic to esp_do_nodma()") Resolves: https://gitlab.com/qemu-project/qemu/-/issues/2464 Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <20250711204636.542964-8-mark.cave-ayland@ilande.co.uk> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>
-rw-r--r--hw/scsi/esp.c37
-rw-r--r--hw/scsi/trace-events1
-rw-r--r--include/hw/scsi/esp.h8
3 files changed, 46 insertions, 0 deletions
diff --git a/hw/scsi/esp.c b/hw/scsi/esp.c
index 4aa58f9..1d264c4 100644
--- a/hw/scsi/esp.c
+++ b/hw/scsi/esp.c
@@ -1129,6 +1129,38 @@ static void parent_esp_reset(ESPState *s, int irq, int level)
}
}
+static bool esp_cmd_is_valid(ESPState *s, uint8_t cmd)
+{
+ uint8_t cmd_group = (cmd & CMD_GRP_MASK) >> 4;
+
+ /* Always allow misc commands */
+ if (cmd_group == CMD_GRP_MISC) {
+ return true;
+ }
+
+ switch (s->asc_mode) {
+ case ESP_ASC_MODE_DIS:
+ /* Disconnected mode: only allow disconnected commands */
+ if (cmd_group == CMD_GRP_DISC) {
+ return true;
+ }
+ break;
+
+ case ESP_ASC_MODE_INI:
+ /* Initiator mode: allow initiator commands */
+ if (cmd_group == CMD_GRP_INIT) {
+ return true;
+ }
+ break;
+
+ default:
+ g_assert_not_reached();
+ }
+
+ trace_esp_invalid_cmd(cmd, s->asc_mode);
+ return false;
+}
+
static void esp_run_cmd(ESPState *s)
{
uint8_t cmd = s->rregs[ESP_CMD];
@@ -1285,6 +1317,11 @@ void esp_reg_write(ESPState *s, uint32_t saddr, uint64_t val)
break;
case ESP_CMD:
s->rregs[saddr] = val;
+ if (!esp_cmd_is_valid(s, s->rregs[saddr])) {
+ s->rregs[ESP_RSTAT] |= INTR_IL;
+ esp_raise_irq(s);
+ break;
+ }
esp_run_cmd(s);
break;
case ESP_WBUSID ... ESP_WSYNO:
diff --git a/hw/scsi/trace-events b/hw/scsi/trace-events
index f0f2a98..6c2788e 100644
--- a/hw/scsi/trace-events
+++ b/hw/scsi/trace-events
@@ -198,6 +198,7 @@ esp_mem_writeb_cmd_ensel(uint32_t val) "Enable selection (0x%2.2x)"
esp_mem_writeb_cmd_dissel(uint32_t val) "Disable selection (0x%2.2x)"
esp_mem_writeb_cmd_ti(uint32_t val) "Transfer Information (0x%2.2x)"
esp_set_phase(const char *phase) "setting bus phase to %s"
+esp_invalid_cmd(uint8_t cmd, uint8_t asc_mode) "command 0x%x asc_mode 0x%x"
# esp-pci.c
esp_pci_error_invalid_dma_direction(void) "invalid DMA transfer direction"
diff --git a/include/hw/scsi/esp.h b/include/hw/scsi/esp.h
index 6327060..3526bad 100644
--- a/include/hw/scsi/esp.h
+++ b/include/hw/scsi/esp.h
@@ -111,6 +111,13 @@ struct SysBusESPState {
#define CMD_DMA 0x80
#define CMD_CMD 0x7f
+#define CMD_GRP_MASK 0x70
+
+#define CMD_GRP_MISC 0x00
+#define CMD_GRP_INIT 0x01
+#define CMD_GRP_TRGT 0x02
+#define CMD_GRP_DISC 0x04
+
#define CMD_NOP 0x00
#define CMD_FLUSH 0x01
#define CMD_RESET 0x02
@@ -145,6 +152,7 @@ struct SysBusESPState {
#define INTR_FC 0x08
#define INTR_BS 0x10
#define INTR_DC 0x20
+#define INTR_IL 0x40
#define INTR_RST 0x80
#define SEQ_0 0x0