DF-5194 / snake_probe.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 | /* * snake_probe.c -- DF-5194 guest harness (run as root) * * 1. positive control: force the snake saver to run on /dev/ttyv0 and * observe its writes in VGA text memory (phys 0xB8000) via /dev/mem; * two samples 1s apart must differ (the snake moved). * 2. guard check: while the snake is blanked, issue CONS_SET (mode change). * sc_set_text_mode -> sc_clean_up -> wait_scrn_saver_stop must unblank * the saver first (snake_saver(adp,0) resets `blanked`), so the ioctl * must return 0 after a brief wait and the console must be unblanked; * the next blank cycle must reinitialize the snake (fresh single spot). * 3. degenerate geometry attempt: there is no ioctl that can install a * 1-row text screen (only CONS_SET with a mode number; kernel clamps * to full mode dims) -- demonstrated by trying bogus values. */ #include <sys/ioctl.h> #include <sys/consio.h> #include <sys/fbio.h> #include <fcntl.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <time.h> #include <unistd.h> #define VRAM_PHYS 0xB8000ULL #define VRAM_LEN 8192 /* text plane window we sample */ static int read_vram(unsigned char *buf) { int fd = open("/dev/mem", O_RDONLY); ssize_t n; if (fd < 0) { perror("open /dev/mem"); return -1; } n = pread(fd, buf, VRAM_LEN, (off_t)VRAM_PHYS); close(fd); if (n != VRAM_LEN) { perror("pread vram"); return -1; } return 0; } /* count cells carrying the snake attribute (0x07) with a letter char */ static int snake_cells(const unsigned char *b) { int i, c = 0; for (i = 0; i < VRAM_LEN; i += 2) if (b[i+1] == 0x07 && b[i] != 0x20 && b[i] != 0) c++; return c; } static double now_sec(void) { struct timespec ts; clock_gettime(CLOCK_MONOTONIC, &ts); return ts.tv_sec + ts.tv_nsec / 1e9; } int main(void) { unsigned char a[VRAM_LEN], b[VRAM_LEN]; int fd, v = 1, t0, t1; double t_start, t_end; fd = open("/dev/ttyv0", O_WRONLY); if (fd < 0) { perror("open /dev/ttyv0"); return 1; } /* 1s blank timeout + force saver start */ if (ioctl(fd, CONS_BLANKTIME, &v)) { perror("CONS_BLANKTIME"); return 1; } if (ioctl(fd, CONS_SAVERSTART, &v)) { perror("CONS_SAVERSTART"); return 1; } printf("[*] waiting 3s for snake to draw...\n"); sleep(3); if (read_vram(a)) return 1; printf("[*] sample1: %d non-blank attr-0x07 cells\n", snake_cells(a)); sleep(1); if (read_vram(b)) return 1; printf("[*] sample2: %d non-blank attr-0x07 cells\n", snake_cells(b)); if (memcmp(a, b, VRAM_LEN) != 0) printf("[+] POSITIVE CONTROL: VRAM changed between samples -- " "snake_saver is executing and moving\n"); else printf("[-] VRAM static -- snake not running?\n"); /* --- guard check: mode change while blanked --- */ printf("[*] issuing CONS_SET(M_VGA_C80x30) while blanked...\n"); t0 = 32; /* M_VGA_C80x30 */ t_start = now_sec(); errno = 0; v = ioctl(fd, CONS_SET, &t0); t_end = now_sec(); printf("[*] CONS_SET -> rc=%d errno=%d after %.3fs\n", v, errno, t_end - t_start); if (v == 0) printf("[+] GUARD: mode change completed cleanly " "(saver was stopped/restarted by sc_clean_up)\n"); /* verify console unblanked and sane, then restore 80x25 */ t1 = 24; /* M_VGA_C80x25 */ v = ioctl(fd, CONS_SET, &t1); printf("[*] restore CONS_SET(80x25) -> rc=%d\n", v); /* force blank again: snake must reinitialize (fresh draw) */ v = 1; ioctl(fd, CONS_SAVERSTART, &v); sleep(2); if (read_vram(b)) return 1; printf("[*] post-reblank sample: %d attr-0x07 cells " "(fresh snake re-init expected)\n", snake_cells(b)); /* stop saver, restore no-blank */ v = 0; ioctl(fd, CONS_SAVERSTART, &v); ioctl(fd, CONS_BLANKTIME, &v); printf("[*] cleanup done; guest still alive: "); fflush(stdout); printf("yes\n"); return 0; } |