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:
parent
754fdef3d7
commit
eefe4b1d21
@ -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
|
||||
;
|
||||
|
@ -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);
|
||||
|
238
src/kernel/boot/platform/bios_ia32/cpu.cpp
Normal file
238
src/kernel/boot/platform/bios_ia32/cpu.cpp
Normal 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
|
||||
}
|
||||
|
23
src/kernel/boot/platform/bios_ia32/cpu.h
Normal file
23
src/kernel/boot/platform/bios_ia32/cpu.h
Normal 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 */
|
3368
src/kernel/boot/platform/bios_ia32/images.h
Normal file
3368
src/kernel/boot/platform/bios_ia32/images.h
Normal file
File diff suppressed because it is too large
Load Diff
533
src/kernel/boot/platform/bios_ia32/smp_boot.c
Normal file
533
src/kernel/boot/platform/bios_ia32/smp_boot.c
Normal 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];
|
||||
}
|
69
src/kernel/boot/platform/bios_ia32/smp_trampoline.S
Normal file
69
src/kernel/boot/platform/bios_ia32/smp_trampoline.S
Normal 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:
|
||||
|
@ -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);
|
||||
}
|
||||
|
84
src/kernel/boot/platform/bios_ia32/support.S
Normal file
84
src/kernel/boot/platform/bios_ia32/support.S
Normal 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
|
||||
|
||||
|
136
src/kernel/boot/platform/bios_ia32/vesa.h
Normal file
136
src/kernel/boot/platform/bios_ia32/vesa.h
Normal 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 */
|
301
src/kernel/boot/platform/bios_ia32/video.cpp
Normal file
301
src/kernel/boot/platform/bios_ia32/video.cpp
Normal 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, ®s);
|
||||
|
||||
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, ®s);
|
||||
|
||||
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, ®s);
|
||||
|
||||
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, ®s);
|
||||
|
||||
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, ®s);
|
||||
#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, ®s);
|
||||
|
||||
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, ®s);
|
||||
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;
|
||||
}
|
||||
|
13
src/kernel/boot/platform/bios_ia32/video.h
Normal file
13
src/kernel/boot/platform/bios_ia32/video.h
Normal 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 */
|
Loading…
Reference in New Issue
Block a user