From afd6dfc8b400a598ff4b610951548c5a7d2007cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Axel=20D=C3=B6rfler?= Date: Sat, 28 Jan 2006 18:11:20 +0000 Subject: [PATCH] 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 --- headers/private/kernel/arch/platform.h | 5 + headers/private/kernel/arch/x86/apm.h | 41 ++++ headers/private/kernel/arch/x86/descriptors.h | 4 +- src/system/boot/platform/bios_ia32/Jamfile | 8 +- src/system/boot/platform/bios_ia32/apm.cpp | 11 +- src/system/kernel/arch/ppc/arch_platform.cpp | 7 + src/system/kernel/arch/x86/Jamfile | 1 + src/system/kernel/arch/x86/apm.cpp | 216 ++++++++++++++++++ src/system/kernel/arch/x86/arch_cpu.c | 3 +- src/system/kernel/arch/x86/arch_platform.c | 22 +- src/system/kernel/arch/x86/arch_vm.c | 8 +- src/system/kernel/arch/x86/bios.cpp | 12 +- src/system/kernel/main.c | 3 +- 13 files changed, 316 insertions(+), 25 deletions(-) create mode 100644 src/system/kernel/arch/x86/apm.cpp diff --git a/headers/private/kernel/arch/platform.h b/headers/private/kernel/arch/platform.h index 471a4ecf96..2766f05c5f 100644 --- a/headers/private/kernel/arch/platform.h +++ b/headers/private/kernel/arch/platform.h @@ -1,20 +1,25 @@ /* + * Copyright 2006, Axel Dörfler, axeld@pinc-software.de. * Copyright 2005, Ingo Weinhold . + * * All rights reserved. Distributed under the terms of the MIT License. */ #ifndef _KERNEL_ARCH_PLATFORM_H #define _KERNEL_ARCH_PLATFORM_H + #include 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" diff --git a/headers/private/kernel/arch/x86/apm.h b/headers/private/kernel/arch/x86/apm.h index 3a973489a9..d6598a549d 100644 --- a/headers/private/kernel/arch/x86/apm.h +++ b/headers/private/kernel/arch/x86/apm.h @@ -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 +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 */ diff --git a/headers/private/kernel/arch/x86/descriptors.h b/headers/private/kernel/arch/x86/descriptors.h index b138d5773d..19d7e93a71 100644 --- a/headers/private/kernel/arch/x86/descriptors.h +++ b/headers/private/kernel/arch/x86/descriptors.h @@ -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()) diff --git a/src/system/boot/platform/bios_ia32/Jamfile b/src/system/boot/platform/bios_ia32/Jamfile index 399725fb94..f75e3e1b97 100644 --- a/src/system/boot/platform/bios_ia32/Jamfile +++ b/src/system/boot/platform/bios_ia32/Jamfile @@ -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 diff --git a/src/system/boot/platform/bios_ia32/apm.cpp b/src/system/boot/platform/bios_ia32/apm.cpp index e191707924..3e4aec15fe 100644 --- a/src/system/boot/platform/bios_ia32/apm.cpp +++ b/src/system/boot/platform/bios_ia32/apm.cpp @@ -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, ®s); - 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; diff --git a/src/system/kernel/arch/ppc/arch_platform.cpp b/src/system/kernel/arch/ppc/arch_platform.cpp index 022d51038d..4939d43c8b 100644 --- a/src/system/kernel/arch/ppc/arch_platform.cpp +++ b/src/system/kernel/arch/ppc/arch_platform.cpp @@ -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; +} diff --git a/src/system/kernel/arch/x86/Jamfile b/src/system/kernel/arch/x86/Jamfile index fbe3e59c07..9830002dd7 100644 --- a/src/system/kernel/arch/x86/Jamfile +++ b/src/system/kernel/arch/x86/Jamfile @@ -25,6 +25,7 @@ KernelStaticLibrary libx86 : arch_interrupts.S arch_system_info.c arch_user_debugger.cpp + apm.cpp bios.cpp cpuid.S diff --git a/src/system/kernel/arch/x86/apm.cpp b/src/system/kernel/arch/x86/apm.cpp new file mode 100644 index 0000000000..d9d0650830 --- /dev/null +++ b/src/system/kernel/arch/x86/apm.cpp @@ -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 + +#include +#include +#include + + +#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(®s) != 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(®s); +} + + +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(®s); +} + + +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(®s); +} + + +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(®s) != 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; +} + diff --git a/src/system/kernel/arch/x86/arch_cpu.c b/src/system/kernel/arch/x86/arch_cpu.c index 70a1e4f814..72232f1501 100644 --- a/src/system/kernel/arch/x86/arch_cpu.c +++ b/src/system/kernel/arch/x86/arch_cpu.c @@ -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, diff --git a/src/system/kernel/arch/x86/arch_platform.c b/src/system/kernel/arch/x86/arch_platform.c index 562dbe491a..fa09d8e666 100644 --- a/src/system/kernel/arch/x86/arch_platform.c +++ b/src/system/kernel/arch/x86/arch_platform.c @@ -1,20 +1,34 @@ /* - * Copyright 2006, Ingo Weinhold . - * 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 + * Axel Dörfler, axeld@pinc-software.de */ + #include +#include 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); +} + diff --git a/src/system/kernel/arch/x86/arch_vm.c b/src/system/kernel/arch/x86/arch_vm.c index 9cf7c00213..02caa1f608 100644 --- a/src/system/kernel/arch/x86/arch_vm.c +++ b/src/system/kernel/arch/x86/arch_vm.c @@ -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; diff --git a/src/system/kernel/arch/x86/bios.cpp b/src/system/kernel/arch/x86/bios.cpp index 9a01b3cd73..1abad6eb0f 100644 --- a/src/system/kernel/arch/x86/bios.cpp +++ b/src/system/kernel/arch/x86/bios.cpp @@ -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: diff --git a/src/system/kernel/main.c b/src/system/kernel/main.c index cdab79e016..6c6e24ef22 100644 --- a/src/system/kernel/main.c +++ b/src/system/kernel/main.c @@ -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);