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_adc-test.c (11466B)


      1/*
      2 * QTests for Nuvoton NPCM7xx ADCModules.
      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/bitops.h"
     19#include "qemu/timer.h"
     20#include "libqos/libqtest.h"
     21#include "qapi/qmp/qdict.h"
     22
     23#define REF_HZ          (25000000)
     24
     25#define CON_OFFSET      0x0
     26#define DATA_OFFSET     0x4
     27
     28#define NUM_INPUTS      8
     29#define DEFAULT_IREF    2000000
     30#define CONV_CYCLES     20
     31#define RESET_CYCLES    10
     32#define R0_INPUT        500000
     33#define R1_INPUT        1500000
     34#define MAX_RESULT      1023
     35
     36#define DEFAULT_CLKDIV  5
     37
     38#define FUSE_ARRAY_BA   0xf018a000
     39#define FCTL_OFFSET     0x14
     40#define FST_OFFSET      0x0
     41#define FADDR_OFFSET    0x4
     42#define FDATA_OFFSET    0x8
     43#define ADC_CALIB_ADDR  24
     44#define FUSE_READ       0x2
     45
     46/* Register field definitions. */
     47#define CON_MUX(rv) ((rv) << 24)
     48#define CON_INT_EN  BIT(21)
     49#define CON_REFSEL  BIT(19)
     50#define CON_INT     BIT(18)
     51#define CON_EN      BIT(17)
     52#define CON_RST     BIT(16)
     53#define CON_CONV    BIT(14)
     54#define CON_DIV(rv) extract32(rv, 1, 8)
     55
     56#define FST_RDST    BIT(1)
     57#define FDATA_MASK  0xff
     58
     59#define MAX_ERROR   10000
     60#define MIN_CALIB_INPUT 100000
     61#define MAX_CALIB_INPUT 1800000
     62
     63static const uint32_t input_list[] = {
     64    100000,
     65    500000,
     66    1000000,
     67    1500000,
     68    1800000,
     69    2000000,
     70};
     71
     72static const uint32_t vref_list[] = {
     73    2000000,
     74    2200000,
     75    2500000,
     76};
     77
     78static const uint32_t iref_list[] = {
     79    1800000,
     80    1900000,
     81    2000000,
     82    2100000,
     83    2200000,
     84};
     85
     86static const uint32_t div_list[] = {0, 1, 3, 7, 15};
     87
     88typedef struct ADC {
     89    int irq;
     90    uint64_t base_addr;
     91} ADC;
     92
     93ADC adc = {
     94    .irq        = 0,
     95    .base_addr  = 0xf000c000
     96};
     97
     98static uint32_t adc_read_con(QTestState *qts, const ADC *adc)
     99{
    100    return qtest_readl(qts, adc->base_addr + CON_OFFSET);
    101}
    102
    103static void adc_write_con(QTestState *qts, const ADC *adc, uint32_t value)
    104{
    105    qtest_writel(qts, adc->base_addr + CON_OFFSET, value);
    106}
    107
    108static uint32_t adc_read_data(QTestState *qts, const ADC *adc)
    109{
    110    return qtest_readl(qts, adc->base_addr + DATA_OFFSET);
    111}
    112
    113static uint32_t adc_calibrate(uint32_t measured, uint32_t *rv)
    114{
    115    return R0_INPUT + (R1_INPUT - R0_INPUT) * (int32_t)(measured - rv[0])
    116        / (int32_t)(rv[1] - rv[0]);
    117}
    118
    119static void adc_qom_set(QTestState *qts, const ADC *adc,
    120        const char *name, uint32_t value)
    121{
    122    QDict *response;
    123    const char *path = "/machine/soc/adc";
    124
    125    g_test_message("Setting properties %s of %s with value %u",
    126            name, path, value);
    127    response = qtest_qmp(qts, "{ 'execute': 'qom-set',"
    128            " 'arguments': { 'path': %s, 'property': %s, 'value': %u}}",
    129            path, name, value);
    130    /* The qom set message returns successfully. */
    131    g_assert_true(qdict_haskey(response, "return"));
    132    qobject_unref(response);
    133}
    134
    135static void adc_write_input(QTestState *qts, const ADC *adc,
    136        uint32_t index, uint32_t value)
    137{
    138    char name[100];
    139
    140    sprintf(name, "adci[%u]", index);
    141    adc_qom_set(qts, adc, name, value);
    142}
    143
    144static void adc_write_vref(QTestState *qts, const ADC *adc, uint32_t value)
    145{
    146    adc_qom_set(qts, adc, "vref", value);
    147}
    148
    149static uint32_t adc_calculate_output(uint32_t input, uint32_t ref)
    150{
    151    uint32_t output;
    152
    153    g_assert_cmpuint(input, <=, ref);
    154    output = (input * (MAX_RESULT + 1)) / ref;
    155    if (output > MAX_RESULT) {
    156        output = MAX_RESULT;
    157    }
    158
    159    return output;
    160}
    161
    162static uint32_t adc_prescaler(QTestState *qts, const ADC *adc)
    163{
    164    uint32_t div = extract32(adc_read_con(qts, adc), 1, 8);
    165
    166    return 2 * (div + 1);
    167}
    168
    169static int64_t adc_calculate_steps(uint32_t cycles, uint32_t prescale,
    170        uint32_t clkdiv)
    171{
    172    return (NANOSECONDS_PER_SECOND / (REF_HZ >> clkdiv)) * cycles * prescale;
    173}
    174
    175static void adc_wait_conv_finished(QTestState *qts, const ADC *adc,
    176        uint32_t clkdiv)
    177{
    178    uint32_t prescaler = adc_prescaler(qts, adc);
    179
    180    /*
    181     * ADC should takes roughly 20 cycles to convert one sample. So we assert it
    182     * should take 10~30 cycles here.
    183     */
    184    qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES / 2, prescaler,
    185                clkdiv));
    186    /* ADC is still converting. */
    187    g_assert_true(adc_read_con(qts, adc) & CON_CONV);
    188    qtest_clock_step(qts, adc_calculate_steps(CONV_CYCLES, prescaler, clkdiv));
    189    /* ADC has finished conversion. */
    190    g_assert_false(adc_read_con(qts, adc) & CON_CONV);
    191}
    192
    193/* Check ADC can be reset to default value. */
    194static void test_init(gconstpointer adc_p)
    195{
    196    const ADC *adc = adc_p;
    197
    198    QTestState *qts = qtest_init("-machine quanta-gsj");
    199    adc_write_con(qts, adc, CON_REFSEL | CON_INT);
    200    g_assert_cmphex(adc_read_con(qts, adc), ==, CON_REFSEL);
    201    qtest_quit(qts);
    202}
    203
    204/* Check ADC can convert from an internal reference. */
    205static void test_convert_internal(gconstpointer adc_p)
    206{
    207    const ADC *adc = adc_p;
    208    uint32_t index, input, output, expected_output;
    209    QTestState *qts = qtest_init("-machine quanta-gsj");
    210    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    211
    212    for (index = 0; index < NUM_INPUTS; ++index) {
    213        for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
    214            input = input_list[i];
    215            expected_output = adc_calculate_output(input, DEFAULT_IREF);
    216
    217            adc_write_input(qts, adc, index, input);
    218            adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
    219                    CON_EN | CON_CONV);
    220            adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
    221            g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) |
    222                    CON_REFSEL | CON_EN);
    223            g_assert_false(qtest_get_irq(qts, adc->irq));
    224            output = adc_read_data(qts, adc);
    225            g_assert_cmpuint(output, ==, expected_output);
    226        }
    227    }
    228
    229    qtest_quit(qts);
    230}
    231
    232/* Check ADC can convert from an external reference. */
    233static void test_convert_external(gconstpointer adc_p)
    234{
    235    const ADC *adc = adc_p;
    236    uint32_t index, input, vref, output, expected_output;
    237    QTestState *qts = qtest_init("-machine quanta-gsj");
    238    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    239
    240    for (index = 0; index < NUM_INPUTS; ++index) {
    241        for (size_t i = 0; i < ARRAY_SIZE(input_list); ++i) {
    242            for (size_t j = 0; j < ARRAY_SIZE(vref_list); ++j) {
    243                input = input_list[i];
    244                vref = vref_list[j];
    245                expected_output = adc_calculate_output(input, vref);
    246
    247                adc_write_input(qts, adc, index, input);
    248                adc_write_vref(qts, adc, vref);
    249                adc_write_con(qts, adc, CON_MUX(index) | CON_INT | CON_EN |
    250                        CON_CONV);
    251                adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
    252                g_assert_cmphex(adc_read_con(qts, adc), ==,
    253                        CON_MUX(index) | CON_EN);
    254                g_assert_false(qtest_get_irq(qts, adc->irq));
    255                output = adc_read_data(qts, adc);
    256                g_assert_cmpuint(output, ==, expected_output);
    257            }
    258        }
    259    }
    260
    261    qtest_quit(qts);
    262}
    263
    264/* Check ADC interrupt files if and only if CON_INT_EN is set. */
    265static void test_interrupt(gconstpointer adc_p)
    266{
    267    const ADC *adc = adc_p;
    268    uint32_t index, input, output, expected_output;
    269    QTestState *qts = qtest_init("-machine quanta-gsj");
    270
    271    index = 1;
    272    input = input_list[1];
    273    expected_output = adc_calculate_output(input, DEFAULT_IREF);
    274
    275    qtest_irq_intercept_in(qts, "/machine/soc/a9mpcore/gic");
    276    adc_write_input(qts, adc, index, input);
    277    g_assert_false(qtest_get_irq(qts, adc->irq));
    278    adc_write_con(qts, adc, CON_MUX(index) | CON_INT_EN | CON_REFSEL | CON_INT
    279            | CON_EN | CON_CONV);
    280    adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
    281    g_assert_cmphex(adc_read_con(qts, adc), ==, CON_MUX(index) | CON_INT_EN
    282            | CON_REFSEL | CON_INT | CON_EN);
    283    g_assert_true(qtest_get_irq(qts, adc->irq));
    284    output = adc_read_data(qts, adc);
    285    g_assert_cmpuint(output, ==, expected_output);
    286
    287    qtest_quit(qts);
    288}
    289
    290/* Check ADC is reset after setting ADC_RST for 10 ADC cycles. */
    291static void test_reset(gconstpointer adc_p)
    292{
    293    const ADC *adc = adc_p;
    294    QTestState *qts = qtest_init("-machine quanta-gsj");
    295
    296    for (size_t i = 0; i < ARRAY_SIZE(div_list); ++i) {
    297        uint32_t div = div_list[i];
    298
    299        adc_write_con(qts, adc, CON_INT | CON_EN | CON_RST | CON_DIV(div));
    300        qtest_clock_step(qts, adc_calculate_steps(RESET_CYCLES,
    301                    adc_prescaler(qts, adc), DEFAULT_CLKDIV));
    302        g_assert_false(adc_read_con(qts, adc) & CON_EN);
    303    }
    304    qtest_quit(qts);
    305}
    306
    307/* Check ADC Calibration works as desired. */
    308static void test_calibrate(gconstpointer adc_p)
    309{
    310    int i, j;
    311    const ADC *adc = adc_p;
    312
    313    for (j = 0; j < ARRAY_SIZE(iref_list); ++j) {
    314        uint32_t iref = iref_list[j];
    315        uint32_t expected_rv[] = {
    316            adc_calculate_output(R0_INPUT, iref),
    317            adc_calculate_output(R1_INPUT, iref),
    318        };
    319        char buf[100];
    320        QTestState *qts;
    321
    322        sprintf(buf, "-machine quanta-gsj -global npcm7xx-adc.iref=%u", iref);
    323        qts = qtest_init(buf);
    324
    325        /* Check the converted value is correct using the calibration value. */
    326        for (i = 0; i < ARRAY_SIZE(input_list); ++i) {
    327            uint32_t input;
    328            uint32_t output;
    329            uint32_t expected_output;
    330            uint32_t calibrated_voltage;
    331            uint32_t index = 0;
    332
    333            input = input_list[i];
    334            /* Calibration only works for input range 0.1V ~ 1.8V. */
    335            if (input < MIN_CALIB_INPUT || input > MAX_CALIB_INPUT) {
    336                continue;
    337            }
    338            expected_output = adc_calculate_output(input, iref);
    339
    340            adc_write_input(qts, adc, index, input);
    341            adc_write_con(qts, adc, CON_MUX(index) | CON_REFSEL | CON_INT |
    342                    CON_EN | CON_CONV);
    343            adc_wait_conv_finished(qts, adc, DEFAULT_CLKDIV);
    344            g_assert_cmphex(adc_read_con(qts, adc), ==,
    345                    CON_REFSEL | CON_MUX(index) | CON_EN);
    346            output = adc_read_data(qts, adc);
    347            g_assert_cmpuint(output, ==, expected_output);
    348
    349            calibrated_voltage = adc_calibrate(output, expected_rv);
    350            g_assert_cmpuint(calibrated_voltage, >, input - MAX_ERROR);
    351            g_assert_cmpuint(calibrated_voltage, <, input + MAX_ERROR);
    352        }
    353
    354        qtest_quit(qts);
    355    }
    356}
    357
    358static void adc_add_test(const char *name, const ADC* wd,
    359        GTestDataFunc fn)
    360{
    361    g_autofree char *full_name = g_strdup_printf("npcm7xx_adc/%s",  name);
    362    qtest_add_data_func(full_name, wd, fn);
    363}
    364#define add_test(name, td) adc_add_test(#name, td, test_##name)
    365
    366int main(int argc, char **argv)
    367{
    368    g_test_init(&argc, &argv, NULL);
    369
    370    add_test(init, &adc);
    371    add_test(convert_internal, &adc);
    372    add_test(convert_external, &adc);
    373    add_test(interrupt, &adc);
    374    add_test(reset, &adc);
    375    add_test(calibrate, &adc);
    376
    377    return g_test_run();
    378}