/*
|
/*
|
* linux/arch/i386/head.S
|
* linux/arch/i386/head.S
|
*
|
*
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
* Copyright (C) 1991, 1992 Linus Torvalds
|
*/
|
*/
|
|
|
/*
|
/*
|
* head.S contains the 32-bit startup code.
|
* head.S contains the 32-bit startup code.
|
*/
|
*/
|
|
|
.text
|
.text
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
#include
|
|
|
|
|
#define CL_MAGIC_ADDR 0x90020
|
#define CL_MAGIC_ADDR 0x90020
|
#define CL_MAGIC 0xA33F
|
#define CL_MAGIC 0xA33F
|
#define CL_BASE_ADDR 0x90000
|
#define CL_BASE_ADDR 0x90000
|
#define CL_OFFSET 0x90022
|
#define CL_OFFSET 0x90022
|
|
|
/*
|
/*
|
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
|
* swapper_pg_dir is the main page directory, address 0x00001000 (or at
|
* address 0x00101000 for a compressed boot).
|
* address 0x00101000 for a compressed boot).
|
*/
|
*/
|
ENTRY(stext)
|
ENTRY(stext)
|
ENTRY(_stext)
|
ENTRY(_stext)
|
startup_32:
|
startup_32:
|
cld
|
cld
|
movl $(KERNEL_DS),%eax
|
movl $(KERNEL_DS),%eax
|
mov %ax,%ds
|
mov %ax,%ds
|
mov %ax,%es
|
mov %ax,%es
|
mov %ax,%fs
|
mov %ax,%fs
|
mov %ax,%gs
|
mov %ax,%gs
|
#ifdef __SMP__
|
#ifdef __SMP__
|
orw %bx,%bx
|
orw %bx,%bx
|
jz 1f /* Initial CPU cleans BSS */
|
jz 1f /* Initial CPU cleans BSS */
|
/*
|
/*
|
* Set up the stack
|
* Set up the stack
|
*/
|
*/
|
mov %ax,%ss
|
mov %ax,%ss
|
xorl %eax,%eax
|
xorl %eax,%eax
|
movw %cx, %ax
|
movw %cx, %ax
|
movl %eax,%esp
|
movl %eax,%esp
|
pushl $0
|
pushl $0
|
popfl
|
popfl
|
jmp checkCPUtype
|
jmp checkCPUtype
|
1:
|
1:
|
lss stack_start,%esp
|
lss stack_start,%esp
|
#endif __SMP__
|
#endif __SMP__
|
/*
|
/*
|
* Clear BSS first so that there are no surprises...
|
* Clear BSS first so that there are no surprises...
|
*/
|
*/
|
xorl %eax,%eax
|
xorl %eax,%eax
|
movl $ SYMBOL_NAME(_edata),%edi
|
movl $ SYMBOL_NAME(_edata),%edi
|
movl $ SYMBOL_NAME(_end),%ecx
|
movl $ SYMBOL_NAME(_end),%ecx
|
subl %edi,%ecx
|
subl %edi,%ecx
|
cld
|
cld
|
rep
|
rep
|
stosb
|
stosb
|
/*
|
/*
|
* start system 32-bit setup. We need to re-do some of the things done
|
* start system 32-bit setup. We need to re-do some of the things done
|
* in 16-bit mode for the "real" operations.
|
* in 16-bit mode for the "real" operations.
|
*/
|
*/
|
call setup_idt
|
call setup_idt
|
xorl %eax,%eax
|
xorl %eax,%eax
|
1: incl %eax # check that A20 really IS enabled
|
1: incl %eax # check that A20 really IS enabled
|
movl %eax,0x000000 # loop forever if it isn't
|
movl %eax,0x000000 # loop forever if it isn't
|
cmpl %eax,0x100000
|
cmpl %eax,0x100000
|
je 1b
|
je 1b
|
/*
|
/*
|
* Initialize eflags. Some BIOS's leave bits like NT set. This would
|
* Initialize eflags. Some BIOS's leave bits like NT set. This would
|
* confuse the debugger if this code is traced.
|
* confuse the debugger if this code is traced.
|
* XXX - best to initialize before switching to protected mode.
|
* XXX - best to initialize before switching to protected mode.
|
*/
|
*/
|
pushl $0
|
pushl $0
|
popfl
|
popfl
|
/*
|
/*
|
* Copy bootup parameters out of the way. First 2kB of
|
* Copy bootup parameters out of the way. First 2kB of
|
* _empty_zero_page is for boot parameters, second 2kB
|
* _empty_zero_page is for boot parameters, second 2kB
|
* is for the command line.
|
* is for the command line.
|
*/
|
*/
|
movl $0x90000,%esi
|
movl $0x90000,%esi
|
movl $ SYMBOL_NAME(empty_zero_page),%edi
|
movl $ SYMBOL_NAME(empty_zero_page),%edi
|
movl $512,%ecx
|
movl $512,%ecx
|
cld
|
cld
|
rep
|
rep
|
movsl
|
movsl
|
xorl %eax,%eax
|
xorl %eax,%eax
|
movl $512,%ecx
|
movl $512,%ecx
|
rep
|
rep
|
stosl
|
stosl
|
cmpw $(CL_MAGIC),CL_MAGIC_ADDR
|
cmpw $(CL_MAGIC),CL_MAGIC_ADDR
|
jne 1f
|
jne 1f
|
movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
|
movl $ SYMBOL_NAME(empty_zero_page)+2048,%edi
|
movzwl CL_OFFSET,%esi
|
movzwl CL_OFFSET,%esi
|
addl $(CL_BASE_ADDR),%esi
|
addl $(CL_BASE_ADDR),%esi
|
movl $2048,%ecx
|
movl $2048,%ecx
|
rep
|
rep
|
movsb
|
movsb
|
1:
|
1:
|
#ifdef __SMP__
|
#ifdef __SMP__
|
checkCPUtype:
|
checkCPUtype:
|
#endif
|
#endif
|
|
|
/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */
|
/* check Processor type: 386, 486, 6x86(L) or CPUID capable processor */
|
/*
|
/*
|
* XXX - this does a lot of unnecessary setup. Alignment checks don't
|
* XXX - this does a lot of unnecessary setup. Alignment checks don't
|
* apply at our cpl of 0 and the stack ought to be aligned already, and
|
* apply at our cpl of 0 and the stack ought to be aligned already, and
|
* we don't need to preserve eflags.
|
* we don't need to preserve eflags.
|
*/
|
*/
|
|
|
movl $3, SYMBOL_NAME(x86)
|
movl $3, SYMBOL_NAME(x86)
|
pushfl # push EFLAGS
|
pushfl # push EFLAGS
|
popl %eax # get EFLAGS
|
popl %eax # get EFLAGS
|
movl %eax,%ecx # save original EFLAGS in ecx
|
movl %eax,%ecx # save original EFLAGS in ecx
|
xorl $0x40000,%eax # flip AC bit in EFLAGS
|
xorl $0x40000,%eax # flip AC bit in EFLAGS
|
pushl %eax # copy to EFLAGS
|
pushl %eax # copy to EFLAGS
|
popfl # set EFLAGS
|
popfl # set EFLAGS
|
pushfl # get new EFLAGS
|
pushfl # get new EFLAGS
|
popl %eax # put it in eax
|
popl %eax # put it in eax
|
xorl %ecx,%eax # change in flags
|
xorl %ecx,%eax # change in flags
|
andl $0x40000,%eax # check if AC bit changed
|
andl $0x40000,%eax # check if AC bit changed
|
je is386
|
je is386
|
movl $4,SYMBOL_NAME(x86)
|
movl $4,SYMBOL_NAME(x86)
|
movl %ecx,%eax
|
movl %ecx,%eax
|
xorl $0x200000,%eax # check ID flag
|
xorl $0x200000,%eax # check ID flag
|
pushl %eax
|
pushl %eax
|
popfl # if we are on a straight 486DX, SX, or
|
popfl # if we are on a straight 486DX, SX, or
|
pushfl # 487SX we can't change it
|
pushfl # 487SX we can't change it
|
popl %eax # Also if we are on a Cyrix 6x86(L)
|
popl %eax # Also if we are on a Cyrix 6x86(L)
|
xorl %ecx,%eax # OTOH 6x86MXs and MIIs check OK
|
xorl %ecx,%eax # OTOH 6x86MXs and MIIs check OK
|
andl $0x200000,%eax
|
andl $0x200000,%eax
|
je is486x
|
je is486x
|
|
|
isnew: pushl %ecx # restore original EFLAGS
|
isnew: pushl %ecx # restore original EFLAGS
|
popfl
|
popfl
|
incl SYMBOL_NAME(have_cpuid) # we have CPUID
|
incl SYMBOL_NAME(have_cpuid) # we have CPUID
|
/*
|
/*
|
* Technically we should use CPUID 0 to see if we have CPUID 1!
|
* Technically we should use CPUID 0 to see if we have CPUID 1!
|
*/
|
*/
|
/* get processor type */
|
/* get processor type */
|
movl $1, %eax # Use the CPUID instruction to
|
movl $1, %eax # Use the CPUID instruction to
|
#ifdef GAS_KNOWS_CPUID
|
#ifdef GAS_KNOWS_CPUID
|
cpuid # check the processor type
|
cpuid # check the processor type
|
#else
|
#else
|
.byte 0x0f, 0xa2 # check the processor type
|
.byte 0x0f, 0xa2 # check the processor type
|
#endif
|
#endif
|
movb %al, %cl # save reg for future use
|
movb %al, %cl # save reg for future use
|
andb $0x0f,%ah # mask processor family
|
andb $0x0f,%ah # mask processor family
|
movb %ah,SYMBOL_NAME(x86)
|
movb %ah,SYMBOL_NAME(x86)
|
andb $0xf0, %al # mask model
|
andb $0xf0, %al # mask model
|
shrb $4, %al
|
shrb $4, %al
|
movb %al,SYMBOL_NAME(x86_model)
|
movb %al,SYMBOL_NAME(x86_model)
|
andb $0x0f, %cl # mask mask revision
|
andb $0x0f, %cl # mask mask revision
|
movb %cl,SYMBOL_NAME(x86_mask)
|
movb %cl,SYMBOL_NAME(x86_mask)
|
movl %edx,SYMBOL_NAME(x86_capability)
|
movl %edx,SYMBOL_NAME(x86_capability)
|
/* get vendor info */
|
/* get vendor info */
|
xorl %eax, %eax # call CPUID with 0 -> return vendor ID
|
xorl %eax, %eax # call CPUID with 0 -> return vendor ID
|
#ifdef GAS_KNOWS_CPUID
|
#ifdef GAS_KNOWS_CPUID
|
cpuid
|
cpuid
|
#else
|
#else
|
.byte 0x0f, 0xa2 # CPUID
|
.byte 0x0f, 0xa2 # CPUID
|
#endif
|
#endif
|
movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
|
movl %ebx,SYMBOL_NAME(x86_vendor_id) # lo 4 chars
|
movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
|
movl %edx,SYMBOL_NAME(x86_vendor_id)+4 # next 4 chars
|
movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars
|
movl %ecx,SYMBOL_NAME(x86_vendor_id)+8 # last 4 chars
|
|
|
movl %cr0,%eax # 486+
|
movl %cr0,%eax # 486+
|
andl $0x80000011,%eax # Save PG,PE,ET
|
andl $0x80000011,%eax # Save PG,PE,ET
|
orl $0x50022,%eax # set AM, WP, NE and MP
|
orl $0x50022,%eax # set AM, WP, NE and MP
|
jmp 2f
|
jmp 2f
|
|
|
/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
|
/* Now we test if we have a Cyrix 6x86(L). We didn't test before to avoid
|
* clobbering the new BX chipset used with the Pentium II, which has a register
|
* clobbering the new BX chipset used with the Pentium II, which has a register
|
* at the same addresses as those used to access the Cyrix special configuration
|
* at the same addresses as those used to access the Cyrix special configuration
|
* registers (CCRs).
|
* registers (CCRs).
|
*/
|
*/
|
/*
|
/*
|
* A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
|
* A Cyrix/IBM 6x86(L) preserves flags after dividing 5 by 2
|
* (and it _must_ be 5 divided by 2) while other CPUs change
|
* (and it _must_ be 5 divided by 2) while other CPUs change
|
* them in undefined ways. We need to know this since we may
|
* them in undefined ways. We need to know this since we may
|
* need to enable the CPUID instruction at least.
|
* need to enable the CPUID instruction at least.
|
* We couldn't use this test before since the PPro and PII behave
|
* We couldn't use this test before since the PPro and PII behave
|
* like Cyrix chips in this respect.
|
* like Cyrix chips in this respect.
|
*/
|
*/
|
is486x: xor %ax,%ax
|
is486x: xor %ax,%ax
|
sahf
|
sahf
|
movb $5,%ax
|
movb $5,%ax
|
movb $2,%bx
|
movb $2,%bx
|
div %bl
|
div %bl
|
lahf
|
lahf
|
cmpb $2,%ah
|
cmpb $2,%ah
|
jne ncyrix
|
jne ncyrix
|
/*
|
/*
|
* N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
|
* N.B. The pattern of accesses to 0x22 and 0x23 is *essential*
|
* so do not try to "optimize" it! For the same reason we
|
* so do not try to "optimize" it! For the same reason we
|
* do all this with interrupts off.
|
* do all this with interrupts off.
|
*/
|
*/
|
#define setCx86(reg, val) \
|
#define setCx86(reg, val) \
|
movb reg,%ax; \
|
movb reg,%ax; \
|
outb %ax,$0x22; \
|
outb %ax,$0x22; \
|
movb val,%ax; \
|
movb val,%ax; \
|
outb %ax,$0x23
|
outb %ax,$0x23
|
|
|
#define getCx86(reg) \
|
#define getCx86(reg) \
|
movb reg,%ax; \
|
movb reg,%ax; \
|
outb %ax,$0x22; \
|
outb %ax,$0x22; \
|
inb $0x23,%ax
|
inb $0x23,%ax
|
|
|
cli
|
cli
|
getCx86($0xc3) # get CCR3
|
getCx86($0xc3) # get CCR3
|
movb %ax,%cx # Save old value
|
movb %ax,%cx # Save old value
|
movb %ax,%bx
|
movb %ax,%bx
|
andb $0x0f,%bx # Enable access to all config registers
|
andb $0x0f,%bx # Enable access to all config registers
|
orb $0x10,%bx # by setting bit 4
|
orb $0x10,%bx # by setting bit 4
|
setCx86($0xc3,%bx)
|
setCx86($0xc3,%bx)
|
|
|
getCx86($0xfe) # DIR0 : let's check this is a 6x86(L)
|
getCx86($0xfe) # DIR0 : let's check this is a 6x86(L)
|
andb $0xf0,%ax # should be 3xh
|
andb $0xf0,%ax # should be 3xh
|
cmpb $0x30,%ax #
|
cmpb $0x30,%ax #
|
jne n6x86
|
jne n6x86
|
|
|
getCx86($0xe8) # now we can get CCR4
|
getCx86($0xe8) # now we can get CCR4
|
orb $0x80,%ax # and set bit 7 (CPUIDEN)
|
orb $0x80,%ax # and set bit 7 (CPUIDEN)
|
movb %ax,%bx # to enable CPUID execution
|
movb %ax,%bx # to enable CPUID execution
|
setCx86($0xe8,%bx)
|
setCx86($0xe8,%bx)
|
|
|
getCx86($0xe9) # CCR5 : we reset the SLOP bit
|
getCx86($0xe9) # CCR5 : we reset the SLOP bit
|
andb $0xfd,%ax # so that udelay calculation
|
andb $0xfd,%ax # so that udelay calculation
|
movb %ax,%bx # is correct on 6x86(L) CPUs
|
movb %ax,%bx # is correct on 6x86(L) CPUs
|
setCx86($0xe9,%bx)
|
setCx86($0xe9,%bx)
|
setCx86($0xc3,%cx) # Restore old CCR3
|
setCx86($0xc3,%cx) # Restore old CCR3
|
sti
|
sti
|
jmp isnew # We enabled CPUID now
|
jmp isnew # We enabled CPUID now
|
|
|
n6x86: setCx86($0xc3,%cx) # Restore old CCR3
|
n6x86: setCx86($0xc3,%cx) # Restore old CCR3
|
sti
|
sti
|
ncyrix: pushl %ecx # restore original EFLAGS
|
ncyrix: pushl %ecx # restore original EFLAGS
|
popfl
|
popfl
|
movl %cr0,%eax # 486
|
movl %cr0,%eax # 486
|
andl $0x80000011,%eax # Save PG,PE,ET
|
andl $0x80000011,%eax # Save PG,PE,ET
|
orl $0x50022,%eax # set AM, WP, NE and MP
|
orl $0x50022,%eax # set AM, WP, NE and MP
|
jmp 2f
|
jmp 2f
|
is386: pushl %ecx # restore original EFLAGS
|
is386: pushl %ecx # restore original EFLAGS
|
popfl
|
popfl
|
movl %cr0,%eax # 386
|
movl %cr0,%eax # 386
|
andl $0x80000011,%eax # Save PG,PE,ET
|
andl $0x80000011,%eax # Save PG,PE,ET
|
orl $2,%eax # set MP
|
orl $2,%eax # set MP
|
2: movl %eax,%cr0
|
2: movl %eax,%cr0
|
call check_x87
|
call check_x87
|
#ifdef __SMP__
|
#ifdef __SMP__
|
movb ready,%al
|
movb ready,%al
|
orb %al,%al
|
orb %al,%al
|
jz 3f
|
jz 3f
|
movl $ SYMBOL_NAME(swapper_pg_dir), %eax
|
movl $ SYMBOL_NAME(swapper_pg_dir), %eax
|
movl %eax, %cr3
|
movl %eax, %cr3
|
#ifdef GAS_KNOWS_CR4
|
#ifdef GAS_KNOWS_CR4
|
movl %cr4,%eax
|
movl %cr4,%eax
|
orl $16,%eax
|
orl $16,%eax
|
movl %eax,%cr4
|
movl %eax,%cr4
|
#else
|
#else
|
.byte 0x0f,0x20,0xe0
|
.byte 0x0f,0x20,0xe0
|
orl $16,%eax
|
orl $16,%eax
|
.byte 0x0f,0x22,0xe0
|
.byte 0x0f,0x22,0xe0
|
#endif
|
#endif
|
movl %cr0, %eax
|
movl %cr0, %eax
|
orl $0x80000000, %eax
|
orl $0x80000000, %eax
|
movl %eax, %cr0
|
movl %eax, %cr0
|
jmp 4f
|
jmp 4f
|
#endif
|
#endif
|
3:
|
3:
|
call setup_paging
|
call setup_paging
|
#ifdef __SMP__
|
#ifdef __SMP__
|
incb ready
|
incb ready
|
#endif
|
#endif
|
4:
|
4:
|
lgdt gdt_descr
|
lgdt gdt_descr
|
lidt idt_descr
|
lidt idt_descr
|
ljmp $(KERNEL_CS),$1f
|
ljmp $(KERNEL_CS),$1f
|
1: movl $(KERNEL_DS),%eax # reload all the segment registers
|
1: movl $(KERNEL_DS),%eax # reload all the segment registers
|
mov %ax,%ds # after changing gdt.
|
mov %ax,%ds # after changing gdt.
|
mov %ax,%es
|
mov %ax,%es
|
mov %ax,%fs
|
mov %ax,%fs
|
mov %ax,%gs
|
mov %ax,%gs
|
#ifdef __SMP__
|
#ifdef __SMP__
|
movl $(KERNEL_DS), %eax
|
movl $(KERNEL_DS), %eax
|
mov %ax,%ss # Reload the stack pointer (segment only)
|
mov %ax,%ss # Reload the stack pointer (segment only)
|
#else
|
#else
|
lss stack_start,%esp # Load processor stack
|
lss stack_start,%esp # Load processor stack
|
#endif
|
#endif
|
xorl %eax,%eax
|
xorl %eax,%eax
|
lldt %ax
|
lldt %ax
|
pushl %eax # These are the parameters to main :-)
|
pushl %eax # These are the parameters to main :-)
|
pushl %eax
|
pushl %eax
|
pushl %eax
|
pushl %eax
|
cld # gcc2 wants the direction flag cleared at all times
|
cld # gcc2 wants the direction flag cleared at all times
|
call SYMBOL_NAME(start_kernel)
|
call SYMBOL_NAME(start_kernel)
|
L6:
|
L6:
|
jmp L6 # main should never return here, but
|
jmp L6 # main should never return here, but
|
# just in case, we know what happens.
|
# just in case, we know what happens.
|
|
|
#ifdef __SMP__
|
#ifdef __SMP__
|
ready: .byte 0
|
ready: .byte 0
|
#endif
|
#endif
|
|
|
/*
|
/*
|
* We depend on ET to be correct. This checks for 287/387.
|
* We depend on ET to be correct. This checks for 287/387.
|
*/
|
*/
|
check_x87:
|
check_x87:
|
movb $0,SYMBOL_NAME(hard_math)
|
movb $0,SYMBOL_NAME(hard_math)
|
clts
|
clts
|
fninit
|
fninit
|
fstsw %ax
|
fstsw %ax
|
cmpb $0,%al
|
cmpb $0,%al
|
je 1f
|
je 1f
|
movl %cr0,%eax /* no coprocessor: have to set bits */
|
movl %cr0,%eax /* no coprocessor: have to set bits */
|
xorl $4,%eax /* set EM */
|
xorl $4,%eax /* set EM */
|
movl %eax,%cr0
|
movl %eax,%cr0
|
ret
|
ret
|
ALIGN
|
ALIGN
|
1: movb $1,SYMBOL_NAME(hard_math)
|
1: movb $1,SYMBOL_NAME(hard_math)
|
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
|
.byte 0xDB,0xE4 /* fsetpm for 287, ignored by 387 */
|
ret
|
ret
|
|
|
/*
|
/*
|
* setup_idt
|
* setup_idt
|
*
|
*
|
* sets up a idt with 256 entries pointing to
|
* sets up a idt with 256 entries pointing to
|
* ignore_int, interrupt gates. It doesn't actually load
|
* ignore_int, interrupt gates. It doesn't actually load
|
* idt - that can be done only after paging has been enabled
|
* idt - that can be done only after paging has been enabled
|
* and the kernel moved to PAGE_OFFSET. Interrupts
|
* and the kernel moved to PAGE_OFFSET. Interrupts
|
* are enabled elsewhere, when we can be relatively
|
* are enabled elsewhere, when we can be relatively
|
* sure everything is ok.
|
* sure everything is ok.
|
*/
|
*/
|
setup_idt:
|
setup_idt:
|
lea ignore_int,%edx
|
lea ignore_int,%edx
|
movl $(KERNEL_CS << 16),%eax
|
movl $(KERNEL_CS << 16),%eax
|
movw %dx,%ax /* selector = 0x0010 = cs */
|
movw %dx,%ax /* selector = 0x0010 = cs */
|
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
|
movw $0x8E00,%dx /* interrupt gate - dpl=0, present */
|
|
|
lea SYMBOL_NAME(idt),%edi
|
lea SYMBOL_NAME(idt),%edi
|
mov $256,%ecx
|
mov $256,%ecx
|
rp_sidt:
|
rp_sidt:
|
movl %eax,(%edi)
|
movl %eax,(%edi)
|
movl %edx,4(%edi)
|
movl %edx,4(%edi)
|
addl $8,%edi
|
addl $8,%edi
|
dec %ecx
|
dec %ecx
|
jne rp_sidt
|
jne rp_sidt
|
ret
|
ret
|
|
|
|
|
/*
|
/*
|
* Setup_paging
|
* Setup_paging
|
*
|
*
|
* This routine sets up paging by setting the page bit
|
* This routine sets up paging by setting the page bit
|
* in cr0. The page tables are set up, identity-mapping
|
* in cr0. The page tables are set up, identity-mapping
|
* the first 4MB. The rest are initialized later.
|
* the first 4MB. The rest are initialized later.
|
*
|
*
|
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
|
* (ref: added support for up to 32mb, 17Apr92) -- Rik Faith
|
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
|
* (ref: update, 25Sept92) -- croutons@crunchy.uucp
|
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
|
* (ref: 92.10.11 - Linus Torvalds. Corrected 16M limit - no upper memory limit)
|
*/
|
*/
|
ALIGN
|
ALIGN
|
setup_paging:
|
setup_paging:
|
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
|
movl $1024*2,%ecx /* 2 pages - swapper_pg_dir+1 page table */
|
xorl %eax,%eax
|
xorl %eax,%eax
|
movl $ SYMBOL_NAME(swapper_pg_dir),%edi /* swapper_pg_dir is at 0x1000 */
|
movl $ SYMBOL_NAME(swapper_pg_dir),%edi /* swapper_pg_dir is at 0x1000 */
|
cld;rep;stosl
|
cld;rep;stosl
|
/* Identity-map the kernel in low 4MB memory for ease of transition */
|
/* Identity-map the kernel in low 4MB memory for ease of transition */
|
/* set present bit/user r/w */
|
/* set present bit/user r/w */
|
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)
|
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)
|
/* But the real place is at PAGE_OFFSET */
|
/* But the real place is at PAGE_OFFSET */
|
/* set present bit/user r/w */
|
/* set present bit/user r/w */
|
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+__USER_PGD_PTRS*4
|
movl $ SYMBOL_NAME(pg0)+7,SYMBOL_NAME(swapper_pg_dir)+__USER_PGD_PTRS*4
|
movl $ SYMBOL_NAME(pg0)+4092,%edi
|
movl $ SYMBOL_NAME(pg0)+4092,%edi
|
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
|
movl $0x03ff007,%eax /* 4Mb - 4096 + 7 (r/w user,p) */
|
std
|
std
|
1: stosl /* fill the page backwards - more efficient :-) */
|
1: stosl /* fill the page backwards - more efficient :-) */
|
subl $0x1000,%eax
|
subl $0x1000,%eax
|
jge 1b
|
jge 1b
|
cld
|
cld
|
movl $ SYMBOL_NAME(swapper_pg_dir),%eax
|
movl $ SYMBOL_NAME(swapper_pg_dir),%eax
|
movl %eax,%cr3 /* cr3 - page directory start */
|
movl %eax,%cr3 /* cr3 - page directory start */
|
movl %cr0,%eax
|
movl %cr0,%eax
|
orl $0x80000000,%eax
|
orl $0x80000000,%eax
|
movl %eax,%cr0 /* set paging (PG) bit */
|
movl %eax,%cr0 /* set paging (PG) bit */
|
ret /* this also flushes the prefetch-queue */
|
ret /* this also flushes the prefetch-queue */
|
|
|
/*
|
/*
|
* page 0 is made non-existent, so that kernel NULL pointer references get
|
* page 0 is made non-existent, so that kernel NULL pointer references get
|
* caught. Thus the swapper page directory has been moved to 0x1000
|
* caught. Thus the swapper page directory has been moved to 0x1000
|
*
|
*
|
* XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
|
* XXX Actually, the swapper page directory is at 0x1000 plus 1 megabyte,
|
* with the introduction of the compressed boot code. Theoretically,
|
* with the introduction of the compressed boot code. Theoretically,
|
* the original design of overlaying the startup code with the swapper
|
* the original design of overlaying the startup code with the swapper
|
* page directory is still possible --- it would reduce the size of the kernel
|
* page directory is still possible --- it would reduce the size of the kernel
|
* by 2-3k. This would be a good thing to do at some point.....
|
* by 2-3k. This would be a good thing to do at some point.....
|
*/
|
*/
|
.org 0x1000
|
.org 0x1000
|
ENTRY(swapper_pg_dir)
|
ENTRY(swapper_pg_dir)
|
/*
|
/*
|
* The page tables are initialized to only 4MB here - the final page
|
* The page tables are initialized to only 4MB here - the final page
|
* tables are set up later depending on memory size.
|
* tables are set up later depending on memory size.
|
*/
|
*/
|
.org 0x2000
|
.org 0x2000
|
ENTRY(pg0)
|
ENTRY(pg0)
|
|
|
.org 0x3000
|
.org 0x3000
|
ENTRY(empty_bad_page)
|
ENTRY(empty_bad_page)
|
|
|
.org 0x4000
|
.org 0x4000
|
ENTRY(empty_bad_page_table)
|
ENTRY(empty_bad_page_table)
|
|
|
.org 0x5000
|
.org 0x5000
|
ENTRY(empty_zero_page)
|
ENTRY(empty_zero_page)
|
|
|
.org 0x6000
|
.org 0x6000
|
|
|
stack_start:
|
stack_start:
|
.long SYMBOL_NAME(init_user_stack)+4096
|
.long SYMBOL_NAME(init_user_stack)+4096
|
.long KERNEL_DS
|
.long KERNEL_DS
|
|
|
/* NOTE: keep the idt short behind the above '.org 0x6000'
|
/* NOTE: keep the idt short behind the above '.org 0x6000'
|
It must fit completely within _one_ page */
|
It must fit completely within _one_ page */
|
ENTRY(idt)
|
ENTRY(idt)
|
.fill 256,8,0 # idt is uninitialized
|
.fill 256,8,0 # idt is uninitialized
|
|
|
/* This is the default interrupt "handler" :-) */
|
/* This is the default interrupt "handler" :-) */
|
int_msg:
|
int_msg:
|
.asciz "Unknown interrupt\n"
|
.asciz "Unknown interrupt\n"
|
ALIGN
|
ALIGN
|
ignore_int:
|
ignore_int:
|
cld
|
cld
|
pushl %eax
|
pushl %eax
|
pushl %ecx
|
pushl %ecx
|
pushl %edx
|
pushl %edx
|
push %ds
|
push %ds
|
push %es
|
push %es
|
push %fs
|
push %fs
|
movl $(KERNEL_DS),%eax
|
movl $(KERNEL_DS),%eax
|
mov %ax,%ds
|
mov %ax,%ds
|
mov %ax,%es
|
mov %ax,%es
|
mov %ax,%fs
|
mov %ax,%fs
|
pushl $int_msg
|
pushl $int_msg
|
call SYMBOL_NAME(printk)
|
call SYMBOL_NAME(printk)
|
popl %eax
|
popl %eax
|
pop %fs
|
pop %fs
|
pop %es
|
pop %es
|
pop %ds
|
pop %ds
|
popl %edx
|
popl %edx
|
popl %ecx
|
popl %ecx
|
popl %eax
|
popl %eax
|
iret
|
iret
|
|
|
/*
|
/*
|
* The interrupt descriptor table has room for 256 idt's
|
* The interrupt descriptor table has room for 256 idt's
|
*/
|
*/
|
ALIGN
|
ALIGN
|
.word 0
|
.word 0
|
idt_descr:
|
idt_descr:
|
.word 256*8-1 # idt contains 256 entries
|
.word 256*8-1 # idt contains 256 entries
|
.long __PAGE_OFFSET+SYMBOL_NAME(idt)
|
.long __PAGE_OFFSET+SYMBOL_NAME(idt)
|
|
|
ALIGN
|
ALIGN
|
.word 0
|
.word 0
|
gdt_descr:
|
gdt_descr:
|
#ifdef CONFIG_APM
|
#ifdef CONFIG_APM
|
.word (11+2*NR_TASKS)*8-1
|
.word (11+2*NR_TASKS)*8-1
|
#else
|
#else
|
.word (8+2*NR_TASKS)*8-1
|
.word (8+2*NR_TASKS)*8-1
|
#endif
|
#endif
|
.long __PAGE_OFFSET+SYMBOL_NAME(gdt)
|
.long __PAGE_OFFSET+SYMBOL_NAME(gdt)
|
|
|
/*
|
/*
|
* This gdt setup gives the kernel a 1GB address space at virtual
|
* This gdt setup gives the kernel a 1GB address space at virtual
|
* address PAGE_OFFSET - space enough for expansion, I hope.
|
* address PAGE_OFFSET - space enough for expansion, I hope.
|
*/
|
*/
|
|
|
#define upper_seg(type,dpl,base,limit) \
|
#define upper_seg(type,dpl,base,limit) \
|
((base) & 0xff000000) | \
|
((base) & 0xff000000) | \
|
(((base) & 0x00ff0000)>>16) | \
|
(((base) & 0x00ff0000)>>16) | \
|
(((limit)>>12) & 0xf0000) | \
|
(((limit)>>12) & 0xf0000) | \
|
((dpl)<<13) | \
|
((dpl)<<13) | \
|
(0x00c09000) | \
|
(0x00c09000) | \
|
((type)<<8)
|
((type)<<8)
|
|
|
#define lower_seg(type,dpl,base,limit) \
|
#define lower_seg(type,dpl,base,limit) \
|
(((base) & 0x0000ffff)<<16) | \
|
(((base) & 0x0000ffff)<<16) | \
|
(((limit)>>12) & 0x0ffff)
|
(((limit)>>12) & 0x0ffff)
|
|
|
#define x86_seg(type,dpl,base,limit) \
|
#define x86_seg(type,dpl,base,limit) \
|
.long lower_seg(type,dpl,base,limit); \
|
.long lower_seg(type,dpl,base,limit); \
|
.long upper_seg(type,dpl,base,limit)
|
.long upper_seg(type,dpl,base,limit)
|
|
|
ENTRY(gdt)
|
ENTRY(gdt)
|
.quad 0x0000000000000000 /* NULL descriptor */
|
.quad 0x0000000000000000 /* NULL descriptor */
|
.quad 0x0000000000000000 /* not used */
|
.quad 0x0000000000000000 /* not used */
|
|
|
/* 0x10 kernel 1GB code at 0xC0000000: */
|
/* 0x10 kernel 1GB code at 0xC0000000: */
|
x86_seg(0xa,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
|
x86_seg(0xa,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
|
|
|
/* 0x18 kernel 1GB data at 0xC0000000: */
|
/* 0x18 kernel 1GB data at 0xC0000000: */
|
x86_seg(0x2,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
|
x86_seg(0x2,0,__PAGE_OFFSET,0xffffffff-__PAGE_OFFSET)
|
|
|
/* 0x23 user 3GB code at 0x00000000: */
|
/* 0x23 user 3GB code at 0x00000000: */
|
x86_seg(0xa,3,0,__PAGE_OFFSET-1)
|
x86_seg(0xa,3,0,__PAGE_OFFSET-1)
|
|
|
/* 0x2b user 3GB data at 0x00000000: */
|
/* 0x2b user 3GB data at 0x00000000: */
|
x86_seg(0x2,3,0,__PAGE_OFFSET-1)
|
x86_seg(0x2,3,0,__PAGE_OFFSET-1)
|
|
|
.quad 0x0000000000000000 /* not used */
|
.quad 0x0000000000000000 /* not used */
|
.quad 0x0000000000000000 /* not used */
|
.quad 0x0000000000000000 /* not used */
|
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
|
.fill 2*NR_TASKS,8,0 /* space for LDT's and TSS's etc */
|
#ifdef CONFIG_APM
|
#ifdef CONFIG_APM
|
.quad 0x00c09a0000000000 /* APM CS code */
|
.quad 0x00c09a0000000000 /* APM CS code */
|
.quad 0x00809a0000000000 /* APM CS 16 code (16 bit) */
|
.quad 0x00809a0000000000 /* APM CS 16 code (16 bit) */
|
.quad 0x00c0920000000000 /* APM DS data */
|
.quad 0x00c0920000000000 /* APM DS data */
|
#endif
|
#endif
|
|
|