start.s   [plain text]


/*
 * Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
 *
 * @APPLE_LICENSE_HEADER_START@
 * 
 * The contents of this file constitute Original Code as defined in and
 * are subject to the Apple Public Source License Version 1.1 (the
 * "License").  You may not use this file except in compliance with the
 * License.  Please obtain a copy of the License at
 * http://www.apple.com/publicsource and read it before using this file.
 * 
 * This Original Code and all software distributed under the License are
 * distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT.  Please see the
 * License for the specific language governing rights and limitations
 * under the License.
 * 
 * @APPLE_LICENSE_HEADER_END@
 */
/*
 * @OSF_COPYRIGHT@
 */
/* 
 * Mach Operating System
 * Copyright (c) 1991,1990 Carnegie Mellon University
 * All Rights Reserved.
 * 
 * Permission to use, copy, modify and distribute this software and its
 * documentation is hereby granted, provided that both the copyright
 * notice and this permission notice appear in all copies of the
 * software, derivative works or modified versions, and any portions
 * thereof, and that both notices appear in supporting documentation.
 * 
 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
 * CONDITION.  CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
 * 
 * Carnegie Mellon requests users of this software to return to
 * 
 *  Software Distribution Coordinator  or  Software.Distribution@CS.CMU.EDU
 *  School of Computer Science
 *  Carnegie Mellon University
 *  Pittsburgh PA 15213-3890
 * 
 * any improvements or extensions that they make and grant Carnegie Mellon
 * the rights to redistribute these changes.
 */
/*
 */

#include <platforms.h>
#include <cpus.h>
#include <mach_kdb.h>

#include <i386/asm.h>
#include <i386/proc_reg.h>
#include <assym.s>

#if	NCPUS > 1

#define	CX(addr,reg)	addr(,reg,4)

#else

#define	CPU_NUMBER(reg)
#define	CX(addr,reg)	addr

#endif	/* NCPUS > 1 */

#include <i386/AT386/mp/mp.h>

/*
 * GAS won't handle an intersegment jump with a relocatable offset.
 */
#define	LJMP(segment,address)	\
	.byte	0xea		;\
	.long	address		;\
	.word	segment



#define KVTOPHYS	(-KERNELBASE)
#define	KVTOLINEAR	LINEAR_KERNELBASE


#define	PA(addr)	(addr)+KVTOPHYS
#define	VA(addr)	(addr)-KVTOPHYS

	.data
	.align 	2
	.globl	EXT(_kick_buffer_)
EXT(_kick_buffer_):
	.long	1
	.long	3
	.set	.,.+16836
/*
 * Interrupt and bootup stack for initial processor.
 */
	.align	ALIGN
	.globl	EXT(intstack)
EXT(intstack):
	.set	., .+INTSTACK_SIZE
	.globl	EXT(eintstack)
EXT(eintstack:)

#if	NCPUS == 1
	.globl	EXT(int_stack_high)	/* all interrupt stacks */
EXT(int_stack_high):			/* must lie below this */
	.long	EXT(eintstack)		/* address */

	.globl	EXT(int_stack_top)	/* top of interrupt stack */
EXT(int_stack_top):
	.long	EXT(eintstack)
#endif

#if	MACH_KDB
/*
 * Kernel debugger stack for each processor.
 */
	.align	ALIGN
	.globl	EXT(db_stack_store)
EXT(db_stack_store):
	.set	., .+(INTSTACK_SIZE*NCPUS)

/*
 * Stack for last-ditch debugger task for each processor.
 */
	.align	ALIGN
	.globl	EXT(db_task_stack_store)
EXT(db_task_stack_store):
	.set	., .+(INTSTACK_SIZE*NCPUS)
#endif	/* MACH_KDB */

/*
 * per-processor kernel debugger stacks
 */
        .align  ALIGN
        .globl  EXT(kgdb_stack_store)
EXT(kgdb_stack_store):
        .set    ., .+(INTSTACK_SIZE*NCPUS)


/*
 * Pointers to GDT and IDT.  These contain linear addresses.
 */
	.align	ALIGN
	.globl	EXT(gdtptr)
LEXT(gdtptr)
	.word	Times(8,GDTSZ)-1
	.long	EXT(gdt)+KVTOLINEAR

	.align	ALIGN
	.globl	EXT(idtptr)
LEXT(idtptr)
	.word	Times(8,IDTSZ)-1
	.long	EXT(idt)+KVTOLINEAR

#if	NCPUS > 1
	.data
	/*
	 *	start_lock is very special.  We initialize the
	 *	lock at allocation time rather than at run-time.
	 *	Although start_lock should be an instance of a
	 *	hw_lock, we hand-code all manipulation of the lock
	 *	because the hw_lock code may require function calls;
	 *	and we'd rather not introduce another dependency on
	 *	a working stack at this point.
	 */
	.globl	EXT(start_lock)
EXT(start_lock):
	.long	0			/* synchronizes processor startup */

	.globl	EXT(master_is_up)
EXT(master_is_up):
	.long	0			/* 1 when OK for other processors */
					/* to start */
	.globl	EXT(mp_boot_pde)
EXT(mp_boot_pde):
	.long	0
#endif	/* NCPUS > 1 */

/*
 * All CPUs start here.
 *
 * Environment:
 *	protected mode, no paging, flat 32-bit address space.
 *	(Code/data/stack segments have base == 0, limit == 4G)
 */
	.text
	.align	ALIGN
	.globl	EXT(pstart)
	.globl	EXT(_start)
LEXT(_start)
LEXT(pstart)
	mov	$0,%ax			/* fs must be zeroed; */
	mov	%ax,%fs			/* some bootstrappers don`t do this */
	mov	%ax,%gs

#if	NCPUS > 1
	jmp	1f
0:	cmpl	$0,PA(EXT(start_lock))
	jne	0b
1:	movb	$1,%eax
	xchgl	%eax,PA(EXT(start_lock)) /* locked */
	testl	%eax,%eax
	jnz	0b

	cmpl	$0,PA(EXT(master_is_up))	/* are we first? */
	jne	EXT(slave_start)		/* no -- system already up. */
	movl	$1,PA(EXT(master_is_up))	/* others become slaves */
#endif	/* NCPUS > 1 */

/*
 * Get startup parameters.
 */

#include <i386/AT386/asm_startup.h>

/*
 * Build initial page table directory and page tables.
 * %ebx holds first available physical address.
 */

	addl	$(NBPG-1),%ebx		/* round first avail physical addr */
	andl	$(-NBPG),%ebx		/* to machine page size */
	leal	-KVTOPHYS(%ebx),%eax	/* convert to virtual address */
	movl	%eax,PA(EXT(kpde))	/* save as kernel page table directory */
	movl	%ebx,%cr3		/* set physical address in CR3 now */

	movl	%ebx,%edi		/* clear page table directory */
	movl	$(PTES_PER_PAGE),%ecx	/* one page of ptes */
	xorl	%eax,%eax
	cld
	rep
	stosl				/* edi now points to next page */

/*
 * Use next few pages for page tables.
 */
	addl	$(KERNELBASEPDE),%ebx	/* point to pde for kernel base */
	movl	%edi,%esi		/* point to end of current pte page */

/*
 * Enter 1-1 mappings for kernel and for kernel page tables.
 */
	movl	$(INTEL_PTE_KERNEL),%eax /* set up pte prototype */
0:
	cmpl	%esi,%edi		/* at end of pte page? */
	jb	1f			/* if so: */
	movl	%edi,%edx		/*    get pte address (physical) */
	andl	$(-NBPG),%edx		/*    mask out offset in page */
	orl	$(INTEL_PTE_KERNEL),%edx /*   add pte bits */
	movl	%edx,(%ebx)		/*    set pde */
	addl	$4,%ebx			/*    point to next pde */
	movl	%edi,%esi		/*    point to */
	addl	$(NBPG),%esi		/*    end of new pte page */
1:
	movl	%eax,(%edi)		/* set pte */
	addl	$4,%edi			/* advance to next pte */
	addl	$(NBPG),%eax		/* advance to next phys page */
	cmpl	%edi,%eax		/* have we mapped this pte page yet? */
	jb	0b			/* loop if not */

/*
 * Zero rest of last pte page.
 */
	xor	%eax,%eax		/* don`t map yet */
2:	cmpl	%esi,%edi		/* at end of pte page? */
	jae	3f
	movl	%eax,(%edi)		/* zero mapping */
	addl	$4,%edi
	jmp	2b
3:

#if	NCPUS > 1
/*
 * Grab (waste?) another page for a bootstrap page directory
 * for the other CPUs.  We don't want the running CPUs to see
 * addresses 0..3fffff mapped 1-1.
 */
	movl	%edi,PA(EXT(mp_boot_pde)) /* save its physical address */
	movl	$(PTES_PER_PAGE),%ecx	/* and clear it */
	rep
	stosl
#endif	/* NCPUS > 1 */
	movl	%edi,PA(EXT(first_avail)) /* save first available phys addr */

/*
 * pmap_bootstrap will enter rest of mappings.
 */

/*
 * Fix initial descriptor tables.
 */
	lea	PA(EXT(idt)),%esi	/* fix IDT */
	movl	$(IDTSZ),%ecx
	movl	$(PA(fix_idt_ret)),%ebx
	jmp	fix_desc_common		/* (cannot use stack) */
fix_idt_ret:

	lea	PA(EXT(gdt)),%esi	/* fix GDT */
	movl	$(GDTSZ),%ecx
	movl	$(PA(fix_gdt_ret)),%ebx
	jmp	fix_desc_common		/* (cannot use stack) */
fix_gdt_ret:

	lea	PA(EXT(ldt)),%esi	/* fix LDT */
	movl	$(LDTSZ),%ecx
	movl	$(PA(fix_ldt_ret)),%ebx
	jmp	fix_desc_common		/* (cannot use stack) */
fix_ldt_ret:

/*
 * Turn on paging.
 */
	movl	%cr3,%eax		/* retrieve kernel PDE phys address */
	movl	KERNELBASEPDE(%eax),%ecx
	movl	%ecx,(%eax)		/* set it also as pte for location */
					/* 0..3fffff, so that the code */
					/* that enters paged mode is mapped */
					/* to identical addresses after */
					/* paged mode is enabled */

	addl	$4,%eax			/* 400000..7fffff */
	movl	KERNELBASEPDE(%eax),%ecx
	movl	%ecx,(%eax)

	movl	$EXT(pag_start),%ebx	/* first paged code address */

	movl	%cr0,%eax
	orl	$(CR0_PG),%eax		/* set PG bit in CR0 */
	orl	$(CR0_WP),%eax
	movl	%eax,%cr0		/* to enable paging */

	jmp	*%ebx			/* flush prefetch queue */

/*
 * We are now paging, and can run with correct addresses.
 */
LEXT(pag_start)
	lgdt	EXT(gdtptr)		/* load GDT */
	lidt	EXT(idtptr)		/* load IDT */
	LJMP(KERNEL_CS,EXT(vstart))	/* switch to kernel code segment */

/*
 * Master is now running with correct addresses.
 */
LEXT(vstart)
	mov	$(KERNEL_DS),%ax	/* set kernel data segment */
	mov	%ax,%ds
	mov	%ax,%es
	mov	%ax,%ss
	mov	%ax,EXT(ktss)+TSS_SS0	/* set kernel stack segment */
					/* for traps to kernel */
#if	MACH_KDB
	mov	%ax,EXT(dbtss)+TSS_SS0	/* likewise for debug task switch */
	mov	%cr3,%eax		/* get PDBR into debug TSS */
	mov	%eax,EXT(dbtss)+TSS_PDBR
	mov	$0,%eax
#endif

	movw	$(KERNEL_LDT),%ax	/* get LDT segment */
	lldt	%ax			/* load LDT */
#if	MACH_KDB
	mov	%ax,EXT(ktss)+TSS_LDT	/* store LDT in two TSS, as well... */
	mov	%ax,EXT(dbtss)+TSS_LDT	/*   ...matters if we switch tasks */
#endif
	movw	$(KERNEL_TSS),%ax
	ltr	%ax			/* set up KTSS */

	mov	$CPU_DATA,%ax
	mov	%ax,%gs

	lea	EXT(eintstack),%esp	/* switch to the bootup stack */
	call	EXT(machine_startup)	/* run C code */
	/*NOTREACHED*/
	hlt

#if	NCPUS > 1
/*
 * master_up is used by the master cpu to signify that it is done
 * with the interrupt stack, etc. See the code in pstart and svstart
 * that this interlocks with.
 */
	.align	ALIGN
	.globl	EXT(master_up)
LEXT(master_up)
	pushl	%ebp			/* set up */
	movl	%esp,%ebp		/* stack frame */
	movl	$0,%ecx			/* unlock start_lock */
	xchgl	%ecx,EXT(start_lock)	/* since we are no longer using */
					/* bootstrap stack */
	leave				/* pop stack frame */
	ret

/*
 * We aren't the first.  Call slave_main to initialize the processor
 * and get Mach going on it.
 */
	.align	ALIGN
	.globl	EXT(slave_start)
LEXT(slave_start)
	cli				/* disable interrupts, so we don`t */
					/* need IDT for a while */
	movl	EXT(kpde)+KVTOPHYS,%ebx	/* get PDE virtual address */
	addl	$(KVTOPHYS),%ebx	/* convert to physical address */

	movl	PA(EXT(mp_boot_pde)),%edx /* point to the bootstrap PDE */
	movl	KERNELBASEPDE(%ebx),%eax
					/* point to pte for KERNELBASE */
	movl	%eax,KERNELBASEPDE(%edx)
					/* set in bootstrap PDE */
	movl	%eax,(%edx)		/* set it also as pte for location */
					/* 0..3fffff, so that the code */
					/* that enters paged mode is mapped */
					/* to identical addresses after */
					/* paged mode is enabled */
	movl	%edx,%cr3		/* use bootstrap PDE to enable paging */

	movl	$EXT(spag_start),%edx	/* first paged code address */

	movl	%cr0,%eax
	orl	$(CR0_PG),%eax		/* set PG bit in CR0 */
	orl	$(CR0_WP),%eax
	movl	%eax,%cr0		/* to enable paging */

	jmp	*%edx			/* flush prefetch queue. */

/*
 * We are now paging, and can run with correct addresses.
 */
LEXT(spag_start)

	lgdt	EXT(gdtptr)		/* load GDT */
	lidt	EXT(idtptr)		/* load IDT */
	LJMP(KERNEL_CS,EXT(svstart))	/* switch to kernel code segment */

/*
 * Slave is now running with correct addresses.
 */
LEXT(svstart)
	mov	$(KERNEL_DS),%ax	/* set kernel data segment */
	mov	%ax,%ds
	mov	%ax,%es
	mov	%ax,%ss

	movl	%ebx,%cr3		/* switch to the real kernel PDE  */

	CPU_NUMBER(%eax)
	movl	CX(EXT(interrupt_stack),%eax),%esp /* get stack */
	addl	$(INTSTACK_SIZE),%esp	/* point to top */
	xorl	%ebp,%ebp		/* for completeness */

	movl	$0,%ecx			/* unlock start_lock */
	xchgl	%ecx,EXT(start_lock)	/* since we are no longer using */
					/* bootstrap stack */

/*
 * switch to the per-cpu descriptor tables
 */

	pushl	%eax			/* pass CPU number */
	call	EXT(mp_desc_init)	/* set up local table */
					/* pointer returned in %eax */
	subl	$4,%esp			/* get space to build pseudo-descriptors */
	
	CPU_NUMBER(%eax)
	movw	$(GDTSZ*8-1),0(%esp)	/* set GDT size in GDT descriptor */
	movl	CX(EXT(mp_gdt),%eax),%edx
	addl	$KVTOLINEAR,%edx
	movl	%edx,2(%esp)		/* point to local GDT (linear address) */
	lgdt	0(%esp)			/* load new GDT */
	
	movw	$(IDTSZ*8-1),0(%esp)	/* set IDT size in IDT descriptor */
	movl	CX(EXT(mp_idt),%eax),%edx
	addl	$KVTOLINEAR,%edx
	movl	%edx,2(%esp)		/* point to local IDT (linear address) */
	lidt	0(%esp)			/* load new IDT */
	
	movw	$(KERNEL_LDT),%ax
	lldt	%ax			/* load new LDT */
	
	movw	$(KERNEL_TSS),%ax
	ltr	%ax			/* load new KTSS */

	mov	$CPU_DATA,%ax
	mov	%ax,%gs

	call	EXT(slave_main)		/* start MACH */
	/*NOTREACHED*/
	hlt
#endif	/* NCPUS > 1 */

/*
 * Convert a descriptor from fake to real format.
 *
 * Calls from assembly code:
 * %ebx = return address (physical) CANNOT USE STACK
 * %esi	= descriptor table address (physical)
 * %ecx = number of descriptors
 *
 * Calls from C:
 * 0(%esp) = return address
 * 4(%esp) = descriptor table address (physical)
 * 8(%esp) = number of descriptors
 *
 * Fake descriptor format:
 *	bytes 0..3		base 31..0
 *	bytes 4..5		limit 15..0
 *	byte  6			access byte 2 | limit 19..16
 *	byte  7			access byte 1
 *
 * Real descriptor format:
 *	bytes 0..1		limit 15..0
 *	bytes 2..3		base 15..0
 *	byte  4			base 23..16
 *	byte  5			access byte 1
 *	byte  6			access byte 2 | limit 19..16
 *	byte  7			base 31..24
 *
 * Fake gate format:
 *	bytes 0..3		offset
 *	bytes 4..5		selector
 *	byte  6			word count << 4 (to match fake descriptor)
 *	byte  7			access byte 1
 *
 * Real gate format:
 *	bytes 0..1		offset 15..0
 *	bytes 2..3		selector
 *	byte  4			word count
 *	byte  5			access byte 1
 *	bytes 6..7		offset 31..16
 */
	.globl	EXT(fix_desc)
LEXT(fix_desc)
	pushl	%ebp			/* set up */
	movl	%esp,%ebp		/* stack frame */
	pushl	%esi			/* save registers */
	pushl	%ebx
	movl	B_ARG0,%esi		/* point to first descriptor */
	movl	B_ARG1,%ecx		/* get number of descriptors */
	lea	0f,%ebx			/* get return address */
	jmp	fix_desc_common		/* call internal routine */
0:	popl	%ebx			/* restore registers */
	popl	%esi
	leave				/* pop stack frame */
	ret				/* return */

fix_desc_common:
0:
	movw	6(%esi),%dx		/* get access byte */
	movb	%dh,%al
	andb	$0x14,%al
	cmpb	$0x04,%al		/* gate or descriptor? */
	je	1f

/* descriptor */
	movl	0(%esi),%eax		/* get base in eax */
	rol	$16,%eax		/* swap 15..0 with 31..16 */
					/* (15..0 in correct place) */
	movb	%al,%dl			/* combine bits 23..16 with ACC1 */
					/* in dh/dl */
	movb	%ah,7(%esi)		/* store bits 31..24 in correct place */
	movw	4(%esi),%ax		/* move limit bits 0..15 to word 0 */
	movl	%eax,0(%esi)		/* store (bytes 0..3 correct) */
	movw	%dx,4(%esi)		/* store bytes 4..5 */
	jmp	2f

/* gate */
1:
	movw	4(%esi),%ax		/* get selector */
	shrb	$4,%dl			/* shift word count to proper place */
	movw	%dx,4(%esi)		/* store word count / ACC1 */
	movw	2(%esi),%dx		/* get offset 16..31 */
	movw	%dx,6(%esi)		/* store in correct place */
	movw	%ax,2(%esi)		/* store selector in correct place */
2:
	addl	$8,%esi			/* bump to next descriptor */
	loop	0b			/* repeat */
	jmp	*%ebx			/* all done */

/*
 * put arg in kbd leds and spin a while
 * eats eax, ecx, edx
 */
#define	K_RDWR		0x60
#define	K_CMD_LEDS	0xed
#define	K_STATUS	0x64
#define	K_IBUF_FULL	0x02		/* input (to kbd) buffer full */
#define	K_OBUF_FULL	0x01		/* output (from kbd) buffer full */

ENTRY(set_kbd_leds)
	mov	S_ARG0,%cl		/* save led value */
	
0:	inb	$(K_STATUS),%al		/* get kbd status */
	testb	$(K_IBUF_FULL),%al	/* input busy? */
	jne	0b			/* loop until not */
	
	mov	$(K_CMD_LEDS),%al	/* K_CMD_LEDS */
	outb	%al,$(K_RDWR)		/* to kbd */

0:	inb	$(K_STATUS),%al		/* get kbd status */
	testb	$(K_OBUF_FULL),%al	/* output present? */
	je	0b			/* loop if not */

	inb	$(K_RDWR),%al		/* read status (and discard) */

0:	inb	$(K_STATUS),%al		/* get kbd status */
	testb	$(K_IBUF_FULL),%al	/* input busy? */
	jne	0b			/* loop until not */
	
	mov	%cl,%al			/* move led value */
	outb	%al,$(K_RDWR)		/* to kbd */

	movl	$10000000,%ecx		/* spin */
0:	nop
	nop
	loop	0b			/* a while */

	ret