backported fixes from 256boss
[bootcensus] / src / lowcode.s
1 # pcboot - bootable PC demo/game kernel
2 # Copyright (C) 2018-2019  John Tsiombikas <nuclear@member.fsf.org>
3 #
4 # This program is free software: you can redistribute it and/or modify
5 # it under the terms of the GNU General Public License as published by
6 # the Free Software Foundation, either version 3 of the License, or
7 # (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY, without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program.  If not, see <https://www.gnu.org/licenses/>.
16
17         .section .lowtext,"ax"
18
19         .code32
20         .align 4
21         # place to save the protected mode IDTR pseudo-descriptor
22         # with sidt, so that it can be restored before returning
23         .short 0
24 saved_idtr:
25 idtlim: .short 0
26 idtaddr:.long 0
27         # real mode IDTR pseudo-descriptor pointing to the IVT at addr 0
28         .short 0
29 rmidt:  .short 0x3ff
30         .long 0
31
32 saved_esp: .long 0
33 saved_ebp: .long 0
34 saved_eax: .long 0
35 saved_es: .word 0
36 saved_ds: .word 0
37 saved_flags: .word 0
38 saved_if: .byte 0
39 saved_pic1_mask: .byte 0
40 saved_pic2_mask: .byte 0
41
42         # drop back to unreal mode to call 16bit interrupt
43         .global int86
44 int86:
45         push %ebp
46         mov %esp, %ebp
47         pushal
48         call get_intr_flag
49         mov %al, saved_if
50         cli
51         # save protected mode IDTR and replace it with the real mode vectors
52         sidt (saved_idtr)
53         lidt (rmidt)
54
55         # save PIC masks
56         pushl $0
57         call get_pic_mask
58         add $4, %esp
59         mov %al, saved_pic1_mask
60         pushl $1
61         call get_pic_mask
62         add $4, %esp
63         mov %al, saved_pic2_mask
64
65         # modify the int instruction. do this here before the
66         # cs-load jumps, to let them flush the instruction cache
67         mov $int_op, %ebx
68         movb 8(%ebp), %al
69         movb %al, 1(%ebx)
70
71         # long jump to load code selector for 16bit code (6)
72         ljmp $0x30,$0f
73 0:
74         .code16
75         # disable protection
76         mov %cr0, %eax
77         and $0xfffe, %ax
78         mov %eax, %cr0
79         # load cs <- 0
80         ljmp $0,$0f
81 0:      # zero data segments
82         xor %ax, %ax
83         mov %ax, %ds
84         mov %ax, %es
85         mov %ax, %ss
86         mov %ax, %fs
87         mov %ax, %gs
88         nop
89
90         # load registers from the int86regs struct
91         # point esp to the regs struct to load registers with popa/popf
92         mov %esp, saved_esp
93         mov %ebp, saved_ebp
94         mov 12(%ebp), %esp
95         popal
96         popfw
97         pop %es
98         pop %ds
99         # ignore fs and gs for now, don't think I'm going to need them
100
101         # move to the real-mode stack, at the top of conventional memory
102         #mov $0x9000, %sp
103         #mov %sp, %ss
104         #mov $0, %esp
105
106         # move to the real-mode stack, accessible from ss=0
107         # just in case the BIOS call screws up our unreal mode
108         mov $0x7be0, %esp
109
110         # call 16bit interrupt
111 int_op: int $0
112         # BIOS call might have enabled interrupts, cli for good measure
113         cli
114
115         # save all registers that we'll clobber before having the
116         # chance to populate the int86regs structure
117         mov %eax, saved_eax
118         mov %ds, saved_ds
119         mov %es, saved_es
120         pushfw
121         popw %ax
122         mov %ax, saved_flags
123
124         # re-enable protection
125         mov %cr0, %eax
126         or $1, %ax
127         mov %eax, %cr0
128         # long jump to load code selector for 32bit code (1)
129         ljmp $0x8,$0f
130 0:
131         .code32
132         # set data selector (2) to all segment regs
133         mov $0x10, %ax
134         mov %ax, %ds
135         mov %ax, %es
136         mov %ax, %ss
137         mov %ax, %fs
138         mov %ax, %gs
139         nop
140
141         # point the esp to our regs struct, to fill it with pusha/pushf
142         mov saved_ebp, %ebp
143         mov 12(%ebp), %esp
144         add $38, %esp
145         mov saved_ds, %ax
146         pushw %ax
147         mov saved_es, %ax
148         pushw %ax
149         # grab the flags and replace the carry bit from the saved flags
150         pushfw
151         popw %ax
152         and $0xfffe, %ax
153         or saved_flags, %ax
154         pushw %ax
155         mov saved_eax, %eax
156         pushal
157         mov saved_esp, %esp
158
159         # restore 32bit interrupt descriptor table
160         lidt (saved_idtr)
161
162         # restore PIC configuration
163         call init_pic
164
165         # restore IRQ masks
166         movzbl saved_pic1_mask, %eax
167         push %eax
168         pushl $0
169         call set_pic_mask
170         add $8, %esp
171
172         movzbl saved_pic2_mask, %eax
173         push %eax
174         pushl $1
175         call set_pic_mask
176         add $8, %esp
177
178         # keyboard voodoo: with some BIOS implementations, after returning from
179         # int13, there's (I guess) leftover data in the keyboard port and we
180         # can't receive any more keyboard interrupts afterwards. Reading from
181         # the keyboard data port (60h) once, seems to resolve this. And it's
182         # cheap enough, so why not... I give up.
183         in $0x60, %al
184
185         # restore interrupts to their previous state
186         movzbl saved_if, %eax
187         pushl %eax
188         call set_intr_flag
189         add $4, %esp
190
191         popal
192         pop %ebp
193         ret