vaseboot/VasEBoot-core/kern/i386/pc/init.c

281 lines
7.5 KiB
C

/*
* VAS_EBOOT -- GRand Unified Bootloader
* Copyright (C) 2002,2003,2004,2005,2006,2007,2008,2009,2010 Free Software Foundation, Inc.
*
* VAS_EBOOT 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 3 of the License, or
* (at your option) any later version.
*
* VAS_EBOOT 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 VAS_EBOOT. If not, see <http://www.gnu.org/licenses/>.
*/
#include <VasEBoot/kernel.h>
#include <VasEBoot/mm.h>
#include <VasEBoot/machine/boot.h>
#include <VasEBoot/i386/floppy.h>
#include <VasEBoot/machine/memory.h>
#include <VasEBoot/machine/console.h>
#include <VasEBoot/machine/kernel.h>
#include <VasEBoot/machine/int.h>
#include <VasEBoot/types.h>
#include <VasEBoot/err.h>
#include <VasEBoot/dl.h>
#include <VasEBoot/misc.h>
#include <VasEBoot/loader.h>
#include <VasEBoot/env.h>
#include <VasEBoot/cache.h>
#include <VasEBoot/time.h>
#include <VasEBoot/cpu/cpuid.h>
#include <VasEBoot/cpu/tsc.h>
#include <VasEBoot/machine/time.h>
struct mem_region
{
VasEBoot_addr_t addr;
VasEBoot_size_t size;
};
#define MAX_REGIONS 32
static struct mem_region mem_regions[MAX_REGIONS];
static int num_regions;
void (*VasEBoot_pc_net_config) (char **device, char **path);
/*
* return the real time in ticks, of which there are about
* 18-20 per second
*/
VasEBoot_uint64_t
VasEBoot_rtc_get_time_ms (void)
{
struct VasEBoot_bios_int_registers regs;
regs.eax = 0;
regs.flags = VAS_EBOOT_CPU_INT_FLAGS_DEFAULT;
VasEBoot_bios_interrupt (0x1a, &regs);
return ((regs.ecx << 16) | (regs.edx & 0xffff)) * 55ULL;
}
void
VasEBoot_machine_get_bootlocation (char **device, char **path)
{
char *ptr;
VasEBoot_uint8_t boot_drive, dos_part, bsd_part;
boot_drive = (VasEBoot_boot_device >> 24);
dos_part = (VasEBoot_boot_device >> 16);
bsd_part = (VasEBoot_boot_device >> 8);
/* No hardcoded root partition - make it from the boot drive and the
partition number encoded at the install time. */
if (boot_drive == VAS_EBOOT_BOOT_MACHINE_PXE_DL)
{
if (VasEBoot_pc_net_config)
VasEBoot_pc_net_config (device, path);
return;
}
/* XXX: This should be enough. */
#define DEV_SIZE 100
*device = VasEBoot_malloc (DEV_SIZE);
ptr = *device;
VasEBoot_snprintf (*device, DEV_SIZE,
"%cd%u", (boot_drive & 0x80) ? 'h' : 'f',
boot_drive & 0x7f);
ptr += VasEBoot_strlen (ptr);
if (dos_part != 0xff)
VasEBoot_snprintf (ptr, DEV_SIZE - (ptr - *device),
",%u", dos_part + 1);
ptr += VasEBoot_strlen (ptr);
if (bsd_part != 0xff)
VasEBoot_snprintf (ptr, DEV_SIZE - (ptr - *device), ",%u",
bsd_part + 1);
ptr += VasEBoot_strlen (ptr);
*ptr = 0;
}
/* Add a memory region. */
static void
add_mem_region (VasEBoot_addr_t addr, VasEBoot_size_t size)
{
if (num_regions == MAX_REGIONS)
/* Ignore. */
return;
mem_regions[num_regions].addr = addr;
mem_regions[num_regions].size = size;
num_regions++;
}
/* Compact memory regions. */
static void
compact_mem_regions (void)
{
int i, j;
/* Sort them. */
for (i = 0; i < num_regions - 1; i++)
for (j = i + 1; j < num_regions; j++)
if (mem_regions[i].addr > mem_regions[j].addr)
{
struct mem_region tmp = mem_regions[i];
mem_regions[i] = mem_regions[j];
mem_regions[j] = tmp;
}
/* Merge overlaps. */
for (i = 0; i < num_regions - 1; i++)
if (mem_regions[i].addr + mem_regions[i].size >= mem_regions[i + 1].addr)
{
j = i + 1;
if (mem_regions[i].addr + mem_regions[i].size
< mem_regions[j].addr + mem_regions[j].size)
mem_regions[i].size = (mem_regions[j].addr + mem_regions[j].size
- mem_regions[i].addr);
VasEBoot_memmove (mem_regions + j, mem_regions + j + 1,
(num_regions - j - 1) * sizeof (struct mem_region));
i--;
num_regions--;
}
}
VasEBoot_addr_t VasEBoot_modbase;
extern VasEBoot_uint8_t _start[], _edata[];
/* Helper for VasEBoot_machine_init. */
static int
mmap_iterate_hook (VasEBoot_uint64_t addr, VasEBoot_uint64_t size,
VasEBoot_memory_type_t type,
void *data __attribute__ ((unused)))
{
/* Avoid the lower memory. */
if (addr < VAS_EBOOT_MEMORY_MACHINE_UPPER_START)
{
if (size <= VAS_EBOOT_MEMORY_MACHINE_UPPER_START - addr)
return 0;
size -= VAS_EBOOT_MEMORY_MACHINE_UPPER_START - addr;
addr = VAS_EBOOT_MEMORY_MACHINE_UPPER_START;
}
/* Ignore >4GB. */
if (addr <= 0xFFFFFFFF && type == VAS_EBOOT_MEMORY_AVAILABLE)
{
VasEBoot_size_t len;
len = (VasEBoot_size_t) ((addr + size > 0xFFFFFFFF)
? 0xFFFFFFFF - addr
: size);
add_mem_region (addr, len);
}
return 0;
}
extern VasEBoot_uint16_t VasEBoot_bios_via_workaround1, VasEBoot_bios_via_workaround2;
/* Via needs additional wbinvd. */
static void
VasEBoot_via_workaround_init (void)
{
VasEBoot_uint32_t manufacturer[3], max_cpuid, proc_info;
if (! VasEBoot_cpu_is_cpuid_supported ())
return;
VasEBoot_cpuid (0, max_cpuid, manufacturer[0], manufacturer[2], manufacturer[1]);
if (VasEBoot_memcmp (manufacturer, "CentaurHauls", 12) != 0)
return;
if (max_cpuid > 0)
{
VasEBoot_cpuid (1, proc_info, /* Don't care. */ manufacturer[0],
manufacturer[2], manufacturer[1]);
/* Check model, apply only to VIA C3 and lower. */
if (((proc_info & 0xf0) >> 4 | (proc_info & 0xf0000) >> 12) > 10)
return;
}
VasEBoot_bios_via_workaround1 = 0x090f;
VasEBoot_bios_via_workaround2 = 0x090f;
asm volatile ("wbinvd");
}
void
VasEBoot_machine_init (void)
{
int i;
#if 0
int VasEBoot_lower_mem;
#endif
VasEBoot_addr_t modend;
/* This has to happen before any BIOS calls. */
VasEBoot_via_workaround_init ();
VasEBoot_modbase = VAS_EBOOT_MEMORY_MACHINE_DECOMPRESSION_ADDR + (_edata - _start);
/* Initialize the console as early as possible. */
VasEBoot_console_init ();
/* This sanity check is useless since top of VAS_EBOOT_MEMORY_MACHINE_RESERVED_END
is used for stack and if it's unavailable we wouldn't have gotten so far.
*/
#if 0
VasEBoot_lower_mem = VasEBoot_get_conv_memsize () << 10;
/* Sanity check. */
if (VasEBoot_lower_mem < VAS_EBOOT_MEMORY_MACHINE_RESERVED_END)
VasEBoot_fatal ("too small memory");
#endif
/* FIXME: This prevents loader/i386/linux.c from using low memory. When our
heap implements support for requesting a chunk in low memory, this should
no longer be a problem. */
#if 0
/* Add the lower memory into free memory. */
if (VasEBoot_lower_mem >= VAS_EBOOT_MEMORY_MACHINE_RESERVED_END)
add_mem_region (VAS_EBOOT_MEMORY_MACHINE_RESERVED_END,
VasEBoot_lower_mem - VAS_EBOOT_MEMORY_MACHINE_RESERVED_END);
#endif
VasEBoot_machine_mmap_iterate (mmap_iterate_hook, NULL);
compact_mem_regions ();
modend = VasEBoot_modules_get_end ();
for (i = 0; i < num_regions; i++)
{
VasEBoot_addr_t beg = mem_regions[i].addr;
VasEBoot_addr_t fin = mem_regions[i].addr + mem_regions[i].size;
if (modend && beg < modend)
beg = modend;
if (beg >= fin)
continue;
VasEBoot_mm_init_region ((void *) beg, fin - beg);
}
VasEBoot_tsc_init ();
}
void
VasEBoot_machine_fini (int flags)
{
if (flags & VAS_EBOOT_LOADER_FLAG_NORETURN)
VasEBoot_console_fini ();
VasEBoot_stop_floppy ();
}