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 12static 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 20uint32_t get_gfx_mode() { 21 efi_graphics_output_protocol* gop = fb_get_gop(); 22 return gop->Mode->Mode; 23} 24 25uint32_t get_gfx_max_mode() { 26 efi_graphics_output_protocol* gop = fb_get_gop(); 27 return gop->Mode->MaxMode; 28} 29 30uint32_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 41uint32_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 52void 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 68void 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 (int 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 110void 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 (int 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 127static efi_graphics_output_blt_pixel font_white = { 128 .Red = 0xFF, 129 .Green = 0xFF, 130 .Blue = 0xFF, 131}; 132 133static efi_graphics_output_blt_pixel font_black = { 134 .Red = 0x0, 135 .Green = 0x0, 136 .Blue = 0x0, 137}; 138 139static efi_graphics_output_blt_pixel font_fuchsia = { 140 .Red = 0xFF, 141 .Green = 0x0, 142 .Blue = 0x80, 143}; 144 145void 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 189static 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 201void 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 222void 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 240void 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