Work in progress:

- began to integrate the SMP/CPU stuff from the old boot loader; currently
  disabled, though
- began VESA/VGA support
- added temporary boot splash screen; it's a bit of a hack and only seem
  to work in Bochs 2.0.2 for now... (but hey, it looks good ;-))


git-svn-id: file:///srv/svn/repos/haiku/trunk/current@8016 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2004-06-17 01:02:40 +00:00
parent 754fdef3d7
commit eefe4b1d21
12 changed files with 4784 additions and 4 deletions

View File

@ -13,5 +13,10 @@ KernelMergeObject boot_platform_bios_ia32.o :
<$(SOURCE_GRIST)>console.cpp
<$(SOURCE_GRIST)>devices.cpp
<$(SOURCE_GRIST)>mmu.cpp
:
<$(SOURCE_GRIST)>cpu.cpp
<$(SOURCE_GRIST)>smp_boot.c
<$(SOURCE_GRIST)>smp_trampoline.S
<$(SOURCE_GRIST)>support.S
<$(SOURCE_GRIST)>video.cpp
: -fno-pic
;

View File

@ -7,6 +7,7 @@
#include <SupportDefs.h>
#include <string.h>
#include <util/kernel_cpp.h>
#include <boot/stage2.h>
#include "console.h"
@ -31,6 +32,9 @@ FILE *stdin, *stdout, *stderr;
static void
clear_screen()
{
if (gKernelArgs.fb.enabled)
return;
for (uint32 i = 0; i < sScreenWidth * sScreenHeight; i++)
sScreenBase[i] = 0xf20;
}
@ -71,6 +75,9 @@ Console::WriteAt(void *cookie, off_t /*pos*/, const void *buffer, size_t bufferS
{
const char *string = (const char *)buffer;
if (gKernelArgs.fb.enabled)
return bufferSize;
for (uint32 i = 0; i < bufferSize; i++) {
if (string[0] == '\n')
sScreenOffset += sScreenWidth - (sScreenOffset % sScreenWidth);

View File

@ -0,0 +1,238 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
**
** calculate_cpu_conversion_factor() was written by Travis Geiselbrecht and
** licensed under the NewOS license.
*/
#include "cpu.h"
#include <OS.h>
#include <boot/platform.h>
#include <boot/stdio.h>
#include <boot/kernel_args.h>
#include <boot/stage2.h>
#include <arch/cpu.h>
#include <arch_kernel.h>
#include <string.h>
#define TRACE_CPU
#ifdef TRACE_SMP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
extern "C" void smp_boot(void);
extern "C" uint64 rdtsc();
uint32 gTimeConversionFactor;
#define TIMER_CLKNUM_HZ (14318180/12)
static uint32
calculate_cpu_conversion_factor()
{
uint32 s_low, s_high;
uint32 low, high;
uint32 expired;
uint64 t1, t2;
uint64 p1, p2, p3;
double r1, r2, r3;
out8(0x34, 0x43); /* program the timer to count down mode */
out8(0xff, 0x40); /* low and then high */
out8(0xff, 0x40);
/* quick sample */
quick_sample:
do {
out8(0x00, 0x43); /* latch counter value */
s_low = in8(0x40);
s_high = in8(0x40);
} while(s_high != 255);
t1 = rdtsc();
do {
out8(0x00, 0x43); /* latch counter value */
low = in8(0x40);
high = in8(0x40);
} while (high > 224);
t2 = rdtsc();
p1 = t2-t1;
r1 = (double)(p1) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
/* not so quick sample */
not_so_quick_sample:
do {
out8(0x00, 0x43); /* latch counter value */
s_low = in8(0x40);
s_high = in8(0x40);
} while (s_high!= 255);
t1 = rdtsc();
do {
out8(0x00, 0x43); /* latch counter value */
low = in8(0x40);
high = in8(0x40);
} while (high> 192);
t2 = rdtsc();
p2 = t2-t1;
r2 = (double)(p2) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
if ((r1/r2) > 1.01) {
//dprintf("Tuning loop(1)\n");
goto quick_sample;
}
if ((r1/r2) < 0.99) {
//dprintf("Tuning loop(1)\n");
goto quick_sample;
}
/* slow sample */
do {
out8(0x00, 0x43); /* latch counter value */
s_low = in8(0x40);
s_high = in8(0x40);
} while (s_high!= 255);
t1 = rdtsc();
do {
out8(0x00, 0x43); /* latch counter value */
low = in8(0x40);
high = in8(0x40);
} while (high > 128);
t2 = rdtsc();
p3 = t2-t1;
r3 = (double)(p3) / (double)(((s_high << 8) | s_low) - ((high << 8) | low));
if ((r2/r3) > 1.01) {
TRACE(("Tuning loop(2)\n"));
goto not_so_quick_sample;
}
if ((r2/r3) < 0.99) {
TRACE(("Tuning loop(2)\n"));
goto not_so_quick_sample;
}
expired = ((s_high << 8) | s_low) - ((high << 8) | low);
p3 *= TIMER_CLKNUM_HZ;
/*
* cv_factor contains time in usecs per CPU cycle * 2^32
*
* The code below is a bit fancy. Originally Michael Noistering
* had it like:
*
* cv_factor = ((uint64)1000000<<32) * expired / p3;
*
* whic is perfect, but unfortunately 1000000ULL<<32*expired
* may overflow in fast cpus with the long sampling period
* i put there for being as accurate as possible under
* vmware.
*
* The below calculation is based in that we are trying
* to calculate:
*
* (C*expired)/p3 -> (C*(x0<<k + x1))/p3 ->
* (C*(x0<<k))/p3 + (C*x1)/p3
*
* Now the term (C*(x0<<k))/p3 is rewritten as:
*
* (C*(x0<<k))/p3 -> ((C*x0)/p3)<<k + reminder
*
* where reminder is:
*
* floor((1<<k)*decimalPart((C*x0)/p3))
*
* which is approximated as:
*
* floor((1<<k)*decimalPart(((C*x0)%p3)/p3)) ->
* (((C*x0)%p3)<<k)/p3
*
* So the final expression is:
*
* ((C*x0)/p3)<<k + (((C*x0)%p3)<<k)/p3 + (C*x1)/p3
*/
/*
* To get the highest accuracy with this method
* x0 should have the 12 most significant bits of expired
* to minimize the error upon <<k.
*/
/*
* Of course, you are not expected to understand any of this.
*/
{
unsigned i;
unsigned k;
uint64 C;
uint64 x0;
uint64 x1;
uint64 a, b, c;
/* first calculate k*/
k = 0;
for (i = 12; i < 16; i++) {
if (expired & (1<<i))
k = i - 11;
}
C = 1000000ULL << 32;
x0 = expired >> k;
x1 = expired & ((1 << k) - 1);
a = ((C * x0) / p3) << k;
b = (((C * x0) % p3) << k) / p3;
c = (C * x1) / p3;
#if 0
dprintf("a=%Ld\n", a);
dprintf("b=%Ld\n", b);
dprintf("c=%Ld\n", c);
dprintf("%d %Ld\n", expired, p3);
#endif
gTimeConversionFactor = a + b + c;
#if 0
dprintf("cvf=%Ld\n", cv_factor);
#endif
}
#ifdef TRACE_CPU
if (p3 / expired / 1000000000LL)
dprintf("CPU at %Ld.%03Ld GHz\n", p3/expired/1000000000LL, ((p3/expired)%1000000000LL)/1000000LL);
else
dprintf("CPU at %Ld.%03Ld MHz\n", p3/expired/1000000LL, ((p3/expired)%1000000LL)/1000LL);
#endif
return gTimeConversionFactor;
}
static status_t
check_cpu_features()
{
// ToDo: for now
return B_OK;
}
void
cpu_boot_other_cpus()
{
smp_boot();
}
void
cpu_init()
{
if (check_cpu_features() != B_OK)
panic("You need a Pentium or higher in order to boot!\n");
//gKernelArgs.arch_args.system_time_cv_factor = calculate_cpu_conversion_factor();
gKernelArgs.num_cpus = 1;
// this will eventually be corrected later on
}

View File

@ -0,0 +1,23 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
#ifndef CPU_H
#define CPU_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C" {
#endif
extern void cpu_init(void);
extern void cpu_boot_other_cpus(void);
#ifdef __cplusplus
}
#endif
#endif /* CPU_H */

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,533 @@
/*
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
**
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
// ToDo: this should be integrated better with the rest of the loader!
#include <KernelExport.h>
#include <boot/stage2.h>
#include <arch/x86/smp_apic.h>
#include <string.h>
// ToDo: SMP is temporarily disabled!
#define NO_SMP 1
//#define TRACE_SMP
#ifdef TRACE_SMP
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define ADDR_MASK 0xfffff000
#define DEFAULT_PAGE_FLAGS (1 | 2) // present/rw
#define STACK_SIZE 2
struct gdt_idt_descr {
uint16 a;
uint32 *b;
} _PACKED;
extern void execute_n_instructions(int count);
extern void smp_boot(void);
extern void smp_trampoline(void);
extern void smp_trampoline_end(void);
static uint32 mp_mem_phys = 0;
static uint32 mp_mem_virt = 0;
static struct mp_flt_struct *mp_flt_ptr = NULL;
static int smp_get_current_cpu(void);
static void
sleep(uint64 time)
{
uint64 start = system_time();
while(system_time() - start <= time)
;
}
static uint32
map_page(uint32 paddr, uint32 vaddr)
{
uint32 *pentry;
uint32 *pgdir = (uint32 *)(gKernelArgs.arch_args.page_hole + (4*1024*1024-B_PAGE_SIZE));
// check to see if a page table exists for this range
if (pgdir[vaddr / B_PAGE_SIZE / 1024] == 0) {
unsigned int pgtable;
// we need to allocate a pgtable
pgtable = gKernelArgs.physical_allocated_range[0].start + gKernelArgs.physical_allocated_range[0].size;
gKernelArgs.physical_allocated_range[0].size += B_PAGE_SIZE;
gKernelArgs.arch_args.pgtables[gKernelArgs.arch_args.num_pgtables++] = pgtable;
// put it in the pgdir
pgdir[vaddr / B_PAGE_SIZE / 1024] = (pgtable & ADDR_MASK) | DEFAULT_PAGE_FLAGS;
// zero it out in it's new mapping
memset((uint32 *)((uint32 *)gKernelArgs.arch_args.page_hole + (vaddr / B_PAGE_SIZE / 1024) * B_PAGE_SIZE), 0, B_PAGE_SIZE);
}
// now, fill in the pentry
pentry = (uint32 *)((uint32 *)gKernelArgs.arch_args.page_hole + vaddr / B_PAGE_SIZE);
*pentry = (paddr & ADDR_MASK) | DEFAULT_PAGE_FLAGS;
asm volatile("invlpg (%0)" : : "r" (vaddr));
return 0;
}
static uint32
apic_read(uint32 offset)
{
return *(uint32 *)((uint32)gKernelArgs.arch_args.apic + offset);
}
static void
apic_write(uint32 offset, uint32 data)
{
uint32 *addr = (uint32 *)((uint32)gKernelArgs.arch_args.apic + offset);
*addr = data;
}
/*
static void *
mp_virt_to_phys(void *ptr)
{
return ((void *)(((unsigned int)ptr - mp_mem_virt) + mp_mem_phys));
}
*/
static void *
mp_phys_to_virt(void *ptr)
{
return ((void *)(((uint32)ptr - mp_mem_phys) + mp_mem_virt));
}
static uint32 *
smp_probe(uint32 base, uint32 limit)
{
uint32 *ptr;
TRACE(("smp_probe: entry base 0x%lx, limit 0x%lx\n", base, limit));
for (ptr = (uint32 *) base; (uint32)ptr < limit; ptr++) {
if (*ptr == MP_FLT_SIGNATURE) {
TRACE(("smp_probe: found floating pointer structure at %p\n", ptr));
return ptr;
}
}
return NULL;
}
static void
smp_do_config(void)
{
char *ptr;
int i;
struct mp_config_table *mpc;
struct mp_ext_pe *pe;
struct mp_ext_ioapic *io;
struct mp_ext_bus *bus;
#if TRACE_SMP
const char *cpu_family[] = { "", "", "", "", "Intel 486",
"Intel Pentium", "Intel Pentium Pro", "Intel Pentium II" };
#endif
/*
* we are not running in standard configuration, so we have to look through
* all of the mp configuration table crap to figure out how many processors
* we have, where our apics are, etc.
*/
gKernelArgs.num_cpus = 0;
mpc = mp_phys_to_virt(mp_flt_ptr->mpc);
/* print out our new found configuration. */
ptr = (char *) &(mpc->oem[0]);
TRACE(("smp: oem id: %c%c%c%c%c%c%c%c product id: "
"%c%c%c%c%c%c%c%c%c%c%c%c\n", ptr[0], ptr[1], ptr[2], ptr[3], ptr[4],
ptr[5], ptr[6], ptr[7], ptr[8], ptr[9], ptr[10], ptr[11], ptr[12],
ptr[13], ptr[14], ptr[15], ptr[16], ptr[17], ptr[18], ptr[19]));
TRACE(("smp: base table has %d entries, extended section %d bytes\n",
mpc->num_entries, mpc->ext_len));
gKernelArgs.arch_args.apic_phys = (uint32)mpc->apic;
ptr = (char *)((uint32)mpc + sizeof(struct mp_config_table));
for (i = 0; i < mpc->num_entries; i++) {
switch (*ptr) {
case MP_EXT_PE:
pe = (struct mp_ext_pe *) ptr;
gKernelArgs.arch_args.cpu_apic_id[gKernelArgs.num_cpus] = pe->apic_id;
gKernelArgs.arch_args.cpu_os_id[pe->apic_id] = gKernelArgs.num_cpus;
gKernelArgs.arch_args.cpu_apic_version[gKernelArgs.num_cpus] = pe->apic_version;
TRACE(("smp: cpu#%ld: %s, apic id %d, version %d%s\n",
gKernelArgs.num_cpus, cpu_family[(pe->signature & 0xf00) >> 8],
pe->apic_id, pe->apic_version, (pe->cpu_flags & 0x2) ?
", BSP" : ""));
ptr += 20;
gKernelArgs.num_cpus++;
break;
case MP_EXT_BUS:
bus = (struct mp_ext_bus *)ptr;
TRACE(("smp: bus%d: %c%c%c%c%c%c\n", bus->bus_id,
bus->name[0], bus->name[1], bus->name[2], bus->name[3],
bus->name[4], bus->name[5]));
ptr += 8;
break;
case MP_EXT_IO_APIC:
io = (struct mp_ext_ioapic *) ptr;
gKernelArgs.arch_args.ioapic_phys = (uint32)io->addr;
TRACE(("smp: found io apic with apic id %d, version %d\n",
io->ioapic_id, io->ioapic_version));
ptr += 8;
break;
case MP_EXT_IO_INT:
ptr += 8;
break;
case MP_EXT_LOCAL_INT:
ptr += 8;
break;
}
}
dprintf("smp: apic @ %p, i/o apic @ %p, total %ld processors detected\n",
(void *)gKernelArgs.arch_args.apic_phys, (void *)gKernelArgs.arch_args.ioapic_phys, gKernelArgs.num_cpus);
// this BIOS looks broken, because it didn't report any cpus (VMWare)
if (gKernelArgs.num_cpus == 0)
gKernelArgs.num_cpus = 1;
}
struct smp_scan_spots_struct {
uint32 start;
uint32 stop;
uint32 len;
};
static struct smp_scan_spots_struct smp_scan_spots[] = {
{ 0x9fc00, 0xa0000, 0xa0000 - 0x9fc00 },
{ 0xf0000, 0x100000, 0x100000 - 0xf0000 },
{ 0, 0, 0 }
};
static int
smp_find_mp_config(void)
{
int i;
// XXX for now, assume the memory is identity mapped by the 1st stage
for (i = 0; smp_scan_spots[i].len > 0; i++) {
mp_flt_ptr = (struct mp_flt_struct *)smp_probe(smp_scan_spots[i].start,
smp_scan_spots[i].stop);
if (mp_flt_ptr != NULL)
break;
}
#if NO_SMP
if (0) {
#else
if (mp_flt_ptr != NULL) {
#endif
mp_mem_phys = smp_scan_spots[i].start;
mp_mem_virt = smp_scan_spots[i].start;
TRACE(("smp_boot: intel mp version %s, %s", (mp_flt_ptr->mp_rev == 1) ? "1.1" :
"1.4", (mp_flt_ptr->mp_feature_2 & 0x80) ?
"imcr and pic compatibility mode.\n" : "virtual wire compatibility mode.\n"));
if (mp_flt_ptr->mpc == 0) {
// XXX need to implement
#if 1
gKernelArgs.num_cpus = 1;
return 1;
#else
/* this system conforms to one of the default configurations */
// mp_num_def_config = mp_flt_ptr->mp_feature_1;
TRACE(("smp: standard configuration %d\n", mp_flt_ptr->mp_feature_1));
/* num_cpus = 2;
gKernelArgs.cpu_apic_id[0] = 0;
gKernelArgs.cpu_apic_id[1] = 1;
apic_phys = (unsigned int *) 0xfee00000;
ioapic_phys = (unsigned int *) 0xfec00000;
kprintf ("smp: WARNING: standard configuration code is untested");
*/
#endif
} else {
smp_do_config();
}
return gKernelArgs.num_cpus;
} else {
gKernelArgs.num_cpus = 1;
return 1;
}
}
/** Target function of the trampoline code.
* The trampoline code should have the pgdir and a gdt set up for us,
* along with us being on the final stack for this processor. We need
* to set up the local APIC and load the global idt and gdt. When we're
* done, we'll jump into the kernel with the cpu number as an argument.
*/
static int
smp_cpu_ready(void)
{
uint32 curr_cpu = smp_get_current_cpu();
struct gdt_idt_descr idt_descr;
struct gdt_idt_descr gdt_descr;
TRACE(("smp_cpu_ready: entry cpu %ld\n", curr_cpu));
// Important. Make sure supervisor threads can fault on read only pages...
asm("movl %%eax, %%cr0" : : "a" ((1 << 31) | (1 << 16) | (1 << 5) | 1));
asm("cld");
asm("fninit");
// Set up the final idt
idt_descr.a = IDT_LIMIT - 1;
idt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_idt;
asm("lidt %0;"
: : "m" (idt_descr));
// Set up the final gdt
gdt_descr.a = GDT_LIMIT - 1;
gdt_descr.b = (uint32 *)gKernelArgs.arch_args.vir_gdt;
asm("lgdt %0;"
: : "m" (gdt_descr));
asm("pushl %0; " // push the cpu number
"pushl %1; " // kernel args
"pushl $0x0;" // dummy retval for call to main
"pushl %2; " // this is the start address
"ret; " // jump.
: : "r" (curr_cpu), "m" (&gKernelArgs), "g" (gKernelArgs.kernel_image.elf_header.e_entry));
// no where to return to
return 0;
}
static int
smp_boot_all_cpus(void)
{
uint32 trampoline_code;
uint32 trampoline_stack;
uint32 i;
// XXX assume low 1 meg is identity mapped by the 1st stage bootloader
// and nothing important is in 0x9e000 & 0x9f000
// allocate a stack and a code area for the smp trampoline
// (these have to be < 1M physical)
trampoline_code = 0x9f000; // 640kB - 4096 == 0x9f000
trampoline_stack = 0x9e000; // 640kB - 8192 == 0x9e000
map_page(0x9f000, 0x9f000);
map_page(0x9e000, 0x9e000);
// copy the trampoline code over
memcpy((char *)trampoline_code, &smp_trampoline,
(uint32)&smp_trampoline_end - (uint32)&smp_trampoline);
// boot the cpus
for (i = 1; i < gKernelArgs.num_cpus; i++) {
uint32 *final_stack;
uint32 *final_stack_ptr;
uint32 *tramp_stack_ptr;
uint32 config;
uint32 num_startups;
uint32 j;
// create a final stack the trampoline code will put the ap processor on
gKernelArgs.cpu_kstack[i].start = gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size;
gKernelArgs.cpu_kstack[i].size = STACK_SIZE * B_PAGE_SIZE;
for (j = 0; j < gKernelArgs.cpu_kstack[i].size / B_PAGE_SIZE; j++) {
// map the pages in
map_page(gKernelArgs.physical_allocated_range[0].start + gKernelArgs.physical_allocated_range[0].size,
gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size);
gKernelArgs.physical_allocated_range[0].size += B_PAGE_SIZE;
gKernelArgs.virtual_allocated_range[0].size += B_PAGE_SIZE;
}
// set this stack up
final_stack = (uint32 *)gKernelArgs.cpu_kstack[i].start;
memset(final_stack, 0, STACK_SIZE * B_PAGE_SIZE);
final_stack_ptr = (final_stack + (STACK_SIZE * B_PAGE_SIZE) / sizeof(uint32)) - 1;
*final_stack_ptr = (uint32)&smp_cpu_ready;
final_stack_ptr--;
// set the trampoline stack up
tramp_stack_ptr = (uint32 *)(trampoline_stack + B_PAGE_SIZE - 4);
// final location of the stack
*tramp_stack_ptr = ((uint32)final_stack) + STACK_SIZE * B_PAGE_SIZE - sizeof(uint32);
tramp_stack_ptr--;
// page dir
*tramp_stack_ptr = gKernelArgs.arch_args.phys_pgdir;
tramp_stack_ptr--;
// put a gdt descriptor at the bottom of the stack
*((uint16 *)trampoline_stack) = 0x18 - 1; // LIMIT
*((uint32 *)(trampoline_stack + 2)) = trampoline_stack + 8;
// put the gdt at the bottom
memcpy(&((uint32 *)trampoline_stack)[2], (void *)gKernelArgs.arch_args.vir_gdt, 6*4);
/* clear apic errors */
if (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) {
apic_write(APIC_ESR, 0);
apic_read(APIC_ESR);
}
/* send (aka assert) INIT IPI */
config = (apic_read(APIC_ICR2) & 0x00ffffff) | (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
apic_write(APIC_ICR2, config); /* set target pe */
config = (apic_read(APIC_ICR1) & 0xfff00000) | 0x0000c500;
apic_write(APIC_ICR1, config);
// wait for pending to end
while ((apic_read(APIC_ICR1) & 0x00001000) == 0x00001000)
;
/* deassert INIT */
config = (apic_read(APIC_ICR2) & 0x00ffffff) | (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
apic_write(APIC_ICR2, config);
config = (apic_read(APIC_ICR1) & 0xfff00000) | 0x00008500;
apic_write(APIC_ICR1, config);
// wait for pending to end
while ((apic_read(APIC_ICR1) & 0x00001000) == 0x00001000)
;
/* wait 10ms */
sleep(10000);
/* is this a local apic or an 82489dx ? */
num_startups = (gKernelArgs.arch_args.cpu_apic_version[i] & 0xf0) ? 2 : 0;
for (j = 0; j < num_startups; j++) {
/* it's a local apic, so send STARTUP IPIs */
apic_write(APIC_ESR, 0);
/* set target pe */
config = (apic_read(APIC_ICR2) & 0xf0ffffff) | (gKernelArgs.arch_args.cpu_apic_id[i] << 24);
apic_write(APIC_ICR2, config);
/* send the IPI */
config = (apic_read(APIC_ICR1) & 0xfff0f800) | APIC_DM_STARTUP |
(0x9f000 >> 12);
apic_write(APIC_ICR1, config);
/* wait */
sleep(200);
while ((apic_read(APIC_ICR1)& 0x00001000) == 0x00001000)
;
}
}
return 0;
}
static void
calculate_apic_timer_conversion_factor(void)
{
int64 t1, t2;
uint32 config;
uint32 count;
// setup the timer
config = apic_read(APIC_LVTT);
config = (config & ~APIC_LVTT_MASK) + APIC_LVTT_M; // timer masked, vector 0
apic_write(APIC_LVTT, config);
config = (apic_read(APIC_TDCR) & ~0x0000000f) + 0xb; // divide clock by one
apic_write(APIC_TDCR, config);
t1 = system_time();
apic_write(APIC_ICRT, 0xffffffff); // start the counter
execute_n_instructions(128*20000);
count = apic_read(APIC_CCRT);
t2 = system_time();
count = 0xffffffff - count;
gKernelArgs.arch_args.apic_time_cv_factor = (uint32)((1000000.0/(t2 - t1)) * count);
TRACE(("APIC ticks/sec = %ld\n", gKernelArgs.arch_args.apic_time_cv_factor));
}
void
smp_boot(void)
{
// dprintf("smp_boot: entry\n");
if (smp_find_mp_config() > 1) {
TRACE(("smp_boot: had found > 1 cpus\n"));
TRACE(("post config:\n"));
TRACE(("num_cpus = %ld\n", gKernelArgs.num_cpus));
TRACE(("apic_phys = %p\n", (void *)gKernelArgs.arch_args.apic_phys));
TRACE(("ioapic_phys = %p\n", (void *)gKernelArgs.arch_args.ioapic_phys));
// map in the apic & ioapic
map_page(gKernelArgs.arch_args.apic_phys, gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size);
gKernelArgs.arch_args.apic = (uint32 *)(gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size);
gKernelArgs.virtual_allocated_range[0].size += B_PAGE_SIZE;
map_page(gKernelArgs.arch_args.ioapic_phys, gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size);
gKernelArgs.arch_args.ioapic = (uint32 *)(gKernelArgs.virtual_allocated_range[0].start + gKernelArgs.virtual_allocated_range[0].size);
gKernelArgs.virtual_allocated_range[0].size += B_PAGE_SIZE;
TRACE(("apic = %p\n", gKernelArgs.arch_args.apic));
TRACE(("ioapic = %p\n", gKernelArgs.arch_args.ioapic));
// calculate how fast the apic timer is
calculate_apic_timer_conversion_factor();
TRACE(("trampolining other cpus\n"));
smp_boot_all_cpus();
TRACE(("done trampolining\n"));
}
TRACE(("smp_boot: exit\n"));
}
static int
smp_get_current_cpu(void)
{
if (gKernelArgs.arch_args.apic == NULL)
return 0;
return gKernelArgs.arch_args.cpu_os_id[(apic_read(APIC_ID) & 0xffffffff) >> 24];
}

View File

@ -0,0 +1,69 @@
/*
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
// expects a stack page like this:
// (stack has to be at (0x9e000)
// 0x9effc : final esp
// 0x9eff8 : page dir
//
// 0x9e000 - 0x9e006 : gdt descriptor
// 0x9e008 - 0x9e020 : gdt
//
// smp_trampoline must be located at 0x9f000
.globl smp_trampoline
.globl smp_trampoline_end
.globl foo
.code16
smp_trampoline:
cli
mov $0x9e00,%ax
mov %ax,%ds
// lgdt 0x9e000 # load the gdt
.byte 0x66, 0x0f, 0x01, 0x15, 0x00, 0xe0, 0x09, 0x00
movl %cr0,%eax
orl $0x01,%eax
movl %eax,%cr0 # switch into protected mode
.code32
_trampoline_32:
.byte 0x66
ljmp $0x08,$(trampoline_32 - smp_trampoline + 0x9f000)
trampoline_32:
mov $0x10, %ax
mov %ax, %ds
mov %ax, %es
mov %ax, %fs
mov %ax, %gs
mov %ax, %ss
movl $0x9eff8,%esp # set up the stack pointer
popl %eax # get the page dir
movl %eax,%cr3 # set the page dir
popl %eax # get the final stack location
movl %eax,%esp
// load an address for an indirect jump
movl $trampoline_after_paging,%ecx
movl %cr0,%eax
orl $0x80000000,%eax
movl %eax,%cr0 # enable paging
// jump to the address previously loaded. NOTE:
// this address is the address at which the code is originally linked,
// which is > 1MB. We will be out of the low memory at this point.
jmp *%ecx
trampoline_after_paging:
// just return, the bsp would have set the return address to the
// target function at the top of the passed stack
ret
smp_trampoline_end:

View File

@ -7,6 +7,7 @@
#include "console.h"
#include "cpu.h"
#include "mmu.h"
#include "video.h"
#include <SupportDefs.h>
#include <boot/platform.h>
@ -62,7 +63,7 @@ platform_start_kernel(void)
// or I don't see something important...
mmu_init_for_kernel();
//cpu_boot_other_cpus();
cpu_boot_other_cpus();
printf("kernel entry at %lx\n", gKernelArgs.kernel_image.elf_header.e_entry);
@ -75,6 +76,8 @@ platform_start_kernel(void)
"pushl %1; " // this is the start address
"ret; " // jump.
: : "g" (args), "g" (gKernelArgs.kernel_image.elf_header.e_entry));
panic("kernel returned!\n");
}
@ -87,15 +90,15 @@ _start(void)
asm("fninit"); // initialize floating point unit
clear_bss();
call_ctors();
// call C++ constructors before doing anything else
args.heap_size = HEAP_SIZE;
console_init();
//cpu_init();
cpu_init();
mmu_init();
video_init();
main(&args);
}

View File

@ -0,0 +1,84 @@
/*
** Copyright 2001, Travis Geiselbrecht. All rights reserved.
** Distributed under the terms of the NewOS License.
*/
/* uint64 rdtsc() */
.global rdtsc
rdtsc:
rdtsc
ret
.global execute_n_instructions
execute_n_instructions:
movl 4(%esp), %ecx
shrl $4, %ecx
.again:
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
xorl %eax, %eax
loop .again
ret
.global system_time
system_time:
/* load 64-bit factor into %eax (low), %edx (high) */
/* hand-assemble rdtsc -- read time stamp counter */
rdtsc /* time in %edx,%eax */
pushl %ebx
movl gTimeConversionFactor, %ebx
movl %edx, %ecx /* save high half */
mull %ebx /* truncate %eax, but keep %edx */
movl %ecx, %eax
movl %edx, %ecx /* save high half of low */
mull %ebx /*, %eax*/
/* now compute [%edx, %eax] + [%ecx], propagating carry */
subl %ebx, %ebx /* need zero to propagate carry */
addl %ecx, %eax
adc %ebx, %edx
popl %ebx
ret
.global cpuid
cpuid:
pushl %ebx
pushl %edi
movl 12(%esp),%eax
movl 16(%esp),%edi
cpuid
movl %eax,0(%edi)
movl %ebx,4(%edi)
movl %ecx,8(%edi)
movl %edx,12(%edi)
popl %edi
popl %ebx
ret
.global get_eflags
get_eflags:
pushfl
popl %eax
ret
.global set_eflags
set_eflags:
pushl 4(%esp)
popfl
ret

View File

@ -0,0 +1,136 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
#ifndef VESA_H
#define VESA_H
#include <SupportDefs.h>
/* VBE info block structure */
#define VESA_SIGNATURE 'ASEV'
#define VBE2_SIGNATURE '2EBV'
struct vbe_info_block {
// VBE 1.x fields
uint32 signature;
struct {
uint8 minor;
uint8 major;
} version;
uint32 oem_string;
uint32 capabilities;
uint32 mode_list;
uint16 total_memory; // in 64k blocks
// VBE 2.0+ fields only
// Note, the block is 256 bytes in size for VBE 1.x as well,
// but doesn't define these fields. VBE 3 doesn't define
// any additional fields.
uint16 oem_software_revision;
uint32 oem_vendor_name_string;
uint32 oem_product_name_string;
uint32 oem_product_revision_string;
uint8 reserved[222];
uint8 oem_data[256];
} _PACKED;
/* VBE mode info structure */
struct vbe_mode_info {
uint16 attributes;
uint8 window_a_attributes;
uint8 window_b_attributes;
uint16 window_granulatiry;
uint16 window_size;
uint16 window_a_segment;
uint16 window_b_segment;
uint32 window_function; // real mode pointer
uint16 bytes_per_row;
// VBE 1.2 and above
uint16 width;
uint16 height;
uint8 char_width;
uint8 char_height;
uint8 num_planes;
uint8 bits_per_pixel;
uint8 num_banks;
uint8 memory_model;
uint8 bank_size;
uint8 num_image_pages;
uint8 _reserved0;
// direct color fields
uint8 red_mask_size;
uint8 red_field_position;
uint8 green_mask_size;
uint8 green_field_position;
uint8 blue_mask_size;
uint8 blue_field_position;
uint8 reserved_mask_size;
uint8 reserved_field_position;
uint8 direct_color_mode_info;
// VBE 2.0 and above
uint32 physical_base;
uint32 _reserved1;
uint16 _reserved2;
// VBE 3.0 and above
uint16 linear_bytes_per_row;
uint8 banked_num_image_pages;
uint8 linear_num_image_pages;
uint8 linear_red_mask_size;
uint8 linear_red_field_position;
uint8 linear_green_mask_size;
uint8 linear_green_field_position;
uint8 linear_blue_mask_size;
uint8 linear_blue_field_position;
uint8 linear_reserved_mask_size;
uint8 linear_reserved_field_position;
uint32 max_pixel_clock; // in Hz
uint8 _reserved[189];
} _PACKED;
// definitions of mode info attributes
#define MODE_ATTR_AVAILABLE 1
#define MODE_ATTR_COLOR_MODE 8
#define MODE_ATTR_GRAPHICS_MODE 16
#define MODE_ATTR_LINEAR_BUFFER 128
// memory models
#define MODE_MEMORY_TEXT 0
#define MODE_MEMORY_PLANAR 3
#define MODE_MEMORY_PACKED_PIXEL 4
#define MODE_MEMORY_DIRECT_COLOR 6
#define MODE_MEMORY_YUV 7
/* VBE 3.0 protected mode interface
* The BIOS area can be scanned for the protected mode
* signature that identifies the structure below.
*/
#define VBE_PM_SIGNATURE 'DIMP'
struct vbe_protected_mode_info {
uint32 signature;
int16 entry_offset;
int16 init_offset;
uint16 data_selector;
uint16 a000_selector;
uint16 b000_selector;
uint16 b800_selector;
uint16 c000_selector;
uint8 in_protected_mode;
uint8 checksum;
} _PACKED;
#endif /* VESA_H */

View File

@ -0,0 +1,301 @@
/*
** Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
** Distributed under the terms of the OpenBeOS License.
*/
#include "video.h"
#include "bios.h"
#include "vesa.h"
#include "mmu.h"
#include "images.h"
#include <arch/cpu.h>
#include <boot/stage2.h>
#include <boot/platform.h>
#include <boot/kernel_args.h>
#include <string.h>
static vbe_info_block sInfo;
uint16 sMode;
bool sVesaCompatible;
static void
vga_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
{
out8(firstIndex, 0x03c8);
// write VGA palette
for (int32 i = firstIndex; i < numEntries; i++) {
// VGA (usually) has only 6 bits per gun
out8(palette[i * 3 + 0] >> 2, 0x03c9);
out8(palette[i * 3 + 1] >> 2, 0x03c9);
out8(palette[i * 3 + 2] >> 2, 0x03c9);
}
}
// #pragma mark -
// VESA functions
static status_t
vesa_get_mode_info(uint16 mode, struct vbe_mode_info *modeInfo)
{
struct vbe_mode_info *info = (vbe_mode_info *)kExtraSegmentScratch;
struct bios_regs regs;
regs.eax = 0x4f01;
regs.ecx = mode;
regs.es = 0;
regs.edi = (addr_t)info;
call_bios(0x10, &regs);
if ((regs.eax & 0xffff) != 0x4f)
return B_ENTRY_NOT_FOUND;
memcpy(modeInfo, info, sizeof(vbe_mode_info));
return B_OK;
}
static addr_t
fix_vbe_pointer(addr_t address, vbe_info_block *target)
{
uint16 segment = address >> 16;
uint16 offset = address & 0xffff;
address = (segment << 4) + offset;
// if the address is a pointer in the info block itself, it has
// to be relocated to the new info block address
if (address >= kExtraSegmentScratch && address <= kExtraSegmentScratch + sizeof(vbe_info_block))
address += (addr_t)target - kExtraSegmentScratch;
return address;
}
static status_t
vesa_init(vbe_info_block *vbeInfo, uint16 *_standardMode)
{
struct vbe_info_block *info = (vbe_info_block *)kExtraSegmentScratch;
memset(info, 0, sizeof(vbe_info_block));
info->signature = VBE2_SIGNATURE;
struct bios_regs regs;
regs.eax = 0x4f00;
regs.es = 0;
regs.edi = (addr_t)info;
call_bios(0x10, &regs);
if ((regs.eax & 0xffff) != 0x4f)
return B_ERROR;
if (info->signature != VESA_SIGNATURE)
return B_ERROR;
dprintf("VESA version = %lx\n", info->version);
if (info->version.major < 2) {
dprintf("VESA support too old\n", info->version);
return B_ERROR;
}
info->oem_string = fix_vbe_pointer(info->oem_string, vbeInfo);
info->mode_list = fix_vbe_pointer(info->mode_list, vbeInfo);
dprintf("oem string: %s\n", (const char *)info->oem_string);
memcpy(vbeInfo, info, sizeof(vbe_info_block));
dprintf("mode list:\n");
int32 i = 0;
while (true) {
uint16 mode = ((uint16 *)vbeInfo->mode_list)[i++];
if (mode == 0xffff)
break;
dprintf(" %lx: ", mode);
struct vbe_mode_info modeInfo;
if (vesa_get_mode_info(mode, &modeInfo) == B_OK) {
dprintf("%ld x %ld x %ld (a = %ld, mem = %ld, phy = %lx, p = %ld, b = %ld)\n",
modeInfo.width, modeInfo.height, modeInfo.bits_per_pixel, modeInfo.attributes,
modeInfo.memory_model, modeInfo.physical_base, modeInfo.num_planes,
modeInfo.num_banks);
const uint32 requiredAttributes = MODE_ATTR_AVAILABLE | MODE_ATTR_GRAPHICS_MODE
| MODE_ATTR_COLOR_MODE | MODE_ATTR_LINEAR_BUFFER;
if (modeInfo.width >= 800
&& modeInfo.physical_base != 0
&& modeInfo.num_planes == 1
&& (modeInfo.memory_model == MODE_MEMORY_PACKED_PIXEL
|| modeInfo.memory_model == MODE_MEMORY_DIRECT_COLOR)
&& (modeInfo.attributes & requiredAttributes) == requiredAttributes
&& modeInfo.bits_per_pixel == 8) {
*_standardMode = mode;
return B_OK;
}
} else
dprintf("(failed)\n");
}
// no usable VESA mode found...
return B_ERROR;
}
#if 0
static status_t
vesa_get_mode(uint16 *_mode)
{
struct bios_regs regs;
regs.eax = 0x4f03;
call_bios(0x10, &regs);
if ((regs.eax & 0xffff) != 0x4f)
return B_ERROR;
*_mode = regs.ebx & 0xffff;
return B_OK;
}
#endif
static status_t
vesa_set_mode(uint16 mode)
{
struct bios_regs regs;
regs.eax = 0x4f02;
regs.ebx = mode;
call_bios(0x10, &regs);
if ((regs.eax & 0xffff) != 0x4f)
return B_ERROR;
#if 0
// make sure we have 8 bits per color channel
regs.eax = 0x4f08;
regs.ebx = 8 << 8;
call_bios(0x10, &regs);
#endif
return B_OK;
}
static status_t
vesa_set_palette(const uint8 *palette, int32 firstIndex, int32 numEntries)
{
#if 0
// is this an 8 bit indexed color mode?
if (gKernelArgs.fb.bit_depth = modeInfo.bits_per_pixel != 8)
return B_BAD_TYPE;
#endif
struct bios_regs regs;
regs.eax = 0x4f09;
regs.ebx = 0;
regs.ecx = numEntries;
regs.edx = firstIndex;
regs.es = (addr_t)palette >> 4;
regs.edi = (addr_t)palette & 0xf;
call_bios(0x10, &regs);
if ((regs.eax & 0xffff) != 0x4f) {
// the VESA call does not work, just try good old VGA mechanism
vga_set_palette(palette, firstIndex, numEntries);
return B_ERROR;
}
return B_OK;
}
// #pragma mark -
extern "C" void
platform_switch_to_logo(void)
{
if (!sVesaCompatible)
// no logo for now...
return;
if (vesa_set_mode(sMode) != B_OK)
return;
gKernelArgs.fb.enabled = 1;
if (!gKernelArgs.fb.already_mapped) {
// the graphics memory has not been mapped yet!
struct vbe_mode_info modeInfo;
if (vesa_get_mode_info(sMode, &modeInfo) != B_OK) {
platform_switch_to_text_mode();
return;
}
gKernelArgs.fb.x_size = modeInfo.width;
gKernelArgs.fb.y_size = modeInfo.height;
gKernelArgs.fb.bit_depth = modeInfo.bits_per_pixel;
gKernelArgs.fb.mapping.size = gKernelArgs.fb.x_size * gKernelArgs.fb.y_size * (gKernelArgs.fb.bit_depth/8);
gKernelArgs.fb.mapping.start = mmu_map_physical_memory(modeInfo.physical_base, gKernelArgs.fb.mapping.size, 0x03);
gKernelArgs.fb.already_mapped = 1;
}
if (vesa_set_palette((const uint8 *)kPalette, 0, 256) != B_OK)
dprintf("set palette failed!\n");
#if 0
uint8 *bits = (uint8 *)gKernelArgs.fb.mapping.start;
uint32 bytesPerRow = gKernelArgs.fb.x_size;
for (int32 y = 10; y < 30; y++) {
for (int32 i = 0; i < 256; i++) {
bits[y * bytesPerRow + i * 3] = i;
bits[y * bytesPerRow + i * 3 + 1] = i;
bits[y * bytesPerRow + i * 3 + 2] = i;
}
}
#endif
// ToDo: this is a temporary hack!
addr_t start = gKernelArgs.fb.mapping.start + gKernelArgs.fb.x_size * (gKernelArgs.fb.y_size - 200) * (gKernelArgs.fb.bit_depth/8) + gKernelArgs.fb.x_size - 400;
for (int32 i = 0; i < kHeight; i++) {
memcpy((void *)(start + gKernelArgs.fb.x_size * i), &kImageData[i * kWidth], kWidth);
}
}
extern "C" void
platform_switch_to_text_mode(void)
{
if (!gKernelArgs.fb.enabled)
return;
bios_regs regs;
regs.eax = 3;
call_bios(0x10, &regs);
gKernelArgs.fb.enabled = 0;
}
// #pragma mark -
extern "C" status_t
video_init(void)
{
gKernelArgs.fb.enabled = 0;
sVesaCompatible = vesa_init(&sInfo, &sMode) == B_OK;
if (!sVesaCompatible) {
dprintf("No VESA compatible graphics!\n");
return B_ERROR;
}
dprintf("VESA compatible graphics!\n");
return B_OK;
}

View File

@ -0,0 +1,13 @@
#ifndef VIDEO_H
#define VIDEO_H
#include <SupportDefs.h>
#ifdef __cplusplus
extern "C"
#endif
status_t video_init(void);
#endif /* VIDEO_H */