summaryrefslogtreecommitdiff
path: root/tools/src/hv_wdt.c
blob: 6010412499766484258c0d875b8f7d3207822912 (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
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
/* SPDX-License-Identifier: MIT */

#include "hv.h"
#include "adt.h"
#include "smp.h"
#include "uart.h"
#include "utils.h"

#define WDT_TIMEOUT 1

static bool hv_wdt_active = false;
static bool hv_wdt_enabled = false;
static volatile u64 hv_wdt_timestamp = 0;
static u64 hv_wdt_timeout = 0;
static volatile u64 hv_wdt_breadcrumbs;

static int hv_wdt_cpu;

static u64 cpu_dbg_base = 0;

void hv_wdt_bark(void)
{
    u64 tmp = hv_wdt_breadcrumbs;
    uart_puts("HV watchdog: bark!");

    uart_printf("Breadcrumbs: ");
    for (int i = 56; i >= 0; i -= 8) {
        char c = (tmp >> i) & 0xff;
        if (c)
            uart_putchar(c);
    }
    uart_putchar('\n');

    uart_puts("Attempting to enter proxy");

    struct uartproxy_msg_start start = {
        .reason = START_HV,
        .code = HV_WDT_BARK,
    };

    uartproxy_run(&start);
    reboot();
}

void hv_wdt_main(void)
{
    while (hv_wdt_active) {
        if (hv_wdt_enabled) {
            sysop("dmb ish");
            u64 timestamp = hv_wdt_timestamp;
            sysop("isb");
            u64 now = mrs(CNTPCT_EL0);
            sysop("isb");
            if ((now - timestamp) > hv_wdt_timeout)
                hv_wdt_bark();
        }

        udelay(1000);

        sysop("dmb ish");
    }
}

void hv_wdt_pet(void)
{
    hv_wdt_timestamp = mrs(CNTPCT_EL0);
    sysop("dmb ish");
}

void hv_wdt_suspend(void)
{
    hv_wdt_enabled = false;
    sysop("dsb ish");
}

void hv_wdt_resume(void)
{
    hv_wdt_pet();
    hv_wdt_enabled = true;
    sysop("dsb ish");
}

void hv_wdt_breadcrumb(char c)
{
    u64 tmp = hv_wdt_breadcrumbs;
    tmp <<= 8;
    tmp |= c;
    hv_wdt_breadcrumbs = tmp;
    sysop("dmb ish");
}

void hv_wdt_init(void)
{
    int node = adt_path_offset(adt, "/cpus/cpu0");
    if (node < 0) {
        printf("Error getting /cpus/cpu0 node\n");
        return;
    }

    u64 reg[2];
    if (ADT_GETPROP_ARRAY(adt, node, "cpu-uttdbg-reg", reg) < 0) {
        printf("Error getting cpu-uttdbg-reg property\n");
        return;
    }

    cpu_dbg_base = reg[0];
}

void hv_wdt_start(int cpu)
{
    if (hv_wdt_active)
        return;

    hv_wdt_cpu = cpu;
    hv_wdt_breadcrumbs = 0;
    hv_wdt_timeout = mrs(CNTFRQ_EL0) * WDT_TIMEOUT;
    hv_wdt_pet();
    hv_wdt_active = true;
    hv_wdt_enabled = true;
    smp_call4(hv_wdt_cpu, hv_wdt_main, 0, 0, 0, 0);
}

void hv_wdt_stop(void)
{
    if (!hv_wdt_active)
        return;

    hv_wdt_active = false;
    smp_wait(hv_wdt_cpu);
}