summaryrefslogtreecommitdiff
path: root/src/cpu_asm.S
blob: e14fb1fcd45694a7ed44b84a0eed6a40d44b07e5 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
/*
 * Copyright (c) 2017 Richard Braun.
 *
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
 * DEALINGS IN THE SOFTWARE.
 */

#include "cpu.h"

.section .text
.code32

.global cpu_get_eflags
cpu_get_eflags:
  pushf
  pop %eax
  ret

.global cpu_set_eflags
cpu_set_eflags:
  mov 4(%esp), %eax
  push %eax
  popf
  ret

.global cpu_intr_enable
cpu_intr_enable:
  sti
  ret

.global cpu_intr_disable
cpu_intr_disable:
  cli
  ret

.global cpu_idle
cpu_idle:
  hlt
  ret

.global cpu_load_gdt
cpu_load_gdt:
  mov 4(%esp), %eax             /* eax = &desc */
  lgdt (%eax)                   /* lgdt(*eax) */

  mov $CPU_GDT_SEL_DATA, %eax
  mov %eax, %ds
  mov %eax, %es
  mov %eax, %fs
  mov %eax, %gs
  mov %eax, %ss

  /*
   * The code segment register cannot directly be written to, and is instead
   * modified by performing a long jump.
   *
   * See Intel 64 and IA-32 Architecture Software Developer's Manual :
   *  - Volume 2 Instruction Set Reference
   *    - 3.2 Instructions (A-L)
   *      - JMP
   *        - Far Jumps in Protected Mode
   *  - Volume 3 System Programming Guide:
   *    - 3.4.3 Segment Registers
   */
  ljmp $CPU_GDT_SEL_CODE, $1f

1:
  ret

.global cpu_load_idt
cpu_load_idt:
  mov 4(%esp), %eax             /* eax = &desc */
  lidt (%eax)                   /* lidt(*eax) */
  ret

/*
 * See struct cpu_intr_frame in cpu.c.
 */
.macro CPU_INTR_STORE_REGISTERS
  push %edi
  push %esi
  push %ebp
  push %edx
  push %ecx
  push %ebx
  push %eax
.endm

.macro CPU_INTR_LOAD_REGISTERS
  pop %eax
  pop %ebx
  pop %ecx
  pop %edx
  pop %ebp
  pop %esi
  pop %edi
.endm

/*
 * Some interrupts push an error code, and some don't.
 * Have a single interrupt frame layout by pushing a dummy error code.
 */
#define CPU_INTR(vector, name)          \
.global name;                           \
name:                                   \
  pushl $0;                             \
  pushl $(vector);                      \
  jmp cpu_intr_common

#define CPU_INTR_ERROR(vector, name)    \
.global name;                           \
name:                                   \
  pushl $(vector);                      \
  jmp cpu_intr_common

/*
 * This is the first common low level entry point for all exceptions and
 * interrupts. When reached, the stack contains the registers automatically
 * pushed by the processor, an error code and the vector. It's important
 * to note that the stack pointer still points to the stack of the thread
 * running when the interrupt occurs. Actually, in this implementation,
 * the entire interrupt handler borrows the stack of the interrupted thread.
 *
 * This is dangerous because the stack must then be large enough for both
 * the largest call chain of the interrupted thread as well as the largest
 * call chain of any interrupt handler. On some implementations, especially
 * those with very demanding real-time constraints, interrupt handling may
 * nest to avoid waiting for the current interrupt to be serviced before
 * starting handling a higher priority one, leading to even larger stack
 * needs. This is why many operating systems dedicate a separate stack for
 * interrupt handling.
 */
cpu_intr_common:
  CPU_INTR_STORE_REGISTERS
  push %esp                 /* push the address of the interrupt frame */
  call cpu_intr_main        /* cpu_intr_main(frame) */
  add $4, %esp              /* restore the stack pointer */
  CPU_INTR_LOAD_REGISTERS
  add $8, %esp              /* skip vector and error */
  iret                      /* return from interrupt */

CPU_INTR(CPU_IDT_VECT_DIV, cpu_isr_divide_error)
CPU_INTR_ERROR(CPU_IDT_VECT_GP, cpu_isr_general_protection)

/*
 * XXX There must be as many low level ISRs as there are possible IRQ vectors.
 *
 * See the i8259 module.
 */
CPU_INTR(32, cpu_isr_32)
CPU_INTR(33, cpu_isr_33)
CPU_INTR(34, cpu_isr_34)
CPU_INTR(35, cpu_isr_35)
CPU_INTR(36, cpu_isr_36)
CPU_INTR(37, cpu_isr_37)
CPU_INTR(38, cpu_isr_38)
CPU_INTR(39, cpu_isr_39)
CPU_INTR(40, cpu_isr_40)
CPU_INTR(41, cpu_isr_41)
CPU_INTR(42, cpu_isr_42)
CPU_INTR(43, cpu_isr_43)
CPU_INTR(44, cpu_isr_44)
CPU_INTR(45, cpu_isr_45)
CPU_INTR(46, cpu_isr_46)
CPU_INTR(47, cpu_isr_47)