/* * GRUB -- GRand Unified Bootloader * Copyright (C) 2013 Free Software Foundation, Inc. * * GRUB is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * GRUB is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with GRUB. If not, see . */ /* All tests need to include test.h for GRUB testing framework. */ #include #include #include #include #include #include #include #ifdef GRUB_MACHINE_EMU #include #include #endif GRUB_MOD_LICENSE ("GPLv3+"); static int ctr; static int nchk; static char *basename; static const grub_uint32_t *checksums; static struct grub_video_mode_info capt_mode_info; struct grub_video_mode_info grub_test_video_modes[30] = { { .width = 640, .height = 480, .pitch = 640, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_NUMCOLORS }, { .width = 800, .height = 600, .pitch = 800, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_NUMCOLORS }, { .width = 1024, .height = 768, .pitch = 1024, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_NUMCOLORS }, { .width = 640, .height = 480, .pitch = 640 * 4, GRUB_VIDEO_MI_RGBA8888() }, { .width = 800, .height = 600, .pitch = 800 * 4, GRUB_VIDEO_MI_RGBA8888() }, { .width = 1024, .height = 768, .pitch = 1024 * 4, GRUB_VIDEO_MI_RGBA8888() }, { .width = 640, .height = 480, .pitch = 640, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_EXT_NUMCOLORS }, { .width = 800, .height = 600, .pitch = 800, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_EXT_NUMCOLORS }, { .width = 1024, .height = 768, .pitch = 1024, .mode_type = GRUB_VIDEO_MODE_TYPE_INDEX_COLOR, .bpp = 8, .bytes_per_pixel = 1, .number_of_colors = GRUB_VIDEO_FBSTD_EXT_NUMCOLORS }, { .width = 640, .height = 480, .pitch = 1280, GRUB_VIDEO_MI_RGB555 () }, { .width = 800, .height = 600, .pitch = 1600, GRUB_VIDEO_MI_RGB555 () }, { .width = 1024, .height = 768, .pitch = 2048, GRUB_VIDEO_MI_RGB555 () }, { .width = 640, .height = 480, .pitch = 1280, GRUB_VIDEO_MI_RGB565 () }, { .width = 800, .height = 600, .pitch = 1600, GRUB_VIDEO_MI_RGB565 () }, { .width = 1024, .height = 768, .pitch = 2048, GRUB_VIDEO_MI_RGB565 () }, { .width = 640, .height = 480, .pitch = 640 * 3, GRUB_VIDEO_MI_RGB888 () }, { .width = 800, .height = 600, .pitch = 800 * 3, GRUB_VIDEO_MI_RGB888 () }, { .width = 1024, .height = 768, .pitch = 1024 * 3, GRUB_VIDEO_MI_RGB888 () }, { .width = 640, .height = 480, .pitch = 1280, GRUB_VIDEO_MI_BGR555 () }, { .width = 800, .height = 600, .pitch = 1600, GRUB_VIDEO_MI_BGR555 () }, { .width = 1024, .height = 768, .pitch = 2048, GRUB_VIDEO_MI_BGR555 () }, { .width = 640, .height = 480, .pitch = 1280, GRUB_VIDEO_MI_BGR565 () }, { .width = 800, .height = 600, .pitch = 1600, GRUB_VIDEO_MI_BGR565 () }, { .width = 1024, .height = 768, .pitch = 2048, GRUB_VIDEO_MI_BGR565 () }, { .width = 640, .height = 480, .pitch = 640 * 3, GRUB_VIDEO_MI_BGR888 () }, { .width = 800, .height = 600, .pitch = 800 * 3, GRUB_VIDEO_MI_BGR888 () }, { .width = 1024, .height = 768, .pitch = 1024 * 3, GRUB_VIDEO_MI_BGR888 () }, { .width = 640, .height = 480, .pitch = 640 * 4, GRUB_VIDEO_MI_BGRA8888() }, { .width = 800, .height = 600, .pitch = 800 * 4, GRUB_VIDEO_MI_BGRA8888() }, { .width = 1024, .height = 768, .pitch = 1024 * 4, GRUB_VIDEO_MI_BGRA8888() }, }; #ifdef GRUB_MACHINE_EMU #include struct bmp_header { grub_uint8_t magic[2]; grub_uint32_t filesize; grub_uint32_t reserved; grub_uint32_t bmp_off; grub_uint32_t head_size; grub_uint16_t width; grub_uint16_t height; grub_uint16_t planes; grub_uint16_t bpp; } __attribute__ ((packed)); static void grub_video_capture_write_bmp (const char *fname, void *ptr, const struct grub_video_mode_info *mode_info) { grub_util_fd_t fd = grub_util_fd_open (fname, GRUB_UTIL_FD_O_WRONLY | GRUB_UTIL_FD_O_CREATTRUNC); struct bmp_header head; if (!GRUB_UTIL_FD_IS_VALID (fd)) { grub_printf (_("cannot open `%s': %s"), fname, grub_util_fd_strerror ()); } grub_memset (&head, 0, sizeof (head)); head.magic[0] = 'B'; head.magic[1] = 'M'; if (mode_info->mode_type & GRUB_VIDEO_MODE_TYPE_RGB) { head.filesize = grub_cpu_to_le32 (sizeof (head) + mode_info->width * mode_info->height * 3); head.bmp_off = grub_cpu_to_le32 (sizeof (head)); head.bpp = grub_cpu_to_le16_compile_time (24); } else { head.filesize = grub_cpu_to_le32 (sizeof (head) + 3 * 256 + mode_info->width * mode_info->height); head.bmp_off = grub_cpu_to_le32 (sizeof (head) + 3 * 256); head.bpp = grub_cpu_to_le16_compile_time (8); } head.head_size = grub_cpu_to_le32 (sizeof (head) - 14); head.width = grub_cpu_to_le16 (mode_info->width); head.height = grub_cpu_to_le16 (mode_info->height); head.planes = grub_cpu_to_le16_compile_time (1); head.width = grub_cpu_to_le16 (mode_info->width); head.height = grub_cpu_to_le16 (mode_info->height); grub_util_fd_write (fd, (char *) &head, sizeof (head)); if (!(mode_info->mode_type & GRUB_VIDEO_MODE_TYPE_RGB)) { struct grub_video_palette_data palette_data[256]; int i; int palette_len = mode_info->number_of_colors; grub_memset (palette_data, 0, sizeof (palette_data)); if (palette_len > 256) palette_len = 256; grub_video_get_palette (0, palette_len, palette_data); for (i = 0; i < 256; i++) { grub_uint8_t r, g, b; r = palette_data[i].r; g = palette_data[i].g; b = palette_data[i].b; grub_util_fd_write (fd, (char *) &b, 1); grub_util_fd_write (fd, (char *) &g, 1); grub_util_fd_write (fd, (char *) &r, 1); } } /* This does essentialy the same as some fbblit functions yet using them would mean testing them against themselves so keep this implemetation separate. */ switch (mode_info->bytes_per_pixel) { case 4: { grub_uint8_t *buffer = xmalloc (mode_info->width * 3); grub_uint32_t rmask = ((1 << mode_info->red_mask_size) - 1); grub_uint32_t gmask = ((1 << mode_info->green_mask_size) - 1); grub_uint32_t bmask = ((1 << mode_info->blue_mask_size) - 1); int rshift = mode_info->red_field_pos; int gshift = mode_info->green_field_pos; int bshift = mode_info->blue_field_pos; int mulrshift = (8 - mode_info->red_mask_size); int mulgshift = (8 - mode_info->green_mask_size); int mulbshift = (8 - mode_info->blue_mask_size); int y; for (y = mode_info->height - 1; y >= 0; y--) { grub_uint32_t *iptr = (grub_uint32_t *) ((grub_uint8_t *) ptr + mode_info->pitch * y); int x; grub_uint8_t *optr = buffer; for (x = 0; x < (int) mode_info->width; x++) { grub_uint32_t val = *iptr++; *optr++ = ((val >> bshift) & bmask) << mulbshift; *optr++ = ((val >> gshift) & gmask) << mulgshift; *optr++ = ((val >> rshift) & rmask) << mulrshift; } grub_util_fd_write (fd, (char *) buffer, mode_info->width * 3); } grub_free (buffer); break; } case 3: { grub_uint8_t *buffer = xmalloc (mode_info->width * 3); grub_uint32_t rmask = ((1 << mode_info->red_mask_size) - 1); grub_uint32_t gmask = ((1 << mode_info->green_mask_size) - 1); grub_uint32_t bmask = ((1 << mode_info->blue_mask_size) - 1); int rshift = mode_info->red_field_pos; int gshift = mode_info->green_field_pos; int bshift = mode_info->blue_field_pos; int mulrshift = (8 - mode_info->red_mask_size); int mulgshift = (8 - mode_info->green_mask_size); int mulbshift = (8 - mode_info->blue_mask_size); int y; for (y = mode_info->height - 1; y >= 0; y--) { grub_uint8_t *iptr = ((grub_uint8_t *) ptr + mode_info->pitch * y); int x; grub_uint8_t *optr = buffer; for (x = 0; x < (int) mode_info->width; x++) { grub_uint32_t val = 0; #ifdef GRUB_CPU_WORDS_BIGENDIAN val |= *iptr++ << 16; val |= *iptr++ << 8; val |= *iptr++; #else val |= *iptr++; val |= *iptr++ << 8; val |= *iptr++ << 16; #endif *optr++ = ((val >> bshift) & bmask) << mulbshift; *optr++ = ((val >> gshift) & gmask) << mulgshift; *optr++ = ((val >> rshift) & rmask) << mulrshift; } grub_util_fd_write (fd, (char *) buffer, mode_info->width * 3); } grub_free (buffer); break; } case 2: { grub_uint8_t *buffer = xmalloc (mode_info->width * 3); grub_uint16_t rmask = ((1 << mode_info->red_mask_size) - 1); grub_uint16_t gmask = ((1 << mode_info->green_mask_size) - 1); grub_uint16_t bmask = ((1 << mode_info->blue_mask_size) - 1); int rshift = mode_info->red_field_pos; int gshift = mode_info->green_field_pos; int bshift = mode_info->blue_field_pos; int mulrshift = (8 - mode_info->red_mask_size); int mulgshift = (8 - mode_info->green_mask_size); int mulbshift = (8 - mode_info->blue_mask_size); int y; for (y = mode_info->height - 1; y >= 0; y--) { grub_uint16_t *iptr = (grub_uint16_t *) ((grub_uint8_t *) ptr + mode_info->pitch * y); int x; grub_uint8_t *optr = buffer; for (x = 0; x < (int) mode_info->width; x++) { grub_uint16_t val = *iptr++; *optr++ = ((val >> bshift) & bmask) << mulbshift; *optr++ = ((val >> gshift) & gmask) << mulgshift; *optr++ = ((val >> rshift) & rmask) << mulrshift; } grub_util_fd_write (fd, (char *) buffer, mode_info->width * 3); } grub_free (buffer); break; } case 1: { int y; for (y = mode_info->height - 1; y >= 0; y--) grub_util_fd_write (fd, ((char *) ptr + mode_info->pitch * y), mode_info->width); break; } } grub_util_fd_close (fd); } #endif const char * grub_video_checksum_get_modename (void) { static char buf[40]; if (capt_mode_info.mode_type & GRUB_VIDEO_MODE_TYPE_INDEX_COLOR) { grub_snprintf (buf, sizeof (buf), "i%d", capt_mode_info.number_of_colors); return buf; } if (capt_mode_info.red_field_pos == 0) { grub_snprintf (buf, sizeof (buf), "bgra%d%d%d%d", capt_mode_info.blue_mask_size, capt_mode_info.green_mask_size, capt_mode_info.red_mask_size, capt_mode_info.reserved_mask_size); return buf; } grub_snprintf (buf, sizeof (buf), "rgba%d%d%d%d", capt_mode_info.red_mask_size, capt_mode_info.green_mask_size, capt_mode_info.blue_mask_size, capt_mode_info.reserved_mask_size); return buf; } #define GENERATE_MODE 1 //#define SAVE_ALL_IMAGES //#define COLLECT_TIME_STATISTICS 1 #if defined (GENERATE_MODE) && defined (GRUB_MACHINE_EMU) grub_util_fd_t genfd = GRUB_UTIL_FD_INVALID; #endif #include static void write_time (void) { #if defined (GRUB_MACHINE_EMU) && defined (COLLECT_TIME_STATISTICS) char buf[60]; static grub_uint64_t prev; grub_uint64_t cur; static grub_util_fd_t tmrfd = GRUB_UTIL_FD_INVALID; if (!GRUB_UTIL_FD_IS_VALID (tmrfd)) tmrfd = grub_util_fd_open ("time.txt", GRUB_UTIL_FD_O_WRONLY | GRUB_UTIL_FD_O_CREATTRUNC); cur = grub_util_get_cpu_time_ms (); grub_snprintf (buf, sizeof (buf), "%s_%dx%dx%s:%d: %" PRIuGRUB_UINT64_T " ms\n", basename, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename (), ctr, cur - prev); prev = cur; if (GRUB_UTIL_FD_IS_VALID (tmrfd)) grub_util_fd_write (tmrfd, buf, grub_strlen (buf)); #endif } static void checksum (void) { void *ptr; grub_uint32_t crc = 0; ptr = grub_video_capture_get_framebuffer (); write_time (); #ifdef GRUB_CPU_WORDS_BIGENDIAN switch (capt_mode_info.bytes_per_pixel) { case 1: crc = grub_getcrc32c (0, ptr, capt_mode_info.pitch * capt_mode_info.height); break; case 2: { unsigned x, y, rowskip; grub_uint8_t *iptr = ptr; crc = 0; rowskip = capt_mode_info.pitch - capt_mode_info.width * 2; for (y = 0; y < capt_mode_info.height; y++) { for (x = 0; x < capt_mode_info.width; x++) { crc = grub_getcrc32c (crc, iptr + 1, 1); crc = grub_getcrc32c (crc, iptr, 1); iptr += 2; } crc = grub_getcrc32c (crc, iptr, rowskip); iptr += rowskip; } break; } case 3: { unsigned x, y, rowskip; grub_uint8_t *iptr = ptr; crc = 0; rowskip = capt_mode_info.pitch - capt_mode_info.width * 3; for (y = 0; y < capt_mode_info.height; y++) { for (x = 0; x < capt_mode_info.width; x++) { crc = grub_getcrc32c (crc, iptr + 2, 1); crc = grub_getcrc32c (crc, iptr + 1, 1); crc = grub_getcrc32c (crc, iptr, 1); iptr += 3; } crc = grub_getcrc32c (crc, iptr, rowskip); iptr += rowskip; } break; } case 4: { unsigned x, y, rowskip; grub_uint8_t *iptr = ptr; crc = 0; rowskip = capt_mode_info.pitch - capt_mode_info.width * 4; for (y = 0; y < capt_mode_info.height; y++) { for (x = 0; x < capt_mode_info.width; x++) { crc = grub_getcrc32c (crc, iptr + 3, 1); crc = grub_getcrc32c (crc, iptr + 2, 1); crc = grub_getcrc32c (crc, iptr + 1, 1); crc = grub_getcrc32c (crc, iptr, 1); iptr += 4; } crc = grub_getcrc32c (crc, iptr, rowskip); iptr += rowskip; } break; } } #else crc = grub_getcrc32c (0, ptr, capt_mode_info.pitch * capt_mode_info.height); #endif #if defined (GENERATE_MODE) && defined (GRUB_MACHINE_EMU) if (GRUB_UTIL_FD_IS_VALID (genfd)) { char buf[20]; grub_snprintf (buf, sizeof (buf), "0x%x, ", crc); grub_util_fd_write (genfd, buf, grub_strlen (buf)); } #endif if (!checksums || ctr >= nchk) { grub_test_assert (0, "Unexpected checksum %s_%dx%dx%s:%d: 0x%x", basename, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename (), ctr, crc); } else if (crc != checksums[ctr]) { grub_test_assert (0, "Checksum %s_%dx%dx%s:%d failed: 0x%x vs 0x%x", basename, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename (), ctr, crc, checksums[ctr]); } #if !(defined (SAVE_ALL_IMAGES) && defined (GRUB_MACHINE_EMU)) else { write_time (); ctr++; return; } #endif #ifdef GRUB_MACHINE_EMU char *name = grub_xasprintf ("%s_%dx%dx%s_%d.bmp", basename, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename (), ctr); grub_video_capture_write_bmp (name, ptr, &capt_mode_info); grub_free (name); #endif write_time (); ctr++; } struct checksum_desc { const char *name; unsigned width; unsigned height; unsigned mode_type; unsigned number_of_colors; unsigned bpp; unsigned bytes_per_pixel; unsigned red_field_pos; unsigned red_mask_size; unsigned green_field_pos; unsigned green_mask_size; unsigned blue_field_pos; unsigned blue_mask_size; unsigned reserved_field_pos; unsigned reserved_mask_size; const grub_uint32_t *checksums; int nchk; }; const struct checksum_desc checksum_table[] = { #include "checksums.h" }; void grub_video_checksum (const char *basename_in) { unsigned i; grub_video_get_info (&capt_mode_info); #if defined (GENERATE_MODE) && defined (GRUB_MACHINE_EMU) if (!GRUB_UTIL_FD_IS_VALID (genfd)) genfd = grub_util_fd_open ("checksums.h", GRUB_UTIL_FD_O_WRONLY | GRUB_UTIL_FD_O_CREATTRUNC); if (GRUB_UTIL_FD_IS_VALID (genfd)) { char buf[400]; grub_snprintf (buf, sizeof (buf), "\", %d, %d, 0x%x, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d /* %dx%dx%s */, (grub_uint32_t []) { ", capt_mode_info.width, capt_mode_info.height, capt_mode_info.mode_type, capt_mode_info.number_of_colors, capt_mode_info.bpp, capt_mode_info.bytes_per_pixel, capt_mode_info.red_field_pos, capt_mode_info.red_mask_size, capt_mode_info.green_field_pos, capt_mode_info.green_mask_size, capt_mode_info.blue_field_pos, capt_mode_info.blue_mask_size, capt_mode_info.reserved_field_pos, capt_mode_info.reserved_mask_size, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename ()); grub_util_fd_write (genfd, " { \"", 5); grub_util_fd_write (genfd, basename_in, grub_strlen (basename_in)); grub_util_fd_write (genfd, buf, grub_strlen (buf)); } #endif basename = grub_strdup (basename_in); nchk = 0; checksums = 0; /* FIXME: optimize this. */ for (i = 0; i < ARRAY_SIZE (checksum_table); i++) if (grub_strcmp (checksum_table[i].name, basename_in) == 0 && capt_mode_info.width == checksum_table[i].width && capt_mode_info.height == checksum_table[i].height && capt_mode_info.mode_type == checksum_table[i].mode_type && capt_mode_info.number_of_colors == checksum_table[i].number_of_colors && capt_mode_info.bpp == checksum_table[i].bpp && capt_mode_info.bytes_per_pixel == checksum_table[i].bytes_per_pixel && capt_mode_info.red_field_pos == checksum_table[i].red_field_pos && capt_mode_info.red_mask_size == checksum_table[i].red_mask_size && capt_mode_info.green_field_pos == checksum_table[i].green_field_pos && capt_mode_info.green_mask_size == checksum_table[i].green_mask_size && capt_mode_info.blue_field_pos == checksum_table[i].blue_field_pos && capt_mode_info.blue_mask_size == checksum_table[i].blue_mask_size && capt_mode_info.reserved_field_pos == checksum_table[i].reserved_field_pos && capt_mode_info.reserved_mask_size == checksum_table[i].reserved_mask_size) { nchk = checksum_table[i].nchk; checksums = checksum_table[i].checksums; break; } ctr = 0; grub_video_capture_refresh_cb = checksum; } void grub_video_checksum_end (void) { #if defined (GENERATE_MODE) && defined (GRUB_MACHINE_EMU) if (GRUB_UTIL_FD_IS_VALID (genfd)) { char buf[40]; grub_snprintf (buf, sizeof (buf), "}, %d },\n", ctr); grub_util_fd_write (genfd, buf, grub_strlen (buf)); } #endif grub_test_assert (ctr == nchk, "Not enough checksums %s_%dx%dx%s: %d vs %d", basename, capt_mode_info.width, capt_mode_info.height, grub_video_checksum_get_modename (), ctr, nchk); grub_free (basename); basename = 0; nchk = 0; checksums = 0; ctr = 0; grub_video_capture_refresh_cb = 0; } static struct grub_term_output *saved_outputs; static struct grub_term_output *saved_gfxnext; static struct grub_term_output *gfxterm; static int use_gfxterm = 0; int grub_test_use_gfxterm (void) { FOR_ACTIVE_TERM_OUTPUTS (gfxterm) if (grub_strcmp (gfxterm->name, "gfxterm") == 0) break; if (!gfxterm) FOR_DISABLED_TERM_OUTPUTS (gfxterm) if (grub_strcmp (gfxterm->name, "gfxterm") == 0) break; if (!gfxterm) { grub_test_assert (0, "terminal `%s' isn't found", "gfxterm"); return 1; } if (gfxterm->init (gfxterm)) { grub_test_assert (0, "terminal `%s' failed: %s", "gfxterm", grub_errmsg); return 1; } saved_outputs = grub_term_outputs; saved_gfxnext = gfxterm->next; grub_term_outputs = gfxterm; gfxterm->next = 0; use_gfxterm = 1; return 0; } void grub_test_use_gfxterm_end (void) { if (!use_gfxterm) return; use_gfxterm = 0; gfxterm->fini (gfxterm); gfxterm->next = saved_gfxnext; grub_term_outputs = saved_outputs; saved_outputs = 0; saved_gfxnext = 0; }