1// Rom layout and bios assembler to C interface.
2//
3// Copyright (C) 2008-2012  Kevin O'Connor <kevin@koconnor.net>
4// Copyright (C) 2002  MandrakeSoft S.A.
5//
6// This file may be distributed under the terms of the GNU LGPLv3 license.
7
8#include "asm-offsets.h" // BREGS_*
9#include "config.h" // CONFIG_*
10#include "entryfuncs.S" // ENTRY_*
11#include "hw/rtc.h" // CMOS_RESET_CODE
12#include "x86.h" // CR0_*
13
14        .code16
15
16
17/****************************************************************
18 * 16bit / 32bit call trampolines
19 ****************************************************************/
20
21// Place CPU into 32bit mode from 16bit mode.
22// %edx = return location (in 32bit mode)
23// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
24        DECLFUNC transition32
25        .global transition32_nmi_off
26transition32:
27        // Disable irqs (and clear direction flag)
28        cli
29        cld
30
31        // Disable nmi
32        movl %eax, %ecx
33        movl $CMOS_RESET_CODE|NMI_DISABLE_BIT, %eax
34        outb %al, $PORT_CMOS_INDEX
35        inb $PORT_CMOS_DATA, %al
36
37        // enable a20
38        inb $PORT_A20, %al
39        orb $A20_ENABLE_BIT, %al
40        outb %al, $PORT_A20
41        movl %ecx, %eax
42
43transition32_nmi_off:
44        // Set segment descriptors
45        lidtw %cs:pmode_IDT_info
46        lgdtw %cs:rombios32_gdt_48
47
48        // Enable protected mode
49        movl %cr0, %ecx
50        andl $~(CR0_PG|CR0_CD|CR0_NW), %ecx
51        orl $CR0_PE, %ecx
52        movl %ecx, %cr0
53
54        // start 32bit protected mode code
55        ljmpl $SEG32_MODE32_CS, $(BUILD_BIOS_ADDR + 1f)
56
57        .code32
58        // init data segments
591:      movl $SEG32_MODE32_DS, %ecx
60        movw %cx, %ds
61        movw %cx, %es
62        movw %cx, %ss
63        movw %cx, %fs
64        movw %cx, %gs
65
66        jmpl *%edx
67        .code16
68
69// Place CPU into 16bit mode from 32bit mode.
70// %edx = return location (in 16bit mode)
71// Clobbers: ecx, flags, segment registers, cr0, idt/gdt
72        DECLFUNC transition16
73        .global transition16big
74        .code32
75transition16:
76        // Reset data segment limits
77        movl $SEG32_MODE16_DS, %ecx
78        movw %cx, %ds
79        movw %cx, %es
80        movw %cx, %ss
81        movw %cx, %fs
82        movw %cx, %gs
83
84        // Jump to 16bit mode
85        ljmpw $SEG32_MODE16_CS, $1f
86
87transition16big:
88        movl $SEG32_MODE16BIG_DS, %ecx
89        movw %cx, %ds
90        movw %cx, %es
91        movw %cx, %ss
92        movw %cx, %fs
93        movw %cx, %gs
94
95        ljmpw $SEG32_MODE16BIG_CS, $1f
96
97        .code16
98        // Disable protected mode
991:      movl %cr0, %ecx
100        andl $~CR0_PE, %ecx
101        movl %ecx, %cr0
102
103        // far jump to flush CPU queue after transition to real mode
104        ljmpw $SEG_BIOS, $2f
105
106        // restore IDT to normal real-mode defaults
1072:      lidtw %cs:rmode_IDT_info
108
109        // Clear segment registers
110        xorw %cx, %cx
111        movw %cx, %fs
112        movw %cx, %gs
113        movw %cx, %es
114        movw %cx, %ds
115        movw %cx, %ss  // Assume stack is in segment 0
116
117        jmpl *%edx
118
119
120/****************************************************************
121 * External calling trampolines
122 ****************************************************************/
123
124// Far call a 16bit function from 16bit mode with a specified cpu register state
125// %eax = address of struct bregs, %edx = segment of struct bregs
126// Clobbers: %e[bc]x, %e[ds]i, flags
127        DECLFUNC __farcall16
128__farcall16:
129        // Save %edx/%eax, %ebp
130        pushl %ebp
131        pushl %eax
132        pushl %edx
133
134        // Setup for iretw call
135        movl %edx, %ds
136        pushw %cs
137        pushw $1f                       // return point
138        pushw BREGS_flags(%eax)         // flags
139        pushl BREGS_code(%eax)          // CS:IP
140
141        // Load calling registers and invoke call
142        RESTOREBREGS_DSEAX
143        iretw                           // XXX - just do a lcalll
1441:
145        // Store flags, es, eax
146        pushfw
147        cli
148        cld
149        pushw %ds
150        pushl %eax
151        movw 0x08(%esp), %ds
152        movl 0x0c(%esp), %eax
153        SAVEBREGS_POP_DSEAX
154        popw BREGS_flags(%eax)
155        movw %ss, %cx
156        movw %cx, %ds                   // Restore %ds == %ss
157
158        // Remove %edx/%eax, restore %ebp
159        popl %edx
160        popl %eax
161        popl %ebp
162
163        retl
164
165// IRQ trampolines
166        .macro IRQ_TRAMPOLINE num
167        DECLFUNC irq_trampoline_0x\num
168        irq_trampoline_0x\num :
169        int $0x\num
170        lretw
171        .endm
172
173        IRQ_TRAMPOLINE 02
174        IRQ_TRAMPOLINE 05
175        IRQ_TRAMPOLINE 10
176        IRQ_TRAMPOLINE 13
177        IRQ_TRAMPOLINE 15
178        IRQ_TRAMPOLINE 16
179        IRQ_TRAMPOLINE 18
180        IRQ_TRAMPOLINE 19
181        IRQ_TRAMPOLINE 1b
182        IRQ_TRAMPOLINE 1c
183        IRQ_TRAMPOLINE 4a
184
185
186/****************************************************************
187 * Misc. entry points.
188 ****************************************************************/
189
190// Entry point for QEMU smi interrupts.
191        DECLFUNC entry_smi
192entry_smi:
193        // Transition to 32bit mode.
194        movl $1f + BUILD_BIOS_ADDR, %edx
195        jmp transition32_nmi_off
196        .code32
1971:      movl $BUILD_SMM_ADDR + 0x8000, %esp
198        calll _cfunc32flat_handle_smi - BUILD_BIOS_ADDR
199        rsm
200        .code16
201
202// Entry point for QEMU smp sipi interrupts.
203        DECLFUNC entry_smp
204entry_smp:
205        // Transition to 32bit mode.
206        cli
207        cld
208        movl $2f + BUILD_BIOS_ADDR, %edx
209        jmp transition32_nmi_off
210        .code32
211        // Acquire lock and take ownership of shared stack
2121:      rep ; nop
2132:      lock btsl $0, SMPLock
214        jc 1b
215        movl SMPStack, %esp
216        // Call handle_smp
217        calll _cfunc32flat_handle_smp - BUILD_BIOS_ADDR
218        // Release lock and halt processor.
219        movl $0, SMPLock
2203:      hlt
221        jmp 3b
222        .code16
223
224// Resume (and reboot) entry point - called from entry_post
225        DECLFUNC entry_resume
226entry_resume:
227        // Disable interrupts
228        cli
229        cld
230        // Use the ExtraStack in low mem.
231        movl $_zonelow_seg, %eax
232        movw %ax, %ds
233        movw %ax, %ss
234        movl $ExtraStack + BUILD_EXTRA_STACK_SIZE, %esp
235        // Call handler.
236        jmp handle_resume
237
238// PMM entry point
239        DECLFUNC entry_pmm
240entry_pmm:
241        pushl %esp              // Backup %esp, then clear high bits
242        movzwl %sp, %esp
243        pushfl                  // Save registers clobbered by C code
244        cli
245        cld
246        PUSHBREGS
247        movl %ss, %ecx          // Move %ss to %ds
248        movw %cx, %ds
249        shll $4, %ecx
250        movl $_cfunc32flat_handle_pmm, %eax // Setup: call32(handle_pmm, args, -1)
251        leal PUSHBREGS_size+12(%esp, %ecx), %edx // %edx points to start of args
252        movl $-1, %ecx
253        calll __call32
254        movw %ax, BREGS_eax(%esp)       // Modify %ax:%dx to return %eax
255        shrl $16, %eax
256        movw %ax, BREGS_edx(%esp)
257        POPBREGS
258        popfl
259        popl %esp
260        lretw
261
262// PnP entry points
263        DECLFUNC entry_pnp_real
264        .global entry_pnp_prot
265entry_pnp_prot:
266        pushl %esp
267        jmp 1f
268entry_pnp_real:
269        pushl %esp              // Backup %esp, then clear high bits
270        movzwl %sp, %esp
2711:
272        pushfl                  // Save registers clobbered by C code
273        cli
274        cld
275        PUSHBREGS
276        movw %ss, %cx           // Move %ss to %ds
277        movw %cx, %ds
278        leal PUSHBREGS_size+12(%esp), %eax  // %eax points to start of u16 args
279        calll handle_pnp
280        movw %ax, BREGS_eax(%esp)   // Modify %eax to return %ax
281        POPBREGS
282        popfl
283        popl %esp
284        lretw
285
286// APM entry points
287        DECLFUNC entry_apm16
288entry_apm16:
289        pushfw          // save flags
290        pushl %eax      // dummy
291        ENTRY_ARG handle_apm
292        addw $4, %sp    // pop dummy
293        popfw           // restore flags
294        lretw
295
296        DECLFUNC entry_apm32
297        .code32
298entry_apm32:
299        pushfl
300        pushl %gs
301        pushl %cs               // Move second descriptor after %cs to %gs
302        addl $16, (%esp)
303        popl %gs
304        ENTRY_ARG_ESP _cfunc32seg_handle_apm
305        popl %gs
306        popfl
307        lretl
308        .code16
309
310// PCI-BIOS entry points
311        DECLFUNC entry_pcibios32
312        .code32
313entry_pcibios32:
314        pushfl
315        pushl %gs               // Backup %gs and set %gs=%ds
316        pushl %ds
317        popl %gs
318        ENTRY_ARG_ESP _cfunc32seg_handle_pcibios
319        popl %gs
320        popfl
321        lretl
322        .code16
323
324        DECLFUNC entry_pcibios16
325entry_pcibios16:
326        ENTRY_ARG handle_pcibios
327        iretw
328
329// int 1589 entry point
330        DECLFUNC entry_1589
331entry_1589:
332        ENTRY_ARG handle_1589
333        iretw
334
335// BIOS32 support
336        DECLFUNC entry_bios32
337        .code32
338entry_bios32:
339        pushfl
340#if CONFIG_PCIBIOS
341        // Check for PCI-BIOS request
342        cmpl $0x49435024, %eax // $PCI
343        jne 1f
344        movl $BUILD_BIOS_ADDR, %ebx
345        movl $BUILD_BIOS_SIZE, %ecx
346        movl $entry_pcibios32, %edx
347        xorb %al, %al
348        jmp 2f
349#endif
350        // Unknown request
3511:      movb $0x80, %al
352        // Return to caller
3532:      popfl
354        lretl
355        .code16
356
357// 32bit elf entry point
358        DECLFUNC entry_elf
359        .code32
360entry_elf:
361        cli
362        cld
363        movl %eax, entry_elf_eax
364        movl %ebx, entry_elf_ebx
365        lidtl (BUILD_BIOS_ADDR + pmode_IDT_info)
366        lgdtl (BUILD_BIOS_ADDR + rombios32_gdt_48)
367        movl $SEG32_MODE32_DS, %eax
368        movw %ax, %ds
369        movw %ax, %es
370        movw %ax, %fs
371        movw %ax, %gs
372        movw %ax, %ss
373        movl $BUILD_STACK_ADDR, %esp
374        ljmpl $SEG32_MODE32_CS, $_cfunc32flat_handle_post
375        .code16
376
377// UEFI Compatibility Support Module (CSM) entry point
378        DECLFUNC entry_csm
379entry_csm:
380        // Backup register state
381        pushfw
382        cli
383        cld
384        pushl %eax                      // dummy
385        PUSHBREGS
386
387        // Backup stack location and convert to a "flat pointer"
388        movl %ss, %eax
389        movw %ax, BREGS_code+2(%esp)    // Store %ss in bregs->code.seg
390        shll $4, %eax
391        addl %esp, %eax
392
393        // Change to BUILD_STACK_ADDR stack and call handle_csm(bregs)
394        ENTRY_INTO32 _cfunc32flat_handle_csm
395
396        DECLFUNC __csm_return
397        .code32
398__csm_return:
399        movl $1f, %edx
400        jmp transition16big
401        .code16
402
403        // Switch back to original stack
4041:      movzwl BREGS_code+2(%eax), %edx
405        movl %edx, %ecx
406        shll $4, %ecx
407        subl %ecx, %eax
408        movl %edx, %ss
409        movl %eax, %esp
410
411        // Restore register state and return.
412        POPBREGS
413        addw $4, %sp                    // pop dummy
414        popfw
415        lretw
416
417// Serial console "hooked vga" entry point
418        DECLFUNC entry_sercon
419entry_sercon:
420        // Setup for chain loading to real vga handler
421        pushfw
422        pushl %cs:sercon_real_vga_handler
423
424        // Set %ds to varlow segment
425        cli
426        cld
427        pushw %ds
428        pushl %eax
429        movl $_zonelow_seg, %eax
430        movl %eax, %ds
431
432        // Test if the sercon handler can be called
433        movl %esp, %eax         // Test for broken x86emu
434        pushl $1f
435        retl
4361:      cmpl %esp, %eax
437        jne 4f
438        cmpb $0, sercon_enable  // Test that sercon is enabled
439        je 3f
440
441        // call handle_sercon
442        popl %eax
443        popw %ds
4442:      pushl $handle_sercon
445#if CONFIG_ENTRY_EXTRASTACK
446        jmp irqentry_arg_extrastack
447#else
448        jmp irqentry_arg
449#endif
450
451        // sercon disabled - check for legacy text modeset and otherwise exit
4523:      popl %eax
453        popw %ds
454        cmpw $0x0007, %ax
455        jle 2b
456        iretw
457
458        // Running on broken x86emu - restore stack and exit
4594:      movl %eax, %esp
460        popl %eax
461        popw %ds
462        iretw
463
464
465/****************************************************************
466 * Interrupt entry points
467 ****************************************************************/
468
469        // Main entry point for hardware interrupts handled on extra stack
470        DECLFUNC irqentry_extrastack
471irqentry_extrastack:
472        cli
473        cld
474        pushw %ds               // Set %ds:%eax to space on ExtraStack
475        pushl %eax
476        movl $_zonelow_seg, %eax
477        movl %eax, %ds
478        movl StackPos, %eax
479        subl $PUSHBREGS_size+8, %eax
480        SAVEBREGS_POP_DSEAX
481        popl %ecx
482        movl %esp, PUSHBREGS_size(%eax)
483        movw %ss, PUSHBREGS_size+4(%eax)
484
485        movw %ds, %dx           // Setup %ss/%esp and call function
486        movw %dx, %ss
487        movl %eax, %esp
488        calll *%ecx
489
490        movl %esp, %eax         // Restore registers and return
491        movw PUSHBREGS_size+4(%eax), %ss
492        movl PUSHBREGS_size(%eax), %esp
493        RESTOREBREGS_DSEAX
494        iretw
495
496        // Main entry point for software interrupts handled on extra stack
497        DECLFUNC irqentry_arg_extrastack
498irqentry_arg_extrastack:
499        cli
500        cld
501        pushw %ds               // Set %ds:%eax to space on ExtraStack
502        pushl %eax
503        movl $_zonelow_seg, %eax
504        movl %eax, %ds
505        movl StackPos, %eax
506        subl $PUSHBREGS_size+16, %eax
507        SAVEBREGS_POP_DSEAX     // Save registers on extra stack
508        popl %ecx
509        movl %esp, PUSHBREGS_size+8(%eax)
510        movw %ss, PUSHBREGS_size+12(%eax)
511        popl BREGS_code(%eax)
512        popw BREGS_flags(%eax)
513
514        movw %ds, %dx           // Setup %ss/%esp and call function
515        movw %dx, %ss
516        movl %eax, %esp
517        calll *%ecx
518
519        movl %esp, %eax         // Restore registers and return
520        movw PUSHBREGS_size+12(%eax), %ss
521        movl PUSHBREGS_size+8(%eax), %esp
522        popl %edx
523        popw %dx
524        pushw BREGS_flags(%eax)
525        pushl BREGS_code(%eax)
526        RESTOREBREGS_DSEAX
527        iretw
528
529        // Main entry point for software interrupts (using caller's stack)
530        DECLFUNC irqentry_arg
531irqentry_arg:
532        ENTRY_ARG_ST
533        iretw
534
535        // Helper macros for hardware interrupt declaration
536        .macro IRQ_ENTRY num
537        .global entry_\num
538        entry_\num :
539        pushl $ handle_\num
540        jmp irqentry_extrastack
541        .endm
542
543        .macro DECL_IRQ_ENTRY num
544        DECLFUNC entry_\num
545        IRQ_ENTRY \num
546        .endm
547
548        // Helper macros for software interrupt declaration
549        .macro IRQ_ENTRY_ARG num
550        .global entry_\num
551        entry_\num :
552        pushl $ handle_\num
553#if CONFIG_ENTRY_EXTRASTACK
554        jmp irqentry_arg_extrastack
555#else
556        jmp irqentry_arg
557#endif
558        .endm
559
560        .macro DECL_IRQ_ENTRY_ARG num
561        DECLFUNC entry_\num
562        IRQ_ENTRY_ARG \num
563        .endm
564
565        // Various entry points (that don't require a fixed location).
566        DECL_IRQ_ENTRY_ARG 13
567        DECL_IRQ_ENTRY 76
568        DECL_IRQ_ENTRY 70
569        DECL_IRQ_ENTRY 74
570        DECL_IRQ_ENTRY 75
571        DECL_IRQ_ENTRY hwpic1
572        DECL_IRQ_ENTRY hwpic2
573
574        // int 18/19 are special - they reset stack and call into 32bit mode.
575        DECLFUNC entry_19
576entry_19:
577        ENTRY_INTO32 _cfunc32flat_handle_19
578
579        DECLFUNC entry_18
580entry_18:
581        ENTRY_INTO32 _cfunc32flat_handle_18
582
583
584/****************************************************************
585 * Fixed position entry points
586 ****************************************************************/
587
588        // Specify a location in the fixed part of bios area.
589        .macro ORG addr
590        .section .fixedaddr.\addr
591        .endm
592
593        ORG 0xe05b
594entry_post:
595        cmpl $0, %cs:HaveRunPost                // Check for resume/reboot
596        jnz entry_resume
597        ENTRY_INTO32 _cfunc32flat_handle_post   // Normal entry point
598
599        ORG 0xe2c3
600        .global entry_02
601entry_02:
602        ENTRY handle_02  // NMI handler does not switch onto extra stack
603        iretw
604
605        ORG 0xe3fe
606        .global entry_13_official
607entry_13_official:
608        jmp entry_13
609
610        // 0xe401 - OldFDPT in misc.c
611
612        ORG 0xe6f2
613        .global entry_19_official
614entry_19_official:
615        jmp entry_19
616
617        // 0xe6f5 - BIOS_CONFIG_TABLE in misc.c
618
619        // 0xe729 - BaudTable in misc.c
620
621        ORG 0xe739
622        IRQ_ENTRY_ARG 14
623
624        ORG 0xe82e
625        IRQ_ENTRY_ARG 16
626
627        ORG 0xe987
628        IRQ_ENTRY 09
629
630        ORG 0xec59
631        IRQ_ENTRY_ARG 40
632
633        ORG 0xef57
634        IRQ_ENTRY 0e
635
636        // 0xefc7 - diskette_param_table in misc.c
637
638        ORG 0xefd2
639        IRQ_ENTRY_ARG 17
640
641        ORG 0xf045
642entry_10_0x0f:
643        // XXX - INT 10 Functions 0-Fh Entry Point
644        iretw
645
646        ORG 0xf065
647entry_10:
648        iretw
649
650        // 0xf0a4 - VideoParams in misc.c
651
652        ORG 0xf841
653        IRQ_ENTRY_ARG 12
654
655        ORG 0xf84d
656        IRQ_ENTRY_ARG 11
657
658        ORG 0xf859
659        .global entry_15_official
660entry_15_official:
661        cmpb $0x89, %ah
662        je entry_1589           // 1589 calls return in protected mode
663        IRQ_ENTRY_ARG 15
664
665        // 0xfa6e - vgafont8 in font.c
666
667        ORG 0xfe6e
668        .global entry_1a_official
669entry_1a_official:
670        cmpb $0xb1, %ah
671        je entry_pcibios16      // PCIBIOS calls can be in protected mode
672        IRQ_ENTRY_ARG 1a
673
674        ORG 0xfea5
675        IRQ_ENTRY 08
676
677        // 0xfef3 - InitVectors in misc.c
678
679        ORG 0xff53
680        .global entry_iret_official
681entry_iret_official:
682        iretw
683
684        ORG 0xff54
685        IRQ_ENTRY_ARG 05
686
687        ORG 0xfff0 // Power-up Entry Point
688        .global reset_vector
689reset_vector:
690        ljmpw $SEG_BIOS, $entry_post
691
692        // 0xfff5 - BiosDate in misc.c
693
694        // 0xfffe - BiosModelId in misc.c
695
696        // 0xffff - BiosChecksum in misc.c
697
698        .end
699