0fb76cc727b82eb7a3e44e89e88b95bddc2d2b83
[oweals/u-boot.git] / lib / efi_selftest / efi_selftest_bitblt.c
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 };