xref: /xv6-public/bootasm.S (revision 7abf49d2)
121a88fd4Skaashoek#include "asm.h"
255e95b16Srtm
355e95b16Srtm.set PROT_MODE_CSEG,0x8		# code segment selector
455e95b16Srtm.set PROT_MODE_DSEG,0x10        # data segment selector
555e95b16Srtm.set CR0_PE_ON,0x1		# protected mode enable flag
655e95b16Srtm
755e95b16Srtm###################################################################################
855e95b16Srtm# ENTRY POINT
955e95b16Srtm#   This code should be stored in the first sector of the hard disk.
1055e95b16Srtm#   After the BIOS initializes the hardware on startup or system reset,
1155e95b16Srtm#   it loads this code at physical address 0x7c00 - 0x7d00 (512 bytes).
1255e95b16Srtm#   Then the BIOS jumps to the beginning of it, address 0x7c00,
1355e95b16Srtm#   while running in 16-bit real-mode (8086 compatibility mode).
1455e95b16Srtm#   The Code Segment register (CS) is initially zero on entry.
1555e95b16Srtm#
1655e95b16Srtm# This code switches into 32-bit protected mode so that all of
1755e95b16Srtm# memory can accessed, then calls into C.
1855e95b16Srtm###################################################################################
1955e95b16Srtm
2055e95b16Srtm.globl start					# Entry point
2155e95b16Srtmstart:		.code16				# This runs in real mode
2255e95b16Srtm		cli				# Disable interrupts
2355e95b16Srtm		cld				# String operations increment
2455e95b16Srtm
2555e95b16Srtm		# Set up the important data segment registers (DS, ES, SS).
2655e95b16Srtm		xorw	%ax,%ax			# Segment number zero
2755e95b16Srtm		movw	%ax,%ds			# -> Data Segment
2855e95b16Srtm		movw	%ax,%es			# -> Extra Segment
2955e95b16Srtm		movw	%ax,%ss			# -> Stack Segment
3055e95b16Srtm
3155e95b16Srtm		# Set up the stack pointer, growing downward from 0x7c00.
3255e95b16Srtm		movw	$start,%sp         	# Stack Pointer
3355e95b16Srtm
3455e95b16Srtm#### Enable A20:
3555e95b16Srtm####   For fascinating historical reasons (related to the fact that
3655e95b16Srtm####   the earliest 8086-based PCs could only address 1MB of physical memory
3755e95b16Srtm####   and subsequent 80286-based PCs wanted to retain maximum compatibility),
3855e95b16Srtm####   physical address line 20 is tied to low when the machine boots.
3955e95b16Srtm####   Obviously this a bit of a drag for us, especially when trying to
4055e95b16Srtm####   address memory above 1MB.  This code undoes this.
4155e95b16Srtm
4255e95b16Srtmseta20.1:	inb	$0x64,%al		# Get status
4355e95b16Srtm		testb	$0x2,%al		# Busy?
4455e95b16Srtm		jnz	seta20.1		# Yes
4555e95b16Srtm		movb	$0xd1,%al		# Command: Write
4655e95b16Srtm		outb	%al,$0x64		#  output port
4755e95b16Srtmseta20.2:	inb	$0x64,%al		# Get status
4855e95b16Srtm		testb	$0x2,%al		# Busy?
4955e95b16Srtm		jnz	seta20.2		# Yes
5055e95b16Srtm		movb	$0xdf,%al		# Enable
5155e95b16Srtm		outb	%al,$0x60		#  A20
5255e95b16Srtm
5355e95b16Srtm#### Switch from real to protected mode
5455e95b16Srtm####     The descriptors in our GDT allow all physical memory to be accessed.
5555e95b16Srtm####     Furthermore, the descriptors have base addresses of 0, so that the
5655e95b16Srtm####     segment translation is a NOP, ie. virtual addresses are identical to
5755e95b16Srtm####     their physical addresses.  With this setup, immediately after
5855e95b16Srtm####	 enabling protected mode it will still appear to this code
5955e95b16Srtm####	 that it is running directly on physical memory with no translation.
6055e95b16Srtm####	 This initial NOP-translation setup is required by the processor
6155e95b16Srtm####	 to ensure that the transition to protected mode occurs smoothly.
6255e95b16Srtm
6355e95b16Srtmreal_to_prot:	cli				# Mandatory since we dont set up an IDT
6455e95b16Srtm		lgdt	gdtdesc			# load GDT -- mandatory in protected mode
6555e95b16Srtm		movl	%cr0, %eax		# turn on protected mode
6655e95b16Srtm		orl	$CR0_PE_ON, %eax	#
6755e95b16Srtm		movl	%eax, %cr0		#
6855e95b16Srtm	        ### CPU magic: jump to relocation, flush prefetch queue, and reload %cs
6955e95b16Srtm		### Has the effect of just jmp to the next instruction, but simultaneous
7055e95b16Srtm		### loads CS with $PROT_MODE_CSEG.
7155e95b16Srtm		ljmp	$PROT_MODE_CSEG, $protcseg
7255e95b16Srtm
7355e95b16Srtm#### we are in 32-bit protected mode (hence the .code32)
7455e95b16Srtm.code32
7555e95b16Srtmprotcseg:
7655e95b16Srtm		# Set up the protected-mode data segment registers
7755e95b16Srtm		movw	$PROT_MODE_DSEG, %ax	# Our data segment selector
7855e95b16Srtm		movw	%ax, %ds		# -> DS: Data Segment
7955e95b16Srtm		movw	%ax, %es		# -> ES: Extra Segment
8055e95b16Srtm		movw	%ax, %fs		# -> FS
8155e95b16Srtm		movw	%ax, %gs		# -> GS
8255e95b16Srtm		movw	%ax, %ss		# -> SS: Stack Segment
8355e95b16Srtm
8455e95b16Srtm		call cmain			# finish the boot load from C.
8555e95b16Srtm						# cmain() should not return
8655e95b16Srtmspin:		jmp spin			# ..but in case it does, spin
8755e95b16Srtm
8855e95b16Srtm.p2align 2					# force 4 byte alignment
8955e95b16Srtmgdt:
90*7abf49d2Skaashoek	SEG_NULLASM				# null seg
91*7abf49d2Skaashoek	SEG_ASM(STA_X|STA_R, 0x0, 0xffffffff)	# code seg
92*7abf49d2Skaashoek	SEG_ASM(STA_W, 0x0, 0xffffffff)	        # data seg
9355e95b16Srtm
9455e95b16Srtmgdtdesc:
9555e95b16Srtm	.word	0x17			# sizeof(gdt) - 1
9655e95b16Srtm	.long	gdt			# address gdt
97