/* * 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 #include #include #include #include #include #include #include #include #include #include #include #define MACH_INCLUDE #include #include #include #include #include #include #include #include /* * 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");*/ }