-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmpos-x86.c
223 lines (185 loc) · 6.38 KB
/
mpos-x86.c
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
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include "mpos-kern.h"
#include "x86.h"
#include "lib.h"
/*****************************************************************************
* mpos-x86.c
*
* This file contains x86-specific code for setting up processor state,
* especially interrupts.
*
* YOU DO NOT NEED TO UNDERSTAND THIS!
*
*****************************************************************************/
/*****************************************************************************
* segments_init
*
* Set up the segment registers and interrupt descriptor table.
*
* The segment registers distinguish the kernel from applications:
* the kernel runs with segments SEGSEL_KERN_CODE and SEGSEL_KERN_DATA,
* and applications with SEGSEL_APP_CODE and SEGSEL_APP_DATA.
* The kernel segment runs with full privilege (level 0), but application
* segments run with less privilege (level 3).
* However, both kernel and applications can access all parts of machine
* memory! So there's no memory protection yet.
*
* The interrupt descriptor table tells the processor where to jump
* when an interrupt or exception happens.
* In miniprocos, it should jump to the assembly code in 'mpos-int.S'.
*
* The taskstate_t, segmentdescriptor_t, and pseduodescriptor_t types
* are defined by the x86 hardware.
*
*****************************************************************************/
// Segment selectors
#define SEGSEL_KERN_CODE 0x8 // kernel code segment
#define SEGSEL_KERN_DATA 0x10 // kernel data segment
#define SEGSEL_APP_CODE 0x18 // application code segment
#define SEGSEL_APP_DATA 0x20 // application data segment
#define SEGSEL_TASKSTATE 0x28 // task state segment
// The task descriptor defines the state the processor should set up
// when taking an interrupt.
static taskstate_t kernel_task_descriptor;
// Segments
static segmentdescriptor_t segments[] = {
SEG_NULL, // ignored
SEG(STA_X | STA_R, 0, 0xFFFFFFFF, 0), // SEGSEL_KERN_CODE
SEG(STA_W, 0, 0xFFFFFFFF, 0), // SEGSEL_KERN_DATA
SEG(STA_X | STA_R, 0, 0xFFFFFFFF, 3), // SEGSEL_APP_CODE
SEG(STA_W, 0, 0xFFFFFFFF, 3), // SEGSEL_APP_DATA
SEG_NULL /* defined below */ // SEGSEL_TASKSTATE
};
pseudodescriptor_t global_descriptor_table = {
sizeof(segments) - 1,
(uintptr_t) segments
};
// Interrupt descriptors
static gatedescriptor_t interrupt_descriptors[256]; /* initialized below */
pseudodescriptor_t interrupt_descriptor_table = {
sizeof(interrupt_descriptors) - 1,
(uintptr_t) interrupt_descriptors
};
// Particular interrupt handler routines
extern void (*sys_int_handlers[])(void);
extern void default_int_handler(void);
void
segments_init(void)
{
int i;
// Set task state segment
segments[SEGSEL_TASKSTATE >> 3]
= SEG16(STS_T32A, (uint32_t) &kernel_task_descriptor,
sizeof(taskstate_t), 0);
segments[SEGSEL_TASKSTATE >> 3].sd_s = 0;
// Set up kernel task descriptor, so we can receive interrupts
kernel_task_descriptor.ts_esp0 = KERNEL_STACK_TOP;
kernel_task_descriptor.ts_ss0 = SEGSEL_KERN_DATA;
// Set up interrupt descriptor table.
// Most interrupts are effectively ignored
for (i = 0; i < sizeof(interrupt_descriptors) / sizeof(gatedescriptor_t); i++)
SETGATE(interrupt_descriptors[i], 0,
SEGSEL_KERN_CODE, default_int_handler, 0);
// System calls get special handling.
// Note that the last argument is '3'. This means that unprivileged
// (level-3) applications may generate these interrupts.
for (i = INT_SYS_GETPID; i < INT_SYS_GETPID + 10; i++)
SETGATE(interrupt_descriptors[i], 0,
SEGSEL_KERN_CODE, sys_int_handlers[i - INT_SYS_GETPID], 3);
// Reload segment pointers
asm volatile("lgdt global_descriptor_table\n\t"
"ltr %0\n\t"
"lidt interrupt_descriptor_table"
: : "r" ((uint16_t) SEGSEL_TASKSTATE));
// Convince compiler that all symbols were used
(void) global_descriptor_table, (void) interrupt_descriptor_table;
}
/*****************************************************************************
* special_registers_init
*
* Set up the first process's registers to required values. In particular,
* tell the first process to use the memory information set up by
* segments_init().
*
*****************************************************************************/
void
special_registers_init(process_t *proc)
{
memset(&proc->p_registers, 0, sizeof(registers_t));
proc->p_registers.reg_cs = SEGSEL_APP_CODE | 3;
proc->p_registers.reg_ds = SEGSEL_APP_DATA | 3;
proc->p_registers.reg_es = SEGSEL_APP_DATA | 3;
proc->p_registers.reg_ss = SEGSEL_APP_DATA | 3;
}
/*****************************************************************************
* console_clear
*
* Clear the console by writing spaces to it, and move the cursor to the
* upper left (row 0, column 0).
*
*****************************************************************************/
void
console_clear(void)
{
int i;
cursorpos = (uint16_t *) 0xB8000;
for (i = 0; i < 80 * 25; i++)
cursorpos[i] = ' ' | 0x0700;
outb(0x3D4, 14);
outb(0x3D5, 0 / 256);
outb(0x3D4, 15);
outb(0x3D5, 0 % 256);
}
/*****************************************************************************
* console_read_digit
*
* Read a single digit from the keyboard and return it.
*
*****************************************************************************/
#define KBSTATP 0x64
#define KBS_DIB 0x01
#define KBDATAP 0x60
int
console_read_digit(void)
{
uint8_t data;
if ((inb(KBSTATP) & KBS_DIB) == 0)
return -1;
data = inb(KBDATAP);
if (data >= 0x02 && data <= 0x0A)
return data - 0x02 + 1;
else if (data == 0x0B)
return 0;
else if (data >= 0x47 && data <= 0x49)
return data - 0x47 + 7;
else if (data >= 0x4B && data <= 0x4D)
return data - 0x4B + 4;
else if (data >= 0x4F && data <= 0x51)
return data - 0x4F + 1;
else if (data == 0x53)
return 0;
else
return -1;
}
/*****************************************************************************
* run
*
* Run the process with the supplied process descriptor.
* This means reloading all the relevant registers from the descriptor's
* p_registers member, using the 'popal', 'popl', and 'iret'
* instructions.
*
*****************************************************************************/
void
run(process_t *proc)
{
current = proc;
asm volatile("movl %0,%%esp\n\t"
"popal\n\t"
"popl %%es\n\t"
"popl %%ds\n\t"
"addl $8, %%esp\n\t"
"iret"
: : "g" (&proc->p_registers) : "memory");
while (1)
/* do nothing */;
}