7 #include <syslinux/memscan.h>
8 #include <syslinux/firmware.h>
9 #include <syslinux/video.h>
11 #include <sys/vesa/vesa.h>
12 #include <sys/vesa/video.h>
13 #include <sys/vesa/debug.h>
17 __export
struct firmware
*firmware
= NULL
;
19 extern struct ansi_ops bios_ansi_ops
;
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)
26 static void bios_text_mode(void)
28 syslinux_force_text_mode();
31 static void bios_get_mode(int *cols
, int *rows
)
33 *rows
= BIOS_ROWS
? BIOS_ROWS
+ 1 : 25;
37 static uint16_t cursor_type
; /* Saved cursor pattern */
39 static void bios_get_cursor(uint8_t *x
, uint8_t *y
)
41 com32sys_t ireg
, oreg
;
43 memset(&ireg
, 0, sizeof(ireg
));
46 ireg
.ebx
.b
[1] = BIOS_PAGE
;
47 __intcall(0x10, &ireg
, &oreg
);
48 cursor_type
= oreg
.ecx
.w
[0];
53 static void bios_erase(int x0
, int y0
, int x1
, int y1
, uint8_t attribute
)
55 static com32sys_t ireg
;
56 memset(&ireg
, 0, sizeof(ireg
));
58 ireg
.eax
.w
[0] = 0x0600; /* Clear window */
59 ireg
.ebx
.b
[1] = attribute
;
64 __intcall(0x10, &ireg
, NULL
);
67 static void bios_showcursor(const struct term_state
*st
)
69 static com32sys_t ireg
;
70 uint16_t cursor
= st
->cursor
? cursor_type
: 0x2020;
72 memset(&ireg
, 0, sizeof(ireg
));
75 ireg
.ecx
.w
[0] = cursor
;
76 __intcall(0x10, &ireg
, NULL
);
79 static void bios_set_cursor(int x
, int y
, bool visible
)
81 const int page
= BIOS_PAGE
;
82 struct curxy xy
= BIOS_CURXY
[page
];
83 static com32sys_t ireg
;
85 memset(&ireg
, 0, sizeof(ireg
));
89 if (xy
.x
!= x
|| xy
.y
!= y
) {
94 __intcall(0x10, &ireg
, NULL
);
98 static void bios_write_char(uint8_t ch
, uint8_t attribute
)
100 static com32sys_t ireg
;
102 memset(&ireg
, 0, sizeof(ireg
));
104 ireg
.eax
.b
[1] = 0x09;
106 ireg
.ebx
.b
[1] = BIOS_PAGE
;
107 ireg
.ebx
.b
[0] = attribute
;
109 __intcall(0x10, &ireg
, NULL
);
112 static void bios_scroll_up(uint8_t cols
, uint8_t rows
, uint8_t attribute
)
114 static com32sys_t ireg
;
116 memset(&ireg
, 0, sizeof(ireg
));
118 ireg
.eax
.w
[0] = 0x0601;
119 ireg
.ebx
.b
[1] = attribute
;
121 ireg
.edx
.b
[1] = rows
;
122 ireg
.edx
.b
[0] = cols
;
123 __intcall(0x10, &ireg
, NULL
); /* Scroll */
126 static void bios_beep(void)
128 static com32sys_t ireg
;
130 memset(&ireg
, 0, sizeof(ireg
));
132 ireg
.eax
.w
[0] = 0x0e07;
133 ireg
.ebx
.b
[1] = BIOS_PAGE
;
134 __intcall(0x10, &ireg
, NULL
);
137 struct output_ops bios_output_ops
= {
139 .write_char
= bios_write_char
,
140 .showcursor
= bios_showcursor
,
141 .set_cursor
= bios_set_cursor
,
142 .scroll_up
= bios_scroll_up
,
144 .get_mode
= bios_get_mode
,
145 .text_mode
= bios_text_mode
,
146 .get_cursor
= bios_get_cursor
,
149 extern char bios_getchar(char *);
150 extern int bios_pollchar(void);
152 struct input_ops bios_input_ops
= {
153 .getchar
= bios_getchar
,
154 .pollchar
= bios_pollchar
,
157 static void bios_get_serial_console_info(uint16_t *iobase
, uint16_t *divisor
,
160 *iobase
= SerialPort
;
161 *divisor
= BaudDivisor
;
163 *flowctl
= FlowOutput
| FlowInput
| (FlowIgnore
<< 4);
166 *flowctl
|= (0x80 << 8);
169 void bios_adv_init(void)
171 static com32sys_t reg
;
173 memset(®
, 0, sizeof(reg
));
174 call16(adv_init
, ®
, NULL
);
177 int bios_adv_write(void)
179 static com32sys_t reg
;
181 memset(®
, 0, sizeof(reg
));
182 call16(adv_write
, ®
, ®
);
183 return (reg
.eflags
.l
& EFLAGS_CF
) ? -1 : 0;
186 struct adv_ops bios_adv_ops
= {
187 .init
= bios_adv_init
,
188 .write
= bios_adv_write
,
192 static int __constfunc
is_power_of_2(unsigned int x
)
194 return x
&& !(x
& (x
- 1));
197 static int vesacon_paged_mode_ok(const struct vesa_mode_info
*mi
)
201 if (!is_power_of_2(mi
->win_size
) ||
202 !is_power_of_2(mi
->win_grain
) || mi
->win_grain
> mi
->win_size
)
203 return 0; /* Impossible... */
205 for (i
= 0; i
< 2; i
++) {
206 if ((mi
->win_attr
[i
] & 0x05) == 0x05 && mi
->win_seg
[i
])
207 return 1; /* Usable window */
210 return 0; /* Nope... */
213 static int bios_vesacon_set_mode(struct vesa_info
*vesa_info
, int *px
, int *py
,
214 enum vesa_pixel_format
*bestpxf
)
217 uint16_t mode
, bestmode
, *mode_ptr
;
218 struct vesa_info
*vi
;
219 struct vesa_general_info
*gi
;
220 struct vesa_mode_info
*mi
;
221 enum vesa_pixel_format pxf
;
222 int x
= *px
, y
= *py
;
225 /* Allocate space in the bounce buffer for these structures */
226 vi
= lzalloc(sizeof *vi
);
228 err
= 10; /* Out of memory */
234 memset(&rm
, 0, sizeof rm
);
236 gi
->signature
= VBE2_MAGIC
; /* Get VBE2 extended data */
237 rm
.eax
.w
[0] = 0x4F00; /* Get SVGA general information */
238 rm
.edi
.w
[0] = OFFS(gi
);
240 __intcall(0x10, &rm
, &rm
);
242 if (rm
.eax
.w
[0] != 0x004F) {
243 err
= 1; /* Function call failed */
246 if (gi
->signature
!= VESA_MAGIC
) {
247 err
= 2; /* No magic */
250 if (gi
->version
< 0x0102) {
251 err
= 3; /* VESA 1.2+ required */
255 /* Copy general info */
256 memcpy(&vesa_info
->gi
, gi
, sizeof *gi
);
258 /* Search for the proper mode with a suitable color and memory model... */
260 mode_ptr
= GET_PTR(gi
->video_mode_ptr
);
264 while ((mode
= *mode_ptr
++) != 0xFFFF) {
265 mode
&= 0x1FF; /* The rest are attributes of sorts */
267 debug("Found mode: 0x%04x\r\n", mode
);
269 memset(&rm
, 0, sizeof rm
);
270 memset(mi
, 0, sizeof *mi
);
271 rm
.eax
.w
[0] = 0x4F01; /* Get SVGA mode information */
273 rm
.edi
.w
[0] = OFFS(mi
);
275 __intcall(0x10, &rm
, &rm
);
277 /* Must be a supported mode */
278 if (rm
.eax
.w
[0] != 0x004f)
282 ("mode_attr 0x%04x, h_res = %4d, v_res = %4d, bpp = %2d, layout = %d (%d,%d,%d)\r\n",
283 mi
->mode_attr
, mi
->h_res
, mi
->v_res
, mi
->bpp
, mi
->memory_layout
,
284 mi
->rpos
, mi
->gpos
, mi
->bpos
);
286 /* Must be an LFB color graphics mode supported by the hardware.
291 1 - mode information available (mandatory in VBE 1.2+)
292 0 - mode supported by hardware
294 if ((mi
->mode_attr
& 0x001b) != 0x001b)
297 /* Must be the chosen size */
298 if (mi
->h_res
!= x
|| mi
->v_res
!= y
)
301 /* We don't support multibank (interlaced memory) modes */
303 * Note: The Bochs VESA BIOS (vbe.c 1.58 2006/08/19) violates the
304 * specification which states that banks == 1 for unbanked modes;
305 * fortunately it does report bank_size == 0 for those.
307 if (mi
->banks
> 1 && mi
->bank_size
) {
308 debug("bad: banks = %d, banksize = %d, pages = %d\r\n",
309 mi
->banks
, mi
->bank_size
, mi
->image_pages
);
313 /* Must be either a flat-framebuffer mode, or be an acceptable
315 if (!(mi
->mode_attr
& 0x0080) && !vesacon_paged_mode_ok(mi
)) {
316 debug("bad: invalid paged mode\r\n");
320 /* Must either be a packed-pixel mode or a direct color mode
321 (depending on VESA version ); must be a supported pixel format */
322 pxf
= PXF_NONE
; /* Not usable */
325 (mi
->memory_layout
== 4 ||
326 (mi
->memory_layout
== 6 && mi
->rpos
== 16 && mi
->gpos
== 8 &&
329 else if (mi
->bpp
== 24 &&
330 (mi
->memory_layout
== 4 ||
331 (mi
->memory_layout
== 6 && mi
->rpos
== 16 && mi
->gpos
== 8 &&
334 else if (mi
->bpp
== 16 &&
335 (mi
->memory_layout
== 4 ||
336 (mi
->memory_layout
== 6 && mi
->rpos
== 11 && mi
->gpos
== 5 &&
338 pxf
= PXF_LE_RGB16_565
;
339 else if (mi
->bpp
== 15 &&
340 (mi
->memory_layout
== 4 ||
341 (mi
->memory_layout
== 6 && mi
->rpos
== 10 && mi
->gpos
== 5 &&
343 pxf
= PXF_LE_RGB15_555
;
345 if (pxf
< *bestpxf
) {
346 debug("Best mode so far, pxf = %d\r\n", pxf
);
348 /* Best mode so far... */
353 memcpy(&vesa_info
->mi
, mi
, sizeof *mi
);
357 if (*bestpxf
== PXF_NONE
) {
358 err
= 4; /* No mode found */
365 memset(&rm
, 0, sizeof rm
);
366 /* Now set video mode */
367 rm
.eax
.w
[0] = 0x4F02; /* Set SVGA video mode */
368 if (mi
->mode_attr
& 0x0080)
369 mode
|= 0x4000; /* Request linear framebuffer if supported */
371 __intcall(0x10, &rm
, &rm
);
372 if (rm
.eax
.w
[0] != 0x004F) {
373 err
= 9; /* Failed to set mode */
384 static void set_window_pos(struct win_info
*wi
, size_t win_pos
)
386 static com32sys_t ireg
;
388 wi
->win_pos
= win_pos
;
391 return; /* This should never happen... */
393 memset(&ireg
, 0, sizeof ireg
);
394 ireg
.eax
.w
[0] = 0x4F05;
395 ireg
.ebx
.b
[0] = wi
->win_num
;
396 ireg
.edx
.w
[0] = win_pos
>> wi
->win_gshift
;
398 __intcall(0x10, &ireg
, NULL
);
401 static void bios_vesacon_screencpy(size_t dst
, const uint32_t * src
,
402 size_t bytes
, struct win_info
*wi
)
404 size_t win_pos
, win_off
;
405 size_t win_size
= wi
->win_size
;
406 size_t omask
= win_size
- 1;
407 char *win_base
= wi
->win_base
;
408 const char *s
= (const char *)src
;
412 win_off
= dst
& omask
;
413 win_pos
= dst
& ~omask
;
415 if (__unlikely(win_pos
!= wi
->win_pos
))
416 set_window_pos(wi
, win_pos
);
418 l
= min(bytes
, win_size
- win_off
);
419 memcpy(win_base
+ win_off
, s
, l
);
427 static int bios_font_query(uint8_t **font
)
431 /* Get BIOS 8x16 font */
433 memset(&rm
, 0, sizeof rm
);
435 rm
.eax
.w
[0] = 0x1130; /* Get Font Information */
436 rm
.ebx
.w
[0] = 0x0600; /* Get 8x16 ROM font */
437 __intcall(0x10, &rm
, &rm
);
438 *font
= MK_PTR(rm
.es
, rm
.ebp
.w
[0]);
443 struct vesa_ops bios_vesa_ops
= {
444 .set_mode
= bios_vesacon_set_mode
,
445 .screencpy
= bios_vesacon_screencpy
,
446 .font_query
= bios_font_query
,
449 static uint32_t min_lowmem_heap
= 65536;
450 extern char __lowmem_heap
[];
451 uint8_t KbdFlags
; /* Check for keyboard escapes */
452 __export
uint8_t KbdMap
[256]; /* Keyboard map */
454 __export
uint16_t PXERetry
;
456 static inline void check_escapes(void)
458 com32sys_t ireg
, oreg
;
460 memset(&ireg
, 0, sizeof ireg
);
461 ireg
.eax
.b
[1] = 0x02; /* Check keyboard flags */
462 __intcall(0x16, &ireg
, &oreg
);
464 KbdFlags
= oreg
.eax
.b
[0];
466 /* Ctrl->skip 386 check */
467 if (oreg
.eax
.b
[0] & 0x04) {
469 * Now check that there is sufficient low (DOS) memory
471 * NOTE: Linux doesn't use all of real_mode_seg, but we use
472 * the same segment for COMBOOT images, which can use all 64K.
476 __intcall(0x12, &ireg
, &oreg
);
478 mem
= ((uint32_t)__lowmem_heap
) + min_lowmem_heap
+ 1023;
481 if (mem
< oreg
.eax
.w
[0]) {
484 snprintf(buf
, sizeof(buf
),
485 "It appears your computer has only "
486 "%dK of low (\"DOS\") RAM.\n"
487 "This version of Syslinux needs "
489 "If you get this\nmessage in error, "
490 "hold down the Ctrl key while booting, "
491 "and I\nwill take your word for it.\n",
499 extern uint32_t BIOS_timer_next
;
500 extern uint32_t timer_irq
;
501 static inline void bios_timer_init(void)
504 uint32_t *hook
= (uint32_t *)BIOS_timer_hook
;
507 BIOS_timer_next
= next
;
508 *hook
= (uint32_t)&timer_irq
;
511 extern uint16_t *bios_free_mem
;
519 static int bios_scan_memory(scan_memory_callback_t callback
, void *data
)
521 static com32sys_t ireg
;
523 struct e820_entry
*e820buf
;
524 uint64_t start
, len
, maxlen
;
528 const addr_t bios_data
= 0x510; /* Amount to reserve for BIOS data */
530 /* Use INT 12h to get DOS memory */
531 __intcall(0x12, &__com32_zero_regs
, &oreg
);
532 dosmem
= oreg
.eax
.w
[0] << 10;
533 if (dosmem
< 32 * 1024 || dosmem
> 640 * 1024) {
534 /* INT 12h reports nonsense... now what? */
535 uint16_t ebda_seg
= *(uint16_t *) 0x40e;
536 if (ebda_seg
>= 0x8000 && ebda_seg
< 0xa000)
537 dosmem
= ebda_seg
<< 4;
539 dosmem
= 640 * 1024; /* Hope for the best... */
541 rv
= callback(data
, bios_data
, dosmem
- bios_data
, SMT_FREE
);
545 /* First try INT 15h AX=E820h */
546 e820buf
= lzalloc(sizeof *e820buf
);
550 memset(&ireg
, 0, sizeof ireg
);
552 ireg
.edx
.l
= 0x534d4150;
554 ireg
.ecx
.l
= sizeof(*e820buf
);
555 ireg
.es
= SEG(e820buf
);
556 ireg
.edi
.w
[0] = OFFS(e820buf
);
559 __intcall(0x15, &ireg
, &oreg
);
561 if ((oreg
.eflags
.l
& EFLAGS_CF
) ||
562 (oreg
.eax
.l
!= 0x534d4150) || (oreg
.ecx
.l
< 20))
565 start
= e820buf
->start
;
568 if (start
< 0x100000000ULL
) {
569 /* Don't rely on E820 being valid for low memory. Doing so
570 could mean stuff like overwriting the PXE stack even when
571 using "keeppxe", etc. */
572 if (start
< 0x100000ULL
) {
573 if (len
> 0x100000ULL
- start
)
574 len
-= 0x100000ULL
- start
;
580 maxlen
= 0x100000000ULL
- start
;
585 enum syslinux_memmap_types type
;
587 type
= e820buf
->type
== 1 ? SMT_FREE
: SMT_RESERVED
;
588 rv
= callback(data
, (addr_t
) start
, (addr_t
) len
, type
);
595 ireg
.ebx
.l
= oreg
.ebx
.l
;
596 } while (oreg
.ebx
.l
);
603 /* Next try INT 15h AX=E801h */
604 memset(&ireg
, 0, sizeof ireg
);
605 ireg
.eax
.w
[0] = 0xe801;
606 __intcall(0x15, &ireg
, &oreg
);
608 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.ecx
.w
[0]) {
609 rv
= callback(data
, (addr_t
) 1 << 20, oreg
.ecx
.w
[0] << 10, SMT_FREE
);
614 rv
= callback(data
, (addr_t
) 16 << 20,
615 oreg
.edx
.w
[0] << 16, SMT_FREE
);
623 /* Finally try INT 15h AH=88h */
624 memset(&ireg
, 0, sizeof ireg
);
625 ireg
.eax
.w
[0] = 0x8800;
626 __intcall(0x15, &ireg
, &oreg
);
627 if (!(oreg
.eflags
.l
& EFLAGS_CF
) && oreg
.eax
.w
[0]) {
628 rv
= callback(data
, (addr_t
) 1 << 20, oreg
.ecx
.w
[0] << 10, SMT_FREE
);
636 static struct syslinux_memscan bios_memscan
= {
637 .func
= bios_scan_memory
,
644 /* Initialize timer */
647 for (i
= 0; i
< 256; i
++)
650 bios_adjust_screen();
652 /* Init the memory subsystem */
653 bios_free_mem
= (uint16_t *)0x413;
654 syslinux_memscan_add(&bios_memscan
);
657 dprintf("%s%s", syslinux_banner
, copyright_str
);
659 /* CPU-dependent initialization and related checks. */
663 * Scan the DMI tables for interesting information.
668 extern void comboot_cleanup_api(void);
669 extern void bios_timer_cleanup(void);
671 extern uint32_t OrigFDCTabPtr
;
673 static void bios_cleanup_hardware(void)
675 /* Restore the original pointer to the floppy descriptor table */
677 *((uint32_t *)(4 * 0x1e)) = OrigFDCTabPtr
;
680 * Linux wants the floppy motor shut off before starting the
681 * kernel, at least bootsect.S seems to imply so. If we don't
682 * load the floppy driver, this is *definitely* so!
684 __intcall(0x13, &zero_regs
, NULL
);
686 call16(comboot_cleanup_api
, &zero_regs
, NULL
);
687 call16(bios_timer_cleanup
, &zero_regs
, NULL
);
689 /* If we enabled serial port interrupts, clean them up now */
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 *);
697 struct mem_ops bios_mem_ops
= {
698 .malloc
= bios_malloc
,
699 .realloc
= bios_realloc
,
703 struct firmware bios_fw
= {
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
,
716 void syslinux_register_bios(void)