/*
 * DF-4351 model proof - advansys.c (narrow AdvanSys CAM layer)
 * ------------------------------------------------------------
 * Two unconditional panic() sites are gated only by bytes the adapter
 * firmware wrote into LRAM (copied unmasked by adv_copy_lram_doneq,
 * advlib.c:802-844):
 *
 *  1) advansys.c:1092-1094
 *         if ((scsiq.q_status != QS_DONE)
 *          && (scsiq.q_status & QS_ABORTED) == 0)
 *                 panic("adv_qdone: completed scsiq with unknown status");
 *     QS_DISC1 (0x02) and QS_DISC2 (0x04) are legitimate queue-state
 *     bits defined by the driver's own ABI (advlib.h:562-569); a
 *     completion status byte of e.g. QS_DONE|QS_DISC1 (0x82) - or any
 *     corrupt LRAM value without the QS_ABORTED bit - is an instant,
 *     unauthenticated kernel panic.
 *
 *  2) advansys.c:1227-1237 (adv_done default arm)
 *         case QHSTA_D_LRAM_CMP_ERROR:
 *         case QHSTA_M_MICRO_CODE_ERROR_HALT:
 *         default:
 *                 panic("%s: Unhandled Host status error %x", ...);
 *     codes the driver ITSELF enumerates (0x81 LRAM compare error,
 *     0xA1 micro-code error halt) fall into the panic arm, plus every
 *     value outside the switch.
 *
 * Same defect family as DF-4042 (amd.c) and DF-4308 (adwcam.c).
 *
 * This model replays the two predicates over a table of device-supplied
 * bytes and classifies the kernel outcome.
 */
#include <stdio.h>

#define QS_FREE    0x00
#define QS_READY   0x01
#define QS_DISC1   0x02
#define QS_DISC2   0x04
#define QS_BUSY    0x08
#define QS_ABORTED 0x40
#define QS_DONE    0x80

/* host status codes from advlib.h:640-667 */
static const struct { int v; const char *n; } host_stats[] = {
    {0x00, "QHSTA_NO_ERROR"},
    {0x11, "QHSTA_M_SEL_TIMEOUT"},
    {0x12, "QHSTA_M_DATA_OVER_RUN"},
    {0x13, "QHSTA_M_UNEXPECTED_BUS_FREE"},
    {0x14, "QHSTA_M_BAD_BUS_PHASE_SEQ"},
    {0x21, "QHSTA_D_QDONE_SG_LIST_CORRUPTED"},   /* -> panic arm */
    {0x22, "QHSTA_D_ASC_DVC_ERROR_CODE_SET"},    /* -> panic arm */
    {0x23, "QHSTA_D_HOST_ABORT_FAILED"},         /* -> panic arm */
    {0x24, "QHSTA_D_EXE_SCSI_Q_FAILED"},         /* -> panic arm */
    {0x26, "QHSTA_D_ASPI_NO_BUF_POOL"},          /* -> panic arm */
    {0x41, "QHSTA_M_WTM_TIMEOUT"},
    {0x42, "QHSTA_M_BAD_CMPL_STATUS_IN"},
    {0x43, "QHSTA_M_NO_AUTO_REQ_SENSE"},
    {0x44, "QHSTA_M_AUTO_REQ_SENSE_FAIL"},
    {0x45, "QHSTA_M_TARGET_STATUS_BUSY"},
    {0x46, "QHSTA_M_BAD_TAG_CODE"},              /* -> panic arm */
    {0x47, "QHSTA_M_BAD_QUEUE_FULL_OR_BUSY"},
    {0x48, "QHSTA_M_HUNG_REQ_SCSI_BUS_RESET"},
    {0x81, "QHSTA_D_LRAM_CMP_ERROR"},            /* -> panic arm (enumerated!) */
    {0xA1, "QHSTA_M_MICRO_CODE_ERROR_HALT"},     /* -> panic arm (enumerated!) */
    {0xEE, "<undefined firmware code>"},         /* -> panic arm */
};

static const char *site1(uint8_t q_status)          /* :1092-1094 */
{
    if ((q_status != QS_DONE) && (q_status & QS_ABORTED) == 0)
        return "PANIC  (adv_qdone: completed scsiq with unknown status)";
    return "ok";
}

static const char *site2(int hs)                    /* :1227-1237 */
{
    switch (hs) {
    case 0x45: case 0x47: case 0x43: case 0x00:
    case 0x11: case 0x12: case 0x13: case 0x14:
    case 0x25:                                   /* QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT */
    case 0x41: case 0x42: case 0x44: case 0x48:
        return "ok (translated to CAM status)";
    default:                                     /* incl. 0x21,0x22,0x23,0x24,
                                                    0x26,0x46,0x81,0xA1 which the
                                                    switch itself routes here */
        return "PANIC  (Unhandled Host status error)";
    }
}

int main(void)
{
    int panics = 0, i;
    const uint8_t qv[] = {
        QS_DONE,                         /* clean */
        QS_DONE | QS_ABORTED,            /* aborted completion */
        QS_DONE | QS_DISC1,              /* legitimate reconnect state bit */
        QS_DONE | QS_DISC2,              /* legitimate reconnect state bit */
        QS_READY, QS_BUSY,               /* mid-flight states in a done slot */
        0x00, 0xFF, 0x55                 /* corrupt LRAM */
    };

    printf("DF-4351 model: device-fed status bytes -> panic() predicates\n");
    printf("site 1: advansys.c:1092-1094 (q_status from LRAM, unmasked)\n");
    printf("site 2: advansys.c:1235-1237 (host_stat from LRAM)\n\n");

    printf("q_status byte | outcome at site 1\n");
    printf("-------------+------------------------------------\n");
    for (i = 0; i < (int)(sizeof(qv)/sizeof(qv[0])); i++) {
        const char *r = site1(qv[i]);
        if (r[0] == 'P') panics++;
        printf("  0x%02X        | %s%s\n", qv[i], r,
               (qv[i] == (QS_DONE|QS_DISC1) ||
                qv[i] == (QS_DONE|QS_DISC2)) ?
               "   <-- legitimate ABI bits" : "");
    }
    printf("\nhost_stat    | outcome at site 2\n");
    printf("------------+------------------------------------\n");
    for (i = 0; i < (int)(sizeof(host_stats)/sizeof(host_stats[0])); i++) {
        const char *r = site2(host_stats[i].v);
        if (r[0] == 'P') panics++;
        printf("  0x%02X %-38s | %s\n", host_stats[i].v, host_stats[i].n, r);
    }
    printf("\n%d/%zu tabulated device-fed values lead to kernel panic\n",
           panics, sizeof(qv)/sizeof(qv[0]) +
                   sizeof(host_stats)/sizeof(host_stats[0]));
    printf("PANIC-ON-DEVICE-STATUS CONFIRMED (model; predicates are "
           "verbatim advansys.c:1092-1094 / :1235-1237)\n");
    return 0;
}
