diff --git a/headers/private/kernel/arch/ppc/arch_cpu.h b/headers/private/kernel/arch/ppc/arch_cpu.h index 407ba61028..18ef2b81c6 100644 --- a/headers/private/kernel/arch/ppc/arch_cpu.h +++ b/headers/private/kernel/arch/ppc/arch_cpu.h @@ -105,6 +105,11 @@ extern void reset_dbats(void); //extern void setl2cr(unsigned int val); extern long long get_time_base(void); +void __ppc_setup_system_time(vint64 *cvFactor); + // defined in libroot: os/arch/system_time.c +int64 __ppc_get_time_base(void); + // defined in libroot: os/arch/system_time_asm.S + extern void ppc_context_switch(void **_oldStackPointer, void *newStackPointer); #ifdef __cplusplus diff --git a/headers/private/kernel/arch/ppc/arch_platform.h b/headers/private/kernel/arch/ppc/arch_platform.h index 8637eeeada..a770b4c95b 100644 --- a/headers/private/kernel/arch/ppc/arch_platform.h +++ b/headers/private/kernel/arch/ppc/arch_platform.h @@ -7,6 +7,8 @@ #include +struct real_time_data; + namespace BPrivate { class PPCPlatform { @@ -19,9 +21,14 @@ public: virtual status_t Init(struct kernel_args *kernelArgs) = 0; virtual status_t InitSerialDebug(struct kernel_args *kernelArgs) = 0; virtual status_t InitPostVM(struct kernel_args *kernelArgs) = 0; + virtual status_t InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data) = 0; virtual char SerialDebugGetChar() = 0; virtual void SerialDebugPutChar(char c) = 0; + + virtual void SetHardwareRTC(uint32 seconds) = 0; + virtual uint32 GetHardwareRTC() = 0; }; } // namespace BPrivate diff --git a/headers/private/kernel/arch/ppc/arch_real_time_data.h b/headers/private/kernel/arch/ppc/arch_real_time_data.h new file mode 100644 index 0000000000..c1e88cd381 --- /dev/null +++ b/headers/private/kernel/arch/ppc/arch_real_time_data.h @@ -0,0 +1,36 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ +#ifndef _KERNEL_ARCH_REAL_TIME_DATA_H +#define _KERNEL_ARCH_REAL_TIME_DATA_H + +#include +#include + + +struct ppc_real_time_data { + vint64 system_time_offset; +}; + +struct arch_real_time_data { + struct ppc_real_time_data data[2]; + vint64 system_time_conversion_factor; + vint32 version; + // Since there're no cheap atomic_{set,get,add}64() on PPC 32 (i.e. one + // that doesn't involve a syscall), we can't have just a single + // system_time_offset and set/get it atomically. + // That's why have our data twice. One set is current (indexed by + // version % 2). When setting the offset, we do that with disabled + // interrupts and protected by a spinlock. We write the new values + // into the other array element and increment the version. + // A reader first reads the version, then the date of interest, and + // finally rechecks the version. If it hasn't changed in the meantime, + // the read value is fine, otherwise it runs the whole procedure again. + // + // system_time_conversion_factor is currently consider constant, + // although that is not necessarily true. We simply don't support + // changing conversion factors at the moment. +}; + +#endif /* _KERNEL_ARCH_REAL_TIME_DATA_H */ diff --git a/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h b/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h index affddc604a..94c8ab6c31 100644 --- a/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h +++ b/headers/private/kernel/boot/platform/openfirmware/platform_kernel_args.h @@ -18,6 +18,7 @@ typedef struct { void *openfirmware_entry; + char rtc_path[128]; } platform_kernel_args; #endif /* KERNEL_BOOT_PLATFORM_OPENFIRMWARE_KERNEL_ARGS_H */ diff --git a/src/system/boot/platform/openfirmware/Jamfile b/src/system/boot/platform/openfirmware/Jamfile index 4a14c20c80..a5ae2e9d52 100644 --- a/src/system/boot/platform/openfirmware/Jamfile +++ b/src/system/boot/platform/openfirmware/Jamfile @@ -11,6 +11,7 @@ KernelMergeObject boot_platform_openfirmware.o : menu.cpp mmu.cpp network.cpp + real_time_clock.cpp start.c support.cpp video.cpp diff --git a/src/system/boot/platform/openfirmware/real_time_clock.cpp b/src/system/boot/platform/openfirmware/real_time_clock.cpp new file mode 100644 index 0000000000..957d73008b --- /dev/null +++ b/src/system/boot/platform/openfirmware/real_time_clock.cpp @@ -0,0 +1,30 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include "real_time_clock.h" + +#include + +#include +#include +#include + +#include "devices.h" + +status_t +init_real_time_clock(void) +{ + // find RTC + int rtcCookie = 0; + if (platform_get_next_device(&rtcCookie, 0, "rtc", + gKernelArgs.platform_args.rtc_path, + sizeof(gKernelArgs.platform_args.rtc_path)) != B_OK) { + printf("init_real_time_clock(): Found no RTC device!"); + return B_ERROR; + } + + return B_OK; +} + diff --git a/src/system/boot/platform/openfirmware/real_time_clock.h b/src/system/boot/platform/openfirmware/real_time_clock.h new file mode 100644 index 0000000000..36b4f9a3a5 --- /dev/null +++ b/src/system/boot/platform/openfirmware/real_time_clock.h @@ -0,0 +1,21 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#ifndef OPENFIRMWARE_REAL_TIME_CLOCK_H +#define OPENFIRMWARE_REAL_TIME_CLOCK_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +status_t init_real_time_clock(void); + +#ifdef __cplusplus +} // extern "C" +#endif + +#endif /* OPENFIRMWARE_REAL_TIME_CLOCK_H */ diff --git a/src/system/boot/platform/openfirmware/start.c b/src/system/boot/platform/openfirmware/start.c index cf004e2c65..c2259f30d0 100644 --- a/src/system/boot/platform/openfirmware/start.c +++ b/src/system/boot/platform/openfirmware/start.c @@ -15,6 +15,7 @@ #include "console.h" #include "machine.h" +#include "real_time_clock.h" #define HEAP_SIZE 65536 @@ -148,6 +149,9 @@ start(void *openFirmwareEntry) if (boot_arch_cpu_init() != B_OK) platform_exit(); + if (init_real_time_clock() != B_OK) + platform_exit(); + gKernelArgs.platform_args.openfirmware_entry = openFirmwareEntry; main(&args); diff --git a/src/system/kernel/arch/ppc/arch_platform.cpp b/src/system/kernel/arch/ppc/arch_platform.cpp index 41cdbb065d..b4e7f09eb6 100644 --- a/src/system/kernel/arch/ppc/arch_platform.cpp +++ b/src/system/kernel/arch/ppc/arch_platform.cpp @@ -11,6 +11,7 @@ #include #include +#include #include @@ -48,13 +49,19 @@ public: virtual status_t Init(struct kernel_args *kernelArgs); virtual status_t InitSerialDebug(struct kernel_args *kernelArgs); virtual status_t InitPostVM(struct kernel_args *kernelArgs); + virtual status_t InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data); virtual char SerialDebugGetChar(); virtual void SerialDebugPutChar(char c); + virtual void SetHardwareRTC(uint32 seconds); + virtual uint32 GetHardwareRTC(); + private: - int fInput; - int fOutput; + int fInput; + int fOutput; + int fRTC; }; } // namespace BPrivate @@ -85,7 +92,8 @@ debug_command_of_enter(int argc, char **argv) // constructor PPCOpenFirmware::PPCOpenFirmware() : fInput(-1), - fOutput(-1) + fOutput(-1), + fRTC(-1) { } @@ -127,6 +135,23 @@ PPCOpenFirmware::InitPostVM(struct kernel_args *kernelArgs) return B_OK; } +// InitRTC +status_t +PPCOpenFirmware::InitRTC(struct kernel_args *kernelArgs, + struct real_time_data *data) +{ +kprintf("PPCOpenFirmware::InitRTC(): opening \"%s\"...\n", kernelArgs->platform_args.rtc_path); + // open RTC + fRTC = of_open(kernelArgs->platform_args.rtc_path); +kprintf(" of_open() returned: %d\n", fRTC); + if (fRTC == OF_FAILED) { + dprintf("PPCOpenFirmware::InitRTC(): Failed open RTC device!\n"); + return B_ERROR; + } + + return B_OK; +} + // DebugSerialGetChar char PPCOpenFirmware::SerialDebugGetChar() @@ -147,6 +172,39 @@ PPCOpenFirmware::SerialDebugPutChar(char c) of_write(fOutput, &c, 1); } +// SetHardwareRTC +void +PPCOpenFirmware::SetHardwareRTC(uint32 seconds) +{ + struct tm t; + rtc_secs_to_tm(seconds, &t); + + t.tm_year += RTC_EPOCHE_BASE_YEAR; + t.tm_mon++; + + if (of_call_method(fRTC, "set-time", 6, 0, t.tm_year, t.tm_mon, t.tm_mday, + t.tm_hour, t.tm_min, t.tm_sec) == OF_FAILED) { + dprintf("PPCOpenFirmware::SetHardwareRTC(): Failed to set RTC!\n"); + } +} + +// GetHardwareRTC +uint32 +PPCOpenFirmware::GetHardwareRTC() +{ + struct tm t; + if (of_call_method(fRTC, "get-time", 0, 6, &t.tm_year, &t.tm_mon, + &t.tm_mday, &t.tm_hour, &t.tm_min, &t.tm_sec) == OF_FAILED) { + dprintf("PPCOpenFirmware::GetHardwareRTC(): Failed to get RTC!\n"); + return 0; + } + + t.tm_year -= RTC_EPOCHE_BASE_YEAR; + t.tm_mon--; + + return rtc_tm_to_secs(&t); +} + // # pragma mark - diff --git a/src/system/kernel/arch/ppc/arch_real_time_clock.cpp b/src/system/kernel/arch/ppc/arch_real_time_clock.cpp index e2d6902e8c..f074536487 100644 --- a/src/system/kernel/arch/ppc/arch_real_time_clock.cpp +++ b/src/system/kernel/arch/ppc/arch_real_time_clock.cpp @@ -1,16 +1,34 @@ /* - * Copyright 2005, Axel Dörfler, axeld@pinc-software.de - * Distributed under the terms of the MIT License. + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. */ - #include -#include +#include +#include +#include + + +static spinlock sSetArchDataLock; status_t arch_rtc_init(kernel_args *args, struct real_time_data *data) { + // init the platform RTC service + status_t error = PPCPlatform::Default()->InitRTC(args, data); + if (error != B_OK) + return error; + + // init the arch specific part of the real_time_data + data->arch_data.data[0].system_time_offset = 0; + data->arch_data.system_time_conversion_factor + = args->arch_args.time_base_frequency; + data->arch_data.version = 0; + + // init spinlock + sSetArchDataLock = 0; + return B_OK; } @@ -18,12 +36,41 @@ arch_rtc_init(kernel_args *args, struct real_time_data *data) uint32 arch_rtc_get_hw_time(void) { - return 0; + return PPCPlatform::Default()->GetHardwareRTC(); } void arch_rtc_set_hw_time(uint32 seconds) { + PPCPlatform::Default()->SetHardwareRTC(seconds); } + +void +arch_rtc_set_system_time_offset(struct real_time_data *data, bigtime_t offset) +{ + cpu_status state = disable_interrupts(); + acquire_spinlock(&sSetArchDataLock); + + int32 version = data->arch_data.version + 1; + data->arch_data.data[version % 2].system_time_offset = offset; + data->arch_data.version = version; + + release_spinlock(&sSetArchDataLock); + restore_interrupts(state); +} + + +bigtime_t +arch_rtc_get_system_time_offset(struct real_time_data *data) +{ + int32 version; + bigtime_t offset; + do { + version = data->arch_data.version; + offset = data->arch_data.data[version % 2].system_time_offset; + } while (version != data->arch_data.version); + + return offset; +} diff --git a/src/system/kernel/lib/Jamfile b/src/system/kernel/lib/Jamfile index d26ab47646..44de4de928 100644 --- a/src/system/kernel/lib/Jamfile +++ b/src/system/kernel/lib/Jamfile @@ -111,7 +111,8 @@ SEARCH_SOURCE += [ FDirName $(librootSources) os arch $(TARGET_ARCH) ] ; KernelMergeObject kernel_os_arch_$(TARGET_ARCH).o : atomic.S byteorder.S - system_time.S + system_time_asm.S + system_time.c : $(TARGET_KERNEL_PIC_CCFLAGS) ; diff --git a/src/system/libroot/os/arch/ppc/Jamfile b/src/system/libroot/os/arch/ppc/Jamfile index 8bbcf87822..8474e3d05d 100644 --- a/src/system/libroot/os/arch/ppc/Jamfile +++ b/src/system/libroot/os/arch/ppc/Jamfile @@ -8,7 +8,8 @@ MergeObject os_arch_$(TARGET_ARCH).o : compatibility.c # only here until the places where those functions are used # are fixed # systeminfo.c - system_time.S + system_time.c + system_time_asm.S thread.c time.c tls.c diff --git a/src/system/libroot/os/arch/ppc/system_time.c b/src/system/libroot/os/arch/ppc/system_time.c new file mode 100644 index 0000000000..8eecc81a60 --- /dev/null +++ b/src/system/libroot/os/arch/ppc/system_time.c @@ -0,0 +1,38 @@ +/* + * Copyright 2006, Ingo Weinhold . + * All rights reserved. Distributed under the terms of the MIT License. + */ + +#include + +#include +#include +#include + + +static vint64 *sConversionFactor; + +void +__ppc_setup_system_time(vint64 *cvFactor) +{ + sConversionFactor = cvFactor; +} + + +// Note: We don't implement system_time() in assembly since the 64 bit +// divisions/multiplications are a bit tough to implement for a 32 bit +// PPC. +bigtime_t +system_time(void) +{ + uint64 timeBase = __ppc_get_time_base(); + // TODO: The multiplication doesn't look that nice. The value can easily + // overflow when timeBase gets big enough. The limit for timebase is + // about 2^(64 - 20). This might sound a lot, but the conversion factor + // might be quite big. Assuming a worst case factor of 2^32, + // this would leave us with only about 2^12 = 4096 seconds we can + // represent. The actual factor for my Mac mini is about 40 * 10^6, i.e. + // the overflow limit is ca. 100 times greater, but that isn't more than + // five days either. + return (timeBase * 1000000ULL) / *sConversionFactor; +} diff --git a/src/system/libroot/os/arch/ppc/system_time.S b/src/system/libroot/os/arch/ppc/system_time_asm.S similarity index 100% rename from src/system/libroot/os/arch/ppc/system_time.S rename to src/system/libroot/os/arch/ppc/system_time_asm.S diff --git a/src/system/libroot/os/arch/ppc/time.c b/src/system/libroot/os/arch/ppc/time.c index 1ee63294fc..b8e9ab6e43 100644 --- a/src/system/libroot/os/arch/ppc/time.c +++ b/src/system/libroot/os/arch/ppc/time.c @@ -10,14 +10,33 @@ #include +static struct arch_real_time_data *sRealTimeData; + void __arch_init_time(struct real_time_data *data, bool setDefaults) { + sRealTimeData = &data->arch_data; + if (setDefaults) { - data->arch_data.data[0].system_time_offset = 0; - data->arch_data.system_time_conversion_factor = 1000000000LL; - data->arch_data.version = 0; + sRealTimeData->data[0].system_time_offset = 0; + sRealTimeData->system_time_conversion_factor = 1000000000LL; + sRealTimeData->version = 0; } - __ppc_setup_system_time(&data->arch_data.system_time_conversion_factor); + __ppc_setup_system_time(&sRealTimeData->system_time_conversion_factor); } + + +bigtime_t +__arch_get_system_time_offset(struct real_time_data *data) +{ + int32 version; + bigtime_t offset; + do { + version = sRealTimeData->version; + offset = sRealTimeData->data[version % 2].system_time_offset; + } while (version != sRealTimeData->version); + + return offset; +} + diff --git a/src/system/libroot/os/arch/x86/Jamfile b/src/system/libroot/os/arch/x86/Jamfile index 4fb1abe019..08eb738494 100644 --- a/src/system/libroot/os/arch/x86/Jamfile +++ b/src/system/libroot/os/arch/x86/Jamfile @@ -8,7 +8,7 @@ MergeObject os_arch_$(TARGET_ARCH).o : compatibility.c get_stack_frame.S system_info.c - system_time.S + system_time_asm.S thread.c time.c tls.c diff --git a/src/system/libroot/os/arch/x86/system_time.c b/src/system/libroot/os/arch/x86/system_time.c new file mode 100644 index 0000000000..6b4e3d7df9 --- /dev/null +++ b/src/system/libroot/os/arch/x86/system_time.c @@ -0,0 +1,4 @@ +/* + Just a dummy to avoid a special case in the build system. system_time() + is implemented in system_time_asm.S. +*/ diff --git a/src/system/libroot/os/arch/x86/system_time.S b/src/system/libroot/os/arch/x86/system_time_asm.S similarity index 100% rename from src/system/libroot/os/arch/x86/system_time.S rename to src/system/libroot/os/arch/x86/system_time_asm.S