// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2015 Anton Ivanov (aivanov@{brocade.com,kot-begemot.co.uk})
* Copyright (C) 2015 Thomas Meyer (thomas@m3y3r.de)
* Copyright (C) 2012-2014 Cisco Systems
* Copyright (C) 2000 - 2007 Jeff Dike (jdike@{addtoit,linux.intel}.com)
* Copyright (C) 2019 Intel Corporation
*/
#include <linux/clockchips.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/jiffies.h>
#include <linux/mm.h>
#include <linux/sched.h>
#include <linux/spinlock.h>
#include <linux/threads.h>
#include <asm/irq.h>
#include <asm/param.h>
#include <kern_util.h>
#include <os.h>
#include <linux/delay.h>
#include <linux/time-internal.h>
#include <linux/um_timetravel.h>
#include <shared/init.h>
#ifdef CONFIG_UML_TIME_TRAVEL_SUPPORT
#include <linux/sched/clock.h>
enum time_travel_mode time_travel_mode;
EXPORT_SYMBOL_GPL(time_travel_mode);
static bool time_travel_start_set;
static unsigned long long time_travel_start;
static unsigned long long time_travel_time;
static unsigned long long time_travel_shm_offset;
static LIST_HEAD(time_travel_events);
static LIST_HEAD(time_travel_irqs);
static unsigned long long time_travel_timer_interval;
static unsigned long long time_travel_next_event;
static struct time_travel_event time_travel_timer_event;
static int time_travel_ext_fd = -1;
static unsigned int time_travel_ext_waiting;
static bool time_travel_ext_prev_request_valid;
static unsigned long long time_travel_ext_prev_request;
static unsigned long long *time_travel_ext_free_until;
static unsigned long long _time_travel_ext_free_until;
static u16 time_travel_shm_id;
static struct um_timetravel_schedshm *time_travel_shm;
static union um_timetravel_schedshm_client *time_travel_shm_client;
unsigned long tt_extra_sched_jiffies;
notrace unsigned long long sched_clock(void)
{
return (unsigned long long)(jiffies - INITIAL_JIFFIES +
tt_extra_sched_jiffies)
* (NSEC_PER_SEC / HZ);
}
static void time_travel_set_time(unsigned long long ns)
{
if (unlikely(ns < time_travel_time))
panic("time-travel: time goes backwards %lld -> %lld\n",
time_travel_time, ns);
else if (unlikely(ns >= S64_MAX))
panic("The system was going to sleep forever, aborting");
time_travel_time = ns;
}
enum time_travel_message_handling {
TTMH_IDLE,
TTMH_POLL,
TTMH_READ,
TTMH_READ_START_ACK,
};
static u64 bc_message;
int time_travel_should_print_bc_msg;
void _time_travel_print_bc_msg(void)
{
time_travel_should_print_bc_msg = 0;
printk(KERN_INFO "time-travel: received broadcast 0x%llx\n", bc_message);
}
static void time_travel_setup_shm(int fd, u16 id)
{
u32 len;
time_travel_shm = os_mmap_rw_shared(fd, sizeof(*time_travel_shm));
if (!time_travel_shm)
goto out;
len = time_travel_shm->len;
if (time_travel_shm->version != UM_TIMETRAVEL_SCHEDSHM_VERSION ||
len < struct_size(time_travel_shm, clients, id + 1)) {
os_unmap_memory(time_travel_shm, sizeof(*time_travel_shm));
time_travel_shm = NULL;
goto out;
}
time_travel_shm = os_mremap_rw_shared(time_travel_shm,
sizeof(*time_travel_shm),
len);
if (!time_travel_shm)
goto out;
time_travel_shm_offset = time_travel_shm->current_time;
time_travel_shm_client = &time_travel_shm->clients[id];
time_travel_shm_client->capa |= UM_TIMETRAVEL_SCHEDSHM_CAP_TIME_SHARE;
time_travel_shm_id = id;
/* always look at that free_until from now on */
time_travel_ext_free_until = &time_travel_shm->free_until;
out:
os_close_file(fd);
}
static void time_travel_handle_message(struct um_timetravel_msg *msg,
enum time_travel_message_handling mode)
{
struct um_timetravel_msg resp = {
.op = UM_TIMETRAVEL_ACK,
};
int ret;
/*
* We can't unlock here, but interrupt signals with a timetravel_handler
* (see um_request_irq