530 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
			
		
		
	
	
			530 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			ArmAsm
		
	
	
	
	
	
| # Copyright (C) 2015 The University of Notre Dame
 | |
| # This software is distributed under the GNU General Public License.
 | |
| # See the file LICENSE for details.
 | |
| 
 | |
| #include "memorylayout.h"
 | |
| 	
 | |
| # _start is the initial entry point for the kernel
 | |
| # Note that we start here merely because it comes
 | |
| # first in the link order.  The name _start is there
 | |
| # merely to keep the linker happy.
 | |
| 
 | |
| .code16
 | |
| .text
 | |
| .global _start
 | |
| _start:
 | |
| 
 | |
| # First, jump to the real code start,
 | |
| # skipping over the immediate data that follows
 | |
| 	jmp	realstart
 | |
| 
 | |
| # At a fixed offset, place an integer that contains
 | |
| # the length of the kernel in bytes.  This is used
 | |
| # by the bootblock code to figure out how many sectors to load.
 | |
| 	
 | |
| .org KERNEL_SIZE_OFFSET
 | |
| .global kernel_size
 | |
| kernel_size:
 | |
| 	.long	_end-_start
 | |
| 		
 | |
| realstart:
 | |
| 	
 | |
| # While we are briefly still in 16-bit real mode,
 | |
| # it is safe to call the BIOS to set things up.
 | |
| 
 | |
| # Reset the disk system in order to quiet motors
 | |
| # and turn off any pending interrupts.
 | |
| 		
 | |
| 	mov	$0,%ah
 | |
| 	int	$0x13
 | |
| 
 | |
| # Turn off the screen cursor, because the
 | |
| # console will have its own.
 | |
| 
 | |
| 	mov	$1,%ah
 | |
| 	mov	$0,%cl
 | |
| 	mov	$0x20,%ch
 | |
| 	int	$0x10
 | |
| 
 | |
| # Get the amount of memory above 1MB and save it for later.
 | |
| # There are two ways to do this:
 | |
| 
 | |
| # BIOS call 0xe801 measures memory as follows:	
 | |
| # %ax returns memory above 1MB in 1KB increments, maximum of 16MB.
 | |
| # %bx returns memory above 64MB in 64KB increments, maximum of 4GB.
 | |
| # However, this call is relatively new, so if it
 | |
| # fails, we fall back on memtest2 below.
 | |
| 
 | |
| memtest1:	
 | |
| 	clc
 | |
| 	mov	$0, %bx
 | |
| 	mov	$0xe801,%ax
 | |
| 	int	$0x15
 | |
| 	jc	memtest2
 | |
| 	
 | |
| 	shr	$10, %ax
 | |
| 	shr	$4, %bx
 | |
| 	add	%ax, %bx
 | |
| 	mov	%bx, total_memory-_start
 | |
| 	jmp	memdone
 | |
| 
 | |
| # BIOS call 0x0088 measures memory as follows:
 | |
| # %ax returns memory above 1MB in 1KB increments, maxiumum of 64MB.
 | |
| 
 | |
| memtest2:
 | |
| 	clc
 | |
| 	mov	$0, %ax
 | |
| 	mov	$0x88, %ah
 | |
| 	int	$0x15
 | |
| 	shr	$10, %ax
 | |
| 	inc	%ax
 | |
| 	mov	%ax, total_memory-_start
 | |
| memdone:	
 | |
| 
 | |
| # Now, set the video mode using VBE interrupts.
 | |
| # Keep trying until we find one that works.
 | |
| 
 | |
| # These are documented on page 30 of the VESA-BIOS manual:
 | |
| # interrupt 0x10
 | |
| # ax = 0x4f02 "Set VBE Mode"
 | |
| # bx = mode
 | |
| #    D0-8   = Mode Number
 | |
| #    D9-10  = Reserved (must be 0)
 | |
| #    D11    = 0 Use current default refresh rate.
 | |
| #    D12-13 = 0 Reserved
 | |
| #    D14    = 0 Use windowed frame buffer model.
 | |
| #           = 1 Use linear frame buffer model.
 | |
| #    D15    = 0 Clear display memory.
 | |
| #    ES:DI  = Pointer to CRCTCInfoBlock structure.
 | |
| 
 | |
| jmp video640
 | |
| 	
 | |
| video1280:
 | |
|  	mov	$0x4f02, %ax
 | |
|  	mov	$0x411b, %bx
 | |
| 	int	$0x10
 | |
| 	cmp	$0x004f, %ax
 | |
| 	je	videodone
 | |
| video1024:
 | |
|  	mov	$0x4f02, %ax
 | |
|  	mov	$0x4118, %bx
 | |
| 	int	$0x10
 | |
| 	cmp	$0x004f, %ax
 | |
| 	je	videodone
 | |
| video800:
 | |
| 	mov	$0x4f02, %ax
 | |
|  	mov	$0x4115, %bx
 | |
| 	int	$0x10
 | |
| 	cmp	$0x004f, %ax
 | |
| 	je	videodone
 | |
| video640:
 | |
| 	mov	$0x4f02, %ax
 | |
|  	mov	$0x4112, %bx
 | |
| 	int	$0x10
 | |
| 	cmp	$0x004f, %ax
 | |
| 	je	videodone
 | |
| video640_lowcolor:
 | |
| 	mov	$0x4f02, %ax
 | |
| 	mov	$0x4111, %bx
 | |
| 	int	$0x10
 | |
| 	cmp	$0x004f, %ax
 | |
| 	je	videodone
 | |
| 
 | |
| videofailed:
 | |
| 	mov	$videomsg, %esi
 | |
| 	call	bios_putstring
 | |
| 	jmp	halt
 | |
| 
 | |
| videodone:	
 | |
| 
 | |
| # After all that, query the video mode and
 | |
| # figure out the dimensions and the frame
 | |
| # buffer address.  The set mode is still in bx.
 | |
| 
 | |
| 	mov	%ds, %ax		# Set up the extra segment
 | |
| 	mov	%ax, %es		# with the data segment
 | |
| 
 | |
| 	mov	$(video_info-_start),%di
 | |
| 	mov	$0x4f01, %ax
 | |
| 	mov	%bx, %cx
 | |
| 	int	$0x10
 | |
| 
 | |
| # In order to use video resolutions higher than 640x480,
 | |
| # we must enable the A20 address line. The following
 | |
| # code works on motherboards with "FAST A20", which should
 | |
| # be everything since the IBM PS/2
 | |
| inb $0x92, %al
 | |
| orb $2, %al
 | |
| outb %al, $0x92
 | |
| 	
 | |
| # Finally, we are ready to enter protected mode.
 | |
| # To do this, we disable interrupts so that
 | |
| # handlers will not see an inconsistent state.
 | |
| # We then load the new interrupt and descriptor
 | |
| # tables, which are given below.  Then, we
 | |
| # enable the protection bit, and load the
 | |
| # segment selectors into the appropriate registers.
 | |
| # Finally, we make a long jump to main,
 | |
| # atomically loading the new code segment and
 | |
| # starting the kernel.
 | |
| 
 | |
| 	cli				# clear interrupts
 | |
| 	lidt	(idt_init-_start)	# load the interrupt table
 | |
| 	lgdt	(gdt_init-_start)	# load the descriptor table
 | |
| 	mov	%cr0, %eax		# get the status word
 | |
| 	or	$0x01, %eax		# turn on the P bit
 | |
| 	mov	%eax, %cr0		# store the status word
 | |
| 					# (we are now in protected mode)
 | |
| 	mov	$2*8, %ax		# selector two is flat 4GB data data
 | |
| 	mov	%ax, %ds		# set data, extra, and stack segments to selector two
 | |
| 	mov	%ax, %es
 | |
| 	mov	%ax, %ss
 | |
| 	mov	$5*8, %ax		# set TSS to selector five
 | |
| 	ltr	%ax
 | |
| 	mov	$0, %ax			# unused segments are nulled out
 | |
| 	mov	%ax, %fs
 | |
| 	mov	%ax, %gs
 | |
| 	mov	$INTERRUPT_STACK_TOP, %sp    # set up initial C stack
 | |
| 	mov	$INTERRUPT_STACK_TOP, %bp    # set up initial C stack
 | |
| 	ljmpl	$(1*8), $(kernel_main)	     # jump to the C main!
 | |
| 
 | |
| # bios_putstring displays an ASCII string pointed to by %si,
 | |
| # useful for basic startup messages or fatal errors.
 | |
| 
 | |
| bios_putstring:
 | |
| 	mov	(%si), %al
 | |
| 	cmp	$0, %al
 | |
| 	jz	bios_putstring_done
 | |
|         call	bios_putchar
 | |
| 	inc	%si
 | |
| 	jmp	bios_putstring
 | |
| bios_putstring_done:
 | |
|         ret
 | |
| 
 | |
| # bios_putchar invokes the bios to display
 | |
| # one character on the screen.
 | |
| 	
 | |
| bios_putchar:
 | |
| 	push	%ax
 | |
| 	push	%bx
 | |
|         mov	$14,%ah
 | |
|         mov	$1,%bl
 | |
|         int	$0x10
 | |
| 	pop	%bx
 | |
| 	pop	%ax
 | |
| 	ret
 | |
| 
 | |
| # The video_info structure is filled in by a BIOS
 | |
| # call above, and is used to record the basic video
 | |
| # layout for the graphics subsystem.  See page 30
 | |
| # of the VBE specification for an explanation of this structure.
 | |
| 
 | |
| .align 4
 | |
| video_info:
 | |
| 	.word	0
 | |
| 	.byte	0,0
 | |
| 	.word	0,0,0,0
 | |
| 	.long	0
 | |
| .global video_xbytes
 | |
| video_xbytes:
 | |
| 	.word	0
 | |
| .global video_xres
 | |
| video_xres:
 | |
| 	.word	0 
 | |
| .global video_yres
 | |
| video_yres:
 | |
| 	.word	0
 | |
| 	.byte	0,0,0,0,0,0,0,0,0
 | |
| 	.byte	0,0,0,0,0,0,0,0,0
 | |
| .global video_buffer
 | |
| video_buffer:
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.word	0
 | |
| 	.word	0
 | |
| 	.byte	0,0,0,0,0,0,0,0,0,0
 | |
| 	.long	0
 | |
| .rept 190
 | |
| 	.byte	0
 | |
| .endr
 | |
| 
 | |
| .align 4
 | |
| videomsg:
 | |
| 	.asciz	"fatal error: couldn't find suitable video mode!\r\n"
 | |
| 
 | |
| ###########################
 | |
| # 32 BIT CODE BEGINS HERE #
 | |
| ###########################
 | |
| 
 | |
| # All code below this point is 32-bit code and data
 | |
| # that is invoked by higher levels of the kernel from C code.
 | |
| 	
 | |
| .code32
 | |
| 
 | |
| # Rebooting the machine is easy.
 | |
| # Set up an invalid interrupt table, and the force an interrupt.
 | |
| # The machine will triple-fault and reboot itself.
 | |
| 
 | |
| .global reboot
 | |
| reboot:
 | |
| 	cli
 | |
| 	lidt	idt_invalid	
 | |
| 	int	$1
 | |
| 
 | |
| 
 | |
| .global halt
 | |
| halt:
 | |
| 	cli
 | |
| 	hlt
 | |
| 	jmp	halt
 | |
| 
 | |
| # This is the global descriptor table to be used by the kernel.
 | |
| # Because we don't really want to use segmentation, we define
 | |
| # very simple descriptors for global code and data and the TSS
 | |
| 
 | |
| .align 16
 | |
| .global gdt
 | |
| gdt:
 | |
| 	.word	0,0,0,0				# seg 0 - null
 | |
| 	.word	0xffff, 0x0000, 0x9a00, 0x00cf	# seg 1 - kernel flat 4GB code
 | |
| 	.word	0xffff, 0x0000, 0x9200, 0x00cf	# seg 2 - kernel flat 4GB data
 | |
| 	.word	0xffff, 0x0000, 0xfa00, 0x00cf	# seg 3 - user flat 4GB code
 | |
| 	.word	0xffff, 0x0000, 0xf200, 0x00cf	# seg 4 - user flat 4GB data
 | |
| 	.word	0x0068, (tss-_start),0x8901, 0x00cf  # seg 5 - TSS
 | |
| 	
 | |
| # This is the initializer for the global descriptor table.
 | |
| # It simply tells us the size and location of the table.
 | |
| 
 | |
| gdt_init:
 | |
| 	.word	gdt_init-gdt
 | |
| 	.long	gdt
 | |
| 
 | |
| # The TSS is a big task management structure used by the 386.
 | |
| # We do not use the TSS, but simply rely on pushing variables
 | |
| # around in stacks.  However, we need to use the TSS in order
 | |
| # to initialize the stack pointer and segment for priv level 0
 | |
| 
 | |
| .align 16
 | |
| .global tss	
 | |
| tss:
 | |
| 	.long	0
 | |
| .global interrupt_stack_pointer
 | |
| interrupt_stack_pointer:	
 | |
| 	.long	INTERRUPT_STACK_TOP # initial interrupt stack ptr at 64 KB
 | |
| 	.long	2*8		    # use segment 2 for the interrupt stack
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 	.long	0
 | |
| 
 | |
| 	
 | |
| .global total_memory
 | |
| total_memory:
 | |
| 	.word	32
 | |
| 	
 | |
| # First, the internal interrupts.
 | |
| # Note that some already push their own detail
 | |
| # code onto the stack.  For the others, we push
 | |
| # a zero, just to get a common layout.
 | |
| 
 | |
| intr00: pushl $0 ; pushl $0  ; jmp intr_handler
 | |
| intr01: pushl $0 ; pushl $1  ; jmp intr_handler
 | |
| intr02: pushl $0 ; pushl $2  ; jmp intr_handler
 | |
| intr03: pushl $0 ; pushl $3  ; jmp intr_handler
 | |
| intr04: pushl $0 ; pushl $4  ; jmp intr_handler
 | |
| intr05: pushl $0 ; pushl $5  ; jmp intr_handler
 | |
| intr06: pushl $0 ; pushl $6  ; jmp intr_handler
 | |
| intr07: pushl $0 ; pushl $7  ; jmp intr_handler
 | |
| intr08:            pushl $8  ; jmp intr_handler
 | |
| intr09: pushl $0 ; pushl $9  ; jmp intr_handler
 | |
| intr10:            pushl $10 ; jmp intr_handler
 | |
| intr11:            pushl $11 ; jmp intr_handler
 | |
| intr12:            pushl $12 ; jmp intr_handler
 | |
| intr13:            pushl $13 ; jmp intr_handler
 | |
| intr14:            pushl $14 ; jmp intr_handler
 | |
| intr15: pushl $0 ; pushl $15 ; jmp intr_handler
 | |
| intr16: pushl $0 ; pushl $16 ; jmp intr_handler
 | |
| intr17:            pushl $17 ; jmp intr_handler
 | |
| intr18: pushl $0 ; pushl $18 ; jmp intr_handler
 | |
| intr19: pushl $0 ; pushl $19 ; jmp intr_handler
 | |
| 
 | |
| # These interrupts are reserved, but could
 | |
| # conceivably occur on the next processor model
 | |
| 
 | |
| intr20: pushl $0 ; pushl $20 ; jmp intr_handler
 | |
| intr21: pushl $0 ; pushl $21 ; jmp intr_handler
 | |
| intr22: pushl $0 ; pushl $22 ; jmp intr_handler
 | |
| intr23: pushl $0 ; pushl $23 ; jmp intr_handler
 | |
| intr24: pushl $0 ; pushl $24 ; jmp intr_handler
 | |
| intr25: pushl $0 ; pushl $25 ; jmp intr_handler
 | |
| intr26: pushl $0 ; pushl $26 ; jmp intr_handler
 | |
| intr27: pushl $0 ; pushl $27 ; jmp intr_handler
 | |
| intr28: pushl $0 ; pushl $28 ; jmp intr_handler
 | |
| intr29: pushl $0 ; pushl $29 ; jmp intr_handler
 | |
| intr30: pushl $0 ; pushl $30 ; jmp intr_handler
 | |
| intr31: pushl $0 ; pushl $31 ; jmp intr_handler
 | |
| 
 | |
| # Now, the external hardware interrupts.
 | |
| 
 | |
| intr32: pushl $0 ; pushl $32 ; jmp intr_handler
 | |
| intr33: pushl $0 ; pushl $33 ; jmp intr_handler
 | |
| intr34: pushl $0 ; pushl $34 ; jmp intr_handler
 | |
| intr35: pushl $0 ; pushl $35 ; jmp intr_handler
 | |
| intr36: pushl $0 ; pushl $36 ; jmp intr_handler
 | |
| intr37: pushl $0 ; pushl $37 ; jmp intr_handler
 | |
| intr38: pushl $0 ; pushl $38 ; jmp intr_handler
 | |
| intr39: pushl $0 ; pushl $39 ; jmp intr_handler
 | |
| intr40: pushl $0 ; pushl $40 ; jmp intr_handler
 | |
| intr41: pushl $0 ; pushl $41 ; jmp intr_handler
 | |
| intr42: pushl $0 ; pushl $42 ; jmp intr_handler
 | |
| intr43: pushl $0 ; pushl $43 ; jmp intr_handler
 | |
| intr44: pushl $0 ; pushl $44 ; jmp intr_handler
 | |
| intr45: pushl $0 ; pushl $45 ; jmp intr_handler
 | |
| intr46: pushl $0 ; pushl $46 ; jmp intr_handler
 | |
| intr47: pushl $0 ; pushl $47 ; jmp intr_handler
 | |
| intr48: pushl $0 ; pushl $48 ; jmp intr_syscall
 | |
| 
 | |
| intr_handler:
 | |
| 	pushl	%ds		# push segment registers
 | |
| 	pushl	%es
 | |
| 	pushl	%fs
 | |
| 	pushl	%gs
 | |
| 	pushl	%ebp		# push general regs
 | |
| 	pushl	%edi
 | |
| 	pushl	%esi
 | |
| 	pushl	%edx
 | |
| 	pushl	%ecx
 | |
| 	pushl	%ebx
 | |
| 	pushl	%eax
 | |
| 	pushl	48(%esp)	# push interrupt code from above
 | |
| 	pushl	48(%esp)	# push interrupt number from above
 | |
| 	movl	$2*8, %eax	# switch to kernel data seg and extra seg
 | |
| 	movl	%eax, %ds
 | |
| 	movl	%eax, %es
 | |
| 	call	interrupt_handler
 | |
| 	addl	$4, %esp	# remove interrupt number
 | |
| 	addl	$4, %esp	# remove interrupt code
 | |
| 	jmp	intr_return
 | |
| 	
 | |
| intr_syscall:
 | |
| 	pushl	%ds		# push segment registers
 | |
| 	pushl	%es
 | |
| 	pushl	%fs
 | |
| 	pushl	%gs
 | |
| 	pushl	%ebp		# push general regs
 | |
| 	pushl	%edi
 | |
| 	pushl	%esi
 | |
| 	pushl	%edx
 | |
| 	pushl	%ecx
 | |
| 	pushl	%ebx
 | |
| 	pushl	%eax		# note these *are* the syscall args
 | |
| 	movl	$2*8, %eax	# switch to kernel data seg and extra seg
 | |
| 	movl	%eax, %ds
 | |
| 	movl	%eax, %es
 | |
| 	call	syscall_handler
 | |
| 	addl	$4, %esp	# remove the old eax
 | |
| 	jmp	syscall_return	
 | |
| 
 | |
| .global intr_return
 | |
| intr_return:
 | |
| 	popl	%eax
 | |
| syscall_return:	
 | |
| 	popl	%ebx
 | |
| 	popl	%ecx
 | |
| 	popl	%edx
 | |
| 	popl	%esi
 | |
| 	popl	%edi
 | |
| 	popl	%ebp
 | |
| 	popl	%gs
 | |
| 	popl	%fs
 | |
| 	popl	%es
 | |
| 	popl	%ds
 | |
| 	addl	$4, %esp	# remove interrupt num
 | |
| 	addl	$4, %esp	# remove detail code
 | |
| 	iret			# iret gets the intr context
 | |
| 			
 | |
| .align 2
 | |
| idt:
 | |
| 	.word	intr00-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr01-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr02-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr03-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr04-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr05-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr06-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr07-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr08-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr09-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr10-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr11-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr12-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr13-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr14-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr15-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr16-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr17-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr18-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr19-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr20-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr21-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr22-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr23-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr24-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr25-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr26-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr27-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr28-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr29-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr30-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr31-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr32-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr33-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr34-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr35-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr36-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr37-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr38-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr39-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr40-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr41-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr42-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr43-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr44-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr45-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr46-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr47-_start,1*8,0x8e00,0x0001
 | |
| 	.word	intr48-_start,1*8,0xee00,0x0001
 | |
| 	
 | |
| # This is the initializer for the global interrupt table.
 | |
| # It simply gives the size and location of the interrupt table
 | |
| 	
 | |
| idt_init:
 | |
| 	.word	idt_init-idt
 | |
| 	.long	idt
 | |
| 
 | |
| # This is the initializer for an invalid interrupt table.
 | |
| # Its only purpose is to assist with the reboot routine.
 | |
| 	
 | |
| idt_invalid:
 | |
| 	.word	0
 | |
| 	.long	0
 |