interrupts.cc revision 5651:7f0c8006c3d7
1/* 2 * Copyright (c) 2008 The Hewlett-Packard Development Company 3 * All rights reserved. 4 * 5 * Redistribution and use of this software in source and binary forms, 6 * with or without modification, are permitted provided that the 7 * following conditions are met: 8 * 9 * The software must be used only for Non-Commercial Use which means any 10 * use which is NOT directed to receiving any direct monetary 11 * compensation for, or commercial advantage from such use. Illustrative 12 * examples of non-commercial use are academic research, personal study, 13 * teaching, education and corporate research & development. 14 * Illustrative examples of commercial use are distributing products for 15 * commercial advantage and providing services using the software for 16 * commercial advantage. 17 * 18 * If you wish to use this software or functionality therein that may be 19 * covered by patents for commercial use, please contact: 20 * Director of Intellectual Property Licensing 21 * Office of Strategy and Technology 22 * Hewlett-Packard Company 23 * 1501 Page Mill Road 24 * Palo Alto, California 94304 25 * 26 * Redistributions of source code must retain the above copyright notice, 27 * this list of conditions and the following disclaimer. Redistributions 28 * in binary form must reproduce the above copyright notice, this list of 29 * conditions and the following disclaimer in the documentation and/or 30 * other materials provided with the distribution. Neither the name of 31 * the COPYRIGHT HOLDER(s), HEWLETT-PACKARD COMPANY, nor the names of its 32 * contributors may be used to endorse or promote products derived from 33 * this software without specific prior written permission. No right of 34 * sublicense is granted herewith. Derivatives of the software and 35 * output created using the software may be prepared, but only for 36 * Non-Commercial Uses. Derivatives of the software may be shared with 37 * others provided: (i) the others agree to abide by the list of 38 * conditions herein which includes the Non-Commercial Use restrictions; 39 * and (ii) such Derivatives of the software include the above copyright 40 * notice to acknowledge the contribution from this software where 41 * applicable, this list of conditions and the disclaimer below. 42 * 43 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 44 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 45 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 46 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 47 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 48 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 49 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 50 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 51 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 52 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 53 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 54 * 55 * Authors: Gabe Black 56 */ 57 58#include "arch/x86/apicregs.hh" 59#include "arch/x86/interrupts.hh" 60#include "cpu/base.hh" 61 62int 63divideFromConf(uint32_t conf) 64{ 65 // This figures out what division we want from the division configuration 66 // register in the local APIC. The encoding is a little odd but it can 67 // be deciphered fairly easily. 68 int shift = ((conf & 0x8) >> 1) | (conf & 0x3); 69 shift = (shift + 1) % 8; 70 return 1 << shift; 71} 72 73namespace X86ISA 74{ 75 76ApicRegIndex 77decodeAddr(Addr paddr) 78{ 79 ApicRegIndex regNum; 80 paddr &= ~mask(3); 81 switch (paddr) 82 { 83 case 0x20: 84 regNum = APIC_ID; 85 break; 86 case 0x30: 87 regNum = APIC_VERSION; 88 break; 89 case 0x80: 90 regNum = APIC_TASK_PRIORITY; 91 break; 92 case 0x90: 93 regNum = APIC_ARBITRATION_PRIORITY; 94 break; 95 case 0xA0: 96 regNum = APIC_PROCESSOR_PRIORITY; 97 break; 98 case 0xB0: 99 regNum = APIC_EOI; 100 break; 101 case 0xD0: 102 regNum = APIC_LOGICAL_DESTINATION; 103 break; 104 case 0xE0: 105 regNum = APIC_DESTINATION_FORMAT; 106 break; 107 case 0xF0: 108 regNum = APIC_SPURIOUS_INTERRUPT_VECTOR; 109 break; 110 case 0x100: 111 case 0x108: 112 case 0x110: 113 case 0x118: 114 case 0x120: 115 case 0x128: 116 case 0x130: 117 case 0x138: 118 case 0x140: 119 case 0x148: 120 case 0x150: 121 case 0x158: 122 case 0x160: 123 case 0x168: 124 case 0x170: 125 case 0x178: 126 regNum = APIC_IN_SERVICE((paddr - 0x100) / 0x8); 127 break; 128 case 0x180: 129 case 0x188: 130 case 0x190: 131 case 0x198: 132 case 0x1A0: 133 case 0x1A8: 134 case 0x1B0: 135 case 0x1B8: 136 case 0x1C0: 137 case 0x1C8: 138 case 0x1D0: 139 case 0x1D8: 140 case 0x1E0: 141 case 0x1E8: 142 case 0x1F0: 143 case 0x1F8: 144 regNum = APIC_TRIGGER_MODE((paddr - 0x180) / 0x8); 145 break; 146 case 0x200: 147 case 0x208: 148 case 0x210: 149 case 0x218: 150 case 0x220: 151 case 0x228: 152 case 0x230: 153 case 0x238: 154 case 0x240: 155 case 0x248: 156 case 0x250: 157 case 0x258: 158 case 0x260: 159 case 0x268: 160 case 0x270: 161 case 0x278: 162 regNum = APIC_INTERRUPT_REQUEST((paddr - 0x200) / 0x8); 163 break; 164 case 0x280: 165 regNum = APIC_ERROR_STATUS; 166 break; 167 case 0x300: 168 regNum = APIC_INTERRUPT_COMMAND_LOW; 169 break; 170 case 0x310: 171 regNum = APIC_INTERRUPT_COMMAND_HIGH; 172 break; 173 case 0x320: 174 regNum = APIC_LVT_TIMER; 175 break; 176 case 0x330: 177 regNum = APIC_LVT_THERMAL_SENSOR; 178 break; 179 case 0x340: 180 regNum = APIC_LVT_PERFORMANCE_MONITORING_COUNTERS; 181 break; 182 case 0x350: 183 regNum = APIC_LVT_LINT0; 184 break; 185 case 0x360: 186 regNum = APIC_LVT_LINT1; 187 break; 188 case 0x370: 189 regNum = APIC_LVT_ERROR; 190 break; 191 case 0x380: 192 regNum = APIC_INITIAL_COUNT; 193 break; 194 case 0x390: 195 regNum = APIC_CURRENT_COUNT; 196 break; 197 case 0x3E0: 198 regNum = APIC_DIVIDE_CONFIGURATION; 199 break; 200 default: 201 // A reserved register field. 202 panic("Accessed reserved register field %#x.\n", paddr); 203 break; 204 } 205 return regNum; 206} 207} 208 209Tick 210X86ISA::Interrupts::read(PacketPtr pkt) 211{ 212 Addr offset = pkt->getAddr() - pioAddr; 213 //Make sure we're at least only accessing one register. 214 if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) 215 panic("Accessed more than one register at a time in the APIC!\n"); 216 ApicRegIndex reg = decodeAddr(offset); 217 uint32_t val = htog(readReg(reg)); 218 DPRINTF(LocalApic, 219 "Reading Local APIC register %d at offset %#x as %#x.\n", 220 reg, offset, val); 221 pkt->setData(((uint8_t *)&val) + (offset & mask(3))); 222 return latency; 223} 224 225Tick 226X86ISA::Interrupts::write(PacketPtr pkt) 227{ 228 Addr offset = pkt->getAddr() - pioAddr; 229 //Make sure we're at least only accessing one register. 230 if ((offset & ~mask(3)) != ((offset + pkt->getSize()) & ~mask(3))) 231 panic("Accessed more than one register at a time in the APIC!\n"); 232 ApicRegIndex reg = decodeAddr(offset); 233 uint32_t val = regs[reg]; 234 pkt->writeData(((uint8_t *)&val) + (offset & mask(3))); 235 DPRINTF(LocalApic, 236 "Writing Local APIC register %d at offset %#x as %#x.\n", 237 reg, offset, gtoh(val)); 238 setReg(reg, gtoh(val)); 239 return latency; 240} 241 242Tick 243X86ISA::Interrupts::recvMessage(PacketPtr pkt) 244{ 245 Addr offset = pkt->getAddr() - x86InterruptAddress(0, 0); 246 assert(pkt->cmd == MemCmd::MessageReq); 247 switch(offset) 248 { 249 case 0: 250 DPRINTF(LocalApic, "Got Trigger Interrupt message.\n"); 251 break; 252 default: 253 panic("Local apic got unknown interrupt message at offset %#x.\n", 254 offset); 255 break; 256 } 257 delete pkt->req; 258 delete pkt; 259 return latency; 260} 261 262 263uint32_t 264X86ISA::Interrupts::readReg(ApicRegIndex reg) 265{ 266 if (reg >= APIC_TRIGGER_MODE(0) && 267 reg <= APIC_TRIGGER_MODE(15)) { 268 panic("Local APIC Trigger Mode registers are unimplemented.\n"); 269 } 270 switch (reg) { 271 case APIC_ARBITRATION_PRIORITY: 272 panic("Local APIC Arbitration Priority register unimplemented.\n"); 273 break; 274 case APIC_PROCESSOR_PRIORITY: 275 panic("Local APIC Processor Priority register unimplemented.\n"); 276 break; 277 case APIC_EOI: 278 panic("Local APIC EOI register unimplemented.\n"); 279 break; 280 case APIC_ERROR_STATUS: 281 regs[APIC_INTERNAL_STATE] &= ~ULL(0x1); 282 break; 283 case APIC_INTERRUPT_COMMAND_LOW: 284 panic("Local APIC Interrupt Command low" 285 " register unimplemented.\n"); 286 break; 287 case APIC_INTERRUPT_COMMAND_HIGH: 288 panic("Local APIC Interrupt Command high" 289 " register unimplemented.\n"); 290 break; 291 case APIC_CURRENT_COUNT: 292 { 293 assert(clock); 294 uint32_t val = regs[reg] - curTick / clock; 295 val /= (16 * divideFromConf(regs[APIC_DIVIDE_CONFIGURATION])); 296 return val; 297 } 298 default: 299 break; 300 } 301 return regs[reg]; 302} 303 304void 305X86ISA::Interrupts::setReg(ApicRegIndex reg, uint32_t val) 306{ 307 uint32_t newVal = val; 308 if (reg >= APIC_IN_SERVICE(0) && 309 reg <= APIC_IN_SERVICE(15)) { 310 panic("Local APIC In-Service registers are unimplemented.\n"); 311 } 312 if (reg >= APIC_TRIGGER_MODE(0) && 313 reg <= APIC_TRIGGER_MODE(15)) { 314 panic("Local APIC Trigger Mode registers are unimplemented.\n"); 315 } 316 if (reg >= APIC_INTERRUPT_REQUEST(0) && 317 reg <= APIC_INTERRUPT_REQUEST(15)) { 318 panic("Local APIC Interrupt Request registers " 319 "are unimplemented.\n"); 320 } 321 switch (reg) { 322 case APIC_ID: 323 newVal = val & 0xFF; 324 break; 325 case APIC_VERSION: 326 // The Local APIC Version register is read only. 327 return; 328 case APIC_TASK_PRIORITY: 329 newVal = val & 0xFF; 330 break; 331 case APIC_ARBITRATION_PRIORITY: 332 panic("Local APIC Arbitration Priority register unimplemented.\n"); 333 break; 334 case APIC_PROCESSOR_PRIORITY: 335 panic("Local APIC Processor Priority register unimplemented.\n"); 336 break; 337 case APIC_EOI: 338 panic("Local APIC EOI register unimplemented.\n"); 339 break; 340 case APIC_LOGICAL_DESTINATION: 341 newVal = val & 0xFF000000; 342 break; 343 case APIC_DESTINATION_FORMAT: 344 newVal = val | 0x0FFFFFFF; 345 break; 346 case APIC_SPURIOUS_INTERRUPT_VECTOR: 347 regs[APIC_INTERNAL_STATE] &= ~ULL(1 << 1); 348 regs[APIC_INTERNAL_STATE] |= val & (1 << 8); 349 if (val & (1 << 9)) 350 warn("Focus processor checking not implemented.\n"); 351 break; 352 case APIC_ERROR_STATUS: 353 { 354 if (regs[APIC_INTERNAL_STATE] & 0x1) { 355 regs[APIC_INTERNAL_STATE] &= ~ULL(0x1); 356 newVal = 0; 357 } else { 358 regs[APIC_INTERNAL_STATE] |= ULL(0x1); 359 return; 360 } 361 362 } 363 break; 364 case APIC_INTERRUPT_COMMAND_LOW: 365 panic("Local APIC Interrupt Command low" 366 " register unimplemented.\n"); 367 break; 368 case APIC_INTERRUPT_COMMAND_HIGH: 369 panic("Local APIC Interrupt Command high" 370 " register unimplemented.\n"); 371 break; 372 case APIC_LVT_TIMER: 373 case APIC_LVT_THERMAL_SENSOR: 374 case APIC_LVT_PERFORMANCE_MONITORING_COUNTERS: 375 case APIC_LVT_LINT0: 376 case APIC_LVT_LINT1: 377 case APIC_LVT_ERROR: 378 { 379 uint64_t readOnlyMask = (1 << 12) | (1 << 14); 380 newVal = (val & ~readOnlyMask) | 381 (regs[reg] & readOnlyMask); 382 } 383 break; 384 case APIC_INITIAL_COUNT: 385 { 386 assert(clock); 387 newVal = bits(val, 31, 0); 388 uint32_t newCount = newVal * 389 (divideFromConf(regs[APIC_DIVIDE_CONFIGURATION]) * 16); 390 regs[APIC_CURRENT_COUNT] = newCount + curTick / clock; 391 // Find out how long a "tick" of the timer should take. 392 Tick timerTick = 16 * clock; 393 // Schedule on the edge of the next tick plus the new count. 394 Tick offset = curTick % timerTick; 395 if (offset) { 396 reschedule(apicTimerEvent, 397 curTick + (newCount + 1) * timerTick - offset, true); 398 } else { 399 reschedule(apicTimerEvent, 400 curTick + newCount * timerTick, true); 401 } 402 } 403 break; 404 case APIC_CURRENT_COUNT: 405 //Local APIC Current Count register is read only. 406 return; 407 case APIC_DIVIDE_CONFIGURATION: 408 newVal = val & 0xB; 409 break; 410 default: 411 break; 412 } 413 regs[reg] = newVal; 414 return; 415} 416 417X86ISA::Interrupts * 418X86LocalApicParams::create() 419{ 420 return new X86ISA::Interrupts(this); 421} 422