aboutsummaryrefslogtreecommitdiff
path: root/kernel/boot/init/boot.s
blob: 8a134c4ee035bebd7c7e36dac58844e4b8805ce6 (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
/*
 * bubbl
 * Copyright (C) 2024-2025  Raghuram Subramani <raghus2247@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

/* Adapted from https://wiki.osdev.org/Bare_Bones */

/* Declare constants for the multiboot header. */
.set ALIGN,    1<<0             /* align loaded modules on page boundaries */
.set MEMINFO,  1<<1             /* provide memory map */
.set FLAGS,    ALIGN | MEMINFO  /* this is the Multiboot 'flag' field */
.set MAGIC,    0x1BADB002       /* 'magic number' lets bootloader find the header */
.set CHECKSUM, -(MAGIC + FLAGS) /* checksum of above, to prove we are multiboot */

/* 
  Declare a multiboot header that marks the program as a kernel. These are magic
  values that are documented in the multiboot standard. The bootloader will
  search for this signature in the first 8KiB of the kernel file, aligned at a
  32-bit boundary. The signature is in its own section so the header can be
  forced to be within the first 8KiB of the kernel file.
*/
.section .multiboot
.align 4
.long MAGIC
.long FLAGS
.long CHECKSUM

/*
  The multiboot standard does not define the value of the stack pointer register
  (esp) and it is up to the kernel to provide a stack. This allocates room for a
  small stack by creating a symbol at the bottom of it, then allocating 16384
  bytes for it, and finally creating a symbol at the top. The stack grows
  downwards on x86. The stack is in its own section so it can be marked nobits,
  which means the kernel file is smaller because it does not contain an
  uninitialized stack. The stack on x86 must be 16-byte aligned according to the
  System V ABI standard and de-facto extensions. The compiler will assume the
  stack is properly aligned and failure to align the stack will result in
  undefined behavior.
*/
.section .bss
.align 16
stack_bottom:
.skip 16384 # 16KiB
stack_top:

/*
  The linker script specifies _start as the entry point to the kernel and the
  bootloader will jump to this position once the kernel has been loaded. It
  doesn't make sense to return from this function as the bootloader is gone.
*/
.section .text
.global _start
.type _start, @function
_start:
  /*
  The bootloader has loaded us into 32-bit protected mode on a x86
  machine. Interrupts are disabled. Paging is disabled. The processor
  state is as defined in the multiboot standard. The kernel has full
  control of the CPU. The kernel can only make use of hardware features
  and any code it provides as part of itself. There's no printf
  function, unless the kernel provides its own <stdio.h> header and a
  printf implementation. There are no security restrictions, no
  safeguards, no debugging mechanisms, only what the kernel provides
  itself. It has absolute and complete power over the
  machine.
  */

  /*
  To set up a stack, we set the esp register to point to the top of the
  stack (as it grows downwards on x86 systems). This is necessarily done
  in assembly as languages such as C cannot function without a stack.
  */
  movl $stack_top, %esp

  /* Push Multiboot values to stack for kernel_main */
  push %ebx
  push %eax

  /*
  This is a good place to initialize crucial processor state before the
  high-level kernel is entered. It's best to minimize the early
  environment where crucial features are offline. Note that the
  processor is not fully initialized yet: Features such as floating
  point instructions and instruction set extensions are not initialized
  yet. The GDT should be loaded here. Paging should be enabled here.
  C++ features such as global constructors and exceptions will require
  runtime support to work as well.
  */

  /* Call the global constructors. */
  call _init

  /*
  Enter the high-level kernel. The ABI requires the stack is 16-byte
  aligned at the time of the call instruction (which afterwards pushes
  the return pointer of size 4 bytes). The stack was originally 16-byte
  aligned above and we've pushed a multiple of 16 bytes to the
  stack since (pushed 0 bytes so far), so the alignment has thus been
  preserved and the call is well defined.
  */
  call kernel_main

/*
Set the size of the _start symbol to the current location '.' minus its start.
This is useful when debugging or when you implement call tracing.
*/
.size _start, . - _start