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