video.c (7248B)
1// SPDX-License-Identifier: GPL-2.0-only 2/* -*- linux-c -*- ------------------------------------------------------- * 3 * 4 * Copyright (C) 1991, 1992 Linus Torvalds 5 * Copyright 2007 rPath, Inc. - All Rights Reserved 6 * Copyright 2009 Intel Corporation; author H. Peter Anvin 7 * 8 * ----------------------------------------------------------------------- */ 9 10/* 11 * Select video mode 12 */ 13 14#include <uapi/asm/boot.h> 15 16#include "boot.h" 17#include "video.h" 18#include "vesa.h" 19 20static u16 video_segment; 21 22static void store_cursor_position(void) 23{ 24 struct biosregs ireg, oreg; 25 26 initregs(&ireg); 27 ireg.ah = 0x03; 28 intcall(0x10, &ireg, &oreg); 29 30 boot_params.screen_info.orig_x = oreg.dl; 31 boot_params.screen_info.orig_y = oreg.dh; 32 33 if (oreg.ch & 0x20) 34 boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR; 35 36 if ((oreg.ch & 0x1f) > (oreg.cl & 0x1f)) 37 boot_params.screen_info.flags |= VIDEO_FLAGS_NOCURSOR; 38} 39 40static void store_video_mode(void) 41{ 42 struct biosregs ireg, oreg; 43 44 /* N.B.: the saving of the video page here is a bit silly, 45 since we pretty much assume page 0 everywhere. */ 46 initregs(&ireg); 47 ireg.ah = 0x0f; 48 intcall(0x10, &ireg, &oreg); 49 50 /* Not all BIOSes are clean with respect to the top bit */ 51 boot_params.screen_info.orig_video_mode = oreg.al & 0x7f; 52 boot_params.screen_info.orig_video_page = oreg.bh; 53} 54 55/* 56 * Store the video mode parameters for later usage by the kernel. 57 * This is done by asking the BIOS except for the rows/columns 58 * parameters in the default 80x25 mode -- these are set directly, 59 * because some very obscure BIOSes supply insane values. 60 */ 61static void store_mode_params(void) 62{ 63 u16 font_size; 64 int x, y; 65 66 /* For graphics mode, it is up to the mode-setting driver 67 (currently only video-vesa.c) to store the parameters */ 68 if (graphic_mode) 69 return; 70 71 store_cursor_position(); 72 store_video_mode(); 73 74 if (boot_params.screen_info.orig_video_mode == 0x07) { 75 /* MDA, HGC, or VGA in monochrome mode */ 76 video_segment = 0xb000; 77 } else { 78 /* CGA, EGA, VGA and so forth */ 79 video_segment = 0xb800; 80 } 81 82 set_fs(0); 83 font_size = rdfs16(0x485); /* Font size, BIOS area */ 84 boot_params.screen_info.orig_video_points = font_size; 85 86 x = rdfs16(0x44a); 87 y = (adapter == ADAPTER_CGA) ? 25 : rdfs8(0x484)+1; 88 89 if (force_x) 90 x = force_x; 91 if (force_y) 92 y = force_y; 93 94 boot_params.screen_info.orig_video_cols = x; 95 boot_params.screen_info.orig_video_lines = y; 96} 97 98static unsigned int get_entry(void) 99{ 100 char entry_buf[4]; 101 int i, len = 0; 102 int key; 103 unsigned int v; 104 105 do { 106 key = getchar(); 107 108 if (key == '\b') { 109 if (len > 0) { 110 puts("\b \b"); 111 len--; 112 } 113 } else if ((key >= '0' && key <= '9') || 114 (key >= 'A' && key <= 'Z') || 115 (key >= 'a' && key <= 'z')) { 116 if (len < sizeof(entry_buf)) { 117 entry_buf[len++] = key; 118 putchar(key); 119 } 120 } 121 } while (key != '\r'); 122 putchar('\n'); 123 124 if (len == 0) 125 return VIDEO_CURRENT_MODE; /* Default */ 126 127 v = 0; 128 for (i = 0; i < len; i++) { 129 v <<= 4; 130 key = entry_buf[i] | 0x20; 131 v += (key > '9') ? key-'a'+10 : key-'0'; 132 } 133 134 return v; 135} 136 137static void display_menu(void) 138{ 139 struct card_info *card; 140 struct mode_info *mi; 141 char ch; 142 int i; 143 int nmodes; 144 int modes_per_line; 145 int col; 146 147 nmodes = 0; 148 for (card = video_cards; card < video_cards_end; card++) 149 nmodes += card->nmodes; 150 151 modes_per_line = 1; 152 if (nmodes >= 20) 153 modes_per_line = 3; 154 155 for (col = 0; col < modes_per_line; col++) 156 puts("Mode: Resolution: Type: "); 157 putchar('\n'); 158 159 col = 0; 160 ch = '0'; 161 for (card = video_cards; card < video_cards_end; card++) { 162 mi = card->modes; 163 for (i = 0; i < card->nmodes; i++, mi++) { 164 char resbuf[32]; 165 int visible = mi->x && mi->y; 166 u16 mode_id = mi->mode ? mi->mode : 167 (mi->y << 8)+mi->x; 168 169 if (!visible) 170 continue; /* Hidden mode */ 171 172 if (mi->depth) 173 sprintf(resbuf, "%dx%d", mi->y, mi->depth); 174 else 175 sprintf(resbuf, "%d", mi->y); 176 177 printf("%c %03X %4dx%-7s %-6s", 178 ch, mode_id, mi->x, resbuf, card->card_name); 179 col++; 180 if (col >= modes_per_line) { 181 putchar('\n'); 182 col = 0; 183 } 184 185 if (ch == '9') 186 ch = 'a'; 187 else if (ch == 'z' || ch == ' ') 188 ch = ' '; /* Out of keys... */ 189 else 190 ch++; 191 } 192 } 193 if (col) 194 putchar('\n'); 195} 196 197#define H(x) ((x)-'a'+10) 198#define SCAN ((H('s')<<12)+(H('c')<<8)+(H('a')<<4)+H('n')) 199 200static unsigned int mode_menu(void) 201{ 202 int key; 203 unsigned int sel; 204 205 puts("Press <ENTER> to see video modes available, " 206 "<SPACE> to continue, or wait 30 sec\n"); 207 208 kbd_flush(); 209 while (1) { 210 key = getchar_timeout(); 211 if (key == ' ' || key == 0) 212 return VIDEO_CURRENT_MODE; /* Default */ 213 if (key == '\r') 214 break; 215 putchar('\a'); /* Beep! */ 216 } 217 218 219 for (;;) { 220 display_menu(); 221 222 puts("Enter a video mode or \"scan\" to scan for " 223 "additional modes: "); 224 sel = get_entry(); 225 if (sel != SCAN) 226 return sel; 227 228 probe_cards(1); 229 } 230} 231 232/* Save screen content to the heap */ 233static struct saved_screen { 234 int x, y; 235 int curx, cury; 236 u16 *data; 237} saved; 238 239static void save_screen(void) 240{ 241 /* Should be called after store_mode_params() */ 242 saved.x = boot_params.screen_info.orig_video_cols; 243 saved.y = boot_params.screen_info.orig_video_lines; 244 saved.curx = boot_params.screen_info.orig_x; 245 saved.cury = boot_params.screen_info.orig_y; 246 247 if (!heap_free(saved.x*saved.y*sizeof(u16)+512)) 248 return; /* Not enough heap to save the screen */ 249 250 saved.data = GET_HEAP(u16, saved.x*saved.y); 251 252 set_fs(video_segment); 253 copy_from_fs(saved.data, 0, saved.x*saved.y*sizeof(u16)); 254} 255 256static void restore_screen(void) 257{ 258 /* Should be called after store_mode_params() */ 259 int xs = boot_params.screen_info.orig_video_cols; 260 int ys = boot_params.screen_info.orig_video_lines; 261 int y; 262 addr_t dst = 0; 263 u16 *src = saved.data; 264 struct biosregs ireg; 265 266 if (graphic_mode) 267 return; /* Can't restore onto a graphic mode */ 268 269 if (!src) 270 return; /* No saved screen contents */ 271 272 /* Restore screen contents */ 273 274 set_fs(video_segment); 275 for (y = 0; y < ys; y++) { 276 int npad; 277 278 if (y < saved.y) { 279 int copy = (xs < saved.x) ? xs : saved.x; 280 copy_to_fs(dst, src, copy*sizeof(u16)); 281 dst += copy*sizeof(u16); 282 src += saved.x; 283 npad = (xs < saved.x) ? 0 : xs-saved.x; 284 } else { 285 npad = xs; 286 } 287 288 /* Writes "npad" blank characters to 289 video_segment:dst and advances dst */ 290 asm volatile("pushw %%es ; " 291 "movw %2,%%es ; " 292 "shrw %%cx ; " 293 "jnc 1f ; " 294 "stosw \n\t" 295 "1: rep;stosl ; " 296 "popw %%es" 297 : "+D" (dst), "+c" (npad) 298 : "bdS" (video_segment), 299 "a" (0x07200720)); 300 } 301 302 /* Restore cursor position */ 303 if (saved.curx >= xs) 304 saved.curx = xs-1; 305 if (saved.cury >= ys) 306 saved.cury = ys-1; 307 308 initregs(&ireg); 309 ireg.ah = 0x02; /* Set cursor position */ 310 ireg.dh = saved.cury; 311 ireg.dl = saved.curx; 312 intcall(0x10, &ireg, NULL); 313 314 store_cursor_position(); 315} 316 317void set_video(void) 318{ 319 u16 mode = boot_params.hdr.vid_mode; 320 321 RESET_HEAP(); 322 323 store_mode_params(); 324 save_screen(); 325 probe_cards(0); 326 327 for (;;) { 328 if (mode == ASK_VGA) 329 mode = mode_menu(); 330 331 if (!set_mode(mode)) 332 break; 333 334 printf("Undefined video mode number: %x\n", mode); 335 mode = ASK_VGA; 336 } 337 boot_params.hdr.vid_mode = mode; 338 vesa_store_edid(); 339 store_mode_params(); 340 341 if (do_restore) 342 restore_screen(); 343}