1 /* 2 * efi_selftest_bitblt 3 * 4 * Copyright (c) 2017 Heinrich Schuchardt <xypron.glpk@gmx.de> 5 * 6 * SPDX-License-Identifier: GPL-2.0+ 7 * 8 * Test the block image transfer in the graphical output protocol. 9 * An animated submarine is shown. 10 */ 11 12 #include <efi_selftest.h> 13 14 #define WIDTH 200 15 #define HEIGHT 120 16 #define DEPTH 60 17 18 static const struct efi_gop_pixel BLACK = { 0, 0, 0, 0}; 19 static const struct efi_gop_pixel RED = { 0, 0, 255, 0}; 20 static const struct efi_gop_pixel ORANGE = { 0, 128, 255, 0}; 21 static const struct efi_gop_pixel YELLOW = { 0, 255, 255, 0}; 22 static const struct efi_gop_pixel GREEN = { 0, 255, 0, 0}; 23 static const struct efi_gop_pixel DARK_BLUE = {128, 0, 0, 0}; 24 static const struct efi_gop_pixel LIGHT_BLUE = {255, 192, 192, 0}; 25 26 static struct efi_boot_services *boottime; 27 static efi_guid_t efi_gop_guid = EFI_GOP_GUID; 28 static struct efi_gop *gop; 29 static struct efi_gop_pixel *bitmap; 30 static struct efi_event *event; 31 static efi_uintn_t xpos; 32 33 static void ellipse(efi_uintn_t x, efi_uintn_t y, 34 efi_uintn_t x0, efi_uintn_t y0, 35 efi_uintn_t x1, efi_uintn_t y1, 36 const struct efi_gop_pixel col, struct efi_gop_pixel *pix) 37 { 38 efi_uintn_t xm = x0 + x1; 39 efi_uintn_t ym = y0 + y1; 40 efi_uintn_t dx = x1 - x0 + 1; 41 efi_uintn_t dy = y1 - y0 + 1; 42 43 if (dy * dy * (2 * x - xm) * (2 * x - xm) + 44 dx * dx * (2 * y - ym) * (2 * y - ym) <= dx * dx * dy * dy) 45 *pix = col; 46 } 47 48 static void rectangle(efi_uintn_t x, efi_uintn_t y, 49 efi_uintn_t x0, efi_uintn_t y0, 50 efi_uintn_t x1, efi_uintn_t y1, 51 const struct efi_gop_pixel col, struct efi_gop_pixel *pix) 52 { 53 if (x >= x0 && y >= y0 && x <= x1 && y <= y1) 54 *pix = col; 55 } 56 57 /* 58 * Notification function, copies image to video. 59 * The position is incremented in each call. 60 * 61 * @event notified event 62 * @context pointer to the notification count 63 */ 64 static void EFIAPI notify(struct efi_event *event, void *context) 65 { 66 efi_uintn_t *pos = context; 67 efi_uintn_t dx, sx, width; 68 69 if (!pos) 70 return; 71 72 /* Increment position */ 73 *pos += 5; 74 if (*pos >= WIDTH + gop->mode->info->width) 75 *pos = 0; 76 77 width = WIDTH; 78 dx = *pos - WIDTH; 79 sx = 0; 80 if (*pos >= gop->mode->info->width) { 81 width = WIDTH + gop->mode->info->width - *pos; 82 } else if (*pos < WIDTH) { 83 dx = 0; 84 sx = WIDTH - *pos; 85 width = *pos; 86 } 87 88 /* Copy image to video */ 89 gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, sx, 0, dx, DEPTH, 90 width, HEIGHT, WIDTH * sizeof(struct efi_gop_pixel)); 91 } 92 93 /* 94 * Setup unit test. 95 * 96 * @handle: handle of the loaded image 97 * @systable: system table 98 * @return: EFI_ST_SUCCESS for success 99 */ 100 static int setup(const efi_handle_t handle, 101 const struct efi_system_table *systable) 102 { 103 efi_status_t ret; 104 struct efi_gop_pixel pix; 105 efi_uintn_t x, y; 106 107 boottime = systable->boottime; 108 109 /* Create event */ 110 ret = boottime->create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, 111 TPL_CALLBACK, notify, (void *)&xpos, 112 &event); 113 if (ret != EFI_SUCCESS) { 114 efi_st_error("could not create event\n"); 115 return EFI_ST_FAILURE; 116 } 117 118 /* Get graphical output protocol */ 119 ret = boottime->locate_protocol(&efi_gop_guid, NULL, (void **)&gop); 120 if (ret != EFI_SUCCESS) { 121 gop = NULL; 122 efi_st_printf("Graphical output protocol is not available.\n"); 123 return EFI_ST_SUCCESS; 124 } 125 126 /* Prepare image of submarine */ 127 ret = boottime->allocate_pool(EFI_LOADER_DATA, 128 sizeof(struct efi_gop_pixel) * 129 WIDTH * HEIGHT, (void **)&bitmap); 130 if (ret != EFI_SUCCESS) { 131 efi_st_error("Out of memory\n"); 132 return EFI_ST_FAILURE; 133 } 134 for (y = 0; y < HEIGHT; ++y) { 135 for (x = 0; x < WIDTH; ++x) { 136 pix = DARK_BLUE; 137 138 /* Propeller */ 139 ellipse(x, y, 35, 55, 43, 75, BLACK, &pix); 140 ellipse(x, y, 36, 56, 42, 74, LIGHT_BLUE, &pix); 141 142 ellipse(x, y, 35, 75, 43, 95, BLACK, &pix); 143 ellipse(x, y, 36, 76, 42, 94, LIGHT_BLUE, &pix); 144 145 /* Shaft */ 146 rectangle(x, y, 35, 73, 100, 77, BLACK, &pix); 147 148 /* Periscope */ 149 ellipse(x, y, 120, 10, 160, 50, BLACK, &pix); 150 ellipse(x, y, 121, 11, 159, 59, YELLOW, &pix); 151 ellipse(x, y, 130, 20, 150, 40, BLACK, &pix); 152 ellipse(x, y, 131, 21, 149, 49, DARK_BLUE, &pix); 153 rectangle(x, y, 135, 10, 160, 50, DARK_BLUE, &pix); 154 ellipse(x, y, 132, 10, 138, 20, BLACK, &pix); 155 ellipse(x, y, 133, 11, 139, 19, RED, &pix); 156 157 /* Rudder */ 158 ellipse(x, y, 45, 40, 75, 70, BLACK, &pix); 159 ellipse(x, y, 46, 41, 74, 69, ORANGE, &pix); 160 ellipse(x, y, 45, 80, 75, 109, BLACK, &pix); 161 ellipse(x, y, 46, 81, 74, 108, RED, &pix); 162 163 /* Bridge */ 164 ellipse(x, y, 100, 30, 120, 50, BLACK, &pix); 165 ellipse(x, y, 101, 31, 119, 49, GREEN, &pix); 166 ellipse(x, y, 140, 30, 160, 50, BLACK, &pix); 167 ellipse(x, y, 141, 31, 159, 49, GREEN, &pix); 168 rectangle(x, y, 110, 30, 150, 50, BLACK, &pix); 169 rectangle(x, y, 110, 31, 150, 50, GREEN, &pix); 170 171 /* Hull */ 172 ellipse(x, y, 50, 40, 199, 109, BLACK, &pix); 173 ellipse(x, y, 51, 41, 198, 108, LIGHT_BLUE, &pix); 174 175 /* Port holes */ 176 ellipse(x, y, 79, 57, 109, 82, BLACK, &pix); 177 ellipse(x, y, 80, 58, 108, 81, LIGHT_BLUE, &pix); 178 ellipse(x, y, 83, 61, 105, 78, BLACK, &pix); 179 ellipse(x, y, 84, 62, 104, 77, YELLOW, &pix); 180 /* 181 * This port hole is created by copying 182 * ellipse(x, y, 119, 57, 149, 82, BLACK, &pix); 183 * ellipse(x, y, 120, 58, 148, 81, LIGHT_BLUE, &pix); 184 * ellipse(x, y, 123, 61, 145, 78, BLACK, &pix); 185 * ellipse(x, y, 124, 62, 144, 77, YELLOW, &pix); 186 */ 187 ellipse(x, y, 159, 57, 189, 82, BLACK, &pix); 188 ellipse(x, y, 160, 58, 188, 81, LIGHT_BLUE, &pix); 189 ellipse(x, y, 163, 61, 185, 78, BLACK, &pix); 190 ellipse(x, y, 164, 62, 184, 77, YELLOW, &pix); 191 192 bitmap[WIDTH * y + x] = pix; 193 } 194 } 195 196 return EFI_ST_SUCCESS; 197 } 198 199 /* 200 * Tear down unit test. 201 * 202 * @return: EFI_ST_SUCCESS for success 203 */ 204 static int teardown(void) 205 { 206 efi_status_t ret; 207 208 if (bitmap) { 209 ret = boottime->free_pool(bitmap); 210 if (ret != EFI_SUCCESS) { 211 efi_st_error("FreePool failed\n"); 212 return EFI_ST_FAILURE; 213 } 214 } 215 if (event) { 216 ret = boottime->close_event(event); 217 event = NULL; 218 if (ret != EFI_SUCCESS) { 219 efi_st_error("could not close event\n"); 220 return EFI_ST_FAILURE; 221 } 222 } 223 return EFI_ST_SUCCESS; 224 } 225 226 /* 227 * Execute unit test. 228 * 229 * @return: EFI_ST_SUCCESS for success 230 */ 231 static int execute(void) 232 { 233 u32 max_mode; 234 efi_status_t ret; 235 struct efi_gop_mode_info *info; 236 237 if (!gop) 238 return EFI_ST_SUCCESS; 239 240 if (!gop->mode) { 241 efi_st_error("EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE missing\n"); 242 return EFI_ST_FAILURE; 243 } 244 info = gop->mode->info; 245 max_mode = gop->mode->max_mode; 246 if (!max_mode) { 247 efi_st_error("No graphical mode available\n"); 248 return EFI_ST_FAILURE; 249 } 250 251 /* Fill background */ 252 ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_FILL, 0, 0, 0, 0, 253 info->width, info->height, 0); 254 if (ret != EFI_SUCCESS) { 255 efi_st_error("EFI_BLT_VIDEO_FILL failed\n"); 256 return EFI_ST_FAILURE; 257 } 258 259 /* Copy image to video */ 260 ret = gop->blt(gop, bitmap, EFI_BLT_BUFFER_TO_VIDEO, 0, 0, 0, DEPTH, 261 WIDTH, HEIGHT, 0); 262 if (ret != EFI_SUCCESS) { 263 efi_st_error("EFI_BLT_BUFFER_TO_VIDEO failed\n"); 264 return EFI_ST_FAILURE; 265 } 266 267 /* Copy left port hole */ 268 ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_VIDEO, 269 79, 57 + DEPTH, 119, 57 + DEPTH, 270 31, 26, 0); 271 if (ret != EFI_SUCCESS) { 272 efi_st_error("EFI_BLT_VIDEO_TO_VIDEO failed\n"); 273 return EFI_ST_FAILURE; 274 } 275 276 /* Copy port holes back to buffer */ 277 ret = gop->blt(gop, bitmap, EFI_BLT_VIDEO_TO_BLT_BUFFER, 278 94, 57 + DEPTH, 94, 57, 279 90, 26, WIDTH * sizeof(struct efi_gop_pixel)); 280 if (ret != EFI_SUCCESS) { 281 efi_st_error("EFI_BLT_VIDEO_TO_BLT_BUFFER failed\n"); 282 return EFI_ST_FAILURE; 283 } 284 285 /* Set 250ms timer */ 286 xpos = WIDTH; 287 ret = boottime->set_timer(event, EFI_TIMER_PERIODIC, 250000); 288 if (ret != EFI_SUCCESS) { 289 efi_st_error("Could not set timer\n"); 290 return EFI_ST_FAILURE; 291 } 292 293 con_out->set_cursor_position(con_out, 0, 0); 294 con_out->set_attribute(con_out, EFI_WHITE | EFI_BACKGROUND_BLUE); 295 efi_st_printf("The submarine should have three yellow port holes.\n"); 296 efi_st_printf("Press any key to continue"); 297 efi_st_get_key(); 298 con_out->set_attribute(con_out, EFI_LIGHTGRAY); 299 efi_st_printf("\n"); 300 301 return EFI_ST_SUCCESS; 302 } 303 304 EFI_UNIT_TEST(bitblt) = { 305 .name = "block image transfer", 306 .phase = EFI_EXECUTE_BEFORE_BOOTTIME_EXIT, 307 .setup = setup, 308 .execute = execute, 309 .teardown = teardown, 310 .on_request = true, 311 }; 312