Mon 14 Oct 23:06:38 CEST 2024
This commit is contained in:
parent
3d38b5af98
commit
36e02efd9e
529
kernel/kernelcore.S
Normal file
529
kernel/kernelcore.S
Normal file
|
@ -0,0 +1,529 @@
|
|||
# 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
|
Loading…
Reference in New Issue
Block a user