Implemented first basic APM driver. Only tested with QEMU so far, that's why

it's currently disabled.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@16120 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Axel Dörfler 2006-01-28 18:11:20 +00:00
parent bfb4078864
commit afd6dfc8b4
13 changed files with 316 additions and 25 deletions

View File

@ -1,20 +1,25 @@
/*
* Copyright 2006, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2005, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
*
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef _KERNEL_ARCH_PLATFORM_H
#define _KERNEL_ARCH_PLATFORM_H
#include <SupportDefs.h>
struct kernel_args;
#ifdef __cplusplus
extern "C" {
#endif
status_t arch_platform_init(struct kernel_args *kernelArgs);
status_t arch_platform_init_post_vm(struct kernel_args *kernelArgs);
status_t arch_platform_init_post_thread(struct kernel_args *kernelArgs);
#ifdef __cplusplus
} // extern "C"

View File

@ -1,9 +1,38 @@
/*
* Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#ifndef KERNEL_APM_H
#define KERNEL_APM_H
#include <SupportDefs.h>
struct kernel_args;
// int 0x15 APM definitions
#define BIOS_APM_CHECK 0x5300
#define BIOS_APM_CONNECT_32_BIT 0x5303
#define BIOS_APM_DISCONNECT 0x5304
#define BIOS_APM_CPU_IDLE 0x5305
#define BIOS_APM_CPU_BUSY 0x5306
#define BIOS_APM_SET_STATE 0x5307
#define BIOS_APM_ENABLE 0x5308
#define BIOS_APM_GET_POWER_STATUS 0x530a
#define BIOS_APM_GET_EVENT 0x530b
#define BIOS_APM_GET_STATE 0x530c
#define BIOS_APM_VERSION 0x530e
#define BIOS_APM_ENGAGE 0x530f
// APM devices
#define APM_ALL_DEVICES 0x0001
// APM power states
#define APM_POWER_STATE_ENABLED 0x0000
#define APM_POWER_STATE_STANDBY 0x0001
#define APM_POWER_STATE_SUSPEND 0x0002
#define APM_POWER_STATE_OFF 0x0003
typedef struct apm_info {
uint16 version;
@ -18,4 +47,16 @@ typedef struct apm_info {
} apm_info;
#ifndef _BOOT_MODE
#ifdef __cplusplus
extern "C" {
#endif
status_t apm_init(struct kernel_args *args);
#ifdef __cplusplus
}
#endif
#endif // !_BOOT_MODE
#endif /* KERNEL_APM_H */

View File

@ -19,11 +19,13 @@
#define APM_CODE16_SEGMENT 0x30
#define APM_DATA_SEGMENT 0x38
#define BIOS_DATA_SEGMENT 0x40
#ifndef _ASSEMBLER
// this file can also be included from assembler as well
// (and is in arch_interrupts.S)
#define DOUBLE_FAULT_TSS_BASE_SEGMENT 8
#define DOUBLE_FAULT_TSS_BASE_SEGMENT 9
#define TSS_BASE_SEGMENT (DOUBLE_FAULT_TSS_BASE_SEGMENT + smp_get_num_cpus())
#define TLS_BASE_SEGMENT (TSS_BASE_SEGMENT + smp_get_num_cpus())
#define APM_BASE_SEGMENT (TLS_BASE_SEGMENT + smp_get_num_cpus())

View File

@ -6,7 +6,13 @@ UsePrivateHeaders [ FDirName kernel disk_device_manager ] ;
UsePrivateHeaders [ FDirName graphics vesa ] ;
UsePrivateHeaders [ FDirName storage ] ;
SubDirC++Flags -fno-rtti ;
{
local defines = _BOOT_MODE ;
defines = [ FDefines $(defines) ] ;
SubDirCcFlags $(defines) -Wall -Wno-multichar ;
SubDirC++Flags $(defines) -Wall -Wno-multichar -fno-rtti ;
}
KernelMergeObject boot_platform_bios_ia32.o :
shell.S

View File

@ -20,12 +20,6 @@
#endif
// int 0x15 APM definitions
#define BIOS_APM_CHECK 0x5300
#define BIOS_APM_CONNECT_32_BIT 0x5303
#define BIOS_APM_DISCONNECT 0x5304
status_t
apm_init(void)
{
@ -68,8 +62,11 @@ apm_init(void)
regs.eax = BIOS_APM_CONNECT_32_BIT;
regs.ebx = 0;
call_bios(0x15, &regs);
if ((regs.flags & CARRY_FLAG) != 0)
if ((regs.flags & CARRY_FLAG) != 0) {
// reset the version, so that the kernel won't try to use APM
info.version = 0;
return B_ERROR;
}
info.code32_segment_base = regs.eax & 0xffff;
info.code32_segment_offset = regs.ebx;

View File

@ -242,3 +242,10 @@ arch_platform_init_post_vm(struct kernel_args *kernelArgs)
{
return sPPCPlatform->InitPostVM(kernelArgs);
}
status_t
arch_platform_init_post_thread(struct kernel_args *kernelArgs)
{
return B_OK;
}

View File

@ -25,6 +25,7 @@ KernelStaticLibrary libx86 :
arch_interrupts.S
arch_system_info.c
arch_user_debugger.cpp
apm.cpp
bios.cpp
cpuid.S

View File

@ -0,0 +1,216 @@
/*
* Copyright 2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
#include <KernelExport.h>
#include <apm.h>
#include <descriptors.h>
#include <boot/kernel_args.h>
#define TRACE_APM
#ifdef TRACE_APM
# define TRACE(x) dprintf x
#else
# define TRACE(x) ;
#endif
#define CARRY_FLAG 0x01
extern segment_descriptor *gGDT;
extern void *gDmaAddress;
extern addr_t gBiosBase;
static struct {
uint32 offset;
uint16 segment;
} sAPMBiosEntry;
struct bios_regs {
bios_regs() : eax(0), ebx(0), ecx(0), edx(0), esi(0), flags(0) {}
uint32 eax;
uint32 ebx;
uint32 ecx;
uint32 edx;
uint32 esi;
uint32 flags;
};
static status_t
call_apm_bios(bios_regs *regs)
{
asm volatile(
"pushfl; "
"pushl %%ebp; "
"lcall *%%cs:sAPMBiosEntry; "
"popl %%ebp; "
"pushfl; "
"popl %%edi; "
"movl %%edi, %5; "
"popfl; "
: "=a" (regs->eax), "=b" (regs->ebx), "=c" (regs->ecx), "=d" (regs->edx),
"=S" (regs->esi), "=m" (regs->flags)
: "a" (regs->eax), "b" (regs->ebx), "c" (regs->ecx)
: "memory", "edi", "cc");
if (regs->flags & CARRY_FLAG)
return B_ERROR;
return B_OK;
}
static status_t
apm_get_event(uint16 &event, uint16 &info)
{
bios_regs regs;
regs.eax = BIOS_APM_GET_EVENT;
if (call_apm_bios(&regs) != B_OK)
return B_ERROR;
event = regs.ebx & 0xffff;
info = regs.ecx & 0xffff;
return B_OK;
}
static status_t
apm_set_state(uint16 device, uint16 state)
{
bios_regs regs;
regs.eax = BIOS_APM_SET_STATE;
regs.ebx = device;
regs.ecx = state;
return call_apm_bios(&regs);
}
static status_t
apm_enable_power_management(uint16 device, bool enable)
{
bios_regs regs;
regs.eax = BIOS_APM_ENABLE;
regs.ebx = device;
return call_apm_bios(&regs);
}
static status_t
apm_engage_power_management(uint16 device, bool enable)
{
bios_regs regs;
regs.eax = BIOS_APM_ENGAGE;
regs.ebx = device;
return call_apm_bios(&regs);
}
status_t
apm_driver_version(uint16 version)
{
dprintf("version: %x\n", version);
bios_regs regs;
regs.eax = BIOS_APM_VERSION;
regs.ecx = version;
if (call_apm_bios(&regs) != B_OK)
return B_ERROR;
dprintf("eax: %lx, flags: %lx\n", regs.eax, regs.flags);
return B_OK;
}
static void
apm_daemon(void *arg, int iteration)
{
uint16 event;
uint16 info;
if (apm_get_event(event, info) != B_OK)
return;
dprintf("APM event: %x, info: %x\n", event, info);
}
status_t
apm_init(kernel_args *args)
{
apm_info &info = args->platform_args.apm;
TRACE(("apm_init()\n"));
if ((info.version & 0xf) < 2) {
// no APM or connect failed
return B_OK;
}
TRACE((" code32: 0x%x, 0x%lx, length 0x%x\n",
info.code32_segment_base, info.code32_segment_offset, info.code32_segment_length));
TRACE((" code16: 0x%x, length 0x%x\n",
info.code16_segment_base, info.code16_segment_length));
TRACE((" data: 0x%x, length 0x%x\n",
info.data_segment_base, info.data_segment_length));
// TODO: remove me when tested on more hardware
if (1)
return B_OK;
// Apparently, some broken BIOS try to access segment 0x40 for the BIOS
// data section - we make sure it can by setting up the GDT accordingly
// (the first 640kB are mapped as DMA area in arch_vm_init()).
addr_t biosData = (addr_t)gDmaAddress + 0x400;
set_segment_descriptor(&gGDT[BIOS_DATA_SEGMENT >> 3],
biosData, B_PAGE_SIZE - biosData,
DT_DATA_WRITEABLE, DPL_KERNEL);
// TODO: test if APM segments really are in the BIOS ROM area (especially the
// data segment)
// setup APM GDTs
set_segment_descriptor(&gGDT[APM_CODE32_SEGMENT >> 3],
gBiosBase + (info.code32_segment_base << 4) - 0xe0000, 0xffff,
DT_CODE_READABLE, DPL_KERNEL);
set_segment_descriptor(&gGDT[APM_CODE16_SEGMENT >> 3],
gBiosBase + (info.code16_segment_base << 4) - 0xe0000, 0xffff,
DT_CODE_READABLE, DPL_KERNEL);
gGDT[APM_CODE16_SEGMENT >> 3].d_b = 0;
// 16-bit segment
set_segment_descriptor(&gGDT[APM_DATA_SEGMENT >> 3],
gBiosBase + (info.data_segment_base << 4) - 0xe0000, info.data_segment_length,
DT_DATA_WRITEABLE, DPL_KERNEL);
// setup APM entry point
sAPMBiosEntry.segment = APM_CODE32_SEGMENT;
sAPMBiosEntry.offset = info.code32_segment_offset;
apm_driver_version(info.version);
if (apm_enable_power_management(APM_ALL_DEVICES, true) != B_OK)
dprintf("APM: cannot enable power management.\n");
if (apm_engage_power_management(APM_ALL_DEVICES, true) != B_OK)
dprintf("APM: cannot engage.\n");
register_kernel_daemon(apm_daemon, NULL, 10);
// run the daemon once every second
//apm_set_state(APM_ALL_DEVICES, APM_POWER_STATE_OFF);
panic("done");
return B_OK;
}

View File

@ -273,8 +273,7 @@ arch_cpu_init_post_vm(kernel_args *args)
// add TSS descriptor for this new TSS
set_tss_descriptor(&gGDT[TSS_BASE_SEGMENT + i], (addr_t)sTSS[i], sizeof(struct tss));
// create double-fault task
sprintf(tssName, "double_fault_tss%lu", i);
area = create_area(tssName, (void **)&sDoubleFaultTSS[i], B_ANY_KERNEL_ADDRESS, B_PAGE_SIZE,

View File

@ -1,20 +1,34 @@
/*
* Copyright 2006, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
* All rights reserved. Distributed under the terms of the MIT License.
* Copyright 2006, Haiku, Inc. All Rights Reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Ingo Weinhold <bonefish@cs.tu-berlin.de>
* Axel Dörfler, axeld@pinc-software.de
*/
#include <arch/platform.h>
#include <apm.h>
status_t
arch_platform_init(struct kernel_args *kernelArgs)
arch_platform_init(struct kernel_args *args)
{
return B_OK;
}
status_t
arch_platform_init_post_vm(struct kernel_args *kernelArgs)
arch_platform_init_post_vm(struct kernel_args *args)
{
return B_OK;
}
status_t
arch_platform_init_post_thread(struct kernel_args *args)
{
return apm_init(args);
}

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de.
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de.
* Distributed under the terms of the MIT License.
*
* Copyright 2001, Travis Geiselbrecht. All rights reserved.
@ -33,6 +33,8 @@
#define kMaxMemoryTypeRegisters 32
void *gDmaAddress;
static uint32 sMemoryTypeBitmap;
static int32 sMemoryTypeIDs[kMaxMemoryTypeRegisters];
static int32 sMemoryTypeRegisterCount;
@ -164,7 +166,6 @@ arch_vm_init(kernel_args *args)
status_t
arch_vm_init_post_area(kernel_args *args)
{
void *dmaAddress;
area_id id;
TRACE(("arch_vm_init_post_area: entry\n"));
@ -174,7 +175,8 @@ arch_vm_init_post_area(kernel_args *args)
// map 0 - 0xa0000 directly
id = map_physical_memory("dma_region", (void *)0x0, 0xa0000,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, &dmaAddress);
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA,
&gDmaAddress);
if (id < 0) {
panic("arch_vm_init_post_area: unable to map dma region\n");
return B_NO_MEMORY;

View File

@ -1,5 +1,5 @@
/*
* Copyright 2004, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2004-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*/
@ -54,7 +54,7 @@ enum {
};
static addr_t sBiosBase;
addr_t gBiosBase;
static addr_t sBios32ServiceDirectory;
@ -121,17 +121,17 @@ bios_init(void)
{
// map BIOS area 0xe0000 - 0xfffff
area_id biosArea = map_physical_memory("pc bios", (void *)0xe0000, 0x20000,
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&sBiosBase);
B_ANY_KERNEL_ADDRESS, B_KERNEL_READ_AREA | B_KERNEL_WRITE_AREA, (void **)&gBiosBase);
if (biosArea < 0)
return biosArea;
TRACE(("PC BIOS mapped to %p\n", (void *)sBiosBase));
TRACE(("PC BIOS mapped to %p\n", (void *)gBiosBase));
// ToDo: add driver settings support to disable the services below
// search for available BIOS services
addr_t base = sBiosBase;
addr_t base = gBiosBase;
addr_t end = base + 0x20000;
while (base < end) {
@ -145,7 +145,7 @@ bios_init(void)
if (bios32->service_directory >= 0xe0000
&& bios32->service_directory <= 0xfffff)
sBios32ServiceDirectory = sBiosBase - 0xe0000 + bios32->service_directory;
sBios32ServiceDirectory = gBiosBase - 0xe0000 + bios32->service_directory;
}
break;
case SMBIOS:

View File

@ -1,5 +1,5 @@
/*
* Copyright 2002-2005, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Copyright 2002-2006, Axel Dörfler, axeld@pinc-software.de. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Copyright 2001-2002, Travis Geiselbrecht. All rights reserved.
@ -141,6 +141,7 @@ _start(kernel_args *bootKernelArgs, int currentCPU)
port_init(&sKernelArgs);
TRACE(("init kernel daemons\n"));
kernel_daemon_init();
arch_platform_init_post_thread(&sKernelArgs);
TRACE(("init VM threads\n"));
vm_init_post_thread(&sKernelArgs);