[e16e8f2] | 1 | #include <sys/ansi.h> |
---|
| 2 | #include <sys/io.h> |
---|
| 3 | #include <fs.h> |
---|
| 4 | #include <bios.h> |
---|
| 5 | #include <com32.h> |
---|
| 6 | #include <graphics.h> |
---|
| 7 | #include <syslinux/memscan.h> |
---|
| 8 | #include <syslinux/firmware.h> |
---|
| 9 | #include <syslinux/video.h> |
---|
| 10 | |
---|
| 11 | #include <sys/vesa/vesa.h> |
---|
| 12 | #include <sys/vesa/video.h> |
---|
| 13 | #include <sys/vesa/debug.h> |
---|
| 14 | #include <minmax.h> |
---|
| 15 | #include "core.h" |
---|
| 16 | |
---|
| 17 | __export struct firmware *firmware = NULL; |
---|
| 18 | |
---|
| 19 | extern struct ansi_ops bios_ansi_ops; |
---|
| 20 | |
---|
| 21 | #define BIOS_CURXY ((struct curxy *)0x450) /* Array for each page */ |
---|
| 22 | #define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */ |
---|
| 23 | #define BIOS_COLS (*(uint16_t *)0x44A) |
---|
| 24 | #define BIOS_PAGE (*(uint8_t *)0x462) |
---|
| 25 | |
---|
| 26 | static void bios_text_mode(void) |
---|
| 27 | { |
---|
| 28 | syslinux_force_text_mode(); |
---|
| 29 | } |
---|
| 30 | |
---|
| 31 | static void bios_get_mode(int *cols, int *rows) |
---|
| 32 | { |
---|
| 33 | *rows = BIOS_ROWS ? BIOS_ROWS + 1 : 25; |
---|
| 34 | *cols = BIOS_COLS; |
---|
| 35 | } |
---|
| 36 | |
---|
| 37 | static uint16_t cursor_type; /* Saved cursor pattern */ |
---|
| 38 | |
---|
| 39 | static void bios_get_cursor(uint8_t *x, uint8_t *y) |
---|
| 40 | { |
---|
| 41 | com32sys_t ireg, oreg; |
---|
| 42 | |
---|
| 43 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 44 | |
---|
| 45 | ireg.eax.b[1] = 0x03; |
---|
| 46 | ireg.ebx.b[1] = BIOS_PAGE; |
---|
| 47 | __intcall(0x10, &ireg, &oreg); |
---|
| 48 | cursor_type = oreg.ecx.w[0]; |
---|
| 49 | *x = oreg.edx.b[0]; |
---|
| 50 | *y = oreg.edx.b[1]; |
---|
| 51 | } |
---|
| 52 | |
---|
| 53 | static void bios_erase(int x0, int y0, int x1, int y1, uint8_t attribute) |
---|
| 54 | { |
---|
| 55 | static com32sys_t ireg; |
---|
| 56 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 57 | |
---|
| 58 | ireg.eax.w[0] = 0x0600; /* Clear window */ |
---|
| 59 | ireg.ebx.b[1] = attribute; |
---|
| 60 | ireg.ecx.b[0] = x0; |
---|
| 61 | ireg.ecx.b[1] = y0; |
---|
| 62 | ireg.edx.b[0] = x1; |
---|
| 63 | ireg.edx.b[1] = y1; |
---|
| 64 | __intcall(0x10, &ireg, NULL); |
---|
| 65 | } |
---|
| 66 | |
---|
| 67 | static void bios_showcursor(const struct term_state *st) |
---|
| 68 | { |
---|
| 69 | static com32sys_t ireg; |
---|
| 70 | uint16_t cursor = st->cursor ? cursor_type : 0x2020; |
---|
| 71 | |
---|
| 72 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 73 | |
---|
| 74 | ireg.eax.b[1] = 0x01; |
---|
| 75 | ireg.ecx.w[0] = cursor; |
---|
| 76 | __intcall(0x10, &ireg, NULL); |
---|
| 77 | } |
---|
| 78 | |
---|
| 79 | static void bios_set_cursor(int x, int y, bool visible) |
---|
| 80 | { |
---|
| 81 | const int page = BIOS_PAGE; |
---|
| 82 | struct curxy xy = BIOS_CURXY[page]; |
---|
| 83 | static com32sys_t ireg; |
---|
| 84 | |
---|
| 85 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 86 | |
---|
| 87 | (void)visible; |
---|
| 88 | |
---|
| 89 | if (xy.x != x || xy.y != y) { |
---|
| 90 | ireg.eax.b[1] = 0x02; |
---|
| 91 | ireg.ebx.b[1] = page; |
---|
| 92 | ireg.edx.b[1] = y; |
---|
| 93 | ireg.edx.b[0] = x; |
---|
| 94 | __intcall(0x10, &ireg, NULL); |
---|
| 95 | } |
---|
| 96 | } |
---|
| 97 | |
---|
| 98 | static void bios_write_char(uint8_t ch, uint8_t attribute) |
---|
| 99 | { |
---|
| 100 | static com32sys_t ireg; |
---|
| 101 | |
---|
| 102 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 103 | |
---|
| 104 | ireg.eax.b[1] = 0x09; |
---|
| 105 | ireg.eax.b[0] = ch; |
---|
| 106 | ireg.ebx.b[1] = BIOS_PAGE; |
---|
| 107 | ireg.ebx.b[0] = attribute; |
---|
| 108 | ireg.ecx.w[0] = 1; |
---|
| 109 | __intcall(0x10, &ireg, NULL); |
---|
| 110 | } |
---|
| 111 | |
---|
| 112 | static void bios_scroll_up(uint8_t cols, uint8_t rows, uint8_t attribute) |
---|
| 113 | { |
---|
| 114 | static com32sys_t ireg; |
---|
| 115 | |
---|
| 116 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 117 | |
---|
| 118 | ireg.eax.w[0] = 0x0601; |
---|
| 119 | ireg.ebx.b[1] = attribute; |
---|
| 120 | ireg.ecx.w[0] = 0; |
---|
| 121 | ireg.edx.b[1] = rows; |
---|
| 122 | ireg.edx.b[0] = cols; |
---|
| 123 | __intcall(0x10, &ireg, NULL); /* Scroll */ |
---|
| 124 | } |
---|
| 125 | |
---|
| 126 | static void bios_beep(void) |
---|
| 127 | { |
---|
| 128 | static com32sys_t ireg; |
---|
| 129 | |
---|
| 130 | memset(&ireg, 0, sizeof(ireg)); |
---|
| 131 | |
---|
| 132 | ireg.eax.w[0] = 0x0e07; |
---|
| 133 | ireg.ebx.b[1] = BIOS_PAGE; |
---|
| 134 | __intcall(0x10, &ireg, NULL); |
---|
| 135 | } |
---|
| 136 | |
---|
| 137 | struct output_ops bios_output_ops = { |
---|
| 138 | .erase = bios_erase, |
---|
| 139 | .write_char = bios_write_char, |
---|
| 140 | .showcursor = bios_showcursor, |
---|
| 141 | .set_cursor = bios_set_cursor, |
---|
| 142 | .scroll_up = bios_scroll_up, |
---|
| 143 | .beep = bios_beep, |
---|
| 144 | .get_mode = bios_get_mode, |
---|
| 145 | .text_mode = bios_text_mode, |
---|
| 146 | .get_cursor = bios_get_cursor, |
---|
| 147 | }; |
---|
| 148 | |
---|
| 149 | extern char bios_getchar(char *); |
---|
| 150 | extern int bios_pollchar(void); |
---|
| 151 | extern uint8_t bios_shiftflags(void); |
---|
| 152 | |
---|
| 153 | struct input_ops bios_input_ops = { |
---|
| 154 | .getchar = bios_getchar, |
---|
| 155 | .pollchar = bios_pollchar, |
---|
| 156 | .shiftflags = bios_shiftflags, |
---|
| 157 | }; |
---|
| 158 | |
---|
| 159 | static void bios_get_serial_console_info(uint16_t *iobase, uint16_t *divisor, |
---|
| 160 | uint16_t *flowctl) |
---|
| 161 | { |
---|
| 162 | *iobase = SerialPort; |
---|
| 163 | *divisor = BaudDivisor; |
---|
| 164 | |
---|
| 165 | *flowctl = FlowOutput | FlowInput | (FlowIgnore << 4); |
---|
| 166 | |
---|
| 167 | if (!DisplayCon) |
---|
| 168 | *flowctl |= (0x80 << 8); |
---|
| 169 | } |
---|
| 170 | |
---|
| 171 | void bios_adv_init(void) |
---|
| 172 | { |
---|
| 173 | static com32sys_t reg; |
---|
| 174 | |
---|
| 175 | memset(®, 0, sizeof(reg)); |
---|
| 176 | call16(adv_init, ®, NULL); |
---|
| 177 | } |
---|
| 178 | |
---|
| 179 | int bios_adv_write(void) |
---|
| 180 | { |
---|
| 181 | static com32sys_t reg; |
---|
| 182 | |
---|
| 183 | memset(®, 0, sizeof(reg)); |
---|
| 184 | call16(adv_write, ®, ®); |
---|
| 185 | return (reg.eflags.l & EFLAGS_CF) ? -1 : 0; |
---|
| 186 | } |
---|
| 187 | |
---|
| 188 | struct adv_ops bios_adv_ops = { |
---|
| 189 | .init = bios_adv_init, |
---|
| 190 | .write = bios_adv_write, |
---|
| 191 | }; |
---|
| 192 | |
---|
| 193 | |
---|
| 194 | static int __constfunc is_power_of_2(unsigned int x) |
---|
| 195 | { |
---|
| 196 | return x && !(x & (x - 1)); |
---|
| 197 | } |
---|
| 198 | |
---|
| 199 | static int vesacon_paged_mode_ok(const struct vesa_mode_info *mi) |
---|
| 200 | { |
---|
| 201 | int i; |
---|
| 202 | |
---|
| 203 | if (!is_power_of_2(mi->win_size) || |
---|
| 204 | !is_power_of_2(mi->win_grain) || mi->win_grain > mi->win_size) |
---|
| 205 | return 0; /* Impossible... */ |
---|
| 206 | |
---|
| 207 | for (i = 0; i < 2; i++) { |
---|
| 208 | if ((mi->win_attr[i] & 0x05) == 0x05 && mi->win_seg[i]) |
---|
| 209 | return 1; /* Usable window */ |
---|
| 210 | } |
---|
| 211 | |
---|
| 212 | return 0; /* Nope... */ |
---|
| 213 | } |
---|
| 214 | |
---|
| 215 | static int bios_vesacon_set_mode(struct vesa_info *vesa_info, int *px, int *py, |
---|
| 216 | enum vesa_pixel_format *bestpxf) |
---|
| 217 | { |
---|
| 218 | com32sys_t rm; |
---|
| 219 | uint16_t mode, bestmode, *mode_ptr; |
---|
| 220 | struct vesa_info *vi; |
---|
| 221 | struct vesa_general_info *gi; |
---|
| 222 | struct vesa_mode_info *mi; |
---|
| 223 | enum vesa_pixel_format pxf; |
---|
| 224 | int x = *px, y = *py; |
---|
| 225 | int err = 0; |
---|
| 226 | |
---|
| 227 | /* Allocate space in the bounce buffer for these structures */ |
---|
| 228 | vi = lzalloc(sizeof *vi); |
---|
| 229 | if (!vi) { |
---|
| 230 | err = 10; /* Out of memory */ |
---|
| 231 | goto exit; |
---|
| 232 | } |
---|
| 233 | gi = &vi->gi; |
---|
| 234 | mi = &vi->mi; |
---|
| 235 | |
---|
| 236 | memset(&rm, 0, sizeof rm); |
---|
| 237 | |
---|
| 238 | gi->signature = VBE2_MAGIC; /* Get VBE2 extended data */ |
---|
| 239 | rm.eax.w[0] = 0x4F00; /* Get SVGA general information */ |
---|
| 240 | rm.edi.w[0] = OFFS(gi); |
---|
| 241 | rm.es = SEG(gi); |
---|
| 242 | __intcall(0x10, &rm, &rm); |
---|
| 243 | |
---|
| 244 | if (rm.eax.w[0] != 0x004F) { |
---|
| 245 | err = 1; /* Function call failed */ |
---|
| 246 | goto exit; |
---|
| 247 | } |
---|
| 248 | if (gi->signature != VESA_MAGIC) { |
---|
| 249 | err = 2; /* No magic */ |
---|
| 250 | goto exit; |
---|
| 251 | } |
---|
| 252 | if (gi->version < 0x0102) { |
---|
| 253 | err = 3; /* VESA 1.2+ required */ |
---|
| 254 | goto exit; |
---|
| 255 | } |
---|
| 256 | |
---|
| 257 | /* Copy general info */ |
---|
| 258 | memcpy(&vesa_info->gi, gi, sizeof *gi); |
---|
| 259 | |
---|
| 260 | /* Search for the proper mode with a suitable color and memory model... */ |
---|
| 261 | |
---|
| 262 | mode_ptr = GET_PTR(gi->video_mode_ptr); |
---|
| 263 | bestmode = 0; |
---|
| 264 | *bestpxf = PXF_NONE; |
---|
| 265 | |
---|
| 266 | while ((mode = *mode_ptr++) != 0xFFFF) { |
---|
| 267 | mode &= 0x1FF; /* The rest are attributes of sorts */ |
---|
| 268 | |
---|
| 269 | debug("Found mode: 0x%04x\r\n", mode); |
---|
| 270 | |
---|
| 271 | memset(&rm, 0, sizeof rm); |
---|
| 272 | memset(mi, 0, sizeof *mi); |
---|
| 273 | rm.eax.w[0] = 0x4F01; /* Get SVGA mode information */ |
---|
| 274 | rm.ecx.w[0] = mode; |
---|
| 275 | rm.edi.w[0] = OFFS(mi); |
---|
| 276 | rm.es = SEG(mi); |
---|
| 277 | __intcall(0x10, &rm, &rm); |
---|
| 278 | |
---|
| 279 | /* Must be a supported mode */ |
---|
| 280 | if (rm.eax.w[0] != 0x004f) |
---|
| 281 | continue; |
---|
| 282 | |
---|
| 283 | debug |
---|
| 284 | ("mode_attr 0x%04x, h_res = %4d, v_res = %4d, bpp = %2d, layout = %d (%d,%d,%d)\r\n", |
---|
| 285 | mi->mode_attr, mi->h_res, mi->v_res, mi->bpp, mi->memory_layout, |
---|
| 286 | mi->rpos, mi->gpos, mi->bpos); |
---|
| 287 | |
---|
| 288 | /* Must be an LFB color graphics mode supported by the hardware. |
---|
| 289 | |
---|
| 290 | The bits tested are: |
---|
| 291 | 4 - graphics mode |
---|
| 292 | 3 - color mode |
---|
| 293 | 1 - mode information available (mandatory in VBE 1.2+) |
---|
| 294 | 0 - mode supported by hardware |
---|
| 295 | */ |
---|
| 296 | if ((mi->mode_attr & 0x001b) != 0x001b) |
---|
| 297 | continue; |
---|
| 298 | |
---|
| 299 | /* Must be the chosen size */ |
---|
| 300 | if (mi->h_res != x || mi->v_res != y) |
---|
| 301 | continue; |
---|
| 302 | |
---|
| 303 | /* We don't support multibank (interlaced memory) modes */ |
---|
| 304 | /* |
---|
| 305 | * Note: The Bochs VESA BIOS (vbe.c 1.58 2006/08/19) violates the |
---|
| 306 | * specification which states that banks == 1 for unbanked modes; |
---|
| 307 | * fortunately it does report bank_size == 0 for those. |
---|
| 308 | */ |
---|
| 309 | if (mi->banks > 1 && mi->bank_size) { |
---|
| 310 | debug("bad: banks = %d, banksize = %d, pages = %d\r\n", |
---|
| 311 | mi->banks, mi->bank_size, mi->image_pages); |
---|
| 312 | continue; |
---|
| 313 | } |
---|
| 314 | |
---|
| 315 | /* Must be either a flat-framebuffer mode, or be an acceptable |
---|
| 316 | paged mode */ |
---|
| 317 | if (!(mi->mode_attr & 0x0080) && !vesacon_paged_mode_ok(mi)) { |
---|
| 318 | debug("bad: invalid paged mode\r\n"); |
---|
| 319 | continue; |
---|
| 320 | } |
---|
| 321 | |
---|
| 322 | /* Must either be a packed-pixel mode or a direct color mode |
---|
| 323 | (depending on VESA version ); must be a supported pixel format */ |
---|
| 324 | pxf = PXF_NONE; /* Not usable */ |
---|
| 325 | |
---|
| 326 | if (mi->bpp == 32 && |
---|
| 327 | (mi->memory_layout == 4 || |
---|
| 328 | (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 && |
---|
| 329 | mi->bpos == 0))) |
---|
| 330 | pxf = PXF_BGRA32; |
---|
| 331 | else if (mi->bpp == 24 && |
---|
| 332 | (mi->memory_layout == 4 || |
---|
| 333 | (mi->memory_layout == 6 && mi->rpos == 16 && mi->gpos == 8 && |
---|
| 334 | mi->bpos == 0))) |
---|
| 335 | pxf = PXF_BGR24; |
---|
| 336 | else if (mi->bpp == 16 && |
---|
| 337 | (mi->memory_layout == 4 || |
---|
| 338 | (mi->memory_layout == 6 && mi->rpos == 11 && mi->gpos == 5 && |
---|
| 339 | mi->bpos == 0))) |
---|
| 340 | pxf = PXF_LE_RGB16_565; |
---|
| 341 | else if (mi->bpp == 15 && |
---|
| 342 | (mi->memory_layout == 4 || |
---|
| 343 | (mi->memory_layout == 6 && mi->rpos == 10 && mi->gpos == 5 && |
---|
| 344 | mi->bpos == 0))) |
---|
| 345 | pxf = PXF_LE_RGB15_555; |
---|
| 346 | |
---|
| 347 | if (pxf < *bestpxf) { |
---|
| 348 | debug("Best mode so far, pxf = %d\r\n", pxf); |
---|
| 349 | |
---|
| 350 | /* Best mode so far... */ |
---|
| 351 | bestmode = mode; |
---|
| 352 | *bestpxf = pxf; |
---|
| 353 | |
---|
| 354 | /* Copy mode info */ |
---|
| 355 | memcpy(&vesa_info->mi, mi, sizeof *mi); |
---|
| 356 | } |
---|
| 357 | } |
---|
| 358 | |
---|
| 359 | if (*bestpxf == PXF_NONE) { |
---|
| 360 | err = 4; /* No mode found */ |
---|
| 361 | goto exit; |
---|
| 362 | } |
---|
| 363 | |
---|
| 364 | mi = &vesa_info->mi; |
---|
| 365 | mode = bestmode; |
---|
| 366 | |
---|
| 367 | memset(&rm, 0, sizeof rm); |
---|
| 368 | /* Now set video mode */ |
---|
| 369 | rm.eax.w[0] = 0x4F02; /* Set SVGA video mode */ |
---|
| 370 | if (mi->mode_attr & 0x0080) |
---|
| 371 | mode |= 0x4000; /* Request linear framebuffer if supported */ |
---|
| 372 | rm.ebx.w[0] = mode; |
---|
| 373 | __intcall(0x10, &rm, &rm); |
---|
| 374 | if (rm.eax.w[0] != 0x004F) { |
---|
| 375 | err = 9; /* Failed to set mode */ |
---|
| 376 | goto exit; |
---|
| 377 | } |
---|
| 378 | |
---|
| 379 | exit: |
---|
| 380 | if (vi) |
---|
| 381 | lfree(vi); |
---|
| 382 | |
---|
| 383 | return err; |
---|
| 384 | } |
---|
| 385 | |
---|
| 386 | static void set_window_pos(struct win_info *wi, size_t win_pos) |
---|
| 387 | { |
---|
| 388 | static com32sys_t ireg; |
---|
| 389 | |
---|
| 390 | wi->win_pos = win_pos; |
---|
| 391 | |
---|
| 392 | if (wi->win_num < 0) |
---|
| 393 | return; /* This should never happen... */ |
---|
| 394 | |
---|
| 395 | memset(&ireg, 0, sizeof ireg); |
---|
| 396 | ireg.eax.w[0] = 0x4F05; |
---|
| 397 | ireg.ebx.b[0] = wi->win_num; |
---|
| 398 | ireg.edx.w[0] = win_pos >> wi->win_gshift; |
---|
| 399 | |
---|
| 400 | __intcall(0x10, &ireg, NULL); |
---|
| 401 | } |
---|
| 402 | |
---|
| 403 | static void bios_vesacon_screencpy(size_t dst, const uint32_t * src, |
---|
| 404 | size_t bytes, struct win_info *wi) |
---|
| 405 | { |
---|
| 406 | size_t win_pos, win_off; |
---|
| 407 | size_t win_size = wi->win_size; |
---|
| 408 | size_t omask = win_size - 1; |
---|
| 409 | char *win_base = wi->win_base; |
---|
| 410 | const char *s = (const char *)src; |
---|
| 411 | size_t l; |
---|
| 412 | |
---|
| 413 | while (bytes) { |
---|
| 414 | win_off = dst & omask; |
---|
| 415 | win_pos = dst & ~omask; |
---|
| 416 | |
---|
| 417 | if (__unlikely(win_pos != wi->win_pos)) |
---|
| 418 | set_window_pos(wi, win_pos); |
---|
| 419 | |
---|
| 420 | l = min(bytes, win_size - win_off); |
---|
| 421 | memcpy(win_base + win_off, s, l); |
---|
| 422 | |
---|
| 423 | bytes -= l; |
---|
| 424 | s += l; |
---|
| 425 | dst += l; |
---|
| 426 | } |
---|
| 427 | } |
---|
| 428 | |
---|
| 429 | static int bios_font_query(uint8_t **font) |
---|
| 430 | { |
---|
| 431 | com32sys_t rm; |
---|
| 432 | |
---|
| 433 | /* Get BIOS 8x16 font */ |
---|
| 434 | |
---|
| 435 | memset(&rm, 0, sizeof rm); |
---|
| 436 | |
---|
| 437 | rm.eax.w[0] = 0x1130; /* Get Font Information */ |
---|
| 438 | rm.ebx.w[0] = 0x0600; /* Get 8x16 ROM font */ |
---|
| 439 | __intcall(0x10, &rm, &rm); |
---|
| 440 | *font = MK_PTR(rm.es, rm.ebp.w[0]); |
---|
| 441 | |
---|
| 442 | return 16; |
---|
| 443 | |
---|
| 444 | } |
---|
| 445 | struct vesa_ops bios_vesa_ops = { |
---|
| 446 | .set_mode = bios_vesacon_set_mode, |
---|
| 447 | .screencpy = bios_vesacon_screencpy, |
---|
| 448 | .font_query = bios_font_query, |
---|
| 449 | }; |
---|
| 450 | |
---|
| 451 | static uint32_t min_lowmem_heap = 65536; |
---|
| 452 | extern char __lowmem_heap[]; |
---|
| 453 | uint8_t KbdFlags; /* Check for keyboard escapes */ |
---|
| 454 | __export uint8_t KbdMap[256]; /* Keyboard map */ |
---|
| 455 | |
---|
| 456 | __export uint16_t PXERetry; |
---|
| 457 | |
---|
| 458 | static inline void check_escapes(void) |
---|
| 459 | { |
---|
| 460 | com32sys_t ireg, oreg; |
---|
| 461 | |
---|
| 462 | memset(&ireg, 0, sizeof ireg); |
---|
| 463 | ireg.eax.b[1] = 0x02; /* Check keyboard flags */ |
---|
| 464 | __intcall(0x16, &ireg, &oreg); |
---|
| 465 | |
---|
| 466 | KbdFlags = oreg.eax.b[0]; |
---|
| 467 | |
---|
| 468 | /* Ctrl->skip 386 check */ |
---|
| 469 | if (!(oreg.eax.b[0] & 0x04)) { |
---|
| 470 | /* |
---|
| 471 | * Now check that there is sufficient low (DOS) memory |
---|
| 472 | * |
---|
| 473 | * NOTE: Linux doesn't use all of real_mode_seg, but we use |
---|
| 474 | * the same segment for COMBOOT images, which can use all 64K. |
---|
| 475 | */ |
---|
| 476 | uint32_t mem; |
---|
| 477 | |
---|
| 478 | __intcall(0x12, &ireg, &oreg); |
---|
| 479 | |
---|
| 480 | mem = ((uint32_t)__lowmem_heap) + min_lowmem_heap + 1023; |
---|
| 481 | mem = mem >> 10; |
---|
| 482 | |
---|
| 483 | if (oreg.eax.w[0] < mem) { |
---|
| 484 | char buf[256]; |
---|
| 485 | |
---|
| 486 | snprintf(buf, sizeof(buf), |
---|
| 487 | "It appears your computer has only " |
---|
| 488 | "%dK of low (\"DOS\") RAM.\n" |
---|
| 489 | "This version of Syslinux needs " |
---|
| 490 | "%dK to boot. " |
---|
| 491 | "If you get this\nmessage in error, " |
---|
| 492 | "hold down the Ctrl key while booting, " |
---|
| 493 | "and I\nwill take your word for it.\n", |
---|
| 494 | oreg.eax.w[0], mem); |
---|
| 495 | writestr(buf); |
---|
| 496 | kaboom(); |
---|
| 497 | } |
---|
| 498 | } |
---|
| 499 | } |
---|
| 500 | |
---|
| 501 | extern uint32_t BIOS_timer_next; |
---|
| 502 | extern uint32_t timer_irq; |
---|
| 503 | static inline void bios_timer_init(void) |
---|
| 504 | { |
---|
| 505 | unsigned long next; |
---|
| 506 | uint32_t *hook = (uint32_t *)BIOS_timer_hook; |
---|
| 507 | |
---|
| 508 | next = *hook; |
---|
| 509 | BIOS_timer_next = next; |
---|
| 510 | *hook = (uint32_t)&timer_irq; |
---|
| 511 | } |
---|
| 512 | |
---|
| 513 | extern uint16_t *bios_free_mem; |
---|
| 514 | |
---|
| 515 | struct e820_entry { |
---|
| 516 | uint64_t start; |
---|
| 517 | uint64_t len; |
---|
| 518 | uint32_t type; |
---|
| 519 | }; |
---|
| 520 | |
---|
| 521 | static int bios_scan_memory(scan_memory_callback_t callback, void *data) |
---|
| 522 | { |
---|
| 523 | static com32sys_t ireg; |
---|
| 524 | com32sys_t oreg; |
---|
| 525 | struct e820_entry *e820buf; |
---|
| 526 | uint64_t start, len, maxlen; |
---|
| 527 | int memfound = 0; |
---|
| 528 | int rv; |
---|
| 529 | addr_t dosmem; |
---|
| 530 | const addr_t bios_data = 0x510; /* Amount to reserve for BIOS data */ |
---|
| 531 | |
---|
| 532 | /* Use INT 12h to get DOS memory */ |
---|
| 533 | __intcall(0x12, &__com32_zero_regs, &oreg); |
---|
| 534 | dosmem = oreg.eax.w[0] << 10; |
---|
| 535 | if (dosmem < 32 * 1024 || dosmem > 640 * 1024) { |
---|
| 536 | /* INT 12h reports nonsense... now what? */ |
---|
| 537 | uint16_t ebda_seg = *(uint16_t *) 0x40e; |
---|
| 538 | if (ebda_seg >= 0x8000 && ebda_seg < 0xa000) |
---|
| 539 | dosmem = ebda_seg << 4; |
---|
| 540 | else |
---|
| 541 | dosmem = 640 * 1024; /* Hope for the best... */ |
---|
| 542 | } |
---|
| 543 | rv = callback(data, bios_data, dosmem - bios_data, SMT_FREE); |
---|
| 544 | if (rv) |
---|
| 545 | return rv; |
---|
| 546 | |
---|
| 547 | /* First try INT 15h AX=E820h */ |
---|
| 548 | e820buf = lzalloc(sizeof *e820buf); |
---|
| 549 | if (!e820buf) |
---|
| 550 | return -1; |
---|
| 551 | |
---|
| 552 | memset(&ireg, 0, sizeof ireg); |
---|
| 553 | ireg.eax.l = 0xe820; |
---|
| 554 | ireg.edx.l = 0x534d4150; |
---|
| 555 | ireg.ebx.l = 0; |
---|
| 556 | ireg.ecx.l = sizeof(*e820buf); |
---|
| 557 | ireg.es = SEG(e820buf); |
---|
| 558 | ireg.edi.w[0] = OFFS(e820buf); |
---|
| 559 | |
---|
| 560 | do { |
---|
| 561 | __intcall(0x15, &ireg, &oreg); |
---|
| 562 | |
---|
| 563 | if ((oreg.eflags.l & EFLAGS_CF) || |
---|
| 564 | (oreg.eax.l != 0x534d4150) || (oreg.ecx.l < 20)) |
---|
| 565 | break; |
---|
| 566 | |
---|
| 567 | start = e820buf->start; |
---|
| 568 | len = e820buf->len; |
---|
| 569 | |
---|
| 570 | if (start < 0x100000000ULL) { |
---|
| 571 | /* Don't rely on E820 being valid for low memory. Doing so |
---|
| 572 | could mean stuff like overwriting the PXE stack even when |
---|
| 573 | using "keeppxe", etc. */ |
---|
| 574 | if (start < 0x100000ULL) { |
---|
| 575 | if (len > 0x100000ULL - start) |
---|
| 576 | len -= 0x100000ULL - start; |
---|
| 577 | else |
---|
| 578 | len = 0; |
---|
| 579 | start = 0x100000ULL; |
---|
| 580 | } |
---|
| 581 | |
---|
| 582 | maxlen = 0x100000000ULL - start; |
---|
| 583 | if (len > maxlen) |
---|
| 584 | len = maxlen; |
---|
| 585 | |
---|
| 586 | if (len) { |
---|
| 587 | enum syslinux_memmap_types type; |
---|
| 588 | |
---|
| 589 | type = e820buf->type == 1 ? SMT_FREE : SMT_RESERVED; |
---|
| 590 | rv = callback(data, (addr_t) start, (addr_t) len, type); |
---|
| 591 | if (rv) |
---|
| 592 | return rv; |
---|
| 593 | memfound = 1; |
---|
| 594 | } |
---|
| 595 | } |
---|
| 596 | |
---|
| 597 | ireg.ebx.l = oreg.ebx.l; |
---|
| 598 | } while (oreg.ebx.l); |
---|
| 599 | |
---|
| 600 | lfree(e820buf); |
---|
| 601 | |
---|
| 602 | if (memfound) |
---|
| 603 | return 0; |
---|
| 604 | |
---|
| 605 | /* Next try INT 15h AX=E801h */ |
---|
| 606 | memset(&ireg, 0, sizeof ireg); |
---|
| 607 | ireg.eax.w[0] = 0xe801; |
---|
| 608 | __intcall(0x15, &ireg, &oreg); |
---|
| 609 | |
---|
| 610 | if (!(oreg.eflags.l & EFLAGS_CF) && oreg.ecx.w[0]) { |
---|
| 611 | rv = callback(data, (addr_t) 1 << 20, oreg.ecx.w[0] << 10, SMT_FREE); |
---|
| 612 | if (rv) |
---|
| 613 | return rv; |
---|
| 614 | |
---|
| 615 | if (oreg.edx.w[0]) { |
---|
| 616 | rv = callback(data, (addr_t) 16 << 20, |
---|
| 617 | oreg.edx.w[0] << 16, SMT_FREE); |
---|
| 618 | if (rv) |
---|
| 619 | return rv; |
---|
| 620 | } |
---|
| 621 | |
---|
| 622 | return 0; |
---|
| 623 | } |
---|
| 624 | |
---|
| 625 | /* Finally try INT 15h AH=88h */ |
---|
| 626 | memset(&ireg, 0, sizeof ireg); |
---|
| 627 | ireg.eax.w[0] = 0x8800; |
---|
| 628 | __intcall(0x15, &ireg, &oreg); |
---|
| 629 | if (!(oreg.eflags.l & EFLAGS_CF) && oreg.eax.w[0]) { |
---|
| 630 | rv = callback(data, (addr_t) 1 << 20, oreg.ecx.w[0] << 10, SMT_FREE); |
---|
| 631 | if (rv) |
---|
| 632 | return rv; |
---|
| 633 | } |
---|
| 634 | |
---|
| 635 | return 0; |
---|
| 636 | } |
---|
| 637 | |
---|
| 638 | static struct syslinux_memscan bios_memscan = { |
---|
| 639 | .func = bios_scan_memory, |
---|
| 640 | }; |
---|
| 641 | |
---|
| 642 | void bios_init(void) |
---|
| 643 | { |
---|
| 644 | int i; |
---|
| 645 | |
---|
| 646 | /* Initialize timer */ |
---|
| 647 | bios_timer_init(); |
---|
| 648 | |
---|
| 649 | for (i = 0; i < 256; i++) |
---|
| 650 | KbdMap[i] = i; |
---|
| 651 | |
---|
| 652 | bios_adjust_screen(); |
---|
| 653 | |
---|
| 654 | /* Init the memory subsystem */ |
---|
| 655 | bios_free_mem = (uint16_t *)0x413; |
---|
| 656 | syslinux_memscan_add(&bios_memscan); |
---|
| 657 | mem_init(); |
---|
| 658 | |
---|
| 659 | dprintf("%s%s", syslinux_banner, copyright_str); |
---|
| 660 | |
---|
| 661 | /* CPU-dependent initialization and related checks. */ |
---|
| 662 | check_escapes(); |
---|
| 663 | |
---|
| 664 | /* |
---|
| 665 | * Scan the DMI tables for interesting information. |
---|
| 666 | */ |
---|
| 667 | dmi_init(); |
---|
| 668 | } |
---|
| 669 | |
---|
| 670 | extern void bios_timer_cleanup(void); |
---|
| 671 | |
---|
| 672 | extern uint32_t OrigFDCTabPtr; |
---|
| 673 | |
---|
| 674 | static void bios_cleanup_hardware(void) |
---|
| 675 | { |
---|
| 676 | /* Restore the original pointer to the floppy descriptor table */ |
---|
| 677 | if (OrigFDCTabPtr) |
---|
| 678 | *((uint32_t *)(4 * 0x1e)) = OrigFDCTabPtr; |
---|
| 679 | |
---|
| 680 | /* |
---|
| 681 | * Linux wants the floppy motor shut off before starting the |
---|
| 682 | * kernel, at least bootsect.S seems to imply so. If we don't |
---|
| 683 | * load the floppy driver, this is *definitely* so! |
---|
| 684 | */ |
---|
| 685 | __intcall(0x13, &zero_regs, NULL); |
---|
| 686 | |
---|
| 687 | call16(bios_timer_cleanup, &zero_regs, NULL); |
---|
| 688 | |
---|
| 689 | /* If we enabled serial port interrupts, clean them up now */ |
---|
| 690 | sirq_cleanup(); |
---|
| 691 | } |
---|
| 692 | |
---|
| 693 | extern void *bios_malloc(size_t, enum heap, size_t); |
---|
| 694 | extern void *bios_realloc(void *, size_t); |
---|
| 695 | extern void bios_free(void *); |
---|
| 696 | |
---|
| 697 | struct mem_ops bios_mem_ops = { |
---|
| 698 | .malloc = bios_malloc, |
---|
| 699 | .realloc = bios_realloc, |
---|
| 700 | .free = bios_free, |
---|
| 701 | }; |
---|
| 702 | |
---|
| 703 | struct firmware bios_fw = { |
---|
| 704 | .init = bios_init, |
---|
| 705 | .adjust_screen = bios_adjust_screen, |
---|
| 706 | .cleanup = bios_cleanup_hardware, |
---|
| 707 | .disk_init = bios_disk_init, |
---|
| 708 | .o_ops = &bios_output_ops, |
---|
| 709 | .i_ops = &bios_input_ops, |
---|
| 710 | .get_serial_console_info = bios_get_serial_console_info, |
---|
| 711 | .adv_ops = &bios_adv_ops, |
---|
| 712 | .vesa = &bios_vesa_ops, |
---|
| 713 | .mem = &bios_mem_ops, |
---|
| 714 | }; |
---|
| 715 | |
---|
| 716 | void syslinux_register_bios(void) |
---|
| 717 | { |
---|
| 718 | firmware = &bios_fw; |
---|
| 719 | } |
---|