cachepc-qemu

Fork of AMDESE/qemu with changes for cachepc side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-qemu
Log | Files | Refs | Submodules | LICENSE | sfeed.txt

npcm7xx_watchdog_timer-test.c (10388B)


      1/*
      2 * QTests for Nuvoton NPCM7xx Timer Watchdog Modules.
      3 *
      4 * Copyright 2020 Google LLC
      5 *
      6 * This program is free software; you can redistribute it and/or modify it
      7 * under the terms of the GNU General Public License as published by the
      8 * Free Software Foundation; either version 2 of the License, or
      9 * (at your option) any later version.
     10 *
     11 * This program is distributed in the hope that it will be useful, but WITHOUT
     12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
     13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
     14 * for more details.
     15 */
     16
     17#include "qemu/osdep.h"
     18#include "qemu/timer.h"
     19
     20#include "libqos/libqtest.h"
     21#include "qapi/qmp/qdict.h"
     22
     23#define WTCR_OFFSET     0x1c
     24#define REF_HZ          (25000000)
     25
     26/* WTCR bit fields */
     27#define WTCLK(rv)       ((rv) << 10)
     28#define WTE             BIT(7)
     29#define WTIE            BIT(6)
     30#define WTIS(rv)        ((rv) << 4)
     31#define WTIF            BIT(3)
     32#define WTRF            BIT(2)
     33#define WTRE            BIT(1)
     34#define WTR             BIT(0)
     35
     36typedef struct Watchdog {
     37    int irq;
     38    uint64_t base_addr;
     39} Watchdog;
     40
     41static const Watchdog watchdog_list[] = {
     42    {
     43        .irq        = 47,
     44        .base_addr  = 0xf0008000
     45    },
     46    {
     47        .irq        = 48,
     48        .base_addr  = 0xf0009000
     49    },
     50    {
     51        .irq        = 49,
     52        .base_addr  = 0xf000a000
     53    }
     54};
     55
     56static int watchdog_index(const Watchdog *wd)
     57{
     58    ptrdiff_t diff = wd - watchdog_list;
     59
     60    g_assert(diff >= 0 && diff < ARRAY_SIZE(watchdog_list));
     61
     62    return diff;
     63}
     64
     65static uint32_t watchdog_read_wtcr(QTestState *qts, const Watchdog *wd)
     66{
     67    return qtest_readl(qts, wd->base_addr + WTCR_OFFSET);
     68}
     69
     70static void watchdog_write_wtcr(QTestState *qts, const Watchdog *wd,
     71        uint32_t value)
     72{
     73    qtest_writel(qts, wd->base_addr + WTCR_OFFSET, value);
     74}
     75
     76static uint32_t watchdog_prescaler(QTestState *qts, const Watchdog *wd)
     77{
     78    switch (extract32(watchdog_read_wtcr(qts, wd), 10, 2)) {
     79    case 0:
     80        return 1;
     81    case 1:
     82        return 256;
     83    case 2:
     84        return 2048;
     85    case 3:
     86        return 65536;
     87    default:
     88        g_assert_not_reached();
     89    }
     90}
     91
     92static QDict *get_watchdog_action(QTestState *qts)
     93{
     94    QDict *ev = qtest_qmp_eventwait_ref(qts, "WATCHDOG");
     95    QDict *data;
     96
     97    data = qdict_get_qdict(ev, "data");
     98    qobject_ref(data);
     99    qobject_unref(ev);
    100    return data;
    101}
    102
    103#define RESET_CYCLES 1024
    104static uint32_t watchdog_interrupt_cycles(QTestState *qts, const Watchdog *wd)
    105{
    106    uint32_t wtis = extract32(watchdog_read_wtcr(qts, wd), 4, 2);
    107    return 1 << (14 + 2 * wtis);
    108}
    109
    110static int64_t watchdog_calculate_steps(uint32_t count, uint32_t prescale)
    111{
    112    return (NANOSECONDS_PER_SECOND / REF_HZ) * count * prescale;
    113}
    114
    115static int64_t watchdog_interrupt_steps(QTestState *qts, const Watchdog *wd)
    116{
    117    return watchdog_calculate_steps(watchdog_interrupt_cycles(qts, wd),
    118            watchdog_prescaler(qts, wd));
    119}
    120
    121/* Check wtcr can be reset to default value */
    122static void test_init(gconstpointer watchdog)
    123{
    124    const Watchdog *wd = watchdog;
    125    QTestState *qts = qtest_init("-machine quanta-gsj");
    126
    127    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    128
    129    watchdog_write_wtcr(qts, wd, WTCLK(1) | WTRF | WTIF | WTR);
    130    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1));
    131
    132    qtest_quit(qts);
    133}
    134
    135/* Check a watchdog can generate interrupt and reset actions */
    136static void test_reset_action(gconstpointer watchdog)
    137{
    138    const Watchdog *wd = watchdog;
    139    QTestState *qts = qtest_init("-machine quanta-gsj");
    140    QDict *ad;
    141
    142    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    143
    144    watchdog_write_wtcr(qts, wd,
    145            WTCLK(0) | WTE | WTRF | WTRE | WTIF | WTIE | WTR);
    146    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
    147            WTCLK(0) | WTE | WTRE | WTIE);
    148
    149    /* Check a watchdog can generate an interrupt */
    150    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
    151    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
    152            WTCLK(0) | WTE | WTIF | WTIE | WTRE);
    153    g_assert_true(qtest_get_irq(qts, wd->irq));
    154
    155    /* Check a watchdog can generate a reset signal */
    156    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
    157                watchdog_prescaler(qts, wd)));
    158    ad = get_watchdog_action(qts);
    159    /* The signal is a reset signal */
    160    g_assert_false(strcmp(qdict_get_str(ad, "action"), "reset"));
    161    qobject_unref(ad);
    162    qtest_qmp_eventwait(qts, "RESET");
    163    /*
    164     * Make sure WTCR is reset to default except for WTRF bit which shouldn't
    165     * be reset.
    166     */
    167    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(1) | WTRF);
    168    qtest_quit(qts);
    169}
    170
    171/* Check a watchdog works with all possible WTCLK prescalers and WTIS cycles */
    172static void test_prescaler(gconstpointer watchdog)
    173{
    174    const Watchdog *wd = watchdog;
    175
    176    for (int wtclk = 0; wtclk < 4; ++wtclk) {
    177        for (int wtis = 0; wtis < 4; ++wtis) {
    178            QTestState *qts = qtest_init("-machine quanta-gsj");
    179
    180            qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    181            watchdog_write_wtcr(qts, wd,
    182                    WTCLK(wtclk) | WTE | WTIF | WTIS(wtis) | WTIE | WTR);
    183            /*
    184             * The interrupt doesn't fire until watchdog_interrupt_steps()
    185             * cycles passed
    186             */
    187            qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd) - 1);
    188            g_assert_false(watchdog_read_wtcr(qts, wd) & WTIF);
    189            g_assert_false(qtest_get_irq(qts, wd->irq));
    190            qtest_clock_step(qts, 1);
    191            g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    192            g_assert_true(qtest_get_irq(qts, wd->irq));
    193
    194            qtest_quit(qts);
    195        }
    196    }
    197}
    198
    199/*
    200 * Check a watchdog doesn't fire if corresponding flags (WTIE and WTRE) are not
    201 * set.
    202 */
    203static void test_enabling_flags(gconstpointer watchdog)
    204{
    205    const Watchdog *wd = watchdog;
    206    QTestState *qts;
    207    QDict *rsp;
    208
    209    /* Neither WTIE or WTRE is set, no interrupt or reset should happen */
    210    qts = qtest_init("-machine quanta-gsj");
    211    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    212    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRF | WTR);
    213    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
    214    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    215    g_assert_false(qtest_get_irq(qts, wd->irq));
    216    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
    217                watchdog_prescaler(qts, wd)));
    218    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    219    g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
    220    qtest_quit(qts);
    221
    222    /* Only WTIE is set, interrupt is triggered but reset should not happen */
    223    qts = qtest_init("-machine quanta-gsj");
    224    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    225    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
    226    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
    227    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    228    g_assert_true(qtest_get_irq(qts, wd->irq));
    229    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
    230                watchdog_prescaler(qts, wd)));
    231    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    232    g_assert_false(watchdog_read_wtcr(qts, wd) & WTRF);
    233    qtest_quit(qts);
    234
    235    /* Only WTRE is set, interrupt is triggered but reset should not happen */
    236    qts = qtest_init("-machine quanta-gsj");
    237    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    238    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTRE | WTRF | WTR);
    239    qtest_clock_step(qts, watchdog_interrupt_steps(qts, wd));
    240    g_assert_true(watchdog_read_wtcr(qts, wd) & WTIF);
    241    g_assert_false(qtest_get_irq(qts, wd->irq));
    242    qtest_clock_step(qts, watchdog_calculate_steps(RESET_CYCLES,
    243                watchdog_prescaler(qts, wd)));
    244    rsp = get_watchdog_action(qts);
    245    g_assert_false(strcmp(qdict_get_str(rsp, "action"), "reset"));
    246    qobject_unref(rsp);
    247    qtest_qmp_eventwait(qts, "RESET");
    248    qtest_quit(qts);
    249
    250    /*
    251     * The case when both flags are set is already tested in
    252     * test_reset_action().
    253     */
    254}
    255
    256/* Check a watchdog can pause and resume by setting WTE bits */
    257static void test_pause(gconstpointer watchdog)
    258{
    259    const Watchdog *wd = watchdog;
    260    QTestState *qts;
    261    int64_t remaining_steps, steps;
    262
    263    qts = qtest_init("-machine quanta-gsj");
    264    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    265    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIF | WTIE | WTRF | WTR);
    266    remaining_steps = watchdog_interrupt_steps(qts, wd);
    267    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
    268
    269    /* Run for half of the execution period. */
    270    steps = remaining_steps / 2;
    271    remaining_steps -= steps;
    272    qtest_clock_step(qts, steps);
    273
    274    /* Pause the watchdog */
    275    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTIE);
    276    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
    277
    278    /* Run for a long period of time, the watchdog shouldn't fire */
    279    qtest_clock_step(qts, steps << 4);
    280    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTIE);
    281    g_assert_false(qtest_get_irq(qts, wd->irq));
    282
    283    /* Resume the watchdog */
    284    watchdog_write_wtcr(qts, wd, WTCLK(0) | WTE | WTIE);
    285    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==, WTCLK(0) | WTE | WTIE);
    286
    287    /* Run for the reset of the execution period, the watchdog should fire */
    288    qtest_clock_step(qts, remaining_steps);
    289    g_assert_cmphex(watchdog_read_wtcr(qts, wd), ==,
    290            WTCLK(0) | WTE | WTIF | WTIE);
    291    g_assert_true(qtest_get_irq(qts, wd->irq));
    292
    293    qtest_quit(qts);
    294}
    295
    296static void watchdog_add_test(const char *name, const Watchdog* wd,
    297        GTestDataFunc fn)
    298{
    299    g_autofree char *full_name = g_strdup_printf(
    300            "npcm7xx_watchdog_timer[%d]/%s", watchdog_index(wd), name);
    301    qtest_add_data_func(full_name, wd, fn);
    302}
    303#define add_test(name, td) watchdog_add_test(#name, td, test_##name)
    304
    305int main(int argc, char **argv)
    306{
    307    g_test_init(&argc, &argv, NULL);
    308    g_test_set_nonfatal_assertions();
    309
    310    for (int i = 0; i < ARRAY_SIZE(watchdog_list); ++i) {
    311        const Watchdog *wd = &watchdog_list[i];
    312
    313        add_test(init, wd);
    314        add_test(reset_action, wd);
    315        add_test(prescaler, wd);
    316        add_test(enabling_flags, wd);
    317        add_test(pause, wd);
    318    }
    319
    320    return g_test_run();
    321}