Dear all,

we are facing an unexpected exception when running the apic timer to drive
a GPIO as a software PWM.

The platform is x86. The software runs in a bare-metal cell. The PWM
frequency is 5 KHz.

When the duty cycle is very high or very low (i.e., two subsequent
interrupts get closer) we face the following unexpected exception:

FATAL: Unhandled VM-Exit, reason 2
qualification 0
vectoring info: 0 interrupt info: 0
RIP: 0x00000000000f15d6 RSP: 0x00000000000dff08 FLAGS: 10002
RAX: 0x000000044b82f9d8 RBX: 0x00000000000f060f RCX: 0x0000000000000838
RDX: 0x0000000000000004 RSI: 0x0000000000000a36 RDI: 0xffffffffffffe134
CS: 10 BASE: 0x0000000000000000 AR-BYTES: a09b EFER.LMA 1
CR0: 0x0000000080010031 CR3: 0x00000000000f3000 CR4: 0x0000000000002020
EFER: 0x0000000000000500
Parking CPU 3 (Cell: "pwm-demo")
Closing cell "pwm-demo"
Page pool usage after cell destruction: mem 4316/16327, remap 16459/131072
CPU 3 received SIPI, vector 98


Attached the code of the inmate.

Any suggestion about the reason of this behavior ?

Many thanks and best regards,

               Claudio

-- 
Claudio Scordino, Ph.D.
Project Manager - Funded research projects

Evidence Srl
Via Carducci 56
56010 S.Giuliano Terme - Pisa - Italy
Phone:  +39 050 99 11 224
Mobile: + 39 393 811 7491
Fax:   +39 050 99 10 812
http://www.evidence.eu.com

-- 
You received this message because you are subscribed to the Google Groups 
"Jailhouse" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
For more options, visit https://groups.google.com/d/optout.
#include <inmate.h>

#define APIC_TIMER_VECTOR   (32U)

#define KILO (1000U)
#define MEGA (1000000U)

#define TICKS_TO_NANO(TICKS, REF_FREQ_HZ)       \
  (((TICKS) / KILO)?                            \
    ((TICKS) * KILO) / ((REF_FREQ_HZ) / MEGA):  \
    ((TICKS) * MEGA) / ((REF_FREQ_HZ) / KILO))

/*==============================================================================
                                GPIO Drv
 =============================================================================*/
#define GPIO_PORT_COMMAND           0xA35U
#define GPIO_PORT_DATA              0xA36U

#define GPIO_COMMAND_READ_ALL       0xA2U
#define GPIO_COMMAND_READ_IGNITION  0xF2U
#define GPIO_COMMAND_WRITE_ALL      0xA1U

#define GPIO_PIN_0                  0x0U
#define GPIO_PIN_1                  0x1U
#define GPIO_PIN_2                  0x2U
#define GPIO_PIN_3                  0x3U
#define GPIO_PIN_4                  0x4U
#define GPIO_PIN_5                  0x5U
#define GPIO_PIN_6                  0x6U
#define GPIO_PIN_7                  0x7U

#define GPIO_PIN_0_MASK             0x1U
#define GPIO_PIN_1_MASK             0x2U
#define GPIO_PIN_2_MASK             0x4U
#define GPIO_PIN_3_MASK             0x8U
#define GPIO_PIN_4_MASK             0x10U
#define GPIO_PIN_5_MASK             0x20U
#define GPIO_PIN_6_MASK             0x40U
#define GPIO_PIN_7_MASK             0x80U

#define GPIO_PIN_INPUT_0            GPIO_PIN_0
#define GPIO_PIN_INPUT_1            GPIO_PIN_1
#define GPIO_PIN_INPUT_2            GPIO_PIN_2
#define GPIO_PIN_INPUT_3            GPIO_PIN_3
#define GPIO_PIN_OUTPUT_0           GPIO_PIN_4
#define GPIO_PIN_OUTPUT_1           GPIO_PIN_5
#define GPIO_PIN_OUTPUT_2           GPIO_PIN_6
#define GPIO_PIN_OUTPUT_3           GPIO_PIN_7

#define GPIO_PIN_IGNITION           GPIO_PIN_INPUT_3

#define WATCHDOG_PORT_DATA          0xA16U
#define WATCHDOG_PORT_COMMAND       0xA15U
#define WATCHDOG_PORT_PME           0xA1AU
#define WATCHDOG_VALUE_ENABLE       0x20U
#define WATCHDOG_VALUE_PME_ENABLE   0x40U
#define WATCHDOG_VALUE_DISABLE      0x0U
#define WATCHDOG_TIME_SECONDS       0x0U
#define WATCHDOG_TIME_MINUTES       0x1U

/** Return all I/O flags. */
static inline u8 gpio_get_pins(void) {
    outb(GPIO_COMMAND_READ_ALL, GPIO_PORT_COMMAND);
    return inb(GPIO_PORT_DATA);
}

/** Write \b all I/O flags. */
static inline void gpio_set_pins(u8 value) {
    outb(GPIO_COMMAND_WRITE_ALL, GPIO_PORT_COMMAND);
    outb(value, GPIO_PORT_DATA);
}

/**
 * \brief Read pin pin from flags.
 * \param[in] pin The pin \b index, [0-7].
 * \return The bit status, 0 or 1.
 * \note It is recommended to use GPIO_PIN_X or GPIO_PIN_INPUT/OUTPUT_X macros.
 */
static inline u8 gpio_get_pin(u8 pin) {
    return (gpio_get_pins() >> pin) & 1;
}

/** Return ignition status, 0 or 1. */
static inline u8 gpio_get_ignition(void) {
    outb(GPIO_COMMAND_READ_IGNITION, GPIO_PORT_COMMAND);
    return (gpio_get_pins() >> GPIO_PIN_IGNITION) & 1;
}

/**
 * \brief Write the value value to pin pin.
 * \param[in] pin The pin \b index, [0-7].
 * \param[in] value The value of the pin to write, must be 0 or 1.
 * \note It is recommended to use GPIO_PIN_X or GPIO_PIN_INPUT/OUTPUT_X macros.
 */
static inline void gpio_set_pin(u8 pin, u8 value) {
    u8 pins = gpio_get_pins();
    pins &= ~(1 << pin);
    pins |= ((value & 1) << pin);
    gpio_set_pins(pins);
}

/**
 * \brief Toggle the pin bit.
 * \param[in] pin The pin \b index, [0-7].
 * \note It is recommended to use GPIO_PIN_X or GPIO_PIN_INPUT/OUTPUT_X macros.
 */
static inline void gpio_toggle_pin(u8 pin) {
    gpio_set_pins(gpio_get_pins() ^ (1 << pin));
}

/**
 * \brief Toggle flags with mask mask.
 * \param[in] mask The mask to xor old flags value with.
 */
static inline void gpio_toggle_pins(u8 mask) {
    gpio_set_pins(gpio_get_pins() ^ mask);
}

/*==============================================================================
                                 Application Flag
 =============================================================================*/
bool volatile app_terminate;

/*==============================================================================
                                BEGIN PWM Driver
 =============================================================================*/
#define PIN_PWM GPIO_PIN_OUTPUT_1
#define PWM_MSK (1 << PIN_PWM)

static u64 apic_freq_hz;

static volatile u64 jitter_cnt;
static volatile u64 jitter_max;
static volatile u64 jitter_avg;
static volatile u64 jitter_tot;
static volatile u64 jitter_min = ((u64)-1);

#define PWM_FREQUENCY_HZ      (4000U)
#define DATA_XCHG_FREQ_HZ     (10U)
#define DATA_XCHG_TICKS       \
  ((PWM_FREQUENCY_HZ*2U)/DATA_XCHG_FREQ_HZ)
/* 10s */
#define PWM_DUTY_SLICES           (100U)

#if 0
#define PWM_MIN_DUTY_SLICES OSEE_MICRO_TO_TICKS(PWM_MIN_INTERARRIVAL_US,
  (PWM_FREQUENCY_HZ * PWM_DUTY_SLICES) )
#else
#define PWM_MIN_DUTY_SLICES (1U)
#endif

#define PWM_STARTING_DUTY         (70U)
//#define PWM_STARTING_DUTY         PWM_MIN_DUTY_SLICES

static volatile u16       pwm_duty;
static volatile u64       pwm_expected_time;

enum pwm_front {
  PWM_ZERO_TO_ONE_FRONT = 0U,
  PWM_ONE_TO_ZERO_FRONT
};

static u64 pwm_get_time_next_front(enum pwm_front front) {
  u64 pwm_time_next_front;
  u16 front_duty;
  if (front == PWM_ONE_TO_ZERO_FRONT) {
    front_duty = pwm_duty;
  } else {
    front_duty = (PWM_DUTY_SLICES - pwm_duty);
  }
  pwm_time_next_front = TICKS_TO_NANO(((apic_freq_hz * front_duty) +
    ((PWM_FREQUENCY_HZ * PWM_DUTY_SLICES)/2U)) /
      (PWM_FREQUENCY_HZ * PWM_DUTY_SLICES), apic_freq_hz);
  return pwm_time_next_front;
}

static inline void pwm_setup_timer(u64 pwm_time_next_front) {
  pwm_expected_time = tsc_read() + pwm_time_next_front;
  apic_timer_set(pwm_time_next_front);
}

static void pwm_start ( u16 duty ) {
  if ( duty > (PWM_DUTY_SLICES - PWM_MIN_DUTY_SLICES) ) {
    duty = (PWM_DUTY_SLICES - PWM_MIN_DUTY_SLICES);
  } if ( duty < PWM_MIN_DUTY_SLICES ) {
    duty = PWM_MIN_DUTY_SLICES;
  }
  /* Set-up configuration */
  pwm_duty      = duty;
  apic_freq_hz = apic_timer_init(APIC_TIMER_VECTOR) * KILO;
  printk("Calibrated APIC frequency: %lu Hz\n", apic_freq_hz);

  /* Assure that output is zero */
  gpio_set_pin(PIN_PWM, 0);

  /* Start with a Zero-To-One Front */
  pwm_setup_timer(pwm_get_time_next_front(PWM_ZERO_TO_ONE_FRONT));
}

static void pwm_front_irq( void )
{
  u8 out;
  u64 pwm_time_next_front;
  /* Get Jitter measurement */
  u64 const delta = tsc_read() - pwm_expected_time;

  /* Handle Output */
  /* Read OUT register */
  out = gpio_get_pins();
  if ( out & PWM_MSK ) {
    out &= ~PWM_MSK;
    pwm_time_next_front = pwm_get_time_next_front(PWM_ZERO_TO_ONE_FRONT);
  } else {
    out |= PWM_MSK;
    pwm_time_next_front = pwm_get_time_next_front(PWM_ONE_TO_ZERO_FRONT);
  }
  /* Commit Changes */
  gpio_set_pins(out);

  /* Save min/MAX Jitter */
  if (delta < jitter_min) {
    jitter_min = delta;
  }
  if (delta > jitter_max) {
    jitter_max = delta;
  }
  /* Increment the counter value (I DO NOT HANDLE WRAP AROUND,
     I hope that in 8-10 hours do not wrap!!!) */
  ++jitter_cnt;
  jitter_tot += delta;
  jitter_avg = jitter_tot / jitter_cnt;

  if (!app_terminate) {
    /* Configure Timer Forn NEXT Front. Adjust it according to actual latency
       measurement + 'Timer configuration correction factor' */
    pwm_time_next_front -= (tsc_read() - pwm_expected_time) + 1400;
    pwm_setup_timer(pwm_time_next_front);
  } else {
    /* If the application is terminated: Assure that output is zero */
    gpio_set_pin(PIN_PWM, 0);
  }
}
/*==============================================================================
                                END PWM Driver
 =============================================================================*/

void inmate_main(void)
{
  unsigned long tsc_freq;
  comm_region->cell_state = JAILHOUSE_CELL_RUNNING_LOCKED;

  tsc_freq = tsc_init();
  printk("Calibrated TSC frequency: %lu.%03u kHz\n", tsc_freq / 1000,
    tsc_freq % 1000);

  int_init();
  int_set_handler(APIC_TIMER_VECTOR, pwm_front_irq);

  pwm_start(PWM_STARTING_DUTY);

  asm volatile("sti");

  while (!app_terminate) {
    asm volatile("hlt");

    {
      static u32 decimator;
      if (++decimator == DATA_XCHG_TICKS) {
        printk(
            "PWM parameters, duty:<%ld>\n"
            "PWM timer jitter, avg:<%6ld> ns, min:<%6ld> ns, max:<%6ld> ns\n"
            "PWM AVG on:<%d> Samples\n",
            pwm_duty,
            jitter_avg,
            jitter_min,
            jitter_max,
            jitter_cnt
        );
        if ( ++pwm_duty >= PWM_DUTY_SLICES - PWM_MIN_DUTY_SLICES ) {
            pwm_duty = PWM_MIN_DUTY_SLICES;
        }
        decimator = 0U;
      }
    }

    switch (comm_region->msg_to_cell) {
      case JAILHOUSE_MSG_SHUTDOWN_REQUEST:
        gpio_set_pin(PIN_PWM, 0);
        app_terminate = true;
        break;
      default:
        jailhouse_send_reply_from_cell(comm_region, JAILHOUSE_MSG_UNKNOWN);
        break;
    }
  }

  printk("PWM demo Stopped\n");
  comm_region->cell_state = JAILHOUSE_CELL_SHUT_DOWN;
}

Reply via email to