[PATCH 5/5] bsps/x86_64: Add APIC timer based clock driver
Amaan Cheval
amaan.cheval at gmail.com
Mon Aug 13 11:27:24 UTC 2018
The APIC timer is calibrated by running the i8254 PIT for a fraction of a
second (determined by PIT_CALIBRATE_DIVIDER) and counting how many times the
APIC counter has ticked. The calibration can be run multiple times (determined
by APIC_TIMER_NUM_CALIBRATIONS) and averaged out.
Updates #2898.
---
bsps/x86_64/amd64/clock/clock.c | 299 ++++++++++++++++++
bsps/x86_64/amd64/headers.am | 3 +
bsps/x86_64/amd64/include/apic.h | 62 ++++
bsps/x86_64/amd64/include/clock.h | 99 ++++++
bsps/x86_64/amd64/include/pic.h | 75 +++++
bsps/x86_64/amd64/interrupts/pic.c | 76 +++++
c/src/lib/libbsp/x86_64/amd64/Makefile.am | 4 +-
.../cpu/x86_64/include/rtems/score/cpu_asm.h | 23 ++
8 files changed, 640 insertions(+), 1 deletion(-)
create mode 100644 bsps/x86_64/amd64/clock/clock.c
create mode 100644 bsps/x86_64/amd64/include/apic.h
create mode 100644 bsps/x86_64/amd64/include/clock.h
create mode 100644 bsps/x86_64/amd64/include/pic.h
create mode 100644 bsps/x86_64/amd64/interrupts/pic.c
diff --git a/bsps/x86_64/amd64/clock/clock.c b/bsps/x86_64/amd64/clock/clock.c
new file mode 100644
index 0000000000..76e537755a
--- /dev/null
+++ b/bsps/x86_64/amd64/clock/clock.c
@@ -0,0 +1,299 @@
+/*
+ * Copyright (c) 2018.
+ * Amaan Cheval <amaan.cheval at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <assert.h>
+#include <bsp.h>
+#include <rtems.h>
+#include <pic.h>
+#include <apic.h>
+#include <clock.h>
+#include <rtems/score/idt.h>
+#include <rtems/timecounter.h>
+#include <rtems/score/cpu.h>
+#include <rtems/score/cpuimpl.h>
+#include <rtems/score/x86_64.h>
+#include <bsp/irq-generic.h>
+
+/* Use the amd64_apic_base as an array of 32-bit APIC registers */
+volatile uint32_t *amd64_apic_base;
+static struct timecounter amd64_clock_tc;
+
+extern volatile uint32_t Clock_driver_ticks;
+extern void apic_spurious_handler(void);
+extern void Clock_isr(void *param);
+
+static uint32_t amd64_clock_get_timecount(struct timecounter *tc)
+{
+ return Clock_driver_ticks;
+}
+
+/*
+ * When the CPUID instruction is executed with a source operand of 1 in the EAX
+ * register, bit 9 of the CPUID feature flags returned in the EDX register
+ * indicates the presence (set) or absence (clear) of a local APIC.
+ */
+bool has_apic_support()
+{
+ uint32_t eax, ebx, ecx, edx;
+ cpuid(1, &eax, &ebx, &ecx, &edx);
+ return (edx >> 9) & 1;
+}
+
+/*
+ * Initializes the APIC by hardware and software enabling it, and sets up the
+ * amd64_apic_base pointer that can be used as a 32-bit addressable array to
+ * access APIC registers.
+ */
+void apic_initialize(void)
+{
+ if ( !has_apic_support() ) {
+ printf("warning: cpuid claims no APIC support - trying anyway.\n");
+ }
+
+ /*
+ * The APIC base address is a 36-bit physical address.
+ * We have identity-paging setup at the moment, which makes this simpler, but
+ * that's something to note since the variables below use virtual addresses.
+ *
+ * Bits 0-11 (inclusive) are 0, making the address page (4KiB) aligned.
+ * Bits 12-35 (inclusive) of the MSR point to the rest of the address.
+ */
+ uint64_t apic_base_msr = rdmsr(APIC_BASE_MSR);
+ amd64_apic_base = (uint32_t*) apic_base_msr;
+ amd64_apic_base = (uint32_t*) ((uintptr_t) amd64_apic_base & 0x0ffffff000);
+
+ /* Hardware enable the APIC just to be sure */
+ wrmsr(
+ APIC_BASE_MSR,
+ apic_base_msr | APIC_BASE_MSR_ENABLE,
+ apic_base_msr >> 32
+ );
+
+ DBG_PRINTF("APIC is at 0x%" PRIxPTR "\n", (uintptr_t) amd64_apic_base);
+ DBG_PRINTF(
+ "APIC ID at *0x%" PRIxPTR "=0x%" PRIx32 "\n",
+ (uintptr_t) &amd64_apic_base[APIC_REGISTER_APICID],
+ amd64_apic_base[APIC_REGISTER_APICID]
+ );
+
+ DBG_PRINTF(
+ "APIC spurious vector register *0x%" PRIxPTR "=0x%" PRIx32 "\n",
+ (uintptr_t) &amd64_apic_base[APIC_REGISTER_SPURIOUS],
+ amd64_apic_base[APIC_REGISTER_SPURIOUS]
+ );
+
+ /*
+ * Software enable the APIC by mapping spurious vector and setting enable bit.
+ */
+ uintptr_t old;
+ amd64_install_raw_interrupt(
+ BSP_VECTOR_SPURIOUS,
+ (uintptr_t) apic_spurious_handler,
+ &old
+ );
+ amd64_apic_base[APIC_REGISTER_SPURIOUS] =
+ APIC_SPURIOUS_ENABLE | BSP_VECTOR_SPURIOUS;
+
+ DBG_PRINTF(
+ "APIC spurious vector register *0x%" PRIxPTR "=0x%" PRIx32 "\n",
+ (uintptr_t) &amd64_apic_base[APIC_REGISTER_SPURIOUS],
+ amd64_apic_base[APIC_REGISTER_SPURIOUS]
+ );
+
+ /*
+ * The PIC may send spurious IRQs even when disabled, and without remapping
+ * IRQ7 would look like an exception.
+ */
+ pic_remap(PIC1_REMAP_DEST, PIC2_REMAP_DEST);
+ pic_disable();
+}
+
+static void apic_isr(void *param)
+{
+ Clock_isr(param);
+ amd64_apic_base[APIC_REGISTER_EOI] = APIC_EOI_ACK;
+}
+
+void apic_timer_install_handler(void)
+{
+ rtems_status_code sc = rtems_interrupt_handler_install(
+ BSP_VECTOR_APIC_TIMER,
+ "APIC timer",
+ RTEMS_INTERRUPT_UNIQUE,
+ apic_isr,
+ NULL
+ );
+ assert(sc == RTEMS_SUCCESSFUL);
+}
+
+uint32_t apic_timer_calibrate(void)
+{
+ /* Configure APIC timer in one-shot mode to prepare for calibration */
+ amd64_apic_base[APIC_REGISTER_LVT_TIMER] = BSP_VECTOR_APIC_TIMER;
+ amd64_apic_base[APIC_REGISTER_TIMER_DIV] = APIC_TIMER_SELECT_DIVIDER;
+
+ /* Enable the channel 2 timer gate and disable the speaker output */
+ uint8_t chan2_value = (inport_byte(PIT_PORT_CHAN2_GATE) | PIT_CHAN2_TIMER_BIT)
+ & ~PIT_CHAN2_SPEAKER_BIT;
+ outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);
+
+ /* Initialize PIT in one-shot mode on Channel 2 */
+ outport_byte(
+ PIT_PORT_MCR,
+ PIT_SELECT_CHAN2 | PIT_SELECT_ACCESS_LOHI |
+ PIT_SELECT_ONE_SHOT_MODE | PIT_SELECT_BINARY_MODE
+ );
+
+ /*
+ * Disable interrupts while we calibrate for 2 reasons:
+ * - Writing values to the PIT should be atomic (for now, this is okay
+ * because we're the only ones ever touching the PIT ports, but an
+ * interrupt resetting the PIT could mess calibration up).
+ * - We need to make sure that no interrupts throw our synchronization of
+ * the APIC timer off.
+ */
+ amd64_disable_interrupts();
+
+ /* Set PIT reload value */
+ uint32_t pit_ticks = PIT_CALIBRATE_TICKS;
+ uint8_t low_tick = pit_ticks & 0xff;
+ uint8_t high_tick = (pit_ticks >> 8) & 0xff;
+
+ outport_byte(PIT_PORT_CHAN2, low_tick);
+ stub_io_wait();
+ outport_byte(PIT_PORT_CHAN2, high_tick);
+
+ /* Start APIC timer's countdown */
+ const uint32_t apic_calibrate_init_count = 0xffffffff;
+
+ /* Restart PIT by disabling the gated input and then re-enabling it */
+ chan2_value &= ~PIT_CHAN2_TIMER_BIT;
+ outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);
+ chan2_value |= PIT_CHAN2_TIMER_BIT;
+ outport_byte(PIT_PORT_CHAN2_GATE, chan2_value);
+ amd64_apic_base[APIC_REGISTER_TIMER_INITCNT] = apic_calibrate_init_count;
+
+ while ( pit_ticks <= PIT_CALIBRATE_TICKS ) {
+ /* Send latch command to read multi-byte value atomically */
+ outport_byte(PIT_PORT_MCR, PIT_SELECT_CHAN2);
+ pit_ticks = inport_byte(PIT_PORT_CHAN2);
+ pit_ticks |= inport_byte(PIT_PORT_CHAN2) << 8;
+ }
+ uint32_t apic_currcnt = amd64_apic_base[APIC_REGISTER_TIMER_CURRCNT];
+
+ DBG_PRINTF("PIT stopped at 0x%" PRIx32 "\n", pit_ticks);
+
+ /* Stop APIC timer to calculate ticks to time ratio */
+ amd64_apic_base[APIC_REGISTER_LVT_TIMER] = APIC_DISABLE;
+
+ /* Get counts passed since we started counting */
+ uint32_t apic_ticks_per_sec = apic_calibrate_init_count - apic_currcnt;
+
+ DBG_PRINTF(
+ "APIC ticks passed in 1/%d of a second: 0x%" PRIx32 "\n",
+ PIT_CALIBRATE_DIVIDER,
+ apic_ticks_per_sec
+ );
+
+ /* We ran the PIT for a fraction of a second */
+ apic_ticks_per_sec = apic_ticks_per_sec * PIT_CALIBRATE_DIVIDER;
+
+ /* Re-enable interrupts now that calibration is complete */
+ amd64_enable_interrupts();
+
+ /* Confirm that the APIC timer never hit 0 and IRQ'd during calibration */
+ assert(Clock_driver_ticks == 0);
+ assert(apic_ticks_per_sec != 0 &&
+ apic_ticks_per_sec != apic_calibrate_init_count);
+
+ DBG_PRINTF(
+ "CPU frequency: 0x%" PRIu64 "\nAPIC ticks/sec: 0x%" PRIu32 "\n",
+ /* Multiply to undo effects of divider */
+ (uint64_t) apic_ticks_per_sec * APIC_TIMER_DIVIDE_VALUE,
+ apic_ticks_per_sec
+ );
+
+ return apic_ticks_per_sec;
+}
+
+void apic_timer_initialize(uint64_t desired_freq_hz)
+{
+ uint32_t apic_ticks_per_sec = 0;
+ uint32_t apic_tick_collections[APIC_TIMER_NUM_CALIBRATIONS] = {0};
+ uint64_t apic_tick_total = 0;
+ for (uint32_t i = 0; i < APIC_TIMER_NUM_CALIBRATIONS; i++) {
+ apic_tick_collections[i] = apic_timer_calibrate();
+ apic_tick_total += apic_tick_collections[i];
+ }
+ apic_ticks_per_sec = apic_tick_total / APIC_TIMER_NUM_CALIBRATIONS;
+
+ /*
+ * The APIC timer counter is decremented at the speed of the CPU bus
+ * frequency (and we use a frequency divider).
+ *
+ * This means:
+ * apic_ticks_per_sec = (cpu_bus_frequency / timer_divide_value)
+ *
+ * Therefore:
+ * reload_value = apic_ticks_per_sec / desired_freq_hz
+ */
+ uint32_t apic_timer_reload_value = apic_ticks_per_sec / desired_freq_hz;
+
+ amd64_apic_base[APIC_REGISTER_LVT_TIMER] = BSP_VECTOR_APIC_TIMER | APIC_SELECT_TMR_PERIODIC;
+ amd64_apic_base[APIC_REGISTER_TIMER_DIV] = APIC_TIMER_SELECT_DIVIDER;
+ amd64_apic_base[APIC_REGISTER_TIMER_INITCNT] = apic_timer_reload_value;
+}
+
+void amd64_clock_driver_initialize(void)
+{
+ uint64_t us_per_tick = rtems_configuration_get_microseconds_per_tick();
+ uint64_t irq_ticks_per_sec = 1000000 / us_per_tick;
+ DBG_PRINTF(
+ "us_per_tick = %d\nDesired frequency = %d irqs/sec\n",
+ us_per_tick,
+ irq_ticks_per_sec
+ );
+
+ /* Setup and enable the APIC itself */
+ apic_initialize();
+ /* Setup and initialize the APIC timer */
+ apic_timer_initialize(irq_ticks_per_sec);
+
+ amd64_clock_tc.tc_get_timecount = amd64_clock_get_timecount;
+ amd64_clock_tc.tc_counter_mask = 0xffffffff;
+ amd64_clock_tc.tc_frequency = irq_ticks_per_sec;
+ amd64_clock_tc.tc_quality = RTEMS_TIMECOUNTER_QUALITY_CLOCK_DRIVER;
+ rtems_timecounter_install(&amd64_clock_tc);
+}
+
+#define Clock_driver_support_install_isr(_new) \
+ apic_timer_install_handler()
+
+#define Clock_driver_support_initialize_hardware() \
+ amd64_clock_driver_initialize()
+
+#include "../../../shared/dev/clock/clockimpl.h"
diff --git a/bsps/x86_64/amd64/headers.am b/bsps/x86_64/amd64/headers.am
index f9f42e7fd3..68f5858406 100644
--- a/bsps/x86_64/amd64/headers.am
+++ b/bsps/x86_64/amd64/headers.am
@@ -1,7 +1,10 @@
## This file was generated by "./boostrap -H".
include_HEADERS =
+include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/apic.h
include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/bsp.h
include_HEADERS += include/bspopts.h
+include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/clock.h
+include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/pic.h
include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/start.h
include_HEADERS += ../../../../../../bsps/x86_64/amd64/include/tm27.h
diff --git a/bsps/x86_64/amd64/include/apic.h b/bsps/x86_64/amd64/include/apic.h
new file mode 100644
index 0000000000..f1e6495daa
--- /dev/null
+++ b/bsps/x86_64/amd64/include/apic.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2018.
+ * Amaan Cheval <amaan.cheval at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _AMD64_APIC_H
+#define _AMD64_APIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The address of the MSR pointing to the APIC base physical address */
+#define APIC_BASE_MSR 0x1B
+/* Value to hardware-enable the APIC through the APIC_BASE_MSR */
+#define APIC_BASE_MSR_ENABLE 0x800
+
+/*
+ * Since amd64_apic_base is an array of 32-bit elements, these byte-offsets
+ * need to be divided by 4 to index the array.
+ */
+#define APIC_OFFSET(val) (val >> 2)
+
+#define APIC_REGISTER_APICID APIC_OFFSET(0x20)
+#define APIC_REGISTER_EOI APIC_OFFSET(0x0B0)
+#define APIC_REGISTER_SPURIOUS APIC_OFFSET(0x0F0)
+#define APIC_REGISTER_LVT_TIMER APIC_OFFSET(0x320)
+#define APIC_REGISTER_TIMER_INITCNT APIC_OFFSET(0x380)
+#define APIC_REGISTER_TIMER_CURRCNT APIC_OFFSET(0x390)
+#define APIC_REGISTER_TIMER_DIV APIC_OFFSET(0x3E0)
+
+#define APIC_DISABLE 0x10000
+#define APIC_EOI_ACK 0
+#define APIC_SELECT_TMR_PERIODIC 0x20000
+#define APIC_SPURIOUS_ENABLE 0x100
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AMD64_APIC_H */
diff --git a/bsps/x86_64/amd64/include/clock.h b/bsps/x86_64/amd64/include/clock.h
new file mode 100644
index 0000000000..4219c92d48
--- /dev/null
+++ b/bsps/x86_64/amd64/include/clock.h
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2018.
+ * Amaan Cheval <amaan.cheval at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _AMD64_CLOCK_H
+#define _AMD64_CLOCK_H
+
+#include <rtems/score/basedefs.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ASM
+ extern volatile uint32_t *amd64_apic_base;
+ bool has_apic_support(void);
+ void apic_initialize(void);
+ void apic_timer_install_handler(void);
+ uint32_t apic_timer_calibrate(void);
+ void apic_timer_initialize(uint64_t desired_freq_hz);
+ void amd64_clock_driver_initialize(void);
+#endif
+
+/* Number of times to calibrate the APIC timer to average it out */
+#define APIC_TIMER_NUM_CALIBRATIONS 5
+/* Default divide value used by APIC timer */
+#define APIC_TIMER_DIVIDE_VALUE 16
+/* Value to set in register to pick the divide value above */
+#define APIC_TIMER_SELECT_DIVIDER 3
+
+#define PIT_FREQUENCY 1193180
+/*
+ * The PIT_FREQUENCY determines how many times the PIT counter is decremented
+ * per second - therefore, we can calculate how many ticks we set based on what
+ * fraction of a second we're okay with spending on calibration
+ */
+#define PIT_CALIBRATE_DIVIDER 20
+#define PIT_CALIBRATE_TICKS (PIT_FREQUENCY/PIT_CALIBRATE_DIVIDER)
+/* Since the PIT only has 2 one-byte registers, the maximum tick value is
+ * limited to 16-bits. We can set the PIT to use a frequency divider if
+ * needed. */
+RTEMS_STATIC_ASSERT(
+ PIT_CALIBRATE_TICKS <= 0xffff,
+ PIT_CALIBRATE_DIVIDER
+);
+
+/* I/O ports for the PIT */
+#define PIT_PORT_CHAN0 0x40
+#define PIT_PORT_CHAN1 0x41
+#define PIT_PORT_CHAN2 0x42
+/*
+ * The input to channel 2 can be gated through software, using bit 0 of port
+ * 0x61.
+ */
+#define PIT_PORT_CHAN2_GATE 0x61
+#define PIT_CHAN2_TIMER_BIT 1
+#define PIT_CHAN2_SPEAKER_BIT 2
+/* The PIT mode/command register */
+#define PIT_PORT_MCR 0x43
+
+/* PIT values to select channels, access, and operating modes */
+#define PIT_SELECT_CHAN0 0b00000000
+#define PIT_SELECT_CHAN1 0b01000000
+#define PIT_SELECT_CHAN2 0b10000000
+/*
+ * In the lo/hi mode, the low-byte is sent to the data port, followed by the
+ * high-byte; this makes it important that this be an atomic operation.
+ */
+#define PIT_SELECT_ACCESS_LOHI 0b00110000
+#define PIT_SELECT_ONE_SHOT_MODE 0b00000010
+#define PIT_SELECT_BINARY_MODE 0
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AMD64_CLOCK_H */
diff --git a/bsps/x86_64/amd64/include/pic.h b/bsps/x86_64/amd64/include/pic.h
new file mode 100644
index 0000000000..f2b0aa83e2
--- /dev/null
+++ b/bsps/x86_64/amd64/include/pic.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2018.
+ * Amaan Cheval <amaan.cheval at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef _AMD64_PIC_H
+#define _AMD64_PIC_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define PIC1 0x20 /* IO base address for master PIC */
+#define PIC2 0xA0 /* IO base address for slave PIC */
+#define PIC1_COMMAND PIC1
+#define PIC1_DATA (PIC1+1)
+#define PIC2_COMMAND PIC2
+#define PIC2_DATA (PIC2+1)
+
+/* reinitialize the PIC controllers, giving them specified vector offsets
+ rather than 8h and 70h, as configured by default */
+
+#define PIC_ICW1_ICW4 0x01 /* ICW4 (not) needed */
+#define PIC_ICW1_SINGLE 0x02 /* Single (cascade) mode */
+#define PIC_ICW1_INTERVAL4 0x04 /* Call address interval 4 (8) */
+#define PIC_ICW1_LEVEL 0x08 /* Level triggered (edge) mode */
+#define PIC_ICW1_INIT 0x10 /* Initialization - required! */
+
+#define PIC_ICW4_8086 0x01 /* 8086/88 (MCS-80/85) mode */
+#define PIC_ICW4_AUTO 0x02 /* Auto (normal) EOI */
+#define PIC_ICW4_BUF_SLAVE 0x08 /* Buffered mode/slave */
+#define PIC_ICW4_BUF_MASTER 0x0C /* Buffered mode/master */
+#define PIC_ICW4_SFNM 0x10 /* Special fully nested (not) */
+
+/* This remaps IRQ0 to vector number 0x20 and so on (i.e. IDT[32]) */
+#define PIC1_REMAP_DEST 0x20
+#define PIC2_REMAP_DEST 0x28
+
+/* Remap PIC1's interrupts to offset1 and PIC2's to offset2 */
+void pic_remap(uint8_t offset1, uint8_t offset2);
+
+/**
+ * Mask all interrupt requests on PIC.
+ *
+ * @note Even with all interrupts masked, the PIC may still send spurious
+ * interrupts (IRQ7), so we should handle them still.
+ */
+void pic_disable(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _AMD64_PIC_H */
diff --git a/bsps/x86_64/amd64/interrupts/pic.c b/bsps/x86_64/amd64/interrupts/pic.c
new file mode 100644
index 0000000000..c5a890b885
--- /dev/null
+++ b/bsps/x86_64/amd64/interrupts/pic.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 2018.
+ * Amaan Cheval <amaan.cheval at gmail.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <stdint.h>
+#include <rtems.h>
+#include <rtems/score/basedefs.h>
+#include <rtems/score/x86_64.h>
+#include <rtems/score/cpuimpl.h>
+#include <bsp/irq-generic.h>
+#include <pic.h>
+
+void pic_remap(uint8_t offset1, uint8_t offset2)
+{
+ uint8_t a1, a2;
+
+ /* save masks */
+ a1 = inport_byte(PIC1_DATA);
+ a2 = inport_byte(PIC2_DATA);
+
+ /* start the initialization sequence in cascade mode */
+ outport_byte(PIC1_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ stub_io_wait();
+ outport_byte(PIC2_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
+ stub_io_wait();
+ /* ICW2: Master PIC vector offset */
+ outport_byte(PIC1_DATA, offset1);
+ stub_io_wait();
+ /* ICW2: Slave PIC vector offset */
+ outport_byte(PIC2_DATA, offset2);
+ stub_io_wait();
+ /* ICW3: tell Master PIC that there is a slave PIC at IRQ2 (0000 0100) */
+ outport_byte(PIC1_DATA, 4);
+ stub_io_wait();
+ /* ICW3: tell Slave PIC its cascade identity (0000 0010) */
+ outport_byte(PIC2_DATA, 2);
+ stub_io_wait();
+
+ outport_byte(PIC1_DATA, PIC_ICW4_8086);
+ stub_io_wait();
+ outport_byte(PIC2_DATA, PIC_ICW4_8086);
+ stub_io_wait();
+
+ /* restore saved masks. */
+ outport_byte(PIC1_DATA, a1);
+ outport_byte(PIC2_DATA, a2);
+}
+
+void pic_disable(void)
+{
+ /* Mask all lines on both master and slave PIC to disable */
+ outport_byte(PIC1_DATA, 0xff);
+ outport_byte(PIC2_DATA, 0xff);
+}
diff --git a/c/src/lib/libbsp/x86_64/amd64/Makefile.am b/c/src/lib/libbsp/x86_64/amd64/Makefile.am
index d4102cdfb4..101c98b96c 100644
--- a/c/src/lib/libbsp/x86_64/amd64/Makefile.am
+++ b/c/src/lib/libbsp/x86_64/amd64/Makefile.am
@@ -27,11 +27,13 @@ librtemsbsp_a_SOURCES += ../../../../../../bsps/x86_64/amd64/start/page.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/start/sbrk.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/getentropy/getentropy-cpucounter.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/start/bspreset-empty.c
+# interrupts
+librtemsbsp_a_SOURCES += ../../../../../../bsps/x86_64/amd64/interrupts/pic.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/x86_64/amd64/interrupts/idt.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/x86_64/amd64/interrupts/isr_handler.S
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/irq/irq-default-handler.c
# clock
-librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/clock/clock-simidle.c
+librtemsbsp_a_SOURCES += ../../../../../../bsps/x86_64/amd64/clock/clock.c
# console
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/console-termios-init.c
librtemsbsp_a_SOURCES += ../../../../../../bsps/shared/dev/serial/console-termios.c
diff --git a/cpukit/score/cpu/x86_64/include/rtems/score/cpu_asm.h b/cpukit/score/cpu/x86_64/include/rtems/score/cpu_asm.h
index 09807f1489..4ad50b9f42 100644
--- a/cpukit/score/cpu/x86_64/include/rtems/score/cpu_asm.h
+++ b/cpukit/score/cpu/x86_64/include/rtems/score/cpu_asm.h
@@ -67,6 +67,21 @@ RTEMS_INLINE_ROUTINE void cpuid(
: "a" (code) );
}
+RTEMS_INLINE_ROUTINE uint64_t rdmsr(uint32_t msr)
+{
+ uint32_t low, high;
+ __asm__ volatile ( "rdmsr" :
+ "=a" (low), "=d" (high) :
+ "c" (msr) );
+ return low | (uint64_t) high << 32;
+}
+
+RTEMS_INLINE_ROUTINE void wrmsr(uint32_t msr, uint32_t low, uint32_t high)
+{
+ __asm__ volatile ( "wrmsr" : :
+ "a" (low), "d" (high), "c" (msr) );
+}
+
RTEMS_INLINE_ROUTINE void amd64_enable_interrupts(void)
{
__asm__ volatile ( "sti" );
@@ -76,6 +91,14 @@ RTEMS_INLINE_ROUTINE void amd64_disable_interrupts(void)
{
__asm__ volatile ( "cli" );
}
+
+RTEMS_INLINE_ROUTINE void stub_io_wait(void)
+{
+ /* XXX: This likely won't be required on any modern boards, but this function
+ * exists so it's easier to find all the places it may be used.
+ */
+}
+
#endif /* !ASM */
#endif
--
2.18.0
More information about the devel
mailing list