Change the timer syscalls so that sysret_t can be 32-bit
[monolithium.git] / kernel / src / timer.c
1 /*
2  * timer.c
3  *
4  * Copyright (C) 2018 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <timer.h>
21 #include <thread.h>
22
23 volatile qword_t total_ticks = 0;
24
25 static dword_t timer_reload_value;
26
27 static void timer_irq(registers_t *regs, byte_t irq_num)
28 {
29     total_ticks++;
30
31     if (scheduler_enabled)
32     {
33         scheduler(regs);
34         get_current_thread()->running_ticks++;
35     }
36 }
37
38 qword_t timer_get_milliseconds(void)
39 {
40     return total_ticks;
41 }
42
43 qword_t timer_get_nanoseconds(void)
44 {
45     critical_t critical;
46     enter_critical(&critical);
47
48     qword_t result = total_ticks * 1000000ULL;
49
50     cpu_write_port_byte(TIMER_CMD_PORT, 0x00);
51     word_t count = cpu_read_port_byte(TIMER_CHANNEL_PORT(0));
52     count |= cpu_read_port_byte(TIMER_CHANNEL_PORT(0)) << 8;
53
54     ASSERT(count <= timer_reload_value);
55     count = timer_reload_value - count;
56
57     result += (count * 1000000000ULL) / (qword_t)TIMER_BASE_FREQUENCY;
58
59     leave_critical(&critical);
60     return result;
61 }
62
63 sysret_t syscall_query_timer(long reserved, timer_info_type_t info_type, void *buffer, size_t size)
64 {
65     if (get_previous_mode() == USER_MODE && !check_usermode(buffer, size)) return ERR_BADPTR;
66     if (size < sizeof(qword_t)) return ERR_SMALLBUF;
67     qword_t value;
68
69     switch (info_type)
70     {
71     case TIMER_MILLISECONDS:
72         value = timer_get_milliseconds();
73         break;
74
75     case TIMER_NANOSECONDS:
76         value = timer_get_nanoseconds();
77         break;
78
79     case TIMER_PRECISION:
80         value = TIMER_BASE_FREQUENCY;
81         break;
82
83     default:
84         return ERR_INVALID;
85     }
86
87     EH_TRY
88     {
89         *(qword_t*)buffer = value;
90     }
91     EH_CATCH
92     {
93         EH_ESCAPE(return ERR_BADPTR);
94     }
95     EH_DONE;
96
97     return ERR_SUCCESS;
98 }
99
100 void timer_init(void)
101 {
102     timer_reload_value = TIMER_BASE_FREQUENCY / TIMER_FREQUENCY;
103
104     dword_t remainder = TIMER_BASE_FREQUENCY - timer_reload_value * TIMER_FREQUENCY;
105     if ((remainder * 2) > TIMER_FREQUENCY) timer_reload_value++;
106
107     if (timer_reload_value == 0) timer_reload_value = 1;
108     if (timer_reload_value >= 65536) timer_reload_value = 0;
109
110     cpu_write_port_byte(TIMER_CMD_PORT, TIMER_RATE_GENERATOR(0));
111     cpu_write_port_byte(TIMER_CHANNEL_PORT(0), timer_reload_value & 0xFF);
112     cpu_write_port_byte(TIMER_CHANNEL_PORT(0), (timer_reload_value >> 8) & 0xFF);
113     register_irq_handler(TIMER_IRQ, &timer_irq, FALSE);
114 }