7 #include <syslinux/memscan.h>
8 #include <syslinux/firmware.h>
10 #include <sys/vesa/vesa.h>
11 #include <sys/vesa/video.h>
12 #include <sys/vesa/debug.h>
15 struct firmware
*firmware
= NULL
;
17 extern struct ansi_ops bios_ansi_ops
;
19 #define BIOS_CURXY ((struct curxy *)0x450) /* Array for each page */
20 #define BIOS_ROWS (*(uint8_t *)0x484) /* Minus one; if zero use 24 (= 25 lines) */
21 #define BIOS_COLS (*(uint16_t *)0x44A)
22 #define BIOS_PAGE (*(uint8_t *)0x462)
24 static void bios_set_mode(uint16_t mode
)
26 syslinux_force_text_mode();
29 static void bios_get_mode(int *cols
, int *rows
)
31 *rows
= BIOS_ROWS
? BIOS_ROWS
+ 1 : 25;
35 static uint16_t cursor_type
; /* Saved cursor pattern */
37 static void bios_get_cursor(int *x
, int *y
)
39 com32sys_t ireg
, oreg
;
41 memset(&ireg
, 0, sizeof(ireg
));
44 ireg
.ebx
.b
[1] = BIOS_PAGE
;
45 __intcall(0x10, &ireg
, &oreg
);
46 cursor_type
= oreg
.ecx
.w
[0];
51 static void bios_erase(int x0
, int y0
, int x1
, int y1
, uint8_t attribute
)
53 static com32sys_t ireg
;
55 ireg
.eax
.w
[0] = 0x0600; /* Clear window */
56 ireg
.ebx
.b
[1] = attribute
;
61 __intcall(0x10, &ireg
, NULL
);
64 static void bios_showcursor(const struct term_state
*st
)
66 static com32sys_t ireg
;
67 uint16_t cursor
= st
->cursor
? cursor_type
: 0x2020;
70 ireg
.ecx
.w
[0] = cursor
;
71 __intcall(0x10, &ireg
, NULL
);
74 static void bios_set_cursor(int x
, int y
, bool visible
)
76 const int page
= BIOS_PAGE
;
77 struct curxy xy
= BIOS_CURXY
[page
];
78 static com32sys_t ireg
;
82 if (xy
.x
!= x
|| xy
.y
!= y
) {
87 __intcall(0x10, &ireg
, NULL
);
91 static void bios_write_char(uint8_t ch
, uint8_t attribute
)
93 static com32sys_t ireg
;
97 ireg
.ebx
.b
[1] = BIOS_PAGE
;
98 ireg
.ebx
.b
[0] = attribute
;
100 __intcall(0x10, &ireg
, NULL
);
103 static void bios_scroll_up(uint8_t cols
, uint8_t rows
, uint8_t attribute
)
105 static com32sys_t ireg
;
107 ireg
.eax
.w
[0] = 0x0601;
108 ireg
.ebx
.b
[1] = attribute
;
110 ireg
.edx
.b
[1] = rows
;
111 ireg
.edx
.b
[0] = cols
;
112 __intcall(0x10, &ireg
, NULL
); /* Scroll */
115 static void bios_beep(void)
117 static com32sys_t ireg
;
119 ireg
.eax
.w
[0] = 0x0e07;
120 ireg
.ebx
.b
[1] = BIOS_PAGE
;
121 __intcall(0x10, &ireg
, NULL
);
124 struct output_ops bios_output_ops
= {
126 .write_char
= bios_write_char
,
127 .showcursor
= bios_showcursor
,
128 .set_cursor
= bios_set_cursor
,
129 .scroll_up
= bios_scroll_up
,
131 .get_mode
= bios_get_mode
,
132 .set_mode
= bios_set_mode
,
133 .get_cursor
= bios_get_cursor
,
136 extern char bios_getchar(char *);
137 extern int bios_pollchar(void);
139 struct input_ops bios_input_ops
= {
140 .getchar
= bios_getchar
,
141 .pollchar
= bios_pollchar
,
144 static const char *syslinux_ipappend_string_list
[32];
145 bool bios_ipappend_strings(char **list
, int *count
)
147 static com32sys_t reg
;
150 reg
.eax
.w
[0] = 0x000f;
151 __intcall(0x22, ®
, ®
);
153 if (reg
.eflags
.l
& EFLAGS_CF
)
156 for (i
= 0; i
< reg
.ecx
.w
[0]; i
++) {
157 syslinux_ipappend_string_list
[i
] =
159 *(uint16_t *) MK_PTR(reg
.es
, reg
.ebx
.w
[0] + i
* 2));
162 *list
= syslinux_ipappend_string_list
;
163 *count
= reg
.ecx
.w
[0];
168 static void bios_get_serial_console_info(uint16_t *iobase
, uint16_t *divisor
,
171 *iobase
= SerialPort
;
172 *divisor
= BaudDivisor
;
174 *flowctl
= FlowOutput
| FlowInput
| (FlowIgnore
<< 4);
177 *flowctl
|= (0x80 << 8);
180 void *__syslinux_adv_ptr
;
181 size_t __syslinux_adv_size
;
183 void bios_adv_init(void)
185 static com32sys_t reg
;
187 reg
.eax
.w
[0] = 0x0025;
188 __intcall(0x22, ®
, ®
);
190 reg
.eax
.w
[0] = 0x001c;
191 __intcall(0x22, ®
, ®
);
192 __syslinux_adv_ptr
= MK_PTR(reg
.es
, reg
.ebx
.w
[0]);
193 __syslinux_adv_size
= reg
.ecx
.w
[0];
196 int bios_adv_write(void)
198 static com32sys_t reg
;
200 reg
.eax
.w
[0] = 0x001d;
201 __intcall(0x22, ®
, ®
);
202 return (reg
.eflags
.l
& EFLAGS_CF
) ? -1 : 0;
205 struct adv_ops bios_adv_ops
= {
206 .init
= bios_adv_init
,
207 .write
= bios_adv_write
,
211 static int __constfunc
is_power_of_2(unsigned int x
)
213 return x
&& !(x
& (x
- 1));
216 static int vesacon_paged_mode_ok(const struct vesa_mode_info
*mi
)
220 if (!is_power_of_2(mi
->win_size
) ||
221 !is_power_of_2(mi
->win_grain
) || mi
->win_grain
> mi
->win_size
)
222 return 0; /* Impossible... */
224 for (i
= 0; i
< 2; i
++) {
225 if ((mi
->win_attr
[i
] & 0x05) == 0x05 && mi
->win_seg
[i
])
226 return 1; /* Usable window */
229 return 0; /* Nope... */
232 static int bios_vesacon_set_mode(struct vesa_info
*vesa_info
, int *px
, int *py
,
233 enum vesa_pixel_format
*bestpxf
)
236 uint16_t mode
, bestmode
, *mode_ptr
;
237 struct vesa_info
*vi
;
238 struct vesa_general_info
*gi
;
239 struct vesa_mode_info
*mi
;
240 enum vesa_pixel_format pxf
;
241 int x
= *px
, y
= *py
;
244 /* Allocate space in the bounce buffer for these structures */
245 vi
= lzalloc(sizeof *vi
);
247 err
= 10; /* Out of memory */
253 memset(&rm
, 0, sizeof rm
);
255 gi
->signature
= VBE2_MAGIC
; /* Get VBE2 extended data */
256 rm
.eax
.w
[0] = 0x4F00; /* Get SVGA general information */
257 rm
.edi
.w
[0] = OFFS(gi
);
259 __intcall(0x10, &rm
, &rm
);
261 if (rm
.eax
.w
[0] != 0x004F) {
262 err
= 1; /* Function call failed */
265 if (gi
->signature
!= VESA_MAGIC
) {
266 err
= 2; /* No magic */
269 if (gi
->version
< 0x0102) {
270 err
= 3; /* VESA 1.2+ required */
274 /* Copy general info */
275 memcpy(&vesa_info
->gi
, gi
, sizeof *gi
);
277 /* Search for the proper mode with a suitable color and memory model... */
279 mode_ptr
= GET_PTR(gi
->video_mode_ptr
);
283 while ((mode
= *mode_ptr
++) != 0xFFFF) {
284 mode
&= 0x1FF; /* The rest are attributes of sorts */
286 debug("Found mode: 0x%04x\r\n", mode
);
288 memset(mi
, 0, sizeof *mi
);
289 rm
.eax
.w
[0] = 0x4F01; /* Get SVGA mode information */
291 rm
.edi
.w
[0] = OFFS(mi
);
293 __intcall(0x10, &rm
, &rm
);
295 /* Must be a supported mode */
296 if (rm
.eax
.w
[0] != 0x004f)
300 ("mode_attr 0x%04x, h_res = %4d, v_res = %4d, bpp = %2d, layout = %d (%d,%d,%d)\r\n",
301 mi
->mode_attr
, mi
->h_res
, mi
->v_res
, mi
->bpp
, mi
->memory_layout
,
302 mi
->rpos
, mi
->gpos
, mi
->bpos
);
304 /* Must be an LFB color graphics mode supported by the hardware.
309 1 - mode information available (mandatory in VBE 1.2+)
310 0 - mode supported by hardware
312 if ((mi
->mode_attr
& 0x001b) != 0x001b)
315 /* Must be the chosen size */
316 if (mi
->h_res
!= x
|| mi
->v_res
!= y
)
319 /* We don't support multibank (interlaced memory) modes */
321 * Note: The Bochs VESA BIOS (vbe.c 1.58 2006/08/19) violates the
322 * specification which states that banks == 1 for unbanked modes;
323 * fortunately it does report bank_size == 0 for those.
325 if (mi
->banks
> 1 && mi
->bank_size
) {
326 debug("bad: banks = %d, banksize = %d, pages = %d\r\n",
327 mi
->banks
, mi
->bank_size
, mi
->image_pages
);
331 /* Must be either a flat-framebuffer mode, or be an acceptable
333 if (!(mi
->mode_attr
& 0x0080) && !vesacon_paged_mode_ok(mi
)) {
334 debug("bad: invalid paged mode\r\n");
338 /* Must either be a packed-pixel mode or a direct color mode
339 (depending on VESA version ); must be a supported pixel format */
340 pxf
= PXF_NONE
; /* Not usable */
343 (mi
->memory_layout
== 4 ||
344 (mi
->memory_layout
== 6 && mi
->rpos
== 16 && mi
->gpos
== 8 &&
347 else if (mi
->bpp
== 24 &&
348 (mi
->memory_layout
== 4 ||
349 (mi
->memory_layout
== 6 && mi
->rpos
== 16 && mi
->gpos
== 8 &&
352 else if (mi
->bpp
== 16 &&
353 (mi
->memory_layout
== 4 ||
354 (mi
->memory_layout
== 6 && mi
->rpos
== 11 && mi
->gpos
== 5 &&
356 pxf
= PXF_LE_RGB16_565
;
357 else if (mi
->bpp
== 15 &&
358 (mi
->memory_layout
== 4 ||
359 (mi
->memory_layout
== 6 && mi
->rpos
== 10 && mi
->gpos
== 5 &&
361 pxf
= PXF_LE_RGB15_555
;
363 if (pxf
< *bestpxf
) {
364 debug("Best mode so far, pxf = %d\r\n", pxf
);
366 /* Best mode so far... */
371 memcpy(&vesa_info
->mi
, mi
, sizeof *mi
);
375 if (*bestpxf
== PXF_NONE
) {
376 err
= 4; /* No mode found */
383 /* Now set video mode */
384 rm
.eax
.w
[0] = 0x4F02; /* Set SVGA video mode */
385 if (mi
->mode_attr
& 0x0080)
386 mode
|= 0x4000; /* Request linear framebuffer if supported */
388 __intcall(0x10, &rm
, &rm
);
389 if (rm
.eax
.w
[0] != 0x004F) {
390 err
= 9; /* Failed to set mode */
401 static void set_window_pos(struct win_info
*wi
, size_t win_pos
)
403 static com32sys_t ireg
;
405 wi
->win_pos
= win_pos
;
408 return; /* This should never happen... */
410 ireg
.eax
.w
[0] = 0x4F05;
411 ireg
.ebx
.b
[0] = wi
->win_num
;
412 ireg
.edx
.w
[0] = win_pos
>> wi
->win_gshift
;
414 __intcall(0x10, &ireg
, NULL
);
417 static void bios_vesacon_screencpy(size_t dst
, const uint32_t * src
,
418 size_t bytes
, struct win_info
*wi
)
420 size_t win_pos
, win_off
;
421 size_t win_size
= wi
->win_size
;
422 size_t omask
= win_size
- 1;
423 char *win_base
= wi
->win_base
;
424 const char *s
= (const char *)src
;
428 win_off
= dst
& omask
;
429 win_pos
= dst
& ~omask
;
431 if (__unlikely(win_pos
!= wi
->win_pos
))
432 set_window_pos(wi
, win_pos
);
434 l
= min(bytes
, win_size
- win_off
);
435 memcpy(win_base
+ win_off
, s
, l
);
443 static int bios_font_query(uint8_t **font
)
447 /* Get BIOS 8x16 font */
449 rm
.eax
.w
[0] = 0x1130; /* Get Font Information */
450 rm
.ebx
.w
[0] = 0x0600; /* Get 8x16 ROM font */
451 __intcall(0x10, &rm
, &rm
);
452 *font
= MK_PTR(rm
.es
, rm
.ebp
.w
[0]);
457 struct vesa_ops bios_vesa_ops
= {
458 .set_mode
= bios_vesacon_set_mode
,
459 .screencpy
= bios_vesacon_screencpy
,
460 .font_query
= bios_font_query
,
463 static uint32_t min_lowmem_heap
= 65536;
464 extern char __lowmem_heap
[];
465 uint8_t KbdFlags
; /* Check for keyboard escapes */
466 __export
uint8_t KbdMap
[256]; /* Keyboard map */
468 __export
uint16_t PXERetry
;
470 static inline void check_escapes(void)
472 com32sys_t ireg
, oreg
;
474 ireg
.eax
.b
[1] = 0x02; /* Check keyboard flags */
475 __intcall(0x16, &ireg
, &oreg
);
477 KbdFlags
= oreg
.eax
.b
[0];
479 /* Ctrl->skip 386 check */
480 if (oreg
.eax
.b
[0] & 0x04) {
482 * Now check that there is sufficient low (DOS) memory
484 * NOTE: Linux doesn't use all of real_mode_seg, but we use
485 * the same segment for COMBOOT images, which can use all 64K.
489 __intcall(0x12, &ireg
, &oreg
);
491 mem
= ((uint32_t)__lowmem_heap
) + min_lowmem_heap
+ 1023;
494 if (mem
< oreg
.eax
.w
[0]) {
497 snprintf(buf
, sizeof(buf
),
498 "It appears your computer has only "
499 "%dK of low (\"DOS\") RAM.\n"
500 "This version of Syslinux needs "
502 "If you get this\nmessage in error, "
503 "hold down the Ctrl key while booting, "
504 "and I\nwill take your word for it.\n",
512 extern uint32_t BIOS_timer_next
;
513 extern uint32_t timer_irq
;
514 static inline void bios_timer_init(void)
517 uint32_t *hook
= (uint32_t *)BIOS_timer_hook
;
520 BIOS_timer_next
= next
;
521 *hook
= (uint32_t)&timer_irq
;
524 extern uint8_t bios_free_mem
;
530 /* Initialize timer */
533 for (i
= 0; i
< 256; i
++)
536 bios_adjust_screen();
538 /* Init the memory subsystem */
539 bios_free_mem
= (uint16_t *)0x413;
541 /* CPU-dependent initialization and related checks. */
545 extern void *bios_malloc(size_t, enum heap
, size_t);
546 extern void *bios_realloc(void *, size_t);
547 extern void bios_free(void *);
549 struct mem_ops bios_mem_ops
= {
550 .malloc
= bios_malloc
,
551 .realloc
= bios_realloc
,
553 .scan_memory
= bios_scan_memory
,
556 struct firmware bios_fw
= {
558 .adjust_screen
= bios_adjust_screen
,
559 .cleanup
= bios_cleanup_hardware
,
560 .disk_init
= bios_disk_init
,
561 .o_ops
= &bios_output_ops
,
562 .i_ops
= &bios_input_ops
,
563 .ipappend_strings
= bios_ipappend_strings
,
564 .get_serial_console_info
= bios_get_serial_console_info
,
565 .adv_ops
= &bios_adv_ops
,
566 .vesa
= &bios_vesa_ops
,
567 .mem
= &bios_mem_ops
,
570 void syslinux_register_bios(void)