message: add notification icon

diff:
@@ -289,7 +289,7 @@ namespace HunterPie.GUI
if (InDesignMode)
{
- DesignModeDetails = $"{Left}x{Top} ({DefaultScaleX * 100:0.0}%) ({stopwatch.ElapsedMilliseconds}ms)";
+ DesignModeDetails = $"{Left:0.0}x{Top:0.0} ({DefaultScaleX * 100:0.0}%) ({stopwatch.ElapsedMilliseconds}ms)";
DesignModeDetailsVisibility = Visibility.Visible;
stopwatch.Restart();
}

diff:
@@ -279,7 +279,11 @@ namespace HunterPie.GUIControls
settingsUI.switchHBGHelper.IsEnabled = settings.Overlay.ClassesWidget.HeavyBowgunHelper.Enabled;
settingsUI.switchLBGHelper.IsEnabled = settings.Overlay.ClassesWidget.LightBowgunHelper.Enabled;
-
+ // Notifications
+ settingsUI.switchEnableNotifications.IsEnabled = settings.Overlay.NotificationsWidget.Enabled;
+ settingsUI.switchNotificationsInitialize.IsEnabled = settings.Overlay.NotificationsWidget.Initialize;
+ settingsUI.NotificationsWidgetOpacity.Value = settings.Overlay.NotificationsWidget.Opacity;
+ settingsUI.switchNotificationStreamerMode.IsEnabled = settings.Overlay.NotificationsWidget.StreamerMode;
}
private async void saveSettings_Click(object sender, RoutedEventArgs e)
@@ -464,6 +468,12 @@ namespace HunterPie.GUIControls
i++;
}
+ // Notifications
+ settings.Overlay.NotificationsWidget.Enabled = settingsUI.switchEnableNotifications.IsEnabled;
+ settings.Overlay.NotificationsWidget.Initialize = settingsUI.switchNotificationsInitialize.IsEnabled;
+ settings.Overlay.NotificationsWidget.Opacity = (float)settingsUI.NotificationsWidgetOpacity.Value;
+ settings.Overlay.NotificationsWidget.StreamerMode = settingsUI.switchNotificationStreamerMode.IsEnabled;
+
// save data from plugin tabs
// TODO: notify user
var hasErrors = settingsUI.Save();

original_message: add notification icon


message: (oops!) added missing changes

change_type: MODIFY

diff:
@@ -49,6 +49,7 @@ namespace HunterPie.Core.Settings
public DPSMeter DPSMeter { get; set; } = new DPSMeter();
public AbnormalitiesWidget AbnormalitiesWidget { get; set; } = new AbnormalitiesWidget();
public ClassesWidget ClassesWidget { get; set; } = new ClassesWidget();
+ public NotificationsWidget NotificationsWidget { get; set; } = new NotificationsWidget();
}
public class PlayerHealthComponent : IWidgetSettings
@@ -158,6 +159,16 @@ namespace HunterPie.Core.Settings
public bool StreamerMode { get; set; } = false;
}
+ public class NotificationsWidget : IWidgetSettings
+ {
+ public bool Initialize { get; set; } = true;
+ public bool Enabled { get; set; } = true;
+ public int[] Position { get; set; } = { 1370, 680 };
+ public float Opacity { get; set; } = 1;
+ public double Scale { get; set; } = 1;
+ public bool StreamerMode { get; set; } = false;
+ }
+
public class Players
{
public string Color { get; set; } = "#FFFFFF";

diff:
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{9E9CCD04-0006-4ECF-A973-8776B638121C}</ProjectGuid>
+ <ProjectTypeGuids>{60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>HunterPie</RootNamespace>
<Compile Include="GUI\Widgets\Notifications\DiscordNotify.xaml.cs">
<DependentUpon>DiscordNotify.xaml</DependentUpon>
</Compile>
+ <Compile Include="GUI\Widgets\Notifications\Notification.xaml.cs">
+ <DependentUpon>Notification.xaml</DependentUpon>
+ </Compile>
+ <Compile Include="GUI\Widgets\Notifications\NotificationsWidget.xaml.cs">
+ <DependentUpon>NotificationsWidget.xaml</DependentUpon>
+ </Compile>
<Compile Include="GUI\WidgetType.cs" />
<Compile Include="Infrastructure\BaseViewModel.cs" />
<Compile Include="Infrastructure\Converters\BooleanToVisibilityConverter.cs" />
<Generator>MSBuild:Compile</Generator>
<SubType>Designer</SubType>
</Page>
+ <Page Include="GUI\Widgets\Notifications\Notification.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
+ <Page Include="GUI\Widgets\Notifications\NotificationsWidget.xaml">
+ <SubType>Designer</SubType>
+ <Generator>MSBuild:Compile</Generator>
+ </Page>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\HunterPie.Core\HunterPie.Core.csproj">

license: MIT License

original_message: (oops!) added missing changes


message: HunterPie.Native: added input interrupt & server logging messages

diff:
@@ -12,6 +12,10 @@ namespace HunterPie.Core.Input
{
private static VirtualInput instance;
private bool disposedValue;
+ private Random random = new Random();
+
+ private uint lastInputId = 0;
+ public static uint LastInputId => Instance.lastInputId;
public static VirtualInput Instance
{
@@ -97,10 +101,11 @@ namespace HunterPie.Core.Input
{
do
{
- newInjId = (uint)(new Random().NextDouble() * uint.MaxValue);
+ newInjId = (uint)(random.NextDouble() * uint.MaxValue);
} while (pendingRequests.ContainsKey(newInjId));
pendingRequests.Add(newInjId, src);
+ this.lastInputId = newInjId;
}
C_QUEUE_INPUT packet = new C_QUEUE_INPUT()
@@ -120,6 +125,42 @@ namespace HunterPie.Core.Input
return await src.Task;
}
+ public static async Task InterruptInput(uint id)
+ {
+ var packet = new C_INTERRUPT_INPUT()
+ {
+ header = new Header() { opcode = OPCODE.InterruptInput, version = 1 },
+ type = INPUT_INTERRUPT_TYPE.by_id,
+ inputId = id
+ };
+
+ await Client.ToServer(packet);
+ }
+
+ public static async Task InterruptAllInput()
+ {
+ var packet = new C_INTERRUPT_INPUT()
+ {
+ header = new Header() { opcode = OPCODE.InterruptInput, version = 1 },
+ type = INPUT_INTERRUPT_TYPE.clear,
+ inputId = 0
+ };
+
+ await Client.ToServer(packet);
+ }
+
+ public static async Task InterruptLastInput()
+ {
+ var packet = new C_INTERRUPT_INPUT()
+ {
+ header = new Header() { opcode = OPCODE.InterruptInput, version = 1 },
+ type = INPUT_INTERRUPT_TYPE.last,
+ inputId = 0
+ };
+
+ await Client.ToServer(packet);
+ }
+
private static byte[] CalculateInputs(char[] keys)
{
byte[] bits = new byte[32];

diff:
<DefineConstants>DEBUG;TRACE</DefineConstants>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
- <LangVersion>7.3</LangVersion>
+ <LangVersion>9</LangVersion>
<ErrorReport>prompt</ErrorReport>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>

diff:
@@ -203,6 +203,13 @@ namespace HunterPie.Native.Connection
OnDamageDealResponse?.Invoke(this, pkt);
break;
}
+
+ case OPCODE.LogMessage:
+ {
+ S_LOG_MESSAGE pkt = PacketParser.Deserialize<S_LOG_MESSAGE>(buffer);
+ Debugger.Write("[Native] " + pkt.message, "#FFC88DF2");
+ break;
+ }
}
}

diff:
@@ -100,6 +100,27 @@ namespace HunterPie.Native.Connection.Packets
public bool isTenderized;
public int attackId;
}
+ public enum INPUT_INTERRUPT_TYPE
+ {
+ last,
+ by_id,
+ clear
+ };
+ [StructLayout(LayoutKind.Sequential)]
+ public struct C_INTERRUPT_INPUT
+ {
+ public Header header;
+ public INPUT_INTERRUPT_TYPE type;
+ public uint inputId;
+ }
+
+ [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
+ public struct S_LOG_MESSAGE
+ {
+ public Header header;
+ [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 256)]
+ public string message;
+ }
#region Other Data structures

diff:
QueueInput,
SendChatMessage,
SendSystemMessage,
- DealDamage
+ DealDamage,
+ InterruptInput,
+ LogMessage
}
}

diff:
@@ -46,6 +46,18 @@ namespace Connection
unsigned int inputId;
};
+ enum INPUT_INTERRUPT_TYPE
+ {
+ last,
+ by_id,
+ clear
+ };
+ typedef struct C_INTERRUPT_INPUT : I_PACKET
+ {
+ INPUT_INTERRUPT_TYPE type;
+ unsigned int inputId;
+ };
+
typedef struct C_SEND_CHAT : I_PACKET
{
char message[256];
@@ -68,5 +80,10 @@ namespace Connection
int attackId;
};
+ typedef struct S_LOG_MESSAGE : I_PACKET
+ {
+ char message[256];
+ };
+
}
}

diff:
@@ -14,7 +14,9 @@ namespace Connection
QueueInput,
SendChatMessage,
SendSystemMessage,
- DealDamage
+ DealDamage,
+ InterruptInput,
+ LogMessage
};
typedef struct header

diff:
@@ -174,6 +174,17 @@ void Connection::Server::receivePackets(char buffer[DEFAULT_BUFFER_SIZE])
break;
}
+ case OPCODE::InterruptInput:
+ {
+ LOG("-> C_INTERRUPT_INPUT\n");
+ C_INTERRUPT_INPUT pkt = *reinterpret_cast<C_INTERRUPT_INPUT*>(buffer);
+
+ inputQueueMutex.lock();
+ inputInterruptQueue.push(&pkt);
+ inputQueueMutex.unlock();
+ break;
+ }
+
case OPCODE::SendChatMessage:
{
C_SEND_CHAT pkt = *reinterpret_cast<C_SEND_CHAT*>(buffer);

diff:
@@ -16,6 +16,7 @@ namespace Connection
{
public:
std::queue<Packets::input*> inputInjectionToQueue;
+ std::queue<Packets::C_INTERRUPT_INPUT*> inputInterruptQueue;
std::mutex inputQueueMutex;
static Server* getInstance();

diff:
@@ -56,12 +56,33 @@ void Game::Damage::HunterPie_DealDamage(
void Game::Damage::InitializeHooks()
{
- MH_CreateHook(
+ MH_STATUS s = MH_CreateHook(
(fnDealDamage)FunDealDamage,
&HunterPie_DealDamage,
reinterpret_cast<LPVOID*>(&originalDealDamage)
);
+ S_LOG_MESSAGE packet{};
+ packet.header.opcode = OPCODE::LogMessage;
+ packet.header.version = 1;
+
+ if (s != MH_OK) {
+ LOG("Game::Damage::InitializeHooks %s\n", MH_StatusToString(s));
+
+ sprintf_s(packet.message
+ , sizeof(packet.message)
+ , "Error on Damage hook %s", MH_StatusToString(s)
+ );
+ }
+ else
+ {
+ sprintf_s(packet.message
+ , sizeof(packet.message)
+ , "Damage Hook initialized successfully"
+ );
+ }
+ Connection::Server::getInstance()->sendData(&packet, sizeof(packet));
+
}
bool Game::Damage::LoadAddress(uintptr_t ptrs[128])

diff:
@@ -25,8 +25,26 @@ void Game::Input::InitializeHooks()
&HunterPie_HandleKeyboardInput,
reinterpret_cast<LPVOID*>(&originalHandleInput));
- if (s != MH_OK)
- LOG("%s\n", MH_StatusToString(s));
+ S_LOG_MESSAGE packet{};
+ packet.header.opcode = OPCODE::LogMessage;
+ packet.header.version = 1;
+
+ if (s != MH_OK) {
+ LOG("Game::Input::InitializeHooks %s\n", MH_StatusToString(s));
+
+ sprintf_s(packet.message
+ , sizeof(packet.message)
+ , "Error on Input hook %s", MH_StatusToString(s)
+ );
+ }
+ else
+ {
+ sprintf_s(packet.message
+ , sizeof(packet.message)
+ , "Input Hook initialized successfully"
+ );
+ }
+ Connection::Server::getInstance()->sendData(&packet, sizeof(packet));
}
void InjectInput(sMhKeyboard* keyboard)
@@ -35,11 +53,14 @@ void InjectInput(sMhKeyboard* keyboard)
{
input* currentInput = inputInjectionQueue.front();
+ if (currentInput->nFrames != 0)
+ {
if (currentInput->ignoreOriginalInputs)
{
memcpy(keyboard->firstArray, currentInput->inputArray, sizeof(keyboard->firstArray));
memcpy(keyboard->thirdArray, currentInput->inputArray, sizeof(keyboard->thirdArray));
- } else
+ }
+ else
{
for (int i = 0; i < sizeof(keyboard->firstArray); i++)
{
@@ -47,6 +68,7 @@ void InjectInput(sMhKeyboard* keyboard)
keyboard->thirdArray[i] |= currentInput->inputArray[i];
}
}
+ }
keyboardInputElapsedFrames++;
if (currentInput->nFrames <= keyboardInputElapsedFrames)
@@ -58,7 +80,7 @@ void InjectInput(sMhKeyboard* keyboard)
delete inputInjectionQueue.front();
- inputInjectionQueue.pop();
+ inputInjectionQueue.pop_front();
Connection::Server::getInstance()->sendData(&packet, sizeof(packet));
@@ -77,14 +99,60 @@ void Game::Input::HunterPie_HandleKeyboardInput(sMhKeyboard* keyboard)
{
std::queue<input*>& sharedQueue = Server::getInstance()->inputInjectionToQueue;
+ std::queue<C_INTERRUPT_INPUT*>& interruptQueue = Server::getInstance()->inputInterruptQueue;
if (!sharedQueue.empty())
{
input* toQueue = sharedQueue.front();
- inputInjectionQueue.push(toQueue);
+ inputInjectionQueue.push_front(toQueue);
sharedQueue.pop();
}
+ while (!interruptQueue.empty())
+ {
+ switch (const auto pkt = interruptQueue.front(); pkt->type)
+ {
+ case last: {
+ if (!inputInjectionQueue.empty()) {
+ input* front = inputInjectionQueue.front();
+ front->nFrames = 0;
+ LOG(" itrp last - ok\n");
+ }
+ else
+ {
+ LOG(" itrp last - empty\n");
+ }
+ break;
+ }
+
+ case by_id: {
+ bool ok = false;
+
+ for (auto input : inputInjectionQueue)
+ {
+ if (input->injectionId == pkt->inputId)
+ {
+ ok = true;
+ input->nFrames = 0;
+ break;
+ }
+ }
+ LOG(" itrp by id %s\n", ok ? "OK" : "not found");
+ break;
+ }
+
+ case clear:
+ for (auto input : inputInjectionQueue)
+ {
+ input->nFrames = 0;
+ }
+ LOG(" itrp all %llu\n", inputInjectionQueue.size());
+ break;
+ }
+
+ interruptQueue.pop();
+ }
+
inputQueueMtx.unlock();
}

diff:
@@ -29,6 +29,6 @@ namespace Game
void InitializeHooks();
bool LoadAddress(uintptr_t ptrs[128]);
- static std::queue<Connection::Packets::input*> inputInjectionQueue;
+ static std::deque<Connection::Packets::input*> inputInjectionQueue;
}
}


diff:
@@ -65,6 +65,16 @@ void sched_lend_prio(thread_t *td, prio_t prio);
*/
void sched_unlend_prio(thread_t *td, prio_t prio);
+/*! \brief Set thread's \a td_base_prio and \a td_prio to \a prio.
+ *
+ * Base priority \a td_base_prio is changed unconditionally.
+ * Active priority \a td_prio is changed on condition that we are not lowering
+ * priority of a thread that borrows priority via \a sched_lend_prio.
+ *
+ * \note Must be called with \a td_spin acquired!
+ */
+void sched_set_prio(thread_t *td, prio_t prio);
+
/*! \brief Takes care of run-time accounting for current thread.
*
* \note Must be called from interrupt context.

diff:
@@ -50,11 +50,11 @@ void sched_wakeup(thread_t *td) {
oldtd->td_flags |= TDF_NEEDSWITCH;
}
-/*! \brief Adjust thread's priority.
+/*! \brief Set thread's active priority \a td_prio to \a prio.
*
- * \note Must be called with td_spin acquired!
+ * \note Must be called with \a td_spin acquired!
*/
-static void sched_set_priority(thread_t *td, prio_t prio) {
+static void sched_set_active_prio(thread_t *td, prio_t prio) {
assert(spin_owned(td->td_spin));
if (td->td_prio == prio)
@@ -70,12 +70,24 @@ static void sched_set_priority(thread_t *td, prio_t prio) {
}
}
+void sched_set_prio(thread_t *td, prio_t prio) {
+ assert(spin_owned(td->td_spin));
+
+ td->td_base_prio = prio;
+
+ /* If thread is borrowing priority, don't lower its active priority. */
+ if (td->td_flags & TDF_BORROWING && td->td_prio > prio)
+ return;
+
+ sched_set_active_prio(td, prio);
+}
+
void sched_lend_prio(thread_t *td, prio_t prio) {
assert(spin_owned(td->td_spin));
assert(td->td_prio < prio);
td->td_flags |= TDF_BORROWING;
- sched_set_priority(td, prio);
+ sched_set_active_prio(td, prio);
}
void sched_unlend_prio(thread_t *td, prio_t prio) {
@@ -83,7 +95,7 @@ void sched_unlend_prio(thread_t *td, prio_t prio) {
if (prio <= td->td_base_prio) {
td->td_flags &= ~TDF_BORROWING;
- sched_set_priority(td, td->td_base_prio);
+ sched_set_active_prio(td, td->td_base_prio);
} else
sched_lend_prio(td, prio);
}

license: BSD 3-Clause New or Revised License

repo: cahirwpz/mimiker

original_message: Added non-static set priority function to scheduler (#420)


diff:
@@ -25,6 +25,9 @@ typedef struct thread thread_t;
#define MTX_RECURSE 1
/*! \brief Basic synchronization primitive.
+ *
+ * \warning You must never access mutex fields directly outside of its
+ * implementation!
*
* \note Mutex must be released by its owner!
*/
@@ -53,6 +56,13 @@ void mtx_init(mtx_t *m, unsigned type);
/*! \brief Check if calling thread is the owner of \a m. */
bool mtx_owned(mtx_t *m);
+/*! \brief Fetch mutex owner.
+ *
+ * \note The function is used by some tests. */
+static inline volatile thread_t *mtx_owner(mtx_t *m) {
+ return m->m_owner;
+}
+
/*! \brief Locks the mutex (with custom \a waitpt) */
void _mtx_lock(mtx_t *m, const void *waitpt);

diff:
@@ -150,4 +150,29 @@ void thread_join(thread_t *td);
* verify test success. */
void thread_reap(void);
+/* Please use following functions to read state of a thread! */
+static inline bool td_is_ready(thread_t *td) {
+ return td->td_state == TDS_READY;
+}
+
+static inline bool td_is_dead(thread_t *td) {
+ return td->td_state == TDS_DEAD;
+}
+
+static inline bool td_is_running(thread_t *td) {
+ return td->td_state == TDS_RUNNING;
+}
+
+static inline bool td_is_inactive(thread_t *td) {
+ return td->td_state == TDS_INACTIVE;
+}
+
+static inline bool td_is_sleeping(thread_t *td) {
+ return td->td_state == TDS_SLEEPING;
+}
+
+static inline bool td_is_borrowing(thread_t *td) {
+ return td->td_flags & TDF_BORROWING;
+}
+
#endif /* !_SYS_THREAD_H_ */

diff:
@@ -32,7 +32,7 @@ void sched_add(thread_t *td) {
void sched_wakeup(thread_t *td) {
assert(spin_owned(td->td_spin));
assert(td != thread_self());
- assert(td->td_state == TDS_SLEEPING || td->td_state == TDS_INACTIVE);
+ assert(td_is_sleeping(td) || td_is_inactive(td));
/* Update sleep time. */
timeval_t now = get_uptime();
@@ -60,7 +60,7 @@ static void sched_set_active_prio(thread_t *td, prio_t prio) {
if (td->td_prio == prio)
return;
- if (td->td_state == TDS_READY) {
+ if (td_is_ready(td)) {
/* Thread is on a run queue. */
runq_remove(&runq, td);
td->td_prio = prio;
@@ -76,7 +76,7 @@ void sched_set_prio(thread_t *td, prio_t prio) {
td->td_base_prio = prio;
/* If thread is borrowing priority, don't lower its active priority. */
- if (td->td_flags & TDF_BORROWING && td->td_prio > prio)
+ if (td_is_borrowing(td) && td->td_prio > prio)
return;
sched_set_active_prio(td, prio);
@@ -121,7 +121,7 @@ void sched_switch(void) {
thread_t *td = thread_self();
assert(spin_owned(td->td_spin));
- assert(td->td_state != TDS_RUNNING);
+ assert(!td_is_running(td));
td->td_flags &= ~(TDF_SLICEEND | TDF_NEEDSWITCH);
@@ -130,14 +130,14 @@ void sched_switch(void) {
timeval_t diff = timeval_sub(&now, &td->td_last_rtime);
td->td_rtime = timeval_add(&td->td_rtime, &diff);
- if (td->td_state == TDS_READY) {
+ if (td_is_ready(td)) {
/* Idle threads need not to be inserted into the run queue. */
if (td != PCPU_GET(idle_thread))
runq_add(&runq, td);
- } else if (td->td_state == TDS_SLEEPING) {
+ } else if (td_is_sleeping(td)) {
/* Record when the thread fell asleep. */
td->td_last_slptime = now;
- } else if (td->td_state == TDS_DEAD) {
+ } else if (td_is_dead(td)) {
/* Don't add dead threads to run queue. */
}

diff:
@@ -73,7 +73,7 @@ int sig_send(proc_t *proc, signo_t sig) {
SCOPED_MTX_LOCK(&target->td_lock);
/* If the thread is already dead, don't post a signal. */
- if (target->td_state == TDS_DEAD)
+ if (td_is_dead(target))
return -EINVAL;
WITH_MTX_LOCK (&target->td_proc->p_lock) {

diff:
@@ -91,7 +91,7 @@ thread_t *thread_create(const char *name, void (*fn)(void *), void *arg) {
}
void thread_delete(thread_t *td) {
- assert(td->td_state == TDS_DEAD);
+ assert(td_is_dead(td));
assert(td->td_sleepqueue != NULL);
klog("Freeing up thread %ld {%p}", td->td_tid, td);
@@ -154,7 +154,7 @@ void thread_join(thread_t *otd) {
klog("Join %ld {%p} with %ld {%p}", td->td_tid, td, otd->td_tid, otd);
- while (otd->td_state != TDS_DEAD)
+ while (!td_is_dead(otd))
cv_wait(&otd->td_waitcv, &otd->td_lock);
}

original_message: Cleanup: do not access directly thread state and mutex owner (#434)


message: Move callout execution to top-half

diff:
@@ -16,7 +16,8 @@ typedef struct callout {
unsigned c_index; /* index of bucket this callout is assigned to */
} callout_t;
-#define CALLOUT_ACTIVE 0x0001 /* callout is currently being executed */
+/* callout has been delegated to callout thread and will be executed soon */
+#define CALLOUT_ACTIVE 0x0001
#define CALLOUT_PENDING 0x0002 /* callout is waiting for timeout */
/*
@@ -33,13 +34,16 @@ void callout_setup_relative(callout_t *handle, systime_t time, timeout_t fn,
void *arg);
/*
- * Delete a callout from the queue without handling it.
+ * Cancel a callout if it is currently pending.
+ *
+ * \return True if the callout was pending and has been stopped, false if the
+ * callout has already been delegated to callout thread or executed.
*/
-void callout_stop(callout_t *handle);
+bool callout_stop(callout_t *handle);
/*
- * Process all callouts that happened since last time.
- * A callout is removed from the queue when handled.
+ * Process all callouts that happened since last time and delegate them to
+ * callout thread.
*/
void callout_process(systime_t now);

diff:
#include <spinlock.h>
#include <sysinit.h>
#include <sleepq.h>
+#include <thread.h>
+#include <sched.h>
#include <interrupt.h>
/* Note: If the difference in time between ticks is greater than the number of
@@ -40,6 +42,32 @@ static inline callout_list_t *ci_list(int i) {
return &ci.heads[i];
}
+static callout_list_t delegated;
+
+static void callout_thread(void *arg) {
+ while (true) {
+ callout_t *elem;
+
+ WITH_INTR_DISABLED {
+ while (TAILQ_EMPTY(&delegated)) {
+ sleepq_wait(&delegated, NULL);
+ }
+
+ elem = TAILQ_FIRST(&delegated);
+ TAILQ_REMOVE(&delegated, elem, c_link);
+ }
+
+ assert(callout_is_active(elem));
+ assert(!callout_is_pending(elem));
+
+ /* Execute callout's function. */
+ elem->c_func(elem->c_arg);
+ callout_clear_active(elem);
+ /* Wake threads that wait for execution of this callout in callout_drain. */
+ sleepq_broadcast(elem);
+ }
+}
+
static void callout_init(void) {
bzero(&ci, sizeof(ci));
@@ -47,12 +75,19 @@ static void callout_init(void) {
for (int i = 0; i < CALLOUT_BUCKETS; i++)
TAILQ_INIT(ci_list(i));
+
+ TAILQ_INIT(&delegated);
+
+ thread_t *td =
+ thread_create("callout-thread", callout_thread, NULL, prio_kthread(0));
+ sched_add(td);
}
static void _callout_setup(callout_t *handle, systime_t time, timeout_t fn,
void *arg) {
assert(spin_owned(&ci.lock));
assert(!callout_is_pending(handle));
+ assert(!callout_is_active(handle));
int index = time % CALLOUT_BUCKETS;
@@ -81,7 +116,7 @@ void callout_setup_relative(callout_t *handle, systime_t time, timeout_t fn,
_callout_setup(handle, now + time, fn, arg);
}
-void callout_stop(callout_t *handle) {
+bool callout_stop(callout_t *handle) {
SCOPED_SPIN_LOCK(&ci.lock);
klog("Remove callout {%p} at %ld.", handle, handle->c_time);
@@ -89,12 +124,15 @@ void callout_stop(callout_t *handle) {
if (callout_is_pending(handle)) {
callout_clear_pending(handle);
TAILQ_REMOVE(ci_list(handle->c_index), handle, c_link);
+ return true;
}
+
+ return false;
}
/*
- * Handle all timeouted callouts from queues between last position and current
- * position.
+ * Process all timeouted callouts from queues between last position and current
+ * position and delegate them to callout thread.
*/
void callout_process(systime_t time) {
unsigned int last_bucket;
@@ -108,8 +146,6 @@ void callout_process(systime_t time) {
last_bucket = time % CALLOUT_BUCKETS;
}
- callout_list_t detached;
-
/* We are in kernel's bottom half. */
assert(intr_disabled());
@@ -117,28 +153,17 @@ void callout_process(systime_t time) {
callout_list_t *head = ci_list(current_bucket);
callout_t *elem, *next;
- TAILQ_INIT(&detached);
-
- /* Detach triggered callouts from the queue. */
+ /* Detach triggered callouts from ci_list queue. */
TAILQ_FOREACH_SAFE(elem, head, c_link, next) {
if (elem->c_time <= time) {
callout_set_active(elem);
callout_clear_pending(elem);
TAILQ_REMOVE(head, elem, c_link);
- TAILQ_INSERT_TAIL(&detached, elem, c_link);
+ /* Attach elem to callout thread's queue. */
+ TAILQ_INSERT_TAIL(&delegated, elem, c_link);
}
}
- /* Now safely call them one by one. */
- TAILQ_FOREACH_SAFE(elem, &detached, c_link, next) {
- TAILQ_REMOVE(&detached, elem, c_link);
- elem->c_func(elem->c_arg);
- /* Wake threads that wait for execution of this callout in function
- * callout_drain. */
- sleepq_broadcast(elem);
- callout_clear_active(elem);
- }
-
if (current_bucket == last_bucket)
break;
@@ -146,16 +171,16 @@ void callout_process(systime_t time) {
}
ci.last = time;
+
+ /* Wake callout thread. */
+ if (!TAILQ_EMPTY(&delegated)) {
+ sleepq_signal(&delegated);
+ }
}
bool callout_drain(callout_t *handle) {
- /* A callout may be in active state only in callout_process,
- * which is called in bottom half (with interrupts disabled),
- * so we can't notice it. */
- assert(!callout_is_active(handle));
-
WITH_INTR_DISABLED {
- if (callout_is_pending(handle)) {
+ if (callout_is_pending(handle) || callout_is_active(handle)) {
sleepq_wait(handle, NULL);
return true;
}
@@ -164,4 +189,4 @@ bool callout_drain(callout_t *handle) {
return false;
}
-SYSINIT_ADD(callout, callout_init, NODEPS);
+SYSINIT_ADD(callout, callout_init, DEPS("sched"));

diff:
#include <ktest.h>
#include <interrupt.h>
-static void periodic_callout(void *arg) {
- callout_t *callout = arg;
- callout_setup_relative(callout, 1, periodic_callout, arg);
-}
-
-static int test_callout_sync(void) {
- const int K = 50;
- const int N = 7;
-
- callout_t callout[N];
- bzero(callout, sizeof(callout_t) * N);
-
- for (int j = 0; j < K; j++) {
- for (int i = 0; i < N; i++)
- callout_setup_relative(&callout[i], 1, periodic_callout, &callout[i]);
-
- for (int i = 0; i < N; i++)
- callout_stop(&callout[i]);
- }
-
- return KTEST_SUCCESS;
-}
-
static int counter;
/* This test verifies whether callouts work at all. */
@@ -59,12 +36,9 @@ static int order[ORDER_N] = {2, 5, 4, 6, 9, 0, 8, 1, 3, 7};
static int current;
static void callout_ordered(void *arg) {
- /* There is no race condition here as callouts run in bottom half (with
- * interrupts disabled). */
- assert(intr_disabled());
-
int ord = (int)arg;
assert(current == ord);
+ /* There is no race condition here since callouts don't run concurrently. */
current++;
}
@@ -119,7 +93,6 @@ static int test_callout_drain(void) {
return KTEST_SUCCESS;
}
-KTEST_ADD(callout_sync, test_callout_sync, 0);
KTEST_ADD(callout_simple, test_callout_simple, 0);
KTEST_ADD(callout_order, test_callout_order, 0);
KTEST_ADD(callout_stop, test_callout_stop, 0);

diff:
static volatile int wakeups;
-static void test_thread(void *p) {
- while (wakeups < (int)p) {
+static void test_thread(void *expected) {
+ while (wakeups < (int)expected) {
WITH_NO_PREEMPTION {
wakeups++;
}
@@ -16,23 +16,22 @@ static void test_thread(void *p) {
}
}
-static void periodic_callout(void *arg) {
- callout_t *callout = arg;
- callout_setup_relative(callout, 1, periodic_callout, callout);
+static void wake_threads_up(void *arg) {
sleepq_broadcast(&test_thread);
}
static int test_sleepq_sync(void) {
- const int K = 5;
- const int N = 20;
+ const int K = 5; /* number of test threads */
+ const int N = 20; /* number of expected wakeups */
wakeups = 0;
- callout_t callout;
- bzero(&callout, sizeof(callout_t));
+ callout_t callout[N];
+ bzero(callout, sizeof(callout_t) * N);
thread_t *td[K];
- callout_setup_relative(&callout, 1, periodic_callout, &callout);
+ for (int i = 0; i < N; i++)
+ callout_setup_relative(&callout[i], i + 1, wake_threads_up, NULL);
for (int i = 0; i < K; i++) {
td[i] =
@@ -43,7 +42,8 @@ static int test_sleepq_sync(void) {
for (int i = 0; i < K; i++)
thread_join(td[i]);
- callout_stop(&callout);
+ for (int i = 0; i < N; i++)
+ callout_drain(&callout[i]);
return KTEST_SUCCESS;
}

original_message: Move callout execution to top-half (#485)


message: Fix UART driver

diff:
@@ -74,11 +74,11 @@ static int ns16550_write(vnode_t *v, uio_t *uio) {
/* For simplicity, copy from the user space one byte at a time. */
if ((error = uiomove(&byte, 1, uio)))
return error;
- while (!ringbuf_putb(&ns16550->rx_buf, byte))
+ while (!ringbuf_putb(&ns16550->tx_buf, byte))
cv_wait(&ns16550->tx_nonfull, &ns16550->lock);
}
if (in(uart, LSR) & LSR_THRE)
- if (ringbuf_getb(&ns16550->rx_buf, &byte))
+ if (ringbuf_getb(&ns16550->tx_buf, &byte))
out(uart, THR, byte);
}

original_message: Fix UART driver (#535)


diff:
# vim: syntax=dockerfile:
-# TODO use :latest when it will be available on docker hub
-FROM cahirwpz/mimiker-circleci:1.6
+FROM cahirwpz/mimiker-circleci:latest
RUN addgroup --gid ${MIMIKER_GID} mimiker && \
adduser --uid ${MIMIKER_UID} --disabled-password \

diff:
@@ -11,7 +11,7 @@ To set up environment:
make run
```
-The above script will
+The above script will:
- generate Dockerfile based on user's uid and gid
- attach volume with mimiker source code from your machine
- compile mimiker

original_message: Change default base of developer's docker image to latest version for CircleCI (#537)


message: Rename intr_chain to intr_event

diff:
@@ -57,14 +57,14 @@ typedef enum {
typedef intr_filter_t driver_filter_t(void *);
typedef void driver_intr_t(void *);
-typedef struct intr_chain intr_chain_t;
+typedef struct intr_event intr_event_t;
typedef struct intr_handler intr_handler_t;
struct intr_handler {
TAILQ_ENTRY(intr_handler) ih_list;
driver_filter_t *ih_filter; /* driver interrupt filter function */
driver_intr_t *ih_handler; /* driver interrupt handler function */
- intr_chain_t *ih_chain; /* chain we are connected to */
+ intr_event_t *ih_event; /* event we are connected to */
void *ih_argument; /* argument to pass to the handler */
char *ih_name; /* name of the handler */
prio_t ih_prio; /* priority of the handler */
@@ -78,21 +78,21 @@ typedef TAILQ_HEAD(, intr_handler) intr_handler_list_t;
.ih_name = (desc), .ih_prio = (prio) \
}
-typedef struct intr_chain {
- spin_t ic_lock;
- TAILQ_ENTRY(intr_chain) ic_list;
- intr_handler_list_t ic_handlers; /* interrupt handlers */
- const char *ic_name; /* individual chain name */
- unsigned ic_irq; /* physical interrupt request line number */
- unsigned ic_count; /* number of handlers attached */
-} intr_chain_t;
-
-typedef TAILQ_HEAD(, intr_chain) intr_chain_list_t;
-
-void intr_chain_init(intr_chain_t *ic, unsigned irq, const char *name);
-void intr_chain_register(intr_chain_t *ic);
-void intr_chain_add_handler(intr_chain_t *ic, intr_handler_t *ih);
-void intr_chain_remove_handler(intr_handler_t *ih);
-void intr_chain_run_handlers(intr_chain_t *ic);
+typedef struct intr_event {
+ spin_t ie_lock;
+ TAILQ_ENTRY(intr_event) ie_list;
+ intr_handler_list_t ie_handlers; /* interrupt handlers */
+ const char *ie_name; /* individual event name */
+ unsigned ie_irq; /* physical interrupt request line number */
+ unsigned ie_count; /* number of handlers attached */
+} intr_event_t;
+
+typedef TAILQ_HEAD(, intr_event) intr_event_list_t;
+
+void intr_event_init(intr_event_t *ie, unsigned irq, const char *name);
+void intr_event_register(intr_event_t *ie);
+void intr_event_add_handler(intr_event_t *ie, intr_handler_t *ih);
+void intr_event_remove_handler(intr_handler_t *ih);
+void intr_event_run_handlers(intr_event_t *ie);
#endif /* _SYS_INTERRUPT_H_ */

diff:
@@ -57,7 +57,7 @@ typedef struct gt_pci_state {
rman_t isa_io_rman;
intr_handler_t intr_handler;
- intr_chain_t intr_chain[16];
+ intr_event_t intr_event[16];
uint16_t imask;
uint16_t elcr;
@@ -155,10 +155,10 @@ static void gt_pci_intr_setup(device_t *pcib, unsigned irq,
assert(pcib->parent->driver == &gt_pci_bus.driver);
gt_pci_state_t *gtpci = pcib->parent->state;
- intr_chain_t *chain = &gtpci->intr_chain[irq];
- WITH_SPIN_LOCK (&chain->ic_lock) {
- intr_chain_add_handler(chain, handler);
- if (chain->ic_count == 1)
+ intr_event_t *event = &gtpci->intr_event[irq];
+ WITH_SPIN_LOCK (&event->ie_lock) {
+ intr_event_add_handler(event, handler);
+ if (event->ie_count == 1)
gt_pci_unmask_irq(gtpci, irq);
}
}
@@ -167,11 +167,11 @@ static void gt_pci_intr_teardown(device_t *pcib, intr_handler_t *handler) {
assert(pcib->parent->driver == &gt_pci_bus.driver);
gt_pci_state_t *gtpci = pcib->parent->state;
- intr_chain_t *chain = handler->ih_chain;
- WITH_SPIN_LOCK (&chain->ic_lock) {
- if (chain->ic_count == 1)
- gt_pci_mask_irq(gtpci, chain->ic_irq);
- intr_chain_remove_handler(handler);
+ intr_event_t *event = handler->ih_event;
+ WITH_SPIN_LOCK (&event->ie_lock) {
+ if (event->ie_count == 1)
+ gt_pci_mask_irq(gtpci, event->ie_irq);
+ intr_event_remove_handler(handler);
}
}
@@ -210,9 +210,9 @@ static intr_filter_t gt_pci_intr(void *data) {
irq = (irq & OCW3_POLL_PENDING) ? (OCW3_POLL_IRQ(irq) + 8) : 2;
}
- /* Irq 2 is used for PIC chaining, ignore it. */
+ /* Irq 2 is used for PIC eventing, ignore it. */
if (irq != 2)
- intr_chain_run_handlers(&gtpci->intr_chain[irq]);
+ intr_event_run_handlers(&gtpci->intr_event[irq]);
/* Send a specific EOI to slave PIC... */
if (irq > 7) {
@@ -228,10 +228,10 @@ static intr_filter_t gt_pci_intr(void *data) {
return IF_FILTERED;
}
-static inline void gt_pci_intr_chain_init(gt_pci_state_t *gtpci, unsigned irq,
+static inline void gt_pci_intr_event_init(gt_pci_state_t *gtpci, unsigned irq,
const char *name) {
- intr_chain_init(&gtpci->intr_chain[irq], irq, name);
- intr_chain_register(&gtpci->intr_chain[irq]);
+ intr_event_init(&gtpci->intr_event[irq], irq, name);
+ intr_event_register(&gtpci->intr_event[irq]);
}
#define MALTA_CORECTRL_SIZE (MALTA_CORECTRL_END - MALTA_CORECTRL_BASE + 1)
@@ -284,22 +284,22 @@ static int gt_pci_attach(device_t *pcib) {
bus_write_1(io, PIIX_REG_ELCR + 0, LO(gtpci->elcr));
bus_write_1(io, PIIX_REG_ELCR + 1, HI(gtpci->elcr));
- gt_pci_intr_chain_init(gtpci, 0, "timer");
- gt_pci_intr_chain_init(gtpci, 1, "kbd"); /* kbd controller (keyboard) */
- gt_pci_intr_chain_init(gtpci, 2, "pic-slave"); /* PIC cascade */
- gt_pci_intr_chain_init(gtpci, 3, "uart(1)"); /* COM 2 */
- gt_pci_intr_chain_init(gtpci, 4, "uart(0)"); /* COM 1 */
- gt_pci_intr_chain_init(gtpci, 5, "unused(0)");
- gt_pci_intr_chain_init(gtpci, 6, "floppy"); /* floppy */
- gt_pci_intr_chain_init(gtpci, 7, "parallel"); /* centronics */
- gt_pci_intr_chain_init(gtpci, 8, "rtc"); /* RTC */
- gt_pci_intr_chain_init(gtpci, 9, "i2c"); /* I2C */
- gt_pci_intr_chain_init(gtpci, 10, "unused(1)");
- gt_pci_intr_chain_init(gtpci, 11, "unused(2)");
- gt_pci_intr_chain_init(gtpci, 12, "mouse"); /* kbd controller (mouse) */
- gt_pci_intr_chain_init(gtpci, 13, "unused(3)");
- gt_pci_intr_chain_init(gtpci, 14, "ide(0)"); /* IDE primary */
- gt_pci_intr_chain_init(gtpci, 15, "ide(1)"); /* IDE secondary */
+ gt_pci_intr_event_init(gtpci, 0, "timer");
+ gt_pci_intr_event_init(gtpci, 1, "kbd"); /* kbd controller (keyboard) */
+ gt_pci_intr_event_init(gtpci, 2, "pic-slave"); /* PIC cascade */
+ gt_pci_intr_event_init(gtpci, 3, "uart(1)"); /* COM 2 */
+ gt_pci_intr_event_init(gtpci, 4, "uart(0)"); /* COM 1 */
+ gt_pci_intr_event_init(gtpci, 5, "unused(0)");
+ gt_pci_intr_event_init(gtpci, 6, "floppy"); /* floppy */
+ gt_pci_intr_event_init(gtpci, 7, "parallel"); /* centronics */
+ gt_pci_intr_event_init(gtpci, 8, "rtc"); /* RTC */
+ gt_pci_intr_event_init(gtpci, 9, "i2c"); /* I2C */
+ gt_pci_intr_event_init(gtpci, 10, "unused(1)");
+ gt_pci_intr_event_init(gtpci, 11, "unused(2)");
+ gt_pci_intr_event_init(gtpci, 12, "mouse"); /* kbd controller (mouse) */
+ gt_pci_intr_event_init(gtpci, 13, "unused(3)");
+ gt_pci_intr_event_init(gtpci, 14, "ide(0)"); /* IDE primary */
+ gt_pci_intr_event_init(gtpci, 15, "ide(1)"); /* IDE secondary */
pci_bus_enumerate(pcib);

diff:
@@ -36,10 +36,10 @@ bool mips_intr_disabled(void) {
return (mips32_getsr() & SR_IE) == 0;
}
-static intr_chain_t mips_intr_chain[8];
+static intr_event_t mips_intr_event[8];
-#define MIPS_INTR_CHAIN(irq, name) \
- intr_chain_init(&mips_intr_chain[irq], irq, name)
+#define MIPS_INTR_EVENT(irq, name) \
+ intr_event_init(&mips_intr_event[irq], irq, name)
void mips_intr_init(void) {
/*
@@ -55,26 +55,26 @@ void mips_intr_init(void) {
/* Set vector spacing to 0. */
mips32_set_c0(C0_INTCTL, INTCTL_VS_0);
- /* Initialize software interrupts handler chains. */
- MIPS_INTR_CHAIN(MIPS_SWINT0, "swint(0)");
- MIPS_INTR_CHAIN(MIPS_SWINT1, "swint(1)");
- /* Initialize hardware interrupts handler chains. */
- MIPS_INTR_CHAIN(MIPS_HWINT0, "hwint(0)");
- MIPS_INTR_CHAIN(MIPS_HWINT1, "hwint(1)");
- MIPS_INTR_CHAIN(MIPS_HWINT2, "hwint(2)");
- MIPS_INTR_CHAIN(MIPS_HWINT3, "hwint(3)");
- MIPS_INTR_CHAIN(MIPS_HWINT4, "hwint(4)");
- MIPS_INTR_CHAIN(MIPS_HWINT5, "hwint(5)");
+ /* Initialize software interrupts handler events. */
+ MIPS_INTR_EVENT(MIPS_SWINT0, "swint(0)");
+ MIPS_INTR_EVENT(MIPS_SWINT1, "swint(1)");
+ /* Initialize hardware interrupts handler events. */
+ MIPS_INTR_EVENT(MIPS_HWINT0, "hwint(0)");
+ MIPS_INTR_EVENT(MIPS_HWINT1, "hwint(1)");
+ MIPS_INTR_EVENT(MIPS_HWINT2, "hwint(2)");
+ MIPS_INTR_EVENT(MIPS_HWINT3, "hwint(3)");
+ MIPS_INTR_EVENT(MIPS_HWINT4, "hwint(4)");
+ MIPS_INTR_EVENT(MIPS_HWINT5, "hwint(5)");
for (unsigned i = 0; i < 8; i++)
- intr_chain_register(&mips_intr_chain[i]);
+ intr_event_register(&mips_intr_event[i]);
}
void mips_intr_setup(intr_handler_t *handler, unsigned irq) {
- intr_chain_t *chain = &mips_intr_chain[irq];
- WITH_SPIN_LOCK (&chain->ic_lock) {
- intr_chain_add_handler(chain, handler);
- if (chain->ic_count == 1) {
+ intr_event_t *event = &mips_intr_event[irq];
+ WITH_SPIN_LOCK (&event->ie_lock) {
+ intr_event_add_handler(event, handler);
+ if (event->ie_count == 1) {
mips32_bs_c0(C0_STATUS, SR_IM0 << irq); /* enable interrupt */
mips32_bc_c0(C0_CAUSE, CR_IP0 << irq); /* clear pending flag */
}
@@ -82,11 +82,11 @@ void mips_intr_setup(intr_handler_t *handler, unsigned irq) {
}
void mips_intr_teardown(intr_handler_t *handler) {
- intr_chain_t *chain = handler->ih_chain;
- WITH_SPIN_LOCK (&chain->ic_lock) {
- if (chain->ic_count == 1)
- mips32_bc_c0(C0_STATUS, SR_IM0 << chain->ic_irq);
- intr_chain_remove_handler(handler);
+ intr_event_t *event = handler->ih_event;
+ WITH_SPIN_LOCK (&event->ie_lock) {
+ if (event->ie_count == 1)
+ mips32_bc_c0(C0_STATUS, SR_IM0 << event->ie_irq);
+ intr_event_remove_handler(handler);
}
}
@@ -98,7 +98,7 @@ static void mips_intr_handler(exc_frame_t *frame) {
unsigned irq = CR_IP0 << i;
if (pending & irq) {
- intr_chain_run_handlers(&mips_intr_chain[i]);
+ intr_event_run_handlers(&mips_intr_event[i]);
pending &= ~irq;
}
}

diff:
#include <mips/intr.h>
#include <interrupt.h>
-static mtx_t all_ichains_mtx = MTX_INITIALIZER(0);
-static intr_chain_list_t all_ichains_list =
- TAILQ_HEAD_INITIALIZER(all_ichains_list);
+static mtx_t all_ievents_mtx = MTX_INITIALIZER(0);
+static intr_event_list_t all_ievents_list =
+ TAILQ_HEAD_INITIALIZER(all_ievents_list);
bool intr_disabled(void) {
thread_t *td = thread_self();
@@ -28,44 +28,44 @@ void intr_enable(void) {
mips_intr_enable();
}
-void intr_chain_init(intr_chain_t *ic, unsigned irq, const char *name) {
- ic->ic_irq = irq;
- ic->ic_name = name;
- ic->ic_lock = SPIN_INITIALIZER(LK_RECURSE);
- TAILQ_INIT(&ic->ic_handlers);
+void intr_event_init(intr_event_t *ie, unsigned irq, const char *name) {
+ ie->ie_irq = irq;
+ ie->ie_name = name;
+ ie->ie_lock = SPIN_INITIALIZER(LK_RECURSE);
+ TAILQ_INIT(&ie->ie_handlers);
}
-void intr_chain_register(intr_chain_t *ic) {
- WITH_MTX_LOCK (&all_ichains_mtx)
- TAILQ_INSERT_TAIL(&all_ichains_list, ic, ic_list);
+void intr_event_register(intr_event_t *ie) {
+ WITH_MTX_LOCK (&all_ievents_mtx)
+ TAILQ_INSERT_TAIL(&all_ievents_list, ie, ie_list);
}
-void intr_chain_add_handler(intr_chain_t *ic, intr_handler_t *ih) {
- SCOPED_SPIN_LOCK(&ic->ic_lock);
+void intr_event_add_handler(intr_event_t *ie, intr_handler_t *ih) {
+ SCOPED_SPIN_LOCK(&ie->ie_lock);
/* Add new handler according to it's priority */
intr_handler_t *it;
- TAILQ_FOREACH (it, &ic->ic_handlers, ih_list)
+ TAILQ_FOREACH (it, &ie->ie_handlers, ih_list)
if (ih->ih_prio > it->ih_prio)
break;
if (it)
TAILQ_INSERT_BEFORE(it, ih, ih_list);
else
- TAILQ_INSERT_TAIL(&ic->ic_handlers, ih, ih_list);
+ TAILQ_INSERT_TAIL(&ie->ie_handlers, ih, ih_list);
- ih->ih_chain = ic;
- ic->ic_count++;
+ ih->ih_event = ie;
+ ie->ie_count++;
}
-void intr_chain_remove_handler(intr_handler_t *ih) {
- intr_chain_t *ic = ih->ih_chain;
+void intr_event_remove_handler(intr_handler_t *ih) {
+ intr_event_t *ie = ih->ih_event;
- SCOPED_SPIN_LOCK(&ic->ic_lock);
+ SCOPED_SPIN_LOCK(&ie->ie_lock);
- TAILQ_REMOVE(&ic->ic_handlers, ih, ih_list);
- ih->ih_chain = NULL;
- ic->ic_count--;
+ TAILQ_REMOVE(&ie->ie_handlers, ih, ih_list);
+ ih->ih_event = NULL;
+ ie->ie_count--;
}
/*
@@ -73,11 +73,11 @@ void intr_chain_remove_handler(intr_handler_t *ih) {
* With current implementation all filters have to either handle filter or
* report that it is stray interrupt.
*/
-void intr_chain_run_handlers(intr_chain_t *ic) {
+void intr_event_run_handlers(intr_event_t *ie) {
intr_handler_t *ih;
intr_filter_t status = IF_STRAY;
- TAILQ_FOREACH (ih, &ic->ic_handlers, ih_list) {
+ TAILQ_FOREACH (ih, &ie->ie_handlers, ih_list) {
assert(ih->ih_filter != NULL);
status = ih->ih_filter(ih->ih_argument);
if (status == IF_FILTERED)
@@ -90,5 +90,5 @@ void intr_chain_run_handlers(intr_chain_t *ic) {
}
}
- klog("Spurious %s interrupt!", ic->ic_name);
+ klog("Spurious %s interrupt!", ie->ie_name);
}

original_message: Rename intr_chain to intr_event (#539)


message: Fix gdb Thread.dump_all function

diff:
@@ -105,7 +105,7 @@ class Kthread(SimpleCommand, AutoCompleteMixin):
def dump_all(self):
print('(*) current thread marker')
threads = Thread.list_all()
- cur_td = Thread.current()
+ cur_td = Thread.from_current()
table = TextTable(types='ittit', align='rrrrl')
table.header(['Id', 'Name', 'State', 'Priority', 'Waiting Point'])
for td in threads:

original_message: Fix gdb Thread.dump_all function (#549)


diff:
#define KL_LOG KL_INTR
#include <klog.h>
-#include <stdc.h>
-#include <mutex.h>
-#include <thread.h>
#include <mips/intr.h>
#include <interrupt.h>
#include <sleepq.h>
-#include <spinlock.h>
#include <sysinit.h>
#include <sched.h>
@@ -85,7 +81,6 @@ void intr_event_remove_handler(intr_handler_t *ih) {
/* interrupt handlers delegated to be called in the interrupt thread */
static ih_list_t delegated = TAILQ_HEAD_INITIALIZER(delegated);
-static spin_t *delegated_lock = &SPIN_INITIALIZER(0);
void intr_event_run_handlers(intr_event_t *ie) {
intr_handler_t *ih, *next;
@@ -102,11 +97,8 @@ void intr_event_run_handlers(intr_event_t *ie) {
ie->ie_disable(ie);
TAILQ_REMOVE(&ie->ie_handlers, ih, ih_link);
-
- WITH_SPIN_LOCK (delegated_lock) {
TAILQ_INSERT_TAIL(&delegated, ih, ih_link);
sleepq_signal(&delegated);
- }
return;
}
}
@@ -118,7 +110,7 @@ static void intr_thread(void *arg) {
while (true) {
intr_handler_t *ih;
- WITH_SPIN_LOCK (delegated_lock) {
+ WITH_INTR_DISABLED {
while (TAILQ_EMPTY(&delegated))
sleepq_wait(&delegated, NULL);
ih = TAILQ_FIRST(&delegated);

original_message: Remove wrong interlock of interrupt thread's spinlock (#550)


message: Fix pgrp / proc locking issue.

diff:
@@ -211,8 +211,9 @@ static void proc_reparent(proc_t *old_parent, proc_t *new_parent) {
__noreturn void proc_exit(int exitstatus) {
proc_t *p = proc_self();
+ assert(mtx_owned(&p->p_lock));
+
/* Mark this process as dying, so others don't attempt to disturb it. */
- WITH_PROC_LOCK(p) {
p->p_state = PS_DYING;
/* Clean up process resources. */
@@ -231,7 +232,8 @@ __noreturn void proc_exit(int exitstatus) {
/* Record process statistics that will stay maintained in zombie state. */
p->p_exitstatus = exitstatus;
- }
+
+ proc_unlock(p);
WITH_MTX_LOCK (all_proc_mtx) {
/* Process orphans, but firstly find init process. */
@@ -301,9 +303,11 @@ int proc_sendsig(pid_t pid, signo_t sig) {
}
WITH_MTX_LOCK (&pgrp->pg_lock) {
- TAILQ_FOREACH (target, &pgrp->pg_members, p_pglist)
+ TAILQ_FOREACH (target, &pgrp->pg_members, p_pglist) {
+ proc_lock(target);
sig_kill(target, sig);
}
+ }
return 0;
}

diff:
@@ -148,7 +148,6 @@ void sig_post(signo_t sig) {
/* Terminate this thread as result of a signal. */
if (sa->sa_handler == SIG_DFL && sig_default(sig) == SA_KILL) {
- proc_unlock(p);
sig_exit(td, sig);
}

diff:
@@ -36,6 +36,7 @@ static int sys_sbrk(proc_t *p, sbrk_args_t *args, register_t *res) {
/* https://pubs.opengroup.org/onlinepubs/9699919799/functions/exit.html */
static int sys_exit(proc_t *p, exit_args_t *args, register_t *res) {
klog("exit(%d)", args->rval);
+ proc_lock(p);
proc_exit(MAKE_STATUS_EXIT(args->rval));
__unreachable();
}

original_message: Fix pgrp / proc locking issue.


diff:
@@ -33,9 +33,9 @@ int readdir_generic(vnode_t *v, uio_t *uio, readdir_ops_t *ops) {
ops->convert(v, it, dir);
error = uiomove(dir, reclen, uio);
kfree(M_TEMP, dir);
- if (error < 0)
- return -error;
+ if (error)
+ return error;
}
- return uio->uio_offset - offset;
+ return 0;
}

original_message: Fix error value returned from readdir_generic (#560)


old_path: bin/utest/Makefile

new_path: bin/utest/Makefile

diff:
@@ -14,6 +14,7 @@ SOURCES = \
signal.c \
stat.c \
setjmp.c \
+ sigaction.c \
utest.c
PROGRAM = utest

diff:
@@ -51,6 +51,8 @@ int main(int argc, char **argv) {
CHECKRUN_TEST(exc_sigsys);
CHECKRUN_TEST(syscall_in_bds);
CHECKRUN_TEST(setjmp);
+ CHECKRUN_TEST(sigaction_with_setjmp);
+ CHECKRUN_TEST(sigaction_handler_returns);
CHECKRUN_TEST(fpu_fcsr);
CHECKRUN_TEST(fpu_gpr_preservation);

diff:
static jmp_buf jump_buffer;
-noreturn void do_longjmp(int count) {
+noreturn static void do_longjmp(int count) {
longjmp(jump_buffer, count);
+ assert(0); /* Shouldn't reach here. */
}
int test_setjmp(void) {

change_type: ADD

diff:
+#include <assert.h>
+#include <signal.h>
+#include <string.h>
+#include <setjmp.h>
+#include <stdnoreturn.h>
+
+static sigjmp_buf jump_buffer;
+
+noreturn static void sigint_handler(int signo) {
+ siglongjmp(jump_buffer, 42);
+ assert(0); /* Shouldn't reach here. */
+}
+
+int test_sigaction_with_setjmp(void) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sigint_handler;
+ assert(sigaction(SIGINT, &sa, NULL) == 0);
+
+ if (sigsetjmp(jump_buffer, 1) != 42) {
+ raise(SIGINT);
+ assert(0); /* Shouldn't reach here. */
+ }
+
+ return 0;
+}
+
+static volatile int sigusr1_handled;
+
+static void sigusr1_handler(int signo) {
+ sigusr1_handled = 1;
+}
+
+int test_sigaction_handler_returns(void) {
+ struct sigaction sa;
+
+ memset(&sa, 0, sizeof(sa));
+ sa.sa_handler = sigusr1_handler;
+ assert(sigaction(SIGUSR1, &sa, NULL) == 0);
+
+ assert(sigusr1_handled == 0);
+ raise(SIGUSR1);
+ assert(sigusr1_handled == 1);
+
+ return 0;
+}

diff:
@@ -55,4 +55,7 @@ int test_syscall_in_bds(void);
int test_setjmp(void);
+int test_sigaction_with_setjmp(void);
+int test_sigaction_handler_returns(void);
+
#endif /* __UTEST_H__ */

diff:
@@ -94,6 +94,9 @@ UTEST_ADD_SIMPLE(fstat);
UTEST_ADD_SIMPLE(setjmp);
+UTEST_ADD_SIMPLE(sigaction_with_setjmp);
+UTEST_ADD_SIMPLE(sigaction_handler_returns);
+
#if 0
UTEST_ADD_SIMPLE(fpu_fcsr);
UTEST_ADD_SIMPLE(fpu_gpr_preservation);

original_message: sigaction tests: does normal / siglongjmp return from sighandler work? (#566)


diff:
@@ -171,12 +171,12 @@ int test_fd_pipe() {
assert(pid >= 0);
if (pid > 0) {
- /* child */
+ /* parent */
assert_close_ok(fd[0]);
assert_write_ok(fd[1], str, strlen(str));
assert_close_ok(fd[1]);
} else {
- /* parent */
+ /* child */
assert_close_ok(fd[1]);
assert_read_equal(fd[0], buf, str);
assert_close_ok(fd[0]);

diff:
@@ -77,20 +77,25 @@ static int pipe_read(file_t *f, uio_t *uio) {
assert(!consumer->closed);
+ /* user requested read of 0 bytes */
+ if (uio->uio_resid == 0)
+ return 0;
+
/* no read atomicity for now! */
WITH_MTX_LOCK (&producer->mtx) {
- do {
+ /* pipe empty, no producers, return end-of-file */
+ if (ringbuf_empty(&producer->buf) && producer->closed)
+ return 0;
+
+ /* pipe empty, producer exists, wait for data */
+ while (ringbuf_empty(&producer->buf) && !producer->closed)
+ cv_wait(&producer->nonempty, &producer->mtx);
+
int res = ringbuf_read(&producer->buf, uio);
if (res)
return res;
/* notify producer that free space is available */
cv_broadcast(&producer->nonfull);
- /* nothing left to read? */
- if (uio->uio_resid == 0)
- break;
- /* the buffer is empty so wait for some data to be produced */
- cv_wait(&producer->nonempty, &producer->mtx);
- } while (!producer->closed);
}
return 0;

original_message: Reading from pipe is allowed to return short count (#576)


diff:
#include <sys/param.h>
#include <sys/types.h>
-#define log2(x) (__builtin_ffs(x) - 1)
+#define log2(x) (CHAR_BIT * sizeof(unsigned long) - __builtin_clzl(x) - 1)
#define ffs(x) (size_t)(__builtin_ffs(x))
#define clz(x) (size_t)(__builtin_clz(x))
#define ctz(x) (size_t)(__builtin_ctz(x))

original_message: Fix log2 function in <sys/mimiker.h> by using clz (#584)


diff:
@@ -15,6 +15,7 @@ typedef enum {
KL_PHYSMEM, /* physical memory management */
KL_VM, /* virtual memory */
KL_KMEM, /* generic kernel memory allocator */
+ KL_VMEM, /* address space allocator */
KL_POOL, /* pooled allocator */
KL_LOCK, /* lock operations tracing */
KL_SCHED, /* scheduler tracing */

diff:
+#ifndef _SYS_VMEM_H_
+#define _SYS_VMEM_H_
+
+#include <sys/types.h>
+
+typedef uintptr_t vmem_addr_t;
+typedef size_t vmem_size_t;
+
+typedef struct vmem vmem_t;
+
+/*
+ * The following interface is a simplified version of NetBSD's vmem interface.
+ * For documentation, please refer to VMEM(9), e.g. here:
+ * https://netbsd.gw.com/cgi-bin/man-cgi?vmem+9+NetBSD-current
+ */
+
+vmem_t *vmem_create(const char *name, vmem_addr_t base, vmem_size_t size,
+ vmem_size_t quantum);
+int vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size);
+int vmem_alloc(vmem_t *vm, vmem_size_t size, vmem_addr_t *addrp);
+void vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size);
+void vmem_destroy(vmem_t *vm);
+
+#endif /* _SYS_VMEM_H_ */

old_path: sys/kern/Makefile

new_path: sys/kern/Makefile

diff:
@@ -65,7 +65,8 @@ SOURCES = \
vfs_vnode.c \
vm_map.c \
vm_object.c \
- vm_pager.c
+ vm_pager.c \
+ vmem.c
FORMAT-EXCLUDE = sysent.h

diff:
@@ -13,12 +13,13 @@ static spin_t klog_lock = SPIN_INITIALIZER(LK_RECURSE);
static const char *subsystems[] = {
[KL_RUNQ] = "runq", [KL_SLEEPQ] = "sleepq", [KL_CALLOUT] = "callout",
[KL_INIT] = "init", [KL_PMAP] = "pmap", [KL_VM] = "vm",
- [KL_KMEM] = "kmem", [KL_POOL] = "pool", [KL_LOCK] = "lock",
- [KL_SCHED] = "sched", [KL_THREAD] = "thread", [KL_INTR] = "intr",
- [KL_DEV] = "dev", [KL_VFS] = "vfs", [KL_VNODE] = "vnode",
- [KL_PROC] = "proc", [KL_SYSCALL] = "syscall", [KL_USER] = "user",
- [KL_TEST] = "test", [KL_SIGNAL] = "signal", [KL_FILESYS] = "filesys",
- [KL_TIME] = "time", [KL_FILE] = "file", [KL_UNDEF] = "???"};
+ [KL_KMEM] = "kmem", [KL_VMEM] = "vmem", [KL_POOL] = "pool",
+ [KL_LOCK] = "lock", [KL_SCHED] = "sched", [KL_THREAD] = "thread",
+ [KL_INTR] = "intr", [KL_DEV] = "dev", [KL_VFS] = "vfs",
+ [KL_VNODE] = "vnode", [KL_PROC] = "proc", [KL_SYSCALL] = "syscall",
+ [KL_USER] = "user", [KL_TEST] = "test", [KL_SIGNAL] = "signal",
+ [KL_FILESYS] = "filesys", [KL_TIME] = "time", [KL_FILE] = "file",
+ [KL_UNDEF] = "???"};
/* Borrowed from mips/malta.c */
char *kenv_get(char *key);

diff:
+#define KL_LOG KL_VMEM
+#include <sys/klog.h>
+#include <sys/vmem.h>
+#include <sys/queue.h>
+#include <sys/pool.h>
+#include <sys/mimiker.h>
+#include <sys/libkern.h>
+#include <sys/errno.h>
+#include <sys/hash.h>
+#include <sys/mutex.h>
+
+#define VMEM_MAXORDER ((int)(sizeof(vmem_size_t) * CHAR_BIT))
+#define VMEM_MAXHASH 512
+#define VMEM_NAME_MAX 16
+
+#define ORDER2SIZE(order) ((vmem_size_t)1 << (order))
+#define SIZE2ORDER(size) ((int)log2(size))
+
+typedef TAILQ_HEAD(vmem_seglist, bt) vmem_seglist_t;
+typedef LIST_HEAD(vmem_freelist, bt) vmem_freelist_t;
+typedef LIST_HEAD(vmem_hashlist, bt) vmem_hashlist_t;
+
+static mtx_t vmem_alllist_lock = MTX_INITIALIZER(0);
+static LIST_HEAD(, vmem) vmem_alllist = LIST_HEAD_INITIALIZER(vmem_alllist);
+
+typedef struct vmem {
+ mtx_t vm_lock;
+ size_t vm_size;
+ size_t vm_inuse;
+ size_t vm_quantum_mask;
+ int vm_quantum_shift;
+ char vm_name[VMEM_NAME_MAX];
+ vmem_seglist_t vm_seglist;
+ vmem_freelist_t vm_freelist[VMEM_MAXORDER];
+ vmem_hashlist_t vm_hashlist[VMEM_MAXHASH];
+ LIST_ENTRY(vmem) vm_alllist_link;
+} vmem_t;
+
+typedef enum { BT_TYPE_FREE, BT_TYPE_BUSY, BT_TYPE_SPAN } bt_type_t;
+
+typedef struct bt {
+ TAILQ_ENTRY(bt) bt_seglink;
+ union {
+ LIST_ENTRY(bt) bt_freelink;
+ LIST_ENTRY(bt) bt_hashlink;
+ };
+ vmem_addr_t bt_start;
+ vmem_size_t bt_size;
+ bt_type_t bt_type;
+} bt_t;
+
+static POOL_DEFINE(P_VMEM, "vmem", sizeof(vmem_t));
+static POOL_DEFINE(P_BT, "vmem boundary tag", sizeof(bt_t));
+
+static vmem_freelist_t *bt_freehead_tofree(vmem_t *vm, vmem_size_t size) {
+ vmem_size_t qsize = size >> vm->vm_quantum_shift;
+ assert(size != 0 && qsize != 0);
+ int idx = SIZE2ORDER(qsize);
+ assert(idx >= 0 && idx < VMEM_MAXORDER);
+ return &vm->vm_freelist[idx];
+}
+
+/* Returns the freelist for the given size and allocation strategy.
+ *
+ * For VMEM_INSTANTFIT, returns the list in which any blocks are large enough.
+ * For VMEM_BESTFIT, returns the list which can have blocks large enough. */
+static vmem_freelist_t *bt_freehead_toalloc(vmem_t *vm, vmem_size_t size) {
+ assert((size & vm->vm_quantum_mask) == 0);
+ vmem_size_t qsize = size >> vm->vm_quantum_shift;
+ assert(size != 0 && qsize != 0);
+ int idx = SIZE2ORDER(qsize);
+ if (ORDER2SIZE(idx) != qsize)
+ idx++;
+ assert(idx >= 0 && idx < VMEM_MAXORDER);
+ return &vm->vm_freelist[idx];
+}
+
+static void vmem_lock(vmem_t *vm) {
+ mtx_lock(&vm->vm_lock);
+}
+
+static void vmem_unlock(vmem_t *vm) {
+ mtx_unlock(&vm->vm_lock);
+}
+
+static void vmem_assert_locked(vmem_t *vm) {
+ assert(mtx_owned(&vm->vm_lock));
+}
+
+static bool bt_isspan(const bt_t *bt) {
+ return bt->bt_type == BT_TYPE_SPAN;
+}
+
+static vmem_addr_t bt_end(const bt_t *bt) {
+ return bt->bt_start + bt->bt_size - 1;
+}
+
+static vmem_hashlist_t *bt_hashhead(vmem_t *vm, vmem_addr_t addr) {
+ unsigned int hash = hash32_buf(&addr, sizeof(addr), HASH32_BUF_INIT);
+ return &vm->vm_hashlist[hash % VMEM_MAXHASH];
+}
+
+static void bt_insfree(vmem_t *vm, bt_t *bt) {
+ vmem_assert_locked(vm);
+ assert(bt->bt_type == BT_TYPE_FREE);
+ vmem_freelist_t *list = bt_freehead_tofree(vm, bt->bt_size);
+ LIST_INSERT_HEAD(list, bt, bt_freelink);
+}
+
+static void bt_remfree(vmem_t *vm, bt_t *bt) {
+ vmem_assert_locked(vm);
+ assert(bt->bt_type == BT_TYPE_FREE);
+ LIST_REMOVE(bt, bt_freelink);
+}
+
+static void bt_insseg_tail(vmem_t *vm, bt_t *bt) {
+ vmem_assert_locked(vm);
+ TAILQ_INSERT_TAIL(&vm->vm_seglist, bt, bt_seglink);
+}
+
+static void bt_insseg_after(vmem_t *vm, bt_t *bt, bt_t *prev) {
+ vmem_assert_locked(vm);
+ TAILQ_INSERT_AFTER(&vm->vm_seglist, prev, bt, bt_seglink);
+}
+
+static void bt_insbusy(vmem_t *vm, bt_t *bt) {
+ vmem_assert_locked(vm);
+ assert(bt->bt_type == BT_TYPE_BUSY);
+ vmem_hashlist_t *list = bt_hashhead(vm, bt->bt_start);
+ LIST_INSERT_HEAD(list, bt, bt_hashlink);
+ vm->vm_inuse += bt->bt_size;
+}
+
+static vmem_size_t vmem_roundup_size(vmem_t *vm, vmem_size_t size) {
+ return (size + vm->vm_quantum_mask) & ~vm->vm_quantum_mask;
+}
+
+static vmem_addr_t vmem_alignup_addr(vmem_addr_t addr, vmem_size_t align) {
+ return (addr + align - 1) & ~(align - 1);
+}
+
+static void vmem_check_sanity(vmem_t *vm) {
+ vmem_assert_locked(vm);
+
+ const bt_t *bt1;
+ TAILQ_FOREACH (bt1, &vm->vm_seglist, bt_seglink)
+ assert(bt1->bt_start <= bt_end(bt1));
+
+ const bt_t *bt2;
+ TAILQ_FOREACH (bt1, &vm->vm_seglist, bt_seglink) {
+ TAILQ_FOREACH (bt2, &vm->vm_seglist, bt_seglink) {
+ if (bt1 == bt2)
+ continue;
+ if (bt_isspan(bt1) != bt_isspan(bt2))
+ continue;
+ assert(bt1->bt_start > bt_end(bt2) || bt2->bt_start > bt_end(bt1));
+ }
+ }
+}
+
+static int vmem_align_and_fit(const bt_t *bt, vmem_size_t size,
+ vmem_size_t align, vmem_addr_t *addrp) {
+ assert(size > 0);
+ assert(bt->bt_type == BT_TYPE_FREE);
+ assert(bt->bt_size >= size); /* caller's responsibility */
+
+ vmem_addr_t start = bt->bt_start;
+ vmem_addr_t end = bt_end(bt);
+
+ vmem_addr_t start_aligned = vmem_alignup_addr(start, align);
+ assert(start_aligned >= start);
+ assert((start_aligned & (align - 1)) == 0);
+
+ if (start_aligned <= end && end - start_aligned >= size - 1) {
+ *addrp = start_aligned;
+ return 0;
+ }
+
+ return ENOMEM;
+}
+
+vmem_t *vmem_create(const char *name, vmem_addr_t base, vmem_size_t size,
+ vmem_size_t quantum) {
+ assert(quantum > 0);
+
+ vmem_t *vm = pool_alloc(P_VMEM, PF_ZERO);
+
+ vm->vm_quantum_mask = quantum - 1;
+ vm->vm_quantum_shift = SIZE2ORDER(quantum);
+ assert(ORDER2SIZE(vm->vm_quantum_shift) == quantum);
+ mtx_init(&vm->vm_lock, 0);
+ strlcpy(vm->vm_name, name, sizeof(vm->vm_name));
+
+ TAILQ_INIT(&vm->vm_seglist);
+ for (int i = 0; i < VMEM_MAXORDER; i++)
+ LIST_INIT(&vm->vm_freelist[i]);
+ for (int i = 0; i < VMEM_MAXHASH; i++)
+ LIST_INIT(&vm->vm_hashlist[i]);
+
+ if (size != 0)
+ vmem_add(vm, base, size);
+
+ WITH_MTX_LOCK (&vmem_alllist_lock)
+ LIST_INSERT_HEAD(&vmem_alllist, vm, vm_alllist_link);
+
+ klog("new vmem '%s' created", name);
+ return vm;
+}
+
+int vmem_add(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) {
+ bt_t *btspan = pool_alloc(P_BT, PF_ZERO);
+ bt_t *btfree = pool_alloc(P_BT, PF_ZERO);
+
+ btspan->bt_type = BT_TYPE_SPAN;
+ btspan->bt_start = addr;
+ btspan->bt_size = size;
+
+ btfree->bt_type = BT_TYPE_FREE;
+ btfree->bt_start = addr;
+ btfree->bt_size = size;
+
+ WITH_MTX_LOCK (&vm->vm_lock) {
+ bt_insseg_tail(vm, btspan);
+ bt_insseg_after(vm, btfree, btspan);
+ bt_insfree(vm, btfree);
+ vm->vm_size += size;
+ vmem_check_sanity(vm);
+ }
+
+ klog("span [%lu, %lu] added", addr, addr + size - 1);
+
+ return 0;
+}
+
+int vmem_alloc(vmem_t *vm, vmem_size_t size, vmem_addr_t *addrp) {
+ assert(size > 0);
+
+ size = vmem_roundup_size(vm, size);
+ assert(size > 0);
+
+ vmem_size_t align = vm->vm_quantum_mask + 1;
+
+ /* Allocate boundary tags before acquiring the vmem lock */
+ bt_t *btnew_prev = pool_alloc(P_BT, PF_ZERO);
+ bt_t *btnew_next = pool_alloc(P_BT, PF_ZERO);
+
+ vmem_freelist_t *first = bt_freehead_toalloc(vm, size);
+ vmem_freelist_t *end = &vm->vm_freelist[VMEM_MAXORDER];
+
+ vmem_lock(vm);
+ vmem_check_sanity(vm);
+
+ bt_t *bt;
+ vmem_addr_t start;
+ bool found = false;
+ for (vmem_freelist_t *list = first; list < end; list++) {
+ bt = LIST_FIRST(list);
+ if (bt != NULL && vmem_align_and_fit(bt, size, align, &start) == 0) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ vmem_unlock(vm);
+ pool_free(P_BT, btnew_prev);
+ pool_free(P_BT, btnew_next);
+ klog("alloc of size %lu for vmem '%s' failed (ENOMEM)", size, vm->vm_name);
+ return ENOMEM;
+ }
+
+ bt_remfree(vm, bt);
+ vmem_check_sanity(vm);
+
+ if (bt->bt_start != start) {
+ /* Split [bt] into [btnew_prev | bt] */
+ btnew_prev->bt_type = BT_TYPE_FREE;
+ btnew_prev->bt_start = bt->bt_start;
+ btnew_prev->bt_size = start - bt->bt_start;
+ bt->bt_start = start;
+ bt->bt_size -= btnew_prev->bt_size;
+ bt_insfree(vm, btnew_prev);
+ bt_insseg_after(vm, btnew_prev, TAILQ_PREV(bt, vmem_seglist, bt_seglink));
+ /* Set btnew_prev to NULL so that it won't be deallocated after exiting
+ * from the vmem lock */
+ btnew_prev = NULL;
+ vmem_check_sanity(vm);
+ }
+
+ assert(bt->bt_start == start);
+ if (bt->bt_size != size && bt->bt_size - size > vm->vm_quantum_mask) {
+ /* Split [bt] into [bt | btnew_next] */
+ btnew_next->bt_type = BT_TYPE_FREE;
+ btnew_next->bt_start = bt->bt_start + size;
+ btnew_next->bt_size = bt->bt_size - size;
+ bt->bt_type = BT_TYPE_BUSY;
+ bt->bt_size = size;
+ bt_insfree(vm, btnew_next);
+ bt_insseg_after(vm, btnew_next, bt);
+ bt_insbusy(vm, bt);
+ /* Set btnew_next to NULL so that it won't be deallocated after exiting
+ * from the vmem lock */
+ btnew_next = NULL;
+ } else {
+ bt->bt_type = BT_TYPE_BUSY;
+ bt_insbusy(vm, bt);
+ }
+
+ vmem_check_sanity(vm);
+ vmem_unlock(vm);
+
+ if (btnew_prev != NULL)
+ pool_free(P_BT, btnew_prev);
+ if (btnew_next != NULL)
+ pool_free(P_BT, btnew_next);
+
+ assert(bt->bt_size >= size);
+ assert(bt->bt_type == BT_TYPE_BUSY);
+
+ if (addrp != NULL)
+ *addrp = bt->bt_start;
+
+ klog("alloc of size %lu for vmem '%s' succeeded", size, vm->vm_name);
+ return 0;
+}
+
+void vmem_free(vmem_t *vm, vmem_addr_t addr, vmem_size_t size) {
+ klog("vmem_free(%p, %ld, %ld) had no effect", vm, addr, size);
+}
+
+void vmem_destroy(vmem_t *vm) {
+ klog("vmem_destroy(%p) had no effect", vm);
+}

old_path: sys/tests/Makefile

new_path: sys/tests/Makefile

diff:
@@ -32,6 +32,7 @@ SOURCES = \
utest.c \
vm_map.c \
devclass.c \
- vfs.c
+ vfs.c \
+ vmem.c
include $(TOPDIR)/build/build.kern.mk

diff:
+#include <sys/mimiker.h>
+#include <sys/ktest.h>
+#include <sys/vmem.h>
+#include <sys/errno.h>
+
+typedef struct span {
+ vmem_addr_t addr;
+ vmem_size_t size;
+} span_t;
+
+static void assert_addr_is_in_span(vmem_addr_t addr, vmem_size_t size,
+ span_t *span) {
+ assert(addr >= span->addr && addr + size <= span->addr + span->size);
+}
+
+static int test_vmem(void) {
+ int quantum = 1 << 12;
+ vmem_t *vm = vmem_create("test vmem", 0, 0, quantum);
+ assert(vm != NULL);
+
+ int rc;
+ vmem_addr_t addr;
+ vmem_size_t size;
+
+ /* add span #1 */
+ span_t span1 = {.addr = 2 * quantum, .size = 8 * quantum};
+ rc = vmem_add(vm, span1.addr, span1.size);
+ assert(rc == 0);
+
+ /* add span #2 */
+ span_t span2 = {.addr = 100 * quantum, .size = 20 * quantum};
+ rc = vmem_add(vm, span2.addr, span2.size);
+ assert(rc == 0);
+
+ /* alloc 16 quantums, should return addr from span #2 */
+ size = 16 * quantum;
+ rc = vmem_alloc(vm, size, &addr);
+ assert(rc == 0);
+ assert_addr_is_in_span(addr, size, &span2);
+
+ /* alloc 8 quantums, should return addr from span #1 */
+ size = 8 * quantum;
+ rc = vmem_alloc(vm, size, &addr);
+ assert(rc == 0);
+ assert_addr_is_in_span(addr, size, &span1);
+
+ /* alloc 1 quantum, should return addr from span #2 */
+ size = 1 * quantum;
+ rc = vmem_alloc(vm, size, &addr);
+ assert(rc == 0);
+ assert_addr_is_in_span(addr, size, &span2);
+
+ /* alloc 10 quantums, should fail */
+ size = 10 * quantum;
+ rc = vmem_alloc(vm, size, &addr);
+ assert(rc == ENOMEM);
+
+ return KTEST_SUCCESS;
+}
+
+KTEST_ADD(vmem, test_vmem, 0);

original_message: Preliminary vmem implementation (only create, add & alloc calls) (#583)


diff:
@@ -73,7 +73,7 @@ typedef struct bt {
static POOL_DEFINE(P_VMEM, "vmem", sizeof(vmem_t));
static POOL_DEFINE(P_BT, "vmem boundary tag", sizeof(bt_t));
-static vmem_freelist_t *bt_freehead_tofree(vmem_t *vm, vmem_size_t size) {
+static vmem_freelist_t *bt_freehead(vmem_t *vm, vmem_size_t size) {
vmem_size_t qsize = size >> vm->vm_quantum_shift;
assert(size != 0 && qsize != 0);
int idx = SIZE2ORDER(qsize);
@@ -81,18 +81,6 @@ static vmem_freelist_t *bt_freehead_tofree(vmem_t *vm, vmem_size_t size) {
return &vm->vm_freelist[idx];
}
-/* Returns the freelist in which any block is large enough for allocation */
-static vmem_freelist_t *bt_freehead_toalloc(vmem_t *vm, vmem_size_t size) {
- assert(is_aligned(size, vm->vm_quantum));
- vmem_size_t qsize = size >> vm->vm_quantum_shift;
- assert(size != 0 && qsize != 0);
- int idx = SIZE2ORDER(qsize);
- if (ORDER2SIZE(idx) != qsize)
- idx++;
- assert(idx >= 0 && idx < VMEM_MAXORDER);
- return &vm->vm_freelist[idx];
-}
-
static void vmem_lock(vmem_t *vm) {
mtx_lock(&vm->vm_lock);
}
@@ -117,7 +105,7 @@ static vmem_hashlist_t *bt_hashhead(vmem_t *vm, vmem_addr_t addr) {
static void bt_insfree(vmem_t *vm, bt_t *bt) {
assert(mtx_owned(&vm->vm_lock));
assert(bt->bt_type == BT_TYPE_FREE);
- vmem_freelist_t *list = bt_freehead_tofree(vm, bt->bt_size);
+ vmem_freelist_t *list = bt_freehead(vm, bt->bt_size);
LIST_INSERT_HEAD(list, bt, bt_freelink);
}
@@ -145,6 +133,23 @@ static void bt_insbusy(vmem_t *vm, bt_t *bt) {
vm->vm_inuse += bt->bt_size;
}
+static bt_t *bt_find_freeseg(vmem_t *vm, vmem_size_t size) {
+ assert(mtx_owned(&vm->vm_lock));
+
+ vmem_freelist_t *first = bt_freehead(vm, size);
+ vmem_freelist_t *end = &vm->vm_freelist[VMEM_MAXORDER];
+
+ for (vmem_freelist_t *list = first; list < end; list++) {
+ bt_t *bt;
+ LIST_FOREACH (bt, list, bt_freelink) {
+ if (bt->bt_size >= size) {
+ return bt;
+ }
+ }
+ }
+ return NULL;
+}
+
static void vmem_check_sanity(vmem_t *vm) {
assert(mtx_owned(&vm->vm_lock));
@@ -229,26 +234,12 @@ int vmem_alloc(vmem_t *vm, vmem_size_t size, vmem_addr_t *addrp) {
/* Allocate new boundary tag before acquiring the vmem lock */
bt_t *btnew = pool_alloc(P_BT, PF_ZERO);
- vmem_freelist_t *first = bt_freehead_toalloc(vm, size);
- vmem_freelist_t *end = &vm->vm_freelist[VMEM_MAXORDER];
-
vmem_lock(vm);
vmem_check_sanity(vm);
- bt_t *bt;
- bool found = false;
- for (vmem_freelist_t *list = first; list < end; list++) {
- bt = LIST_FIRST(list);
- if (bt != NULL) {
- /* This is instant-fit strategy, we know that any segment found on these
- * lists is large enough. */
- assert(bt->bt_size >= size);
- found = true;
- break;
- }
- }
+ bt_t *bt = bt_find_freeseg(vm, size);
- if (!found) {
+ if (bt == NULL) {
vmem_unlock(vm);
pool_free(P_BT, btnew);
klog("%s: block of %lu bytes not found in '%s'", __func__, size,

diff:
@@ -32,8 +32,8 @@ static int test_vmem(void) {
rc = vmem_add(vm, span2.addr, span2.size);
assert(rc == 0);
- /* alloc 16 quantums, should return addr from span #2 */
- size = 16 * quantum;
+ /* alloc 17 quantums, should return addr from span #2 */
+ size = 17 * quantum;
rc = vmem_alloc(vm, size, &addr);
assert(rc == 0);
assert_addr_is_in_span(addr, size, &span2);

original_message: vmem: change allocation strategy from instant-fit to best-fit (#600)


diff:
register_t t0, t1, t2, t3, t4, t5, t6, t7; \
register_t s0, s1, s2, s3, s4, s5, s6, s7; \
register_t t8, t9; \
+ register_t k0, k1; \
register_t gp, sp, fp, ra; \
register_t lo, hi; \
- register_t pc, sr, badvaddr, cause; \
+ register_t cause, pc, sr, badvaddr; \
}
#define FPU_FRAME \

diff:
@@ -60,15 +60,11 @@ int do_setcontext(thread_t *td, ucontext_t *uc) {
mcontext_t *from = &uc->uc_mcontext;
exc_frame_t *to = td->td_uframe;
- /* registers AT-T9 */
- memcpy(&to->at, &from->__gregs[_REG_AT], sizeof(__greg_t) * 25);
- /* registers GP-HI */
- memcpy(&to->gp, &from->__gregs[_REG_GP], sizeof(__greg_t) * 6);
+ /* registers AT-PC */
+ memcpy(&to->at, &from->__gregs[_REG_AT],
+ sizeof(__greg_t) * (_REG_EPC - _REG_AT + 1));
- to->cause = from->__gregs[_REG_CAUSE];
- to->pc = from->__gregs[_REG_EPC];
-
- /* FP registers + FP CSR */
+ /* 32 FP registers + FP CSR */
memcpy(&to->f0, &from->__fpregs.__fp_r,
sizeof(from->__fpregs.__fp_r) + sizeof(from->__fpregs.__fp_csr));

original_message: Simplify setcontext syscall by rearranging regs in exc_frame_t (#558)


diff:
@@ -11,7 +11,7 @@ int kmain(void) {
char *init = kenv_get("init");
char *test = kenv_get("test");
- /* Main kernel thread becomes PID(0) - a god process! */
+ /* Main kernel thread becomes PID(1) - a god process! */
proc_add(proc_create(thread_self(), NULL));
if (init) {

diff:
@@ -29,7 +29,7 @@ static pgrp_list_t pgrp_list = TAILQ_HEAD_INITIALIZER(pgrp_list);
/* Pid 0 is never available, because of its special treatment by some
* syscalls e.g. kill. */
-static bitstr_t pid_used[bitstr_size(NPROC)] = {[0] = 1, 0};
+static bitstr_t pid_used[bitstr_size(NPROC)] = {1};
/* Process ID management functions */
static pid_t pid_alloc(void) {

original_message: Cleanup after "Do not deliver signals to zombies" (#609)


diff:
@@ -30,15 +30,17 @@ static int test_sleepq_sync(void) {
bzero(callout, sizeof(callout_t) * N);
thread_t *td[K];
- for (int i = 0; i < N; i++)
- callout_setup_relative(&callout[i], i + 1, wake_threads_up, NULL);
-
+ /* Create the threads before setting up the callouts so that not a single
+ * wake-up will be lost! */
for (int i = 0; i < K; i++) {
td[i] = thread_create("test-sleepq", test_thread, (void *)(intptr_t)N,
prio_kthread(0));
sched_add(td[i]);
}
+ for (int i = 0; i < N; i++)
+ callout_setup_relative(&callout[i], i + 1, wake_threads_up, NULL);
+
for (int i = 0; i < K; i++)
thread_join(td[i]);

original_message: Fix possible race condition in sleepq_sync test (#623)


diff:
@@ -68,9 +68,9 @@ __boot_text void mips_init(void) {
for (int i = 0; i < PT_ENTRIES; i++)
pte[i] = PTE_GLOBAL;
- paddr_t text = MIPS_KSEG0_TO_PHYS(__text);
- paddr_t data = MIPS_KSEG0_TO_PHYS(__data);
- paddr_t ebss = MIPS_KSEG0_TO_PHYS(roundup((vaddr_t)__ebss, PAGESIZE));
+ paddr_t text = MIPS_KSEG2_TO_PHYS(__text);
+ paddr_t data = MIPS_KSEG2_TO_PHYS(__data);
+ paddr_t ebss = MIPS_KSEG2_TO_PHYS(roundup((vaddr_t)__ebss, PAGESIZE));
vaddr_t va = MIPS_PHYS_TO_KSEG2(text);
/* assume that kernel image will be covered by single PDE (4MiB) */
@@ -91,7 +91,7 @@ __boot_text void mips_init(void) {
/* User root PDE is NULL */
mips32_setentrylo0(PTE_GLOBAL);
/* Kernel root PDE is set to _kernel_pmap_pde */
- mips32_setentrylo1(PTE_PFN(MIPS_KSEG0_TO_PHYS(_kernel_pmap_pde)) |
+ mips32_setentrylo1(PTE_PFN(MIPS_KSEG2_TO_PHYS(_kernel_pmap_pde)) |
PTE_KERNEL);
mips32_setindex(0);
mips32_tlbwi();

original_message: Build initial page table using kseg2 addresses (#634)


message: Fix strntoul implementation in libkern

diff:
@@ -68,6 +68,7 @@ size_t strlcat(char *dst, const char *src, size_t dsize);
size_t strlcpy(char *dst, const char *src, size_t dsize);
size_t strlen(const char *str);
int strncmp(const char *s1, const char *s2, size_t n);
+char *strncpy(char *dst, const char *src, size_t n);
size_t strnlen(const char *str, size_t maxlen);
char *strrchr(const char *s, int c);
char *strsep(char **stringp, const char *delim);

diff:
@@ -99,6 +99,7 @@ unsigned long strtoul(const char *nptr, char **endptr, int base) {
unsigned long strntoul(const char *nptr, size_t len, char **endptr, int base) {
char copy[len + 1];
- strlcpy(copy, nptr, len + 1);
+ strncpy(copy, nptr, len);
+ copy[len] = 0;
return strtoul(copy, endptr, base);
}

old_path: sys/libkern/string/Makefile

new_path: sys/libkern/string/Makefile

diff:
@@ -19,6 +19,7 @@ SOURCES = bcopy.S \
strlcat.c \
strlcpy.c \
strncmp.c \
+ strncpy.c \
strnlen.c \
strrchr.c \
strsep.c \

original_message: Fix strntoul implementation in libkern (#640)


message: Fix link to tests/README.md

diff:
@@ -76,7 +76,7 @@ argument. Some useful kernel arguments:
Browse `bin` and `usr.bin` directories for currently available programs.
* `klog-quiet=1` - Turns off printing kernel diagnostic messages.
-If you want to run tests please read [this document](tests/README.md).
+If you want to run tests please read [this document](sys/tests/README.md).
Documentation
---

original_message: Fix link to tests/README.md (#642)


message: Fix race condition in vm_map test

diff:
#include <sys/errno.h>
#include <sys/thread.h>
#include <sys/ktest.h>
+#include <sys/sched.h>
static int paging_on_demand_and_memory_protection_demo(void) {
+ /* This test cannot be preempted since PCPU's user-space vm_map will not be
+ * restored while switching back. */
+ SCOPED_NO_PREEMPTION();
+
vm_map_t *orig = vm_map_user();
vm_map_activate(vm_map_new());

original_message: Fix race condition in vm_map test (#644)


diff:
@@ -34,8 +34,8 @@ void thread_reap(void) {
TAILQ_INIT(&zombie_threads);
}
- thread_t *td;
- TAILQ_FOREACH (td, &zombies, td_zombieq)
+ thread_t *td, *next;
+ TAILQ_FOREACH_SAFE (td, &zombies, td_zombieq, next)
thread_delete(td);
}

original_message: Fix use-after-free in thread_reap while browsing zombie list (#645)


diff:
@@ -29,7 +29,10 @@ static int videomode_write(vnode_t *v, uio_t *uio, int ioflag) {
int error = vga_get_videomode(vga, &xres, &yres, &bpp);
if (error)
return error;
- char buffer[RES_CTRL_BUFFER_SIZE];
+ /* Allocate one more byte since sscanf expects the buffer to be
+ * NULL-terminated */
+ char buffer[RES_CTRL_BUFFER_SIZE + 1];
+ buffer[RES_CTRL_BUFFER_SIZE] = 0;
error = uiomove_frombuf(buffer, RES_CTRL_BUFFER_SIZE, uio);
if (error)
return error;

original_message: Fix stack buffer overflow in video driver (#647)


diff:
#ifndef _ARM64_TYPES_H_
#define _ARM64_TYPES_H_
+#include <stdint.h>
+
typedef int64_t register_t;
typedef int64_t fpregister_t;

diff:
#ifndef _MIPS_PCPU_H_
#define _MIPS_PCPU_H_
-#ifndef _MACHDEP
-#error "Do not use this header file outside kernel machine dependent code!"
-#endif
+#define PCPU_MD_FIELDS \
+ struct { \
+ /*!< kernel sp restored on user->kernel transition */ \
+ void *ksp; \
+ /*!< registers that cannot be saved directly to kernel stack */ \
+ register_t status, sp, cause, epc, badvaddr; \
+ }
+#ifdef _MACHDEP
#ifdef __ASSEMBLER__
#include <mips/asm.h>
#include <mips/mips.h>
#define LOAD_PCPU(reg) LA reg, _pcpu_data
-#define LOAD_PCPU_KSEG0(reg) LA reg, MIPS_KSEG2_TO_KSEG0(_pcpu_data)
#endif /* !__ASSEMBLER__ */
+#endif /* !_MACHDEP */
#endif /* !_MIPS_PCPU_H_ */

diff:
@@ -28,7 +28,7 @@ typedef uint32_t pde_t;
#define PD_ENTRIES (PAGESIZE / (int)sizeof(pde_t))
static_assert(PD_ENTRIES == 1 << 10,
- "Page table describes 10-bit range addresses!");
+ "Page directory describes 10-bit range addresses!");
/* Number of page table entries. */
#define PT_ENTRIES (PAGESIZE / (int)sizeof(pte_t))
@@ -47,10 +47,6 @@ static_assert(PT_ENTRIES == 1 << 10,
#define PMAP_USER_BEGIN 0x00001000
#define PMAP_USER_END 0x80000000
-#ifndef __ASSEMBLER__
-void *pmap_kseg2_to_kseg0(void *va);
-#endif /* __ASSEMBLER__ */
-
#endif /* !_MACHDEP */
#endif /* !_MIPS_PMAP_H_ */

diff:
#ifndef _MIPS_TYPES_H_
#define _MIPS_TYPES_H_
+#include <stdint.h>
+
typedef int32_t register_t;
typedef int32_t fpregister_t;

diff:
#ifndef _SYS_PCPU_H_
#define _SYS_PCPU_H_
+#include <machine/types.h>
+#include <machine/pcpu.h>
#include <stdbool.h>
typedef struct thread thread_t;
@@ -14,7 +16,9 @@ typedef struct pcpu {
thread_t *idle_thread; /*!< idle thread executed on this CPU */
pmap_t *curpmap; /*!< current page table */
vm_map_t *uspace; /*!< user space virtual memory map */
- void *ksp; /*!< (MIPS) sp restored on user->kernel transition */
+
+ /* Machine-dependent part */
+ PCPU_MD_FIELDS;
} pcpu_t;
extern pcpu_t _pcpu_data[1];

diff:
__boot_data void *_kernel_end_kseg0;
/* Kernel page directory entries allocated in kseg0. */
__boot_data pde_t *_kernel_pmap_pde;
+/* The boot stack is used before we switch out to thread0. */
+static alignas(PAGESIZE) uint8_t _boot_stack[PAGESIZE];
/* Allocates pages in kseg0. The argument must be multiple of PAGESIZE. */
static __boot_text void *bootmem_alloc(size_t bytes) {
@@ -23,7 +25,7 @@ __boot_text static void halt(void) {
continue;
}
-__boot_text void mips_init(void) {
+__boot_text void *mips_init(void) {
/*
* Ensure we're in kernel mode, disable FPU,
* leave error level & exception level and disable interrupts.
@@ -107,6 +109,12 @@ __boot_text void mips_init(void) {
mips32_setentrylo1(PTE_PFN(MIPS_KSEG0_TO_PHYS(pde)) | PTE_KERNEL);
mips32_setindex(0);
mips32_tlbwi();
+
+ /* Return the end of boot stack (grows downwards on MIPS) as new sp.
+ * This is done in order to move kernel boot process to kseg2, since
+ * current KASAN implementation requires all instrumented stack accesses
+ * to be done through kseg2. */
+ return &_boot_stack[PAGESIZE];
}
/* Following code is used by gdb scripts. */

diff:
@@ -7,7 +7,7 @@ void ctx_init(ctx_t *ctx, void *pc, void *sp) {
bzero(ctx, sizeof(ctx_t));
ctx->pc = (register_t)pc;
- ctx->sp = (register_t)pmap_kseg2_to_kseg0(sp);
+ ctx->sp = (register_t)sp;
/* Take SR from caller's context and enable interrupts. */
ctx->sr = (register_t)mips32_get_c0(C0_STATUS) | SR_IE;
}

diff:
#define LOAD_FPU_REG(reg, offset, base) lwc1 reg, (EXC_FPU_##offset)(base)
-#define SAVE_CPU_CTX(_sp, reg) \
+#define SAVE_CPU_CTX(reg) \
SAVE_REG_CFI(AT, AT, reg); \
SAVE_REG_CFI(v0, V0, reg); \
SAVE_REG_CFI(v1, V1, reg); \
SAVE_REG_CFI(t8, T8, reg); \
SAVE_REG_CFI(t9, T9, reg); \
SAVE_REG_CFI(gp, GP, reg); \
- SAVE_REG(_sp, SP, reg); \
- .cfi_rel_offset sp, EXC_SP; \
SAVE_REG_CFI(fp, FP, reg); \
/* Save value of user-space RA just before syscall. */ \
SAVE_REG(ra, RA, reg); \
mflo t0; \
mfhi t1; \
SAVE_REG(t0, LO, reg); \
- SAVE_REG(t1, HI, reg); \
- mfc0 t0, C0_STATUS; \
- mfc0 t1, C0_CAUSE; \
- mfc0 t2, C0_BADVADDR; \
- mfc0 t3, C0_EPC; \
- SAVE_REG(t0, SR, reg); \
- SAVE_REG(t1, CAUSE, reg); \
- SAVE_REG(t2, BADVADDR, reg); \
- /* Save value of user-space PC just before syscall. */ \
- SAVE_REG(t3, PC, reg); \
- .cfi_return_column t3; \
- .cfi_rel_offset t3, EXC_PC
+ SAVE_REG(t1, HI, reg);
#define LOAD_CPU_CTX() \
- LOAD_REG(t0, PC, sp); \
- mtc0 t0, C0_EPC; \
LOAD_REG(t0, HI, sp); \
LOAD_REG(t1, LO, sp); \
mthi t0; \
LOAD_FPU_REG($f31, F31, sp); \
LOAD_FPU_REG($31, FSR, sp)
+#define LOAD_PCPU_KSEG0(reg) \
+ LA reg, MIPS_KSEG2_TO_KSEG0(_pcpu_data)
+
# Increment interrupt disable nest level.
#define TD_INC_IDNEST(reg) \
lw t0, TD_IDNEST(reg); \
ehb; \
ext reg, reg, SR_IMASK_SHIFT, SR_IMASK_BITS
+# Remove given bits from the status register.
+#define STATUS_CLEAR(mask) \
+ mfc0 k0, C0_STATUS; \
+ li k1, ~(mask); \
+ and k0, k1; \
+ mtc0 k0, C0_STATUS
+
+#define PCPU_SAVE(reg) \
+ mfc0 k1, C0_##reg; \
+ sw k1, PCPU_##reg(k0)
+
+#define PCPU_RESTORE(reg) \
+ lw k1, PCPU_##reg(k0); \
+ mtc0 k1, C0_##reg
+
+#define PCPU_TO_FRAME(reg) \
+ lw t1, PCPU_##reg(t0); \
+ SAVE_REG(t1, reg, sp)
.global _ebase
@@ -289,28 +295,49 @@ exc_enter:
beqz k0, kern_exc_enter
nop
-# IMPORTANT: While exception level is active the code may only access kernel
-# stack and local pcpu structure! FTTB These are accessed through kseg0.
-# It is UNSAFE to use k0/k1 registers when TLB miss handler may be triggered!
+# IMPORTANT:
+# - While exception level is active, the code may only access local pcpu
+# structure! FTTB It's accessed through kseg0.
+# - It is UNSAFE to use k0/k1 registers when TLB miss handler may be triggered!
+# - Status, cause, epc & badvaddr registers cannot be saved directly to kernel
+# stack (kseg2) as it may trigger TLB handler which would overwrite them!
SNESTED(user_exc_enter, USER_EXC_FRAME_SIZE, ra)
.cfi_signal_frame
- # Move user sp to temporary register and replace it with kernel sp.
- move k1, sp
- # Previous value of sp is saved in register k1.
- .cfi_register sp, k1
-
- # Exception frame will be saved on kernel stack.
- # Fetch exception frame pointer where context will be saved.
+ # Fetch PCPU address
LOAD_PCPU_KSEG0(k0)
- # sp contains kernel stack pointer.
+ # Store sp, status, cause, epc, badvaddr registers in PCPU
+ sw sp, PCPU_SP(k0)
+ PCPU_SAVE(STATUS)
+ PCPU_SAVE(CAUSE)
+ PCPU_SAVE(EPC)
+ PCPU_SAVE(BADVADDR)
+
+ # sp now contains kernel stack pointer.
lw sp, PCPU_KSP(k0)
- # Define a rule for computing CFA as: *sp + 0.
+ # Define a rule for computing CFA as: sp + 0
.cfi_def_cfa sp, 0
- SAVE_CPU_CTX(k1, sp)
+
+ # We cannot access stack (KSEG2) when exception level is enabled
+ # Let's enter kernel mode, drop exception level, disable interrupts
+ STATUS_CLEAR(SR_KSU_MASK|SR_EXL|SR_IE)
+
+ # Save context on the kernel stack
+ SAVE_CPU_CTX(sp)
+
+ # Copy sp, status, cause, epc, badvaddr from PCPU to stack frame
+ LOAD_PCPU_KSEG0(t0)
+ PCPU_TO_FRAME(SP)
+ .cfi_rel_offset sp, EXC_SP
+ PCPU_TO_FRAME(SR)
+ PCPU_TO_FRAME(CAUSE)
+ PCPU_TO_FRAME(PC)
+ .cfi_return_column t1
+ .cfi_rel_offset t1, EXC_PC
+ PCPU_TO_FRAME(BADVADDR)
# If FPU is enabled save FPU registers.
mfc0 t0, C0_STATUS
@@ -324,12 +351,8 @@ skip_fpu_save:
# Load kernel global pointer.
la gp, _gp
- # Turn off FPU, enter kernel mode,
- # drop exception level and disable interrupts.
- mfc0 t0, C0_STATUS
- li t1, ~(SR_CU1|SR_KSU_MASK|SR_EXL|SR_IE)
- and t0, t1
- mtc0 t0, C0_STATUS
+ # Turn off FPU
+ STATUS_CLEAR(SR_CU1)
# Fetch thread control block pointer to s0 for later use.
LOAD_PCPU(s0)
@@ -352,21 +375,23 @@ user_exc_leave:
# Set current stack pointer to user exception frame.
# This is crucial on first entry to user-space for this thread.
- LOAD_PCPU(s0)
+ LOAD_PCPU_KSEG0(s0)
lw t0, PCPU_CURTHREAD(s0)
lw sp, TD_UFRAME(t0)
# Update kernel stack pointer to be used on kernel reentry.
sw sp, PCPU_KSP(s0)
- # Update status register held in exception frame (only interrupt mask).
+ # Load status register from exception frame, update it with current
+ # interrupt mask and exception mode, and store it within PCPU
LOAD_REG(t0, SR, sp)
ins t0, t1, SR_IMASK_SHIFT, SR_IMASK_BITS
- SAVE_REG(t0, SR, sp)
-
- # Enter exception level with user-mode settings.
ori t0, SR_EXL
- mtc0 t0, C0_STATUS
+ sw t0, PCPU_SR(s0)
+
+ # Copy pc register from stack frame to PCPU
+ LOAD_REG(t0, PC, sp)
+ sw t0, PCPU_PC(s0)
# If FPU has been enabled, then restore FPU registers.
ext t1, t0, SR_CU1_SHIFT, 1
@@ -379,6 +404,11 @@ skip_fpu_restore:
# Load context from exception frame on stack, sp will get overwritten.
LOAD_CPU_CTX()
+ # Restore status and epc registers from PCPU
+ LOAD_PCPU_KSEG0(k0)
+ PCPU_RESTORE(STATUS)
+ PCPU_RESTORE(EPC)
+
# This label is useful for debugging.
sync
user_return:
@@ -386,10 +416,33 @@ user_return:
END(user_exc_enter)
SNESTED(kern_exc_enter, KERN_EXC_FRAME_SIZE, ra)
+ # Fetch PCPU address
+ LOAD_PCPU_KSEG0(k0)
+
+ # Store sp, status, cause, epc, badvaddr registers in PCPU
+ sw sp, PCPU_SP(k0)
+ PCPU_SAVE(STATUS)
+ PCPU_SAVE(CAUSE)
+ PCPU_SAVE(EPC)
+ PCPU_SAVE(BADVADDR)
+
# Allocate stack frame (of KERN_EXC_FRAME_SIZE bytes) and save context.
- move k0, sp # save original sp value
subu sp, KERN_EXC_FRAME_SIZE # required by gdb to recognize frame
- SAVE_CPU_CTX(k0, sp)
+
+ # We cannot access stack (KSEG2) when exception level is enabled
+ # Let's drop exception level and disable interrupts.
+ STATUS_CLEAR(SR_EXL|SR_IE)
+
+ # Save context on the kernel stack
+ SAVE_CPU_CTX(sp)
+
+ # Copy sp, status, cause, epc, badvaddr from PCPU to stack frame
+ LOAD_PCPU_KSEG0(t0)
+ PCPU_TO_FRAME(SP)
+ PCPU_TO_FRAME(SR)
+ PCPU_TO_FRAME(CAUSE)
+ PCPU_TO_FRAME(PC)
+ PCPU_TO_FRAME(BADVADDR)
# In normal conditions we're going to enter regular exception handler.
la s1, mips_exc_handler
@@ -407,12 +460,6 @@ SNESTED(kern_exc_enter, KERN_EXC_FRAME_SIZE, ra)
# Load kernel global pointer.
1: la gp, _gp
- # Drop exception level and disable interrupts.
- mfc0 t0, C0_STATUS
- li t1, ~(SR_EXL|SR_IE)
- and t0, t1
- mtc0 t0, C0_STATUS
-
# Fetch thread control block pointer to s0 for later use.
LOAD_PCPU(t0)
lw s0, PCPU_CURTHREAD(t0)
@@ -431,18 +478,27 @@ SNESTED(kern_exc_enter, KERN_EXC_FRAME_SIZE, ra)
kern_exc_leave:
DISABLE_INT(t1)
- # Load status register from exception frame and update it with current
- # interrupt mask.
+ LOAD_PCPU_KSEG0(k0)
+
+ # Load status register from exception frame, update it with current
+ # interrupt mask and exception mode, and store it within PCPU
LOAD_REG(t0, SR, sp)
ins t0, t1, SR_IMASK_SHIFT, SR_IMASK_BITS
-
- # Enter exception level with kernel-mode settings.
ori t0, SR_EXL
- mtc0 t0, C0_STATUS
+ sw t0, PCPU_SR(k0)
+
+ # Copy pc register from exception frame to PCPU
+ LOAD_REG(t0, PC, sp)
+ sw t0, PCPU_PC(k0)
# Load context from exception frame on stack, sp will get overwritten.
LOAD_CPU_CTX()
+ # Restore status and epc registers from PCPU
+ LOAD_PCPU_KSEG0(k0)
+ PCPU_RESTORE(STATUS)
+ PCPU_RESTORE(EPC)
+
sync
eret
END(kern_exc_enter)

diff:
@@ -34,7 +34,7 @@ void exc_frame_init(exc_frame_t *frame, void *pc, void *sp, unsigned flags) {
frame->gp = usermode ? 0 : (register_t)_gp;
frame->pc = (register_t)pc;
- frame->sp = (register_t)pmap_kseg2_to_kseg0(sp);
+ frame->sp = (register_t)sp;
/* For user-mode exception frame we must make sure that:
* - user mode is active,

diff:
@@ -113,5 +113,13 @@ define CTX_FRAME_SIZE sizeof(ctx_t)
define P_USPACE offsetof(proc_t, p_uspace)
-define PCPU_CURTHREAD offsetof(pcpu_t, curthread)
+# PC/EPC and SR/STATUS definitions are duplicated for convenience reasons
+define PCPU_SP offsetof(pcpu_t, sp)
+define PCPU_PC offsetof(pcpu_t, epc)
+define PCPU_EPC offsetof(pcpu_t, epc)
define PCPU_KSP offsetof(pcpu_t, ksp)
+define PCPU_CAUSE offsetof(pcpu_t, cause)
+define PCPU_SR offsetof(pcpu_t, status)
+define PCPU_STATUS offsetof(pcpu_t, status)
+define PCPU_BADVADDR offsetof(pcpu_t, badvaddr)
+define PCPU_CURTHREAD offsetof(pcpu_t, curthread)

diff:
@@ -118,7 +118,7 @@ static void *malta_kenv(int argc, char **argv, char **envp) {
kenv_bootstrap(kenvp, kinit);
- return (void *)MIPS_KSEG2_TO_KSEG0(stk->stk_ptr);
+ return stk->stk_ptr;
}
intptr_t ramdisk_get_start(void) {

diff:
@@ -295,14 +295,6 @@ void pmap_copy_page(vm_page_t *src, vm_page_t *dst) {
memcpy(PG_KSEG0_ADDR(dst), PG_KSEG0_ADDR(src), PAGESIZE);
}
-void *pmap_kseg2_to_kseg0(void *va) {
- if (!MIPS_IN_KSEG2_P(va))
- return va;
- paddr_t pa;
- assert(pmap_extract(pmap_kernel(), (vaddr_t)va, &pa));
- return (void *)MIPS_PHYS_TO_KSEG0(pa);
-}
-
/* TODO: at any given moment there're two page tables in use:
* - kernel-space pmap for kseg2 & kseg3
* - user-space pmap for useg

diff:
#include <mips/asm.h>
#include <mips/pcpu.h>
#include <mips/regdef.h>
-#include <mips/m32c0.h>
#include "assym.h"
@@ -32,6 +31,7 @@ LEAF(_start)
move s3, a3
jal mips_init
+ move sp, v0 # Set boot stack (already in kseg2)
# From this point kernel sections are accessible through kseg2!
move a0, s0

diff:
@@ -19,7 +19,7 @@ void thread_entry_setup(thread_t *td, entry_fn_t target, void *arg) {
exc_frame_t *kframe = kstack_alloc_s(stk, cpu_exc_frame_t);
ctx_t *kctx = kstack_alloc_s(stk, ctx_t);
- td->td_uframe = pmap_kseg2_to_kseg0(uframe);
+ td->td_uframe = uframe;
td->td_kframe = kframe;
td->td_kctx = kctx;

original_message: Access kernel stack using only KSEG2 addresses (#636)


diff:
@@ -14,7 +14,7 @@ void mtx_init(mtx_t *m, lock_type_t type) {
m->m_owner = NULL;
m->m_count = 0;
m->m_lockpt = NULL;
- m->m_type = type;
+ m->m_type = type | LK_SLEEP;
}
void _mtx_lock(mtx_t *m, const void *waitpt) {

diff:
@@ -14,7 +14,7 @@ void spin_init(spin_t *s, lock_type_t type) {
s->s_owner = NULL;
s->s_count = 0;
s->s_lockpt = NULL;
- s->s_type = type;
+ s->s_type = type | LK_SPIN;
}
void _spin_lock(spin_t *s, const void *waitpt) {

original_message: mtx_init & spin_init should set proper lock type (#677)


message: KASAN: rename PTE to PDE

diff:
#define KASAN_MD_SHADOW_START 0xF0000000
#define KASAN_MD_SHADOW_SIZE (1 << 24) /* 16 MB */
static_assert(KASAN_MD_SHADOW_SIZE % SUPERPAGESIZE == 0,
- "Shadow memory is described by several full PTEs");
+ "Shadow memory is described by several full PDEs");
#define KASAN_MD_SHADOW_END (KASAN_MD_SHADOW_START + KASAN_MD_SHADOW_SIZE)
/* Sanitized memory (accesses within this range are checked) */

diff:
@@ -104,10 +104,10 @@ __boot_text void *mips_init(void) {
va = KASAN_MD_SHADOW_START;
/* Allocate physical memory for shadow area */
paddr_t pa = (paddr_t)bootmem_alloc(KASAN_MD_SHADOW_SIZE);
- /* How many PTEs should we use? */
- int num_pte = KASAN_MD_SHADOW_SIZE / SUPERPAGESIZE;
- for (int i = 0; i < num_pte; i++) {
- /* Allocate a new PTE */
+ /* How many PDEs should we use? */
+ int num_pde = KASAN_MD_SHADOW_SIZE / SUPERPAGESIZE;
+ for (int i = 0; i < num_pde; i++) {
+ /* Allocate a new PT */
pte = bootmem_alloc(PAGESIZE);
pde[PDE_INDEX(va)] = PTE_PFN((intptr_t)pte) | PTE_KERNEL;
for (int j = 0; j < PT_ENTRIES; j++) {

original_message: KASAN: rename PTE to PDE (#699)


message: setgroups and getgroups syscalls

diff:
#include <stdio.h>
#include <errno.h>
#include <assert.h>
+#include <limits.h>
int test_get_set_uid(void) {
uid_t ruid, euid, suid;
@@ -89,3 +90,66 @@ int test_get_set_gid(void) {
return 0;
}
+
+int test_get_set_groups(void) {
+ gid_t rgrp[NGROUPS_MAX], gidset[NGROUPS_MAX] = {0, 1, 2, 3, 4, 5};
+ int r, ngroups = 6;
+ uid_t euid;
+
+ /* check if we are a root at start */
+ getresuid(NULL, &euid, NULL);
+ assert(euid == 0);
+ r = getgroups(0, NULL);
+ assert(r == 0);
+
+ /* setting too many groups */
+ r = setgroups(NGROUPS_MAX + 2, gidset);
+ assert(r == -1 && errno == EINVAL);
+
+ /* setting groups (without fail now) */
+ r = setgroups(ngroups, gidset);
+ assert(r == 0);
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* first argument is too small */
+ r = getgroups(ngroups - 1, rgrp);
+ assert(r == -1 && errno == EINVAL);
+
+ /* dropping all supplementary groups */
+ r = setgroups(0, NULL);
+ assert(r == 0);
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r == 0);
+
+ r = setgroups(-ngroups, gidset);
+ assert(r < 0 && errno == EINVAL);
+ /* setting for further tests */
+ r = setgroups(ngroups, gidset);
+ assert(r == 0);
+
+ /* dropping privileges */
+ setresuid(1, 1, 1);
+
+ /* dropping shouldn't affect supplementary groups */
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* we can't change suplementary groups when we are not root user */
+ r = setgroups(ngroups, gidset);
+ assert(r == -1 && errno == EPERM);
+
+ r = getgroups(NGROUPS_MAX, rgrp);
+ assert(r = ngroups);
+ for (int i = 0; i < r; ++i)
+ assert(rgrp[i] == gidset[i]);
+
+ /* first argument is too small */
+ r = getgroups(3, rgrp);
+ assert(r == -1 && errno == EINVAL);
+ return 0;
+}

diff:
@@ -85,6 +85,7 @@ int main(int argc, char **argv) {
CHECKRUN_TEST(get_set_uid);
CHECKRUN_TEST(get_set_gid);
+ CHECKRUN_TEST(get_set_groups);
printf("No user test \"%s\" available.\n", test_name);
return 1;

diff:
@@ -86,5 +86,6 @@ int test_killpg_other_group(void);
int test_get_set_uid(void);
int test_get_set_gid(void);
+int test_get_set_groups(void);
#endif /* __UTEST_H__ */

diff:
@@ -27,6 +27,7 @@ void do_getresuid(proc_t *p, uid_t *ruid, uid_t *euid, uid_t *suid);
void do_getresgid(proc_t *p, gid_t *rgid, gid_t *egid, gid_t *sgid);
int do_setresuid(proc_t *p, uid_t ruid, uid_t euid, uid_t suid);
int do_setresgid(proc_t *p, gid_t rgid, gid_t egid, gid_t sgid);
+int do_setgroups(proc_t *p, int ngroups, const gid_t *gidset);
void cred_fork(proc_t *to, proc_t *from);

diff:
#define SYS_sigsuspend 55
#define SYS_statvfs 56
#define SYS_fstatvfs 57
-#define SYS_MAXSYSCALL 58
+#define SYS_getgroups 58
+#define SYS_setgroups 59
+#define SYS_MAXSYSCALL 60
#define SYS_MAXSYSARGS 6

diff:
@@ -299,3 +299,13 @@ typedef struct {
int fd;
struct statvfs * buf;
} fstatvfs_args_t;
+
+typedef struct {
+ int ngroups;
+ gid_t * gidset;
+} getgroups_args_t;
+
+typedef struct {
+ int ngroups;
+ const gid_t * gidset;
+} setgroups_args_t;

diff:
@@ -37,6 +37,7 @@ gid_t getegid(void);
uid_t geteuid(void);
gid_t getgid(void);
int getgroups(int, gid_t[]);
+int setgroups(int, gid_t[]);
char *getlogin(void);
int getlogin_r(char *, size_t);
pid_t getpgrp(void);

diff:
@@ -56,3 +56,5 @@ SYSCALL(sched_yield, SYS_sched_yield)
SYSCALL(sigsuspend, SYS_sigsuspend)
SYSCALL(statvfs, SYS_statvfs)
SYSCALL(fstatvfs, SYS_fstatvfs)
+SYSCALL(getgroups, SYS_getgroups)
+SYSCALL(setgroups, SYS_setgroups)

diff:
@@ -120,3 +120,14 @@ end:
proc_unlock(p);
return error;
}
+
+int do_setgroups(proc_t *p, int ngroups, const gid_t *gidset) {
+ /* only root user can change supplementary groups */
+ if (p->p_cred.cr_euid != 0)
+ return EPERM;
+
+ p->p_cred.cr_ngroups = ngroups;
+ memcpy(p->p_cred.cr_groups, gidset, ngroups * sizeof(gid_t));
+
+ return 0;
+}

diff:
@@ -897,3 +897,47 @@ static int sys_fstatvfs(proc_t *p, fstatvfs_args_t *args, register_t *res) {
return error;
}
+
+static int sys_getgroups(proc_t *p, getgroups_args_t *args, register_t *res) {
+ int ngroups = args->ngroups;
+ gid_t *ugidset = args->gidset;
+ int pngroups = p->p_cred.cr_ngroups;
+
+ klog("getgroups(%d, %p)", ngroups, ugidset);
+
+ if (ngroups == 0) {
+ /* just return number of groups */
+ *res = pngroups;
+ return 0;
+ }
+
+ if (ngroups < pngroups)
+ return EINVAL;
+
+ *res = pngroups;
+ return copyout(p->p_cred.cr_groups, ugidset, pngroups * sizeof(gid_t));
+}
+
+static int sys_setgroups(proc_t *p, setgroups_args_t *args, register_t *res) {
+ int error = 0;
+ int ungroups = args->ngroups;
+ const gid_t *ugidset = args->gidset;
+
+ klog("setgroups(%d, %p)", ungroups, ugidset);
+
+ /* too many groups */
+ if (ungroups < 0 || ungroups > NGROUPS_MAX)
+ return EINVAL;
+
+ gid_t *gidset = kmalloc(M_TEMP, ungroups * sizeof(gid_t), M_NOWAIT);
+
+ /* if we set 0 groups kmalloc returns NULL, but it is not an error */
+ if (ungroups > 0 && gidset == NULL)
+ return ENOMEM;
+
+ if (!(error = copyin(ugidset, gidset, ungroups * sizeof(gid_t))))
+ error = do_setgroups(p, ungroups, gidset);
+
+ kfree(M_TEMP, gidset);
+ return error;
+}

diff:
55 { int sys_sigsuspend(sigset_t *sigmask); }
56 { int sys_statvfs(const char *path, struct statvfs *buf); }
57 { int sys_fstatvfs(int fd, struct statvfs *buf); }
+58 { int sys_getgroups(int ngroups, gid_t *gidset); }
+59 { int sys_setgroups(int ngroups, const gid_t *gidset); }
; vim: ts=4 sw=4 sts=4 et

diff:
@@ -63,6 +63,8 @@ static int sys_sched_yield(proc_t *, void *, register_t *);
static int sys_sigsuspend(proc_t *, sigsuspend_args_t *, register_t *);
static int sys_statvfs(proc_t *, statvfs_args_t *, register_t *);
static int sys_fstatvfs(proc_t *, fstatvfs_args_t *, register_t *);
+static int sys_getgroups(proc_t *, getgroups_args_t *, register_t *);
+static int sys_setgroups(proc_t *, setgroups_args_t *, register_t *);
struct sysent sysent[] = {
[SYS_syscall] = { .nargs = 1, .call = (syscall_t *)sys_syscall },
@@ -123,5 +125,7 @@ struct sysent sysent[] = {
[SYS_sigsuspend] = { .nargs = 1, .call = (syscall_t *)sys_sigsuspend },
[SYS_statvfs] = { .nargs = 2, .call = (syscall_t *)sys_statvfs },
[SYS_fstatvfs] = { .nargs = 2, .call = (syscall_t *)sys_fstatvfs },
+ [SYS_getgroups] = { .nargs = 2, .call = (syscall_t *)sys_getgroups },
+ [SYS_setgroups] = { .nargs = 2, .call = (syscall_t *)sys_setgroups },
};

diff:
@@ -141,3 +141,4 @@ UTEST_ADD_SIMPLE(killpg_other_group);
UTEST_ADD_SIMPLE(get_set_uid);
UTEST_ADD_SIMPLE(get_set_gid);
+UTEST_ADD_SIMPLE(get_set_groups);

original_message: setgroups and getgroups syscalls (#720)


message: Handle selected queries to sysconf

diff:
+#include <sys/syslimits.h>
+#include <unistd.h>
#include <errno.h>
long sysconf(int name) {
+ switch (name) {
+ case _SC_ARG_MAX:
+ return ARG_MAX;
+ case _SC_CHILD_MAX:
+ return CHILD_MAX;
+ case _SC_NGROUPS_MAX:
+ return NGROUPS_MAX;
+ case _SC_OPEN_MAX:
+ return OPEN_MAX;
+ case _SC_JOB_CONTROL:
+ default:
errno = EINVAL;
return -1;
}
+}

original_message: Handle selected queries to sysconf (#778)


message: Fix credential locking during fork

diff:
@@ -35,6 +35,10 @@ int do_setgid(proc_t *p, gid_t gid);
int do_setegid(proc_t *p, gid_t egid);
int do_setregid(proc_t *p, gid_t rgid, gid_t egid);
+/* \note Must be called with from::p_lock held. Returns with form::p_lock held.
+ * (We do this only while doing fork, so to is new proc not visible for other
+ * processes.)
+ */
void cred_fork(proc_t *to, proc_t *from);
#endif /* !_KERNEL */

diff:
#include <sys/cred.h>
#include <sys/proc.h>
#include <sys/libkern.h>
+#include <sys/mutex.h>
void cred_fork(proc_t *to, proc_t *from) {
+ assert(mtx_owned(&from->p_lock));
memcpy(&to->p_cred, &from->p_cred, sizeof(cred_t));
}

diff:
@@ -53,8 +53,13 @@ int do_fork(void (*start)(void *), void *arg, pid_t *cldpidp) {
/* Now, prepare a new process. */
proc_t *child = proc_create(newtd, parent);
- /* Clone credentials. */
+ /* Clone credentials.
+ * We have to do it with parent::p_lock held.
+ * We don't need to hold child::p_lock because child is not visible yet.
+ */
+ WITH_PROC_LOCK(parent) {
cred_fork(child, parent);
+ }
/* Clone the entire process memory space. */
child->p_uspace = vm_map_clone(parent->p_uspace);

original_message: Fix credential locking during fork (#801)


diff:
#include <sys/types.h>
#include <sys/syslimits.h>
+#include <sys/signal.h>
#ifdef _KERNEL
@@ -41,6 +42,14 @@ int do_setregid(proc_t *p, gid_t rgid, gid_t egid);
*/
void cred_fork(proc_t *to, proc_t *from);
+/* \note Must be called with p::p_lock. Returns p::p_lock held. */
+int cred_cansignal(proc_t *p, cred_t *cred);
+
+/* Checks whether the current process has permission to send `sig` to `target`
+ * process. \note Must be called with all_proc_mtx and target::p_lock. Returns
+ * with lock held. */
+int proc_cansignal(proc_t *target, signo_t sig);
+
#endif /* !_KERNEL */
#endif /* _SYS_CRED_H_ */

diff:
@@ -91,7 +91,7 @@ struct proc {
TAILQ_ENTRY(proc) p_hash; /* (a) link on pid hash chain */
thread_t *p_thread; /* (@) the only thread running in this process */
pid_t p_pid; /* (!) Process ID */
- cred_t p_cred; /* (@) Process credentials */
+ cred_t p_cred; /* (@, *) Process credentials */
char *p_elfpath; /* (!) path of loaded elf file */
TAILQ_ENTRY(proc) p_pglist; /* (g + a) link on pg_members list */
pgrp_t *p_pgrp; /* (@ + a) process group */
@@ -140,6 +140,7 @@ void proc_add(proc_t *p);
proc_t *proc_find(pid_t pid);
/*! \brief Sends signal to process group or process.
+ * Signal is send on behalf of the current process. Performs privilege checks.
* (pid > 0) sends signal to the process with the ID specified by pid.
* (pid = 0) sends signal to processes in process group of the calling process.
* (pid <-1) sends signal to processes in process group with ID equal (-pid). */

diff:
@@ -11,6 +11,7 @@ SOURCES = \
condvar.c \
console.c \
cred.c \
+ cred_checks.c \
cred_syscalls.c \
devclass.c \
device.c \

diff:
+#include <sys/cred.h>
+#include <sys/proc.h>
+#include <sys/errno.h>
+#include <sys/mutex.h>
+
+int cred_cansignal(proc_t *target, cred_t *cred) {
+ assert(mtx_owned(&target->p_lock));
+
+ if (cred->cr_euid == 0)
+ return 0;
+
+ if (cred->cr_ruid != target->p_cred.cr_ruid &&
+ cred->cr_ruid != target->p_cred.cr_suid &&
+ cred->cr_euid != target->p_cred.cr_ruid &&
+ cred->cr_euid != target->p_cred.cr_suid)
+ return EPERM;
+
+ return 0;
+}
+
+int proc_cansignal(proc_t *target, signo_t sig) {
+ assert(mtx_owned(all_proc_mtx));
+ assert(mtx_owned(&target->p_lock));
+
+ proc_t *p = proc_self();
+
+ if (target == p)
+ return 0;
+
+ /* process can send SIGCONT to every process in the same session */
+ if (sig == SIGCONT && p->p_pgrp->pg_session == target->p_pgrp->pg_session)
+ return 0;
+
+ return cred_cansignal(target, &p->p_cred);
+}

diff:
@@ -590,42 +590,66 @@ __noreturn void proc_exit(int exitstatus) {
thread_exit();
}
+static int proc_pgsignal(pgid_t pgid, signo_t sig) {
+ pgrp_t *pgrp = NULL;
+ SCOPED_MTX_LOCK(all_proc_mtx);
+
+ if (pgid == 0) {
+ pgrp = proc_self()->p_pgrp;
+ } else {
+ pgrp = pgrp_lookup(pgid);
+ if (!pgrp)
+ return ESRCH;
+ }
+
+ proc_t *target;
+ int send = 0, error = 0;
+ TAILQ_FOREACH (target, &pgrp->pg_members, p_pglist) {
+ WITH_PROC_LOCK(target) {
+ if (!(error = proc_cansignal(target, sig))) {
+ sig_kill(target, sig);
+ send++;
+ }
+ }
+ }
+
+ /* We return error when signal can't be send to any process. Returned error is
+ * last error obtained from checking privileges.*/
+ return send > 0 ? 0 : error;
+}
+
int proc_sendsig(pid_t pid, signo_t sig) {
+ if (sig >= NSIG)
+ return EINVAL;
+
+ int error;
proc_t *target;
if (pid > 0) {
- WITH_MTX_LOCK (all_proc_mtx)
+ SCOPED_MTX_LOCK(all_proc_mtx);
target = proc_find(pid);
if (target == NULL)
- return EINVAL;
+ return ESRCH;
+ if (!(error = proc_cansignal(target, sig)))
sig_kill(target, sig);
proc_unlock(target);
- return 0;
+ return error;
}
+ switch (pid) {
+ case -1:
/* TODO send sig to every process for which the calling process has
* permission to send signals, except init process */
- if (pid == -1)
- return ENOTSUP;
-
- pgrp_t *pgrp = NULL;
-
- WITH_MTX_LOCK (all_proc_mtx) {
- if (pid == 0)
- pgrp = proc_self()->p_pgrp;
-
- if (pid < -1) {
- pgrp = pgrp_lookup(-pid);
- if (!pgrp)
- return EINVAL;
- }
- mtx_lock(&pgrp->pg_lock);
+ error = ENOTSUP;
+ break;
+ case 0:
+ error = proc_pgsignal(0, sig);
+ break;
+ default:
+ error = proc_pgsignal(-pid, sig);
}
-
- sig_pgkill(pgrp, sig);
- mtx_unlock(&pgrp->pg_lock);
- return 0;
+ return error;
}
static bool is_zombie(proc_t *p) {

original_message: Check if process is allowed to send signal (#802)