summaryrefslogtreecommitdiffstats
path: root/PcAtChipsetPkg/Library/BaseIoApicLib/IoApicLib.c
blob: 9e4a58049e494b7e40232dde601eaccc17157612 (plain)
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
/** @file
  I/O APIC library.

  I/O APIC library assumes I/O APIC is enabled. It does not
  handles cases where I/O APIC is disabled.

  Copyright (c) 2011 - 2018, Intel Corporation. All rights reserved.<BR>
  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#include <Base.h>

#include <Library/IoApicLib.h>

#include <Library/DebugLib.h>
#include <Library/PcdLib.h>
#include <Library/IoLib.h>
#include <Library/LocalApicLib.h>

#include <Register/IoApic.h>

/**
  Read a 32-bit I/O APIC register.

  If Index is >= 0x100, then ASSERT().

  @param  Index  Specifies the I/O APIC register to read.

  @return  The 32-bit value read from the I/O APIC register specified by Index.
**/
UINT32
EFIAPI
IoApicRead (
  IN UINTN  Index
  )
{
  ASSERT (Index < 0x100);
  MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);
  return MmioRead32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET);
}

/**
  Write a 32-bit I/O APIC register.

  If Index is >= 0x100, then ASSERT().

  @param  Index  Specifies the I/O APIC register to write.
  @param  Value  Specifies the value to write to the I/O APIC register specified by Index.

  @return  The 32-bit value written to I/O APIC register specified by Index.
**/
UINT32
EFIAPI
IoApicWrite (
  IN UINTN   Index,
  IN UINT32  Value
  )
{
  ASSERT (Index < 0x100);
  MmioWrite8 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_INDEX_OFFSET, (UINT8)Index);
  return MmioWrite32 (PcdGet32 (PcdIoApicBaseAddress) + IOAPIC_DATA_OFFSET, Value);
}

/**
  Set the interrupt mask of an I/O APIC interrupt.

  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().

  @param  Irq     Specifies the I/O APIC interrupt to enable or disable.
  @param  Enable  If TRUE, then enable the I/O APIC interrupt specified by Irq.
                  If FALSE, then disable the I/O APIC interrupt specified by Irq.
**/
VOID
EFIAPI
IoApicEnableInterrupt (
  IN UINTN    Irq,
  IN BOOLEAN  Enable
  )
{
  IO_APIC_VERSION_REGISTER         Version;
  IO_APIC_REDIRECTION_TABLE_ENTRY  Entry;

  Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);
  ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);
  ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);

  Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);
  Entry.Bits.Mask = Enable ? 0 : 1;
  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);
}

/**
  Configures an I/O APIC interrupt.

  Configure an I/O APIC Redirection Table Entry to deliver an interrupt in physical
  mode to the Local APIC of the currently executing CPU.  The default state of the
  entry is for the interrupt to be disabled (masked).  IoApicEnableInterrupts() must
  be used to enable(unmask) the I/O APIC Interrupt.

  If Irq is larger than the maximum number I/O APIC redirection entries, then ASSERT().
  If Vector >= 0x100, then ASSERT().
  If DeliveryMode is not supported, then ASSERT().

  @param  Irq             Specifies the I/O APIC interrupt to initialize.
  @param  Vector          The 8-bit interrupt vector associated with the I/O APIC
                          Interrupt.  Must be in the range 0x10..0xFE.
  @param  DeliveryMode    A 3-bit value that specifies how the recept of the I/O APIC
                          interrupt is handled.  The only supported values are:
                            0: IO_APIC_DELIVERY_MODE_FIXED
                            1: IO_APIC_DELIVERY_MODE_LOWEST_PRIORITY
                            2: IO_APIC_DELIVERY_MODE_SMI
                            4: IO_APIC_DELIVERY_MODE_NMI
                            5: IO_APIC_DELIVERY_MODE_INIT
                            7: IO_APIC_DELIVERY_MODE_EXTINT
  @param  LevelTriggered  TRUE specifies a level triggered interrupt.
                          FALSE specifies an edge triggered interrupt.
  @param  AssertionLevel  TRUE specified an active high interrupt.
                          FALSE specifies an active low interrupt.
**/
VOID
EFIAPI
IoApicConfigureInterrupt (
  IN UINTN    Irq,
  IN UINTN    Vector,
  IN UINTN    DeliveryMode,
  IN BOOLEAN  LevelTriggered,
  IN BOOLEAN  AssertionLevel
  )
{
  IO_APIC_VERSION_REGISTER         Version;
  IO_APIC_REDIRECTION_TABLE_ENTRY  Entry;

  Version.Uint32 = IoApicRead (IO_APIC_VERSION_REGISTER_INDEX);
  ASSERT (Version.Bits.MaximumRedirectionEntry < 0xF0);
  ASSERT (Irq <= Version.Bits.MaximumRedirectionEntry);
  ASSERT (Vector <= 0xFF);
  ASSERT (DeliveryMode < 8 && DeliveryMode != 6 && DeliveryMode != 3);

  Entry.Uint32.Low = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2);
  Entry.Bits.Vector          = (UINT8)Vector;
  Entry.Bits.DeliveryMode    = (UINT32)DeliveryMode;
  Entry.Bits.DestinationMode = 0;
  Entry.Bits.Polarity        = AssertionLevel ? 0 : 1;
  Entry.Bits.TriggerMode     = LevelTriggered ? 1 : 0;
  Entry.Bits.Mask            = 1;
  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2, Entry.Uint32.Low);

  Entry.Uint32.High = IoApicRead (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1);
  Entry.Bits.DestinationID = GetApicId ();
  IoApicWrite (IO_APIC_REDIRECTION_TABLE_ENTRY_INDEX + Irq * 2 + 1, Entry.Uint32.High);
}