[haiku-commits] Change in haiku[master]: kernel/arch/int: implement for riscv64

  • From: Gerrit <review@xxxxxxxxxxxxxxxxxxx>
  • To: waddlesplash <waddlesplash@xxxxxxxxx>, haiku-commits@xxxxxxxxxxxxx
  • Date: Sun, 6 Jun 2021 14:44:55 +0000

From X512 <danger_mail@xxxxxxx>:

X512 has uploaded this change for review. ( 
https://review.haiku-os.org/c/haiku/+/4052 ;)


Change subject: kernel/arch/int: implement for riscv64
......................................................................

kernel/arch/int: implement for riscv64
---
M headers/private/kernel/arch/riscv64/arch_int.h
M src/system/kernel/arch/riscv64/Jamfile
M src/system/kernel/arch/riscv64/arch_int.cpp
A src/system/kernel/arch/riscv64/arch_traps.S
4 files changed, 614 insertions(+), 0 deletions(-)



  git pull ssh://git.haiku-os.org:22/haiku refs/changes/52/4052/1

diff --git a/headers/private/kernel/arch/riscv64/arch_int.h 
b/headers/private/kernel/arch/riscv64/arch_int.h
index 537e915..ac09c63 100644
--- a/headers/private/kernel/arch/riscv64/arch_int.h
+++ b/headers/private/kernel/arch/riscv64/arch_int.h
@@ -10,7 +10,66 @@
 #define _KERNEL_ARCH_RISCV64_INT_H

 #include <SupportDefs.h>
+#include <arch_cpu_defs.h>

 #define NUM_IO_VECTORS 256

+
+#define Sstatus() ({uint64_t x; asm volatile("csrr %0, sstatus" : "=r" (x)); 
x;})
+#define SetSstatus(x) {asm volatile("csrw sstatus, %0" : : "r" (x));}
+
+
+static inline void
+arch_int_enable_interrupts_inline(void)
+{
+       SstatusReg status(Sstatus());
+       status.ie |= (1 << modeS);
+       SetSstatus(status.val);
+}
+
+
+static inline int
+arch_int_disable_interrupts_inline(void)
+{
+       SstatusReg status(Sstatus());
+       int oldState = ((1 << modeS) & status.ie) != 0;
+       status.ie &= ~(1 << modeS);
+       SetSstatus(status.val);
+       return oldState;
+}
+
+
+static inline void
+arch_int_restore_interrupts_inline(int oldState)
+{
+       if (oldState)
+               arch_int_enable_interrupts_inline();
+}
+
+
+static inline bool
+arch_int_are_interrupts_enabled_inline(void)
+{
+       SstatusReg status(Sstatus());
+       return ((1 << modeS) & status.ie) != 0;
+}
+
+
+// map the functions to the inline versions
+#define arch_int_enable_interrupts()   arch_int_enable_interrupts_inline()
+#define arch_int_disable_interrupts()  arch_int_disable_interrupts_inline()
+#define arch_int_restore_interrupts(status)    \
+       arch_int_restore_interrupts_inline(status)
+#define arch_int_are_interrupts_enabled()      \
+       arch_int_are_interrupts_enabled_inline()
+
+
+enum {
+       switchToSmodeMmodeSyscall = 0,
+       setTimerMmodeSyscall = 1,
+};
+
+extern "C" status_t MSyscall(...);
+
+
 #endif /* _KERNEL_ARCH_RISCV64_INT_H */
diff --git a/src/system/kernel/arch/riscv64/Jamfile 
b/src/system/kernel/arch/riscv64/Jamfile
index e6d005b..0371ebb 100644
--- a/src/system/kernel/arch/riscv64/Jamfile
+++ b/src/system/kernel/arch/riscv64/Jamfile
@@ -5,6 +5,7 @@

 KernelMergeObject kernel_arch_riscv64.o :
         arch_asm.S
+        arch_traps.S
         arch_commpage.cpp
         arch_cpu.cpp
         arch_debug.cpp
diff --git a/src/system/kernel/arch/riscv64/arch_int.cpp 
b/src/system/kernel/arch/riscv64/arch_int.cpp
index 885044d..24b3228 100644
--- a/src/system/kernel/arch/riscv64/arch_int.cpp
+++ b/src/system/kernel/arch/riscv64/arch_int.cpp
@@ -8,11 +8,381 @@


 #include <int.h>
+#include <cpu.h>
+#include <thread.h>
+#include <vm/vm_priv.h>
+#include <ksyscalls.h>
+#include <syscall_numbers.h>
+#include <arch_cpu_defs.h>
+#include <arch_thread_types.h>
+#include <arch/debug.h>
+#include <util/AutoLock.h>
+#include <Htif.h>
+#include <Plic.h>
+#include <Clint.h>
+
+#include <algorithm>
+
+
+__attribute__ ((aligned (16))) char sMStack[64*1024];
+
+
+extern "C" void MVec();
+extern "C" void MVecS();
+extern "C" void SVec();
+extern "C" void SVecU();
+
+
+//#pragma mark debug output
+
+void WriteMode(int mode)
+{
+       switch (mode) {
+               case modeU: dprintf("u"); break;
+               case modeS: dprintf("s"); break;
+               case modeM: dprintf("m"); break;
+               default: dprintf("%d", mode);
+       }
+}
+
+void WriteModeSet(uint32_t val)
+{
+       bool first = true;
+       dprintf("{");
+       for (int i = 0; i < 32; i++) {
+               if (((1LL << i) & val) != 0) {
+                       if (first) first = false; else dprintf(", ");
+                       WriteMode(i);
+               }
+       }
+       dprintf("}");
+}
+
+void WriteMstatus(uint64_t val)
+{
+       MstatusReg status(val);
+       dprintf("(");
+       dprintf("ie: "); WriteModeSet(status.ie);
+       dprintf(", pie: "); WriteModeSet(status.pie);
+       dprintf(", spp: "); WriteMode(status.spp);
+       dprintf(", mpp: "); WriteMode(status.mpp);
+       dprintf(", sum: %d", (int)status.sum);
+       dprintf(")");
+}
+
+void WriteSstatus(uint64_t val)
+{
+       SstatusReg status(val);
+       dprintf("(");
+       dprintf("ie: "); WriteModeSet(status.ie);
+       dprintf(", pie: "); WriteModeSet(status.pie);
+       dprintf(", spp: "); WriteMode(status.spp);
+       dprintf(", sum: %d", (int)status.sum);
+       dprintf(")");
+}
+
+void WriteInterrupt(uint64_t val)
+{
+       switch (val) {
+               case 0 + modeU: dprintf("uSoft"); break;
+               case 0 + modeS: dprintf("sSoft"); break;
+               case 0 + modeM: dprintf("mSoft"); break;
+               case 4 + modeU: dprintf("uTimer"); break;
+               case 4 + modeS: dprintf("sTimer"); break;
+               case 4 + modeM: dprintf("mTimer"); break;
+               case 8 + modeU: dprintf("uExtern"); break;
+               case 8 + modeS: dprintf("sExtern"); break;
+               case 8 + modeM: dprintf("mExtern"); break;
+               default: dprintf("%" B_PRId64, val);
+       }
+}
+
+void WriteInterruptSet(uint64_t val)
+{
+       bool first = true;
+       dprintf("{");
+       for (int i = 0; i < 64; i++) {
+               if (((1LL << i) & val) != 0) {
+                       if (first) first = false; else dprintf(", ");
+                       WriteInterrupt(i);
+               }
+       }
+       dprintf("}");
+}
+
+void WriteCause(uint64_t cause)
+{
+       if ((cause & causeInterrupt) == 0) {
+               dprintf("exception ");
+               switch (cause) {
+                       case causeExecMisalign: dprintf("execMisalign"); break;
+                       case causeExecAccessFault: dprintf("execAccessFault"); 
break;
+                       case causeIllegalInst: dprintf("illegalInst"); break;
+                       case causeBreakpoint: dprintf("breakpoint"); break;
+                       case causeLoadMisalign: dprintf("loadMisalign"); break;
+                       case causeLoadAccessFault: dprintf("loadAccessFault"); 
break;
+                       case causeStoreMisalign: dprintf("storeMisalign"); 
break;
+                       case causeStoreAccessFault: 
dprintf("storeAccessFault"); break;
+                       case causeUEcall: dprintf("uEcall"); break;
+                       case causeSEcall: dprintf("sEcall"); break;
+                       case causeMEcall: dprintf("mEcall"); break;
+                       case causeExecPageFault: dprintf("execPageFault"); 
break;
+                       case causeLoadPageFault: dprintf("loadPageFault"); 
break;
+                       case causeStorePageFault: dprintf("storePageFault"); 
break;
+                       default: dprintf("%" B_PRId64, cause);
+                       }
+       } else {
+               dprintf("interrupt "); WriteInterrupt(cause & ~causeInterrupt);
+       }
+}
+
+void WriteTrapInfo()
+{
+       dprintf("STrap("); WriteCause(Scause()); dprintf(")\n");
+       dprintf("  sstatus: "); WriteSstatus(Sstatus()); dprintf("\n");
+       dprintf("  sie: "); WriteInterruptSet(Sie()); dprintf("\n");
+       dprintf("  sip: "); WriteInterruptSet(Sip()); dprintf("\n");
+       dprintf("  stval: "); WritePC(Stval()); dprintf("\n");
+       dprintf("  tp: 0x%" B_PRIxADDR "(%s)\n", Tp(), 
thread_get_current_thread()->name);
+       //dprintf("  stval: 0x%" B_PRIx64 "\n", Stval());
+}
+
+
+//#pragma mark -
+
+extern "C" void MTrap(iframe* frame)
+{
+       uint64 cause = Mcause();
+/*
+       HtifOutString("+MTrap("); WriteCause(Mcause()); HtifOutString(")\n");
+       dprintf("  mstatus: "); WriteMstatus(Mstatus()); dprintf("\n");
+       dprintf("  mie: "); WriteInterruptSet(Mie()); dprintf("\n");
+       dprintf("  mip: "); WriteInterruptSet(Mip()); dprintf("\n");
+       dprintf("  sie: "); WriteInterruptSet(Sie()); dprintf("\n");
+       dprintf("  sip: "); WriteInterruptSet(Sip()); dprintf("\n");
+       dprintf("  mscratch: 0x%" B_PRIxADDR "\n", Mscratch());
+       DoStackTrace(Fp(), 0);
+*/
+       switch (cause) {
+               case causeMEcall:
+               case causeSEcall: {
+                       frame->epc += 4;
+                       uint64 op = frame->a0;
+                       switch (op) {
+                               case switchToSmodeMmodeSyscall: {
+                                       
HtifOutString("switchToSmodeMmodeSyscall()\n");
+                                       MstatusReg status(Mstatus());
+                                       status.mpp = modeS;
+                                       SetMedeleg(0xffff & ~((1 << 
causeMEcall) | (1 << causeSEcall)));
+                                       SetMideleg(0xffff & ~(1 << mTimerInt));
+                                       SetMstatus(status.val);
+                                       dprintf("modeM stack: 0x%" B_PRIxADDR 
", 0x%" B_PRIxADDR "\n", (addr_t)sMStack, (addr_t)(sMStack + sizeof(sMStack)));
+                                       SetMscratch((addr_t)(sMStack + 
sizeof(sMStack)));
+                                       SetMtvec((uint64)MVecS);
+                                       frame->a0 = B_OK;
+                                       return;
+                               }
+                               case setTimerMmodeSyscall: {
+                                       // 
HtifOutString("setTimerMmodeSyscall()\n");
+                                       bool enable = frame->a1 != 0;
+                                       SetSip(Sip() & ~(1 << sTimerInt));
+                                       if (!enable) {
+                                               SetMie(Mie() & ~(1 << 
mTimerInt));
+                                       } else {
+                                               gClintRegs->mTimeCmp[0] = 
frame->a2;
+                                               SetMie(Mie() | (1 << 
mTimerInt));
+                                       }
+                                       frame->a0 = B_OK;
+                                       return;
+                               }
+                               default:
+                                       frame->a0 = B_NOT_SUPPORTED;
+                                       return;
+                       }
+                       break;
+               }
+               case causeInterrupt + mTimerInt: {
+                       disable_interrupts();
+                       SetMie(Mie() & ~(1 << mTimerInt));
+                       SetMip(Mip() | (1 << sTimerInt));
+                       return;
+               }
+       }
+       HtifOutString("unhandled MTrap\n");
+       // DoStackTrace(Fp(), 0);
+       HtifShutdown();
+}
+
+
+static void SendSignal(debug_exception_type type, uint32 signalNumber, int32 
signalCode, addr_t signalAddress = 0, int32 signalError = B_ERROR)
+{
+       if (SstatusReg(Sstatus()).spp == modeU) {
+               struct sigaction action;
+               Thread* thread = thread_get_current_thread();
+
+               WriteTrapInfo();
+               DoStackTrace(Fp(), 0);
+
+               enable_interrupts();
+
+               // If the thread has a signal handler for the signal, we simply 
send it
+               // the signal. Otherwise we notify the user debugger first.
+               if ((sigaction(signalNumber, NULL, &action) == 0
+                               && action.sa_handler != SIG_DFL
+                               && action.sa_handler != SIG_IGN)
+                       || user_debug_exception_occurred(type, signalNumber)) {
+                       Signal signal(signalNumber, signalCode, signalError,
+                               thread->team->id);
+                       signal.SetAddress((void*)signalAddress);
+                       send_signal_to_thread(thread, signal, 0);
+               }
+       } else {
+               WriteTrapInfo();
+               panic("Unexpected exception occurred in kernel mode!");
+       }
+}
+
+static void AfterInterrupt()
+{
+       Thread* thread = thread_get_current_thread();
+       cpu_status state = disable_interrupts();
+       if (thread->cpu->invoke_scheduler) {
+               SpinLocker schedulerLocker(thread->scheduler_lock);
+               scheduler_reschedule(B_THREAD_READY);
+               schedulerLocker.Unlock();
+               restore_interrupts(state);
+       } else if (thread->post_interrupt_callback != NULL) {
+               void (*callback)(void*) = thread->post_interrupt_callback;
+               void* data = thread->post_interrupt_data;
+
+               thread->post_interrupt_callback = NULL;
+               thread->post_interrupt_data = NULL;
+
+               restore_interrupts(state);
+
+               callback(data);
+       }
+}
+
+
+extern "C" void STrap(iframe* frame)
+{
+       // dprintf("STrap("); WriteCause(Scause()); dprintf(")\n");
+       if (SstatusReg(Sstatus()).spp == modeU) {
+               thread_get_current_thread()->arch_info.userFrame = frame;
+               thread_at_kernel_entry(system_time());
+       }
+       struct ScopeExit {
+               ~ScopeExit()
+               {
+                       if (SstatusReg(Sstatus()).spp == modeU) {
+                               if ((thread_get_current_thread()->flags & 
(THREAD_FLAGS_SIGNALS_PENDING | THREAD_FLAGS_DEBUG_THREAD | 
THREAD_FLAGS_TRAP_FOR_CORE_DUMP)) != 0) {
+                                       enable_interrupts();
+                                       thread_at_kernel_exit();
+                               } else {
+                                       thread_at_kernel_exit_no_signals();
+                               }
+                               
thread_get_current_thread()->arch_info.userFrame = NULL;
+                       }
+               }
+       } scopeExit;
+
+       uint64 cause = Scause();
+       switch (cause) {
+               case causeIllegalInst:
+                       return SendSignal(B_INVALID_OPCODE_EXCEPTION, SIGILL, 
ILL_ILLOPC, frame->epc);
+               case causeExecMisalign:
+               case causeLoadMisalign:
+               case causeStoreMisalign:
+                       return SendSignal(B_ALIGNMENT_EXCEPTION, SIGBUS, 
BUS_ADRALN, Stval());
+               // case causeBreakpoint:
+               // case causeExecAccessFault:
+               // case causeLoadAccessFault:
+               // case causeStoreAccessFault:
+               case causeExecPageFault:
+               case causeLoadPageFault:
+               case causeStorePageFault: {
+                       uint64 stval = Stval();
+                       SstatusReg status(Sstatus());
+                       addr_t newIP = 0;
+                       enable_interrupts();
+                       vm_page_fault(stval, frame->epc, cause == 
causeStorePageFault, cause == causeExecPageFault, status.spp == modeU, &newIP);
+                       if (newIP != 0)
+                               frame->epc = newIP;
+                       SetSstatus(status.val);
+                       return;
+               }
+               case causeInterrupt + sTimerInt: {
+                       SstatusReg status(Sstatus());
+                       timer_interrupt();
+                       AfterInterrupt();
+                       SetSstatus(status.val);
+                       return;
+               }
+               case causeInterrupt + sExternInt: {
+                       SstatusReg status(Sstatus());
+                       uint64 irq = gPlicRegs->contexts[0].claimAndComplete;
+                       int_io_interrupt_handler(irq, true);
+                       gPlicRegs->contexts[0].claimAndComplete = irq;
+                       AfterInterrupt();
+                       SetSstatus(status.val);
+                       return;
+               }
+               case causeUEcall: {
+                       frame->epc += 4; // skip ecall
+                       uint64 syscall = frame->t0;
+                       uint64 args[20];
+                       if (syscall < (uint64)kSyscallCount) {
+                               uint32 argCnt = 
kExtendedSyscallInfos[syscall].parameter_count;
+                               memcpy(&args[0], &frame->a0, 
sizeof(uint64)*std::min<uint32>(argCnt, 8));
+                               if (argCnt > 8) {
+                                       if(user_memcpy(&args[8], 
(void*)frame->sp, sizeof(uint64)*(argCnt - 8)) < B_OK) {
+                                               dprintf("can't read syscall 
arguments on user stack\n");
+                                               frame->a0 = B_BAD_ADDRESS;
+                                               return;
+                                       }
+                               }
+                       }
+/*
+                       switch (syscall) {
+                               case SYSCALL_READ_PORT_ETC:
+                               case SYSCALL_WRITE_PORT_ETC:
+                                       WriteTrapInfo();
+                                       DoStackTrace(Fp(), 0);
+                                       break;
+                       }
+*/
+                       // dprintf("syscall: %s\n", 
kExtendedSyscallInfos[syscall].name);
+                       SstatusReg status(Sstatus());
+                       enable_interrupts();
+                       uint64 returnValue = 0;
+                       syscall_dispatcher(syscall, (void*)args, &returnValue);
+                       frame->a0 = returnValue;
+                       SetSstatus(status.val);
+                       return;
+               }
+       }
+       WriteTrapInfo();
+       panic("unhandled STrap");
+}


 status_t
 arch_int_init(kernel_args *args)
 {
+       SetMtvec((uint64)MVec);
+       SetStvec((uint64)SVec);
+       MstatusReg mstatus(Mstatus());
+       mstatus.ie = 1 << modeM;
+       mstatus.fs = extStatusInitial; // enable FPU
+       mstatus.xs = extStatusOff;
+       SetMstatus(mstatus.val);
+       MSyscall(switchToSmodeMmodeSyscall);
+       SetSie(Sie() | (1 << sTimerInt) | (1 << sExternInt));
+
+       // TODO: read from FDT
+       reserve_io_interrupt_vectors(32, 0, INTERRUPT_TYPE_IRQ);
+
        return B_OK;
 }

diff --git a/src/system/kernel/arch/riscv64/arch_traps.S 
b/src/system/kernel/arch/riscv64/arch_traps.S
new file mode 100644
index 0000000..ea69696
--- /dev/null
+++ b/src/system/kernel/arch/riscv64/arch_traps.S
@@ -0,0 +1,184 @@
+# NOTE: this macro don't save SP, it should be saved manually
+.macro PushTrapFrame
+       addi sp, sp, -256
+
+       sd ra,   0*8(sp)
+       sd t6,   1*8(sp)
+#      sd sp,   2*8(sp) # sp
+       sd gp,   3*8(sp)
+       sd tp,   4*8(sp)
+       sd t0,   5*8(sp)
+       sd t1,   6*8(sp)
+       sd t2,   7*8(sp)
+       sd t5,   8*8(sp)
+       sd s1,   9*8(sp)
+       sd a0,  10*8(sp)
+       sd a1,  11*8(sp)
+       sd a2,  12*8(sp)
+       sd a3,  13*8(sp)
+       sd a4,  14*8(sp)
+       sd a5,  15*8(sp)
+       sd a6,  16*8(sp)
+       sd a7,  17*8(sp)
+       sd s2,  18*8(sp)
+       sd s3,  19*8(sp)
+       sd s4,  20*8(sp)
+       sd s5,  21*8(sp)
+       sd s6,  22*8(sp)
+       sd s7,  23*8(sp)
+       sd s8,  24*8(sp)
+       sd s9,  25*8(sp)
+       sd s10, 26*8(sp)
+       sd s11, 27*8(sp)
+       sd t3,  28*8(sp)
+       sd t4,  29*8(sp)
+       sd fp,  30*8(sp)
+
+       addi fp, sp, 256
+.endm
+
+
+.macro PopTrapFrame
+       ld ra,   0*8(sp)
+       ld t6,   1*8(sp)
+#      ld sp,   2*8(sp) restore later
+       ld gp,   3*8(sp)
+#      ld tp,   4*8(sp)
+       ld t0,   5*8(sp)
+       ld t1,   6*8(sp)
+       ld t2,   7*8(sp)
+       ld t5,   8*8(sp)
+       ld s1,   9*8(sp)
+       ld a0,  10*8(sp)
+       ld a1,  11*8(sp)
+       ld a2,  12*8(sp)
+       ld a3,  13*8(sp)
+       ld a4,  14*8(sp)
+       ld a5,  15*8(sp)
+       ld a6,  16*8(sp)
+       ld a7,  17*8(sp)
+       ld s2,  18*8(sp)
+       ld s3,  19*8(sp)
+       ld s4,  20*8(sp)
+       ld s5,  21*8(sp)
+       ld s6,  22*8(sp)
+       ld s7,  23*8(sp)
+       ld s8,  24*8(sp)
+       ld s9,  25*8(sp)
+       ld s10, 26*8(sp)
+       ld s11, 27*8(sp)
+       ld t3,  28*8(sp)
+       ld t4,  29*8(sp)
+       ld fp,  30*8(sp)
+
+       ld sp,   2*8(sp)
+.endm
+
+
+.globl MVec
+.type  MVec, @function
+.align 4
+MVec:
+       PushTrapFrame
+       sd fp, 2*8(sp)
+       csrr t0, mepc
+       sd   t0, 31*8(sp)
+
+       mv a0, sp
+       call MTrap
+
+       ld t0, 31*8(sp)
+       csrw mepc, t0
+       PopTrapFrame
+       mret
+.size  MVec, .-MVec
+
+
+.globl MVecS
+.type  MVecS, @function
+.align 4
+MVecS:
+       csrrw sp, mscratch, sp
+
+       PushTrapFrame
+
+       csrr t0, mscratch
+       sd t0, 2*8(sp) # save supervisor SP
+       csrw mscratch, fp
+
+       csrr t0, mepc
+       sd   t0, 31*8(sp)
+
+       la   t0,    MVec
+       csrw mtvec, t0
+
+       mv a0, sp
+       call MTrap
+
+       la   t0,    MVecS
+       csrw mtvec, t0
+
+       ld t0, 31*8(sp)
+       csrw mepc, t0
+       PopTrapFrame
+       mret
+.size  MVecS, .-MVecS
+
+
+.globl SVec
+.type  SVec, @function
+.align 4
+SVec:
+       PushTrapFrame
+       sd fp, 2*8(sp)
+       csrr t0, sepc
+       sd   t0, 31*8(sp)
+
+       mv a0, sp
+       call STrap
+
+       ld t0, 31*8(sp)
+       csrw sepc, t0
+       PopTrapFrame
+       sret
+.size  SVec, .-SVec
+
+
+.globl SVecU
+.type  SVecU, @function
+.align 4
+SVecU:
+       csrrw t0, sscratch, t0   # t0: &arch_thread
+       ld    tp,        0*8(t0) # tp = arch_thread.thread
+       ld    t0, (1 + 13)*8(t0) # t0 = arch_thread.context.sp
+       sd    sp,  2*8 - 256(t0) # save user SP
+       mv    sp, t0             # switch to kernel stack
+       csrr  t0, sscratch
+
+       PushTrapFrame
+
+       csrr t0, sepc
+       sd   t0, 31*8(sp)
+
+       la   t0,    SVec
+       csrw stvec, t0
+
+       mv a0, sp
+       call STrap
+
+.globl SVecURet
+.type  SVecURet, @function
+SVecURet:
+       call RestoreUserRegs
+
+       csrr  t0, sscratch
+       sd    fp, (1 + 13)*8(t0) # arch_thread.context.sp = fp
+
+       la   t0,    SVecU
+       csrw stvec, t0
+
+       ld t0, 31*8(sp)
+       csrw sepc, t0
+       PopTrapFrame
+       sret
+.size  SVecU, .-SVecU

--
To view, visit https://review.haiku-os.org/c/haiku/+/4052
To unsubscribe, or for help writing mail filters, visit 
https://review.haiku-os.org/settings

Gerrit-Project: haiku
Gerrit-Branch: master
Gerrit-Change-Id: I62d9bff75d35a685983c626720514ff17b1cef00
Gerrit-Change-Number: 4052
Gerrit-PatchSet: 1
Gerrit-Owner: X512 <danger_mail@xxxxxxx>
Gerrit-MessageType: newchange

Other related posts: