File: apic.cc

package info (click to toggle)
bochs 2.3-2etch1
  • links: PTS
  • area: main
  • in suites: etch
  • size: 14,116 kB
  • ctags: 16,927
  • sloc: cpp: 130,524; ansic: 18,822; sh: 7,922; makefile: 3,836; yacc: 1,056; asm: 463; perl: 381; lex: 280; csh: 3
file content (987 lines) | stat: -rw-r--r-- 29,097 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
/////////////////////////////////////////////////////////////////////////
// $Id: apic.cc,v 1.94 2006/06/20 16:51:03 sshwarts Exp $
/////////////////////////////////////////////////////////////////////////
//
//  Copyright (C) 2001  MandrakeSoft S.A.
//
//    MandrakeSoft S.A.
//    43, rue d'Aboukir
//    75002 Paris - France
//    http://www.linux-mandrake.com/
//    http://www.mandrakesoft.com/
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
//

#define NEED_CPU_REG_SHORTCUTS 1
#include "bochs.h"
#include "cpu.h"
#include "iodev/iodev.h"

#if BX_SUPPORT_APIC

#define LOG_THIS this->

#define APIC_BROADCAST_PHYSICAL_DESTINATION_MODE (APIC_MAX_ID)

///////////// APIC BUS /////////////

int apic_bus_deliver_interrupt(Bit8u vector, Bit8u dest, Bit8u delivery_mode, Bit8u dest_mode, bx_bool level, bx_bool trig_mode)
{
  if(delivery_mode == APIC_DM_LOWPRI)
  {
     if(dest_mode == 0) {
       // I/O subsytem initiated interrupt with lowest priority delivery
       // mode is not supported in physical destination mode
//     BX_ERROR(("Ignoring lowest priority interrupt in physical dest mode !"));
       return 0;
     }
     else {
       return apic_bus_deliver_lowest_priority(vector, dest, trig_mode, 0);
     }
  }

  // determine destination local apics and deliver
  if(dest_mode == 0) {
    if(dest == APIC_BROADCAST_PHYSICAL_DESTINATION_MODE)
    {
       return apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
    }
    else {
       // the destination is single agent
       for (unsigned i=0;i<BX_NUM_LOCAL_APICS;i++)
       {
         if(BX_CPU_APIC(i)->get_id() == dest) {
           BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
           return 1;
         }
       }

       return 0;
    }
  } 
  else {
    // logical destination mode
    if(dest == 0) return 0;

    bx_bool interrupt_delivered = 0;

    for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
      if(BX_CPU_APIC(i)->match_logical_addr(dest)) {
        BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
        interrupt_delivered = 1;
      }
    }

    return interrupt_delivered;
  }
}

int apic_bus_deliver_lowest_priority(Bit8u vector, Bit8u dest, bx_bool trig_mode, bx_bool broadcast)
{
#ifndef BX_IMPLEMENT_XAPIC
  // search for if focus processor exists
  for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
    if(BX_CPU_APIC(i)->is_focus(vector)) {
      BX_CPU_APIC(i)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
      return 1;
    }
  }
#endif

  // focus processor not found, looking for lowest priority agent
  int lowest_priority_agent = -1, lowest_priority = 0x100;

  for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
    if(broadcast || BX_CPU_APIC(i)->match_logical_addr(dest)) {
#ifndef BX_IMPLEMENT_XAPIC
      int priority = BX_CPU_APIC(i)->get_apr();
#else
      int priority = BX_CPU_APIC(i)->get_tpr();
#endif
      if(priority < lowest_priority) {
        lowest_priority = priority;
        lowest_priority_agent = i;
      }
    }
  }

  if(lowest_priority_agent >= 0)
  {
    BX_CPU_APIC(lowest_priority_agent)->deliver(vector, APIC_DM_LOWPRI, trig_mode);
    return 1;
  }

  return 0;
}

int apic_bus_broadcast_interrupt(Bit8u vector, Bit8u delivery_mode, bx_bool trig_mode, int exclude_cpu)
{
  if(delivery_mode == APIC_DM_LOWPRI)
  {
    return apic_bus_deliver_lowest_priority(vector, 0 /* doesn't matter */, trig_mode, 1);
  }

  // deliver to all bus agents except 'exclude_cpu'
  for (int i=0; i<BX_NUM_LOCAL_APICS; i++) {
    if(i == exclude_cpu) continue;
    BX_CPU_APIC(i)->deliver(vector, delivery_mode, trig_mode);
  }

  return 1;
}

static void apic_bus_broadcast_eoi(Bit8u vector)
{
  bx_devices.ioapic->receive_eoi(vector);
}

////////////////////////////////////

bx_generic_apic_c::bx_generic_apic_c(bx_phy_address base)
{
  put("APIC?");
  settype(APICLOG);
  id = APIC_UNKNOWN_ID;
  set_base(base);
}

void bx_generic_apic_c::set_base(bx_phy_address newbase)
{
  newbase &= (~0xfff);
  base_addr = newbase;
  if (id != APIC_UNKNOWN_ID)
    BX_INFO(("relocate APIC id=%d to 0x%08x", id, newbase));
}

void bx_generic_apic_c::set_id(Bit8u newid) 
{
  BX_INFO(("set APIC ID to %d", newid));
  id = newid;
}

bx_bool bx_generic_apic_c::is_selected(bx_phy_address addr, unsigned len)
{
  if((addr & ~0xfff) == get_base()) {
    if((addr & 0xf) != 0)
      BX_INFO(("warning: misaligned APIC access. addr=0x%08x, len=%d", addr, len));
    return 1;
  }
  return 0;
}

void bx_generic_apic_c::read(bx_phy_address addr, void *data, unsigned len)
{
  if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
    BX_PANIC(("APIC read at address 0x%08x spans 32-bit boundary !", addr));
    return;
  }
  Bit32u value;
  read_aligned(addr & ~0x3, &value);
  if(len == 4) { // must be 32-bit aligned
    *((Bit32u *)data) = value;
    return;
  }
  // handle partial read, independent of endian-ness
  value >>= (addr&3)*8;
  if (len == 1)
    *((Bit8u *) data) = value & 0xff;
  else if (len == 2)
    *((Bit16u *)data) = value & 0xffff;
  else
    BX_PANIC(("Unsupported APIC read at address 0x%08x, len=%d", addr, len));
}

void bx_generic_apic_c::write(bx_phy_address addr, void *data, unsigned len)
{
  if((addr & ~0x3) != ((addr+len-1) & ~0x3)) {
    BX_PANIC(("APIC write at address 0x%08x spans 32-bit boundary !", addr));
    return;
  }
  bx_phy_address addr_aligned = addr & ~0x3;
  if(len == 4) { // must be 32-bit aligned
    write_aligned(addr_aligned, (Bit32u*) data);
    return;
  }
  // partial write to the apic register, need to update some bytes 
  // and do not touch the others, i.e. to do RMW operation
  Bit32u value;
  read_aligned(addr_aligned, &value);  // apic read has no side effects
  // handle partial write, independent of endian-ness
  unsigned shift = (addr&3)*8;
  if (len == 1) {
    value &= ~(0xff << shift);
    value |= (*((Bit8u *) data) << shift);
  }
  else if (len == 2) {
    value &= ~(0xffff << shift);
    value |= (*((Bit16u *)data) << shift);
  }
  else {
    BX_PANIC(("Unsupported APIC write at address 0x%08x, len=%d", addr, len));
  }
  write_aligned(addr_aligned, &value);
}

bx_local_apic_c::bx_local_apic_c(BX_CPU_C *mycpu)
  : bx_generic_apic_c(BX_LAPIC_BASE_ADDR), cpu(mycpu), cpu_id(cpu->which_cpu())
{
  // KPL: Register a non-active timer for use when the timer is started.
  timer_handle = bx_pc_system.register_timer_ticks(this,
            BX_CPU(0)->local_apic.periodic_smf, 0, 0, 0, "lapic");
  timer_active = 0;

  reset();
	
  INTR = 0;
}

void bx_local_apic_c::reset() 
{
  /* same as INIT but also sets arbitration ID and APIC ID */
  init();
}

void bx_local_apic_c::init()
{
  int i;

  bx_generic_apic_c::init();

  BX_INFO(("local apic in %s initializing",
     (cpu && cpu->name) ? cpu->name : "?"));

  // default address for a local APIC, can be moved
  base_addr = BX_LAPIC_BASE_ADDR;
  error_status = shadow_error_status = 0;
  log_dest = 0;
  dest_format = 0xf;
  icr_hi = 0;
  icr_lo = 0;
  log_dest = 0;
  task_priority = 0;

  for(i=0; i<BX_LOCAL_APIC_MAX_INTS; i++) {
    irr[i] = isr[i] = tmr[i] = 0;
  }

  timer_divconf = 0;
  timer_divide_factor = 1;
  timer_initial = 0;
  timer_current = 0;

  if(timer_active) {
    bx_pc_system.deactivate_timer(timer_handle);
    timer_active = 0;
  }

  for(i=0; i<APIC_LVT_ENTRIES; i++) {
    lvt[i] = 0x10000;	// all LVT are masked
  }

  spurious_vector  = 0xff;   // software disabled(bit 8)
  software_enabled = 0;
  focus_disable    = 0;
}

void bx_local_apic_c::set_id(Bit8u newid)
{
  bx_generic_apic_c::set_id(newid);
  sprintf(cpu->name, "CPU apicid=%02x",(Bit32u)id);
  if(id < APIC_MAX_ID) {
    char buffer[16];
    sprintf(buffer, "APIC%x", id);
    put(buffer);
    settype(CPU0LOG + id);
    sprintf(buffer, "CPU%x", id);
    cpu->put(buffer);
  } else {
    BX_INFO(("naming convention for apics requires id=0-%d only", APIC_MAX_ID));
  }
  if(BX_CPU_LEVEL<2)
    BX_INFO(("8086"));
  else
    BX_INFO(("80%d86", BX_CPU_LEVEL));
}

// APIC write: 4 byte write to 16-byte aligned APIC address
void bx_local_apic_c::write_aligned(bx_phy_address addr, Bit32u *data)
{
  BX_DEBUG(("%s: LAPIC write 0x%08x to address %08x", cpu->name, *data, addr));
  BX_ASSERT((addr & 0xf) == 0);
  addr &= 0xff0;
  Bit32u value = *data;
  switch(addr) {
    case 0x20: // local APIC id
      id = (value>>24) & APIC_ID_MASK;
      break;
    case 0x80: // task priority
      set_tpr(value & 0xff);
      break;
    case 0xb0: // EOI
      receive_EOI(value);
      break;
    case 0xd0: // logical destination
      log_dest = (value >> 24) & APIC_ID_MASK;
      BX_DEBUG(("set logical destination to %02x", log_dest));
      break;
    case 0xe0: // destination format
      dest_format = (value >> 28) & 0xf;
      BX_DEBUG(("set destination format to %02x", dest_format));
      break;
    case 0xf0: // spurious interrupt vector
      write_spurious_interrupt_register(value);
      break;
    case 0x280: // error status reg
      // Here's what the IA-devguide-3 says on p.7-45:
      // The ESR is a read/write register and is reset after being written to
      // by the processor. A write to the ESR must be done just prior to
      // reading the ESR to allow the register to be updated.
      error_status = shadow_error_status;
      shadow_error_status = 0;
      break;
    case 0x300: // interrupt command reg 0-31
      icr_lo = value & ~(1<<12);  // force delivery status bit = 0(idle)
      send_ipi();
      break;
    case 0x310: // interrupt command reg 31-63
      icr_hi = value & 0xff000000;
      break;
    case 0x320: // LVT Timer Reg
      lvt[APIC_LVT_TIMER] = value & 0x300ff;
      if(! software_enabled) lvt[APIC_LVT_TIMER] |= 0x10000;
      break;
    case 0x330: // LVT Thermal Monitor
      lvt[APIC_LVT_THERMAL] = value & 0x107ff;
      if(! software_enabled) lvt[APIC_LVT_THERMAL] |= 0x10000;
      break;
    case 0x340: // LVT Performance Counter
      lvt[APIC_LVT_PERFORM] = value & 0x107ff;
      if(! software_enabled) lvt[APIC_LVT_PERFORM] |= 0x10000;
      break;
    case 0x350: // LVT LINT0 Reg
      lvt[APIC_LVT_LINT0] = value & 0x1a7ff;
      if(! software_enabled) lvt[APIC_LVT_LINT0] |= 0x10000;
      break;
    case 0x360: // LVT Lint1 Reg
      lvt[APIC_LVT_LINT1] = value & 0x1a7ff;
      if(! software_enabled) lvt[APIC_LVT_LINT1] |= 0x10000;
      break;
    case 0x370: // LVT Error Reg
      lvt[APIC_LVT_ERROR] = value & 0x100ff;
      if(! software_enabled) lvt[APIC_LVT_ERROR] |= 0x10000;
      break;
    case 0x380: // initial count for timer
      set_initial_timer_count(value);
      break;
    case 0x3e0: // timer divide configuration
      // only bits 3, 1, and 0 are writable
      timer_divconf = value & 0xb;
      set_divide_configuration(timer_divconf);
      break;
    /* all read-only registers go here */
    case 0x30: // local APIC version
    case 0x90: // arbitration priority
    case 0xa0: // processor priority
    // ISRs not writable
    case 0x100: case 0x110: case 0x120: case 0x130:
    case 0x140: case 0x150: case 0x160: case 0x170:
    // TMRs not writable
    case 0x180: case 0x190: case 0x1a0: case 0x1b0:
    case 0x1c0: case 0x1d0: case 0x1e0: case 0x1f0:
    // IRRs not writable
    case 0x200: case 0x210: case 0x220: case 0x230:
    case 0x240: case 0x250: case 0x260: case 0x270:
      // current count for timer
    case 0x390:
      // all read-only registers should fall into this line
      BX_INFO(("warning: write to read-only APIC register 0x%02x", addr));
      break;
    default:
      shadow_error_status |= APIC_ERR_ILLEGAL_ADDR;
      // but for now I want to know about it in case I missed some.
      BX_PANIC(("APIC register %08x not implemented", addr));
  }
}

void bx_local_apic_c::send_ipi(void)
{
  int dest = (icr_hi >> 24) & 0xff;
  int dest_shorthand = (icr_lo >> 18) & 3;
  int trig_mode = (icr_lo >> 15) & 1;
  int level = (icr_lo >> 14) & 1;
  int dest_mode = (icr_lo >> 11) & 1;
  int delivery_mode = (icr_lo >> 8) & 7;
  int vector = (icr_lo & 0xff);
  int accepted = 0;

  if(delivery_mode == APIC_DM_INIT)
  {
    if(level == 0 && trig_mode == 1) {
      // special mode in local apic.  See "INIT Level Deassert" in the
      // Intel Soft. Devel. Guide Vol 3, page 7-34.  This magic code
      // causes all APICs(regardless of dest address) to set their
      // arbitration ID to their APIC ID. Not supported by Pentium 4
      // and Intel Xeon processors.
      return; // we not model APIC bus arbitration ID anyway
    }
  }

  switch(dest_shorthand) {
  case 0:  // no shorthand, use real destination value
    accepted = apic_bus_deliver_interrupt(vector, dest, delivery_mode, dest_mode, level, trig_mode);
    break;
  case 1:  // self
    trigger_irq(vector, trig_mode);
    accepted = 1;
    break;
  case 2:  // all including self
    accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, APIC_MAX_ID);
    break;
  case 3:  // all but self
    accepted = apic_bus_broadcast_interrupt(vector, delivery_mode, trig_mode, get_id());
    break;
  default:
    BX_PANIC(("Invalid desination shorthand %#x\n", dest_shorthand));
  }

  if(! accepted) {
    BX_DEBUG(("An IPI wasn't accepted, raise APIC_ERR_TX_ACCEPT_ERR"));
    shadow_error_status |= APIC_ERR_TX_ACCEPT_ERR;
  }
}

void bx_local_apic_c::write_spurious_interrupt_register(Bit32u value)
{
  BX_DEBUG(("write %08x to spurious interrupt register", value));

#ifdef BX_IMPLEMENT_XAPIC
  spurious_vector = value & 0xff;
#else
  // bits 0-3 of the spurious vector hardwired to '1
  spurious_vector = (value & 0xf0) | 0xf;
#endif

  software_enabled = (value >> 8) & 1;
  focus_disable    = (value >> 9) & 1;

  if(! software_enabled) {
    for(unsigned i=0; i<APIC_LVT_ENTRIES; i++) {
      lvt[i] |= 0x10000;	// all LVT are masked
    }
  }
}

void bx_local_apic_c::receive_EOI(Bit32u value)
{
  BX_DEBUG(("%s: Wrote 0x%x to EOI", cpu->name, value));
  int vec = highest_priority_int(isr);
  if(vec < 0) {
    BX_DEBUG(("EOI written without any bit in ISR"));
  } else {
      if((Bit32u) vec != spurious_vector) {
        BX_DEBUG(("%s: local apic received EOI, hopefully for vector 0x%02x", cpu->name, vec));
        isr[vec] = 0; 
        if(tmr[vec]) {
            apic_bus_broadcast_eoi(vec);
            tmr[vec] = 0;
        }
        service_local_apic();
      }
  }

  if(bx_dbg.apic)
      print_status();
}

void bx_local_apic_c::startup_msg(Bit32u vector)
{
  if(cpu->debug_trap & BX_DEBUG_TRAP_HALT_STATE) {
    cpu->debug_trap &= ~BX_DEBUG_TRAP_HALT_STATE;
    cpu->dword.eip = 0;
    cpu->load_seg_reg(&cpu->sregs[BX_SEG_REG_CS], vector*0x100);
    BX_INFO(("%s started up at %04X:%08X by APIC", cpu->name, vector*0x100, cpu->dword.eip));
  } else {
    BX_INFO(("%s started up by APIC, but was not halted at the time", cpu->name));
  }
}

// APIC read: 4 byte read from 16-byte aligned APIC address
void bx_local_apic_c::read_aligned(bx_phy_address addr, Bit32u *data)
{
  BX_DEBUG(("%s: LAPIC read from address %08x", cpu->name, addr));
  BX_ASSERT((addr & 0xf) == 0);
  *data = 0;  // default value for unimplemented registers
  bx_phy_address addr2 = addr & 0xff0;
  switch(addr2) {
  case 0x20: // local APIC id
    *data = (id) << 24; break;
  case 0x30: // local APIC version
    *data = BX_LAPIC_VERSION_ID; break;
  case 0x80: // task priority
    *data = task_priority & 0xff; break;
  case 0x90: // arbitration priority
    *data = get_apr(); break;
  case 0xa0: // processor priority
    *data = get_ppr(); break;
  case 0xb0: // EOI
    /*
     * Read-modify-write operations should operate without generating
     * exceptions, and are used by some operating systems to EOI.
     * The results of reads should be ignored by the OS.
     */
    break;
  case 0xd0: // logical destination
    *data = (log_dest & APIC_ID_MASK) << 24; break;
  case 0xe0: // destination format
    *data = ((dest_format & 0xf) << 24) | 0x0fffffff; break;
  case 0xf0: // spurious interrupt vector
    {
      Bit32u reg = spurious_vector;
      if(software_enabled) reg |= 0x100;
      if(focus_disable) reg |= 0x200;
      *data = reg; 
    }
    break;
  case 0x100: case 0x110:
  case 0x120: case 0x130:
  case 0x140: case 0x150:
  case 0x160: case 0x170:
    {
      unsigned index = (addr2 - 0x100) << 1;
      Bit32u value = 0, mask = 1;
      for(int i=0;i<32;i++) {
        if(isr[index+i]) value |= mask;
        mask <<= 1;
      }
      *data = value;
    }
    break;
  case 0x180: case 0x190:
  case 0x1a0: case 0x1b0:
  case 0x1c0: case 0x1d0:
  case 0x1e0: case 0x1f0:
    {
      unsigned index = (addr2 - 0x180) << 1;
      Bit32u value = 0, mask = 1;
      for(int i=0;i<32;i++) {
        if(tmr[index+i]) value |= mask;
        mask <<= 1;
      }
      *data = value;
    }
    break;
  case 0x200: case 0x210:
  case 0x220: case 0x230:
  case 0x240: case 0x250:
  case 0x260: case 0x270:
    {
      unsigned index = (addr2 - 0x200) << 1;
      Bit32u value = 0, mask = 1;
      for(int i=0;i<32;i++) {
        if(irr[index+i]) value |= mask;
        mask <<= 1;
      }
      *data = value;
    }
    break;
  case 0x280: // error status reg
    *data = error_status; break;
  case 0x300: // interrupt command reg  0-31
    *data = icr_lo; break;
  case 0x310: // interrupt command reg 31-63
    *data = icr_hi; break;
  case 0x320: // LVT Timer Reg
  case 0x330: // LVT Thermal Monitor
  case 0x340: // LVT Performance Counter
  case 0x350: // LVT LINT0 Reg
  case 0x360: // LVT Lint1 Reg
  case 0x370: // LVT Error Reg
    {
      int index = (addr2 - 0x320) >> 4;
      *data = lvt[index];
      break;
    }
  case 0x380: // initial count for timer
    *data = timer_initial;
    break;
  case 0x390: // current count for timer
    if(timer_active==0) {
      *data = timer_current;
    } else {
      Bit64u delta64;
      Bit32u delta32;
      delta64 = (bx_pc_system.time_ticks() - ticksInitial) / timer_divide_factor;
      delta32 = (Bit32u) delta64;
      if(delta32 > timer_initial)
        BX_PANIC(("APIC: R(curr timer count): delta < initial"));
      timer_current = timer_initial - delta32;
      *data = timer_current;
    }
    break;
  case 0x3e0: // timer divide configuration
    *data = timer_divconf; 
    break;
  default:
    BX_INFO(("APIC register %08x not implemented", addr));
  }

  BX_DEBUG(("%s: read from APIC address %08x = %08x", cpu->name, addr, *data));
}

int bx_local_apic_c::highest_priority_int(Bit8u *array)
{
  for(int i=BX_APIC_LAST_VECTOR; i>=BX_APIC_FIRST_VECTOR; i--)
    if(array[i]) return i;

  return -1;
}

void bx_local_apic_c::service_local_apic(void)
{
  if(bx_dbg.apic) {
    BX_INFO(("service_local_apic()"));
    print_status();
  }
  if(INTR) return;  // INTR already up; do nothing
  // find first interrupt in irr.
  int first_irr = highest_priority_int(irr);
  if (first_irr < 0) return;   // no interrupts, leave INTR=0
  int first_isr = highest_priority_int(isr);
  if (first_isr >= 0 && first_irr <= first_isr) {
    BX_DEBUG(("local apic(%s): not delivering int 0x%02x because int 0x%02x is in service", cpu->name, first_irr, first_isr));
    return;
  }
  if(((Bit32u)(first_irr) & 0xf0) <= (task_priority & 0xf0)) {
    BX_DEBUG(("local apic(%s): not delivering int 0x%02X because task_priority is 0x%02X", cpu->name, first_irr, task_priority));
    return;
  }
  // interrupt has appeared in irr. Raise INTR. When the CPU
  // acknowledges, we will run highest_priority_int again and
  // return it.
  BX_DEBUG(("service_local_apic(): setting INTR=1 for vector 0x%02x", first_irr));
  INTR = 1;
  cpu->async_event = 1;
}

bx_bool bx_local_apic_c::deliver(Bit8u vector, Bit8u delivery_mode, Bit8u trig_mode)
{
  switch(delivery_mode) {
  case APIC_DM_FIXED:
  case APIC_DM_LOWPRI:
    BX_DEBUG(("Deliver lowest priority of fixed interrupt vector %02x", vector));
    trigger_irq(vector, trig_mode);
    break;
  case APIC_DM_SMI:
    BX_PANIC(("Delivery of SMI still not implemented !"));
    cpu->deliver_SMI();
    return 1;
  case APIC_DM_NMI:
    BX_PANIC(("Delivery of NMI still not implemented !"));
    cpu->deliver_NMI();
    return 1;
  case APIC_DM_INIT:
    BX_DEBUG(("Deliver INIT IPI"));
    init();
    break;
  case APIC_DM_SIPI:
    BX_DEBUG(("Deliver Start Up IPI"));
    startup_msg(vector);
    break;
  case APIC_DM_EXTINT:
    BX_DEBUG(("Deliver EXTINT vector %02x", vector));
    trigger_irq(vector, trig_mode, 1);
    break;
  default:
    return 0;
  }

  return 1;
}

void bx_local_apic_c::trigger_irq(unsigned vector, unsigned trigger_mode, bx_bool bypass_irr_isr)
{
  BX_DEBUG(("Local apic on %s: trigger interrupt vector=0x%x", cpu->name, vector));
  
  if(vector > BX_APIC_LAST_VECTOR || vector < BX_APIC_FIRST_VECTOR) {
    shadow_error_status |= APIC_ERR_RX_ILLEGAL_VEC;
    BX_INFO(("bogus vector %#x, ignoring ...", vector));
    return;
  }

  BX_DEBUG(("triggered vector %#02x", vector));

  if(bypass_irr_isr) {
    goto service_vector;
  }

  if(irr[vector] != 0) {
    BX_DEBUG(("triggered vector %#02x not accepted", vector));
    return;
  }

service_vector:
  irr[vector] = 1;
  tmr[vector] = trigger_mode;	// set for level triggered
  service_local_apic();
}

void bx_local_apic_c::untrigger_irq(unsigned vector, unsigned trigger_mode)
{
  BX_DEBUG(("Local apic on %s: untrigger interrupt vector=0x%x", cpu->name, vector));
  // hardware says "no more".  clear the bit.  If the CPU hasn't yet
  // acknowledged the interrupt, it will never be serviced.
  BX_ASSERT(irr[vector] == 1);
  irr[vector] = 0;
  if(bx_dbg.apic) print_status();
}

Bit8u bx_local_apic_c::acknowledge_int(void)
{
  // CPU calls this when it is ready to service one interrupt
  if(!INTR)
    BX_PANIC(("%s: acknowledged an interrupt, but INTR=0", cpu->name));
  BX_ASSERT(INTR);
  int vector = highest_priority_int(irr);
  if (vector < 0) goto spurious;
  if((vector & 0xf0) <= get_ppr()) goto spurious;
  BX_ASSERT(irr[vector] == 1);
  BX_DEBUG(("%s: acknowledge_int returning vector 0x%x", cpu->name, vector));
  irr[vector] = 0;
  isr[vector] = 1;
  if(bx_dbg.apic) {
    BX_INFO(("Status after setting isr:"));
    print_status();
  }
  INTR = 0;
  cpu->async_event = 1;
  service_local_apic();  // will set INTR again if another is ready
  return vector;

spurious:
  INTR = 0;
  cpu->async_event = 1;
  return spurious_vector;
}

void bx_local_apic_c::print_status(void)
{
  BX_INFO(("%s local apic: status is {:", cpu->name));
  for(int vec=0; vec<BX_LOCAL_APIC_MAX_INTS; vec++) {
    if(irr[vec] || isr[vec]) {
      BX_INFO(("vec 0x%x: irr=%d, isr=%d", vec,(int)irr[vec],(int)isr[vec]));
    }
  }
  BX_INFO(("}"));
}

bx_bool bx_local_apic_c::match_logical_addr(Bit8u address) 
{
  if(dest_format != 0xf) {
    BX_PANIC(("bx_local_apic_c::match_logical_addr: cluster model addressing not implemented"));
  }
  bx_bool match = ((address & log_dest) != 0);
  BX_DEBUG(("%s: comparing MDA %02x to my LDR %02x -> %s", cpu->name,
    address, log_dest, match? "Match" : "Not a match"));
  return match;
}

Bit8u bx_local_apic_c::get_ppr(void)
{
  int ppr = highest_priority_int(isr);

  if((ppr < 0) || ((task_priority & 0xF0) >= ((Bit32u) ppr & 0xF0))) 
    ppr = task_priority;
  else 
    ppr &= 0xF0;

  return ppr;
}

Bit8u bx_local_apic_c::get_tpr(void)
{
  return task_priority;
}

void  bx_local_apic_c::set_tpr(Bit8u priority)
{
  if(priority < task_priority) {
    task_priority = priority;
    service_local_apic();
  } else {
    task_priority = priority;
  }
}

Bit8u bx_local_apic_c::get_apr(void)
{
  Bit32u tpr  = (task_priority >> 4) & 0xf;
  Bit32u isrv = (highest_priority_int(isr) >> 4) & 0xf;
  Bit32u irrv = (highest_priority_int(irr) >> 4) & 0xf;
  Bit8u  apr;

  if(isrv < 0) isrv = 0;
  if(irrv < 0) irrv = 0;

  if((tpr >= irrv) && (tpr > isrv)) {
    apr = task_priority & 0xff;
  }
  else {
    apr = ((tpr & isrv) > irrv) ?(tpr & isrv) : irrv;
    apr <<= 4;
  }

  BX_DEBUG(("apr = %d\n", apr));

  return(Bit8u) apr;
}

bx_bool bx_local_apic_c::is_focus(Bit8u vector)
{
  if(focus_disable) return 0;
  return(irr[vector] || isr[vector]) ? 1 : 0;
}

void bx_local_apic_c::periodic_smf(void *this_ptr)
{
  bx_local_apic_c *class_ptr = (bx_local_apic_c *) this_ptr;
  class_ptr->periodic();
}

void bx_local_apic_c::periodic(void)
{
  if(!timer_active) {
    BX_ERROR(("%s: bx_local_apic_c::periodic called, timer_active==0", cpu->name));
    return;
  }

  // timer reached zero since the last call to periodic.
  Bit32u timervec = lvt[APIC_LVT_TIMER];
  if(timervec & 0x20000) {
    // Periodic mode.
    // If timer is not masked, trigger interrupt.
    if((timervec & 0x10000)==0) {
      trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
    }
    else {
      BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
    }
    // Reload timer values.
    timer_current = timer_initial;
    ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
    BX_DEBUG(("%s: local apic timer(periodic) triggered int, reset counter to 0x%08x", cpu->name, timer_current));
  }
  else {
    // one-shot mode
    timer_current = 0;
    // If timer is not masked, trigger interrupt.
    if((timervec & 0x10000)==0) {
      trigger_irq(timervec & 0xff, APIC_EDGE_TRIGGERED);
    }
    else {
      BX_DEBUG(("%s: local apic timer LVT masked", cpu->name));
    }
    timer_active = 0;
    BX_DEBUG(("%s: local apic timer(one-shot) triggered int", cpu->name));
    bx_pc_system.deactivate_timer(timer_handle);
  }
}

void bx_local_apic_c::set_divide_configuration(Bit32u value)
{
  BX_ASSERT(value == (value & 0x0b));
  // move bit 3 down to bit 0.
  value = ((value & 8) >> 1) | (value & 3);
  BX_ASSERT(value >= 0 && value <= 7);
  timer_divide_factor = (value==7)? 1 : (2 << value);
  BX_INFO(("%s: set timer divide factor to %d", cpu->name, timer_divide_factor));
}

void bx_local_apic_c::set_initial_timer_count(Bit32u value)
{
  // If active before, deactive the current timer before changing it.
  if(timer_active) {
    bx_pc_system.deactivate_timer(timer_handle);
    timer_active = 0;
  }

  timer_initial = value;
  timer_current = 0;

  if(timer_initial != 0)  // terminate the counting if timer_initial = 0
  {
    // This should trigger the counter to start.  If already started,
    // restart from the new start value.
    BX_DEBUG(("APIC: Initial Timer Count Register = %u", value));
    timer_current = timer_initial;
    timer_active = 1;
    Bit32u timervec = lvt[APIC_LVT_TIMER];
    bx_bool continuous = (timervec & 0x20000) > 0;
    ticksInitial = bx_pc_system.time_ticks(); // Take a reading.
    bx_pc_system.activate_timer_ticks(timer_handle,
            Bit64u(timer_initial) * Bit64u(timer_divide_factor), continuous);
  }
}

#if BX_SUPPORT_SAVE_RESTORE
void bx_local_apic_c::register_state(bx_param_c *parent)
{
  unsigned i;
  char name[6];

  bx_list_c *lapic = new bx_list_c(parent, "local_apic", 25);

  BXRS_HEX_PARAM_SIMPLE(lapic, base_addr);
  BXRS_HEX_PARAM_SIMPLE(lapic, id);
  BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
  BXRS_PARAM_BOOL(lapic, software_enabled, software_enabled);
  BXRS_PARAM_BOOL(lapic, focus_disable, focus_disable);
  BXRS_HEX_PARAM_SIMPLE(lapic, task_priority);
  BXRS_HEX_PARAM_SIMPLE(lapic, spurious_vector);
  BXRS_HEX_PARAM_SIMPLE(lapic, log_dest);
  BXRS_HEX_PARAM_SIMPLE(lapic, dest_format);
  
  bx_list_c *ISR = new bx_list_c(lapic, "isr", BX_LOCAL_APIC_MAX_INTS);
  bx_list_c *TMR = new bx_list_c(lapic, "tmr", BX_LOCAL_APIC_MAX_INTS);
  bx_list_c *IRR = new bx_list_c(lapic, "irr", BX_LOCAL_APIC_MAX_INTS);
  for (i=0; i<BX_LOCAL_APIC_MAX_INTS; i++) {
    sprintf(name, "0x%02x", i);
    new bx_shadow_num_c(ISR, name, &isr[i]);
    new bx_shadow_num_c(TMR, name, &tmr[i]);
    new bx_shadow_num_c(IRR, name, &irr[i]);
  }

  BXRS_HEX_PARAM_SIMPLE(lapic, error_status);
  BXRS_HEX_PARAM_SIMPLE(lapic, shadow_error_status);
  BXRS_HEX_PARAM_SIMPLE(lapic, icr_hi);
  BXRS_HEX_PARAM_SIMPLE(lapic, icr_lo);

  bx_list_c *LVT = new bx_list_c(lapic, "lvt", APIC_LVT_ENTRIES);
  for (i=0; i<APIC_LVT_ENTRIES; i++) {
    sprintf(name, "%d", i);
    new bx_shadow_num_c(LVT, name, &lvt[i], BASE_HEX);
  }

  BXRS_HEX_PARAM_SIMPLE(lapic, timer_initial);
  BXRS_HEX_PARAM_SIMPLE(lapic, timer_current);
  BXRS_HEX_PARAM_SIMPLE(lapic, timer_divconf);
  BXRS_DEC_PARAM_SIMPLE(lapic, timer_divide_factor);
  BXRS_PARAM_BOOL(lapic, timer_active, timer_active);
  BXRS_HEX_PARAM_SIMPLE(lapic, ticksInitial);
  BXRS_PARAM_BOOL(lapic, INTR, INTR);
}
#endif

#endif /* if BX_SUPPORT_APIC */