summaryrefslogtreecommitdiffhomepage
path: root/Kernel/HALKit/AMD64/HalCoreMultiProcessingAMD64.cpp
blob: e4a2d99c91ed78281fb16846a13ee07dea29418d (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
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
/* -------------------------------------------

	Copyright Zeta Electronics Corporation

------------------------------------------- */

#include <Modules/ACPI/ACPIFactoryInterface.hxx>
#include <HALKit/AMD64/Processor.hpp>
#include <NewKit/KernelCheck.hpp>
#include <ArchKit/ArchKit.hpp>
#include <KernelKit/Semaphore.hpp>
#include <KernelKit/ProcessScheduler.hxx>
#include <KernelKit/Timer.hpp>

#define kAPIC_ICR_Low	  0x300
#define kAPIC_ICR_High	  0x310
#define kAPIC_SIPI_Vector 0x00500
#define kAPIC_EIPI_Vector 0x00400

/// @brief assembly routine. internal use only.
EXTERN_C void _hal_enable_smp(void);

/// @note: _hal_switch_context

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

//! NOTE: fGSI stands 'Field Global System Interrupt'

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

namespace NewOS::HAL
{
	constexpr Int32 kThreadAPIC	  = 0;
	constexpr Int32 kThreadLAPIC  = 1;
	constexpr Int32 kThreadIOAPIC = 2;
	constexpr Int32 kThreadAPIC64 = 3;
	constexpr Int32 kThreadBoot	  = 4;

	/*
	 *
	 * this is used to store info about the current running thread
	 * we use this struct to determine if we can use it, or mark it as used or on
	 * sleep.
	 *
	 */

	struct ProcessorInfoAMD64 final
	{
		Int32	ThreadType;
		UIntPtr JumpAddress;

		struct
		{
			UInt32 Code;
			UInt32 Data;
			UInt32 BSS;
		} Selector;
	};

	STATIC voidPtr	   kApicMadt	  = nullptr;
	STATIC const char* kApicSignature = "APIC";

	/// @brief Multiple APIC Descriptor Table.
	struct MadtType final : public SDT
	{
		struct MadtAddress final
		{
			Char RecordType;
			Char RecordLen; // record length

			UInt32 Address;
			UInt32 Flags; // 1 = Dual Legacy PICs installed
		} MadtRecords[];
	};

	struct MadtProcessorLocalApic final
	{
		Char   AcpiProcessorId;
		Char   ApicId;
		UInt32 Flags;
	};

	struct MadtIOApic final
	{
		Char   ApicId;
		Char   Reserved;
		UInt32 Address;
		UInt32 SystemInterruptBase;
	};

	struct MadtInterruptSource final
	{
		Char   BusSource;
		Char   IrqSource;
		UInt32 GSI;
		UInt16 Flags;
	};

	struct MadtInterruptNmi final
	{
		Char   NmiSource;
		Char   Reserved;
		UInt16 Flags;
		UInt32 GSI;
	};

	struct MadtLocalApicAddressOverride final
	{
		UInt16	Resvered;
		UIntPtr Address;
	};

	STATIC Void hal_switch_context(HAL::StackFramePtr stackFrame);

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

	STATIC MadtType* kApicInfoBlock = nullptr;

	STATIC struct
	{
		UIntPtr fAddress{0};
		UInt32	fKind{0};
	} kApicMadtAddresses[255] = {};

	STATIC SizeT   kApicMadtAddressesCount = 0UL;
	STATIC UIntPtr cBaseAddressAPIC		   = 0xFEE00000;

	/// @brief this will help us schedule our cores.
	STATIC Boolean* cProgramInitialized = nullptr;

	enum
	{
		cAPICEOI = 0xb0,
	};

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

	/// @brief Send start IPI for CPU.
	/// @param apicId
	/// @param vector
	/// @param targetAddress
	/// @return
	Void hal_send_start_ipi(UInt32 apicId, UInt8 vector, UInt32 targetAddress)
	{
		NewOS::ke_dma_write(targetAddress, kAPIC_ICR_High, apicId << 24);
		NewOS::ke_dma_write(targetAddress, kAPIC_ICR_Low, kAPIC_SIPI_Vector | vector);
	}

	EXTERN_C Void _hal_spin_core(Void);

	/// @brief Send end IPI for CPU.
	/// @param apicId
	/// @param vector
	/// @param targetAddress
	/// @return
	Void hal_send_end_ipi(UInt32 apicId, UInt8 vector, UInt32 targetAddress)
	{
		NewOS::ke_dma_write(targetAddress, kAPIC_ICR_High, apicId << 24);
		NewOS::ke_dma_write(targetAddress, kAPIC_ICR_Low, kAPIC_EIPI_Vector | vector);
	}

	STATIC HAL::StackFramePtr cFramePtr = nullptr;

	EXTERN_C Void hal_apic_acknowledge_cont(Void)
	{
		kcout << "newoskrnl: stopping core...\r";
		ke_stop(RUNTIME_CHECK_BOOTSTRAP);
	}

	EXTERN_C StackFramePtr _hal_leak_current_context(Void)
	{
		return cFramePtr;
	}

	EXTERN_C Void hal_apic_acknowledge(Void)
	{
		hal_apic_acknowledge_cont();
	}

	EXTERN_C Void _hal_switch_context(HAL::StackFramePtr stackFrame)
	{
		hal_switch_context(stackFrame);
	}

	STATIC Void hal_switch_context(HAL::StackFramePtr stackFrame)
	{
		Semaphore sem;

		sem.LockOrWait(&ProcessScheduler::The().Leak().TheCurrent().Leak(), Seconds(5));

		cFramePtr = stackFrame;

		/// yes the exception field contains the core id.
		hal_send_start_ipi(stackFrame->Rcx, 0x40, cBaseAddressAPIC);

		sem.Unlock();
	}

	/// @brief Fetch and enable cores inside main CPU.
	/// @param rsdPtr RSD PTR structure.
	Void hal_system_get_cores(voidPtr rsdPtr)
	{
		auto acpi = ACPIFactoryInterface(rsdPtr);
		kApicMadt = acpi.Find(kApicSignature).Leak().Leak();

		if (kApicMadt != nullptr)
		{
			MadtType* madt = reinterpret_cast<MadtType*>(kApicMadt);

			constexpr auto cMaxProbableCores = 4; // the amount of cores we want.
			constexpr auto cStartAt			 = 0; // start here to avoid boot core.

			for (SizeT coreAt = cStartAt; coreAt < cMaxProbableCores; ++coreAt)
			{
				if (madt->MadtRecords[coreAt].Flags < kThreadBoot) // if local apic.
				{
					MadtType::MadtAddress& madtRecord = madt->MadtRecords[coreAt];

					// then register as a core for scheduler.
					kcout << "newoskrnl: Register APIC.\r";

					kApicMadtAddresses[kApicMadtAddressesCount].fAddress = madtRecord.Address;
					kApicMadtAddresses[kApicMadtAddressesCount].fKind	 = madt->MadtRecords[coreAt].Flags;

					++kApicMadtAddressesCount;
				}
			}

			///////////////////////////////////////////////////////////////////////////
			/// Start local APIC now.
			///////////////////////////////////////////////////////////////////////////

			auto flagsSet = NewOS::ke_dma_read(cBaseAddressAPIC, 0xF0); // SVR register.

			// enable APIC.
			flagsSet |= 0x100;

			NewOS::ke_dma_write(cBaseAddressAPIC, 0xF0, flagsSet);

			/// Set sprurious interrupt vector.
			NewOS::ke_dma_write(cBaseAddressAPIC, 0xF0, 0x100 | 0xFF);

			// highest task priority. for our realtime kernel.
			NewOS::ke_dma_write(cBaseAddressAPIC, 0x21, 0);

			cProgramInitialized = new Boolean(true);

			constexpr auto cWhereToInterrupt = 0x40;
			constexpr auto cWhatCore		 = 1;

			hal_send_start_ipi(cWhatCore, cWhereToInterrupt, cBaseAddressAPIC);
		}
		else
		{
			kcout << "newoskrnl: APIC is not present! it is a vital component to enable SMP.\r";
			ke_stop(RUNTIME_CHECK_FAILED);
		}
	}
} // namespace NewOS::HAL

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