From 262a48f346bd0a2e6f136cdae1e17675fa6ce5a2 Mon Sep 17 00:00:00 2001 From: Wu Zhou Date: Tue, 8 Aug 2023 16:48:38 +0800 Subject: [PATCH] dm: vm_event: add support for RTC change event MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a guest OS performs an RTC change action, we wish this event be captured by developers, and then they can decide what to do with it. (e.g., whether to change physical RTC) There are some facts that makes RTC change event a bit complicated: - There are 7 RTC date/time regs (year, month…). They can only be updated one by one. - RTC time is not reliable before date/time update is finished. - Guests can update RTC date/time regs in any order. - Guests may update RTC date/time regs during either RTC halted or not halted. A single date/time update event is not reliable. We have to wait for the guest to finish the update process. So the DM's event handler sets up a timer, and wait for some time (1 second). If no more change happens befor the timer expires, we can conclude that the RTC change has been done. Then the rtc change event is emitted. This logic of event handler can be used to process HV vrtc time change event too. Tracked-On: #8547 Signed-off-by: Wu Zhou Reviewed-by: Jian Jun Chen --- devicemodel/core/vm_event.c | 88 +++++++++++++++++++++++-- devicemodel/hw/platform/rtc.c | 34 +++++++++- devicemodel/include/vm_event.h | 1 + hypervisor/include/public/acrn_common.h | 10 +++ 4 files changed, 127 insertions(+), 6 deletions(-) diff --git a/devicemodel/core/vm_event.c b/devicemodel/core/vm_event.c index 6a797b609..11ac0ed87 100644 --- a/devicemodel/core/vm_event.c +++ b/devicemodel/core/vm_event.c @@ -33,6 +33,8 @@ #define THROTTLE_WINDOW 1U /* time window for throttle counter, in secs*/ +#define BROKEN_TIME ((time_t)-1) + typedef void (*vm_event_handler)(struct vmctx *ctx, struct vm_event *event); typedef void (*vm_event_generate_jdata)(cJSON *event_obj, struct vm_event *event); @@ -43,6 +45,9 @@ static char dm_vm_event_page[4096] __aligned(4096); static pthread_t vm_event_tid; static void general_event_handler(struct vmctx *ctx, struct vm_event *event); +static void rtc_chg_event_handler(struct vmctx *ctx, struct vm_event *event); + +static void gen_rtc_chg_jdata(cJSON *event_obj, struct vm_event *event); enum event_source_type { EVENT_SOURCE_TYPE_HV, @@ -70,14 +75,13 @@ struct vm_event_proc { vm_event_handler ve_handler; uint32_t throttle_rate; /* how many events allowed per sec */ struct event_throttle_ctl throttle_ctl; - vm_event_generate_jdata gen_jdata_handler; + vm_event_generate_jdata gen_jdata_handler; /* how to transtfer vm_event data to json txt */ }; - static struct vm_event_proc ve_proc[VM_EVENT_COUNT] = { [VM_EVENT_RTC_CHG] = { - .ve_handler = general_event_handler, - .gen_jdata_handler = NULL, + .ve_handler = rtc_chg_event_handler, + .gen_jdata_handler = gen_rtc_chg_jdata, .throttle_rate = 1, }, [VM_EVENT_POWEROFF] = { @@ -206,6 +210,7 @@ static char *generate_vm_event_message(struct vm_event *event) fprintf(stderr, "Failed to generate vm_event message.\n"); cJSON_Delete(event_obj); + return event_msg; } @@ -225,6 +230,79 @@ static void general_event_handler(struct vmctx *ctx, struct vm_event *event) emit_vm_event(ctx, event); } +static void gen_rtc_chg_jdata(cJSON *event_obj, struct vm_event *event) +{ + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event->event_data; + cJSON *val; + + val = cJSON_CreateNumber(data->delta_time); + if (val != NULL) { + cJSON_AddItemToObject(event_obj, "delta_time", val); + } + val = cJSON_CreateNumber(data->last_time); + if (val != NULL) { + cJSON_AddItemToObject(event_obj, "last_time", val); + } +} + +/* assume we only have one unique rtc source */ + +static struct acrn_timer rtc_chg_event_timer = { + .clockid = CLOCK_MONOTONIC, +}; +static pthread_mutex_t rtc_chg_mutex = PTHREAD_MUTEX_INITIALIZER; +static struct timespec time_window_start; +static time_t last_time_cached = BROKEN_TIME; +static time_t delta_time_sum = 0; +#define RTC_CHG_WAIT_TIME 1 /* 1 second */ +static void rtc_chg_event_handler(struct vmctx *ctx, struct vm_event *event) +{ + struct itimerspec timer_spec; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event->event_data; + + /* + * RTC time is not reliable until guest finishes updating all RTC date/time regs. + * So wait for some time, if no more change happens, we can conclude that the RTC + * change has been done. + */ + timer_spec.it_value.tv_sec = RTC_CHG_WAIT_TIME; + timer_spec.it_value.tv_nsec = 0; + timer_spec.it_interval.tv_sec = 0; + timer_spec.it_interval.tv_nsec = 0; + pthread_mutex_lock(&rtc_chg_mutex); + if (last_time_cached == BROKEN_TIME) { + last_time_cached = data->last_time; + } + delta_time_sum += data->delta_time; + /* The last timer will be overwriten if it is not triggered yet. */ + acrn_timer_settime(&rtc_chg_event_timer, &timer_spec); + clock_gettime(CLOCK_MONOTONIC, &time_window_start); + pthread_mutex_unlock(&rtc_chg_mutex); +} + +static void rtc_chg_timer_cb(void *arg, uint64_t nexp) +{ + struct timespec now, delta; + struct timespec time_window_size = {RTC_CHG_WAIT_TIME, 0}; + struct vmctx *ctx = arg; + struct vm_event send_event; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)send_event.event_data; + + pthread_mutex_lock(&rtc_chg_mutex); + clock_gettime(CLOCK_MONOTONIC, &now); + delta = now; + timespecsub(&delta, &time_window_start); + /* possible racing problem here. make sure this is the right timer cb for the vm_event */ + if (timespeccmp(&delta, &time_window_size, >=)) { + data->delta_time = delta_time_sum; + data->last_time = last_time_cached; + emit_vm_event(ctx, &send_event); + last_time_cached = BROKEN_TIME; + delta_time_sum = 0; + } + pthread_mutex_unlock(&rtc_chg_mutex); +} + static void *vm_event_thread(void *param) { int n, i; @@ -372,6 +450,8 @@ int vm_event_init(struct vmctx *ctx) goto out; } + acrn_timer_init(&rtc_chg_event_timer, rtc_chg_timer_cb, ctx); + started = true; return 0; diff --git a/devicemodel/hw/platform/rtc.c b/devicemodel/hw/platform/rtc.c index f3b90c16c..82de9b7fb 100644 --- a/devicemodel/hw/platform/rtc.c +++ b/devicemodel/hw/platform/rtc.c @@ -41,6 +41,7 @@ #include "timer.h" #include "acpi.h" #include "lpc.h" +#include "vm_event.h" #include "log.h" @@ -80,6 +81,7 @@ struct vrtc { u_int addr; /* RTC register to read or write */ time_t base_uptime; time_t base_rtctime; + time_t halted_rtctime; struct rtcdev rtcdev; }; @@ -725,6 +727,18 @@ vrtc_set_reg_c(struct vrtc *vrtc, uint8_t newval) } } +static void +send_rtc_chg_event(time_t newtime, time_t lasttime) +{ + struct vm_event event; + struct rtc_change_event_data *data = (struct rtc_change_event_data *)event.event_data; + + event.type = VM_EVENT_RTC_CHG; + data->delta_time = newtime - lasttime; + data->last_time = lasttime; + dm_send_vm_event(&event); +} + static int vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) { @@ -751,12 +765,19 @@ vrtc_set_reg_b(struct vrtc *vrtc, uint8_t newval) if (rtctime == VRTC_BROKEN_TIME) { if (rtc_flag_broken_time) return -1; + } else { + /* send rtc change event if rtc time changed during halt */ + if (vrtc->halted_rtctime != VRTC_BROKEN_TIME && + rtctime != vrtc->halted_rtctime) { + send_rtc_chg_event(rtctime, vrtc->halted_rtctime); + vrtc->halted_rtctime = VRTC_BROKEN_TIME; + } } } else { curtime = vrtc_curtime(vrtc, &basetime); if (curtime != vrtc->base_rtctime) return -1; - + vrtc->halted_rtctime = curtime; /* * Force a refresh of the RTC date/time fields so * they reflect the time right before the guest set @@ -982,10 +1003,18 @@ vrtc_data_handler(struct vmctx *ctx, int vcpu, int in, int port, * so re-calculate the RTC date/time. */ if (vrtc_is_time_register(offset) && !rtc_halted(vrtc)) { + time_t last_time = curtime; curtime = rtc_to_secs(vrtc); error = vrtc_time_update(vrtc, curtime, monotonic_time()); - if ((error != 0) || (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)) + if ((error != 0) || (curtime == VRTC_BROKEN_TIME && rtc_flag_broken_time)) { error = -1; + } else { + /* We don't know when the Guest has finished the RTC change action. + * So send an event each time the date/time regs has been updated. + * The event handler will process those events. + */ + send_rtc_chg_event(rtc_to_secs(vrtc), last_time); + } } } @@ -1105,6 +1134,7 @@ vrtc_init(struct vmctx *ctx) /* Reset the index register to a safe value. */ vrtc->addr = RTC_STATUSD; + vrtc->halted_rtctime = VRTC_BROKEN_TIME; /* * Initialize RTC time to 00:00:00 Jan 1, 1970 if curtime = 0 */ diff --git a/devicemodel/include/vm_event.h b/devicemodel/include/vm_event.h index 7a7ea423a..2ed4e498b 100644 --- a/devicemodel/include/vm_event.h +++ b/devicemodel/include/vm_event.h @@ -13,5 +13,6 @@ int vm_event_init(struct vmctx *ctx); int vm_event_deinit(void); int dm_send_vm_event(struct vm_event *event); +uint32_t get_dm_vm_event_overrun_count(void); #endif /* VM_EVENT_H */ diff --git a/hypervisor/include/public/acrn_common.h b/hypervisor/include/public/acrn_common.h index c61314f86..eb2053a2d 100644 --- a/hypervisor/include/public/acrn_common.h +++ b/hypervisor/include/public/acrn_common.h @@ -842,6 +842,16 @@ struct vm_event { uint8_t event_data[VM_EVENT_DATA_LEN]; }; +struct rtc_change_event_data { + int64_t last_time; /* time(in secs) of the RTC defore been set */ + int64_t delta_time; /* delta of time(in secs) the RTC has been changed */ +}; + +/* DM vrtc's reference time is besed on sys time, while the HV vrtc is based on pRTC. + * When the delta time is sent to users, they need to know what it is relative to. + */ +#define RTC_CHG_RELATIVE_PHYSICAL_RTC 0 +#define RTC_CHG_RELATIVE_SERVICE_VM_SYS_TIME 1 /** * @} */