1 /* ----------------------------------------------------------------------- * 2 * 3 * Copyright 1999-2012 H. Peter Anvin - All Rights Reserved 4 * Chandramouli Narayanan - extended for EFI support 5 * 6 * Permission is hereby granted, free of charge, to any person 7 * obtaining a copy of this software and associated documentation 8 * files (the "Software"), to deal in the Software without 9 * restriction, including without limitation the rights to use, 10 * copy, modify, merge, publish, distribute, sublicense, and/or 11 * sell copies of the Software, and to permit persons to whom 12 * the Software is furnished to do so, subject to the following 13 * conditions: 14 * 15 * The above copyright notice and this permission notice shall 16 * be included in all copies or substantial portions of the Software. 17 * 18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 19 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES 20 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 21 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT 22 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 23 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 24 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 25 * OTHER DEALINGS IN THE SOFTWARE. 26 * 27 * ----------------------------------------------------------------------- */ 28 29 #include <inttypes.h> 30 #include <com32.h> 31 #include <stdlib.h> 32 #include <string.h> 33 #include <sys/fpu.h> 34 #include <syslinux/video.h> 35 #include <dprintf.h> 36 #include "efi.h" 37 /* We use cp865_8x16.psf as the standard font for EFI implementation 38 * the header file below contains raw data parsed from cp865_8x16.psf 39 */ 40 #include "cp865_8x16.h" 41 #include "sys/vesa/vesa.h" 42 #include "sys/vesa/video.h" 43 #include "sys/vesa/fill.h" 44 #include "sys/vesa/debug.h" 45 46 /* EFI GOP support 47 * Note GOP support uses the VESA info structure as much as possible and 48 * extends it as needed for EFI support. Not all of the vesa info structure 49 * is populated. Care must be taken in the routines that rely the vesa 50 * informataion structure 51 */ 52 static void find_pixmask_bits(uint32_t mask, uint8_t *first_bit, uint8_t *len) { 53 uint8_t bit_pos = 0, bit_len = 0; 54 55 *first_bit = 0; 56 *len = 0; 57 if (mask == 0) 58 return; 59 while (!(mask & 0x1)) { 60 mask = mask >> 1; 61 bit_pos++; 62 } 63 while (mask & 0x1) { 64 mask = mask >> 1; 65 bit_len++; 66 } 67 *first_bit = bit_pos; 68 *len = bit_len; 69 } 70 71 unsigned long lfb_size; 72 uint16_t lfb_line_size; 73 uint8_t lfb_rsize; 74 uint8_t lfb_gsize; 75 uint8_t lfb_bsize; 76 uint8_t lfb_resv_size; 77 78 static int efi_vesacon_set_mode(struct vesa_info *vesa_info, int *x, int *y, 79 enum vesa_pixel_format *bestpxf) 80 { 81 EFI_GUID GraphicsOutputProtocolGuid = EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID; 82 EFI_GRAPHICS_OUTPUT_PROTOCOL *GraphicsOutput = NULL; 83 EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE *gop_mode; 84 EFI_GRAPHICS_OUTPUT_MODE_INFORMATION *mode_info; 85 EFI_STATUS st; 86 UINT32 mode_num = 0, bestmode; 87 BOOLEAN mode_match = FALSE; 88 UINTN sz_info; 89 struct vesa_info *vi; 90 struct vesa_mode_info *mi; 91 int err = 0; 92 93 //debug("Hello, World!\r\n"); 94 /* At this point, we assume that gnu-efi library is initialized */ 95 st = LibLocateProtocol(&GraphicsOutputProtocolGuid, (VOID **) &GraphicsOutput); 96 if (EFI_ERROR(st)) { 97 debug("LiblocateProtocol for GOP failed %d\n", st); 98 return 1; /* function call failed */ 99 } 100 101 /* We use the VESA info structure to store relevant GOP info as much as possible */ 102 gop_mode = GraphicsOutput->Mode; 103 104 mode_info = gop_mode->Info; 105 dprintf("mode %d version %d pixlfmt %d hres=%d vres=%d\n", mode_num, 106 mode_info->Version, mode_info->PixelFormat, 107 mode_info->HorizontalResolution, mode_info->VerticalResolution); 108 109 /* simply pick the best mode that suits the caller's resolution */ 110 for (mode_num = 0; mode_num < gop_mode->MaxMode; mode_num++) { 111 st = uefi_call_wrapper(GraphicsOutput->QueryMode, 4, GraphicsOutput, mode_num, &sz_info, &mode_info); 112 debug("mode_num = %d query_status %d\n", mode_num, st); 113 if (st == EFI_SUCCESS && sz_info >= sizeof(EFI_GRAPHICS_OUTPUT_MODE_INFORMATION)) { 114 115 /* For now, simply pick the best mode that suits caller's resolution (x,y) 116 * FIXME: Consider any additional criteria for matching mode 117 */ 118 mode_match = ((uint32_t)*x == mode_info->HorizontalResolution && (uint32_t)*y == mode_info->VerticalResolution); 119 debug("mode %d hres=%d vres=%d\n", mode_num, mode_info->HorizontalResolution, mode_info->VerticalResolution); 120 if (mode_match) { 121 bestmode = mode_num; 122 break; 123 } 124 } 125 } 126 127 if (!mode_match) { 128 /* Instead of bailing out, set the mode to the system default. 129 * Some systems do not have support for 640x480 for instance 130 * This code deals with such cases. 131 */ 132 mode_info = gop_mode->Info; 133 *x = mode_info->HorizontalResolution; 134 *y = mode_info->VerticalResolution; 135 bestmode = gop_mode->Mode; 136 debug("No matching mode, setting to available default mode %d (x=%d, y=%d)\n", bestmode, *x, *y); 137 } 138 139 /* Allocate space in the bounce buffer for these structures */ 140 vi = malloc(sizeof(*vi)); 141 if (!vi) { 142 err = 10; /* Out of memory */ 143 goto exit; 144 } 145 /* Note that the generic info is untouched as we don't find any relevance to EFI */ 146 mi = &vi->mi; 147 /* Set up mode-specific information */ 148 mi->h_res = *x; 149 mi->v_res = *y; 150 mi->lfb_ptr = (uint8_t *)(VOID *)(UINTN)gop_mode->FrameBufferBase; 151 lfb_size = gop_mode->FrameBufferSize; 152 153 /* FIXME: 154 * The code below treats bpp == lfb_depth ; verify 155 */ 156 157 switch (mode_info->PixelFormat) { 158 case PixelRedGreenBlueReserved8BitPerColor: 159 dprintf("RGB8bit "); 160 mi->mode_attr = 0x0080; /* supports physical frame buffer */ 161 mi->bpp = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * 8; 162 mi->rpos = 0; 163 mi->gpos = 8; 164 mi->bpos = 16; 165 mi->resv_pos = 24; 166 lfb_resv_size = 8; 167 mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8; 168 *bestpxf = PXF_BGRA32; 169 dprintf("bpp %d pixperScanLine %d logical_scan %d bytesperPix %d\n", mi->bpp, mode_info->PixelsPerScanLine, 170 mi->logical_scan, (mi->bpp + 7)>>3); 171 break; 172 case PixelBlueGreenRedReserved8BitPerColor: 173 dprintf("BGR8bit "); 174 mi->mode_attr = 0x0080; /* supports physical frame buffer */ 175 mi->bpp = sizeof(EFI_GRAPHICS_OUTPUT_BLT_PIXEL) * 8; 176 mi->bpos = 0; 177 mi->gpos = 8; 178 mi->rpos = 16; 179 mi->resv_pos = 24; 180 lfb_resv_size = 8; 181 mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8; 182 *bestpxf = PXF_BGRA32; 183 dprintf("bpp %d pixperScanLine %d logical_scan %d bytesperPix %d\n", mi->bpp, mode_info->PixelsPerScanLine, 184 mi->logical_scan, (mi->bpp + 7)>>3); 185 break; 186 case PixelBitMask: 187 mi->mode_attr = 0x0080; /* supports physical frame buffer */ 188 dprintf("RedMask 0x%x GrnMask 0x%x BluMask 0x%x RsvMask 0x%x\n", 189 mode_info->PixelInformation.RedMask, 190 mode_info->PixelInformation.GreenMask, 191 mode_info->PixelInformation.BlueMask, 192 mode_info->PixelInformation.ReservedMask); 193 find_pixmask_bits(mode_info->PixelInformation.RedMask, 194 &mi->rpos, &lfb_rsize); 195 find_pixmask_bits(mode_info->PixelInformation.GreenMask, 196 &mi->gpos, &lfb_gsize); 197 find_pixmask_bits(mode_info->PixelInformation.BlueMask, 198 &mi->bpos, &lfb_bsize); 199 find_pixmask_bits(mode_info->PixelInformation.ReservedMask, 200 &mi->resv_pos, &lfb_resv_size); 201 mi->bpp = lfb_rsize + lfb_gsize + 202 lfb_bsize + lfb_resv_size; 203 mi->logical_scan = lfb_line_size = (mode_info->PixelsPerScanLine * mi->bpp) / 8; 204 dprintf("RPos %d Rsize %d GPos %d Gsize %d\n", mi->rpos, lfb_rsize, mi->gpos, lfb_gsize); 205 dprintf("BPos %d Bsize %d RsvP %d RsvSz %d\n", mi->bpos, lfb_bsize, mi->resv_pos, lfb_resv_size); 206 dprintf("bpp %d logical_scan %d bytesperPix %d\n", mi->bpp, mi->logical_scan, (mi->bpp + 7)>>3); 207 switch (mi->bpp) { 208 case 32: 209 *bestpxf = PXF_BGRA32; 210 break; 211 case 24: 212 *bestpxf = PXF_BGR24; 213 break; 214 case 16: 215 *bestpxf = PXF_LE_RGB16_565; 216 break; 217 default: 218 dprintf("Unable to handle bits per pixel %d, bailing out\n", mi->bpp); 219 err = 4; 220 goto exit; 221 } 222 break; 223 case PixelBltOnly: 224 /* FIXME: unsupported */ 225 mi->mode_attr = 0x0000; /* no support for physical frame buffer */ 226 err = 4; /* no mode found */ 227 goto exit; 228 break; 229 default: 230 /* should not get here, but let's error out */ 231 err = 4; /* no mode found */ 232 goto exit; 233 break; 234 } 235 236 memcpy(&vesa_info->mi, mi, sizeof *mi); 237 238 /* Now set video mode */ 239 st = uefi_call_wrapper(GraphicsOutput->SetMode, 2, GraphicsOutput, bestmode); 240 if (EFI_ERROR(st)) { 241 err = 9; /* Failed to set mode */ 242 dprintf("Failed to set mode %d\n", bestmode); 243 goto exit; 244 } 245 246 /* TODO: Follow the code usage of vesacon_background & vesacon_shadowfb */ 247 /* 248 __vesacon_background = calloc(mi->h_res*mi->v_res, 4); 249 __vesacon_shadowfb = calloc(mi->h_res*mi->v_res, 4); 250 */ 251 /* FIXME: the allocation takes the possible padding into account 252 * whereas BIOS code simply allocates hres * vres bytes. 253 * Which is correct? 254 */ 255 /* 256 * For performance reasons, or due to hardware restrictions, scan lines 257 * may be padded to an amount of memory alignment. These padding pixel elements 258 * are outside the area covered by HorizontalResolution and are not visible. 259 * For direct frame buffer access, this number is used as a span between starts 260 * of pixel lines in video memory. Based on the size of an individual pixel element 261 * and PixelsPerScanline, the offset in video memory from pixel element (x, y) 262 * to pixel element (x, y+1) has to be calculated as 263 * "sizeof( PixelElement ) * PixelsPerScanLine", and not 264 * "sizeof( PixelElement ) * HorizontalResolution", though in many cases 265 * those values can coincide. 266 */ 267 268 exit: 269 if (vi) 270 free(vi); 271 272 return err; 273 } 274 275 static void efi_vesacon_screencpy(size_t dst, const uint32_t *s, 276 size_t bytes, struct win_info *wi) 277 { 278 size_t win_off; 279 char *win_base = wi->win_base; 280 281 /* For EFI, we simply take the offset from the framebuffer and write to it 282 * FIXME: any gotchas? 283 */ 284 win_off = dst; 285 memcpy(win_base + win_off, s, bytes); 286 } 287 288 static int efi_vesacon_font_query(uint8_t **font) 289 { 290 /* set up font info 291 * For now, font info is stored as raw data and used 292 * as such. Altenatively, the font data stored in a file 293 * could be read and parsed. (note: for this, EFI 294 * file support should be exposed via firmware structure) 295 */ 296 *font = (uint8_t *)cp865_8x16_font_data; 297 return cp865_8x16_font_height; 298 } 299 300 __export int __vesacon_i915resolution(int x, int y) 301 { 302 /* We don't support this function */ 303 return 1; 304 } 305 306 struct vesa_ops efi_vesa_ops = { 307 .set_mode = efi_vesacon_set_mode, 308 .screencpy = efi_vesacon_screencpy, 309 .font_query = efi_vesacon_font_query, 310 }; 311