diff --git a/devicemodel/Makefile b/devicemodel/Makefile index 57f73541c..5ff899c85 100644 --- a/devicemodel/Makefile +++ b/devicemodel/Makefile @@ -122,6 +122,7 @@ SRCS += core/mptbl.c SRCS += core/main.c SRCS += core/hugetlb.c SRCS += core/vrpmb.c +SRCS += core/timer.c # arch SRCS += arch/x86/pm.c diff --git a/devicemodel/core/timer.c b/devicemodel/core/timer.c new file mode 100644 index 000000000..e620a45bb --- /dev/null +++ b/devicemodel/core/timer.c @@ -0,0 +1,122 @@ +/* + * Copyright (C) <2018> Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#include +#include +#include +#include + +#include "vmmapi.h" +#include "mevent.h" +#include "timer.h" + +/* We can use timerfd and epoll mechanism to emulate kinds of timers like + * PIT/RTC/WDT/PMTIMER/... in device model under Linux. + * Compare with sigevent mechanism, timerfd has a advantage that it could + * avoid race condition on resource accessing in the async sigev thread. + * + * Please note timerfd and epoll are all Linux specific. If the code need to be + * ported to other OS, we can modify the api with POSIX timers and sigevent + * mechanism. + */ + +static void +timer_handler(int fd __attribute__((unused)), + enum ev_type t __attribute__((unused)), + void *arg) +{ + struct acrn_timer *timer = arg; + uint64_t buf; + int32_t size; + + if (timer == NULL) { + return; + } + + /* Consume I/O event for default EPOLLLT type. + * Here is a temporary solution, the processing could be moved to + * mevent.c once EVF_TIMER is supported. + */ + size = read(timer->fd, &buf, sizeof(buf)); + if (size < 1) { + fprintf(stderr, "acrn_timer read timerfd error!"); + return; + } + + if (timer->callback != NULL) { + (*timer->callback)(timer->callback_param); + } +} + +int32_t +acrn_timer_init(struct acrn_timer *timer, void (*cb)(void *), void *param) +{ + if ((timer == NULL) || (cb == NULL)) { + return -1; + } + + timer->fd = -1; + if ((timer->clockid == CLOCK_REALTIME) || + (timer->clockid == CLOCK_MONOTONIC)) { + timer->fd = timerfd_create(timer->clockid, + TFD_NONBLOCK | TFD_CLOEXEC); + } else { + perror("acrn_timer clockid is not supported.\n"); + } + + if (timer->fd <= 0) { + perror("acrn_timer create failed.\n"); + return -1; + } + + timer->mevp = mevent_add(timer->fd, EVF_READ, timer_handler, timer); + if (timer->mevp == NULL) { + close(timer->fd); + perror("acrn_timer mevent add failed.\n"); + return -1; + } + + timer->callback = cb; + timer->callback_param = param; + + return 0; +} + +void +acrn_timer_deinit(struct acrn_timer *timer) +{ + if (timer == NULL) { + return; + } + + if (timer->mevp != NULL) { + mevent_delete_close(timer->mevp); + timer->mevp = NULL; + } + + timer->fd = -1; + timer->callback = NULL; + timer->callback_param = NULL; +} + +int32_t +acrn_timer_settime(struct acrn_timer *timer, struct itimerspec *new_value) +{ + if (timer == NULL) { + return -1; + } + + return timerfd_settime(timer->fd, 0, new_value, NULL); +} + +int32_t +acrn_timer_gettime(struct acrn_timer *timer, struct itimerspec *cur_value) +{ + if (timer == NULL) { + return -1; + } + + return timerfd_gettime(timer->fd, cur_value); +} diff --git a/devicemodel/include/timer.h b/devicemodel/include/timer.h new file mode 100644 index 000000000..ab6a07d02 --- /dev/null +++ b/devicemodel/include/timer.h @@ -0,0 +1,26 @@ +/* + * Copyright (C) <2018> Intel Corporation + * SPDX-License-Identifier: BSD-3-Clause + */ + +#ifndef _TIMER_H_ +#define _TIMER_H_ + +struct acrn_timer { + int32_t fd; + int32_t clockid; + struct mevent *mevp; + void (*callback)(void *); + void *callback_param; +}; + +int32_t +acrn_timer_init(struct acrn_timer *timer, void (*cb)(void *), void *param); +void +acrn_timer_deinit(struct acrn_timer *timer); +int32_t +acrn_timer_settime(struct acrn_timer *timer, struct itimerspec *new_value); +int32_t +acrn_timer_gettime(struct acrn_timer *timer, struct itimerspec *cur_value); + +#endif /* _VTIMER_ */