Index: head/sys/amd64/amd64/locore.S =================================================================== --- head/sys/amd64/amd64/locore.S (revision 37888) +++ head/sys/amd64/amd64/locore.S (revision 37889) @@ -1,1024 +1,1029 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)locore.s 7.3 (Berkeley) 5/13/91 - * $Id: locore.s,v 1.109 1998/06/21 18:02:34 bde Exp $ + * $Id: locore.s,v 1.110 1998/06/30 03:01:35 jmg Exp $ * * originally from: locore.s, by William F. Jolitz * * Substantially rewritten by David Greenman, Rod Grimes, * Bruce Evans, Wolfgang Solfrank, Poul-Henning Kamp * and many others. */ #include "apm.h" #include "opt_bootp.h" #include "opt_ddb.h" #include "opt_nfsroot.h" #include "opt_userconfig.h" #include "opt_vm86.h" #include #include #include #include #include #include #include #include "assym.s" /* * XXX * * Note: This version greatly munged to avoid various assembler errors * that may be fixed in newer versions of gas. Perhaps newer versions * will have more pleasant appearance. */ /* * PTmap is recursive pagemap at top of virtual address space. * Within PTmap, the page directory can be found (third indirection). */ .globl _PTmap,_PTD,_PTDpde .set _PTmap,(PTDPTDI << PDRSHIFT) .set _PTD,_PTmap + (PTDPTDI * PAGE_SIZE) .set _PTDpde,_PTD + (PTDPTDI * PDESIZE) /* * APTmap, APTD is the alternate recursive pagemap. * It's used when modifying another process's page tables. */ .globl _APTmap,_APTD,_APTDpde .set _APTmap,APTDPTDI << PDRSHIFT .set _APTD,_APTmap + (APTDPTDI * PAGE_SIZE) .set _APTDpde,_PTD + (APTDPTDI * PDESIZE) /* * Globals */ .data ALIGN_DATA /* just to be sure */ .globl HIDENAME(tmpstk) .space 0x2000 /* space for tmpstk - temporary stack */ HIDENAME(tmpstk): .globl _boothowto,_bootdev .globl _cpu,_cpu_vendor,_cpu_id,_bootinfo .globl _cpu_high, _cpu_feature _cpu: .long 0 /* are we 386, 386sx, or 486 */ _cpu_id: .long 0 /* stepping ID */ _cpu_high: .long 0 /* highest arg to CPUID */ _cpu_feature: .long 0 /* features */ _cpu_vendor: .space 20 /* CPU origin code */ _bootinfo: .space BOOTINFO_SIZE /* bootinfo that we can handle */ _KERNend: .long 0 /* phys addr end of kernel (just after bss) */ physfree: .long 0 /* phys addr of next free page */ #ifdef SMP cpu0pp: .long 0 /* phys addr cpu0 private pg */ cpu0pt: .long 0 /* phys addr cpu0 private pt */ .globl _cpu0prvpage,_cpu0prvpt _cpu0prvpage: .long 0 /* relocated version */ _cpu0prvpt: .long 0 /* relocated version */ #endif /* SMP */ .globl _IdlePTD _IdlePTD: .long 0 /* phys addr of kernel PTD */ #ifdef SMP .globl _KPTphys #endif _KPTphys: .long 0 /* phys addr of kernel page tables */ .globl _proc0paddr _proc0paddr: .long 0 /* address of proc 0 address space */ p0upa: .long 0 /* phys addr of proc0's UPAGES */ #ifdef VM86 +vm86phystk: .long 0 /* PA of vm86/bios stack */ + .globl _vm86paddr, _vm86pa _vm86paddr: .long 0 /* address of vm86 region */ _vm86pa: .long 0 /* phys addr of vm86 region */ #endif #ifdef BDE_DEBUGGER .globl _bdb_exists /* flag to indicate BDE debugger is present */ _bdb_exists: .long 0 #endif /********************************************************************** * * Some handy macros * */ #define R(foo) ((foo)-KERNBASE) #define ALLOCPAGES(foo) \ movl R(physfree), %esi ; \ movl $((foo)*PAGE_SIZE), %eax ; \ addl %esi, %eax ; \ movl %eax, R(physfree) ; \ movl %esi, %edi ; \ movl $((foo)*PAGE_SIZE),%ecx ; \ xorl %eax,%eax ; \ cld ; \ rep ; \ stosb /* * fillkpt * eax = page frame address * ebx = index into page table * ecx = how many pages to map * base = base address of page dir/table * prot = protection bits */ #define fillkpt(base, prot) \ shll $2,%ebx ; \ addl base,%ebx ; \ orl $PG_V,%eax ; \ orl prot,%eax ; \ 1: movl %eax,(%ebx) ; \ addl $PAGE_SIZE,%eax ; /* increment physical address */ \ addl $4,%ebx ; /* next pte */ \ loop 1b /* * fillkptphys(prot) * eax = physical address * ecx = how many pages to map * prot = protection bits */ #define fillkptphys(prot) \ movl %eax, %ebx ; \ shrl $PAGE_SHIFT, %ebx ; \ fillkpt(R(_KPTphys), prot) .text /********************************************************************** * * This is where the bootblocks start us, set the ball rolling... * */ NON_GPROF_ENTRY(btext) #ifdef PC98 jmp 1f .globl _pc98_system_parameter .org 0x400 _pc98_system_parameter: .space 0x240 /* BIOS parameter block */ 1: /* save SYSTEM PARAMETER for resume (NS/T or other) */ movl $0xa1000,%esi movl $0x100000,%edi movl $0x0630,%ecx cld rep movsb #else /* IBM-PC */ #ifdef BDE_DEBUGGER #ifdef BIOS_STEALS_3K cmpl $0x0375c339,0x95504 #else cmpl $0x0375c339,0x96104 /* XXX - debugger signature */ #endif jne 1f movb $1,R(_bdb_exists) 1: #endif /* Tell the bios to warmboot next time */ movw $0x1234,0x472 #endif /* PC98 */ /* Set up a real frame in case the double return in newboot is executed. */ pushl %ebp movl %esp, %ebp /* Don't trust what the BIOS gives for eflags. */ pushl $PSL_KERNEL popfl /* * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap * to set %cs, %ds, %es and %ss. */ mov %ds, %ax mov %ax, %fs mov %ax, %gs call recover_bootinfo /* Get onto a stack that we can trust. */ /* * XXX this step is delayed in case recover_bootinfo needs to return via * the old stack, but it need not be, since recover_bootinfo actually * returns via the old frame. */ movl $R(HIDENAME(tmpstk)),%esp #ifdef PC98 testb $0x02,0x100620 /* pc98_machine_type & M_EPSON_PC98 */ jz 3f cmpb $0x0b,0x100624 /* epson_machine_id <= 0x0b */ ja 3f /* count up memory */ movl $0x100000,%eax /* next, talley remaining memory */ movl $0xFFF-0x100,%ecx 1: movl 0(%eax),%ebx /* save location to check */ movl $0xa55a5aa5,0(%eax) /* write test pattern */ cmpl $0xa55a5aa5,0(%eax) /* does not check yet for rollover */ jne 2f movl %ebx,0(%eax) /* restore memory */ addl $PAGE_SIZE,%eax loop 1b 2: subl $0x100000,%eax shrl $17,%eax movb %al,0x100401 3: #endif call identify_cpu /* clear bss */ /* * XXX this should be done a little earlier. * * XXX we don't check that there is memory for our bss and page tables * before using it. * * XXX the boot program somewhat bogusly clears the bss. We still have * to do it in case we were unzipped by kzipboot. Then the boot program * only clears kzipboot's bss. * * XXX the gdt and idt are still somewhere in the boot program. We * depend on the convention that the boot program is below 1MB and we * are above 1MB to keep the gdt and idt away from the bss and page * tables. The idt is only used if BDE_DEBUGGER is enabled. */ movl $R(_end),%ecx movl $R(_edata),%edi subl %edi,%ecx xorl %eax,%eax cld rep stosb #if NAPM > 0 #ifndef VM86 /* * XXX it's not clear that APM can live in the current environonment. * Only pc-relative addressing works. */ call _apm_setup #endif #endif call create_pagetables #ifdef VM86 /* * If the CPU has support for VME, turn it on. */ testl $CPUID_VME, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_VME, %eax movl %eax, %cr4 1: #endif /* VM86 */ #ifdef BDE_DEBUGGER /* * Adjust as much as possible for paging before enabling paging so that the * adjustments can be traced. */ call bdb_prepare_paging #endif /* Now enable paging */ movl R(_IdlePTD), %eax movl %eax,%cr3 /* load ptd addr into mmu */ movl %cr0,%eax /* get control word */ orl $CR0_PE|CR0_PG,%eax /* enable paging */ movl %eax,%cr0 /* and let's page NOW! */ #ifdef BDE_DEBUGGER /* * Complete the adjustments for paging so that we can keep tracing through * initi386() after the low (physical) addresses for the gdt and idt become * invalid. */ call bdb_commit_paging #endif pushl $begin /* jump to high virtualized address */ ret /* now running relocated at KERNBASE where the system is linked to run */ begin: /* set up bootstrap stack */ movl _proc0paddr,%esp /* location of in-kernel pages */ addl $UPAGES*PAGE_SIZE,%esp /* bootstrap stack end location */ xorl %eax,%eax /* mark end of frames */ movl %eax,%ebp movl _proc0paddr,%eax movl _IdlePTD, %esi movl %esi,PCB_CR3(%eax) movl $_proc0,_curproc movl physfree, %esi pushl %esi /* value of first for init386(first) */ call _init386 /* wire 386 chip for unix operation */ popl %esi .globl __ucodesel,__udatasel pushl $0 /* unused */ pushl __udatasel /* ss */ pushl $0 /* esp - filled in by execve() */ pushl $PSL_USER /* eflags (IOPL 0, int enab) */ pushl __ucodesel /* cs */ pushl $0 /* eip - filled in by execve() */ subl $(12*4),%esp /* space for rest of registers */ pushl %esp /* call main with frame pointer */ call _main /* autoconfiguration, mountroot etc */ hlt /* never returns to here */ /* * When starting init, call this to configure the process for user * mode. This will be inherited by other processes. */ NON_GPROF_ENTRY(prepare_usermode) /* * Now we've run main() and determined what cpu-type we are, we can * enable write protection and alignment checking on i486 cpus and * above. */ #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) cmpl $CPUCLASS_386,_cpu_class je 1f movl %cr0,%eax /* get control word */ orl $CR0_WP|CR0_AM,%eax /* enable i486 features */ movl %eax,%cr0 /* and do it */ 1: #endif /* * on return from main(), we are process 1 * set up address space and stack so that we can 'return' to user mode */ movl __ucodesel,%eax movl __udatasel,%ecx #if 0 movl %cx,%ds #endif movl %cx,%es movl %ax,%fs /* double map cs to fs */ movl %cx,%gs /* and ds to gs */ ret /* goto user! */ #define LCALL(x,y) .byte 0x9a ; .long y ; .word x /* * Signal trampoline, copied to top of user stack */ NON_GPROF_ENTRY(sigcode) call SIGF_HANDLER(%esp) lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */ /* copy at 8(%esp)) */ pushl %eax pushl %eax /* junk to fake return address */ movl $SYS_sigreturn,%eax /* sigreturn() */ LCALL(0x7,0) /* enter kernel with args on stack */ hlt /* never gets here */ ALIGN_TEXT _esigcode: .data .globl _szsigcode _szsigcode: .long _esigcode-_sigcode .text /********************************************************************** * * Recover the bootinfo passed to us from the boot program * */ recover_bootinfo: /* * This code is called in different ways depending on what loaded * and started the kernel. This is used to detect how we get the * arguments from the other code and what we do with them. * * Old disk boot blocks: * (*btext)(howto, bootdev, cyloffset, esym); * [return address == 0, and can NOT be returned to] * [cyloffset was not supported by the FreeBSD boot code * and always passed in as 0] * [esym is also known as total in the boot code, and * was never properly supported by the FreeBSD boot code] * * Old diskless netboot code: * (*btext)(0,0,0,0,&nfsdiskless,0,0,0); * [return address != 0, and can NOT be returned to] * If we are being booted by this code it will NOT work, * so we are just going to halt if we find this case. * * New uniform boot code: * (*btext)(howto, bootdev, 0, 0, 0, &bootinfo) * [return address != 0, and can be returned to] * * There may seem to be a lot of wasted arguments in here, but * that is so the newer boot code can still load very old kernels * and old boot code can load new kernels. */ /* * The old style disk boot blocks fake a frame on the stack and * did an lret to get here. The frame on the stack has a return * address of 0. */ cmpl $0,4(%ebp) je olddiskboot /* * We have some form of return address, so this is either the * old diskless netboot code, or the new uniform code. That can * be detected by looking at the 5th argument, if it is 0 * we are being booted by the new uniform boot code. */ cmpl $0,24(%ebp) je newboot /* * Seems we have been loaded by the old diskless boot code, we * don't stand a chance of running as the diskless structure * changed considerably between the two, so just halt. */ hlt /* * We have been loaded by the new uniform boot code. * Let's check the bootinfo version, and if we do not understand * it we return to the loader with a status of 1 to indicate this error */ newboot: movl 28(%ebp),%ebx /* &bootinfo.version */ movl BI_VERSION(%ebx),%eax cmpl $1,%eax /* We only understand version 1 */ je 1f movl $1,%eax /* Return status */ leave /* * XXX this returns to our caller's caller (as is required) since * we didn't set up a frame and our caller did. */ ret 1: /* * If we have a kernelname copy it in */ movl BI_KERNELNAME(%ebx),%esi cmpl $0,%esi je 2f /* No kernelname */ movl $MAXPATHLEN,%ecx /* Brute force!!! */ movl $R(_kernelname),%edi cmpb $'/',(%esi) /* Make sure it starts with a slash */ je 1f movb $'/',(%edi) incl %edi decl %ecx 1: cld rep movsb 2: /* * Determine the size of the boot loader's copy of the bootinfo * struct. This is impossible to do properly because old versions * of the struct don't contain a size field and there are 2 old * versions with the same version number. */ movl $BI_ENDCOMMON,%ecx /* prepare for sizeless version */ testl $RB_BOOTINFO,8(%ebp) /* bi_size (and bootinfo) valid? */ je got_bi_size /* no, sizeless version */ movl BI_SIZE(%ebx),%ecx got_bi_size: /* * Copy the common part of the bootinfo struct */ movl %ebx,%esi movl $R(_bootinfo),%edi cmpl $BOOTINFO_SIZE,%ecx jbe got_common_bi_size movl $BOOTINFO_SIZE,%ecx got_common_bi_size: cld rep movsb #ifdef NFS #ifndef BOOTP_NFSV3 /* * If we have a nfs_diskless structure copy it in */ movl BI_NFS_DISKLESS(%ebx),%esi cmpl $0,%esi je olddiskboot movl $R(_nfs_diskless),%edi movl $NFSDISKLESS_SIZE,%ecx cld rep movsb movl $R(_nfs_diskless_valid),%edi movl $1,(%edi) #endif #endif /* * The old style disk boot. * (*btext)(howto, bootdev, cyloffset, esym); * Note that the newer boot code just falls into here to pick * up howto and bootdev, cyloffset and esym are no longer used */ olddiskboot: movl 8(%ebp),%eax movl %eax,R(_boothowto) movl 12(%ebp),%eax movl %eax,R(_bootdev) #if defined(USERCONFIG_BOOT) && defined(USERCONFIG) movl $0x10200, %esi movl $R(_userconfig_from_boot),%edi movl $512,%ecx cld rep movsb #endif /* USERCONFIG_BOOT */ ret /********************************************************************** * * Identify the CPU and initialize anything special about it * */ identify_cpu: /* Try to toggle alignment check flag; does not exist on 386. */ pushfl popl %eax movl %eax,%ecx orl $PSL_AC,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_AC,%eax pushl %ecx popfl testl %eax,%eax jnz try486 /* NexGen CPU does not have aligment check flag. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jz trynexgen popfl movl $CPU_386,R(_cpu) jmp 3f trynexgen: popfl movl $CPU_NX586,R(_cpu) movl $0x4778654e,R(_cpu_vendor) # store vendor string movl $0x72446e65,R(_cpu_vendor+4) movl $0x6e657669,R(_cpu_vendor+8) movl $0,R(_cpu_vendor+12) jmp 3f try486: /* Try to toggle identification flag; does not exist on early 486s. */ pushfl popl %eax movl %eax,%ecx xorl $PSL_ID,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_ID,%eax pushl %ecx popfl testl %eax,%eax jnz trycpuid movl $CPU_486,R(_cpu) /* * Check Cyrix CPU * Cyrix CPUs do not change the undefined flags following * execution of the divide instruction which divides 5 by 2. * * Note: CPUID is enabled on M2, so it passes another way. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jnc trycyrix popfl jmp 3f /* You may use Intel CPU. */ trycyrix: popfl /* * IBM Bluelighting CPU also doesn't change the undefined flags. * Because IBM doesn't disclose the information for Bluelighting * CPU, we couldn't distinguish it from Cyrix's (including IBM * brand of Cyrix CPUs). */ movl $0x69727943,R(_cpu_vendor) # store vendor string movl $0x736e4978,R(_cpu_vendor+4) movl $0x64616574,R(_cpu_vendor+8) jmp 3f trycpuid: /* Use the `cpuid' instruction. */ xorl %eax,%eax .byte 0x0f,0xa2 # cpuid 0 movl %eax,R(_cpu_high) # highest capability movl %ebx,R(_cpu_vendor) # store vendor string movl %edx,R(_cpu_vendor+4) movl %ecx,R(_cpu_vendor+8) movb $0,R(_cpu_vendor+12) movl $1,%eax .byte 0x0f,0xa2 # cpuid 1 movl %eax,R(_cpu_id) # store cpu_id movl %edx,R(_cpu_feature) # store cpu_feature rorl $8,%eax # extract family type andl $15,%eax cmpl $5,%eax jae 1f /* less than Pentium; must be 486 */ movl $CPU_486,R(_cpu) jmp 3f 1: /* a Pentium? */ cmpl $5,%eax jne 2f movl $CPU_586,R(_cpu) jmp 3f 2: /* Greater than Pentium...call it a Pentium Pro */ movl $CPU_686,R(_cpu) 3: ret /********************************************************************** * * Create the first page directory and its page tables. * */ create_pagetables: testl $CPUID_PGE, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_PGE, %eax movl %eax, %cr4 1: /* Find end of kernel image (rounded up to a page boundary). */ movl $R(_end),%esi /* include symbols in "kernel image" if they are loaded and useful */ #ifdef DDB movl R(_bootinfo+BI_ESYMTAB),%edi testl %edi,%edi je over_symalloc movl %edi,%esi movl $KERNBASE,%edi addl %edi,R(_bootinfo+BI_SYMTAB) addl %edi,R(_bootinfo+BI_ESYMTAB) over_symalloc: #endif addl $PAGE_MASK,%esi andl $~PAGE_MASK,%esi movl %esi,R(_KERNend) /* save end of kernel */ movl %esi,R(physfree) /* next free page is at end of kernel */ /* Allocate Kernel Page Tables */ ALLOCPAGES(NKPT) movl %esi,R(_KPTphys) /* Allocate Page Table Directory */ ALLOCPAGES(1) movl %esi,R(_IdlePTD) /* Allocate UPAGES */ ALLOCPAGES(UPAGES) movl %esi,R(p0upa) addl $KERNBASE, %esi movl %esi, R(_proc0paddr) #ifdef VM86 - ALLOCPAGES(4) /* IOPAGES + ext + stack */ + ALLOCPAGES(1) /* vm86/bios stack */ + movl %esi,R(vm86phystk) + + ALLOCPAGES(3) /* pgtable + ext + IOPAGES */ movl %esi,R(_vm86pa) addl $KERNBASE, %esi movl %esi, R(_vm86paddr) #endif /* VM86 */ #ifdef SMP /* Allocate cpu0's private data page */ ALLOCPAGES(1) movl %esi,R(cpu0pp) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpage) /* relocated to KVM space */ /* Allocate cpu0's private page table for mapping priv page, apic, etc */ ALLOCPAGES(1) movl %esi,R(cpu0pt) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpt) /* relocated to KVM space */ #endif /* SMP */ /* Map read-only from zero to the end of the kernel text section */ xorl %eax, %eax #ifdef BDE_DEBUGGER /* If the debugger is present, actually map everything read-write. */ cmpl $0,R(_bdb_exists) jne map_read_write #endif xorl %edx,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 2f orl $PG_G,%edx #endif 2: movl $R(_etext),%ecx addl $PAGE_MASK,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map read-write, data, bss and symbols */ movl $R(_etext),%eax addl $PAGE_MASK, %eax andl $~PAGE_MASK, %eax map_read_write: movl $PG_RW,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 1f orl $PG_G,%edx #endif 1: movl R(_KERNend),%ecx subl %eax,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map page directory. */ movl R(_IdlePTD), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map proc0's UPAGES in the physical way ... */ movl R(p0upa), %eax movl $UPAGES, %ecx fillkptphys($PG_RW) /* Map ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkptphys($PG_RW) #ifdef VM86 /* Map space for the vm86 region */ - movl R(_vm86pa), %eax + movl R(vm86phystk), %eax movl $4, %ecx fillkptphys($PG_RW) /* Map page 0 into the vm86 page table */ movl $0, %eax movl $0, %ebx movl $1, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) /* ...likewise for the ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_START>>PAGE_SHIFT, %ebx movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) #endif /* VM86 */ #ifdef SMP /* Map cpu0's private page into global kmem (4K @ cpu0prvpage) */ movl R(cpu0pp), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map cpu0's private page table into global kmem FWIW */ movl R(cpu0pt), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map the private page into the private page table into private space */ movl R(cpu0pp), %eax movl $0, %ebx /* pte offset = 0 */ movl $1, %ecx /* one private page coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Map the page table page into private space */ movl R(cpu0pt), %eax movl $1, %ebx /* pte offset = 1 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* ... and put the page table table in the pde. */ movl R(cpu0pt), %eax movl $MPPTDI, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* Fakeup VA for the local apic to allow early traps. */ ALLOCPAGES(1) movl %esi, %eax movl $2, %ebx /* pte offset = 2 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Initialize mp lock to allow early traps */ movl $1, R(_mp_lock) /* Initialize my_idlePTD to IdlePTD */ movl R(cpu0pp), %eax movl R(_IdlePTD), %ecx movl %ecx,GD_MY_IDLEPTD(%eax) #endif /* SMP */ /* install a pde for temporary double map of bottom of VA */ movl R(_KPTphys), %eax xorl %ebx, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install pde's for pt's */ movl R(_KPTphys), %eax movl $KPTDI, %ebx movl $NKPT, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install a pde recursively mapping page directory as a page table */ movl R(_IdlePTD), %eax movl $PTDPTDI, %ebx movl $1,%ecx fillkpt(R(_IdlePTD), $PG_RW) ret #ifdef BDE_DEBUGGER bdb_prepare_paging: cmpl $0,R(_bdb_exists) je bdb_prepare_paging_exit subl $6,%esp /* * Copy and convert debugger entries from the bootstrap gdt and idt * to the kernel gdt and idt. Everything is still in low memory. * Tracing continues to work after paging is enabled because the * low memory addresses remain valid until everything is relocated. * However, tracing through the setidt() that initializes the trace * trap will crash. */ sgdt (%esp) movl 2(%esp),%esi /* base address of bootstrap gdt */ movl $R(_gdt),%edi movl %edi,2(%esp) /* prepare to load kernel gdt */ movl $8*18/4,%ecx cld rep /* copy gdt */ movsl movl $R(_gdt),-8+2(%edi) /* adjust gdt self-ptr */ movb $0x92,-8+5(%edi) lgdt (%esp) sidt (%esp) movl 2(%esp),%esi /* base address of current idt */ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */ movw 8(%esi),%ax movl %eax,R(bdb_dbg_ljmp+1) /* ... immediate offset ... */ movl 8+2(%esi),%eax movw %ax,R(bdb_dbg_ljmp+5) /* ... and selector for ljmp */ movl 24+4(%esi),%eax /* same for bpt descriptor */ movw 24(%esi),%ax movl %eax,R(bdb_bpt_ljmp+1) movl 24+2(%esi),%eax movw %ax,R(bdb_bpt_ljmp+5) movl $R(_idt),%edi movl %edi,2(%esp) /* prepare to load kernel idt */ movl $8*4/4,%ecx cld rep /* copy idt */ movsl lidt (%esp) addl $6,%esp bdb_prepare_paging_exit: ret /* Relocate debugger gdt entries and gdt and idt pointers. */ bdb_commit_paging: cmpl $0,_bdb_exists je bdb_commit_paging_exit movl $_gdt+8*9,%eax /* adjust slots 9-17 */ movl $9,%ecx reloc_gdt: movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */ addl $8,%eax /* now KERNBASE>>24 */ loop reloc_gdt subl $6,%esp sgdt (%esp) addl $KERNBASE,2(%esp) lgdt (%esp) sidt (%esp) addl $KERNBASE,2(%esp) lidt (%esp) addl $6,%esp int $3 bdb_commit_paging_exit: ret #endif /* BDE_DEBUGGER */ Index: head/sys/amd64/amd64/locore.s =================================================================== --- head/sys/amd64/amd64/locore.s (revision 37888) +++ head/sys/amd64/amd64/locore.s (revision 37889) @@ -1,1024 +1,1029 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)locore.s 7.3 (Berkeley) 5/13/91 - * $Id: locore.s,v 1.109 1998/06/21 18:02:34 bde Exp $ + * $Id: locore.s,v 1.110 1998/06/30 03:01:35 jmg Exp $ * * originally from: locore.s, by William F. Jolitz * * Substantially rewritten by David Greenman, Rod Grimes, * Bruce Evans, Wolfgang Solfrank, Poul-Henning Kamp * and many others. */ #include "apm.h" #include "opt_bootp.h" #include "opt_ddb.h" #include "opt_nfsroot.h" #include "opt_userconfig.h" #include "opt_vm86.h" #include #include #include #include #include #include #include #include "assym.s" /* * XXX * * Note: This version greatly munged to avoid various assembler errors * that may be fixed in newer versions of gas. Perhaps newer versions * will have more pleasant appearance. */ /* * PTmap is recursive pagemap at top of virtual address space. * Within PTmap, the page directory can be found (third indirection). */ .globl _PTmap,_PTD,_PTDpde .set _PTmap,(PTDPTDI << PDRSHIFT) .set _PTD,_PTmap + (PTDPTDI * PAGE_SIZE) .set _PTDpde,_PTD + (PTDPTDI * PDESIZE) /* * APTmap, APTD is the alternate recursive pagemap. * It's used when modifying another process's page tables. */ .globl _APTmap,_APTD,_APTDpde .set _APTmap,APTDPTDI << PDRSHIFT .set _APTD,_APTmap + (APTDPTDI * PAGE_SIZE) .set _APTDpde,_PTD + (APTDPTDI * PDESIZE) /* * Globals */ .data ALIGN_DATA /* just to be sure */ .globl HIDENAME(tmpstk) .space 0x2000 /* space for tmpstk - temporary stack */ HIDENAME(tmpstk): .globl _boothowto,_bootdev .globl _cpu,_cpu_vendor,_cpu_id,_bootinfo .globl _cpu_high, _cpu_feature _cpu: .long 0 /* are we 386, 386sx, or 486 */ _cpu_id: .long 0 /* stepping ID */ _cpu_high: .long 0 /* highest arg to CPUID */ _cpu_feature: .long 0 /* features */ _cpu_vendor: .space 20 /* CPU origin code */ _bootinfo: .space BOOTINFO_SIZE /* bootinfo that we can handle */ _KERNend: .long 0 /* phys addr end of kernel (just after bss) */ physfree: .long 0 /* phys addr of next free page */ #ifdef SMP cpu0pp: .long 0 /* phys addr cpu0 private pg */ cpu0pt: .long 0 /* phys addr cpu0 private pt */ .globl _cpu0prvpage,_cpu0prvpt _cpu0prvpage: .long 0 /* relocated version */ _cpu0prvpt: .long 0 /* relocated version */ #endif /* SMP */ .globl _IdlePTD _IdlePTD: .long 0 /* phys addr of kernel PTD */ #ifdef SMP .globl _KPTphys #endif _KPTphys: .long 0 /* phys addr of kernel page tables */ .globl _proc0paddr _proc0paddr: .long 0 /* address of proc 0 address space */ p0upa: .long 0 /* phys addr of proc0's UPAGES */ #ifdef VM86 +vm86phystk: .long 0 /* PA of vm86/bios stack */ + .globl _vm86paddr, _vm86pa _vm86paddr: .long 0 /* address of vm86 region */ _vm86pa: .long 0 /* phys addr of vm86 region */ #endif #ifdef BDE_DEBUGGER .globl _bdb_exists /* flag to indicate BDE debugger is present */ _bdb_exists: .long 0 #endif /********************************************************************** * * Some handy macros * */ #define R(foo) ((foo)-KERNBASE) #define ALLOCPAGES(foo) \ movl R(physfree), %esi ; \ movl $((foo)*PAGE_SIZE), %eax ; \ addl %esi, %eax ; \ movl %eax, R(physfree) ; \ movl %esi, %edi ; \ movl $((foo)*PAGE_SIZE),%ecx ; \ xorl %eax,%eax ; \ cld ; \ rep ; \ stosb /* * fillkpt * eax = page frame address * ebx = index into page table * ecx = how many pages to map * base = base address of page dir/table * prot = protection bits */ #define fillkpt(base, prot) \ shll $2,%ebx ; \ addl base,%ebx ; \ orl $PG_V,%eax ; \ orl prot,%eax ; \ 1: movl %eax,(%ebx) ; \ addl $PAGE_SIZE,%eax ; /* increment physical address */ \ addl $4,%ebx ; /* next pte */ \ loop 1b /* * fillkptphys(prot) * eax = physical address * ecx = how many pages to map * prot = protection bits */ #define fillkptphys(prot) \ movl %eax, %ebx ; \ shrl $PAGE_SHIFT, %ebx ; \ fillkpt(R(_KPTphys), prot) .text /********************************************************************** * * This is where the bootblocks start us, set the ball rolling... * */ NON_GPROF_ENTRY(btext) #ifdef PC98 jmp 1f .globl _pc98_system_parameter .org 0x400 _pc98_system_parameter: .space 0x240 /* BIOS parameter block */ 1: /* save SYSTEM PARAMETER for resume (NS/T or other) */ movl $0xa1000,%esi movl $0x100000,%edi movl $0x0630,%ecx cld rep movsb #else /* IBM-PC */ #ifdef BDE_DEBUGGER #ifdef BIOS_STEALS_3K cmpl $0x0375c339,0x95504 #else cmpl $0x0375c339,0x96104 /* XXX - debugger signature */ #endif jne 1f movb $1,R(_bdb_exists) 1: #endif /* Tell the bios to warmboot next time */ movw $0x1234,0x472 #endif /* PC98 */ /* Set up a real frame in case the double return in newboot is executed. */ pushl %ebp movl %esp, %ebp /* Don't trust what the BIOS gives for eflags. */ pushl $PSL_KERNEL popfl /* * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap * to set %cs, %ds, %es and %ss. */ mov %ds, %ax mov %ax, %fs mov %ax, %gs call recover_bootinfo /* Get onto a stack that we can trust. */ /* * XXX this step is delayed in case recover_bootinfo needs to return via * the old stack, but it need not be, since recover_bootinfo actually * returns via the old frame. */ movl $R(HIDENAME(tmpstk)),%esp #ifdef PC98 testb $0x02,0x100620 /* pc98_machine_type & M_EPSON_PC98 */ jz 3f cmpb $0x0b,0x100624 /* epson_machine_id <= 0x0b */ ja 3f /* count up memory */ movl $0x100000,%eax /* next, talley remaining memory */ movl $0xFFF-0x100,%ecx 1: movl 0(%eax),%ebx /* save location to check */ movl $0xa55a5aa5,0(%eax) /* write test pattern */ cmpl $0xa55a5aa5,0(%eax) /* does not check yet for rollover */ jne 2f movl %ebx,0(%eax) /* restore memory */ addl $PAGE_SIZE,%eax loop 1b 2: subl $0x100000,%eax shrl $17,%eax movb %al,0x100401 3: #endif call identify_cpu /* clear bss */ /* * XXX this should be done a little earlier. * * XXX we don't check that there is memory for our bss and page tables * before using it. * * XXX the boot program somewhat bogusly clears the bss. We still have * to do it in case we were unzipped by kzipboot. Then the boot program * only clears kzipboot's bss. * * XXX the gdt and idt are still somewhere in the boot program. We * depend on the convention that the boot program is below 1MB and we * are above 1MB to keep the gdt and idt away from the bss and page * tables. The idt is only used if BDE_DEBUGGER is enabled. */ movl $R(_end),%ecx movl $R(_edata),%edi subl %edi,%ecx xorl %eax,%eax cld rep stosb #if NAPM > 0 #ifndef VM86 /* * XXX it's not clear that APM can live in the current environonment. * Only pc-relative addressing works. */ call _apm_setup #endif #endif call create_pagetables #ifdef VM86 /* * If the CPU has support for VME, turn it on. */ testl $CPUID_VME, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_VME, %eax movl %eax, %cr4 1: #endif /* VM86 */ #ifdef BDE_DEBUGGER /* * Adjust as much as possible for paging before enabling paging so that the * adjustments can be traced. */ call bdb_prepare_paging #endif /* Now enable paging */ movl R(_IdlePTD), %eax movl %eax,%cr3 /* load ptd addr into mmu */ movl %cr0,%eax /* get control word */ orl $CR0_PE|CR0_PG,%eax /* enable paging */ movl %eax,%cr0 /* and let's page NOW! */ #ifdef BDE_DEBUGGER /* * Complete the adjustments for paging so that we can keep tracing through * initi386() after the low (physical) addresses for the gdt and idt become * invalid. */ call bdb_commit_paging #endif pushl $begin /* jump to high virtualized address */ ret /* now running relocated at KERNBASE where the system is linked to run */ begin: /* set up bootstrap stack */ movl _proc0paddr,%esp /* location of in-kernel pages */ addl $UPAGES*PAGE_SIZE,%esp /* bootstrap stack end location */ xorl %eax,%eax /* mark end of frames */ movl %eax,%ebp movl _proc0paddr,%eax movl _IdlePTD, %esi movl %esi,PCB_CR3(%eax) movl $_proc0,_curproc movl physfree, %esi pushl %esi /* value of first for init386(first) */ call _init386 /* wire 386 chip for unix operation */ popl %esi .globl __ucodesel,__udatasel pushl $0 /* unused */ pushl __udatasel /* ss */ pushl $0 /* esp - filled in by execve() */ pushl $PSL_USER /* eflags (IOPL 0, int enab) */ pushl __ucodesel /* cs */ pushl $0 /* eip - filled in by execve() */ subl $(12*4),%esp /* space for rest of registers */ pushl %esp /* call main with frame pointer */ call _main /* autoconfiguration, mountroot etc */ hlt /* never returns to here */ /* * When starting init, call this to configure the process for user * mode. This will be inherited by other processes. */ NON_GPROF_ENTRY(prepare_usermode) /* * Now we've run main() and determined what cpu-type we are, we can * enable write protection and alignment checking on i486 cpus and * above. */ #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) cmpl $CPUCLASS_386,_cpu_class je 1f movl %cr0,%eax /* get control word */ orl $CR0_WP|CR0_AM,%eax /* enable i486 features */ movl %eax,%cr0 /* and do it */ 1: #endif /* * on return from main(), we are process 1 * set up address space and stack so that we can 'return' to user mode */ movl __ucodesel,%eax movl __udatasel,%ecx #if 0 movl %cx,%ds #endif movl %cx,%es movl %ax,%fs /* double map cs to fs */ movl %cx,%gs /* and ds to gs */ ret /* goto user! */ #define LCALL(x,y) .byte 0x9a ; .long y ; .word x /* * Signal trampoline, copied to top of user stack */ NON_GPROF_ENTRY(sigcode) call SIGF_HANDLER(%esp) lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */ /* copy at 8(%esp)) */ pushl %eax pushl %eax /* junk to fake return address */ movl $SYS_sigreturn,%eax /* sigreturn() */ LCALL(0x7,0) /* enter kernel with args on stack */ hlt /* never gets here */ ALIGN_TEXT _esigcode: .data .globl _szsigcode _szsigcode: .long _esigcode-_sigcode .text /********************************************************************** * * Recover the bootinfo passed to us from the boot program * */ recover_bootinfo: /* * This code is called in different ways depending on what loaded * and started the kernel. This is used to detect how we get the * arguments from the other code and what we do with them. * * Old disk boot blocks: * (*btext)(howto, bootdev, cyloffset, esym); * [return address == 0, and can NOT be returned to] * [cyloffset was not supported by the FreeBSD boot code * and always passed in as 0] * [esym is also known as total in the boot code, and * was never properly supported by the FreeBSD boot code] * * Old diskless netboot code: * (*btext)(0,0,0,0,&nfsdiskless,0,0,0); * [return address != 0, and can NOT be returned to] * If we are being booted by this code it will NOT work, * so we are just going to halt if we find this case. * * New uniform boot code: * (*btext)(howto, bootdev, 0, 0, 0, &bootinfo) * [return address != 0, and can be returned to] * * There may seem to be a lot of wasted arguments in here, but * that is so the newer boot code can still load very old kernels * and old boot code can load new kernels. */ /* * The old style disk boot blocks fake a frame on the stack and * did an lret to get here. The frame on the stack has a return * address of 0. */ cmpl $0,4(%ebp) je olddiskboot /* * We have some form of return address, so this is either the * old diskless netboot code, or the new uniform code. That can * be detected by looking at the 5th argument, if it is 0 * we are being booted by the new uniform boot code. */ cmpl $0,24(%ebp) je newboot /* * Seems we have been loaded by the old diskless boot code, we * don't stand a chance of running as the diskless structure * changed considerably between the two, so just halt. */ hlt /* * We have been loaded by the new uniform boot code. * Let's check the bootinfo version, and if we do not understand * it we return to the loader with a status of 1 to indicate this error */ newboot: movl 28(%ebp),%ebx /* &bootinfo.version */ movl BI_VERSION(%ebx),%eax cmpl $1,%eax /* We only understand version 1 */ je 1f movl $1,%eax /* Return status */ leave /* * XXX this returns to our caller's caller (as is required) since * we didn't set up a frame and our caller did. */ ret 1: /* * If we have a kernelname copy it in */ movl BI_KERNELNAME(%ebx),%esi cmpl $0,%esi je 2f /* No kernelname */ movl $MAXPATHLEN,%ecx /* Brute force!!! */ movl $R(_kernelname),%edi cmpb $'/',(%esi) /* Make sure it starts with a slash */ je 1f movb $'/',(%edi) incl %edi decl %ecx 1: cld rep movsb 2: /* * Determine the size of the boot loader's copy of the bootinfo * struct. This is impossible to do properly because old versions * of the struct don't contain a size field and there are 2 old * versions with the same version number. */ movl $BI_ENDCOMMON,%ecx /* prepare for sizeless version */ testl $RB_BOOTINFO,8(%ebp) /* bi_size (and bootinfo) valid? */ je got_bi_size /* no, sizeless version */ movl BI_SIZE(%ebx),%ecx got_bi_size: /* * Copy the common part of the bootinfo struct */ movl %ebx,%esi movl $R(_bootinfo),%edi cmpl $BOOTINFO_SIZE,%ecx jbe got_common_bi_size movl $BOOTINFO_SIZE,%ecx got_common_bi_size: cld rep movsb #ifdef NFS #ifndef BOOTP_NFSV3 /* * If we have a nfs_diskless structure copy it in */ movl BI_NFS_DISKLESS(%ebx),%esi cmpl $0,%esi je olddiskboot movl $R(_nfs_diskless),%edi movl $NFSDISKLESS_SIZE,%ecx cld rep movsb movl $R(_nfs_diskless_valid),%edi movl $1,(%edi) #endif #endif /* * The old style disk boot. * (*btext)(howto, bootdev, cyloffset, esym); * Note that the newer boot code just falls into here to pick * up howto and bootdev, cyloffset and esym are no longer used */ olddiskboot: movl 8(%ebp),%eax movl %eax,R(_boothowto) movl 12(%ebp),%eax movl %eax,R(_bootdev) #if defined(USERCONFIG_BOOT) && defined(USERCONFIG) movl $0x10200, %esi movl $R(_userconfig_from_boot),%edi movl $512,%ecx cld rep movsb #endif /* USERCONFIG_BOOT */ ret /********************************************************************** * * Identify the CPU and initialize anything special about it * */ identify_cpu: /* Try to toggle alignment check flag; does not exist on 386. */ pushfl popl %eax movl %eax,%ecx orl $PSL_AC,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_AC,%eax pushl %ecx popfl testl %eax,%eax jnz try486 /* NexGen CPU does not have aligment check flag. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jz trynexgen popfl movl $CPU_386,R(_cpu) jmp 3f trynexgen: popfl movl $CPU_NX586,R(_cpu) movl $0x4778654e,R(_cpu_vendor) # store vendor string movl $0x72446e65,R(_cpu_vendor+4) movl $0x6e657669,R(_cpu_vendor+8) movl $0,R(_cpu_vendor+12) jmp 3f try486: /* Try to toggle identification flag; does not exist on early 486s. */ pushfl popl %eax movl %eax,%ecx xorl $PSL_ID,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_ID,%eax pushl %ecx popfl testl %eax,%eax jnz trycpuid movl $CPU_486,R(_cpu) /* * Check Cyrix CPU * Cyrix CPUs do not change the undefined flags following * execution of the divide instruction which divides 5 by 2. * * Note: CPUID is enabled on M2, so it passes another way. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jnc trycyrix popfl jmp 3f /* You may use Intel CPU. */ trycyrix: popfl /* * IBM Bluelighting CPU also doesn't change the undefined flags. * Because IBM doesn't disclose the information for Bluelighting * CPU, we couldn't distinguish it from Cyrix's (including IBM * brand of Cyrix CPUs). */ movl $0x69727943,R(_cpu_vendor) # store vendor string movl $0x736e4978,R(_cpu_vendor+4) movl $0x64616574,R(_cpu_vendor+8) jmp 3f trycpuid: /* Use the `cpuid' instruction. */ xorl %eax,%eax .byte 0x0f,0xa2 # cpuid 0 movl %eax,R(_cpu_high) # highest capability movl %ebx,R(_cpu_vendor) # store vendor string movl %edx,R(_cpu_vendor+4) movl %ecx,R(_cpu_vendor+8) movb $0,R(_cpu_vendor+12) movl $1,%eax .byte 0x0f,0xa2 # cpuid 1 movl %eax,R(_cpu_id) # store cpu_id movl %edx,R(_cpu_feature) # store cpu_feature rorl $8,%eax # extract family type andl $15,%eax cmpl $5,%eax jae 1f /* less than Pentium; must be 486 */ movl $CPU_486,R(_cpu) jmp 3f 1: /* a Pentium? */ cmpl $5,%eax jne 2f movl $CPU_586,R(_cpu) jmp 3f 2: /* Greater than Pentium...call it a Pentium Pro */ movl $CPU_686,R(_cpu) 3: ret /********************************************************************** * * Create the first page directory and its page tables. * */ create_pagetables: testl $CPUID_PGE, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_PGE, %eax movl %eax, %cr4 1: /* Find end of kernel image (rounded up to a page boundary). */ movl $R(_end),%esi /* include symbols in "kernel image" if they are loaded and useful */ #ifdef DDB movl R(_bootinfo+BI_ESYMTAB),%edi testl %edi,%edi je over_symalloc movl %edi,%esi movl $KERNBASE,%edi addl %edi,R(_bootinfo+BI_SYMTAB) addl %edi,R(_bootinfo+BI_ESYMTAB) over_symalloc: #endif addl $PAGE_MASK,%esi andl $~PAGE_MASK,%esi movl %esi,R(_KERNend) /* save end of kernel */ movl %esi,R(physfree) /* next free page is at end of kernel */ /* Allocate Kernel Page Tables */ ALLOCPAGES(NKPT) movl %esi,R(_KPTphys) /* Allocate Page Table Directory */ ALLOCPAGES(1) movl %esi,R(_IdlePTD) /* Allocate UPAGES */ ALLOCPAGES(UPAGES) movl %esi,R(p0upa) addl $KERNBASE, %esi movl %esi, R(_proc0paddr) #ifdef VM86 - ALLOCPAGES(4) /* IOPAGES + ext + stack */ + ALLOCPAGES(1) /* vm86/bios stack */ + movl %esi,R(vm86phystk) + + ALLOCPAGES(3) /* pgtable + ext + IOPAGES */ movl %esi,R(_vm86pa) addl $KERNBASE, %esi movl %esi, R(_vm86paddr) #endif /* VM86 */ #ifdef SMP /* Allocate cpu0's private data page */ ALLOCPAGES(1) movl %esi,R(cpu0pp) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpage) /* relocated to KVM space */ /* Allocate cpu0's private page table for mapping priv page, apic, etc */ ALLOCPAGES(1) movl %esi,R(cpu0pt) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpt) /* relocated to KVM space */ #endif /* SMP */ /* Map read-only from zero to the end of the kernel text section */ xorl %eax, %eax #ifdef BDE_DEBUGGER /* If the debugger is present, actually map everything read-write. */ cmpl $0,R(_bdb_exists) jne map_read_write #endif xorl %edx,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 2f orl $PG_G,%edx #endif 2: movl $R(_etext),%ecx addl $PAGE_MASK,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map read-write, data, bss and symbols */ movl $R(_etext),%eax addl $PAGE_MASK, %eax andl $~PAGE_MASK, %eax map_read_write: movl $PG_RW,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 1f orl $PG_G,%edx #endif 1: movl R(_KERNend),%ecx subl %eax,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map page directory. */ movl R(_IdlePTD), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map proc0's UPAGES in the physical way ... */ movl R(p0upa), %eax movl $UPAGES, %ecx fillkptphys($PG_RW) /* Map ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkptphys($PG_RW) #ifdef VM86 /* Map space for the vm86 region */ - movl R(_vm86pa), %eax + movl R(vm86phystk), %eax movl $4, %ecx fillkptphys($PG_RW) /* Map page 0 into the vm86 page table */ movl $0, %eax movl $0, %ebx movl $1, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) /* ...likewise for the ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_START>>PAGE_SHIFT, %ebx movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) #endif /* VM86 */ #ifdef SMP /* Map cpu0's private page into global kmem (4K @ cpu0prvpage) */ movl R(cpu0pp), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map cpu0's private page table into global kmem FWIW */ movl R(cpu0pt), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map the private page into the private page table into private space */ movl R(cpu0pp), %eax movl $0, %ebx /* pte offset = 0 */ movl $1, %ecx /* one private page coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Map the page table page into private space */ movl R(cpu0pt), %eax movl $1, %ebx /* pte offset = 1 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* ... and put the page table table in the pde. */ movl R(cpu0pt), %eax movl $MPPTDI, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* Fakeup VA for the local apic to allow early traps. */ ALLOCPAGES(1) movl %esi, %eax movl $2, %ebx /* pte offset = 2 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Initialize mp lock to allow early traps */ movl $1, R(_mp_lock) /* Initialize my_idlePTD to IdlePTD */ movl R(cpu0pp), %eax movl R(_IdlePTD), %ecx movl %ecx,GD_MY_IDLEPTD(%eax) #endif /* SMP */ /* install a pde for temporary double map of bottom of VA */ movl R(_KPTphys), %eax xorl %ebx, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install pde's for pt's */ movl R(_KPTphys), %eax movl $KPTDI, %ebx movl $NKPT, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install a pde recursively mapping page directory as a page table */ movl R(_IdlePTD), %eax movl $PTDPTDI, %ebx movl $1,%ecx fillkpt(R(_IdlePTD), $PG_RW) ret #ifdef BDE_DEBUGGER bdb_prepare_paging: cmpl $0,R(_bdb_exists) je bdb_prepare_paging_exit subl $6,%esp /* * Copy and convert debugger entries from the bootstrap gdt and idt * to the kernel gdt and idt. Everything is still in low memory. * Tracing continues to work after paging is enabled because the * low memory addresses remain valid until everything is relocated. * However, tracing through the setidt() that initializes the trace * trap will crash. */ sgdt (%esp) movl 2(%esp),%esi /* base address of bootstrap gdt */ movl $R(_gdt),%edi movl %edi,2(%esp) /* prepare to load kernel gdt */ movl $8*18/4,%ecx cld rep /* copy gdt */ movsl movl $R(_gdt),-8+2(%edi) /* adjust gdt self-ptr */ movb $0x92,-8+5(%edi) lgdt (%esp) sidt (%esp) movl 2(%esp),%esi /* base address of current idt */ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */ movw 8(%esi),%ax movl %eax,R(bdb_dbg_ljmp+1) /* ... immediate offset ... */ movl 8+2(%esi),%eax movw %ax,R(bdb_dbg_ljmp+5) /* ... and selector for ljmp */ movl 24+4(%esi),%eax /* same for bpt descriptor */ movw 24(%esi),%ax movl %eax,R(bdb_bpt_ljmp+1) movl 24+2(%esi),%eax movw %ax,R(bdb_bpt_ljmp+5) movl $R(_idt),%edi movl %edi,2(%esp) /* prepare to load kernel idt */ movl $8*4/4,%ecx cld rep /* copy idt */ movsl lidt (%esp) addl $6,%esp bdb_prepare_paging_exit: ret /* Relocate debugger gdt entries and gdt and idt pointers. */ bdb_commit_paging: cmpl $0,_bdb_exists je bdb_commit_paging_exit movl $_gdt+8*9,%eax /* adjust slots 9-17 */ movl $9,%ecx reloc_gdt: movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */ addl $8,%eax /* now KERNBASE>>24 */ loop reloc_gdt subl $6,%esp sgdt (%esp) addl $KERNBASE,2(%esp) lgdt (%esp) sidt (%esp) addl $KERNBASE,2(%esp) lidt (%esp) addl $6,%esp int $3 bdb_commit_paging_exit: ret #endif /* BDE_DEBUGGER */ Index: head/sys/i386/i386/locore.s =================================================================== --- head/sys/i386/i386/locore.s (revision 37888) +++ head/sys/i386/i386/locore.s (revision 37889) @@ -1,1024 +1,1029 @@ /*- * Copyright (c) 1990 The Regents of the University of California. * All rights reserved. * * This code is derived from software contributed to Berkeley by * William Jolitz. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. All advertising materials mentioning features or use of this software * must display the following acknowledgement: * This product includes software developed by the University of * California, Berkeley and its contributors. * 4. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)locore.s 7.3 (Berkeley) 5/13/91 - * $Id: locore.s,v 1.109 1998/06/21 18:02:34 bde Exp $ + * $Id: locore.s,v 1.110 1998/06/30 03:01:35 jmg Exp $ * * originally from: locore.s, by William F. Jolitz * * Substantially rewritten by David Greenman, Rod Grimes, * Bruce Evans, Wolfgang Solfrank, Poul-Henning Kamp * and many others. */ #include "apm.h" #include "opt_bootp.h" #include "opt_ddb.h" #include "opt_nfsroot.h" #include "opt_userconfig.h" #include "opt_vm86.h" #include #include #include #include #include #include #include #include "assym.s" /* * XXX * * Note: This version greatly munged to avoid various assembler errors * that may be fixed in newer versions of gas. Perhaps newer versions * will have more pleasant appearance. */ /* * PTmap is recursive pagemap at top of virtual address space. * Within PTmap, the page directory can be found (third indirection). */ .globl _PTmap,_PTD,_PTDpde .set _PTmap,(PTDPTDI << PDRSHIFT) .set _PTD,_PTmap + (PTDPTDI * PAGE_SIZE) .set _PTDpde,_PTD + (PTDPTDI * PDESIZE) /* * APTmap, APTD is the alternate recursive pagemap. * It's used when modifying another process's page tables. */ .globl _APTmap,_APTD,_APTDpde .set _APTmap,APTDPTDI << PDRSHIFT .set _APTD,_APTmap + (APTDPTDI * PAGE_SIZE) .set _APTDpde,_PTD + (APTDPTDI * PDESIZE) /* * Globals */ .data ALIGN_DATA /* just to be sure */ .globl HIDENAME(tmpstk) .space 0x2000 /* space for tmpstk - temporary stack */ HIDENAME(tmpstk): .globl _boothowto,_bootdev .globl _cpu,_cpu_vendor,_cpu_id,_bootinfo .globl _cpu_high, _cpu_feature _cpu: .long 0 /* are we 386, 386sx, or 486 */ _cpu_id: .long 0 /* stepping ID */ _cpu_high: .long 0 /* highest arg to CPUID */ _cpu_feature: .long 0 /* features */ _cpu_vendor: .space 20 /* CPU origin code */ _bootinfo: .space BOOTINFO_SIZE /* bootinfo that we can handle */ _KERNend: .long 0 /* phys addr end of kernel (just after bss) */ physfree: .long 0 /* phys addr of next free page */ #ifdef SMP cpu0pp: .long 0 /* phys addr cpu0 private pg */ cpu0pt: .long 0 /* phys addr cpu0 private pt */ .globl _cpu0prvpage,_cpu0prvpt _cpu0prvpage: .long 0 /* relocated version */ _cpu0prvpt: .long 0 /* relocated version */ #endif /* SMP */ .globl _IdlePTD _IdlePTD: .long 0 /* phys addr of kernel PTD */ #ifdef SMP .globl _KPTphys #endif _KPTphys: .long 0 /* phys addr of kernel page tables */ .globl _proc0paddr _proc0paddr: .long 0 /* address of proc 0 address space */ p0upa: .long 0 /* phys addr of proc0's UPAGES */ #ifdef VM86 +vm86phystk: .long 0 /* PA of vm86/bios stack */ + .globl _vm86paddr, _vm86pa _vm86paddr: .long 0 /* address of vm86 region */ _vm86pa: .long 0 /* phys addr of vm86 region */ #endif #ifdef BDE_DEBUGGER .globl _bdb_exists /* flag to indicate BDE debugger is present */ _bdb_exists: .long 0 #endif /********************************************************************** * * Some handy macros * */ #define R(foo) ((foo)-KERNBASE) #define ALLOCPAGES(foo) \ movl R(physfree), %esi ; \ movl $((foo)*PAGE_SIZE), %eax ; \ addl %esi, %eax ; \ movl %eax, R(physfree) ; \ movl %esi, %edi ; \ movl $((foo)*PAGE_SIZE),%ecx ; \ xorl %eax,%eax ; \ cld ; \ rep ; \ stosb /* * fillkpt * eax = page frame address * ebx = index into page table * ecx = how many pages to map * base = base address of page dir/table * prot = protection bits */ #define fillkpt(base, prot) \ shll $2,%ebx ; \ addl base,%ebx ; \ orl $PG_V,%eax ; \ orl prot,%eax ; \ 1: movl %eax,(%ebx) ; \ addl $PAGE_SIZE,%eax ; /* increment physical address */ \ addl $4,%ebx ; /* next pte */ \ loop 1b /* * fillkptphys(prot) * eax = physical address * ecx = how many pages to map * prot = protection bits */ #define fillkptphys(prot) \ movl %eax, %ebx ; \ shrl $PAGE_SHIFT, %ebx ; \ fillkpt(R(_KPTphys), prot) .text /********************************************************************** * * This is where the bootblocks start us, set the ball rolling... * */ NON_GPROF_ENTRY(btext) #ifdef PC98 jmp 1f .globl _pc98_system_parameter .org 0x400 _pc98_system_parameter: .space 0x240 /* BIOS parameter block */ 1: /* save SYSTEM PARAMETER for resume (NS/T or other) */ movl $0xa1000,%esi movl $0x100000,%edi movl $0x0630,%ecx cld rep movsb #else /* IBM-PC */ #ifdef BDE_DEBUGGER #ifdef BIOS_STEALS_3K cmpl $0x0375c339,0x95504 #else cmpl $0x0375c339,0x96104 /* XXX - debugger signature */ #endif jne 1f movb $1,R(_bdb_exists) 1: #endif /* Tell the bios to warmboot next time */ movw $0x1234,0x472 #endif /* PC98 */ /* Set up a real frame in case the double return in newboot is executed. */ pushl %ebp movl %esp, %ebp /* Don't trust what the BIOS gives for eflags. */ pushl $PSL_KERNEL popfl /* * Don't trust what the BIOS gives for %fs and %gs. Trust the bootstrap * to set %cs, %ds, %es and %ss. */ mov %ds, %ax mov %ax, %fs mov %ax, %gs call recover_bootinfo /* Get onto a stack that we can trust. */ /* * XXX this step is delayed in case recover_bootinfo needs to return via * the old stack, but it need not be, since recover_bootinfo actually * returns via the old frame. */ movl $R(HIDENAME(tmpstk)),%esp #ifdef PC98 testb $0x02,0x100620 /* pc98_machine_type & M_EPSON_PC98 */ jz 3f cmpb $0x0b,0x100624 /* epson_machine_id <= 0x0b */ ja 3f /* count up memory */ movl $0x100000,%eax /* next, talley remaining memory */ movl $0xFFF-0x100,%ecx 1: movl 0(%eax),%ebx /* save location to check */ movl $0xa55a5aa5,0(%eax) /* write test pattern */ cmpl $0xa55a5aa5,0(%eax) /* does not check yet for rollover */ jne 2f movl %ebx,0(%eax) /* restore memory */ addl $PAGE_SIZE,%eax loop 1b 2: subl $0x100000,%eax shrl $17,%eax movb %al,0x100401 3: #endif call identify_cpu /* clear bss */ /* * XXX this should be done a little earlier. * * XXX we don't check that there is memory for our bss and page tables * before using it. * * XXX the boot program somewhat bogusly clears the bss. We still have * to do it in case we were unzipped by kzipboot. Then the boot program * only clears kzipboot's bss. * * XXX the gdt and idt are still somewhere in the boot program. We * depend on the convention that the boot program is below 1MB and we * are above 1MB to keep the gdt and idt away from the bss and page * tables. The idt is only used if BDE_DEBUGGER is enabled. */ movl $R(_end),%ecx movl $R(_edata),%edi subl %edi,%ecx xorl %eax,%eax cld rep stosb #if NAPM > 0 #ifndef VM86 /* * XXX it's not clear that APM can live in the current environonment. * Only pc-relative addressing works. */ call _apm_setup #endif #endif call create_pagetables #ifdef VM86 /* * If the CPU has support for VME, turn it on. */ testl $CPUID_VME, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_VME, %eax movl %eax, %cr4 1: #endif /* VM86 */ #ifdef BDE_DEBUGGER /* * Adjust as much as possible for paging before enabling paging so that the * adjustments can be traced. */ call bdb_prepare_paging #endif /* Now enable paging */ movl R(_IdlePTD), %eax movl %eax,%cr3 /* load ptd addr into mmu */ movl %cr0,%eax /* get control word */ orl $CR0_PE|CR0_PG,%eax /* enable paging */ movl %eax,%cr0 /* and let's page NOW! */ #ifdef BDE_DEBUGGER /* * Complete the adjustments for paging so that we can keep tracing through * initi386() after the low (physical) addresses for the gdt and idt become * invalid. */ call bdb_commit_paging #endif pushl $begin /* jump to high virtualized address */ ret /* now running relocated at KERNBASE where the system is linked to run */ begin: /* set up bootstrap stack */ movl _proc0paddr,%esp /* location of in-kernel pages */ addl $UPAGES*PAGE_SIZE,%esp /* bootstrap stack end location */ xorl %eax,%eax /* mark end of frames */ movl %eax,%ebp movl _proc0paddr,%eax movl _IdlePTD, %esi movl %esi,PCB_CR3(%eax) movl $_proc0,_curproc movl physfree, %esi pushl %esi /* value of first for init386(first) */ call _init386 /* wire 386 chip for unix operation */ popl %esi .globl __ucodesel,__udatasel pushl $0 /* unused */ pushl __udatasel /* ss */ pushl $0 /* esp - filled in by execve() */ pushl $PSL_USER /* eflags (IOPL 0, int enab) */ pushl __ucodesel /* cs */ pushl $0 /* eip - filled in by execve() */ subl $(12*4),%esp /* space for rest of registers */ pushl %esp /* call main with frame pointer */ call _main /* autoconfiguration, mountroot etc */ hlt /* never returns to here */ /* * When starting init, call this to configure the process for user * mode. This will be inherited by other processes. */ NON_GPROF_ENTRY(prepare_usermode) /* * Now we've run main() and determined what cpu-type we are, we can * enable write protection and alignment checking on i486 cpus and * above. */ #if defined(I486_CPU) || defined(I586_CPU) || defined(I686_CPU) cmpl $CPUCLASS_386,_cpu_class je 1f movl %cr0,%eax /* get control word */ orl $CR0_WP|CR0_AM,%eax /* enable i486 features */ movl %eax,%cr0 /* and do it */ 1: #endif /* * on return from main(), we are process 1 * set up address space and stack so that we can 'return' to user mode */ movl __ucodesel,%eax movl __udatasel,%ecx #if 0 movl %cx,%ds #endif movl %cx,%es movl %ax,%fs /* double map cs to fs */ movl %cx,%gs /* and ds to gs */ ret /* goto user! */ #define LCALL(x,y) .byte 0x9a ; .long y ; .word x /* * Signal trampoline, copied to top of user stack */ NON_GPROF_ENTRY(sigcode) call SIGF_HANDLER(%esp) lea SIGF_SC(%esp),%eax /* scp (the call may have clobbered the */ /* copy at 8(%esp)) */ pushl %eax pushl %eax /* junk to fake return address */ movl $SYS_sigreturn,%eax /* sigreturn() */ LCALL(0x7,0) /* enter kernel with args on stack */ hlt /* never gets here */ ALIGN_TEXT _esigcode: .data .globl _szsigcode _szsigcode: .long _esigcode-_sigcode .text /********************************************************************** * * Recover the bootinfo passed to us from the boot program * */ recover_bootinfo: /* * This code is called in different ways depending on what loaded * and started the kernel. This is used to detect how we get the * arguments from the other code and what we do with them. * * Old disk boot blocks: * (*btext)(howto, bootdev, cyloffset, esym); * [return address == 0, and can NOT be returned to] * [cyloffset was not supported by the FreeBSD boot code * and always passed in as 0] * [esym is also known as total in the boot code, and * was never properly supported by the FreeBSD boot code] * * Old diskless netboot code: * (*btext)(0,0,0,0,&nfsdiskless,0,0,0); * [return address != 0, and can NOT be returned to] * If we are being booted by this code it will NOT work, * so we are just going to halt if we find this case. * * New uniform boot code: * (*btext)(howto, bootdev, 0, 0, 0, &bootinfo) * [return address != 0, and can be returned to] * * There may seem to be a lot of wasted arguments in here, but * that is so the newer boot code can still load very old kernels * and old boot code can load new kernels. */ /* * The old style disk boot blocks fake a frame on the stack and * did an lret to get here. The frame on the stack has a return * address of 0. */ cmpl $0,4(%ebp) je olddiskboot /* * We have some form of return address, so this is either the * old diskless netboot code, or the new uniform code. That can * be detected by looking at the 5th argument, if it is 0 * we are being booted by the new uniform boot code. */ cmpl $0,24(%ebp) je newboot /* * Seems we have been loaded by the old diskless boot code, we * don't stand a chance of running as the diskless structure * changed considerably between the two, so just halt. */ hlt /* * We have been loaded by the new uniform boot code. * Let's check the bootinfo version, and if we do not understand * it we return to the loader with a status of 1 to indicate this error */ newboot: movl 28(%ebp),%ebx /* &bootinfo.version */ movl BI_VERSION(%ebx),%eax cmpl $1,%eax /* We only understand version 1 */ je 1f movl $1,%eax /* Return status */ leave /* * XXX this returns to our caller's caller (as is required) since * we didn't set up a frame and our caller did. */ ret 1: /* * If we have a kernelname copy it in */ movl BI_KERNELNAME(%ebx),%esi cmpl $0,%esi je 2f /* No kernelname */ movl $MAXPATHLEN,%ecx /* Brute force!!! */ movl $R(_kernelname),%edi cmpb $'/',(%esi) /* Make sure it starts with a slash */ je 1f movb $'/',(%edi) incl %edi decl %ecx 1: cld rep movsb 2: /* * Determine the size of the boot loader's copy of the bootinfo * struct. This is impossible to do properly because old versions * of the struct don't contain a size field and there are 2 old * versions with the same version number. */ movl $BI_ENDCOMMON,%ecx /* prepare for sizeless version */ testl $RB_BOOTINFO,8(%ebp) /* bi_size (and bootinfo) valid? */ je got_bi_size /* no, sizeless version */ movl BI_SIZE(%ebx),%ecx got_bi_size: /* * Copy the common part of the bootinfo struct */ movl %ebx,%esi movl $R(_bootinfo),%edi cmpl $BOOTINFO_SIZE,%ecx jbe got_common_bi_size movl $BOOTINFO_SIZE,%ecx got_common_bi_size: cld rep movsb #ifdef NFS #ifndef BOOTP_NFSV3 /* * If we have a nfs_diskless structure copy it in */ movl BI_NFS_DISKLESS(%ebx),%esi cmpl $0,%esi je olddiskboot movl $R(_nfs_diskless),%edi movl $NFSDISKLESS_SIZE,%ecx cld rep movsb movl $R(_nfs_diskless_valid),%edi movl $1,(%edi) #endif #endif /* * The old style disk boot. * (*btext)(howto, bootdev, cyloffset, esym); * Note that the newer boot code just falls into here to pick * up howto and bootdev, cyloffset and esym are no longer used */ olddiskboot: movl 8(%ebp),%eax movl %eax,R(_boothowto) movl 12(%ebp),%eax movl %eax,R(_bootdev) #if defined(USERCONFIG_BOOT) && defined(USERCONFIG) movl $0x10200, %esi movl $R(_userconfig_from_boot),%edi movl $512,%ecx cld rep movsb #endif /* USERCONFIG_BOOT */ ret /********************************************************************** * * Identify the CPU and initialize anything special about it * */ identify_cpu: /* Try to toggle alignment check flag; does not exist on 386. */ pushfl popl %eax movl %eax,%ecx orl $PSL_AC,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_AC,%eax pushl %ecx popfl testl %eax,%eax jnz try486 /* NexGen CPU does not have aligment check flag. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jz trynexgen popfl movl $CPU_386,R(_cpu) jmp 3f trynexgen: popfl movl $CPU_NX586,R(_cpu) movl $0x4778654e,R(_cpu_vendor) # store vendor string movl $0x72446e65,R(_cpu_vendor+4) movl $0x6e657669,R(_cpu_vendor+8) movl $0,R(_cpu_vendor+12) jmp 3f try486: /* Try to toggle identification flag; does not exist on early 486s. */ pushfl popl %eax movl %eax,%ecx xorl $PSL_ID,%eax pushl %eax popfl pushfl popl %eax xorl %ecx,%eax andl $PSL_ID,%eax pushl %ecx popfl testl %eax,%eax jnz trycpuid movl $CPU_486,R(_cpu) /* * Check Cyrix CPU * Cyrix CPUs do not change the undefined flags following * execution of the divide instruction which divides 5 by 2. * * Note: CPUID is enabled on M2, so it passes another way. */ pushfl movl $0x5555, %eax xorl %edx, %edx movl $2, %ecx clc divl %ecx jnc trycyrix popfl jmp 3f /* You may use Intel CPU. */ trycyrix: popfl /* * IBM Bluelighting CPU also doesn't change the undefined flags. * Because IBM doesn't disclose the information for Bluelighting * CPU, we couldn't distinguish it from Cyrix's (including IBM * brand of Cyrix CPUs). */ movl $0x69727943,R(_cpu_vendor) # store vendor string movl $0x736e4978,R(_cpu_vendor+4) movl $0x64616574,R(_cpu_vendor+8) jmp 3f trycpuid: /* Use the `cpuid' instruction. */ xorl %eax,%eax .byte 0x0f,0xa2 # cpuid 0 movl %eax,R(_cpu_high) # highest capability movl %ebx,R(_cpu_vendor) # store vendor string movl %edx,R(_cpu_vendor+4) movl %ecx,R(_cpu_vendor+8) movb $0,R(_cpu_vendor+12) movl $1,%eax .byte 0x0f,0xa2 # cpuid 1 movl %eax,R(_cpu_id) # store cpu_id movl %edx,R(_cpu_feature) # store cpu_feature rorl $8,%eax # extract family type andl $15,%eax cmpl $5,%eax jae 1f /* less than Pentium; must be 486 */ movl $CPU_486,R(_cpu) jmp 3f 1: /* a Pentium? */ cmpl $5,%eax jne 2f movl $CPU_586,R(_cpu) jmp 3f 2: /* Greater than Pentium...call it a Pentium Pro */ movl $CPU_686,R(_cpu) 3: ret /********************************************************************** * * Create the first page directory and its page tables. * */ create_pagetables: testl $CPUID_PGE, R(_cpu_feature) jz 1f movl %cr4, %eax orl $CR4_PGE, %eax movl %eax, %cr4 1: /* Find end of kernel image (rounded up to a page boundary). */ movl $R(_end),%esi /* include symbols in "kernel image" if they are loaded and useful */ #ifdef DDB movl R(_bootinfo+BI_ESYMTAB),%edi testl %edi,%edi je over_symalloc movl %edi,%esi movl $KERNBASE,%edi addl %edi,R(_bootinfo+BI_SYMTAB) addl %edi,R(_bootinfo+BI_ESYMTAB) over_symalloc: #endif addl $PAGE_MASK,%esi andl $~PAGE_MASK,%esi movl %esi,R(_KERNend) /* save end of kernel */ movl %esi,R(physfree) /* next free page is at end of kernel */ /* Allocate Kernel Page Tables */ ALLOCPAGES(NKPT) movl %esi,R(_KPTphys) /* Allocate Page Table Directory */ ALLOCPAGES(1) movl %esi,R(_IdlePTD) /* Allocate UPAGES */ ALLOCPAGES(UPAGES) movl %esi,R(p0upa) addl $KERNBASE, %esi movl %esi, R(_proc0paddr) #ifdef VM86 - ALLOCPAGES(4) /* IOPAGES + ext + stack */ + ALLOCPAGES(1) /* vm86/bios stack */ + movl %esi,R(vm86phystk) + + ALLOCPAGES(3) /* pgtable + ext + IOPAGES */ movl %esi,R(_vm86pa) addl $KERNBASE, %esi movl %esi, R(_vm86paddr) #endif /* VM86 */ #ifdef SMP /* Allocate cpu0's private data page */ ALLOCPAGES(1) movl %esi,R(cpu0pp) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpage) /* relocated to KVM space */ /* Allocate cpu0's private page table for mapping priv page, apic, etc */ ALLOCPAGES(1) movl %esi,R(cpu0pt) addl $KERNBASE, %esi movl %esi, R(_cpu0prvpt) /* relocated to KVM space */ #endif /* SMP */ /* Map read-only from zero to the end of the kernel text section */ xorl %eax, %eax #ifdef BDE_DEBUGGER /* If the debugger is present, actually map everything read-write. */ cmpl $0,R(_bdb_exists) jne map_read_write #endif xorl %edx,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 2f orl $PG_G,%edx #endif 2: movl $R(_etext),%ecx addl $PAGE_MASK,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map read-write, data, bss and symbols */ movl $R(_etext),%eax addl $PAGE_MASK, %eax andl $~PAGE_MASK, %eax map_read_write: movl $PG_RW,%edx #if !defined(SMP) testl $CPUID_PGE, R(_cpu_feature) jz 1f orl $PG_G,%edx #endif 1: movl R(_KERNend),%ecx subl %eax,%ecx shrl $PAGE_SHIFT,%ecx fillkptphys(%edx) /* Map page directory. */ movl R(_IdlePTD), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map proc0's UPAGES in the physical way ... */ movl R(p0upa), %eax movl $UPAGES, %ecx fillkptphys($PG_RW) /* Map ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkptphys($PG_RW) #ifdef VM86 /* Map space for the vm86 region */ - movl R(_vm86pa), %eax + movl R(vm86phystk), %eax movl $4, %ecx fillkptphys($PG_RW) /* Map page 0 into the vm86 page table */ movl $0, %eax movl $0, %ebx movl $1, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) /* ...likewise for the ISA hole */ movl $ISA_HOLE_START, %eax movl $ISA_HOLE_START>>PAGE_SHIFT, %ebx movl $ISA_HOLE_LENGTH>>PAGE_SHIFT, %ecx fillkpt(R(_vm86pa), $PG_RW|PG_U) #endif /* VM86 */ #ifdef SMP /* Map cpu0's private page into global kmem (4K @ cpu0prvpage) */ movl R(cpu0pp), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map cpu0's private page table into global kmem FWIW */ movl R(cpu0pt), %eax movl $1, %ecx fillkptphys($PG_RW) /* Map the private page into the private page table into private space */ movl R(cpu0pp), %eax movl $0, %ebx /* pte offset = 0 */ movl $1, %ecx /* one private page coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Map the page table page into private space */ movl R(cpu0pt), %eax movl $1, %ebx /* pte offset = 1 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* ... and put the page table table in the pde. */ movl R(cpu0pt), %eax movl $MPPTDI, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* Fakeup VA for the local apic to allow early traps. */ ALLOCPAGES(1) movl %esi, %eax movl $2, %ebx /* pte offset = 2 */ movl $1, %ecx /* one private pt coming right up */ fillkpt(R(cpu0pt), $PG_RW) /* Initialize mp lock to allow early traps */ movl $1, R(_mp_lock) /* Initialize my_idlePTD to IdlePTD */ movl R(cpu0pp), %eax movl R(_IdlePTD), %ecx movl %ecx,GD_MY_IDLEPTD(%eax) #endif /* SMP */ /* install a pde for temporary double map of bottom of VA */ movl R(_KPTphys), %eax xorl %ebx, %ebx movl $1, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install pde's for pt's */ movl R(_KPTphys), %eax movl $KPTDI, %ebx movl $NKPT, %ecx fillkpt(R(_IdlePTD), $PG_RW) /* install a pde recursively mapping page directory as a page table */ movl R(_IdlePTD), %eax movl $PTDPTDI, %ebx movl $1,%ecx fillkpt(R(_IdlePTD), $PG_RW) ret #ifdef BDE_DEBUGGER bdb_prepare_paging: cmpl $0,R(_bdb_exists) je bdb_prepare_paging_exit subl $6,%esp /* * Copy and convert debugger entries from the bootstrap gdt and idt * to the kernel gdt and idt. Everything is still in low memory. * Tracing continues to work after paging is enabled because the * low memory addresses remain valid until everything is relocated. * However, tracing through the setidt() that initializes the trace * trap will crash. */ sgdt (%esp) movl 2(%esp),%esi /* base address of bootstrap gdt */ movl $R(_gdt),%edi movl %edi,2(%esp) /* prepare to load kernel gdt */ movl $8*18/4,%ecx cld rep /* copy gdt */ movsl movl $R(_gdt),-8+2(%edi) /* adjust gdt self-ptr */ movb $0x92,-8+5(%edi) lgdt (%esp) sidt (%esp) movl 2(%esp),%esi /* base address of current idt */ movl 8+4(%esi),%eax /* convert dbg descriptor to ... */ movw 8(%esi),%ax movl %eax,R(bdb_dbg_ljmp+1) /* ... immediate offset ... */ movl 8+2(%esi),%eax movw %ax,R(bdb_dbg_ljmp+5) /* ... and selector for ljmp */ movl 24+4(%esi),%eax /* same for bpt descriptor */ movw 24(%esi),%ax movl %eax,R(bdb_bpt_ljmp+1) movl 24+2(%esi),%eax movw %ax,R(bdb_bpt_ljmp+5) movl $R(_idt),%edi movl %edi,2(%esp) /* prepare to load kernel idt */ movl $8*4/4,%ecx cld rep /* copy idt */ movsl lidt (%esp) addl $6,%esp bdb_prepare_paging_exit: ret /* Relocate debugger gdt entries and gdt and idt pointers. */ bdb_commit_paging: cmpl $0,_bdb_exists je bdb_commit_paging_exit movl $_gdt+8*9,%eax /* adjust slots 9-17 */ movl $9,%ecx reloc_gdt: movb $KERNBASE>>24,7(%eax) /* top byte of base addresses, was 0, */ addl $8,%eax /* now KERNBASE>>24 */ loop reloc_gdt subl $6,%esp sgdt (%esp) addl $KERNBASE,2(%esp) lgdt (%esp) sidt (%esp) addl $KERNBASE,2(%esp) lidt (%esp) addl $6,%esp int $3 bdb_commit_paging_exit: ret #endif /* BDE_DEBUGGER */ Index: head/sys/i386/i386/vm86.c =================================================================== --- head/sys/i386/i386/vm86.c (revision 37888) +++ head/sys/i386/i386/vm86.c (revision 37889) @@ -1,711 +1,729 @@ /*- * Copyright (c) 1997 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: vm86.c,v 1.11 1998/03/24 16:47:12 jlemon Exp $ + * $Id: vm86.c,v 1.12 1998/04/15 17:45:08 bde Exp $ */ #include "opt_vm86.h" #include #include #include #include #include #include #include #include #include #include #include #include #include /* pcb.h included via sys/user.h */ #include #include extern int i386_extend_pcb __P((struct proc *)); extern struct segment_descriptor common_tssd; extern int vm86paddr, vm86pa; extern struct pcb *vm86pcb; extern int vm86_bioscall(struct vm86frame *); extern void vm86_biosret(struct vm86frame *); void vm86_prepcall(struct vm86frame); #define HLT 0xf4 #define CLI 0xfa #define STI 0xfb #define PUSHF 0x9c #define POPF 0x9d #define INTn 0xcd #define IRET 0xcf #define CALLm 0xff #define OPERAND_SIZE_PREFIX 0x66 #define ADDRESS_SIZE_PREFIX 0x67 #define PUSH_MASK ~(PSL_VM | PSL_RF | PSL_I) #define POP_MASK ~(PSL_VIP | PSL_VIF | PSL_VM | PSL_RF | PSL_IOPL) static __inline caddr_t MAKE_ADDR(u_short sel, u_short off) { return ((caddr_t)((sel << 4) + off)); } static __inline void GET_VEC(u_long vec, u_short *sel, u_short *off) { *sel = vec >> 16; *off = vec & 0xffff; } static __inline u_long MAKE_VEC(u_short sel, u_short off) { return ((sel << 16) | off); } static __inline void PUSH(u_short x, struct vm86frame *vmf) { vmf->vmf_sp -= 2; susword(MAKE_ADDR(vmf->vmf_ss, vmf->vmf_sp), x); } static __inline void PUSHL(u_long x, struct vm86frame *vmf) { vmf->vmf_sp -= 4; suword(MAKE_ADDR(vmf->vmf_ss, vmf->vmf_sp), x); } static __inline u_short POP(struct vm86frame *vmf) { u_short x = fusword(MAKE_ADDR(vmf->vmf_ss, vmf->vmf_sp)); vmf->vmf_sp += 2; return (x); } static __inline u_long POPL(struct vm86frame *vmf) { u_long x = fuword(MAKE_ADDR(vmf->vmf_ss, vmf->vmf_sp)); vmf->vmf_sp += 4; return (x); } int vm86_emulate(vmf) struct vm86frame *vmf; { struct vm86_kernel *vm86; caddr_t addr; u_char i_byte; u_long temp_flags; int inc_ip = 1; int retcode = 0; /* * pcb_ext contains the address of the extension area, or zero if * the extension is not present. (This check should not be needed, * as we can't enter vm86 mode until we set up an extension area) */ if (curpcb->pcb_ext == 0) return (SIGBUS); vm86 = &curpcb->pcb_ext->ext_vm86; if (vmf->vmf_eflags & PSL_T) retcode = SIGTRAP; addr = MAKE_ADDR(vmf->vmf_cs, vmf->vmf_ip); i_byte = fubyte(addr); if (i_byte == ADDRESS_SIZE_PREFIX) { i_byte = fubyte(++addr); inc_ip++; } if (vm86->vm86_has_vme) { switch (i_byte) { case OPERAND_SIZE_PREFIX: i_byte = fubyte(++addr); inc_ip++; switch (i_byte) { case PUSHF: if (vmf->vmf_eflags & PSL_VIF) PUSHL((vmf->vmf_eflags & PUSH_MASK) | PSL_IOPL | PSL_I, vmf); else PUSHL((vmf->vmf_eflags & PUSH_MASK) | PSL_IOPL, vmf); vmf->vmf_ip += inc_ip; return (0); case POPF: temp_flags = POPL(vmf) & POP_MASK; vmf->vmf_eflags = (vmf->vmf_eflags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; vmf->vmf_ip += inc_ip; if (temp_flags & PSL_I) { vmf->vmf_eflags |= PSL_VIF; if (vmf->vmf_eflags & PSL_VIP) break; } else { vmf->vmf_eflags &= ~PSL_VIF; } return (0); } break; /* VME faults here if VIP is set, but does not set VIF. */ case STI: vmf->vmf_eflags |= PSL_VIF; vmf->vmf_ip += inc_ip; if ((vmf->vmf_eflags & PSL_VIP) == 0) { uprintf("fatal sti\n"); return (SIGKILL); } break; /* VME if no redirection support */ case INTn: break; /* VME if trying to set PSL_TF, or PSL_I when VIP is set */ case POPF: temp_flags = POP(vmf) & POP_MASK; vmf->vmf_flags = (vmf->vmf_flags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; vmf->vmf_ip += inc_ip; if (temp_flags & PSL_I) { vmf->vmf_eflags |= PSL_VIF; if (vmf->vmf_eflags & PSL_VIP) break; } else { vmf->vmf_eflags &= ~PSL_VIF; } return (retcode); /* VME if trying to set PSL_TF, or PSL_I when VIP is set */ case IRET: vmf->vmf_ip = POP(vmf); vmf->vmf_cs = POP(vmf); temp_flags = POP(vmf) & POP_MASK; vmf->vmf_flags = (vmf->vmf_flags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; if (temp_flags & PSL_I) { vmf->vmf_eflags |= PSL_VIF; if (vmf->vmf_eflags & PSL_VIP) break; } else { vmf->vmf_eflags &= ~PSL_VIF; } return (retcode); } return (SIGBUS); } switch (i_byte) { case OPERAND_SIZE_PREFIX: i_byte = fubyte(++addr); inc_ip++; switch (i_byte) { case PUSHF: if (vm86->vm86_eflags & PSL_VIF) PUSHL((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL | PSL_I, vmf); else PUSHL((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL, vmf); vmf->vmf_ip += inc_ip; return (retcode); case POPF: temp_flags = POPL(vmf) & POP_MASK; vmf->vmf_eflags = (vmf->vmf_eflags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; vmf->vmf_ip += inc_ip; if (temp_flags & PSL_I) { vm86->vm86_eflags |= PSL_VIF; if (vm86->vm86_eflags & PSL_VIP) break; } else { vm86->vm86_eflags &= ~PSL_VIF; } return (retcode); } return (SIGBUS); case CLI: vm86->vm86_eflags &= ~PSL_VIF; vmf->vmf_ip += inc_ip; return (retcode); case STI: /* if there is a pending interrupt, go to the emulator */ vm86->vm86_eflags |= PSL_VIF; vmf->vmf_ip += inc_ip; if (vm86->vm86_eflags & PSL_VIP) break; return (retcode); case PUSHF: if (vm86->vm86_eflags & PSL_VIF) PUSH((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL | PSL_I, vmf); else PUSH((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL, vmf); vmf->vmf_ip += inc_ip; return (retcode); case INTn: i_byte = fubyte(addr + 1); if ((vm86->vm86_intmap[i_byte >> 3] & (1 << (i_byte & 7))) != 0) break; if (vm86->vm86_eflags & PSL_VIF) PUSH((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL | PSL_I, vmf); else PUSH((vmf->vmf_flags & PUSH_MASK) | PSL_IOPL, vmf); PUSH(vmf->vmf_cs, vmf); PUSH(vmf->vmf_ip + inc_ip + 1, vmf); /* increment IP */ GET_VEC(fuword((caddr_t)(i_byte * 4)), &vmf->vmf_cs, &vmf->vmf_ip); vmf->vmf_flags &= ~PSL_T; vm86->vm86_eflags &= ~PSL_VIF; return (retcode); case IRET: vmf->vmf_ip = POP(vmf); vmf->vmf_cs = POP(vmf); temp_flags = POP(vmf) & POP_MASK; vmf->vmf_flags = (vmf->vmf_flags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; if (temp_flags & PSL_I) { vm86->vm86_eflags |= PSL_VIF; if (vm86->vm86_eflags & PSL_VIP) break; } else { vm86->vm86_eflags &= ~PSL_VIF; } return (retcode); case POPF: temp_flags = POP(vmf) & POP_MASK; vmf->vmf_flags = (vmf->vmf_flags & ~POP_MASK) | temp_flags | PSL_VM | PSL_I; vmf->vmf_ip += inc_ip; if (temp_flags & PSL_I) { vm86->vm86_eflags |= PSL_VIF; if (vm86->vm86_eflags & PSL_VIP) break; } else { vm86->vm86_eflags &= ~PSL_VIF; } return (retcode); } return (SIGBUS); } +#define PGTABLE_SIZE ((1024 + 64) * 1024 / PAGE_SIZE) +#define INTMAP_SIZE 32 +#define IOMAP_SIZE ctob(IOPAGES) +#define TSS_SIZE \ + (sizeof(struct pcb_ext) - sizeof(struct segment_descriptor) + \ + INTMAP_SIZE + IOMAP_SIZE + 1) + +struct vm86_layout { + pt_entry_t vml_pgtbl[PGTABLE_SIZE]; + struct pcb vml_pcb; + struct pcb_ext vml_ext; + char vml_intmap[INTMAP_SIZE]; + char vml_iomap[IOMAP_SIZE]; + char vml_iomap_trailer; +}; + static void vm86_initialize(void) { - int i, offset; + int i; u_long *addr; + struct vm86_layout *vml = (struct vm86_layout *)vm86paddr; struct pcb *pcb; struct pcb_ext *ext; struct segment_descriptor sd; struct soft_segment_descriptor ssd = { 0, /* segment base address (overwritten) */ - ctob(IOPAGES + 1) - 1, /* length */ + 0, /* length (overwritten) */ SDT_SYS386TSS, /* segment type */ 0, /* priority level */ 1, /* descriptor present */ 0, 0, - 0, /* default 32 size */ + 0, /* default 16 size */ 0 /* granularity */ }; /* + * this should be a compile time error, but cpp doesn't grok sizeof(). + */ + if (sizeof(struct vm86_layout) > ctob(3)) + panic("struct vm86_layout exceeds space allocated in locore.s"); + + /* * Below is the memory layout that we use for the vm86 region. * - * The last byte of the i/o map must be followed by an 0xff byte. - * We arbitrarily allocate 16 bytes here, to keep the starting - * address on a doubleword boundary. - * - * If a ~2K stack is enough for interrupt handling, then - * it may be possible to get the page count down to 3 pages. - * - * +--------+ +--------+ - * | | |Page Tbl| 1M + 64K = 272 entries = 1088 bytes + * +--------+ + * | | + * | | + * | page 0 | * | | +--------+ - * | page 0 | - * | | +--------+ * | | | stack | - * +--------+ +--------+ - * +--------+ +--------+ + * +--------+ +--------+ <--------- vm86paddr + * | | |Page Tbl| 1M + 64K = 272 entries = 1088 bytes + * | | +--------+ * | | | PCB | size: ~240 bytes - * | | |PCB Ext | size: ~140 bytes (includes TSS) + * | page 1 | |PCB Ext | size: ~140 bytes (includes TSS) * | | +--------+ - * | page 1 | - * | | +--------+ * | | |int map | - * | | +--------+ <-- &(PAGE 1) - 16 + * | | +--------+ * +--------+ | | * | page 2 | | I/O | * +--------+ | bitmap | * | page 3 | | | - * +--------+ +--------+ + * | | +--------+ + * +--------+ */ /* * A rudimentary PCB must be installed, in order to get to the * PCB extension area. We use the PCB area as a scratchpad for * data storage, the layout of which is shown below. * * pcb_esi = new PTD entry 0 * pcb_ebp = pointer to frame on vm86 stack * pcb_esp = stack frame pointer at time of switch * pcb_ebx = va of vm86 page table * pcb_eip = argument pointer to initial call * pcb_fs = saved TSS descriptor, word 0 * pcb_gs = saved TSS descriptor, word 1 */ +#define new_ptd pcb_esi +#define vm86_frame pcb_ebp +#define pgtable_va pcb_ebx - pcb = (struct pcb *)(vm86paddr + PAGE_SIZE); - bzero(pcb, sizeof(struct pcb)); - pcb->pcb_esi = vm86pa | PG_V | PG_RW | PG_U; - pcb->pcb_ebp = vm86paddr + PAGE_SIZE - sizeof(struct vm86frame); - pcb->pcb_ebx = vm86paddr; + pcb = &vml->vml_pcb; + ext = &vml->vml_ext; - ext = (struct pcb_ext *)((u_int)pcb + sizeof(struct pcb)); + bzero(pcb, sizeof(struct pcb)); + pcb->new_ptd = vm86pa | PG_V | PG_RW | PG_U; + pcb->vm86_frame = vm86paddr - sizeof(struct vm86frame); + pcb->pgtable_va = vm86paddr; pcb->pcb_ext = ext; bzero(ext, sizeof(struct pcb_ext)); - ext->ext_tss.tss_esp0 = vm86paddr + PAGE_SIZE; + ext->ext_tss.tss_esp0 = vm86paddr; ext->ext_tss.tss_ss0 = GSEL(GDATA_SEL, SEL_KPL); - - offset = PAGE_SIZE - 16; ext->ext_tss.tss_ioopt = - (offset - ((u_int)&ext->ext_tss & PAGE_MASK)) << 16; - ext->ext_iomap = (caddr_t)(offset + ((u_int)&ext->ext_tss & PG_FRAME)); + ((u_int)vml->vml_iomap - (u_int)&ext->ext_tss) << 16; + ext->ext_iomap = vml->vml_iomap; + ext->ext_vm86.vm86_intmap = vml->vml_intmap; - ext->ext_vm86.vm86_intmap = ext->ext_iomap - 32; if (cpu_feature & CPUID_VME) ext->ext_vm86.vm86_has_vme = (rcr4() & CR4_VME ? 1 : 0); addr = (u_long *)ext->ext_vm86.vm86_intmap; - for (i = 0; i < (ctob(IOPAGES) + 32 + 16) / sizeof(u_long); i++) + for (i = 0; i < (INTMAP_SIZE + IOMAP_SIZE) / sizeof(u_long); i++) *addr++ = 0; + vml->vml_iomap_trailer = 0xff; ssd.ssd_base = (u_int)&ext->ext_tss; - ssd.ssd_limit -= ((u_int)&ext->ext_tss & PAGE_MASK); + ssd.ssd_limit = TSS_SIZE - 1; ssdtosd(&ssd, &ext->ext_tssd); vm86pcb = pcb; } void initial_bioscalls(u_int *basemem, u_int *extmem) { int i, method; struct vm86frame vmf; u_int64_t highwat = 0; struct { u_int64_t base; u_int64_t length; u_int32_t type; } smap; bzero(&vmf, sizeof(struct vm86frame)); /* safety */ vm86_initialize(); #ifndef PC98 vm86_intcall(0x12, &vmf); *basemem = vmf.vmf_ax; *extmem = 0; /* * if basemem != 640, map pages r/w into vm86 page table so * that the bios can scribble on it. */ for (i = *basemem / 4; i < 160; i++) { u_int *pte = (u_int *)vm86paddr; pte[i] = (i << PAGE_SHIFT) | PG_V | PG_RW | PG_U; } /* * get memory map with INT 15:E820 */ #define SMAPSIZ sizeof(smap) #define SMAP_SIG 0x534D4150 /* 'SMAP' */ vmf.vmf_ebx = 0; do { vmf.vmf_eax = 0xE820; vmf.vmf_edx = SMAP_SIG; vmf.vmf_ecx = SMAPSIZ; i = vm86_datacall(0x15, &vmf, (char *)&smap, SMAPSIZ, &vmf.vmf_es, &vmf.vmf_di); if (i || vmf.vmf_eax != SMAP_SIG) break; if (smap.type == 0x01 && smap.base >= highwat) { *extmem += (smap.length / 1024); highwat = smap.base + smap.length; } } while (vmf.vmf_ebx != 0); if (*extmem != 0) { if (*extmem > *basemem) { *extmem -= *basemem; method = 0xE820; goto done; } printf("E820: extmem (%d) < basemem (%d)\n", *extmem, *basemem); } /* * try memory map with INT 15:E801 */ vmf.vmf_ax = 0xE801; if (vm86_intcall(0x15, &vmf) == 0) { *extmem = vmf.vmf_cx + vmf.vmf_dx * 64; method = 0xE801; goto done; } vmf.vmf_ah = 0x88; vm86_intcall(0x15, &vmf); *extmem = vmf.vmf_ax; method = 0x88; done: printf("BIOS basemem: %dK, extmem: %dK (from %p call)\n", *basemem, *extmem, method); #endif /* !PC98 */ #if 0 /* VESA setup -- ? */ vmf.vmf_ax = 0x4f02; error = vm86_intcall(0x10, &vmf); #endif } static void vm86_initflags(struct vm86frame *vmf) { int eflags = vmf->vmf_eflags; struct vm86_kernel *vm86 = &curpcb->pcb_ext->ext_vm86; if (vm86->vm86_has_vme) { eflags = (vmf->vmf_eflags & ~VME_USERCHANGE) | (eflags & VME_USERCHANGE) | PSL_VM; } else { vm86->vm86_eflags = eflags; /* save VIF, VIP */ eflags = (vmf->vmf_eflags & ~VM_USERCHANGE) | (eflags & VM_USERCHANGE) | PSL_VM; } vmf->vmf_eflags = eflags | PSL_VM; } /* * called from vm86_bioscall, while in vm86 address space, to finalize setup. */ void vm86_prepcall(struct vm86frame vmf) { u_long addr[] = { 0xA00, 0x1000 }; /* code, stack */ u_char intcall[] = { CLI, INTn, 0x00, STI, HLT }; if ((vmf.vmf_trapno & PAGE_MASK) <= 0xff) { /* interrupt call requested */ intcall[2] = (u_char)(vmf.vmf_trapno & 0xff); memcpy((void *)addr[0], (void *)intcall, sizeof(intcall)); vmf.vmf_ip = addr[0]; vmf.vmf_cs = 0; } vmf.vmf_sp = addr[1] - 2; /* keep aligned */ vmf.kernel_es = vmf.kernel_ds = 0; vmf.vmf_ss = 0; vmf.vmf_eflags = PSL_VIF | PSL_VM | PSL_USER; vm86_initflags(&vmf); } /* * vm86 trap handler; determines whether routine succeeded or not. * Called while in vm86 space, returns to calling process. */ void vm86_trap(struct vm86frame *vmf) { caddr_t addr; /* "should not happen" */ if ((vmf->vmf_eflags & PSL_VM) == 0) panic("vm86_trap called, but not in vm86 mode"); addr = MAKE_ADDR(vmf->vmf_cs, vmf->vmf_ip); if (*(u_char *)addr == HLT) vmf->vmf_trapno = vmf->vmf_eflags & PSL_C; else vmf->vmf_trapno = vmf->vmf_trapno << 16; vm86_biosret(vmf); } int vm86_intcall(int intnum, struct vm86frame *vmf) { if (intnum < 0 || intnum > 0xff) return (EINVAL); vmf->vmf_trapno = intnum; return (vm86_bioscall(vmf)); } /* * buffer must be entirely contained in a wired down page in kernel memory, * and is mapped into page 1 in vm86 space. segment/offset will be filled * in to create a vm86 pointer to the buffer. If intnum is a valid * interrupt number (0-255), then the "interrupt trampoline" will be * used, otherwise we use the caller's cs:ip routine. * * a future revision may allow multiple pages to be mapped, or allow * the caller to pass in a custom page table to use. */ int vm86_datacall(intnum, vmf, buffer, buflen, segment, offset) int intnum; struct vm86frame *vmf; char *buffer; int buflen; u_short *segment, *offset; { u_int page; page = (u_int)buffer & PG_FRAME; *offset = (u_int)buffer & PAGE_MASK; if ((*offset + buflen) & PG_FRAME) return (-1); /* XXX fixme! */ *segment = 0x100; page = vtophys(page); vmf->vmf_trapno = page | (intnum & PAGE_MASK); return (vm86_bioscall(vmf)); } #if 0 int vm86_datacall(int intnum, u_int kpage, struct vm86frame *vmf) { if (kpage & PAGE_MASK) return (EINVAL); kpage = vtophys(kpage); vmf->vmf_trapno = kpage | (intnum & PAGE_MASK); return (vm86_bioscall(vmf)); } #endif int vm86_sysarch(p, args) struct proc *p; char *args; { int error = 0; struct i386_vm86_args ua; struct vm86_kernel *vm86; if (error = copyin(args, &ua, sizeof(struct i386_vm86_args))) return (error); if (p->p_addr->u_pcb.pcb_ext == 0) if (error = i386_extend_pcb(p)) return (error); vm86 = &p->p_addr->u_pcb.pcb_ext->ext_vm86; switch (ua.sub_op) { case VM86_INIT: { struct vm86_init_args sa; if (error = copyin(ua.sub_args, &sa, sizeof(sa))) return (error); if (cpu_feature & CPUID_VME) vm86->vm86_has_vme = (rcr4() & CR4_VME ? 1 : 0); else vm86->vm86_has_vme = 0; vm86->vm86_inited = 1; vm86->vm86_debug = sa.debug; bcopy(&sa.int_map, vm86->vm86_intmap, 32); } break; #if 0 case VM86_SET_VME: { struct vm86_vme_args sa; if ((cpu_feature & CPUID_VME) == 0) return (ENODEV); if (error = copyin(ua.sub_args, &sa, sizeof(sa))) return (error); if (sa.state) load_cr4(rcr4() | CR4_VME); else load_cr4(rcr4() & ~CR4_VME); } break; #endif case VM86_GET_VME: { struct vm86_vme_args sa; sa.state = (rcr4() & CR4_VME ? 1 : 0); error = copyout(&sa, ua.sub_args, sizeof(sa)); } break; #if 0 case VM86_INTCALL: { struct vm86_intcall_args sa; if (error = copyin(ua.sub_args, &sa, sizeof(sa))) return (error); if (error = vm86_intcall(sa.intnum, &sa.vmf)) return (error); error = copyout(&sa, ua.sub_args, sizeof(sa)); } break; #endif default: error = EINVAL; } return (error); } Index: head/sys/i386/i386/vm86bios.s =================================================================== --- head/sys/i386/i386/vm86bios.s (revision 37888) +++ head/sys/i386/i386/vm86bios.s (revision 37889) @@ -1,210 +1,218 @@ /*- * Copyright (c) 1998 Jonathan Lemon * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * - * $Id: vm86bios.s,v 1.1 1998/03/23 19:52:39 jlemon Exp $ + * $Id: vm86bios.s,v 1.2 1998/03/24 16:51:36 jlemon Exp $ */ #include "opt_vm86.h" #include /* miscellaneous asm macros */ #include #include "assym.s" +#define SCR_NEWPTD PCB_ESI /* readability macros */ +#define SCR_VMFRAME PCB_EBP /* see vm86.c for explanation */ +#define SCR_STACK PCB_ESP +#define SCR_PGTABLE PCB_EBX +#define SCR_ARGFRAME PCB_EIP +#define SCR_TSS0 PCB_FS +#define SCR_TSS1 PCB_GS + .data ALIGN_DATA .globl _in_vm86call, _vm86pcb _in_vm86call: .long 0 _vm86pcb: .long 0 .text /* * vm86_bioscall(struct trapframe_vm86 *vm86) */ ENTRY(vm86_bioscall) - movl _vm86pcb,%edx /* data area, see vm86.c for layout */ + movl _vm86pcb,%edx /* scratch data area */ movl 4(%esp),%eax - movl %eax,PCB_EIP(%edx) /* save argument pointer */ + movl %eax,SCR_ARGFRAME(%edx) /* save argument pointer */ pushl %ebx pushl %ebp pushl %esi pushl %edi pushl %fs pushl %gs #ifdef SMP pushl %edx ALIGN_LOCK /* Get global lock */ popl %edx #endif movl _curproc,%ecx pushl %ecx /* save _curproc value */ testl %ecx,%ecx je 1f /* no process to save */ #if NNPX > 0 cmpl %ecx,_npxproc /* do we need to save fp? */ jne 1f movl P_ADDR(%ecx),%ecx addl $PCB_SAVEFPU,%ecx pushl %edx call _npxsave popl %edx /* recover our pcb */ #endif 1: - movl PCB_EBP(%edx),%ebx /* target frame location */ + movl SCR_VMFRAME(%edx),%ebx /* target frame location */ movl %ebx,%edi /* destination */ - movl PCB_EIP(%edx),%esi /* source (set on entry) */ + movl SCR_ARGFRAME(%edx),%esi /* source (set on entry) */ movl $21,%ecx /* sizeof(struct vm86frame)/4 */ cld rep movsl /* copy frame to new stack */ movl TF_TRAPNO(%ebx),%ebx cmpl $256,%ebx jb 1f /* no page frame to map */ andl $~PAGE_MASK,%ebx #if 0 orl $PG_V|PG_RW|PG_U,%ebx /* XXX assembler error?? */ #endif orl $0x7,%ebx - movl PCB_EBX(%edx),%eax /* va of vm86 page table */ + movl SCR_PGTABLE(%edx),%eax /* va of vm86 page table */ movl %ebx,4(%eax) /* set vm86 PTE entry 1 */ 1: movl _curpcb,%eax pushl %eax /* save curpcb */ movl %edx,_curpcb /* set curpcb to vm86pcb */ movl $0,_curproc /* erase curproc */ movl _my_tr,%esi leal _gdt(,%esi,8),%ebx /* entry in GDT */ movl 0(%ebx),%eax - movl %eax,PCB_FS(%edx) /* save first word */ + movl %eax,SCR_TSS0(%edx) /* save first word */ movl 4(%ebx),%eax andl $~0x200, %eax /* flip 386BSY -> 386TSS */ - movl %eax,PCB_GS(%edx) /* save second word */ + movl %eax,SCR_TSS1(%edx) /* save second word */ movl PCB_EXT(%edx),%edi /* vm86 tssd entry */ movl 0(%edi),%eax movl %eax,0(%ebx) movl 4(%edi),%eax movl %eax,4(%ebx) shll $3,%esi /* GSEL(entry, SEL_KPL) */ ltr %si movl %cr3,%eax pushl %eax /* save address space */ #ifdef SMP movl _my_idlePTD,%ecx #else movl _IdlePTD,%ecx #endif movl %ecx,%ebx addl $KERNBASE,%ebx /* va of Idle PTD */ movl 0(%ebx),%eax pushl %eax /* old ptde != 0 when booting */ pushl %ebx /* keep for reuse */ - movl %esp,PCB_ESP(%edx) /* save current stack location */ + movl %esp,SCR_STACK(%edx) /* save current stack location */ - movl PCB_ESI(%edx),%eax /* mapping for vm86 page table */ + movl SCR_NEWPTD(%edx),%eax /* mapping for vm86 page table */ movl %eax,0(%ebx) /* ... install as PTD entry 0 */ movl %ecx,%cr3 /* new page tables */ - movl PCB_EBP(%edx),%esp /* switch to new stack */ + movl SCR_VMFRAME(%edx),%esp /* switch to new stack */ call _vm86_prepcall /* finish setup */ movl $1,_in_vm86call /* set flag for trap() */ /* * Return via _doreti */ #ifdef SMP ECPL_LOCK #ifdef CPL_AND_CML #error Not ready for CPL_AND_CML #endif pushl _cpl /* cpl to restore */ ECPL_UNLOCK #else pushl _cpl /* cpl to restore */ #endif subl $4,%esp /* dummy unit */ MPLOCKED incb _intr_nesting_level MEXITCOUNT jmp _doreti /* * vm86_biosret(struct trapframe_vm86 *vm86) */ ENTRY(vm86_biosret) movl _vm86pcb,%edx /* data area */ movl 4(%esp),%esi /* source */ - movl PCB_EIP(%edx),%edi /* destination */ + movl SCR_ARGFRAME(%edx),%edi /* destination */ movl $21,%ecx /* size */ cld rep movsl /* copy frame to original frame */ - movl PCB_ESP(%edx),%esp /* back to old stack */ + movl SCR_STACK(%edx),%esp /* back to old stack */ popl %ebx /* saved va of Idle PTD */ popl %eax movl %eax,0(%ebx) /* restore old pte */ popl %eax movl %eax,%cr3 /* install old page table */ movl $0,_in_vm86call /* reset trapflag */ - movl PCB_EBX(%edx),%ebx /* va of vm86 page table */ + movl SCR_PGTABLE(%edx),%ebx /* va of vm86 page table */ movl $0,4(%ebx) /* ...clear entry 1 */ movl _my_tr,%esi leal _gdt(,%esi,8),%ebx /* entry in GDT */ - movl PCB_FS(%edx),%eax + movl SCR_TSS0(%edx),%eax movl %eax,0(%ebx) /* restore first word */ - movl PCB_GS(%edx),%eax + movl SCR_TSS1(%edx),%eax movl %eax,4(%ebx) /* restore second word */ shll $3,%esi /* GSEL(entry, SEL_KPL) */ ltr %si popl _curpcb /* restore curpcb/curproc */ popl _curproc - movl PCB_EIP(%edx),%edx /* original stack frame */ + movl SCR_ARGFRAME(%edx),%edx /* original stack frame */ movl TF_TRAPNO(%edx),%eax /* return (trapno) */ popl %gs popl %fs popl %edi popl %esi popl %ebp popl %ebx ret /* back to our normal program */