Initial commit
This commit is contained in:
49
chap/appendix_figures.tex
Normal file
49
chap/appendix_figures.tex
Normal file
@ -0,0 +1,49 @@
|
||||
\chapter{Figures}
|
||||
\label{ch:figures}
|
||||
|
||||
\clearpage
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{1.0\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/mp_spec_discrete_apic_configuration.svg}
|
||||
\end{subfigure}
|
||||
\caption{The discrete APIC architecture~\cite[sec.~5.1]{mpspec}.}
|
||||
\label{fig:discreteapic}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{1.0\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/mp_spec_integrated_apic_configuration.svg}
|
||||
\end{subfigure}
|
||||
\caption{The integrated APIC architecture~\cite[sec.~5.2]{mpspec}.}
|
||||
\label{fig:integratedapic}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.7\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_manual_system_vs_apic_bus.svg}
|
||||
\end{subfigure}
|
||||
\caption{System vs APIC bus~\cite[sec.~3.11.1]{ia32}.}
|
||||
\label{fig:systemvsapicbus}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{1.0\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_manual_local_apic_blockdiagram.svg}
|
||||
\end{subfigure}
|
||||
\caption{The local APIC block diagram~\cite[sec.~3.11.4.1]{ia32}.}
|
||||
\label{fig:localapicblock}
|
||||
\end{figure}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{1.0\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_lvt.svg}
|
||||
\end{subfigure}
|
||||
\caption{The Local Vector Table~\cite[sec.~3.11.5.1]{ia32}.}
|
||||
\label{fig:localapiclvt}
|
||||
\end{figure}
|
||||
360
chap/appendix_listings.tex
Normal file
360
chap/appendix_listings.tex
Normal file
@ -0,0 +1,360 @@
|
||||
\chapter{Listings}
|
||||
\label{ch:listings}
|
||||
|
||||
This chapter contains concrete implementation examples for concepts mentioned in
|
||||
\autoref{ch:implementation}, demonstrated on the example of hhuOS\@.
|
||||
|
||||
\clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Accessing Local APIC Registers in xApic Mode}
|
||||
\label{sec:apxxapicregacc}
|
||||
|
||||
The xApic register access requires a single page of strong uncachable memory, but since this
|
||||
requires setting attributes in the ``Page Attribute Table''\footnote{See
|
||||
\url{https://docs.kernel.org/x86/pat.html} (visited on 12/02/2023).}, this implementation only uses
|
||||
hhuOS' \code{mapIO} function\cite[MemoryService.cpp]{hhuos}, which maps a physical address to the
|
||||
kernel heap, with hhuOS' ``CACHE\textunderscore{}DISABLE'' flag set in the kernel page table:
|
||||
|
||||
\begin{codeblock}{Allocating the Local APIC's MMIO Region (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_mmio_alloc.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
A register can now be written as follows:
|
||||
|
||||
\begin{codeblock}[label=lst:lapicmmiowrite]{Writing a Local APIC MMIO Register (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_mmio_write.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
To reduce the usage of manual bit-shifting and -masking, this implementation provides structures
|
||||
for some commonly used registers, that implement conversion operators to the register format:
|
||||
|
||||
\begin{codeblock}[label=lst:msrentry]{The MSREntry Structure (LocalApicRegisters.h)}{C++}
|
||||
\cppfile{code/lapic_msr_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:svrentry]{The SVREntry Structure (LocalApicRegisters.h)}{C++}
|
||||
\cppfile{code/lapic_svr_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:lvtentry]{The LVTEntry Structure (LocalApicRegisters.h)}{C++}
|
||||
\cppfile{code/lapic_lvt_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:icrentry]{The ICREntry Structure (LocalApicRegisters.h)}{C++}
|
||||
\cppfile{code/lapic_icr_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
These can be used in combination with some convenience functions:
|
||||
|
||||
\begin{codeblock}{Writing the IA32\textunderscore{}APIC\textunderscore{}BASE MSR (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_write_msr.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}{Writing the SVR (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_write_svr.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}{Writing the LVT (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_write_lvt.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}{Writing the ICR (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_write_icr.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Disabling PIC-Mode}
|
||||
\label{sec:apxdisablepic}
|
||||
|
||||
Setting the IMCR\footnote{Writing the IMCR is detailed in the MultiProcessor
|
||||
specification~\cite[sec. 3.6.2.1]{mpspec}.} using hhuOS' \code{IoPort} class:
|
||||
|
||||
\begin{codeblock}{Disabling PIC-Mode (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_imcr.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Initializing the LVT}
|
||||
\label{sec:apxlvtinit}
|
||||
|
||||
The interrupt vectors are set as defined by the \code{InterruptVector} enum. This implementation
|
||||
configures the LVT by using the \code{LVTEntry} struct (see \autoref{lst:lvtentry}):
|
||||
|
||||
\begin{codeblock}{Configuring the LINT0 Local Interrupt (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_lvt_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
This process is repeated for each local interrupt.
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Handling the Spurious Interrupt}
|
||||
\label{sec:apxsvr}
|
||||
|
||||
This implementation sets the SVR by using the \code{SVREntry} struct (see \autoref{lst:svrentry}):
|
||||
|
||||
\begin{codeblock}{Setting the Spurious Interrupt Vector (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_svr_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Because hhuOS uses a two-stage interrupt handling approach (described in
|
||||
\autoref{sec:currenthhuos}), the spurious interrupt does not receive its own interrupt handler.
|
||||
Instead, it is ignored in the \code{dispatch} function, hhuOS' ``first-stage'' interrupt handler:
|
||||
|
||||
\begin{codeblock}{Checking for Spurious Interrupts (InterruptDispatcher.cpp)}{C++}
|
||||
\cppfile{code/interruptdispatcher_check_spurious.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}{Ignoring Spurious Interrupts (InterruptDispatcher.cpp)}{C++}
|
||||
\cppfile{code/interruptdispatcher_ignore_spurious.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Using the APIC Timer}
|
||||
\label{sec:apxapictimer}
|
||||
|
||||
The timer frequency is determined by counting the ticks in one millisecond, using the PIT as
|
||||
calibration source:
|
||||
|
||||
\begin{codeblock}{Calibrating the APIC Timer (ApicTimer.cpp)}{C++}
|
||||
\cppfile{code/apictimer_calibrate.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
This calibration is performed before the interrupts get enabled, so it is not possible to use
|
||||
hhuOS' \code{TimeService} for the delay. Instead, the \code{PIT::earlyDelay} function is used,
|
||||
which configures the PIT for mode 0 on channel 0 and polls the channel's output status~\cite{pit}].
|
||||
Furthermore, the calibration is only performed once, even if multiple APIC timers are used. This
|
||||
removes the possibility of using multiple timers with different dividers.
|
||||
|
||||
To handle the APIC timer interrupt on multiple cores, \(n\) \code{ApicTimer} instances are
|
||||
registered to the appropriate interrupt vector, where \(n\) is the number of CPUs. Because this
|
||||
means, that each APIC timer interrupt on any CPU core triggers all \(n\) interrupt handlers, the
|
||||
handler has to determine if it belongs to the calling CPU:
|
||||
|
||||
\begin{codeblock}{Handling the APIC Timer Interrupt (ApicTimer.cpp)}{C++}
|
||||
\cppfile{code/apictimer_trigger.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Handling local APIC Errors}
|
||||
\label{sec:apxhandlingerror}
|
||||
|
||||
The error interrupt handler obtains the ESR's contents by writing to it first:
|
||||
|
||||
\begin{codeblock}{The local APIC Error Interrupt Handler (ApicErrorHandler.cpp)}{C++}
|
||||
\cppfile{code/apicerror_trigger.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Because every CPU core can only access its own local APIC's registers, a single instance of this
|
||||
interrupt handler can be used for each AP in the system.
|
||||
|
||||
\clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Accessing I/O APIC Registers}
|
||||
\label{sec:iolistings}
|
||||
|
||||
The I/O APIC's indirect register access requires two MMIO registers, that can be written similar to
|
||||
the local APIC's registers:
|
||||
|
||||
\begin{codeblock}{Writing an I/O APIC MMIO Register (IoApic.h)}{C++}
|
||||
\cppfile{code/ioapic_write_mmio.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Using the ``Index'' and ``Data'' registers to access the I/O APIC's indirect registers:
|
||||
|
||||
\begin{codeblock}{Writing an I/O APIC indirect Register (IoApic.cpp)}{C++}
|
||||
\cppfile{code/ioapic_write_indirect.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
To reduce the manual bit-shifting and -masking, the same approach is used as for the local APIC:
|
||||
|
||||
\begin{codeblock}[label=lst:redtblentry]{The REDTBLEntry Structure (IoApicRegisters.h)}{C++}
|
||||
\cppfile{code/ioapic_redtbl_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:writeredtbl]{Writing the REDTBL (IoApic.cpp)}{C++}
|
||||
\cppfile{code/ioapic_write_redtbl.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{I/O APIC Interrupt Overrides}
|
||||
\label{sec:apxirqoverrides}
|
||||
|
||||
This implementation represents an interrupt override using the following structure:
|
||||
|
||||
\begin{codeblock}{The External Interrupt Override Structure (IoApic.h)}{C++}
|
||||
\cppfile{code/ioapic_irqoverride.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
During initialization, they are used to set the correct interrupt vectors, polarities and trigger
|
||||
modes for each REDTBL entry. The \code{REDTBLEntry} struct (see \autoref{lst:redtblentry}) is used
|
||||
to write the REDTBL:
|
||||
|
||||
\begin{codeblock}{Initializing the REDTBL (IoApic.cpp)}{C++}
|
||||
\cppfile{code/ioapic_redtbl_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
During regular OS operation, they are used to determine the correct REDTBL entries for e.g.\
|
||||
unmasking an interrupt:
|
||||
|
||||
\begin{codeblock}{Unmasking an IRQ (Apic.cpp)}{C++}
|
||||
\cppfile{code/apic_allow.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
To convey this deviation between GSIs and PC/AT compatible IRQs very clearly, the internal
|
||||
\code{IoApic::allow} function only accepts a \code{GlobalSystemInterrupt} as argument, while the
|
||||
public \code{Apic::allow} function accepts an \code{InterruptRequest}, that allows addressing the
|
||||
interrupt by name.
|
||||
|
||||
\clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Issuing Inter-Processor Interrupts}
|
||||
\label{sec:apxipis}
|
||||
|
||||
To issue an IPI, the ICR is written by using the \code{ICREntry} struct (see
|
||||
\autoref{lst:icrentry}):
|
||||
|
||||
\begin{codeblock}{Issuing an INIT IPI (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_initipi_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}{Issuing a SIPI (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_sipi_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Preparing Symmetric Multiprocessing Startup}
|
||||
\label{sec:apxpreparesmp}
|
||||
|
||||
Before executing the ``Universal Startup Algorithm'', the boot code has to be relocated to physical
|
||||
lower memory. The memory region used for this copy has to be identity mapped to the virtual kernel
|
||||
address space, so the effective addresses don't change after enabling paging in protected mode. To
|
||||
keep required variables available to the startup code, these are located in the routines ``TEXT''
|
||||
section and initialized during runtime. This approach was taken from~\cite[APIC.cpp]{serenity}:
|
||||
|
||||
\begin{codeblock}{Preparing the Boot Routine's Variables (ApicSmp.cpp)}{C++}
|
||||
\cppfile{code/ap_boot_variables.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Now, the initialized startup routine can be copied to \code{0x8000}:
|
||||
|
||||
\begin{codeblock}{Relocating the Boot Routine (ApicSmp.cpp)}{C++}
|
||||
\cppfile{code/ap_boot_copy.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
The \code{boot\textunderscore{}ap} function is the entry of the startup routine, it is described
|
||||
further in \autoref{sec:apxapboot}.
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Universal Startup Algorithm}
|
||||
\label{sec:apxmpusa}
|
||||
|
||||
The ``INIT-SIPI-SIPI'' sequence, or ``Universal Startup Algorithm'' is performed by issuing IPIs as
|
||||
described in \autoref{sec:apxipis} and using the PIT as time source (see
|
||||
\autoref{sec:apxapictimer}):
|
||||
|
||||
\begin{codeblock}{The Universal Startup Algorithm (ApicSmp.cpp)}{C++}
|
||||
\cppfile{code/ap_boot_usa.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Boot completion is signaled in the AP's entry function:
|
||||
|
||||
\begin{codeblock}{Signaling AP Boot Completion (smp\textunderscore{}entry.cpp)}{C++}
|
||||
\cppfile{code/smp_entry.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Application Processor Boot Routine}
|
||||
\label{sec:apxapboot}
|
||||
|
||||
Because like the BSP, every AP is initialized in real mode, it has to be switched to protected mode
|
||||
first:
|
||||
|
||||
\begin{codeblock}{Preparing the Switch to Protected Mode (smp\textunderscore{}boot.asm)}{nasm}
|
||||
\nasmfile{code/ap_boot_real_prepare.asm}
|
||||
\end{codeblock}
|
||||
|
||||
Then, by enabling protected mode, setting the segment registers and far-jumping to the 32-bit code
|
||||
segment, the switch is performed:
|
||||
|
||||
\begin{codeblock}{Switching from Real Mode to Protected Mode (smp\textunderscore{}boot.asm)}{nasm}
|
||||
\nasmfile{code/ap_boot_real.asm}
|
||||
\end{codeblock}
|
||||
|
||||
In 32-bit protected mode, paging is enabled and interrupt processing is prepared by reusing the
|
||||
control register values and the IDT from the BSP:
|
||||
|
||||
\begin{codeblock}{Loading Registers from the BSP (smp\textunderscore{}boot.asm)}{nasm}
|
||||
\nasmfile{code/ap_boot_protected_bsp.asm}
|
||||
\end{codeblock}
|
||||
|
||||
Finally, to call the AP entry function, the AP requires its own GDT and stack:
|
||||
|
||||
\begin{codeblock}{Calling the Entry Function (smp\textunderscore{}boot.asm)}{nasm}
|
||||
\nasmfile{code/ap_boot_protected_ap.asm}
|
||||
\end{codeblock}
|
||||
|
||||
The rest of the AP's initialization is performed from the entry function.
|
||||
|
||||
% \clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Application Processor Post-Boot Routine}
|
||||
\label{sec:apxappostboot}
|
||||
|
||||
When the AP has booted, it initializes its own APIC, including the APIC timer and error handler:
|
||||
|
||||
\begin{codeblock}{Initializing the Core's Local APIC (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/ap_boot_post.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Because this added another possible recipient to internal APIC messages, the arbitration IDs are
|
||||
synchronized by issuing an ``INIT-level-deassert IPI'' using the \code{ICREntry} struct (see
|
||||
\autoref{lst:icrentry}):
|
||||
|
||||
\begin{codeblock}{Synchronizing the Arbitration IDs (LocalApic.cpp)}{C++}
|
||||
\cppfile{code/lapic_apr_example.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
Because hhuOS' paging is not designed for multiple processors, the booted AP remains in a busy loop
|
||||
and does not enable its interrupts.
|
||||
|
||||
\clearpage %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
||||
|
||||
\section{Integration into hhuOS}
|
||||
\label{sec:hhuosintegration}
|
||||
|
||||
To integrate the APIC implementation into hhuOS, some preexisting components of its interrupt
|
||||
infrastructure (described in \autoref{sec:currenthhuos}) have to be modified:
|
||||
|
||||
\begin{enumerate}
|
||||
\item The \code{InterruptService} has to forward calls to allow or forbid a hardware interrupt to the
|
||||
\code{Apic} class instead of the \code{Pic} class, depending on if the APIC system is enabled - to
|
||||
keep the option of running the OS on hardware that does not support the APIC (see
|
||||
\autoref{lst:interruptserviceafter}).
|
||||
\item The \code{Pit} interrupt handler may no longer trigger the scheduler preemption if the APIC timer
|
||||
is enabled (see \autoref{lst:pithandlerafter}).
|
||||
\item The \code{System::initializeSystem} function needs to enable the APIC system if it is available
|
||||
(see \autoref{lst:systemafter}).
|
||||
\end{enumerate}
|
||||
|
||||
\begin{codeblock}[label=lst:interruptserviceafter]{Using the correct interrupt controller (InterruptService.cpp)}{C++}
|
||||
\cppfile{code/interruptservice_after.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:pithandlerafter]{Disable preemption if necessary (Pit.cpp)}{C++}
|
||||
\cppfile{code/pit_after.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:systemafter]{Enabling the APIC system (System.cpp)}{C++}
|
||||
\cppfile{code/system_after.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\cleardoublepage
|
||||
440
chap/appendix_tables.tex
Normal file
440
chap/appendix_tables.tex
Normal file
@ -0,0 +1,440 @@
|
||||
\chapter{Tables}
|
||||
\label{ch:tables}
|
||||
|
||||
This section lists all the registers and structures required to follow \autoref{ch:implementation}.
|
||||
|
||||
\clearpage
|
||||
|
||||
\renewcommand{\arraystretch}{1.2}
|
||||
|
||||
\section{Local APIC Registers}
|
||||
\label{sec:localapicregisters}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Register Name} & \textbf{Memory Offset} \\ \hline\hline
|
||||
Local APIC ID Register & 0x20 \\ \hline
|
||||
Local APIC Version Register & 0x30 \\ \hline
|
||||
Task Priority Register & 0x80 \\ \hline
|
||||
EOI Register & 0xB0 \\ \hline
|
||||
Spurious Interrupt Vector Register & 0xF0 \\ \hline
|
||||
Error Status Register & 0x280 \\ \hline
|
||||
Interrupt Command Register[0:31] & 0x300 \\ \hline
|
||||
Interrupt Command Register[32:63] & 0x310 \\ \hline
|
||||
LVT Timer Register & 0x320 \\ \hline
|
||||
LVT LINT1 Register & 0x360 \\ \hline
|
||||
LVT Error Register & 0x370 \\ \hline
|
||||
Timer Initial Count Register & 0x380 \\ \hline
|
||||
Timer Divide Configuration Register & 0x3E0 \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Local APIC Registers used in this Implementation~\cite[sec.~3.11.4.1]{ia32}.}
|
||||
\label{tab:lapicregs}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:23 & Reserved \\ \hline
|
||||
24:31 & Local APIC ID \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Local APIC ID Register (xApic since Pentium 4)~\cite[sec.~3.11.4.6]{ia32}.}
|
||||
\label{tab:lapicregsid}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Local APIC Version \\ \hline
|
||||
8:15 & Reserved \\ \hline
|
||||
16:23 & Max LVT Entry \\ \hline
|
||||
24 & EOI Broadcast Suppression Support \\ \hline
|
||||
25:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Local APIC Version Register~\cite[sec.~3.11.4.8]{ia32}.}
|
||||
\label{tab:lapicregsver}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:3 & Task-Priority Subclass \\ \hline
|
||||
4:7 & Task-Priority Class \\ \hline
|
||||
8:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Task Priority Register~\cite[sec.~3.11.8.3.1]{ia32}.}
|
||||
\label{tab:lapicregstpr}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:31 & Send EOI Signal \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Local APIC EOI Register~\cite[sec.~3.11.8.5]{ia32}.}
|
||||
\label{tab:lapicregseoi}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Spurious Interrupt Vector \\ \hline
|
||||
8 & APIC Software Enable/Disable \\ \hline
|
||||
9 & Focus Processor Checking \\ \hline
|
||||
10:11 & Reserved \\ \hline
|
||||
12 & EOI Broadcast Suppression \\ \hline
|
||||
13:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Spurious Interrupt Vector Register~\cite[sec.~3.11.9]{ia32}.}
|
||||
\label{tab:lapicregssvr}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:4 & Reserved \\ \hline
|
||||
5 & Send Illegal Vector \\ \hline
|
||||
6 & Receive Illegal Vector \\ \hline
|
||||
7 & Illegal Register Access \\ \hline
|
||||
8:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Error Status Register (Pentium 4)~\cite[sec.~3.11.5.3]{ia32}.}
|
||||
\label{tab:lapicregsesr}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Interrupt Vector \\ \hline
|
||||
8:10 & Delivery Mode \\ \hline
|
||||
11 & Destination Mode \\ \hline
|
||||
12 & Delivery Status \\ \hline
|
||||
13 & Reserved \\ \hline
|
||||
14 & Level \\ \hline
|
||||
15 & Trigger Mode \\ \hline
|
||||
16:17 & Reserved \\ \hline
|
||||
18:19 & Destination Shorthand \\ \hline
|
||||
20:55 & Reserved \\ \hline
|
||||
56:63 & Destination Field \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Interrupt Command Register~\cite[sec.~3.11.6.1]{ia32}.}
|
||||
\label{tab:lapicregsicr}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Interrupt Vector \\ \hline
|
||||
8:11 & Reserved \\ \hline
|
||||
12 & Delivery Status \\ \hline
|
||||
13:15 & Reserved \\ \hline
|
||||
16 & Masked \\ \hline
|
||||
17:18 & Timer Mode \\ \hline
|
||||
19:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{LVT Timer Register~\cite[sec.~3.11.5.1]{ia32}.}
|
||||
\label{tab:lapicregslvtt}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Interrupt Vector \\ \hline
|
||||
8:11 & Reserved \\ \hline
|
||||
12 & Delivery Status \\ \hline
|
||||
13:15 & Reserved \\ \hline
|
||||
16 & Masked \\ \hline
|
||||
17:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{LVT Error Register~\cite[sec.~3.11.5.1]{ia32}.}
|
||||
\label{tab:lapicregslvterr}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Interrupt Vector \\ \hline
|
||||
8:10 & Delivery Mode \\ \hline
|
||||
11 & Reserved \\ \hline
|
||||
12 & Delivery Status \\ \hline
|
||||
13 & Pin Polarity \\ \hline
|
||||
14 & Remote IRR \\ \hline
|
||||
15 & Pin Polarity \\ \hline
|
||||
16 & Masked \\ \hline
|
||||
17:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{LVT LINT1 Register~\cite[sec.~3.11.5.1]{ia32}.}
|
||||
\label{tab:lapicregslvtlint}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:31 & Initial Count \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Timer Initial Count Register~\cite[sec.~3.11.5.4]{ia32}.}
|
||||
\label{tab:lapicregstimerinit}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & Divider \\ \hline
|
||||
2 & Reserved \\ \hline
|
||||
3 & Divider \\ \hline
|
||||
4:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Timer Divide Configuration Register~\cite[sec.~3.11.5.4]{ia32}.}
|
||||
\label{tab:lapicregstimerdiv}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Reserved \\ \hline
|
||||
8 & BSP Flag \\ \hline
|
||||
9 & Reserved \\ \hline
|
||||
10 & Enable x2Apic \\ \hline
|
||||
11 & Enable xApic \\ \hline
|
||||
12:35 & APIC Base Address \\ \hline
|
||||
36:63 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{IA32\textunderscore{}APIC\textunderscore{}BASE MSR~\cite[sec.~3.11.12.1]{ia32}.}
|
||||
\label{tab:lapicregsmsr}
|
||||
\end{table}
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{I/O APIC Registers}
|
||||
\label{sec:ioapicregs}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Register Name} & \textbf{Memory Offset} \\ \hline\hline
|
||||
Index Register & 0x00 \\ \hline
|
||||
Data Register & 0x10 \\ \hline\hline
|
||||
\textbf{Register Name} & \textbf{Index Offset} \\ \hline\hline
|
||||
I/O APIC ID Register & 0x00 \\ \hline
|
||||
I/O APIC Version Register & 0x01 \\ \hline
|
||||
Redirection Table & 0x10:0x3F \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC Registers used in this Implementation~\cite[sec.~9.5]{ich5}.}
|
||||
\label{tab:ioapicregs}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Indirect Register Index \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC Index Register~\cite[sec.~9.5.2]{ich5}.}
|
||||
\label{tab:ioapicregsidx}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:31 & Indirect Register Data \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC Data Register~\cite[sec.~9.5.3]{ich5}.}
|
||||
\label{tab:ioapicregsdat}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:14 & Reserved \\ \hline
|
||||
15 & Scratchpad Bit \\ \hline
|
||||
16:23 & Reserved \\ \hline
|
||||
24:27 & I/O APIC ID \\ \hline
|
||||
28:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC ID Register~\cite[sec.~9.5.6]{ich5}.}
|
||||
\label{tab:ioapicregsid}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & I/O APIC Version \\ \hline
|
||||
8:14 & Reserved \\ \hline
|
||||
15 & PRQ \\ \hline
|
||||
16:23 & Maximum Redirection Entries \\ \hline
|
||||
24:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC Version Register~\cite[sec.~9.5.7]{ich5}.}
|
||||
\label{tab:ioapicregsver}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:7 & Interrupt Vector \\ \hline
|
||||
8:10 & Delivery Mode \\ \hline
|
||||
11 & Destination Mode \\ \hline
|
||||
12 & Delivery Status \\ \hline
|
||||
13 & Pin Polarity \\ \hline
|
||||
14 & Remote IRR \\ \hline
|
||||
15 & Trigger Mode \\ \hline
|
||||
16 & Masked \\ \hline
|
||||
17:47 & Reserved \\ \hline
|
||||
48:55 & Extended Destination ID \\ \hline
|
||||
56:63 & Destination \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{I/O APIC REDTBL Register~\cite[sec.~9.5.8]{ich5}.}
|
||||
\label{tab:ioapicregsredtbl}
|
||||
\end{table}
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{System Description Tables}
|
||||
\label{sec:sdts}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:35 & MADT Header \\ \hline
|
||||
36:39 & Local APIC Base Address \\ \hline
|
||||
40:43 & Local APIC Flags \\ \hline
|
||||
44: & List of APIC Structures \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{ACPI MADT~\cite[sec.~5.2.8]{acpi1}.}
|
||||
\label{tab:madt}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & APIC Structure Header \\ \hline
|
||||
2 & ACPI Processor ID \\ \hline
|
||||
3 & APIC ID \\ \hline
|
||||
4:7 & Local APIC Flags (see \autoref{tab:madtlapicflags}) \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{MADT Processor Local APIC Structure~\cite[sec.~5.2.8.1]{acpi1}.}
|
||||
\label{tab:madtlapic}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0 & Enabled \\ \hline
|
||||
1:31 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Local APIC Flags~\cite[sec.~5.2.8.1]{acpi1}.}
|
||||
\label{tab:madtlapicflags}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & APIC Structure Header \\ \hline
|
||||
2 & I/O APIC ID \\ \hline
|
||||
3 & Reserved \\ \hline
|
||||
4:7 & I/O APIC Base Address \\ \hline
|
||||
8:11 & I/O APIC GSI Base \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{MADT I/O APIC Structure~\cite[sec.~5.2.8.2]{acpi1}.}
|
||||
\label{tab:madtioapic}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & APIC Structure Header \\ \hline
|
||||
2 & Bus \\ \hline
|
||||
3 & Source \\ \hline
|
||||
4:7 & GSI \\ \hline
|
||||
8:9 & Interrupt Input Flags (see \autoref{tab:madtintiflags}) \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{MADT Interrupt Source Override Structure~\cite[sec.~5.2.8.3.1]{acpi1}.}
|
||||
\label{tab:madtirqoverride}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Bit Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & Pin Polarity \\ \hline
|
||||
2:3 & Trigger Mode \\ \hline
|
||||
4:11 & Reserved \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{Interrupt Input Flags~\cite[sec.~5.2.8.3.1]{acpi1}.}
|
||||
\label{tab:madtintiflags}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & APIC Structure Header \\ \hline
|
||||
2:3 & Interrupt Input Flags (see \autoref{tab:madtintiflags}) \\ \hline
|
||||
4:7 & GSI
|
||||
\end{tabularx}
|
||||
\caption{MADT I/O APIC NMI Source~\cite[sec.~5.2.8.3.2]{acpi1}.}
|
||||
\label{tab:madtionmi}
|
||||
\end{table}
|
||||
|
||||
\begin{table}[H]
|
||||
\centering
|
||||
\begin{tabularx}{1.0\textwidth}{| X | X |}
|
||||
\hline
|
||||
\textbf{Byte Number} & \textbf{Description} \\ \hline\hline
|
||||
0:1 & APIC Structure Header \\ \hline
|
||||
2 & ACPI Processor ID \\ \hline
|
||||
3:4 & Interrupt Input Flags (see \autoref{tab:madtintiflags}) \\ \hline
|
||||
5 & Local APIC Interrupt Input \\ \hline
|
||||
\end{tabularx}
|
||||
\caption{MADT Local APIC NMI Source~\cite[sec.~5.2.8.3.3]{acpi1}.}
|
||||
\label{tab:madtlnmi}
|
||||
\end{table}
|
||||
427
chap/background.tex
Normal file
427
chap/background.tex
Normal file
@ -0,0 +1,427 @@
|
||||
\chapter{Background}
|
||||
\label{ch:background}
|
||||
|
||||
In this section, important domain specific concepts will be explained, to create the necessary
|
||||
foundation to follow \autoref{ch:implementation}. Important terms that are present in the glossary
|
||||
are marked in \textbf{bold} on their first occurrence.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Handling of External Events}
|
||||
\label{sec:eventhandling}
|
||||
|
||||
There are two different strategies to ``listen'' for external events: ``Polling'' and
|
||||
``Interrupts''.
|
||||
|
||||
The first strategy works by periodically \textit{polling} a device to check for changes. This could
|
||||
mean reading a register of the keyboard every 50 ms to determine if a key was pressed, or reading a
|
||||
sensor output even when the value remains unchanged. Because every device would have to be polled
|
||||
constantly, no matter if any change actually occurred, this method is computationally inefficient
|
||||
(although easy to implement without extra hardware).
|
||||
|
||||
The second strategy are \textbf{\glspl{interrupt}}. Instead of the CPU actively ``looking'' for
|
||||
changes, the devices signal the events to the CPU themselves. Every time the CPU is notified of an
|
||||
external event, it pauses the current code execution and calls a function designated to handle this
|
||||
specific event.
|
||||
|
||||
This approach is much more efficient than the polling strategy, because the CPU does not have to
|
||||
waste its processing power to check for events when none are occurring.
|
||||
|
||||
\section{Fundamental Concepts}
|
||||
\label{sec:fundamentals}
|
||||
|
||||
\subsection{Interrupt}
|
||||
\label{subsec:interrupt}
|
||||
|
||||
When a device signals an external event to the CPU, the current code execution gets
|
||||
\textit{interrupted}, to handle the event. Thus, the process of signaling and handling an external
|
||||
event is called ``interrupt''.
|
||||
|
||||
Interrupts can be caused by all sorts of events, like key presses on a keyboard or packets received
|
||||
via a network card. These interrupts from external hardware devices are called
|
||||
\textbf{\glspl{external interrupt}}.
|
||||
|
||||
Other types of interrupts mentioned in this thesis are \textbf{\glspl{ipi}}, \textbf{\glspl{msi}}
|
||||
and \textbf{\glspl{local interrupt}}:
|
||||
|
||||
\begin{itemize}
|
||||
\item IPIs: Interrupts sent between different processor cores in multiprocessor systems, for example to
|
||||
initialize different cores on system startup.
|
||||
\item MSIs: Interrupts sent in-band\footnote{In in-band signaling some control information (like an IRQ)
|
||||
is sent over the same channel as the data. This stands in contrast to out-of-band signaling, which
|
||||
uses a dedicated control line (like an interrupt line).}, for example over a PCI-bus\footnote{PCI
|
||||
supports MSIs since PCI 2.2~\cite[sec.~6.8]{pci22}.}.
|
||||
\item Local Interrupts: Some specific CPU-internal interrupts.
|
||||
\end{itemize}
|
||||
|
||||
Some interrupts are essential for continuous CPU operation. These \textbf{\glspl{nmi}} always have
|
||||
to be handled and cannot be ignored (usually\footnote{An example where NMIs should be disabled is
|
||||
the startup of additional processors in multiprocessor systems.}). Hardware error interrupts are a
|
||||
typical example for NMIs.
|
||||
|
||||
The existence of NMIs hints that it is possible to ignore regular interrupts. Marking an interrupt
|
||||
as ``ignored'' is called \textbf{\gls{masking}} an interrupt.
|
||||
|
||||
\subsection{Interrupt Controller}
|
||||
\label{subsec:controller}
|
||||
|
||||
A computer system has to process interrupts from many sources, so it is practical to have a
|
||||
designated \textbf{\gls{interrupt controller}}, that receives interrupts from different devices and
|
||||
forwards them to the CPU. The masking functionality is integrated into the interrupt controller, so
|
||||
masked interrupts don't reach the CPU at all.
|
||||
|
||||
The interrupt controller receives interrupts through signals over physical connections (interrupt
|
||||
lines) to different devices. These signals can be represented in multiple ways through the level on
|
||||
the interrupt lines:
|
||||
|
||||
\textbf{\Gls{trigger mode}}:
|
||||
|
||||
\begin{itemize}
|
||||
\item Edge-triggered signal: A signal is received when the interrupt line level changes
|
||||
\item Level-triggered signal: A signal is received when the interrupt line reaches a certain level
|
||||
\end{itemize}
|
||||
|
||||
\textbf{\Gls{pin polarity}}:
|
||||
|
||||
\begin{itemize}
|
||||
\item High: A signal is received when the interrupt line level changes from either low to high (in
|
||||
edge-triggered mode) or reaches a level above a threshold (in level-triggered mode)
|
||||
\item Low: A signal is received when the interrupt line level changes from either high to low (in
|
||||
edge-triggered mode) or reaches a level below a threshold (in level-triggered mode)
|
||||
\end{itemize}
|
||||
|
||||
A signal is represented through a combination of trigger mode and pin polarity, so there are a
|
||||
total of 4 combinations.
|
||||
|
||||
When the interrupt controller receives an interrupt signal, it requests the CPU to handle the
|
||||
specific event: The interrupt controller sends an \textbf{\gls{irq}} to the CPU. Because there are
|
||||
multiple devices that can signal interrupts, an IRQ is usually indexed by its hardware pin on the
|
||||
interrupt controller: If a keyboard is connected to the interrupt controller on pin 1, the CPU
|
||||
receives an IRQ1.
|
||||
|
||||
To signal that an interrupt has been handled by the software, the interrupt controller receives an
|
||||
\textbf{\gls{eoi}} notice from the OS\@.
|
||||
|
||||
Information on specific interrupt controllers follows in \autoref{sec:intelcontrollers}.
|
||||
|
||||
\subsection{Interrupt Handler}
|
||||
\label{subsec:handler}
|
||||
|
||||
When the CPU receives an IRQ, it pauses its current code execution to handle the interrupt. This is
|
||||
done by executing the \textbf{\gls{interrupt handler}} function, that is registered to the specific
|
||||
interrupt.
|
||||
|
||||
During interrupt servicing (execution of the interrupt handler), other interrupts can occur. When
|
||||
the execution of an interrupt handler is paused to handle another interrupt, this is called a
|
||||
\textbf{\gls{cascaded interrupt}}.
|
||||
|
||||
Interrupt handlers have to be registered to different IRQs, so the CPU can locate and execute the
|
||||
specific code that is designated to handle a certain interrupt. The interrupt handler addresses are
|
||||
stored in an \textit{interrupt vector table}: Each address corresponds to an \textbf{\gls{interrupt
|
||||
vector}} number, interrupt vectors 0 to 31 are reserved for the CPU, interrupt vectors 32 to 255
|
||||
are usable for IRQs. In Intel's 32 bit IA-32 CPU architecture, this table is called the
|
||||
\textbf{\gls{idt}}. The \textbf{\gls{idtr}} keeps the location of the IDT, it is written by the
|
||||
\code{lidt}~\cite{x86isa} instruction.
|
||||
|
||||
\subsection{Interrupt Trigger Mode}
|
||||
\label{subsec:triggermode}
|
||||
|
||||
When an edge-triggered IRQ is received, its interrupt handler is called a single time, serviced,
|
||||
and marked as completed in the interrupt controller (completion does not require interacting with
|
||||
the device the interrupt originated from). The handler of an edge-triggered IRQ is called every
|
||||
time the interrupt line changes its level. This could lead to problems if ``glitches'' occur on the
|
||||
interrupt line.
|
||||
|
||||
An alternative is the level-triggered IRQ: When the interrupt line is above/below a certain level,
|
||||
the interrupt is signaled continuously. Servicing the interrupt thus requires not only signaling
|
||||
completion to the interrupt controller, but also to the device the interrupt originated from, to
|
||||
de-assert the interrupt line. Otherwise, the interrupt handler would be called again after
|
||||
completion.
|
||||
|
||||
\subsection{Spurious Interrupt}
|
||||
\label{subsec:spurious}
|
||||
|
||||
When an interrupt disappears while the interrupt controller is issuing the IRQ to the CPU, the
|
||||
interrupt controller sends a \textbf{\gls{spurious interrupt}}. The reason for this could be
|
||||
electrical noise on the interrupt line, masking of an interrupt through software at the same moment
|
||||
this interrupt was received, or incorrectly sent EOIs. Thus, before an interrupt handler is called,
|
||||
the OS has to check the validity of the occurred interrupt and ignore it, if it is deemed spurious.
|
||||
|
||||
\section{Used Technologies}
|
||||
\label{sec:technologies}
|
||||
|
||||
\subsection{Advanced Configuration and Power Interface}
|
||||
\label{subsec:acpi}
|
||||
|
||||
\textbf{\gls{acpi}} allows the kernel to gather information about the system hardware during
|
||||
runtime. It also provides interactive functionality for power management, plug and play,
|
||||
hot-swapping or status monitoring. To interact with ACPI, the
|
||||
\textbf{\gls{aml}}~\cite[sec.~16]{acpi1}, a small language interpreted by the kernel, has to be
|
||||
used\footnote{In this thesis, information from ACPI is only read, so AML is not required.}.
|
||||
|
||||
ACPI defines abstractions for different types of hardware, that are organized in multiple
|
||||
\textit{system description tables}. In this thesis, ACPI 1.0~\cite{acpi1} is used to read
|
||||
information about the system's interrupt hardware configuration, located in the ACPI
|
||||
\textbf{\gls{madt}}~\cite[sec.~5.2.8]{acpi1}. The MADT contains information on used interrupt
|
||||
controllers (version, physical memory addresses to access registers, etc.), available CPU cores in
|
||||
multiprocessor systems and specific interrupt configuration (trigger mode and pin polarity).
|
||||
|
||||
To allow compatibility to different interrupt controllers, ACPI abstracts external interrupts
|
||||
through \textbf{\glspl{gsi}}~\cite[sec.~5.2.9]{acpi1}. Different interrupt controllers may have the
|
||||
same devices connected to different interrupt lines, maintaining a mapping between actual hardware
|
||||
interrupt lines and GSIs allows the OS to operate on different interrupt
|
||||
controllers\footnote{Additional information in \autoref{subsec:ioapicpcat}.}.
|
||||
|
||||
\subsection{CPU Identification}
|
||||
\label{subsec:cpuid}
|
||||
|
||||
\textbf{\gls{cpuid}} is used to gather information about a system's CPU. The x86 architecture
|
||||
provides the \code{cpuid}~\cite{x86isa} instruction, which allows the software to identify present
|
||||
CPU features at runtime, e.g.\ what instruction set extensions a processor implements\footnote{This
|
||||
thesis uses CPUID to determine what APIC feature set is supported, see ``APIC'' and ``x2APIC''
|
||||
features in the CPUID application note~\cite[sec.~5.1.2]{cpuid}.}.
|
||||
|
||||
\subsection{Symmetric Multiprocessing}
|
||||
\label{subsec:smp}
|
||||
|
||||
\textbf{\gls{smp}} is a form of multiprocessing, where a system consists of multiple, homogeneous
|
||||
CPU cores, that all have access to a shared main memory and the I/O devices. SMP systems are
|
||||
controlled by a single OS, that treats all cores as individual, but equal processors. The shared
|
||||
main memory allows every core to work on an arbitrary task, independent of its memory location.
|
||||
Programming for SMP systems requires the usage of multithreading, to allow for an efficient
|
||||
distribution of tasks to multiple CPU cores.
|
||||
|
||||
In SMP systems, a single CPU core is active initially, this core is called the \textbf{\gls{bsp}}.
|
||||
Other cores, called \textbf{\glspl{ap}}, will be initialized by this core.
|
||||
|
||||
% TODO: I think I could remove this section
|
||||
% \subsection{BIOS and UEFI}
|
||||
% \label{subsec:biosuefi}
|
||||
%
|
||||
% The \textbf{\gls{bios}} provides low-level control over a computer system's hardware, most commonly
|
||||
% used for booting an OS on system power-up. Its pre-boot environment is limited to the 16-bit
|
||||
% \textbf{\gls{real mode}} and 1 MB of addressable memory. BIOS originates from the IBM PC and has
|
||||
% been the default convention for computer system firmware until the \textbf{\gls{uefi}} standard.
|
||||
%
|
||||
% The UEFI standard is developed by the UEFI Forum, based on Intel's EFI. It improves on BIOS by
|
||||
% providing modularity, support for larger partitions, 32-bit or 64-bit environments before booting
|
||||
% and advanced features like networking capabilities, graphical user interfaces with mouse support or
|
||||
% security features\footnote{See \url{https://uefi.org/specifications} (visited on 22/02/2023).}.
|
||||
|
||||
\subsection{Model Specific Register}
|
||||
\label{subsec:msr}
|
||||
|
||||
A \textbf{\gls{msr}} is a special control register in an IA-32 CPU, that is not guaranteed to be
|
||||
present in future CPU generations (it is not part of the architectural standard). MSRs that carried
|
||||
over to future generations and are now guaranteed to be included in future IA-32 or Intel 64 CPUs
|
||||
are called ``architectural'' MSRs. To interact with MSRs, the special instructions \code{rdmsr} and
|
||||
\code{wrmsr} are used~\cite{x86isa}. These instructions allow atomic read/write operations on
|
||||
MSRs\footnote{Detailed description in the IA-32 manual~\cite[sec.~4.2]{ia32}.}.
|
||||
|
||||
\subsection{Memory Mapped I/O}
|
||||
\label{subsec:mmio}
|
||||
|
||||
\textbf{\gls{mmio}} is a way for the CPU to perform I/O operations with an external device.
|
||||
Registers of external devices are mapped to the same address space as the main memory, thus a
|
||||
memory address can either be a location in physical memory, or belong to a device's register. Read
|
||||
and write operations are then performed by reading or writing the address the register is mapped
|
||||
to.
|
||||
|
||||
\subsection{Programmable Interval Timer}
|
||||
\label{subsec:pit}
|
||||
|
||||
The \textbf{\gls{pit}}\footnote{Specifically the Intel 8253/8254.} is a hardware timer from the
|
||||
original IBM PC. It consists of multiple decrementing counters and can generate a signal (for
|
||||
example an interrupt) once 0 is reached. This can be used to control a preemptive\footnote{A
|
||||
scheduler is called ``preemptive'', when it is able to forcefully take away the CPU from the
|
||||
currently working thread, even when the thread did not signal any form of completion.} scheduler or
|
||||
the integrated PC speaker.
|
||||
|
||||
\section{Intel's Interrupt Controllers}
|
||||
\label{sec:intelcontrollers}
|
||||
|
||||
Because it is logistically difficult to connect every external device directly to the CPU, this
|
||||
task is delegated to an interrupt controller, usually located in the system's chipset. Notable
|
||||
interrupt controllers (and the only ones considered in this thesis) are the Intel
|
||||
\textbf{\gls{pic}} and Intel \textbf{\gls{apic}}.
|
||||
|
||||
\subsection{Programmable Interrupt Controller}
|
||||
\label{subsec:intelpic}
|
||||
|
||||
The Intel 8259A PIC~\cite{pic} is the interrupt controller Intel used in the original Intel
|
||||
8086\footnote{The 8259 was used in the Intel 8080 and 8085, the 8259A is a revised version for the
|
||||
Intel 8086}, the ``father'' of the x86 processor architecture. A single PIC has 8 interrupt lines
|
||||
for external devices, but multiple PICs can be cascaded for a maximum of \(8 \cdot 8 = 64\)
|
||||
interrupt lines with 9 PICs. The most common PIC configuration supports 15 IRQs with two PICs: A
|
||||
slave, connected to interrupt line 2 of the master, which is connected to the CPU\footnote{This
|
||||
configuration is called the \textbf{\gls{pcat pic architecture}}, as it was used in the IBM
|
||||
PC/AT~\cite[sec.~1.13]{pcat}.}.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.3\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/mp_spec_pic_configuration.svg}
|
||||
\end{subfigure}
|
||||
\caption{The PC/AT PIC architecture~\cite[sec.~1.13]{pcat}.}
|
||||
\label{fig:pcatpic}
|
||||
\end{figure}
|
||||
|
||||
If multiple interrupts get requested simultaneously, the PIC decides which one to service first
|
||||
based on its \textbf{\gls{interrupt priority}}. The PIC IRQ priorities are determined by the used
|
||||
interrupt lines, IRQ0 has the highest priority.
|
||||
|
||||
The PIC interrupt handling sequence can be briefly outlined as follows:
|
||||
|
||||
\begin{enumerate}
|
||||
\item When the PIC receives an interrupt from a device (PIC interrupts are edge-triggered with high pin
|
||||
polarity), it sets the corresponding bit in its \textbf{\gls{irr}}\footnote{Received IRQs that are
|
||||
requesting servicing are marked in the IRR.}
|
||||
\item If this interrupt is unmasked, an IRQ is sent to the CPU
|
||||
\item The CPU acknowledges the IRQ
|
||||
\item The PIC sets the corresponding bit in its \textbf{\gls{isr}}\footnote{IRQs that are being serviced
|
||||
are marked in the ISR. To reduce confusion, interrupt service routines will be denoted as
|
||||
``interrupt handler'' instead of ``ISR''.} and clears the previously set bit in the IRR. If the
|
||||
same interrupt occurs again, it can now be queued a single time in the IRR\@.
|
||||
\item When the interrupt has been handled by the OS, it sends an EOI to the PIC, which clears the
|
||||
corresponding bit in the ISR\@.
|
||||
\end{enumerate}
|
||||
|
||||
With the introduction of multiprocessor systems and devices with an ever-increasing need for large
|
||||
amounts of interrupts (like high-performance networking hardware), the PIC reached its
|
||||
architectural limits:
|
||||
|
||||
\begin{itemize}
|
||||
\item Multiple CPU cores require sending IPIs for initialization and coordination, which is not possible
|
||||
using the PIC\@.
|
||||
\item The PIC is hardwired to a single CPU core. It can be inefficient to handle the entire interrupt
|
||||
workload on a single core.
|
||||
\item The IRQ priorities depend on how the devices are physically wired to the PIC\@.
|
||||
\item The PC/AT PIC architecture only has a very limited number of interrupt lines.
|
||||
\item The PIC does not support MSIs, which allow efficient handling of PCI interrupts.
|
||||
\end{itemize}
|
||||
|
||||
\subsection{Advanced Programmable Interrupt Controller}
|
||||
\label{subsec:intelapic}
|
||||
|
||||
The original Intel 82489DX discrete APIC was introduced with the Intel i486 processor. It consisted
|
||||
of two parts: A \textbf{\gls{local apic}}, responsible for receiving special \textbf{\glspl{local
|
||||
interrupt}}, and external interrupts from the second part, the \textbf{\gls{io apic}}. Unlike in
|
||||
modern systems, the i486's local APIC was not integrated into the CPU core (hence ``discrete''),
|
||||
see \autoref{fig:discreteapic}.
|
||||
|
||||
The i486 was superseded by the Intel P54C\footnote{The Intel P54C is the successor of the Intel P5,
|
||||
the first Pentium processor.}, which contained an integrated local APIC for the first time, see
|
||||
\autoref{fig:integratedapic}\footnote{This is now the default configuration.}.
|
||||
|
||||
The APIC was designed to fix the shortcomings of the PIC, specifically regarding multiprocessor
|
||||
systems:
|
||||
|
||||
\begin{itemize}
|
||||
\item Each CPU core contains its own local APIC. It has the capabilities to issue IPIs and communicate
|
||||
with the chipset I/O APIC. Also, it includes the APIC timer, which can be used for per-core
|
||||
scheduling.
|
||||
\item The chipset I/O APIC allows configuration on a per-interrupt basis (priorities, destination,
|
||||
trigger mode or pin polarity are all configurable). Also, it is able to send interrupts to
|
||||
different CPU cores, allowing the efficient processing of large amounts of
|
||||
interrupts\footnote{There are tools that dynamically reprogram the I/O APIC to distribute
|
||||
interrupts to available CPU cores, depending on heuristics of past interrupts (IRQ-balancing).}.
|
||||
\item The order in which external devices are connected to the I/O APIC is flexible.
|
||||
\item The APIC architecture supports MSIs.
|
||||
\end{itemize}
|
||||
|
||||
To allow distributing the APIC hardware between local APICs of multiple CPU cores and I/O APICs,
|
||||
the APIC system sends messages over a bus. In the Intel P6 and original Pentium processors a
|
||||
dedicated APIC bus is used, but since the Intel Pentium 4 and Xeon processors the chipset's system
|
||||
bus is used instead (see \autoref{fig:systemvsapicbus})\footnote{The Intel Core microarchitecture
|
||||
is based on the P6 architecture (replacing the Pentium 4's NetBurst architecture), but still uses
|
||||
the system bus instead of a discrete bus.}. Using the system bus is considered the default in this
|
||||
document.
|
||||
|
||||
Because the order in which external devices are connected to the interrupt controller is flexible
|
||||
in the APIC architecture, but fixed in the PC/AT PIC architecture, there can be deviations in
|
||||
device-to-IRQ mappings between the PIC and APIC interrupt controllers. To handle this, ACPI
|
||||
provides information about how the PIC compatible interrupts (IRQ0 to IRQ15) map to ACPI's GSIs in
|
||||
the MADT, which has to be taken into account by the os when operating with an APIC. The mapping
|
||||
information for a single interrupt will be called \textbf{\gls{irq override}}.
|
||||
|
||||
There have been different revisions on the APIC architecture after the original, discrete APIC,
|
||||
notably the \textbf{\gls{xapic}} and \textbf{\gls{x2apic}} architectures. This thesis is mostly
|
||||
concerned about the older xApic architecture\footnote{The x2Apic architecture is backwards
|
||||
compatible to the xApic architecture, also, QEMU's TCG does not support the x2Apic architecture.}.
|
||||
A notable difference between xApic and x2Apic architectures is the register access: xApic uses
|
||||
32-bit MMIO based register access, while x2Apic uses an MSR based register access, which allows
|
||||
atomic register access to the 32- and 64-bit wide APIC control MSRs~\cite[sec.~3.11.12]{ia32}.
|
||||
|
||||
\section{PC/AT Compatibility}
|
||||
\label{sec:pcatcompat}
|
||||
|
||||
For compatibility to older computer systems, two cascaded PICs are usually present in current
|
||||
computer systems (see \autoref{fig:discreteapic}/\autoref{fig:integratedapic}). The local APIC can
|
||||
be initialized to operate with these PICs instead of the I/O APIC, this is called
|
||||
\textbf{\gls{virtual wire mode}}. It is possible to operate with both the PIC and I/O APIC as
|
||||
controllers for external interrupts in ``mixed mode'', but this is very uncommon. Because the
|
||||
presence of a local APIC usually guarantees the presence of an I/O APIC, this thesis only focuses
|
||||
on interrupt handling with either two PICs (\textbf{\gls{pic mode}}), in case no APIC is available,
|
||||
or the full APIC architecture (local APIC and I/O APIC in \textbf{\gls{symmetric io
|
||||
mode}})\footnote{For more information on operating modes, consult the MultiProcessor
|
||||
specification~\cite[sec.~3.6.2.1]{mpspec}.}.
|
||||
|
||||
To switch from PIC to symmetric I/O mode, some\footnote{References to the IMCR are only found in
|
||||
the relatively old MultiProcessor specification~\cite{mpspec}.} hardware\footnote{QEMU does not
|
||||
emulate the IMCR.} provides the \textbf{\gls{imcr}}, a special register that controls the physical
|
||||
connection of the PIC to the CPU. In the original discrete APIC architecture, the IMCR can choose
|
||||
if either the PIC or local APIC is connected to the CPU (see \autoref{fig:discreteapic}), since the
|
||||
xApic architecture the IMCR only connects or disconnects the PIC to the local APIC's LINT0 pin (see
|
||||
\autoref{fig:integratedapic}). When the IMCR is set to connect the PIC, and the xApic ``global
|
||||
enable/disable'' flag is unset (see \autoref{subsec:lapicenable}), the system is functionally
|
||||
equivalent to an IA-32 CPU without an integrated local APIC~\cite[sec.~3.11.4.3]{ia32}.
|
||||
|
||||
Specifics on handling PC/AT compatible external interrupts follow in \autoref{subsec:ioapicpcat}.
|
||||
|
||||
\section{Interrupt Handling in hhuOS}
|
||||
\label{sec:currenthhuos}
|
||||
|
||||
In hhuOS, external interrupts are handled in two steps:
|
||||
|
||||
\begin{enumerate}
|
||||
\item After an IRQ is sent by an interrupt controller, the CPU looks up the interrupt handler address in
|
||||
the IDT. In hhuOS, every IDT entry contains the address of the \code{dispatch} function (see
|
||||
\autoref{lst:irqdispatch}), which is invoked with the vector number of the interrupt.
|
||||
\item The \code{dispatch} function determines which interrupt handler will be called, based on the
|
||||
supplied vector number. In hhuOS, interrupt handlers are implemented through the
|
||||
\code{InterruptHandler} interface (see \autoref{lst:irqhandler}), that provides the \code{trigger}
|
||||
function, which contains the interrupt handling routine. To allow the \code{dispatch} function to
|
||||
find the desired interrupt handler, it has to be registered to a vector number by the OS
|
||||
beforehand. This process is handled by the \code{plugin} function of the interrupt handler
|
||||
interface, which uses the interrupt dispatcher's \code{assign} function (see
|
||||
\autoref{lst:irqassign}) to register itself to the correct vector number. HhuOS supports assigning
|
||||
multiple interrupt handlers to a single interrupt vector and cascading interrupts.
|
||||
\end{enumerate}
|
||||
|
||||
\begin{codeblock}[label=lst:irqhandler]{The InterruptHandler interface (InterruptHandler.h)~\cite{hhuos}}{C++}
|
||||
\cppfile{code/interrupthandler_def.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:irqassign]{Assigning an interrupt handler (InterruptDispatcher.cpp)~\cite{hhuos}}{C++}
|
||||
\cppfile{code/interruptdispatcher_assign.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
\begin{codeblock}[label=lst:irqdispatch]{Triggering an interrupt handler (InterruptDispatcher.cpp)~\cite{hhuos}}{C++}
|
||||
\cppfile{code/interruptdispatcher_dispatch.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
To prevent the need of interacting with a specific interrupt controller implementation (e.g.
|
||||
\code{Pic} class) or the dispatcher, a system service (the \code{InterruptService}) is implemented
|
||||
to expose this functionality to other parts of the OS (it allows e.g.\ registering interrupt
|
||||
handlers or masking and unmasking interrupts).
|
||||
|
||||
Currently, hhuOS utilizes the PIT to manage the global system time (see \autoref{lst:pithandler}),
|
||||
which in turn is used to trigger hhuOS's preemptive round-robin scheduler (the \code{Scheduler}
|
||||
class).
|
||||
|
||||
\begin{codeblock}[label=lst:pithandler]{The PIT interrupt handler (Pit.cpp)~\cite{hhuos}}{C++}
|
||||
\cppfile{code/pit_trigger.cpp}
|
||||
\end{codeblock}
|
||||
|
||||
The PIT and other devices are initialized before the system entry point, in the
|
||||
\code{System::initializeSystem} function.
|
||||
107
chap/conclusion.tex
Normal file
107
chap/conclusion.tex
Normal file
@ -0,0 +1,107 @@
|
||||
\chapter{Conclusion}
|
||||
\label{ch:conclusion}
|
||||
|
||||
In this thesis, support for the APIC was integrated into hhuOS. The implementation supports usage
|
||||
of the local APIC in combination with the I/O APIC for regular interrupt handling without the
|
||||
PIC\@. Also, the APIC's included timer is used to trigger hhuOS' scheduler, and it is demonstrated
|
||||
how to initialize a multiprocessor system using the APIC's IPI capabilities. All of this is
|
||||
implemented with real hardware in mind, so ACPI is used to gather system information during runtime
|
||||
and adapt the initialization and usage accordingly.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Comparing PIC and APIC Implementations}
|
||||
\label{sec:comparingpicapic}
|
||||
|
||||
Handling interrupts using the PIC is extremely simple, as seen in hhuOS' PIC implementation.
|
||||
Initialization and usage can be performed by using a total of only four different registers, two
|
||||
per PIC\footnote{Omitting the infrastructure that is required for both, PIC and APIC, like the IDT
|
||||
or dispatcher.}.
|
||||
|
||||
In comparison, the code complexity required to use the APIC is very high. The most obvious reason
|
||||
is its significantly increased set of features: Local interrupts are special to the APIC, also the
|
||||
APIC system is made up of multiple components that require internal communication and individual
|
||||
initialization. Additionally, the APIC supports advanced processor topologies with large amounts of
|
||||
cores and offers increased flexibility by allowing to configure individual interrupt's vectors,
|
||||
destinations, signals and priorities, which results in additional setup.
|
||||
|
||||
Another source of complexity that is not present with the PIC is the APIC's variability: With the
|
||||
PC/AT architecture, the entire hardware interrupt configuration is known before boot. This is not
|
||||
the case when using the APIC, as the amount of interrupt controllers or even the number and order
|
||||
of connected devices is only known while the system is running. Parsing this information from ACPI
|
||||
tables and allowing the implementation to adapt to different hardware configurations, all while
|
||||
maintaining PC/AT compatibility, increases the amount of code by a large margin.
|
||||
|
||||
In general, all of this effort results in a much more powerful and future-proof system: It is not
|
||||
limited to specific hardware configurations, allows scaling to a large amount of interrupts or
|
||||
processors, and is required for multiprocessor machines, which equals to almost all modern computer
|
||||
systems.
|
||||
|
||||
\section{Future Improvements}
|
||||
\label{sec:futureimprov}
|
||||
|
||||
\subsection{Dependence on ACPI}
|
||||
\label{subsec:acpidependance}
|
||||
|
||||
The APIC system requires a supplier of system information during operation, but this must not
|
||||
necessarily be ACPI. Systems following Intel's ``MultiProcessor Specification''~\cite{mpspec} can
|
||||
acquire the required information by parsing configuration tables similar to ACPI's system
|
||||
description tables, but provided in accordance to the MultiProcessor Specification. This would
|
||||
increase the amount of systems supported by this APIC implementation, but the general compatibility
|
||||
improvement is difficult to quantize, as single-core systems are still supported by the old PIC
|
||||
implementation\footnote{The MultiProcessor Specification was (pre-) released in
|
||||
1993~\cite{mpspecpre}, the first ACPI release was in 1996~\cite{acpipre}. Multiprocessor systems
|
||||
between these years would be affected.}.
|
||||
|
||||
Alternatively, systems that do not support ACPI could be supported partially by utilizing the local
|
||||
APIC's virtual wire mode~\cite[sec.~3.6.2.2]{mpspec}. The reliance on information provided in the
|
||||
MADT stems mostly from using the I/O APIC as the external interrupt controller. By using the local
|
||||
APIC for its local interrupt and multiprocessor functionality, but keeping the PC/AT compatible PIC
|
||||
as the external interrupt controller, multiprocessor systems without ACPI could be supported.
|
||||
|
||||
\subsection{Discrete APIC and x2Apic}
|
||||
\label{subsec:discretex2}
|
||||
|
||||
This implementation only supports the xApic architecture, as it's convenient to emulate and the
|
||||
x2Apic architecture is fully backwards compatible~\cite[sec.~3.11.12]{ia32}. The original, discrete
|
||||
local APIC (Intel 82489DX) is not supported explicitly, which reduces this implementation's
|
||||
compatibility to older systems\footnote{Although it is possible that no or only very few
|
||||
modifications are necessary, as this implementation does not utilize many of the xApic's exclusive
|
||||
features~\cite[sec.~3.23.27]{ia32}.}. Supporting the x2Apic architecture does not increase
|
||||
compatibility, but using the x2Apic's MSR-based atomic register access is beneficial in
|
||||
multiprocessor systems. Newer ACPI versions provide separate x2Apic specific structures in the
|
||||
MADT~\cite[sec.~5.2.12.12]{acpi65}, so supporting x2Apic also requires supporting modern ACPI
|
||||
versions\footnote{Currently, hhuOS supports ACPI 1.0b~\cite{acpi1}.}.
|
||||
|
||||
\subsection{PCI Devices}
|
||||
\label{subsec:pcidevices}
|
||||
|
||||
The APIC is capable of handling MSIs, but this is not implemented in this thesis. To support PCI
|
||||
devices using the APIC instead of the PIC, either support for MSIs has to be implemented, or ACPI
|
||||
has to be used to determine the corresponding I/O APIC interrupt inputs. This is rather
|
||||
complicated, as it requires interacting with ACPI using AML~\cite[sec.~6.2.13]{acpi65}, which needs
|
||||
an according interpreter\footnote{There exist modular solutions that can be ported to the target
|
||||
OS~\cite{acpica}.}.
|
||||
|
||||
\subsection{Multiprocessor Systems}
|
||||
\label{subsec:multiprocessor}
|
||||
|
||||
This implementation demonstrates AP startup in SMP systems by using the APIC's IPI mechanism, but
|
||||
processors are only initialized to a spin-loop. Because the APIC is a prerequisite for more
|
||||
in-depth SMP support, this implementation enables more substantial improvements in this area, like
|
||||
distributing tasks to different CPU cores and using the core's local APIC timer to manage a
|
||||
core-local scheduler.
|
||||
|
||||
Another possible area of improvement is the execution of the interrupt handlers: At the moment,
|
||||
each I/O APIC redirects every received interrupt to the BSP's local APIC. Distributing interrupts
|
||||
to different CPU cores could improve interrupt handling performance, especially with frequent
|
||||
interrupts, like from network devices. This redirection could also happen dynamically
|
||||
(``IRQ-balancing''~\cite{irqbalance}), depending on interrupt workload measurements.
|
||||
|
||||
\subsection{UEFI Support}
|
||||
\label{subsec:uefisupport}
|
||||
|
||||
Currently, hhuOS' ACPI system only works with BIOS, so when booting an UEFI system, this
|
||||
implementation is disabled, because it requires ACPI. By supporting ACPI on UEFI systems, this
|
||||
implementation could also be used for those systems, which would improve compatibility with modern
|
||||
platforms.
|
||||
645
chap/implementation.tex
Normal file
645
chap/implementation.tex
Normal file
@ -0,0 +1,645 @@
|
||||
\chapter{Implementation}
|
||||
\label{ch:implementation}
|
||||
|
||||
This section will detail how and to what extent the APIC support was implemented. The steps
|
||||
performed in this implementation are described in a generally applicable way, concrete
|
||||
implementation details with hhuOS as an example are found in \autoref{ch:listings}.
|
||||
\autoref{sec:hhuosintegration} deals with the modifications done to the hhuOS system to integrate
|
||||
the APIC implementation.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Design Decisions and Scope}
|
||||
\label{sec:design}
|
||||
|
||||
The APIC interrupt architecture is split into multiple hardware components and tasks: The
|
||||
(potentially multiple) local APICs, the (usually single) I/O APIC and the APIC timer (part of each
|
||||
local APIC). Furthermore, the APIC system needs to interact with its memory mapped registers and
|
||||
the hhuOS ACPI subsystem, to gather information about the CPU topology and IRQ overrides. Also, the
|
||||
OS should be able to interact with the APIC system in a simple and easy manner, without needing to
|
||||
know all of its individual parts.
|
||||
|
||||
To keep the whole system structured and simple, the implementation is split into the following main
|
||||
components (see \autoref{fig:implarch}):
|
||||
|
||||
\begin{itemize}
|
||||
\item \code{LocalApic}: Provides functionality to interact with the local APIC
|
||||
(masking and unmasking, register access, etc.).
|
||||
\item \code{IoApic}: Provides functionality to interact with the I/O APIC (masking and
|
||||
unmasking, register access, etc.)
|
||||
\item \code{ApicTimer}: Provides functionality to calibrate the APIC timer and handle its interrupts.
|
||||
\item \code{Apic}: Condenses all the functionality above and exposes it to other parts
|
||||
of the OS\@.
|
||||
\end{itemize}
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.5\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/Architecture.svg}
|
||||
\end{subfigure}
|
||||
\caption{Caller hierarchy of the main components.}
|
||||
\label{fig:implarch}
|
||||
\end{figure}
|
||||
|
||||
This implementation is targeted to support systems with a single I/O APIC\footnote{Operation with
|
||||
more than one I/O APIC is described further in the MultiProcessor
|
||||
specification~\cite[sec.~3.6.8]{mpspec}.}, because consumer hardware typically only uses a single
|
||||
one, and so does QEMU emulation. General information on implementing multiple I/O APIC support can
|
||||
be found in \autoref{subsec:multiioapic}.
|
||||
|
||||
With the introduction of ACPI's GSIs to the OS, new types are introduced to clearly differentiate
|
||||
different representations of interrupts and prevent unintentional conversions:
|
||||
|
||||
\begin{itemize}
|
||||
\item \code{GlobalSystemInterrupt}: ACPI's interrupt abstraction, detached from the hardware
|
||||
interrupt lines.
|
||||
\item \code{InterruptRequest}: Represents an IRQ, allowing the OS to address interrupts by
|
||||
the name of the device that triggers it. When the APIC is used, IRQ overrides map IRQs to GSIs.
|
||||
\item \code{InterruptVector}: Represents an interrupt's vector number, as used by the
|
||||
\code{InterruptDispatcher}. The dispatcher maps interrupt vectors to interrupt handlers.
|
||||
\end{itemize}
|
||||
|
||||
Both BIOS and UEFI are supported by hhuOS, but the hhuOS ACPI subsystem is currently only available
|
||||
with BIOS\footnote{State from 11/02/23.}, so when hhuOS is booted using UEFI, APIC support can't be
|
||||
enabled. Also, the APIC can handle MSIs, but they are not included in this implementation, as hhuOS
|
||||
currently does not utilize them.
|
||||
|
||||
SMP systems are partially supported: The APs are initialized, but only to a busy-looping state, as
|
||||
hhuOS currently is a single-core OS and lacks some required infrastructure. All interrupts are
|
||||
handled using the BSP\@.
|
||||
|
||||
Summary of features that are outside the scope of this thesis:
|
||||
|
||||
\begin{itemize}
|
||||
\item Operation with a discrete APIC or x2Apic.
|
||||
\item Interrupts with logical destinations or custom priorities.
|
||||
\item Returning from APIC operation to PIC mode\footnote{This would be theoretically possible with
|
||||
single-core hardware, but probably useless.}.
|
||||
\item Relocation of a local APIC's MMIO memory region\footnote{Relocation is possible by writing a new
|
||||
physical APIC base address to the IA32\textunderscore{}APIC\textunderscore{}BASE MSR.}.
|
||||
\item Distributing external interrupts to different APs in SMP enabled systems.
|
||||
\item Usage of the system's performance counter or monitoring interrupts.
|
||||
\item Meaningful APIC error handling.
|
||||
\item Handling of MSIs.
|
||||
\end{itemize}
|
||||
|
||||
To be able to easily extend an APIC implementation for single-core systems to SMP systems, some
|
||||
things have to be taken into account:
|
||||
|
||||
\begin{itemize}
|
||||
\item SMP systems need to manage multiple \code{LocalApic} and \code{ApicTimer} instances. This is
|
||||
handled by the \code{Apic} class.
|
||||
\item Initialization of the different components can no longer happen at the same ``location'': The local
|
||||
APICs and APIC timers of additional APs have to be initialized by the APs themselves, because the
|
||||
BSP can not access an AP's registers.
|
||||
\item APs are only allowed to access instances of APIC classes that belong to them.
|
||||
\item Interrupt handlers that get called on multiple APs may need to take the current processor into
|
||||
account (for example the APIC timer interrupt handler).
|
||||
\item Register access has to be synchronized, if it is performed in multiple operations on the same
|
||||
address space.
|
||||
\end{itemize}
|
||||
|
||||
\section{Code Style}
|
||||
\label{sec:codestyle}
|
||||
|
||||
Individual state of local and I/O APICs is managed through instances of their respective classes.
|
||||
Because each CPU core can only access the local APIC contained in itself, this can create a
|
||||
misconception: It is not possible to (e.g.) allow an interrupt in a certain local APIC by calling a
|
||||
function on a certain \code{LocalApic} instance. This is communicated through code by declaring a
|
||||
range of functions as \code{static}. It is also in direct contrast to the \code{IoApic} class: I/O
|
||||
APICs can be addressed by instances, because they are not part of the CPU core: Each core can
|
||||
always access all I/O APICs (if there are multiple).
|
||||
|
||||
Error checking is done to a small extent in this implementation: Publicly exposed functions (from
|
||||
the \code{Apic} class) do check for invalid invocations, but the internally used classes do not
|
||||
protect their invariants, because they are not used directly by other parts of the OS. These
|
||||
classes only selectively expose their interfaces (by using the \code{friend} declaration) for the
|
||||
same reason.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Local APIC}
|
||||
\label{sec:lapicinit}
|
||||
|
||||
The local APIC block diagram (see \autoref{fig:localapicblock}) shows a number of registers that
|
||||
are required for initialization:
|
||||
|
||||
\begin{itemize}
|
||||
\item \textbf{\gls{lvt}}: Used to configure how local interrupts are handled.
|
||||
\item \textbf{\gls{svr}}: Contains the software-enable flag and the spurious interrupt vector number.
|
||||
\item \textbf{\gls{tpr}}: Decides the order in which interrupts are handled and a possible interrupt
|
||||
priority threshold, to ignore low priority interrupts.
|
||||
\item \textbf{\gls{icr}}: Controls the sending of IPIs for starting up APs in SMP enabled systems.
|
||||
\item \textbf{\gls{apr}}: Controls the priority in the APIC arbitration mechanism.
|
||||
\end{itemize}
|
||||
|
||||
There are multiple registers associated with the LVT, those belong to the different local
|
||||
interrupts the local APIC can handle. Local interrupts this implementation is concerned about are
|
||||
listed below:
|
||||
|
||||
\begin{itemize}
|
||||
\item LINT1: The local APIC's NMI source.
|
||||
\item Timer: Periodic interrupt triggered by the APIC timer.
|
||||
\item Error: Triggered by errors in the APIC system (e.g.\ invalid vector numbers or corrupted messages
|
||||
in internal APIC communication).
|
||||
\end{itemize}
|
||||
|
||||
The LINT0 interrupt is unlisted, because it is mainly important for virtual wire mode (it can be
|
||||
triggered by external interrupts from the PIC). The performance and thermal monitoring interrupts
|
||||
also remain unused in this implementation.
|
||||
|
||||
Besides the local APIC's own registers, the IMCR and \textbf{\gls{ia32 apic base msr}} also require
|
||||
initialization (described in \autoref{subsec:lapicenable}).
|
||||
|
||||
After system power-up, the local APIC is in the following state~\cite[sec.~3.11.4.7]{ia32}:
|
||||
|
||||
\begin{itemize}
|
||||
\item IRR, ISR and TPR are reset to \code{0x00000000}.
|
||||
\item The LVT is reset to \code{0x00000000}, except for the masking bits (all local interrupts are masked
|
||||
on power-up).
|
||||
\item The SVR is reset to \code{0x000000FF}.
|
||||
\item The APIC is in xApic mode, even if x2Apic support is present.
|
||||
\item Only the BSP is enabled, other APs have to be enabled by the BSP's local APIC\@.
|
||||
\end{itemize}
|
||||
|
||||
The initialization sequence consists of these steps:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Enable symmetric I/O mode and set the APIC operating mode.
|
||||
\item Initialize the LVT and NMI\@.
|
||||
\item Initialize the SVR\@.
|
||||
\item Clear outstanding signals.
|
||||
\item Initialize the TPR\@.
|
||||
\item Initialize the APIC timer and error handler.
|
||||
\item Startup the APs and initialize their respective local APICs.
|
||||
\item Synchronize the APRs.
|
||||
\end{enumerate}
|
||||
|
||||
\subsection{Accessing Local APIC Registers in xApic Mode}
|
||||
\label{subsec:xapicregacc}
|
||||
|
||||
Accessing registers in xApic mode is done via MMIO and requires a single page (4 KB) of memory,
|
||||
mapped to the ``APIC Base'' address, which can be obtained by reading the
|
||||
IA32\textunderscore{}APIC\textunderscore{}BASE MSR (see
|
||||
\autoref{fig:ia32apicbasemsr}/\autoref{tab:lapicregsmsr}) or parsing the MADT (see
|
||||
\autoref{tab:madt}). The IA-32 manual specifies a caching strategy of ``strong
|
||||
uncachable''\footnote{See IA-32 manual for information on this caching
|
||||
strategy~\cite[sec.~3.12.3]{ia32}.}~\cite[sec.~3.11.4.1]{ia32} for this region (see
|
||||
\autoref{sec:apxxapicregacc} for the example implementation).
|
||||
|
||||
The address offsets (from the base address) for the local APIC registers are listed in the IA-32
|
||||
manual~\cite[sec.~3.11.4.1]{ia32} and in \autoref{tab:lapicregs}.
|
||||
|
||||
\subsection{Enabling the Local APIC}
|
||||
\label{subsec:lapicenable}
|
||||
|
||||
The following steps have to be executed before any interrupt handling has been enabled by the OS\@.
|
||||
|
||||
Because the system boots in PIC mode, \code{0x01} should be written to the
|
||||
IMCR~\cite[sec.~3.6.2.1]{mpspec} to disconnect the PIC from the local APIC's LINT0 pin (see
|
||||
\autoref{fig:integratedapic}, for the example implementation see \autoref{sec:apxdisablepic}).
|
||||
|
||||
To set the operating mode, it is first determined which modes are supported by executing the
|
||||
\code{cpuid} instruction with \code{eax=1}: If bit 9 of the \code{edx} register is set, xApic mode
|
||||
is supported, if bit 21 of the \code{ecx} register is set, x2Apic mode is
|
||||
supported~\cite[sec.~5.1.2]{cpuid}.
|
||||
|
||||
If xApic mode is supported (if the local APIC is an integrated APIC), it will be in that mode
|
||||
already. The ``global enable/disable'' (``EN'') bit in the
|
||||
IA32\textunderscore{}APIC\textunderscore{}BASE MSR (see
|
||||
\autoref{fig:ia32apicbasemsr}/\autoref{tab:lapicregsmsr}) allows disabling xApic mode, and thus the
|
||||
entire local APIC, globally\footnote{If the system uses the discrete APIC bus, xApic mode cannot be
|
||||
re-enabled without a system reset~\cite[sec.~3.11.4.3]{ia32}.}.
|
||||
|
||||
Enabling x2Apic mode is done by setting the ``EXTD'' bit (see
|
||||
\autoref{fig:ia32apicbasemsr}/\autoref{tab:lapicregsmsr}) of the
|
||||
IA32\textunderscore{}APIC\textunderscore{}BASE MSR~\cite[sec.~3.11.4.3]{ia32}.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.7\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_apic_base_msr.svg}
|
||||
\end{subfigure}
|
||||
\caption{The IA32\textunderscore{}APIC\textunderscore{}BASE MSR~\cite[sec.~3.11.4.4]{ia32}.}
|
||||
\label{fig:ia32apicbasemsr}
|
||||
\end{figure}
|
||||
|
||||
Because QEMU does not support x2Apic mode via its TCG\footnote{QEMU Tiny Code Generator transforms
|
||||
target instructions to instructions for the host CPU.}, this implementation only uses xApic mode.
|
||||
|
||||
Besides the ``global enable/disable'' (``EN'') flag, the APIC can also be enabled/disabled by using
|
||||
the ``software enable/disable'' flag in the SVR, see \autoref{subsec:lapicsoftenable}.
|
||||
|
||||
\subsection{Handling Local Interrupts}
|
||||
\label{subsec:lapiclvtinit}
|
||||
|
||||
To configure how the local APIC handles the different local interrupts, the LVT registers are
|
||||
written (see \autoref{fig:localapiclvt}).
|
||||
|
||||
Local interrupts can be configured to use different delivery modes
|
||||
(excerpt)~\cite[sec.~3.11.5.1]{ia32}:
|
||||
|
||||
\begin{itemize}
|
||||
\item Fixed: Simply delivers the interrupt vector stored in the LVT register.
|
||||
\item NMI: Configures this interrupt as non-maskable, this ignores the stored vector number.
|
||||
\item ExtINT: The interrupt is treated as an external interrupt (instead of local interrupt), used e.g.\
|
||||
in virtual wire mode for LINT0.
|
||||
\end{itemize}
|
||||
|
||||
Initially, all local interrupts are initialized to PC/AT defaults: Masked, edge-triggered,
|
||||
active-high and with ``fixed'' delivery mode. Most importantly, the vector fields (bits 0:7 of an
|
||||
LVT register) are set to their corresponding interrupt vector (see \autoref{sec:apxlvtinit} for an
|
||||
example implementation).
|
||||
|
||||
After default-initializing the local interrupts, LINT1 has to be configured separately (see
|
||||
\autoref{tab:lapicregslvtlint}), because it is the local APIC's NMI source\footnote{In older
|
||||
specifications~\cite{mpspec}, LINT1 is defined as NMI source (see \autoref{fig:integratedapic}). It
|
||||
is possible that this changed with newer architectures, so for increased compatibility this
|
||||
implementation configures the local APIC NMI source as reported by ACPI. It is also possible that
|
||||
ACPI reports information on the NMI source just for future-proofing.}. The delivery mode is set to
|
||||
``NMI'' and the interrupt vector to \code{0x00}. This information is also provided by ACPI in the
|
||||
MADT (see \autoref{tab:madtlnmi}). Other local interrupts (APIC timer and the error interrupt) will
|
||||
be configured later (see \autoref{subsec:lapictimer} and \autoref{subsec:lapicerror}).
|
||||
|
||||
\subsection{Allowing Interrupt Processing}
|
||||
\label{subsec:lapicsoftenable}
|
||||
|
||||
To complete a minimal local APIC initialization, the ``software enable/disable'' flag and the
|
||||
spurious interrupt vector (both contained in the SVR, see
|
||||
\autoref{fig:ia32apicsvr}/\autoref{tab:lapicregssvr}), are set. It makes sense to choose
|
||||
\code{0xFF} for the spurious interrupt vector, as on P6 and Pentium processors, the lowest 4 bits
|
||||
must be set (see \autoref{fig:ia32apicsvr}).
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.7\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_apic_svr.svg}
|
||||
\end{subfigure}
|
||||
\caption{The local APIC SVR register~\cite[sec.~3.11.9]{ia32}.}
|
||||
\label{fig:ia32apicsvr}
|
||||
\end{figure}
|
||||
|
||||
Because the APIC's spurious interrupt has a dedicated interrupt vector (unlike the PIC's spurious
|
||||
interrupt), it can be ignored easily by registering a stub interrupt handler for the appropriate
|
||||
vector (see \autoref{sec:apxsvr} for an implementation example).
|
||||
|
||||
The final step to initialize the BSP's local APIC is to allow the local APIC to receive interrupts
|
||||
of all priorities. This is done by writing \code{0x00} to the TPR~\cite[sec.~3.11.8.3]{ia32} (see
|
||||
\autoref{tab:lapicregstpr}). By configuring the TPRs of different local APICs to different
|
||||
priorities or priority classes, distribution of external interrupts to CPUs can be controlled, but
|
||||
this is not used in this thesis.
|
||||
|
||||
\subsection{Local Interrupt EOI}
|
||||
\label{subsec:lapiceoi}
|
||||
|
||||
To notify the local APIC that a local interrupt has been handled, its EOI register (see
|
||||
\autoref{tab:lapicregseoi}) has to be written. Not all local interrupts require EOIs: NMI, SMI,
|
||||
INIT, ExtINT, STARTUP, or INIT-Deassert interrupts are excluded~\cite[sec.~3.11.8.5]{ia32}.
|
||||
|
||||
EOIs for external interrupts are also handled by the local APIC, this is described in
|
||||
\autoref{subsec:ioapiceoi}.
|
||||
|
||||
\subsection{APIC Timer}
|
||||
\label{subsec:lapictimer}
|
||||
|
||||
The APIC timer is integrated into the local APIC, so it requires initialization of the latter. Like
|
||||
the PIT, the APIC timer can generate periodic interrupts in a specified interval by using a
|
||||
counter, that is initialized with a starting value depending on the desired interval. Because the
|
||||
APIC timer doesn't tick with a fixed frequency, but at bus frequency, the initial counter has to be
|
||||
determined at runtime by using an external time source. In addition to the counter register, the
|
||||
APIC timer interval is influenced by a divider: Instead of decrementing the counter at every bus
|
||||
clock, it will be decremented every \(n\)-th bus clock, where \(n\) is the divider. This is useful
|
||||
to allow for long intervals (with decreased precision), that would require a larger counter
|
||||
register otherwise.
|
||||
|
||||
The APIC timer supports three different timer modes, that can be set in the timer's LVT register:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Oneshot: Trigger exactly one interrupt when the counter reaches zero.
|
||||
\item Periodic: Trigger an interrupt each time the counter reaches zero, on zero the counter reloads its
|
||||
initial value.
|
||||
\item TSC-Deadline: Trigger exactly one interrupt at an absolute time.
|
||||
\end{enumerate}
|
||||
|
||||
This implementation uses the APIC timer in periodic mode, to trigger the scheduler preemption.
|
||||
Initialization requires the following steps (order recommended by OSDev~\cite{osdev}):
|
||||
|
||||
\begin{enumerate}
|
||||
\item Measure the timer frequency with an external time source.
|
||||
\item Configuration of the timer's divider register (see \autoref{tab:lapicregstimerdiv}).
|
||||
\item Setting the timer mode to periodic (see \autoref{tab:lapicregslvtt}).
|
||||
\item Initializing the counter register (see \autoref{tab:lapicregstimerinit}), depending on the measured
|
||||
timer frequency and the desired interval.
|
||||
\end{enumerate}
|
||||
|
||||
In this implementation, the APIC timer is calibrated by counting the amount of ticks in one
|
||||
millisecond using oneshot mode (see \autoref{sec:apxapictimer} for an example implementation). The
|
||||
measured amount of timer ticks can then be used to calculate the required counter for an arbitrary
|
||||
millisecond interval, although very large intervals could require the use of a larger divider,
|
||||
while very small intervals (in micro- or nanosecond scale) could require the opposite, to provide
|
||||
the necessary precision. For this approach it is important that the timer is initialized with the
|
||||
same divider that was used during calibration.
|
||||
|
||||
To use the timer, an interrupt handler has to be registered to its interrupt vector (see
|
||||
\autoref{sec:apxapictimer} for an example implementation).
|
||||
|
||||
\subsection{APIC Error Interrupt}
|
||||
\label{subsec:lapicerror}
|
||||
|
||||
Errors can occur for example when the local APIC receives an invalid vector number, or an APIC
|
||||
message gets corrupted on the system bus. To handle these cases, the local APIC provides the local
|
||||
error interrupt, whose interrupt handler can read the error status from the local APIC's
|
||||
\textbf{\gls{esr}} (see \autoref{fig:ia32esr}/\autoref{tab:lapicregsesr}) and take appropriate
|
||||
action.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.7\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_error_status_register.svg}
|
||||
\end{subfigure}
|
||||
\caption{Error Status Register~\cite[sec.~3.11.5.3]{ia32}.}
|
||||
\label{fig:ia32esr}
|
||||
\end{figure}
|
||||
|
||||
The ESR is a ``write/read'' register: Before reading a value from the ESR, it has to be written,
|
||||
which updates the ESR's contents to the error status since the last write. Writing the ESR also
|
||||
arms the local error interrupt again~\cite[sec.~3.11.5.3]{ia32}.
|
||||
|
||||
Enabling the local error interrupt is now as simple as enabling it in the local APIC's LVT and
|
||||
registering an interrupt handler for the appropriate vector (see \autoref{sec:apxhandlingerror} for
|
||||
an example implementation).
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{I/O APIC}
|
||||
\label{sec:ioapicinit}
|
||||
|
||||
% TODO: Continue moving code to the appendix from here on
|
||||
|
||||
To fully replace the PIC and handle external interrupts using the APIC, the I/O APIC, located in
|
||||
the system chipset, has to be initialized by setting its \textbf{\gls{redtbl}}
|
||||
registers~\cite[sec.~9.5.8]{ich5} (see \autoref{tab:ioapicregsredtbl}). Like the local APIC's LVT,
|
||||
the REDTBL allows configuration of interrupt vectors, masking bits, interrupt delivery modes, pin
|
||||
polarities and trigger modes (see \autoref{subsec:lapiclvtinit}).
|
||||
|
||||
Additionally, for external interrupts a destination and destination mode can be specified. This is
|
||||
required because the I/O APIC is able to forward external interrupts to different local APICs over
|
||||
the system bus (see \autoref{fig:integratedapic}). SMP systems use this mechanism to distribute
|
||||
external interrupts to different CPU cores for performance benefits. Because this implementation's
|
||||
focus is not on SMP, all external interrupts are default initialized to ``physical'' destination
|
||||
mode\footnote{The alternative is "logical" destination mode, which allows addressing individual or
|
||||
clusters of local APIC's in a larger volume of
|
||||
processors~\cite[sec.~3.11.6.2.2]{ia32}.}~\cite[sec.~3.11.6.2.1]{ia32} and are sent to the BSP for
|
||||
servicing, by using the BSP's local APIC ID as the destination. The other fields are set to
|
||||
\textbf{\gls{isa}} bus defaults\footnote{Edge-triggered, active-high.}, with ``fixed'' delivery
|
||||
mode, masked, and the corresponding interrupt vector, as defined by the \code{InterruptVector}
|
||||
enum.
|
||||
|
||||
The I/O APIC does not have to be enabled explicitly, if the local APIC is enabled and the REDTBL is
|
||||
initialized correctly, external interrupts will be redirected to the local APIC and handled by the
|
||||
CPU\@.
|
||||
|
||||
Unlike the local APIC's registers, the REDTBL registers are accessed indirectly: Two registers, the
|
||||
``Index'' and ``Data'' register~\cite[sec.~9.5.1]{ich5}, are mapped to the main memory and can be
|
||||
used analogous to the local APIC's registers. The MMIO base address can be parsed from the MADT
|
||||
(see \autoref{tab:madtioapic}). Writing an offset to the index register exposes an indirectly
|
||||
accessible I/O APIC register through the data register (see \autoref{sec:iolistings} for an example
|
||||
implementation). This indirect addressing scheme is useful, because the number of external
|
||||
interrupts an I/O APIC supports, and in turn the number of REDTBL registers, can
|
||||
vary\footnote{Intel's consumer \textbf{\glspl{ich}} always support a fixed amount of 24 external
|
||||
interrupts though~\cite[sec.~9.5.7]{ich5}.}.
|
||||
|
||||
It is possible that one or multiple of the I/O APIC's interrupt inputs act as an NMI source. If
|
||||
this is the case is reported in the MADT (see \autoref{tab:madtionmi}), so when necessary, the
|
||||
corresponding REDTBL entries are initialized like the local APIC's NMI source (see
|
||||
\autoref{subsec:lapiclvtinit}), and using these interrupt inputs for external interrupts is
|
||||
forbidden.
|
||||
|
||||
\subsection{Interrupt Overrides}
|
||||
\label{subsec:ioapicpcat}
|
||||
|
||||
In every PC/AT compatible system, external devices are hardwired to the PIC in the same order.
|
||||
Because this is not the case for the I/O APIC, the interrupt line used by each PC/AT compatible
|
||||
interrupt has to be determined by the OS at runtime, by using ACPI. ACPI provides ``Interrupt
|
||||
Source Override'' structures~\cite[sec.~5.2.8.3.1]{acpi1} inside the MADT (see
|
||||
\autoref{tab:madtirqoverride}) for each PC/AT compatible interrupt that is mapped differently to
|
||||
the I/O APIC than to the PIC\@.
|
||||
|
||||
In addition to the interrupt input mapping, these structures also allow to customize the pin
|
||||
polarity and trigger mode of PC/AT compatible interrupts.
|
||||
|
||||
This information does not only apply to the REDTBL initialization, but it has to be taken into
|
||||
account every time an action is performed on a PC/AT compatible interrupt, like masking or
|
||||
unmasking: If \code{IRQ0} (PIT) should be unmasked, it has to be determined what GSI (or in other
|
||||
words, I/O APIC interrupt input) it belongs to. In many systems \code{IRQ0} is mapped to
|
||||
\code{GSI2}, because the PC/AT compatible PICs are connected to \code{GSI0}. Thus, to allow the PIT
|
||||
interrupt in those systems, the REDTBL entry belonging to \code{GSI2} instead of \code{GSI0} has to
|
||||
be written (see \autoref{sec:apxirqoverrides} for an example implementation).
|
||||
|
||||
\subsection{External Interrupt EOI}
|
||||
\label{subsec:ioapiceoi}
|
||||
|
||||
Notifying the I/O APIC that an external interrupt has been handled differs depending on the
|
||||
interrupt trigger mode: Edge-triggered external interrupts are completed by writing the local
|
||||
APIC's EOI register (see \autoref{subsec:lapiceoi})\footnote{Because external interrupts are
|
||||
forwarded to the local APIC, the local APIC is responsible for tracking them in its IRR and ISR.}.
|
||||
Level-triggered interrupts are treated separately: Upon registering a level-triggered external
|
||||
interrupt, the I/O APIC sets an internal ``Remote IRR'' bit in the corresponding REDTBL
|
||||
entry~\cite[sec.~9.5.8]{ich5} (see \autoref{tab:ioapicregsredtbl}).
|
||||
|
||||
There are three possible ways to signal completion of a level-triggered external interrupt to clear
|
||||
the remote IRR bit:
|
||||
|
||||
\begin{enumerate}
|
||||
\item Using the local APIC's EOI broadcasting feature: If EOI broadcasting is enabled, writing the local
|
||||
APIC's EOI register also triggers EOIs for each I/O APIC (for the appropriate interrupt), which
|
||||
clears the remote IRR bit.
|
||||
\item Sending a directed EOI to an I/O APIC: I/O APICs with versions greater than \code{0x20} include an
|
||||
I/O EOI register. Writing the vector number of the handled interrupt to this register clears the
|
||||
remote IRR bit.
|
||||
\item Simulating a directed EOI for I/O APICs with versions smaller than \code{0x20}: Temporarily masking
|
||||
and setting a completed interrupt as edge-triggered clears the remote IRR
|
||||
bit~\cite[io\textunderscore{}apic.c]{linux}.
|
||||
\end{enumerate}
|
||||
|
||||
Because the first option is the only one supported by all APIC versions, it is used in this
|
||||
implementation\footnote{Disabling EOI broadcasting is not supported by all local
|
||||
APICs~\cite[sec.~3.11.8.5]{ia32}.}.
|
||||
|
||||
At this point, after initializing the local and I/O APIC for the BSP, the APIC system is fully
|
||||
usable. External interrupts now have to be enabled/disabled by writing the ``masked'' bit in these
|
||||
interrupts' REDTBL entries, interrupt handler completion is signaled by writing the local APIC's
|
||||
EOI register, and spurious interrupts are detected by using the local APIC's spurious interrupt
|
||||
vector.
|
||||
|
||||
\subsection{Multiple I/O APICs}
|
||||
\label{subsec:multiioapic}
|
||||
|
||||
Most consumer hardware, for example all IA processors~\cite{ia32} and ICH hubs~\cite{ich5}, only
|
||||
provide a single I/O APIC, although technically multiple I/O APICs are supported by the
|
||||
MultiProcessor specification~\cite[sec.~3.6.8]{mpspec}.
|
||||
|
||||
If ACPI reports multiple I/O APICs (by supplying multiple MADT I/O APIC structures, see
|
||||
\autoref{tab:madtioapic}), the previously described initialization has to be performed for each I/O
|
||||
APIC individually. Additionally, the I/O APIC's ID, also reported by ACPI, has to be written to the
|
||||
corresponding I/O APIC's ID register (see \autoref{tab:ioapicregsid}), because this register is
|
||||
always initialized to zero~\cite[sec.~9.5.6]{ich5}.
|
||||
|
||||
Using a variable number of I/O APICs requires determining the target I/O APIC for each operation
|
||||
that concerns a GSI, like masking or unmasking. For this reason, ACPI provides the ``GSI
|
||||
Base''~\cite[sec.~5.2.8.2]{acpi1} for each available I/O APIC, the number of GSIs a single I/O APIC
|
||||
can handle can be determined by reading the I/O APIC's version register~\cite[sec.~9.5.7]{ich5}
|
||||
(see \autoref{tab:ioapicregsver})\footnote{This approach was previously used in this
|
||||
implementation, but removed for simplicity.}.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Symmetric Multiprocessing}
|
||||
\label{sec:smpinit}
|
||||
|
||||
Like single-core systems, SMP systems boot using only a single core, the BSP. By using the APIC's
|
||||
capabilities to send IPIs between cores, additional APs can be put into startup state and booted
|
||||
for system use.
|
||||
|
||||
To determine the amount of usable processors, the MADT is parsed (see \autoref{tab:madtlapic}).
|
||||
Note, that some processors may be reported as disabled, those may not be used by the OS (see
|
||||
\autoref{tab:madtlapicflags}).
|
||||
|
||||
\subsection{Inter-Processor Interrupts}
|
||||
\label{subsec:ipis}
|
||||
|
||||
Issuing IPIs works by writing the local APIC's ICR (see
|
||||
\autoref{fig:ia32icr}/\autoref{tab:lapicregsicr}). It allows specifying IPI type, destination
|
||||
(analogous to REDTBL destinations, see \autoref{sec:ioapicinit}) and vector (see
|
||||
\autoref{sec:apxipis} for an example implementation).
|
||||
|
||||
Depending on the APIC architecture, two different IPIs are required: The INIT IPI for systems using
|
||||
a discrete APIC, and the \textbf{\gls{sipi}} for systems using the xApic or x2Apic architectures:
|
||||
|
||||
\begin{itemize}
|
||||
\item The INIT IPI causes an AP to reset its state and start executing at the address specified at its
|
||||
system reset vector. If paired with a system warm-reset, the AP can be instructed to start
|
||||
executing the AP boot sequence by writing the appropriate address to the warm-reset
|
||||
vector~\cite[sec.~B.4.1]{mpspec}.
|
||||
\item Since the xApic architecture, the SIPI is used for AP startup: It causes the AP to start executing
|
||||
code in real mode, at a page specified in the IPIs interrupt vector~\cite[sec.~B.4.2]{mpspec}. By
|
||||
copying the AP boot routine to a page in lower physical memory, and sending the SIPI with the
|
||||
correct page number, an AP can be booted.
|
||||
\end{itemize}
|
||||
|
||||
To wait until the IPI is sent, the ICR's delivery status bit can be polled.
|
||||
|
||||
\begin{figure}[h]
|
||||
\centering
|
||||
\begin{subfigure}[b]{0.7\textwidth}
|
||||
\includesvg[width=1.0\linewidth]{img/ia32_interrupt_command_register.svg}
|
||||
\end{subfigure}
|
||||
\caption{Interrupt Command Register~\cite[sec.~3.11.6.1]{ia32}.}
|
||||
\label{fig:ia32icr}
|
||||
\end{figure}
|
||||
|
||||
\subsection{Universal Startup Algorithm}
|
||||
\label{subsec:apstartup}
|
||||
|
||||
SMP initialization is performed differently on various processors. Intel's MultiProcessor
|
||||
specification defines a ``universal startup algorithm'' for multiprocessor
|
||||
systems~\cite[sec.~B.4]{mpspec}, which can be used to boot SMP systems with either discrete APIC,
|
||||
xApic or x2Apic, as it issues both, INIT IPI and SIPI\footnote{Technically, it always issues the
|
||||
INIT IPI, and the SIPI only for xApic or x2Apic, but since the SIPI is ignored by discrete APICs,
|
||||
it can be sent either way. This ``INIT-SIPI-SIPI'' sequence is also stated in the IA-32
|
||||
manual~\cite[sec.~3.9.4]{ia32}.}.
|
||||
|
||||
This algorithm has some prerequisites: It is required to copy the AP boot routine (detailed in
|
||||
\autoref{subsec:apboot}) to lower memory, where the APs will start their execution. Also, the APs
|
||||
need allocated stack memory to call the entry function, and in case of a discrete APIC that uses
|
||||
the INIT IPI, the system needs to be configured for a warm-reset (by writing \code{0xAH} to the
|
||||
CMOS shutdown status byte, located at \code{0xF}~\cite[sec.~B.4]{mpspec}), because the INIT IPI
|
||||
does not support supplying the address where AP execution should begin, unlike the SIPI. The
|
||||
warm-reset vector (a 32-bit field, located at physical address
|
||||
\code{40:67}~\cite[sec.~B.4]{mpspec}) needs to be set to the physical address the AP startup
|
||||
routine was copied to. Additionally, the entire AP startup procedure has to be performed with all
|
||||
sources of interrupts disabled, which offers a small challenge, since some timings need to be taken
|
||||
into account\footnote{This implementation uses the PIT's mode 0 on channel 0 for timekeeping.}.
|
||||
|
||||
The usage of delays in the algorithm is quite specific, but the specification provides no further
|
||||
information on the importance of these timings or required precision. The algorithm allowed for
|
||||
successful startup of additional APs when tested in QEMU (with and without KVM) and on certain real
|
||||
hardware, although for different processors or emulators (like Bochs), different timings might be
|
||||
required~\cite[lapic.c]{xv6}.
|
||||
|
||||
After preparation, the universal startup algorithm is now performed as follows, for each AP
|
||||
sequentially (see \autoref{sec:apxmpusa} for an example implementation):
|
||||
|
||||
\begin{enumerate}
|
||||
\item Assert and de-assert the level-triggered INIT IPI\@.
|
||||
\item Delay for 10 milliseconds.
|
||||
\item Send the SIPI\@.
|
||||
\item Delay for 200 microseconds.
|
||||
\item Send the SIPI again.
|
||||
\item Delay for 200 microseconds again.
|
||||
\item Wait until the AP has signaled boot completion, then continue to the next.
|
||||
\end{enumerate}
|
||||
|
||||
If the system uses a discrete APIC, the APs will reach the boot routine by starting execution at
|
||||
the location specified in the warm-reset vector, if the system uses the xApic or x2Apic
|
||||
architecture, the APs will reach the boot routine because its location was specified in the SIPI\@.
|
||||
|
||||
Signaling boot completion from the APs entry function can be done by using a global bitmap
|
||||
variable, where the \(n\)-th bit indicates the running state of the \(n\)-th processor. This
|
||||
variable does not have to be synchronized across APs, because the startup is performed
|
||||
sequentially.
|
||||
|
||||
\subsection{Application Processor Boot Routine}
|
||||
\label{subsec:apboot}
|
||||
|
||||
After executing the ``INIT-SIPI-SIPI'' sequence, the targeted AP will start executing its boot
|
||||
routine in real mode. The general steps required are similar to those required when booting a
|
||||
single-core system, but since the BSP in SMP systems is already fully operational at this point,
|
||||
much can be recycled. The AP boot routine this implementation uses can be roughly described as
|
||||
follows (see \autoref{sec:apxapboot} for an example implementation):
|
||||
|
||||
\begin{enumerate}
|
||||
\item Load a temporary \textbf{\gls{gdt}}, used for switching to protected mode.
|
||||
\item Enable protected mode by writing \code{cr0}.
|
||||
\item Far jump to switch to protected mode and reload the code-segment register, set up the other
|
||||
segments manually.
|
||||
\item Load the \code{cr3}, \code{cr0} and \code{cr4} values used by the BSP to enable paging (in that
|
||||
order).
|
||||
\item Load the IDT used by the BSP\@.
|
||||
\item Determine the AP's APIC ID by using CPUID\@.
|
||||
\item Load the GDT and \textbf{\gls{tss}} prepared for this AP\@.
|
||||
\item Load the stack prepared for this AP\@.
|
||||
\item Call the (C++) AP entry function.
|
||||
\end{enumerate}
|
||||
|
||||
The APIC ID is used to determine which GDT and stack were prepared for a certain AP\@. It is
|
||||
necessary for each AP to have its own GDT, because each processor needs its own TSS for context
|
||||
switching, for example when interrupt-based system calls are used on all CPUs.
|
||||
|
||||
Because it is relocated into lower physical memory (in this implementation to \code{0x8000}), this
|
||||
code has to be position independent. For this reason, absolute physical addresses have to be used
|
||||
when jumping, loading the IDTR and GDTR, or referencing variables. Also, any variables required
|
||||
during boot have to be available after relocation, this can be achieved by locating them inside the
|
||||
``TEXT'' section of the routine, so they stay with the rest of the instructions when copying. These
|
||||
variables have to be initialized during runtime, before the routine is copied (see
|
||||
\autoref{sec:apxpreparesmp} for an example implementation).
|
||||
|
||||
\subsection{Application Processor Post-Boot Routine}
|
||||
\label{subsec:apsystementry}
|
||||
|
||||
In the entry function, called at the end of the boot routine, the AP signals boot completion as
|
||||
described in \autoref{subsec:apstartup} and initializes its local APIC by repeating the necessary
|
||||
steps from \autoref{subsec:lapiclvtinit}, \autoref{subsec:lapicsoftenable},
|
||||
\autoref{subsec:lapictimer} and \autoref{subsec:lapicerror}\footnote{MMIO memory does not have to
|
||||
be allocated again, as all local APICs use the same memory region in this implementation. Also, the
|
||||
initial value for the APIC timer's counter can be reused, if already calibrated.}.
|
||||
|
||||
Because multiple local APICs are present and active in the system now, the possibility arises that
|
||||
a certain local APIC receives multiple messages from different local APICs at a similar time. To
|
||||
decide the order of handling these messages, an arbitration mechanism based on the local APIC's ID
|
||||
is used~\cite[sec.~3.11.7]{ia32}. To make sure the arbitration priority matches the local APIC's
|
||||
ID, the ARPs can be synchronized by issuing an INIT-level-deassert IPI\footnote{This is not
|
||||
supported on Pentium 4 and Xeon processors.} (see \autoref{sec:apxappostboot} for an example
|
||||
implementation).
|
||||
|
||||
\clearpage
|
||||
26
chap/introduction.tex
Normal file
26
chap/introduction.tex
Normal file
@ -0,0 +1,26 @@
|
||||
\chapter{Introduction}
|
||||
\label{ch:introduction}
|
||||
|
||||
Computer systems are very useful, because they are able to interact with the ``outside world'', for
|
||||
instance by reading values from sensors, controlling external appliances or interacting with a user
|
||||
through human interface devices. In each of these scenarios, the system's CPU has to react to
|
||||
``external changes'', like a key press or sensor reading. An efficient hardware solution to this
|
||||
problem are ``interrupts''.
|
||||
|
||||
In this thesis, support for the ``APIC'', a modern and widely used interrupt controller
|
||||
architecture, introduced by Intel for the Pentium 4 processor, will be implemented into hhuOS, ``A
|
||||
small operating system for learning purposes''~\cite{hhuos}. This support will cover a complete
|
||||
replacement of the older ``PIC'' interrupt controller, introduction of an alternative timer - a
|
||||
part of the APIC architecture - for scheduling, and utilizing the APIC to boot multiprocessor
|
||||
systems.
|
||||
|
||||
The following chapter explains important background concepts, in \autoref{ch:implementation} the
|
||||
required steps to use the APIC and their implementation are explained in general,
|
||||
\autoref{ch:verification} deals with the verification process of the developed software on emulated
|
||||
and real hardware, and \autoref{ch:conclusion} draws conclusions regarding the previous
|
||||
implementation and future improvements.
|
||||
|
||||
Specific details on the code created during this thesis are given in \autoref{ch:listings},
|
||||
separated from the main body.
|
||||
|
||||
\clearpage
|
||||
82
chap/verification.tex
Normal file
82
chap/verification.tex
Normal file
@ -0,0 +1,82 @@
|
||||
\chapter{Verification}
|
||||
\label{ch:verification}
|
||||
|
||||
Common techniques for testing software include component-based tests, like ``unit tests'', and
|
||||
``end-to-end tests'', where the complete functionality of a software system with all its parts is
|
||||
tested. As unit testing is mostly suitable for independent slices of a system's application logic,
|
||||
it is not very useful for testing low-level software designed to run directly on hardware devices.
|
||||
|
||||
This chapter deals with the process and results of testing hhuOS with the APIC implementation
|
||||
developed during this thesis.
|
||||
|
||||
\clearpage
|
||||
|
||||
\section{Methods of Verification}
|
||||
\label{sec:verificationmethods}
|
||||
|
||||
This application can be tested by running the entire hhuOS operating system on both emulated and
|
||||
real hardware, and monitoring its state of operation\footnote{There is almost no logic that can be
|
||||
tested isolated or hardware independently.}.
|
||||
|
||||
This can be done in multiple ways:
|
||||
|
||||
\begin{itemize}
|
||||
\item When running hhuOS in an emulated environment, the current machine state can be inspected
|
||||
directly\footnote{QEMU offers the ``QEMU monitor'' to query information about specific hardware
|
||||
components, like the local or I/O APIC (\code{info lapic} and \code{info pic}).}.
|
||||
\item To test the functionality of an interrupt controller specifically, stub interrupt handlers can be
|
||||
used to verify that a specific interrupt (like an IPI) has been registered.
|
||||
\item By attaching a debugger (very simple for emulated hardware), it can be observed that e.g.\ the
|
||||
timer interrupt is correctly increasing the timestamp for each running CPU\@.
|
||||
\item By exposing some of the APIC's internal data through hhuOS' virtual file system, end-to-end
|
||||
verification can be performed from the system itself, as debugging and monitoring is significantly
|
||||
more difficult when running on real hardware.
|
||||
\item Because the interrupt controller is a vital part for many components of an operating system,
|
||||
observing a successful boot is already a significant indicator for a correct implementation.
|
||||
\end{itemize}
|
||||
|
||||
\section{Results}
|
||||
\label{sec:verificationresults}
|
||||
|
||||
QEMU was used as the main development platform, the implemented features were tested by using the
|
||||
QEMU monitor. Specifically, QEMU provides the current register state of all local and I/O APICs, by
|
||||
attaching a debugger to the running operating system correct behavior was observed on the emulated
|
||||
hardware level:
|
||||
|
||||
\begin{itemize}
|
||||
\item Working local interrupts - verified by observing the local APIC's IRR\@.
|
||||
\item Propagation of interrupts to the CPU - verified by observing the local APIC's ISR\@.
|
||||
\item A working local APIC timer - verified by observing the APIC timer's registers.
|
||||
\end{itemize}
|
||||
|
||||
Although hhuOS is developed mainly for learning purposes, every OS' core task remains to be the
|
||||
management of computer hardware. For this reason this implementation was additionally tested on a
|
||||
``ThinkPad T60s'' with an Intel ``Core 2 Duo'' processor. By providing internal status data through
|
||||
the virtual file system, implemented features (including booting additional APs) were verified on
|
||||
this very specific set of hardware.
|
||||
|
||||
More specifically, information about detected and enabled local APICs and the I/O APIC, register
|
||||
values from the BSP's LVT and the REDTBL, the contents of the PIC's \textbf{\gls{imr}}, and amounts
|
||||
of occurred interrupts by core (similar to \code{/proc/interrupts} in Linux~\cite{linux}]) are
|
||||
exposed on the path \code{/device/apic/}. This way, the following things could be verified on real
|
||||
hardware:
|
||||
|
||||
\begin{itemize}
|
||||
\item The APIC is indeed used instead of the PIC - verified by observing the PIC's IMR\@.
|
||||
\item Successful MMIO for local and I/O APIC - verified by observing the LVT and REDTBL\@.
|
||||
\item Devices ``plugging in'' to the APIC instead of the PIC - verified by observing the LVT and
|
||||
REDTBL\@.
|
||||
\item Interrupt handlers are called correctly - verified by using the keyboard.
|
||||
\item Working startup of additional APs - verified by checking the state of the AP's local APICs and
|
||||
observing the system log.
|
||||
\item Handling interrupts on another AP - verified by redirecting the keyboard interrupt to another
|
||||
processor and observing the number of occurred keyboard interrupts on this
|
||||
processor\footnote{Interestingly, this was easier to verify on real hardware than in QEMU, because
|
||||
QEMU usually instantly crashes when interrupts are enabled on an AP beside the BSP. Also,
|
||||
redirecting the keyboard interrupt in QEMU significantly changes the behaviour of any key press and
|
||||
renders the keyboard unusable. This is expected, as hhuOS' paging is not designed for multiple
|
||||
processors. On the ThinkPad T60s though, it was possible (surprisingly) to use the redirected
|
||||
keyboard interrupt to \code{cat} the \code{/device/apic/irqs} file to observe the interrupt
|
||||
statistics, which showed the keyboard interrupts arriving on a different core than the BSP. After a
|
||||
certain time, the system crashes on the ThinkPad aswell.}.
|
||||
\end{itemize}
|
||||
Reference in New Issue
Block a user