DF-3608 / ncr_dma32.c
/* DF-3608 trigger — ncr(4) programs 32-bit-only DMA descriptors from * unrestricted 64-bit vtophys() results. * * Target: sys/dev/disk/ncr/ncr.c. Every address the driver hands the * 53C8xx script processor lives in a 32-bit field: * - struct link { ncrcmd l_cmd, l_paddr; } (ncrreg.h, ncrcmd=u32) * - struct scr_tblmove { u_int32_t size, addr; } (ncrreg.h:306) * - np->squeue[MAX_START] is u_int32_t (ncr.c:1081) * but the sources are full-width, unconstrained vtophys() results: * - ncr.c:6577 cp->p_nccb = vtophys(cp) (kmalloc'd nccb) * - ncr.c:4149 np->squeue[..] = CCB_PHYS(cp, phys) -> u32 store * - ncr.c:4094 cp->phys.cmd.addr = vtophys(cdb_bytes) * - ncr.c:4110 cp->phys.sense.addr = vtophys(&csio->sense_data) * - ncr.c:6701 phys->data[i].addr = segaddr (vtophys of buffer) * Nothing below 4 GB is guaranteed except the script images * (kmem_alloc_contig(.., 0, 0xffffffff), ncr.c:3588-3605). On amd64 * with RAM above 4 GB the low 32 bits are programmed silently and the * chip DMAs to the WRONG physical address. * * Test: raw write+read+FNV-1a compare over an ncr-attached da(4) disk. * A: guest RAM <= 4GB -> checksums match (baseline) * B: guest RAM > 4GB -> mismatch / wedge / panic * * Build (root, in guest): cc -O2 -o ncr_dma32 ncr_dma32.c * Run (root, in guest): ./ncr_dma32 /dev/daN 8 */ #include <sys/types.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #define CHUNK (64 * 1024) static u_int64_t fnv; static void fnv_init(void) { fnv = 0xcbf29ce484222325ull; } static void fnv_update(const unsigned char *d, size_t n) { for (size_t i = 0; i < n; i++) { fnv ^= d[i]; fnv *= 0x100000001b3ull; } } int main(int argc, char **argv) { unsigned char *buf, *rb; u_int64_t w_sum, r_sum; off_t off, size; int fd; if (argc < 3) { fprintf(stderr, "usage: %s /dev/daN <size_mb>\n", argv[0]); return (2); } size = (off_t)atoi(argv[2]) * 1024 * 1024; if (size < CHUNK) size = CHUNK; buf = malloc(CHUNK); rb = malloc(CHUNK); if (!buf || !rb) { perror("malloc"); return (1); } fd = open(argv[1], O_RDWR); if (fd < 0) { perror(argv[1]); return (1); } srandom(0xdfdf); fnv_init(); for (off = 0; off < size; off += CHUNK) { u_int32_t *p = (u_int32_t *)buf; for (size_t i = 0; i < CHUNK / sizeof(*p); i++) p[i] = (u_int32_t)random(); if (pwrite(fd, buf, CHUNK, off) != CHUNK) { perror("pwrite"); close(fd); return (1); } fnv_update(buf, CHUNK); } w_sum = fnv; fnv_init(); for (off = 0; off < size; off += CHUNK) { if (pread(fd, rb, CHUNK, off) != CHUNK) { perror("pread"); close(fd); return (1); } fnv_update(rb, CHUNK); } r_sum = fnv; close(fd); printf("write fnv1a64 = %016llx\nread fnv1a64 = %016llx\n%s\n", (unsigned long long)w_sum, (unsigned long long)r_sum, w_sum == r_sum ? "match" : "MISMATCH — data corrupted in flight"); return (w_sum == r_sum ? 0 : 1); } |