|
|
GNU Mach 1.0
/*
* Linux initialization.
*
* Copyright (C) 1996 The University of Utah and the Computer Systems
* Laboratory at the University of Utah (CSL)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
*
* Author: Shantanu Goel, University of Utah CSL
*/
/*
* linux/init/main.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*/
#include <sys/types.h>
#include <mach/vm_param.h>
#include <mach/vm_prot.h>
#include <mach/machine.h>
#include <vm/vm_page.h>
#include <i386/ipl.h>
#include <i386/pic.h>
#include <i386/pit.h>
#include <i386/machspl.h>
#include <i386/pmap.h>
#include <i386/vm_param.h>
#include <i386at/gpl/linux/linux_emul.h>
#define MACH_INCLUDE
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/ioport.h>
#include <linux/string.h>
#include <asm/system.h>
#include <i386/device-drivers.h>
/*
* Set if the machine has an EISA bus.
*/
int EISA_bus = 0;
/*
* Timing loop count.
*/
unsigned long loops_per_sec = 1;
/*
* End of physical memory.
*/
unsigned long high_memory;
/*
* Flag to indicate auto-configuration is in progress.
*/
int linux_auto_config = 1;
/*
* Hard drive parameters obtained from the BIOS.
*/
struct drive_info_struct {
char dummy[32];
} drive_info;
/*
* Forward declarations.
*/
static void calibrate_delay(void);
extern int hz;
extern vm_offset_t phys_last_addr;
extern void timer_bh(void *);
extern void tqueue_bh(void *);
extern void startrtclock(void);
extern void linux_version_init(void);
extern void linux_kmem_init(void);
extern unsigned long pci_init(unsigned long, unsigned long);
extern void linux_net_emulation_init (void);
extern void device_setup(void);
extern void linux_printk(char *, ...);
extern int linux_timer_intr();
/*
* Amount of contiguous memory to allocate for initialization.
*/
#define CONTIG_ALLOC (512 * 1024)
/*
* Initialize Linux drivers.
*/
void
linux_init()
{
char *p;
int i, addr;
int (*old_clock_handler)(), old_clock_pri;
unsigned memory_start, memory_end;
vm_page_t pages;
/*
* Initialize memory size.
*/
high_memory = phys_last_addr;
/*
* Ensure interrupts are disabled.
*/
(void) splhigh();
/*
* Program counter 0 of 8253 to interrupt hz times per second.
*/
outb(PITCTL_PORT, PIT_C0|PIT_SQUAREMODE|PIT_READMODE);
outb(PITCTR0_PORT, CLKNUM / hz);
outb(PITCTR0_PORT, (CLKNUM / hz) >> 8);
/*
* Install our clock interrupt handler.
*/
old_clock_handler = ivect[0];
old_clock_pri = intpri[0];
ivect[0] = linux_timer_intr;
intpri[0] = SPLHI;
form_pic_mask();
/*
* Enable interrupts.
*/
(void) spl0();
/*
* Set Linux version.
*/
linux_version_init();
/*
* Check if the machine has an EISA bus.
*/
p = (char *)0x0FFFD9;
if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A')
EISA_bus = 1;
/*
* Permanently allocate standard device ports.
*/
request_region(0x00, 0x20, "dma1");
request_region(0x40, 0x20, "timer");
request_region(0x70, 0x10, "rtc");
request_region(0x80, 0x20, "dma page reg");
request_region(0xc0, 0x20, "dma2");
request_region(0xf0, 0x02, "fpu");
request_region(0xf8, 0x08, "fpu");
/*
* Install software interrupt handlers.
*/
bh_base[TIMER_BH].routine = timer_bh;
bh_base[TIMER_BH].data = 0;
enable_bh(TIMER_BH);
bh_base[TQUEUE_BH].routine = tqueue_bh;
bh_base[TQUEUE_BH].data = 0;
enable_bh(TQUEUE_BH);
/*
* Set loop count.
*/
calibrate_delay();
/*
* Initialize drive info.
*/
addr = *((unsigned *)phystokv(0x104));
memcpy (&drive_info,
(void *)((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16);
addr = *((unsigned *)phystokv(0x118));
memcpy ((char *)&drive_info + 16,
(void *)((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16);
/*
* Initialize Linux memory allocator.
*/
linux_kmem_init();
/*
* Allocate contiguous memory below 16 MB.
*/
memory_start = (unsigned long)alloc_contig_mem(CONTIG_ALLOC,
16 * 1024 * 1024,
0, &pages);
if (memory_start == 0)
panic("linux_init: alloc_contig_mem failed");
memory_end = memory_start + CONTIG_ALLOC;
/*
* Initialize PCI bus.
*/
memory_start = pci_init(memory_start, memory_end);
if (memory_start > memory_end)
panic("linux_init: ran out memory");
/*
* Free unused memory.
*/
while (pages && pages->phys_addr < round_page(memory_start))
pages = (vm_page_t)pages->pageq.next;
if (pages)
free_contig_mem(pages);
/*
* Initialize devices.
*/
#ifdef CONFIG_INET
linux_net_emulation_init();
#endif
cli();
device_setup();
/*
* Disable interrupts.
*/
(void) splhigh();
/*
* Restore clock interrupt handler.
*/
ivect[0] = old_clock_handler;
intpri[0] = old_clock_pri;
form_pic_mask();
linux_auto_config = 0;
}
#ifndef NBPW
#define NBPW 32
#endif
/*
* Allocate contiguous memory with the given constraints.
* This routine is horribly inefficient but it is presently
* only used during initialization so it's not that bad.
*/
void *
alloc_contig_mem(unsigned size, unsigned limit,
unsigned mask, vm_page_t *pages)
{
int i, j, bits_len;
unsigned *bits, len;
void *m;
vm_page_t p, page_list, tail, prev;
vm_offset_t addr, max_addr;
if (size == 0)
return (NULL);
size = round_page(size);
if ((size >> PAGE_SHIFT) > vm_page_free_count)
return (NULL);
/* Allocate bit array. */
max_addr = phys_last_addr;
if (max_addr > limit)
max_addr = limit;
bits_len = ((((max_addr >> PAGE_SHIFT) + NBPW - 1) / NBPW)
* sizeof(unsigned));
bits = (unsigned *)kalloc(bits_len);
if (!bits)
return (NULL);
memset (bits, 0, bits_len);
/*
* Walk the page free list and set a bit for every usable page.
*/
simple_lock(&vm_page_queue_free_lock);
p = vm_page_queue_free;
while (p) {
if (p->phys_addr < limit)
(bits[(p->phys_addr >> PAGE_SHIFT) / NBPW]
|= 1 << ((p->phys_addr >> PAGE_SHIFT) % NBPW));
p = (vm_page_t)p->pageq.next;
}
/*
* Scan bit array for contiguous pages.
*/
len = 0;
m = NULL;
for (i = 0; len < size && i < bits_len / sizeof (unsigned); i++)
for (j = 0; len < size && j < NBPW; j++)
if (!(bits[i] & (1 << j))) {
len = 0;
m = NULL;
} else {
if (len == 0) {
addr = ((vm_offset_t)(i * NBPW + j)
<< PAGE_SHIFT);
if ((addr & mask) == 0) {
len += PAGE_SIZE;
m = (void *) addr;
}
} else
len += PAGE_SIZE;
}
if (len != size) {
simple_unlock(&vm_page_queue_free_lock);
kfree ((vm_offset_t)bits, bits_len);
return (NULL);
}
/*
* Remove pages from free list
* and construct list to return to caller.
*/
page_list = NULL;
for (len = 0; len < size; len += PAGE_SIZE, addr += PAGE_SIZE) {
prev = NULL;
for (p = vm_page_queue_free; p; p = (vm_page_t)p->pageq.next) {
if (p->phys_addr == addr)
break;
prev = p;
}
if (!p)
panic("alloc_contig_mem: page not on free list");
if (prev)
prev->pageq.next = p->pageq.next;
else
vm_page_queue_free = (vm_page_t)p->pageq.next;
p->free = FALSE;
p->pageq.next = NULL;
if (!page_list)
page_list = tail = p;
else {
tail->pageq.next = (queue_entry_t)p;
tail = p;
}
vm_page_free_count--;
}
simple_unlock(&vm_page_queue_free_lock);
kfree((vm_offset_t)bits, bits_len);
if (pages)
*pages = page_list;
return (m);
}
/*
* Free memory allocated by alloc_contig_mem.
*/
void
free_contig_mem(vm_page_t pages)
{
int i;
vm_page_t p;
for (p = pages, i = 0; p->pageq.next; p = (vm_page_t)p->pageq.next, i++)
p->free = TRUE;
p->free = TRUE;
simple_lock(&vm_page_queue_free_lock);
vm_page_free_count += i + 1;
p->pageq.next = (queue_entry_t)vm_page_queue_free;
vm_page_queue_free = pages;
simple_unlock(&vm_page_queue_free_lock);
}
/*
* Calibrate delay loop.
* Lifted straight from Linux.
*/
static void
calibrate_delay()
{
int ticks;
/* printk("Calibrating delay loop.. "); */
while (loops_per_sec <<= 1) {
/* Wait for "start of" clock tick. */
ticks = jiffies;
while (ticks == jiffies)
/* nothing */;
/* Go .. */
ticks = jiffies;
__delay(loops_per_sec);
ticks = jiffies - ticks;
if (ticks >= hz) {
loops_per_sec = muldiv(loops_per_sec,
hz, ticks);
/*printk("ok - %lu.%02lu BogoMips\n",
loops_per_sec / 500000,
(loops_per_sec / 5000) % 100);*/
return;
}
}
/* printk("failed\n");*/
}
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.