| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | /* 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 8 KiB 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 8 KiB 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 | 
					
						
							| 
									
										
										
										
											2025-10-20 21:57:30 -05:00
										 |  |  | small stack by creating a symbol at the bottom of it, then allocating 65536 | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | 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. | 
					
						
							|  |  |  | */ | 
					
						
							| 
									
										
										
										
											2025-10-20 21:57:30 -05:00
										 |  |  | .section .stack | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | .align 16
 | 
					
						
							|  |  |  | stack_bottom: | 
					
						
							| 
									
										
										
										
											2025-10-20 21:57:30 -05:00
										 |  |  | .skip 65536 # 64 KiB | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | 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
 | 
					
						
							|  |  |  | .global _kernel_early
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .type _start, @function
 | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  | enable_sse_asm: | 
					
						
							|  |  |  |     push %eax | 
					
						
							|  |  |  |     push %ebx | 
					
						
							|  |  |  |     push %ecx | 
					
						
							|  |  |  |     push %edx | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  |     push %esi | 
					
						
							|  |  |  |     push %edi | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Check CPUID support | 
					
						
							|  |  |  |     pushf | 
					
						
							|  |  |  |     pop %eax | 
					
						
							|  |  |  |     mov %eax, %ecx | 
					
						
							|  |  |  |     xor $0x200000, %eax | 
					
						
							|  |  |  |     push %eax | 
					
						
							|  |  |  |     popf | 
					
						
							|  |  |  |     pushf | 
					
						
							|  |  |  |     pop %eax | 
					
						
							|  |  |  |     xor %ecx, %eax | 
					
						
							|  |  |  |     jz .no_cpuid | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  |     # CPUID function 1 | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  |     mov $1, %eax | 
					
						
							|  |  |  |     cpuid | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  |     mov %edx, %ebx        # EDX = SSE1/SSE2 bits | 
					
						
							|  |  |  |     mov %ecx, %esi        # ECX = SSE3/SSSE3/SSE4.1 bits | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test $0x02000000, %ebx   # SSE | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  |     jz .no_sse | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  |     # Enable SSE (required for SSE1/2/3/SSSE3/4.1) | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  |     mov %cr0, %eax | 
					
						
							|  |  |  |     and $~0x4, %eax       # Clear EM (bit 2) | 
					
						
							|  |  |  |     or  $0x2, %eax        # Set MP (bit 1) | 
					
						
							|  |  |  |     mov %eax, %cr0 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     mov %cr4, %eax | 
					
						
							|  |  |  |     or  $0x600, %eax      # Set OSFXSR | OSXMMEXCPT | 
					
						
							|  |  |  |     mov %eax, %cr4 | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # Set version = 1 (SSE1) | 
					
						
							|  |  |  |     mov $1, %eax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     test $0x04000000, %ebx     # SSE2 (bit 26) | 
					
						
							|  |  |  |     jz .check_sse3 | 
					
						
							|  |  |  |     mov $2, %eax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .check_sse3: | 
					
						
							|  |  |  |     test $0x00000001, %esi     # SSE3 (bit 0) | 
					
						
							|  |  |  |     jz .check_ssse3 | 
					
						
							|  |  |  |     mov $3, %eax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .check_ssse3: | 
					
						
							|  |  |  |     test $0x00000200, %esi     # SSSE3 (bit 9) | 
					
						
							|  |  |  |     jz .check_sse41 | 
					
						
							|  |  |  |     mov $4, %eax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .check_sse41: | 
					
						
							|  |  |  |     test $0x00080000, %esi     # SSE4.1 (bit 19) | 
					
						
							|  |  |  |     jz .set_result | 
					
						
							|  |  |  |     mov $5, %eax | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .set_result: | 
					
						
							|  |  |  |     lea sse_initialized, %edi | 
					
						
							|  |  |  |     mov %eax, (%edi) | 
					
						
							|  |  |  |     jmp .done | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | .no_sse: | 
					
						
							|  |  |  | .no_cpuid: | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  |     lea sse_initialized, %edi | 
					
						
							| 
									
										
										
										
											2025-07-07 10:41:05 -05:00
										 |  |  |     movl $0, (%edi) | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  | .done: | 
					
						
							|  |  |  |     pop %edi | 
					
						
							|  |  |  |     pop %esi | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  |     pop %edx | 
					
						
							|  |  |  |     pop %ecx | 
					
						
							|  |  |  |     pop %ebx | 
					
						
							|  |  |  |     pop %eax | 
					
						
							|  |  |  |     ret | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  |          | 
					
						
							|  |  |  | _kernel_early: | 
					
						
							|  |  |  |         call _init | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         /* | 
					
						
							|  |  |  |           TODO: add more stuff here that needs to be ran before the main kernel code. | 
					
						
							|  |  |  |         */ | 
					
						
							|  |  |  |          | 
					
						
							|  |  |  |         ret | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _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 | 
					
						
							|  |  |  |         andl $0xFFFFFFF0, %esp | 
					
						
							|  |  |  |         movl %esp, %ebp | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	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. | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	cli /* Just in case */ | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	call enable_sse_asm | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	push %eax | 
					
						
							|  |  |  | 	push %ebx | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	 | 
					
						
							|  |  |  | 	/* | 
					
						
							| 
									
										
										
										
											2025-07-07 10:41:05 -05:00
										 |  |  | 	Call _kernel_early, early low-level initialization will happen there. | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | 	*/ | 
					
						
							|  |  |  | 	call _kernel_early | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* | 
					
						
							|  |  |  | 	If the system has nothing more to do, put the computer into an | 
					
						
							|  |  |  | 	infinite loop. To do that: | 
					
						
							|  |  |  | 	1) Disable interrupts with cli (clear interrupt enable in eflags). | 
					
						
							|  |  |  | 	   They are already disabled by the bootloader, so this is not needed. | 
					
						
							|  |  |  | 	   Mind that you might later enable interrupts and return from | 
					
						
							|  |  |  | 	   kernel_main (which is sort of nonsensical to do). | 
					
						
							|  |  |  | 	2) Wait for the next interrupt to arrive with hlt (halt instruction). | 
					
						
							|  |  |  | 	   Since they are disabled, this will lock up the computer. | 
					
						
							|  |  |  | 	3) Jump to the hlt instruction if it ever wakes up due to a | 
					
						
							|  |  |  | 	   non-maskable interrupt occurring or due to system management mode. | 
					
						
							|  |  |  | 	*/ | 
					
						
							|  |  |  | 	cli | 
					
						
							|  |  |  | 1:	hlt | 
					
						
							|  |  |  | 	jmp 1b | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* | 
					
						
							|  |  |  | 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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-28 14:41:02 -05:00
										 |  |  | .section .data | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | .global sse_initialized
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-06-27 14:48:06 -05:00
										 |  |  | sse_initialized: .int 0 |