FreeNOS
IntelAPIC.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (C) 2015 Niek Linnenbank
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include <Log.h>
19 #include <MemoryContext.h>
20 #include <CoreInfo.h>
21 #include <FreeNOS/System.h>
22 #include "IntelPIT.h"
23 #include "IntelAPIC.h"
24 
25 #pragma clang optimize off
26 #pragma GCC push_options
27 #pragma GCC optimize ("O0")
28 
29 #define APIC_DEST(x) ((x) << 24)
30 #define APIC_DEST_FIELD 0x00000
31 #define APIC_DEST_LEVELTRIG 0x08000
32 #define APIC_DEST_ASSERT 0x04000
33 #define APIC_DEST_DM_INIT 0x00500
34 #define APIC_DEST_DM_STARTUP 0x00600
35 
37  : IntController()
38 {
39  m_frequency = 0;
41  m_initialCounter = 0;
43 }
44 
46 {
47  return m_io;
48 }
49 
51 {
52  return m_io.read(InitialCount);
53 }
54 
56 {
57  u32 t1, t2, loops = 20;
58 
59  // Start the APIC timer
61  m_io.write(InitialCount, 0xffffffff);
63 
64  // Measure the speed of the APIC timer using the
65  // known absolute frequency of the PIT timer. First
66  // wait for the next PIT trigger.
67  pit->waitTrigger();
68 
69  // Collect the current APIC timer counter
70  t1 = m_io.read(CurrentCount);
71 
72  // Wait for several PIT triggers
73  for (uint i = 0; i < loops; i++)
74  pit->waitTrigger();
75 
76  // Measure the current APIC timer counter again.
77  t2 = m_io.read(CurrentCount);
78 
79  // Configure the APIC timer to run at the same frequency as the PIT.
80  m_initialCounter = (t1 - t2) / loops;
81  m_frequency = pit->getFrequency();
83 
84  // Calculate APIC bus frequency in hertz using the known PIT
85  // frequency as reference for diagnostics.
86  u32 busFreq = m_initialCounter * 16 * pit->getFrequency();
87  NOTICE("Detected " << busFreq / 1000000 << "."
88  << busFreq % 1000000 << " Mhz APIC bus");
89  NOTICE("APIC counter set at " << m_initialCounter);
90  return Timer::Success;
91 }
92 
93 Timer::Result IntelAPIC::wait(u32 microseconds) const
94 {
95  if (!isKernel)
96  {
97  Timer::Info info;
98 
99  // Get current kernel timer ticks
100  if (ProcessCtl(SELF, InfoTimer, (Address) &info) != API::Success)
101  return Timer::IOError;
102 
103  // Frequency must be set
104  if (!info.frequency)
105  return Timer::IOError;
106 
107  // Set time to wait (very rough approximation)
108  u32 msecPerTick = 1000.0 / info.frequency;
109  u32 msecToWait = microseconds / 1000;
110  info.ticks += (msecToWait / msecPerTick) + 1;
111 
112  // Wait until the timer expires
113  if (ProcessCtl(SELF, WaitTimer, (Address) &info) != API::Success)
114  return Timer::IOError;
115  }
116  else
117  {
118  Size usecPerInt = 1000000 / m_frequency;
119  Size usecPerTick = usecPerInt / m_io.read(InitialCount);
120  u32 t1 = m_io.read(CurrentCount), t2;
121  u32 waited = 0;
122 
123  while (waited < microseconds)
124  {
125  t2 = m_io.read(CurrentCount);
126  if (t2 < t1)
127  {
128  waited += (t1 - t2) * usecPerTick;
129  t1 = t2;
130  }
131  t1 = t2;
132  }
133  }
134  return Timer::Success;
135 }
136 
137 Timer::Result IntelAPIC::start(u32 initialCounter, uint hertz)
138 {
139  // Set members
140  m_frequency = hertz;
141  m_initialCounter = initialCounter;
142 
143  // Start the APIC timer
146  return start();
147 }
148 
150 {
151  // Start the APIC timer
153  return Timer::Success;
154 }
155 
157 {
159  return Timer::Success;
160 }
161 
163 {
164  // Map the registers into the address space
166  return Timer::IOError;
167 
168  // Initialize and disable the timer
170  m_io.write(InitialCount, 0);
173 
174  // Enable the APIC
176  return Timer::Success;
177 }
178 
180 {
182 }
183 
185 {
187 }
188 
190 {
192  return IntController::Success;
193 }
194 
196 {
197  ulong cfg;
198 
199  // Write APIC Destination
200  cfg = m_io.read(IntCommand2);
201  cfg &= 0x00ffffff;
202  m_io.write(IntCommand2, cfg | APIC_DEST(cpuId));
203 
204  // Assert INIT
205  cfg = m_io.read(IntCommand1);
206  cfg &= ~0xcdfff;
209  m_io.write(IntCommand1, cfg);
210 
211  // Wait 10 miliseconds
212  wait(10000);
213 
214  // Send two SIPI's
215  for (Size i = 0; i < 2; i++)
216  {
217  // Write APIC Destination
218  cfg = m_io.read(IntCommand2);
219  cfg &= 0x00ffffff;
220  m_io.write(IntCommand2, cfg | APIC_DEST(cpuId));
221 
222  // Assert STARTUP
223  cfg = m_io.read(IntCommand1);
224  cfg &= ~0xcdfff;
226  (addr >> 12));
227  m_io.write(IntCommand1, cfg);
228 
229  // Wait 1 milisecond
230  wait(1000);
231  }
232 
233  // Startup interrupt delivered.
234  return IntController::Success;
235 }
236 
238 {
239  ulong cfg;
240 
241  // Write APIC Destination
242  cfg = m_io.read(IntCommand2);
243  cfg &= 0x00ffffff;
244  m_io.write(IntCommand2, cfg | APIC_DEST(cpuId));
245 
246  // Write to lower 32-bits of Interrupt Command, which triggers the IPI.
249  cfg |= vector;
250  m_io.write(IntCommand1, cfg);
251 
252  // Done
253  return IntController::Success;
254 }
Timer::getFrequency
Size getFrequency() const
Get timer frequency.
Definition: Timer.cpp:33
IntelPIT
Intel 8254 Programmable Interrupt Timer (PIT).
Definition: IntelPIT.h:40
IntelAPIC::IntelAPIC
IntelAPIC()
Constructor.
Definition: IntelAPIC.cpp:36
Timer::Success
@ Success
Definition: Timer.h:54
IntController
Interrupt controller interface.
Definition: IntController.h:35
IntelAPIC.h
APIC_DEST_DM_STARTUP
#define APIC_DEST_DM_STARTUP
Definition: IntelAPIC.cpp:34
Timer::m_int
Size m_int
Timer interrupt number.
Definition: Timer.h:165
IntelAPIC::getCounter
uint getCounter() const
Get timer initial counter.
Definition: IntelAPIC.cpp:50
ulong
unsigned long ulong
Unsigned long number.
Definition: Types.h:47
NOTICE
#define NOTICE(msg)
Output a notice message.
Definition: Log.h:75
IntelAPIC::clear
virtual IntController::Result clear(uint irq)
Clear hardware interrupt (IRQ).
Definition: IntelAPIC.cpp:189
IntelIO::set
void set(Address addr, u32 data)
Set bits in memory mapped register.
Definition: IntelIO.h:188
IntelAPIC::CurrentCount
@ CurrentCount
Definition: IntelAPIC.h:84
IO::map
Result map(Address phys, Size size=4096, Memory::Access access=Memory::Readable|Memory::Writable|Memory::User)
Map I/O address space.
Definition: IO.cpp:38
IntelPIT.h
WaitTimer
@ WaitTimer
Definition: ProcessCtl.h:50
IntelAPIC::InitialCount
@ InitialCount
Definition: IntelAPIC.h:83
IntelAPIC::stop
virtual Timer::Result stop()
Stop the APIC timer.
Definition: IntelAPIC.cpp:156
isKernel
C uint isKernel
Non-zero if this executable is linked as the kernel.
IntelAPIC::sendIPI
IntController::Result sendIPI(uint coreId, uint vector)
Send Intercore-Processor-Interrupt.
Definition: IntelAPIC.cpp:237
IntelAPIC::start
virtual Timer::Result start()
(Re)start the APIC timer.
Definition: IntelAPIC.cpp:149
IntelAPIC::Divide16
@ Divide16
Definition: IntelAPIC.h:101
IntelAPIC::disable
virtual IntController::Result disable(uint irq)
Disable hardware interrupt (IRQ).
Definition: IntelAPIC.cpp:184
IntController::Success
@ Success
Definition: IntController.h:44
Address
unsigned long Address
A memory address.
Definition: Types.h:131
IntelAPIC::IOBase
static const uint IOBase
APIC memory mapped I/O register base offset (physical address).
Definition: IntelAPIC.h:52
APIC_DEST_FIELD
#define APIC_DEST_FIELD
Definition: IntelAPIC.cpp:30
ProcessCtl
API::Result ProcessCtl(const ProcessID proc, const ProcessOperation op, const Address addr=0, const Address output=0)
Prototype for user applications.
Definition: ProcessCtl.h:93
Timer::Info
Timer information structure.
Definition: Timer.h:42
Timer
Represents a configurable timer device.
Definition: Timer.h:35
Timer::m_frequency
Size m_frequency
Frequency of the Timer.
Definition: Timer.h:162
Log.h
IntController::NotFound
@ NotFound
Definition: IntController.h:48
IntelAPIC::TimerMasked
@ TimerMasked
Definition: IntelAPIC.h:109
IntelAPIC::APICEnable
@ APICEnable
Definition: IntelAPIC.h:93
uint
unsigned int uint
Unsigned integer number.
Definition: Types.h:44
SELF
#define SELF
Definition: ProcessID.h:35
IntelIO::read
u32 read(const Address addr) const
Read memory mapped register.
Definition: IntelIO.h:134
InfoTimer
@ InfoTimer
Definition: ProcessCtl.h:49
IntelAPIC::PeriodicMode
@ PeriodicMode
Definition: IntelAPIC.h:110
IO::setBase
void setBase(const Address base)
Set memory I/O base offset.
Definition: IO.cpp:33
IntelAPIC::enable
virtual IntController::Result enable(uint irq)
Enable hardware interrupt (IRQ).
Definition: IntelAPIC.cpp:179
IntelAPIC::IntCommand2
@ IntCommand2
Definition: IntelAPIC.h:76
IntelAPIC::m_io
IntelIO m_io
I/O object.
Definition: IntelAPIC.h:234
u32
unsigned int u32
Unsigned 32-bit number.
Definition: Types.h:53
Size
unsigned int Size
Any sane size indicator cannot go negative.
Definition: Types.h:128
IntelAPIC::SpuriousIntVec
@ SpuriousIntVec
Definition: IntelAPIC.h:70
IO::Success
@ Success
Definition: IO.h:44
IntelAPIC::EndOfInterrupt
@ EndOfInterrupt
Definition: IntelAPIC.h:69
Timer::Result
Result
Result codes.
Definition: Timer.h:52
Timer::IOError
@ IOError
Definition: Timer.h:56
IntelAPIC::wait
virtual Timer::Result wait(u32 microseconds) const
Busy wait a number of microseconds.
Definition: IntelAPIC.cpp:93
API::Success
@ Success
Definition: API.h:70
APIC_DEST_ASSERT
#define APIC_DEST_ASSERT
Definition: IntelAPIC.cpp:32
APIC_DEST_LEVELTRIG
#define APIC_DEST_LEVELTRIG
Definition: IntelAPIC.cpp:31
APIC_DEST_DM_INIT
#define APIC_DEST_DM_INIT
Definition: IntelAPIC.cpp:33
IntelPIT::waitTrigger
Result waitTrigger()
Busy wait for one trigger period.
Definition: IntelPIT.cpp:58
CoreInfo.h
APIC_DEST
#define APIC_DEST(x)
Definition: IntelAPIC.cpp:29
IntelIO
Intel I/O functions.
Definition: IntelIO.h:38
MemoryContext.h
IntelIO::write
void write(const Address addr, const u32 data)
Write memory mapped register.
Definition: IntelIO.h:161
IntelAPIC::sendStartupIPI
IntController::Result sendStartupIPI(uint cpuId, Address addr)
Send startup Intercore-Processor-Interrupt.
Definition: IntelAPIC.cpp:195
IntelAPIC::IntCommand1
@ IntCommand1
Definition: IntelAPIC.h:75
IntController::Result
Result
Result codes.
Definition: IntController.h:42
IntelAPIC::initialize
virtual Timer::Result initialize()
Initialize the APIC.
Definition: IntelAPIC.cpp:162
IntelAPIC::getIO
IntelIO & getIO()
Get I/O object.
Definition: IntelAPIC.cpp:45
IntelAPIC::m_initialCounter
uint m_initialCounter
Saved initial counter value for APIC timer.
Definition: IntelAPIC.h:237
IntelAPIC::TimerVector
static const uint TimerVector
APIC timer interrupt vector is fixed at 48.
Definition: IntelAPIC.h:55
IntelAPIC::DivideConfig
@ DivideConfig
Definition: IntelAPIC.h:85