Source to osfmk/i386/mp_desc.c
/*
* 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 [email protected]
* 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 <cpus.h>
#if NCPUS > 1
#include <kern/cpu_number.h>
#include <kern/cpu_data.h>
#include <mach/machine.h>
#include <vm/vm_kern.h>
#include <i386/mp_desc.h>
#include <i386/lock.h>
#include <i386/misc_protos.h>
#include <kern/misc_protos.h>
#include <mach_kdb.h>
/*
* The i386 needs an interrupt stack to keep the PCB stack from being
* overrun by interrupts. All interrupt stacks MUST lie at lower addresses
* than any thread`s kernel stack.
*/
/*
* Addresses of bottom and top of interrupt stacks.
*/
vm_offset_t interrupt_stack[NCPUS];
vm_offset_t int_stack_top[NCPUS];
/*
* Barrier address.
*/
vm_offset_t int_stack_high;
/*
* First cpu`s interrupt stack.
*/
extern char intstack[]; /* bottom */
extern char eintstack[]; /* top */
/*
* We allocate interrupt stacks from physical memory.
*/
extern
vm_offset_t avail_start;
/*
* Multiprocessor i386/i486 systems use a separate copy of the
* GDT, IDT, LDT, and kernel TSS per processor. The first three
* are separate to avoid lock contention: the i386 uses locked
* memory cycles to access the descriptor tables. The TSS is
* separate since each processor needs its own kernel stack,
* and since using a TSS marks it busy.
*/
/*
* Allocated descriptor tables.
*/
struct mp_desc_table *mp_desc_table[NCPUS] = { 0 };
/*
* Pointer to TSS for access in load_context.
*/
struct i386_tss *mp_ktss[NCPUS] = { 0 };
#if MACH_KDB
/*
* Pointer to TSS for debugger use.
*/
struct i386_tss *mp_dbtss[NCPUS] = { 0 };
#endif /* MACH_KDB */
/*
* Pointer to GDT to reset the KTSS busy bit.
*/
struct fake_descriptor *mp_gdt[NCPUS] = { 0 };
struct fake_descriptor *mp_idt[NCPUS] = { 0 };
/*
* Allocate and initialize the per-processor descriptor tables.
*/
struct fake_descriptor ldt_desc_pattern = {
(unsigned int) 0,
LDTSZ * sizeof(struct fake_descriptor) - 1,
0,
ACC_P|ACC_PL_K|ACC_LDT
};
struct fake_descriptor tss_desc_pattern = {
(unsigned int) 0,
sizeof(struct i386_tss),
0,
ACC_P|ACC_PL_K|ACC_TSS
};
struct fake_descriptor cpudata_desc_pattern = {
(unsigned int) 0,
sizeof(cpu_data_t)-1,
SZ_32,
ACC_P|ACC_PL_K|ACC_DATA_W
};
struct mp_desc_table *
mp_desc_init(
int mycpu)
{
register struct mp_desc_table *mpt;
if (mycpu == master_cpu) {
/*
* Master CPU uses the tables built at boot time.
* Just set the TSS and GDT pointers.
*/
mp_ktss[mycpu] = &ktss;
#if MACH_KDB
mp_dbtss[mycpu] = &dbtss;
#endif /* MACH_KDB */
mp_gdt[mycpu] = gdt;
mp_idt[mycpu] = idt;
return 0;
}
else {
mpt = mp_desc_table[mycpu];
mp_ktss[mycpu] = &mpt->ktss;
mp_gdt[mycpu] = mpt->gdt;
mp_idt[mycpu] = mpt->idt;
/*
* Copy the tables
*/
bcopy((char *)idt,
(char *)mpt->idt,
sizeof(idt));
bcopy((char *)gdt,
(char *)mpt->gdt,
sizeof(gdt));
bcopy((char *)ldt,
(char *)mpt->ldt,
sizeof(ldt));
bzero((char *)&mpt->ktss,
sizeof(struct i386_tss));
bzero((char *)&cpu_data[mycpu],
sizeof(cpu_data_t));
#if MACH_KDB
mp_dbtss[mycpu] = &mpt->dbtss;
bcopy((char *)&dbtss,
(char *)&mpt->dbtss,
sizeof(struct i386_tss));
#endif /* MACH_KDB */
/*
* Fix up the entries in the GDT to point to
* this LDT and this TSS.
*/
mpt->gdt[sel_idx(KERNEL_LDT)] = ldt_desc_pattern;
mpt->gdt[sel_idx(KERNEL_LDT)].offset =
LINEAR_KERNEL_ADDRESS + (unsigned int) mpt->ldt;
fix_desc(&mpt->gdt[sel_idx(KERNEL_LDT)], 1);
mpt->gdt[sel_idx(KERNEL_TSS)] = tss_desc_pattern;
mpt->gdt[sel_idx(KERNEL_TSS)].offset =
LINEAR_KERNEL_ADDRESS + (unsigned int) &mpt->ktss;
fix_desc(&mpt->gdt[sel_idx(KERNEL_TSS)], 1);
mpt->gdt[sel_idx(CPU_DATA)] = cpudata_desc_pattern;
mpt->gdt[sel_idx(CPU_DATA)].offset =
LINEAR_KERNEL_ADDRESS + (unsigned int) &cpu_data[mycpu];
fix_desc(&mpt->gdt[sel_idx(CPU_DATA)], 1);
#if MACH_KDB
mpt->gdt[sel_idx(DEBUG_TSS)] = tss_desc_pattern;
mpt->gdt[sel_idx(DEBUG_TSS)].offset =
LINEAR_KERNEL_ADDRESS + (unsigned int) &mpt->dbtss;
fix_desc(&mpt->gdt[sel_idx(DEBUG_TSS)], 1);
mpt->dbtss.esp0 = (int)(db_task_stack_store +
(INTSTACK_SIZE * (mycpu + 1)) - sizeof (natural_t));
mpt->dbtss.esp = mpt->dbtss.esp0;
mpt->dbtss.eip = (int)&db_task_start;
#endif /* MACH_KDB */
mpt->ktss.ss0 = KERNEL_DS;
mpt->ktss.io_bit_map_offset = 0x0FFF; /* no IO bitmap */
return mpt;
}
}
/*
* Called after all CPUs have been found, but before the VM system
* is running. The machine array must show which CPUs exist.
*/
void
interrupt_stack_alloc(void)
{
register int i;
int cpu_count;
vm_offset_t stack_start;
struct mp_desc_table *mpt;
/*
* Count the number of CPUs.
*/
cpu_count = 0;
for (i = 0; i < NCPUS; i++)
if (machine_slot[i].is_cpu)
cpu_count++;
/*
* Allocate an interrupt stack for each CPU except for
* the master CPU (which uses the bootstrap stack)
*/
stack_start = phystokv(avail_start);
avail_start = round_page(avail_start + INTSTACK_SIZE*(cpu_count-1));
bzero((char *)stack_start, INTSTACK_SIZE*(cpu_count-1));
/*
* Set up pointers to the top of the interrupt stack.
*/
for (i = 0; i < NCPUS; i++) {
if (i == master_cpu) {
interrupt_stack[i] = (vm_offset_t) intstack;
int_stack_top[i] = (vm_offset_t) eintstack;
}
else if (machine_slot[i].is_cpu) {
interrupt_stack[i] = stack_start;
int_stack_top[i] = stack_start + INTSTACK_SIZE;
stack_start += INTSTACK_SIZE;
}
}
/*
* Allocate descriptor tables for each CPU except for
* the master CPU (which already has them initialized)
*/
mpt = (struct mp_desc_table *) phystokv(avail_start);
avail_start = round_page((vm_offset_t)avail_start +
sizeof(struct mp_desc_table)*(cpu_count-1));
for (i = 0; i < NCPUS; i++)
if (i != master_cpu)
mp_desc_table[i] = mpt++;
/*
* Set up the barrier address. All thread stacks MUST
* be above this address.
*/
/*
* intstack is at higher addess than stack_start for AT mps
* so int_stack_high must point at eintstack.
* XXX
* But what happens if a kernel stack gets allocated below
* 1 Meg ? Probably never happens, there is only 640 K available
* There.
*/
int_stack_high = (vm_offset_t) eintstack;
}
#endif /* NCPUS > 1 */