The Pedigree Project  0.1
LocalApic.cc
1 /*
2  * Copyright (c) 2008-2014, Pedigree Developers
3  *
4  * Please see the CONTRIB file in the root of the source tree for a full
5  * list of contributors.
6  *
7  * Permission to use, copy, modify, and distribute this software for any
8  * purpose with or without fee is hereby granted, provided that the above
9  * copyright notice and this permission notice appear in all copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18  */
19 
20 #if defined(APIC)
21 
22 #include "LocalApic.h"
23 #include "pedigree/kernel/Log.h"
24 #include "pedigree/kernel/machine/TimerHandler.h"
25 #include "pedigree/kernel/processor/InterruptManager.h"
26 #include "pedigree/kernel/processor/PhysicalMemoryManager.h"
27 #include "pedigree/kernel/processor/Processor.h"
28 #include "pedigree/kernel/processor/VirtualAddressSpace.h"
29 
30 #define LAPIC_REG_ID 0x0020
31 #define LAPIC_REG_VERSION 0x0030
32 #define LAPIC_REG_TASK_PRIORITY 0x0080
33 #define LAPIC_REG_PROCESSOR_PRIORITY 0x00A0
34 #define LAPIC_REG_EOI 0x00B0
35 #define LAPIC_REG_LOGICAL_DESTINATION 0x00D0
36 #define LAPIC_REG_DESTINATION_FORMAT 0x00E0
37 #define LAPIC_REG_SPURIOUS_INT 0x00F0
38 // NOTE ISR
39 // NOTE TMR
40 // NOTE IRR
41 #define LAPIC_REG_ERR_STATUS 0x0280
42 #define LAPIC_REG_INT_CMD_LOW 0x0300
43 #define LAPIC_REG_INT_CMD_HIGH 0x0310
44 #define LAPIC_REG_LVT_TIMER 0x0320
45 #define LAPIC_REG_LVT_THERMAL 0x0330
46 #define LAPIC_REG_LVT_PERFORMANCE 0x0340
47 #define LAPIC_REG_LVT_LINT0 0x0350
48 #define LAPIC_REG_LVT_LINT1 0x0360
49 #define LAPIC_REG_LVT_ERROR 0x0370
50 #define LAPIC_REG_INITIAL_COUNT 0x0380
51 #define LAPIC_REG_CURRENT_COUNT 0x0390
52 #define LAPIC_REG_DIVIDE_CONFIG 0x03E0
53 
54 #define LAPIC_TIMER_PERIODIC 0x00020000
55 #define LAPIC_MASKED 0x00010000
56 
59 #define INITIAL_COUNT_VALUE (78125 * 40)
60 
62 #define INITIAL_HZ 100
63 
64 bool LocalApic::initialise(uint64_t physicalAddress)
65 {
66  // Detect local APIC presence
67  uint32_t eax, ebx, ecx, edx;
68  Processor::cpuid(1, 0, eax, ebx, ecx, edx);
69  if (((edx >> 9) & 0x01) != 0x01)
70  {
71  ERROR("Local APIC: No local APIC present");
72  return false;
73  }
74 
75  // Some checks
76  if (check(physicalAddress) == false)
77  return false;
78 
79  // Allocate the local APIC memory-mapped I/O space
80  PhysicalMemoryManager &physicalMemoryManager =
82  if (physicalMemoryManager.allocateRegion(
83  m_IoSpace, 1,
89  physicalAddress) == false)
90  {
91  ERROR("Local APIC: Could not allocate the memory region");
92  return false;
93  }
94 
95  // Register the timer vector.
96  if (!InterruptManager::instance().registerInterruptHandler(
97  TIMER_VECTOR, this))
98  return false;
99 
100  // Register the IPI halt vector.
101  if (!InterruptManager::instance().registerInterruptHandler(
102  IPI_HALT_VECTOR, this))
103  return false;
104 
105  return initialiseProcessor();
106 }
107 
108 bool LocalApic::initialiseProcessor()
109 {
110  // Some checks
111  if (check(m_IoSpace.physicalAddress()) == false)
112  return false;
113 
114  // Enable the Local APIC and set the spurious interrupt vector
115  uint32_t tmp = m_IoSpace.read32(LAPIC_REG_SPURIOUS_INT);
116  m_IoSpace.write32(
117  (tmp & 0xFFFFFE00) | 0x100 | SPURIOUS_VECTOR, LAPIC_REG_SPURIOUS_INT);
118 
119  // Set the task priority to 0
120  tmp = m_IoSpace.read32(LAPIC_REG_TASK_PRIORITY);
121  m_IoSpace.write32(tmp & 0xFFFFFF00, LAPIC_REG_TASK_PRIORITY);
122 
123  // Set the LVT error register
124  tmp = m_IoSpace.read32(LAPIC_REG_LVT_ERROR);
125  m_IoSpace.write32((tmp & 0xFFFEEF00) | ERROR_VECTOR, LAPIC_REG_LVT_ERROR);
126 
127  if (!m_BusFrequency)
128  {
129  // Divide by 16
130  m_IoSpace.write32(0x3, LAPIC_REG_DIVIDE_CONFIG);
131 
132  // Set the maximum count so we can calculate the frequency without this
133  // rolling over.
134  m_IoSpace.write32(0xFFFFFFFF, LAPIC_REG_INITIAL_COUNT);
135 
136  // This should be approximately 10000 useconds (10 ms).
137  for (size_t i = 0; i < 10000; ++i)
138  {
139  uint8_t a = 0;
140  __asm__ __volatile__("outb %0, %1" ::"a"(a), "Nd"(0x80));
141  }
142  uint32_t out = m_IoSpace.read32(LAPIC_REG_CURRENT_COUNT);
143 
144  uint32_t ticks = 0xFFFFFFFFU - out;
145 
146  // We want the bus frequency to be in Hz (ticks/second).
147  m_BusFrequency = ticks * 100U;
148  }
149 
150  // Set the LVT timer register.
151  m_IoSpace.write32(LAPIC_TIMER_PERIODIC | TIMER_VECTOR, LAPIC_REG_LVT_TIMER);
152 
153  // Initialise the intial-count register
154  m_IoSpace.write32(m_BusFrequency / INITIAL_HZ, LAPIC_REG_INITIAL_COUNT);
155 
156  // Initialise the divisor register. (Divide by 16)
157  m_IoSpace.write32(0x3, LAPIC_REG_DIVIDE_CONFIG);
158 
159  // TODO
160 
161  return true;
162 }
163 
164 void LocalApic::interProcessorInterrupt(
165  uint8_t destinationApicId, uint8_t vector, size_t deliveryMode,
166  bool bAssert, bool bLevelTriggered)
167 {
168  while ((m_IoSpace.read32(LAPIC_REG_INT_CMD_LOW) & 0x1000) != 0)
169  ;
170 
171  m_IoSpace.write32(destinationApicId << 24, LAPIC_REG_INT_CMD_HIGH);
172  m_IoSpace.write32(
173  vector | (deliveryMode << 8) | (bAssert ? (1 << 14) : 0) |
174  (bLevelTriggered ? (1 << 15) : 0),
175  LAPIC_REG_INT_CMD_LOW);
176 }
177 
178 void LocalApic::interProcessorInterruptAllExcludingThis(
179  uint8_t vector, size_t deliveryMode)
180 {
181  while ((m_IoSpace.read32(LAPIC_REG_INT_CMD_LOW) & 0x1000) != 0)
182  ;
183 
184  m_IoSpace.write32(
185  vector | (deliveryMode << 8) | (1 << 14) | (0x3 << 18),
186  LAPIC_REG_INT_CMD_LOW);
187 }
188 
189 uint8_t LocalApic::getId()
190 {
191  return ((m_IoSpace.read32(LAPIC_REG_ID) >> 24) & 0xFF);
192 }
193 
194 bool LocalApic::check(uint64_t physicalAddress)
195 {
196  // Check whether the Local APIC is enabled or not
197  if ((Processor::readMachineSpecificRegister(0x1B) & 0x800) == 0)
198  {
199  ERROR("Local APIC: Disabled");
200  return false;
201  }
202 
203  // Check Local APIC base address
204  if ((Processor::readMachineSpecificRegister(0x1B) & 0xFFFFFF000ULL) !=
205  physicalAddress)
206  {
207  ERROR("Local APIC: Wrong physical address");
208  return false;
209  }
210 
211  return true;
212 }
213 
214 void LocalApic::interrupt(size_t nInterruptNumber, InterruptState &state)
215 {
216  if (nInterruptNumber == TIMER_VECTOR)
217  {
218  // Ack early, timer() may not return for quite some time (if it
219  // schedules).
220  ack();
221 
222  TimerHandler *handler = m_Handlers.lookup(Processor::id());
223  // TODO: Delta is wrong.
224  if (LIKELY(handler != 0))
225  {
226  // NOTICE("Timer " << Processor::id());
227  handler->timer(0, state);
228  }
229  }
230 
231  // The halt IPI is used in the debugger to stop all other cores.
232  if (nInterruptNumber == IPI_HALT_VECTOR)
233  {
234  NOTICE("Halting processor #" << Dec << Processor::id());
235  Processor::halt();
236  }
237 }
238 
239 void LocalApic::ack()
240 {
241  // Send EOI.
242  m_IoSpace.write32(0x00000000, LAPIC_REG_EOI);
243 }
244 
245 #endif
static uint64_t readMachineSpecificRegister(uint32_t index)
static void cpuid(uint32_t inEax, uint32_t inEcx, uint32_t &eax, uint32_t &ebx, uint32_t &ecx, uint32_t &edx)
static PhysicalMemoryManager & instance()
uintptr_t physicalAddress(physical_uintptr_t address) PURE
Definition: utils.h:38
#define NOTICE(text)
Definition: Log.h:74
virtual void timer(uint64_t delta, InterruptState &state)=0
static void halt()
static ProcessorId id()
Definition: Processor.cc:40
virtual bool allocateRegion(MemoryRegion &Region, size_t cPages, size_t pageConstraints, size_t Flags, physical_uintptr_t start=-1)=0
#define ERROR(text)
Definition: Log.h:82
Definition: Log.h:138
static InterruptManager & instance()