Update markdown and tabs and stuff

This commit is contained in:
2016-10-22 17:07:50 +02:00
parent 67e817490e
commit a27daafa0a
39 changed files with 2249 additions and 2139 deletions

View File

@@ -39,95 +39,96 @@ Since the return address is stored on the stack, if you were to switch
stacks inside a function, when you return, you'll be somewhere else.
This is a common way of making usermode threads. Ponder the following:
void switch_thread()
{
push_all_registers();
switch_stack_pointer();
pop_all_registers();
return;
}
void a()
{
while(1)
{
do_something();
switch_thread();
}
}
:::c
void switch_thread()
{
push_all_registers();
switch_stack_pointer();
pop_all_registers();
return;
}
void b()
{
while(1)
{
do_something_else();
switch_thread();
}
}
void a()
{
while(1)
{
do_something();
switch_thread();
}
}
void b()
{
while(1)
{
do_something_else();
switch_thread();
}
}
Imagine two threads - __A__ and __B__ running, __A__ runs `a()` and __B__
runs `b()`. Each has a stack somewhere in memory, and __A__ is currently
running. The top of the stacks looks like:
+-----------------------+
|switch_stack_pointer RA|
|all registers |
+----------ESP----------+ |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+-----------------------+
|switch_stack_pointer RA|
|all registers |
+----------ESP----------+ |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
where `RA` means Return Address and `ESP` is where the stack pointer is
currently pointing.
As execution of __A__ continues, the processor will `do_something()` and
then call `switch_thread()`...
+-----------------------+
|switch_stack_pointer RA|
+----------ESP----------+ |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+-----------------------+
|switch_stack_pointer RA|
+----------ESP----------+ |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
`switch_thread()` pushes all registers to the stack and calls
`switch_stack_pointer()`
+----------ESP----------+ +-----------------------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+----------ESP----------+ +-----------------------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
`switch_stack_pointer()` performs some scheduling to find out which
thread is to run next, and then switches the stack pointer over to the
top of __B__'s stack.
+-----------------------+ +----------ESP----------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+-----------------------+ +----------ESP----------+
|switch_stack_pointer RA| |switch_stack_pointer RA|
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
The processor keeps on executing code, and `switch_stack_pointer()` soon
returns
+-----------------------+
|switch_stack_pointer RA| +----------ESP----------+
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+-----------------------+
|switch_stack_pointer RA| +----------ESP----------+
|all registers | |all registers |
|switch_thread RA | |switch_thread RA |
|a RA | |b RA |
| ... | | ... |
`switch_thread()` pops all registers and returns...
+-----------------------+
|switch_stack_pointer RA|
|all registers |
|switch_thread RA | +----------ESP----------+
|a RA | |b RA |
| ... | | ... |
{: .nopretty}
+-----------------------+
|switch_stack_pointer RA|
|all registers |
|switch_thread RA | +----------ESP----------+
|a RA | |b RA |
| ... | | ... |
... and we're now in `b()` with all registers of __B__ loaded.
###Stacks in the kernel
@@ -144,38 +145,41 @@ Molloys](http://www.jamesmolloy.co.uk/tutorial_html/) or [Brandon
Friesens](http://www.osdever.net/bkerndev/Docs/title.htm)) you probably
have something like this to handle interrupts:
int_stub:
pusha
xor eax, eax
mov ax, ds
push eax
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call int_handler
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
{: .lang-nasm}
:::nasm
int_stub:
pusha
void int_handler(registers_t r)
{
do_stuff();
}
xor eax, eax
mov ax, ds
push eax
mov eax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
call int_handler
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
 
:::c
void int_handler(registers_t r)
{
do_stuff();
}
In fact, if you've been following one of those tutorials, you probably
have the above code twice, for some reason...
@@ -184,42 +188,45 @@ Anyway. This would take care of both pushing and poping all registers,
and with only a small modification, it becomes very easy to switch the
stacks too...
int_stub:
pusha
xor eax, eax
mov ax, ds
push eax
mov eax 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ;Pass stack pointer to int_handler
call int_handler
mov esp, eax ;int_handler returns a new stack pointer
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
{: .lang-nasm }
:::nasm
int_stub:
pusha
registers_t *int_handler(registers_t *r)
{
do_stuff();
r = get_next_thread(r);
return r;
}
xor eax, eax
mov ax, ds
push eax
mov eax 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
push esp ;Pass stack pointer to int_handler
call int_handler
mov esp, eax ;int_handler returns a new stack pointer
pop eax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
popa
add esp, 8
iret
 
:::c
registers_t *int_handler(registers_t *r)
{
do_stuff();
r = get_next_thread(r);
return r;
}
This gives a pointer to the threads registers as input to the ISR and
expect a pointer to some registers in return. They may or may not be the
@@ -246,56 +253,55 @@ single data structure. So let's think about it for a while.
While the thread is running we want some information stored somewhere in
kernel space about it.
+-----------------------+
|thread information |
+-----------------------+
{: .nopretty}
+-----------------------+
|thread information |
+-----------------------+
Then, when an interrupt or syscall happens, a new stack is loaded
and some stuff is pushed onto it. If we want this near our thread
information it will have to go right before it, since the stack grows
backwards.
+-----------------------+
|thread registers |
|thread information |
+-----------------------+
{: .nopretty}
+-----------------------+
|thread registers |
|thread information |
+-----------------------+
Finally, we want the kernel mode stack. Well... the stack pointer is
right at the start of the registers now, so why not just continue the
stack from there?
+-----------------------+
| ... |
|kernel mode stack |
|thread registers |
|thread information |
+-----------------------+
{: .nopretty}
+-----------------------+
| ... |
|kernel mode stack |
|thread registers |
|thread information |
+-----------------------+
###Setting this up
To set this up, the thread information structure has to be set up
something like:
struct thread_info_struct
{
uint8_t stack_space[KERNEL_STACK_SIZE];
registers_t r;
struct thread_data_struct thread_data;
} my_thread_info;
:::c
struct thread_info_struct
{
uint8_t stack_space[KERNEL_STACK_SIZE];
registers_t r;
struct thread_data_struct thread_data;
} my_thread_info;
When the thread is running in user mode, the TSS should be set up in
such a way that the stack pointer loaded at an interrupt points to the
end of the registers, i.e. the beginning of the thread data.
TSS.esp0 = &my_thread_info.thread_data;
:::c
TSS.esp0 = &my_thread_info.thread_data;
And that's really all there is to it. Unbelievable, really, how many
years it took for me to figure this out.
In the process, I've found inspiration in [Rhombus by Nick
Johnson](https://github.com/nickbjohnson4224/rhombus/) and
Johnson](https://github.com/nickbjohnson4224/rhombus/) and
[linux](http://www.linux.org).
###Some considerations
@@ -335,15 +341,16 @@ I recently learned - the hard way - that the [clang
compiler](http://clang.llvm.org) does not use this calling convention
for functions which do not in turn call other functions. I.e
int double_integer(int a)
{
return 2*a;
}
:::c
int double_integer(int a)
{
return 2*a;
}
int main(int argc, char **argv)
{
double_integer(5);
}
int main(int argc, char **argv)
{
double_integer(5);
}
If this code is compiled with clang `double_integer` will (in some
cases) not push `ebp` to stack.