summaryrefslogtreecommitdiff
path: root/kernel/timer.c
blob: b84c16a2e2b8c685c0f0fc8457ea49d97ebbd09d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <arch/i386/tsc.h>
#include <drivers/cmos.h>
#include <fs/devfs.h>
#include <interrupts.h>
#include <lib/sv.h>
#include <math.h>
#include <random.h>
#include <time.h>
#include <timer.h>
#include <typedefs.h>

int has_unix_time;
i64 start_unix_time;
u64 start_tsc_time;

void timer_start_init(void) {
  tsc_init();
  start_tsc_time = tsc_get();
  random_add_entropy((u8 *)&start_tsc_time, sizeof(start_tsc_time));
  enable_interrupts();
  cmos_init();
  cmos_start_call(1, &has_unix_time, &start_unix_time);
}

void timer_wait_for_init(void) {
  enable_interrupts();
  for (; !has_unix_time;)
    ;
}

u64 timer_current_uptime = 0; // This gets updated by the PIT handler
u64 timer_get_uptime(void) {
  return timer_current_uptime;
}

void timer_get(struct timespec *tp) {
  u64 offset_tsc = tsc_get() - start_tsc_time;
  i64 current_unix_time_seconds =
      start_unix_time + tsc_calculate_ms(offset_tsc) / 1000;
  tp->tv_sec = current_unix_time_seconds;
  tp->tv_nsec = tsc_calculate_ms(offset_tsc);
}

u64 timer_get_ms(void) {
  u64 offset_tsc = tsc_get() - start_tsc_time;
  return start_unix_time * 1000 + tsc_calculate_ms(offset_tsc);
}

int clock_read(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
  if (0 != offset) {
    return 0;
  }
  u64 r = timer_get_ms();
  return min(len, (u64)kbnprintf(buffer, len, "%llu", r));
}

int clock_write(u8 *buffer, u64 offset, u64 len, vfs_fd_t *fd) {
  if (0 != offset) {
    return 0;
  }

  struct sv string_view = sv_init(buffer, len);

  u64 new_value_ms = sv_parse_unsigned_number(string_view, &string_view);

  i64 new_value_seconds = new_value_ms / 1000;

  u64 offset_tsc = tsc_get() - start_tsc_time;
  i64 current_unix_time_seconds =
      start_unix_time + tsc_calculate_ms(offset_tsc) / 1000;

  i64 delta = new_value_seconds - current_unix_time_seconds;
  start_unix_time += delta;
  int done;
  enable_interrupts();
  cmos_start_call(0, &done, &new_value_seconds);
  for (; !done;)
    ;
  return sizeof(i64);
}

int always_has_data(vfs_inode_t *inode);
int always_can_write(vfs_inode_t *inode);

int timer_add_clock(void) {
  devfs_add_file("/clock", clock_read, clock_write, NULL, always_has_data,
                 always_can_write, FS_TYPE_BLOCK_DEVICE);
  return 1;
}