1 // Copyright 2016 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include <framebuffer.h>
6 #include <xefi.h>
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
fb_get_gop()12 static efi_graphics_output_protocol* fb_get_gop() {
13     static efi_graphics_output_protocol* gop = NULL;
14     if (!gop) {
15         gBS->LocateProtocol(&GraphicsOutputProtocol, NULL, (void**)&gop);
16     }
17     return gop;
18 }
19 
get_gfx_mode()20 uint32_t get_gfx_mode() {
21     efi_graphics_output_protocol* gop = fb_get_gop();
22     return gop->Mode->Mode;
23 }
24 
get_gfx_max_mode()25 uint32_t get_gfx_max_mode() {
26     efi_graphics_output_protocol* gop = fb_get_gop();
27     return gop->Mode->MaxMode;
28 }
29 
get_gfx_hres()30 uint32_t get_gfx_hres() {
31     efi_graphics_output_protocol* gop = fb_get_gop();
32     efi_graphics_output_mode_information* mode_info;
33     size_t info_size = 0;
34     efi_status status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info);
35     if (EFI_ERROR(status)) {
36         return 0;
37     }
38     return mode_info->HorizontalResolution;
39 }
40 
get_gfx_vres()41 uint32_t get_gfx_vres() {
42     efi_graphics_output_protocol* gop = fb_get_gop();
43     efi_graphics_output_mode_information* mode_info;
44     size_t info_size = 0;
45     efi_status status = gop->QueryMode(gop, gop->Mode->Mode, &info_size, &mode_info);
46     if (EFI_ERROR(status)) {
47         return 0;
48     }
49     return mode_info->VerticalResolution;
50 }
51 
set_gfx_mode(uint32_t mode)52 void set_gfx_mode(uint32_t mode) {
53     efi_graphics_output_protocol* gop = fb_get_gop();
54     if (!gop)
55         return;
56     if (mode >= gop->Mode->MaxMode) {
57         printf("invalid framebuffer mode: %u\n", mode);
58         return;
59     }
60     efi_status s = gop->SetMode(gop, mode);
61     if (EFI_ERROR(s)) {
62         printf("could not set mode: %s\n", xefi_strerror(s));
63     }
64     gBS->Stall(1000);
65     gSys->ConOut->SetCursorPosition(gSys->ConOut, 0, 0);
66 }
67 
set_gfx_mode_from_cmdline(const char * fbres)68 void set_gfx_mode_from_cmdline(const char* fbres) {
69     if (!fbres)
70         return;
71     efi_graphics_output_protocol* gop = fb_get_gop();
72     if (!gop)
73         return;
74 
75     uint32_t hres = 0;
76     hres = atol(fbres);
77 
78     char* x = strchr(fbres, 'x');
79     if (!x)
80         return;
81     x++;
82 
83     uint32_t vres = 0;
84     vres = atol(x);
85     if (!hres || !vres)
86         return;
87 
88     uint32_t max_mode = gop->Mode->MaxMode;
89 
90     for (uint32_t i = 0; i < max_mode; i++) {
91         efi_graphics_output_mode_information* mode_info;
92         size_t info_size = 0;
93         efi_status status = gop->QueryMode(gop, i, &info_size, &mode_info);
94         if (EFI_ERROR(status)) {
95             printf("Could not retrieve mode %d: %s\n", i, xefi_strerror(status));
96             continue;
97         }
98 
99         if (mode_info->HorizontalResolution == hres &&
100             mode_info->VerticalResolution == vres) {
101             set_gfx_mode(i);
102             return;
103         }
104     }
105     printf("Could not find framebuffer mode %ux%u; using default mode = %ux%u\n",
106            hres, vres, gop->Mode->Info->HorizontalResolution, gop->Mode->Info->VerticalResolution);
107     gBS->Stall(5000000);
108 }
109 
print_fb_modes()110 void print_fb_modes() {
111     efi_graphics_output_protocol* gop = fb_get_gop();
112     uint32_t max_mode = gop->Mode->MaxMode;
113     uint32_t cur_mode = gop->Mode->Mode;
114     for (uint32_t i = 0; i < max_mode; i++) {
115         efi_graphics_output_mode_information* mode_info;
116         size_t info_size = 0;
117         efi_status status = gop->QueryMode(gop, i, &info_size, &mode_info);
118         if (EFI_ERROR(status))
119             continue;
120         printf(" (%u) %u x %u%s\n", i, mode_info->HorizontalResolution,
121                mode_info->VerticalResolution, i == cur_mode ? " (current)" : "");
122     }
123 }
124 
125 #include "logo.h"
126 
127 static efi_graphics_output_blt_pixel font_white = {
128     .Red = 0xFF,
129     .Green = 0xFF,
130     .Blue = 0xFF,
131 };
132 
133 static efi_graphics_output_blt_pixel font_black = {
134     .Red = 0x0,
135     .Green = 0x0,
136     .Blue = 0x0,
137 };
138 
139 static efi_graphics_output_blt_pixel font_fuchsia = {
140     .Red = 0xFF,
141     .Green = 0x0,
142     .Blue = 0x80,
143 };
144 
draw_logo()145 void draw_logo() {
146     efi_graphics_output_protocol* gop = fb_get_gop();
147     if (!gop)
148         return;
149 
150     const uint32_t h_res = gop->Mode->Info->HorizontalResolution;
151     const uint32_t v_res = gop->Mode->Info->VerticalResolution;
152 
153     // Blank the screen, removing vendor UEFI logos
154     gop->Blt(gop, &font_black, EfiBltVideoFill, 0, 0, 0, 0, h_res, v_res, 0);
155 
156     efi_graphics_output_blt_pixel* tmp;
157     unsigned sz = sizeof(efi_graphics_output_blt_pixel) * logo_width * logo_height;
158     if (EFI_ERROR(gBS->AllocatePool(EfiLoaderData, sz, (void*)&tmp))) {
159         // Draw the Fuchsia stripe on the top of the screen
160         gop->Blt(gop, &font_fuchsia, EfiBltVideoFill, 0, 0, 0, 0, h_res, v_res / 100, 0);
161         return;
162     }
163 
164     // Un-RLE the logo
165     unsigned char* iptr = logo_rle;
166     efi_graphics_output_blt_pixel* optr = tmp;
167     unsigned entries = sizeof(logo_rle) / 2;
168     while (entries-- > 0) {
169         unsigned count = *iptr++;
170         unsigned alpha = *iptr++;
171         efi_graphics_output_blt_pixel px = {
172             .Red = (alpha * 0xFF) / 255,
173             .Green = 0,
174             .Blue = (alpha * 0x80) / 255,
175         };
176         while (count-- > 0) {
177             *optr++ = px;
178         }
179     }
180 
181     gop->Blt(gop, tmp, EfiBltBufferToVideo, 0, 0,
182              h_res - logo_width - (h_res / 75), v_res - logo_height - (v_res / 75),
183              logo_width, logo_height, 0);
184 }
185 
186 #include <zircon/font/font-9x16.h>
187 #include <zircon/font/font-18x32.h>
188 
putchar(efi_graphics_output_protocol * gop,fb_font * font,unsigned ch,unsigned x,unsigned y,unsigned scale_x,unsigned scale_y,efi_graphics_output_blt_pixel * fg,efi_graphics_output_blt_pixel * bg)189 static void putchar(efi_graphics_output_protocol* gop, fb_font* font, unsigned ch, unsigned x, unsigned y, unsigned scale_x, unsigned scale_y, efi_graphics_output_blt_pixel* fg, efi_graphics_output_blt_pixel* bg) {
190     const uint16_t* cdata = font->data + ch * font->height;
191     unsigned fw = font->width;
192     for (unsigned i = 0; i <= font->height; i++) {
193         uint16_t xdata = *cdata++;
194         for (unsigned j = 0; j < fw; j++) {
195             gop->Blt(gop, (xdata & 1) ? fg : bg, EfiBltVideoFill, 0, 0, x + scale_x * j, y + scale_y * i, scale_x, scale_y, 0);
196             xdata >>= 1;
197         }
198     }
199 }
200 
draw_text(const char * text,size_t length,fb_font * font,int x,int y)201 void draw_text(const char* text, size_t length, fb_font* font, int x, int y) {
202     efi_graphics_output_protocol* gop = fb_get_gop();
203     efi_graphics_output_blt_pixel* fg_color = &font_white;
204     if (!gop)
205         return;
206 
207     if (font->color != NULL) {
208         fg_color = font->color;
209     }
210 
211     size_t offset = 0;
212     size_t scale = 1;
213     for (size_t i = 0; i < length; ++i) {
214         unsigned char c = text[i];
215         if (c > 127)
216             continue;
217         putchar(gop, font, c, x + offset, y, scale, scale, fg_color, &font_black);
218         offset += font->width * scale;
219     }
220 }
221 
draw_nodename(const char * nodename)222 void draw_nodename(const char* nodename) {
223     efi_graphics_output_protocol* gop = fb_get_gop();
224     if (!gop)
225         return;
226 
227     fb_font font = {
228         .data = FONT18X32,
229         .width = FONT18X32_WIDTH,
230         .height = FONT18X32_HEIGHT,
231         .color = &font_white,
232     };
233 
234     const uint32_t h_res = gop->Mode->Info->HorizontalResolution;
235     const uint32_t v_res = gop->Mode->Info->VerticalResolution;
236     size_t length = strlen(nodename);
237     draw_text(nodename, length, &font, h_res - (length + 1) * font.width, v_res / 100 + font.height);
238 }
239 
draw_version(const char * version)240 void draw_version(const char* version) {
241     efi_graphics_output_protocol* gop = fb_get_gop();
242     if (!gop)
243         return;
244 
245     const char* prefix = "GigaBoot 20X6 - Version";
246     size_t prefix_len = strlen(prefix);
247     size_t version_len = strlen(version);
248 
249     fb_font font = {
250         .data = FONT9X16,
251         .width = FONT9X16_WIDTH,
252         .height = FONT9X16_HEIGHT,
253         .color = &font_fuchsia,
254     };
255 
256     draw_text(prefix, prefix_len, &font, 0, 0);
257     draw_text(version, version_len, &font, (prefix_len + 1) * font.width, 0);
258 }
259