硬件和程序员的约定

CPU Reset 后寄存器会有确定的初始状态

  • EIP = 0x0000fff0
  • CR0 = 0x60000010
    • 处理器处于 16-bit 模式
  • EFLAGS = 0x00000002
    • interrupt disabled

Reset 后处理器从固定地址(Reset Vector)启动

  • MIPS: 0xbfc000000
    • Specification 规定
  • ARM: 0x00000000
    • Specification 规定
    • 允许配置 Reset Vector Base Address Register
  • RISC-V: Implementation defined
    • 给厂商最大程度的自由

Demo: firmware

1
2
3
4
5
6
7
8
9
10
11
12
13
14
mbr.img: mbr.S
gcc -ggdb -c $<
ld mbr.o -Ttext 0x7c00
objcopy -S -O binary -j .text a.out $@

run: mbr.img
qemu-system-x86_64 $<

debug: mbr.img
qemu-system-x86_64 -s -S $< & # Run QEMU in background
gdb -x init.gdb # RTFM: gdb (1)

clean:
rm -f *.img *.o a.out
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
#define SECT_SIZE  512

.code16 // 16-bit assembly

// Entry of the code
.globl _start
_start:
lea (msg), %si // R[si] = &msg;

again:
movb (%si), %al // R[al] = *R[si]; <--+
incw %si // R[si]++; |
orb %al, %al // if (!R[al]) |
jz done // goto done; --+ |
movb $0x0e, %ah // R[ah] = 0x0e; | |
movb $0x00, %bh // R[bh] = 0x00; | |
int $0x10 // bios_call(); | |
jmp again // goto again; ---+---+
// |
done: // |
jmp . // goto done; <---+

// Data: const char msg[] = "...";
msg:
.asciz "This is a baby step towards operating systems!\r\n"

// Magic number for bootable device
.org SECT_SIZE - 2
.byte 0x55, 0xAA
1
2
3
4
5
6
7
8
9
10
11
# Kill process (QEMU) on gdb exits
define hook-quit
kill
end

# Connect to remote
target remote localhost:1234
file a.out
break *0x7c00
layout src
continue

实现最小的操作系统

1
2
3
4
5
NAME := hello
SRCS := hello.c
export ARCH := x86_64-qemu

include $(AM_HOME)/Makefile
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
#include <am.h>
#include <klib.h>
#include <klib-macros.h>

typedef union task {
struct {
const char *name;
union task *next;
void (*entry)(void *);
Context *context;
};
uint8_t stack[8192];
} Task;

Task *current;

void func(void *arg) {
while (1) {
putch(*(char *)arg);
for (int volatile i = 0; i < 100000; i++) ;
}
}

Task tasks[] = {
{ .name = "a", .entry = func },
{ .name = "b", .entry = func },
};

Context *on_interrupt(Event ev, Context *ctx) {
if (!current) {
current = &tasks[0];
} else {
current->context = ctx;
}
return (current = current->next)->context;
}

int main() {
cte_init(on_interrupt); // call on_interrupt() on traps/interrupts

for (int i = 0; i < LENGTH(tasks); i++) {
Task *task = &tasks[i];
Area stack = (Area) { &task->context + 1, task + 1 };
task->context = kcontext(stack, task->entry, (void *)task->name);
task->next = &tasks[(i + 1) % LENGTH(tasks)];
}
iset(true); // Enable external interrupts (timer, I/O, ...)
yield(); // Trap
}
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <am.h>

#define ESC "\033["
#define RED ESC "01;31m"
#define CLR ESC "0m"

const char *MESSAGE = RED "Hello, OS World\n" CLR;

int main() {
for (const char *s = MESSAGE; *s; s++) {
putch(*s); // Prints to platform-dependent debug console
}
}