summaryrefslogtreecommitdiffhomepage
path: root/Kernel/Sources/SMPManager.cxx
blob: 29e0fa26ddbcbb1f547f44864e89e4582155c624 (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
/* -------------------------------------------

	Copyright SoftwareLabs

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

#include <ArchKit/ArchKit.hpp>
#include <KernelKit/ProcessScheduler.hxx>
#include <KernelKit/SMPManager.hpp>

///! BUGS: 0

///! @file SMPManager.cxx
///! @brief This file handles multi processing in NewOS.
///! @brief Multi processing is needed for multi-tasking operations.

namespace NewOS
{
	///! A HardwareThread class takes care of it's owned hardware thread.
	///! It has a stack for it's core.

	///! @brief constructor
	HardwareThread::HardwareThread() = default;

	///! @brief destructor
	HardwareThread::~HardwareThread() = default;

	//! @brief returns the id

	const ThreadID& HardwareThread::ID() noexcept
	{
		return fID;
	}

	//! @brief returns the kind

	const ThreadKind& HardwareThread::Kind() noexcept
	{
		return fKind;
	}

	//! @brief is the core busy?

	bool HardwareThread::IsBusy() noexcept
	{
		return fBusy;
	}

	/// @brief Get processor stack frame.

	HAL::StackFramePtr HardwareThread::StackFrame() noexcept
	{
		MUST_PASS(fStack);
		return fStack;
	}

	void HardwareThread::Busy(const bool busy) noexcept
	{
		fBusy = busy;
	}

	HardwareThread::operator bool()
	{
		return fStack;
	}

	/// @brief Wakeup the processor.

	void HardwareThread::Wake(const bool wakeup) noexcept
	{
		fWakeup = wakeup;

		if (!fWakeup)
			rt_hang_thread(fStack);
		else
			rt_wakeup_thread(fStack);
	}

	extern bool rt_check_stack(HAL::StackFramePtr stackPtr);

	/// @brief Switch to hardware thread.
	/// @param stack the new hardware thread.
	/// @retval true stack was changed, code is running.
	/// @retval false stack is invalid, previous code is running.
	bool HardwareThread::Switch(HAL::StackFramePtr stack)
	{
		if (!rt_check_stack(stack))
			return false;

		if (!fStack)
		{
			fStack = stack;
		}
		else
		{
			/// Keep the arguments, switch the base pointer, stack pointer
			/// fs and gs registers.
			fStack->Rbp = stack->Rbp;
			fStack->Rsp = stack->Rsp;
			fStack->Fs	= stack->Fs;
			fStack->Gs	= stack->Gs;
		}

		rt_do_context_switch(fStack);
		
		return true;
	}

	///! @brief Tells if processor is waked up.
	bool HardwareThread::IsWakeup() noexcept
	{
		return fWakeup;
	}

	//! @brief Constructor and destructor

	///! @brief Default constructor.
	SMPManager::SMPManager() = default;

	///! @brief Default destructor.
	SMPManager::~SMPManager() = default;

	/// @brief Shared singleton function
	Ref<SMPManager> SMPManager::The()
	{
		static SMPManager manager;
		return {manager};
	}

	/// @brief Get Stack Frame of Core
	HAL::StackFramePtr SMPManager::GetStackFrame() noexcept
	{
		if (fThreadList[fCurrentThread].Leak() &&
			ProcessHelper::GetCurrentPID() ==
				fThreadList[fCurrentThread].Leak().Leak().fPID)
			return fThreadList[fCurrentThread].Leak().Leak().fStack;

		return nullptr;
	}

	/// @brief Finds and switch to a free core.
	bool SMPManager::Switch(HAL::StackFramePtr stack)
	{
		if (stack == nullptr)
			return false;

		for (SizeT idx = 0; idx < kMaxHarts; ++idx)
		{
			// stack != nullptr -> if core is used, then continue.
			if (!fThreadList[idx].Leak() ||
				!fThreadList[idx].Leak().Leak().IsWakeup() ||
				fThreadList[idx].Leak().Leak().IsBusy())
				continue;

			// to avoid any null deref.
			if (!fThreadList[idx].Leak().Leak().fStack)
				continue;
			if (fThreadList[idx].Leak().Leak().fStack->Rsp == 0)
				continue;
			if (fThreadList[idx].Leak().Leak().fStack->Rbp == 0)
				continue;

			fThreadList[idx].Leak().Leak().Busy(true);

			fThreadList[idx].Leak().Leak().fID = idx;

			/// I figured out this:
			/// Allocate stack
			/// Set APIC base to stack
			/// Do stuff and relocate stack based on this code.
			/// - Amlel
			rt_copy_memory(stack, fThreadList[idx].Leak().Leak().fStack,
						   sizeof(HAL::StackFrame));

			fThreadList[idx].Leak().Leak().Switch(fThreadList[idx].Leak().Leak().fStack);

			fThreadList[idx].Leak().Leak().fPID = ProcessHelper::GetCurrentPID();

			fThreadList[idx].Leak().Leak().Busy(false);

			return true;
		}

		return false;
	}

	/**
	 * Index Hardware thread
	 * @param idx the index
	 * @return the reference to the hardware thread.
	 */
	Ref<HardwareThread> SMPManager::operator[](const SizeT& idx)
	{
		if (idx == 0)
		{
			if (fThreadList[idx].Leak().Leak().Kind() != kHartSystemReserved)
			{
				fThreadList[idx].Leak().Leak().fKind = kHartBoot;
			}
		}
		else if (idx >= kMaxHarts)
		{
			HardwareThread fakeThread;
			fakeThread.fKind = kInvalidHart;

			return {fakeThread};
		}

		return fThreadList[idx].Leak();
	}

	/**
	 * Check if thread pool isn't empty.
	 * @return
	 */
	SMPManager::operator bool() noexcept
	{
		return !fThreadList.Empty();
	}

	/**
	 * Reverse operator bool
	 * @return
	 */
	bool SMPManager::operator!() noexcept
	{
		return fThreadList.Empty();
	}

	/// @brief Returns the amount of core present.
	/// @return the number of cores.
	SizeT SMPManager::Count() noexcept
	{
		return fThreadList.Count();
	}
} // namespace NewOS