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;
}