Handout
This commit is contained in:
		
							
								
								
									
										199
									
								
								arch/lapic.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										199
									
								
								arch/lapic.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,199 @@
 | 
			
		||||
/*! \file
 | 
			
		||||
 *  \brief \ref LAPIC abstracts access to the Local \ref APIC
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
#include "../types.h"
 | 
			
		||||
 | 
			
		||||
/*! \brief Abstracts the local APIC (which is integrated into every CPU core)
 | 
			
		||||
 *  \ingroup interrupts
 | 
			
		||||
 *
 | 
			
		||||
 *  In modern (x86) PCs, every CPU core has its own Local APIC (LAPIC). The
 | 
			
		||||
 * LAPIC is the link between the local CPU core and the I/O APIC (that takes
 | 
			
		||||
 * care about external interrupt sources. Interrupt messages received by the
 | 
			
		||||
 * LAPIC will be passed to the corresponding CPU core and trigger the interrupt
 | 
			
		||||
 * handler on this core.
 | 
			
		||||
 *
 | 
			
		||||
 *  \see [ISDMv3 10.4 Local APIC](intel_manual_vol3.pdf#page=366)
 | 
			
		||||
 */
 | 
			
		||||
namespace LAPIC {
 | 
			
		||||
/*! \brief Initialized the local APIC of the calling CPU core and sets the
 | 
			
		||||
 * logical LAPIC ID in the LDR register
 | 
			
		||||
 *  \param logical_id APIC ID to be set
 | 
			
		||||
 */
 | 
			
		||||
void init(uint8_t logical_id);
 | 
			
		||||
 | 
			
		||||
/*! \brief Signalize EOI (End of interrupt)
 | 
			
		||||
 *
 | 
			
		||||
 *  Signals to the LAPIC that the handling of the current interrupt finished.
 | 
			
		||||
 * This function must be called at the end of interrupt handling before ireting.
 | 
			
		||||
 */
 | 
			
		||||
void endOfInterrupt();
 | 
			
		||||
 | 
			
		||||
/*! \brief Get the ID of the current core's LAPIC
 | 
			
		||||
 *  \return LAPIC ID
 | 
			
		||||
 */
 | 
			
		||||
uint8_t getID();
 | 
			
		||||
 | 
			
		||||
/*! \brief Get the Logical ID of the current core's LAPIC
 | 
			
		||||
 *  \return Logical ID
 | 
			
		||||
 */
 | 
			
		||||
uint8_t getLogicalID();
 | 
			
		||||
 | 
			
		||||
/*! \brief Set the Logical ID of the current core's LAPIC
 | 
			
		||||
 *  \param id new Logical ID
 | 
			
		||||
 */
 | 
			
		||||
void setLogicalID(uint8_t id);
 | 
			
		||||
 | 
			
		||||
/*! \brief Get version number of local APIC
 | 
			
		||||
 *  \return version number
 | 
			
		||||
 */
 | 
			
		||||
uint8_t getVersion();
 | 
			
		||||
 | 
			
		||||
/*! \brief Inter-Processor Interrupts
 | 
			
		||||
 *
 | 
			
		||||
 *  For multi-core systems, the LAPIC enables sending messages (Inter-Processor
 | 
			
		||||
 * Interrupts, IPIs) to other CPU cores and receiving those sent from other
 | 
			
		||||
 * cores.
 | 
			
		||||
 *
 | 
			
		||||
 * \see [ISDMv3 10.6 Issuing Interprocessor
 | 
			
		||||
 * Interrupts](intel_manual_vol3.pdf#page=380)
 | 
			
		||||
 */
 | 
			
		||||
namespace IPI {
 | 
			
		||||
 | 
			
		||||
/*! \brief Check if the previously sent IPI has reached its destination.
 | 
			
		||||
 *
 | 
			
		||||
 *  \return `true` if the previous IPI was accepted from its target processor,
 | 
			
		||||
 * otherwise `false`
 | 
			
		||||
 */
 | 
			
		||||
bool isDelivered();
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an Inter-Processor Interrupt (IPI)
 | 
			
		||||
 * \param destination ID of the target processor (use APIC::getLAPICID(core) )
 | 
			
		||||
 * \param vector      Interrupt vector number to be triggered
 | 
			
		||||
 */
 | 
			
		||||
void send(uint8_t destination, uint8_t vector);
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an Inter-Processor Interrupt (IPI) to a group of processors
 | 
			
		||||
 * \param logical_destination Mask containing the logical APIC IDs of the target
 | 
			
		||||
 * processors (use APIC::getLogicalLAPICID())
 | 
			
		||||
 * \param vector              Interrupt vector number to be triggered
 | 
			
		||||
 */
 | 
			
		||||
void sendGroup(uint8_t logical_destination, uint8_t vector);
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an Inter-Processor Interrupt (IPI) to all processors (including
 | 
			
		||||
 * self)
 | 
			
		||||
 * \param vector Interrupt vector number to be triggered
 | 
			
		||||
 */
 | 
			
		||||
void sendAll(uint8_t vector);
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an Inter-Processor Interrupt (IPI) to all other processors (all
 | 
			
		||||
 * but self)
 | 
			
		||||
 * \param vector Interrupt vector number to be triggered
 | 
			
		||||
 */
 | 
			
		||||
void sendOthers(uint8_t vector);
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an INIT request IPI to all other processors
 | 
			
		||||
 *
 | 
			
		||||
 * \note Only required for startup
 | 
			
		||||
 *
 | 
			
		||||
 * \param assert if `true` send an INIT,
 | 
			
		||||
 *               on `false` send an INIT Level De-assert
 | 
			
		||||
 */
 | 
			
		||||
void sendInit(bool assert = true);
 | 
			
		||||
 | 
			
		||||
/*! \brief Send an Startup IPI to all other processors
 | 
			
		||||
 *
 | 
			
		||||
 * \note Only required for startup
 | 
			
		||||
 *
 | 
			
		||||
 * \param vector Pointer to a startup routine
 | 
			
		||||
 */
 | 
			
		||||
void sendStartup(uint8_t vector);
 | 
			
		||||
 | 
			
		||||
}  // namespace IPI
 | 
			
		||||
 | 
			
		||||
/*! \brief Local Timer (for each LAPIC / CPU)
 | 
			
		||||
 *
 | 
			
		||||
 * \see [ISDMv3 10.5.4 APIC Timer](intel_manual_vol3.pdf#page=378)
 | 
			
		||||
 */
 | 
			
		||||
namespace Timer {
 | 
			
		||||
 | 
			
		||||
/*! \brief Determines the \ref LAPIC::Timer frequency.
 | 
			
		||||
 *
 | 
			
		||||
 * This function will calculate the number of LAPIC-timer ticks passing in the
 | 
			
		||||
 * course of one millisecond. To do so, this function will rely on PIT timer
 | 
			
		||||
 * functionality and measure the tick delta between start and end of waiting for
 | 
			
		||||
 * a predefined period.
 | 
			
		||||
 *
 | 
			
		||||
 * For measurement, the LAPIC-timer single-shot mode (without interrupts) is
 | 
			
		||||
 * used; after measurement, the timer is disabled again.
 | 
			
		||||
 *
 | 
			
		||||
 * \note The timer is counting towards zero.
 | 
			
		||||
 *
 | 
			
		||||
 * \return Number of LAPIC-timer ticks per millisecond
 | 
			
		||||
 *
 | 
			
		||||
 * \todo(15) Implement Method
 | 
			
		||||
 */
 | 
			
		||||
uint32_t ticks(void);
 | 
			
		||||
 | 
			
		||||
/*! \brief Set the \ref LAPIC::Timer.
 | 
			
		||||
 *  \param counter  Initial counter value; decremented on every LAPIC timer tick
 | 
			
		||||
 *  \param divide   Divider (power of 2, i.e., 1 2 4 8 16 32...) used as
 | 
			
		||||
 * prescaler between bus frequency and LAPIC timer frequency: `LAPIC timer
 | 
			
		||||
 * frequency = divide * bus frequency`. `divide` is a numerical parameter, the
 | 
			
		||||
 * conversion to the corresponding bit mask is done internally by calling
 | 
			
		||||
 * getClockDiv().
 | 
			
		||||
 *  \param vector   Interrupt vector number to be triggered on counter expiry
 | 
			
		||||
 *  \param periodic If set, the interrupt will be issued periodically
 | 
			
		||||
 *  \param masked   If set, interrupts on counter expiry are suppressed
 | 
			
		||||
 *
 | 
			
		||||
 *  \todo(15) Implement Method
 | 
			
		||||
 */
 | 
			
		||||
void set(uint32_t counter, uint8_t divide, uint8_t vector, bool periodic,
 | 
			
		||||
         bool masked = false);
 | 
			
		||||
 | 
			
		||||
/*! \brief Setup the \ref LAPIC::Timer.
 | 
			
		||||
 *
 | 
			
		||||
 * Initializes the \ref LAPIC::Timer
 | 
			
		||||
 * in such a way that regular interrupts are triggered approx. every `us`
 | 
			
		||||
 * microseconds when \ref LAPIC::Timer:::activate() is called.
 | 
			
		||||
 * For this purpose, a suitable timer divisor is determined
 | 
			
		||||
 * based on the timer frequency determined with \ref LAPIC::Timer::ticks().
 | 
			
		||||
 * This timer divisor has to be as small as possible, but large enough to
 | 
			
		||||
 * prevent the 32bit counter from overflowing.
 | 
			
		||||
 *
 | 
			
		||||
 * \param us Desired interrupt interval in microseconds.
 | 
			
		||||
 * \return Indicates if the interval could be set.
 | 
			
		||||
 *
 | 
			
		||||
 * \todo(15) Implement Method
 | 
			
		||||
 */
 | 
			
		||||
bool setup(uint32_t us);
 | 
			
		||||
 | 
			
		||||
/*! \brief Retrieve the interrupt interval set during \ref LAPIC::Timer::setup()
 | 
			
		||||
 *
 | 
			
		||||
 *  \return Interval in microseconds
 | 
			
		||||
 *
 | 
			
		||||
 *  \todo(15) Implement method
 | 
			
		||||
 */
 | 
			
		||||
uint32_t interval();
 | 
			
		||||
 | 
			
		||||
/*! \brief Activate the timer on this core.
 | 
			
		||||
 *
 | 
			
		||||
 * The core local timer starts with the interval previously configured in
 | 
			
		||||
 * \ref LAPIC::Timer::setup(). To get timer interrupts on all cores, this method
 | 
			
		||||
 * must be called once per core (however, it is sufficient to call \ref
 | 
			
		||||
 * LAPIC::Timer::setup() only once since the APIC-Bus frequency is the same on
 | 
			
		||||
 * each core).
 | 
			
		||||
 *
 | 
			
		||||
 *  \todo(15) Implement method
 | 
			
		||||
 */
 | 
			
		||||
void activate();
 | 
			
		||||
 | 
			
		||||
/*! \brief Set the LAPIC-timer interrupt mask
 | 
			
		||||
 *  \param masked If set, interrupts are suppressed on counter expiry.
 | 
			
		||||
 *
 | 
			
		||||
 *  \todo(16) Implement for tick-less kernel
 | 
			
		||||
 */
 | 
			
		||||
void setMasked(bool masked);
 | 
			
		||||
}  // namespace Timer
 | 
			
		||||
}  // namespace LAPIC
 | 
			
		||||
		Reference in New Issue
	
	Block a user