forked from xuos/xiuos
Fully support userland interrupt handler. Use fixed common abort handler.
This commit is contained in:
parent
a7cbb0d041
commit
a24d73f710
|
@ -41,30 +41,13 @@ Modification:
|
||||||
*************************************************/
|
*************************************************/
|
||||||
#include "core.h"
|
#include "core.h"
|
||||||
#include "memlayout.h"
|
#include "memlayout.h"
|
||||||
|
|
||||||
#include "log.h"
|
|
||||||
#include "multicores.h"
|
|
||||||
#include "spinlock.h"
|
#include "spinlock.h"
|
||||||
#include "syscall.h"
|
|
||||||
#include "trap_common.h"
|
#include "trap_common.h"
|
||||||
|
|
||||||
__attribute__((always_inline)) static inline void _abort_reason(uint32_t fault_status)
|
#include "assert.h"
|
||||||
{
|
#include "multicores.h"
|
||||||
if ((fault_status & 0xd) == 0x1) // Alignment failure
|
#include "syscall.h"
|
||||||
KPrintf("reason: alignment\n");
|
#include "task.h"
|
||||||
else if ((fault_status & 0xd) == 0x5) // External abort "on translation"
|
|
||||||
KPrintf("reason: ext. abort on trnslt.\n");
|
|
||||||
else if ((fault_status & 0xd) == 0x5) // Translation
|
|
||||||
KPrintf("reason: sect. translation\n");
|
|
||||||
else if ((fault_status & 0xd) == 0x9) // Domain
|
|
||||||
KPrintf("reason: sect. domain\n");
|
|
||||||
else if ((fault_status & 0xd) == 0xd) // Permission
|
|
||||||
KPrintf("reason: sect. permission\n");
|
|
||||||
else if ((fault_status & 0xd) == 0x8) // External abort
|
|
||||||
KPrintf("reason: ext. abort\n");
|
|
||||||
else
|
|
||||||
KPrintf("reason: unknown???\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
void dump_tf(struct trapframe* tf)
|
void dump_tf(struct trapframe* tf)
|
||||||
{
|
{
|
||||||
|
@ -86,6 +69,57 @@ void dump_tf(struct trapframe* tf)
|
||||||
KPrintf(" pc: 0x%x\n", tf->pc);
|
KPrintf(" pc: 0x%x\n", tf->pc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void dabort_reason(struct trapframe* r)
|
||||||
|
{
|
||||||
|
uint32_t fault_status, dfa;
|
||||||
|
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 0" : "=r"(fault_status)::);
|
||||||
|
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 0" : "=r"(dfa)::);
|
||||||
|
LOG("program counter: 0x%x caused\n", r->pc);
|
||||||
|
LOG("data abort at 0x%x, status 0x%x\n", dfa, fault_status);
|
||||||
|
|
||||||
|
if ((fault_status & 0xd) == 0x1) // Alignment failure
|
||||||
|
KPrintf("reason: alignment\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x5) // External abort "on translation"
|
||||||
|
KPrintf("reason: ext. abort on trnslt.\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x5) // Translation
|
||||||
|
KPrintf("reason: sect. translation\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x9) // Domain
|
||||||
|
KPrintf("reason: sect. domain\n");
|
||||||
|
else if ((fault_status & 0xd) == 0xd) // Permission
|
||||||
|
KPrintf("reason: sect. permission\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x8) // External abort
|
||||||
|
KPrintf("reason: ext. abort\n");
|
||||||
|
else
|
||||||
|
KPrintf("reason: unknown???\n");
|
||||||
|
|
||||||
|
dump_tf(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void iabort_reason(struct trapframe* r)
|
||||||
|
{
|
||||||
|
uint32_t fault_status, ifa;
|
||||||
|
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 1" : "=r"(fault_status)::);
|
||||||
|
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 2" : "=r"(ifa)::);
|
||||||
|
LOG("prefetch abort at 0x%x, status 0x%x\n", ifa, fault_status);
|
||||||
|
|
||||||
|
if ((fault_status & 0xd) == 0x1) // Alignment failure
|
||||||
|
KPrintf("reason: alignment\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x5) // External abort "on translation"
|
||||||
|
KPrintf("reason: ext. abort on trnslt.\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x5) // Translation
|
||||||
|
KPrintf("reason: sect. translation\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x9) // Domain
|
||||||
|
KPrintf("reason: sect. domain\n");
|
||||||
|
else if ((fault_status & 0xd) == 0xd) // Permission
|
||||||
|
KPrintf("reason: sect. permission\n");
|
||||||
|
else if ((fault_status & 0xd) == 0x8) // External abort
|
||||||
|
KPrintf("reason: ext. abort\n");
|
||||||
|
else
|
||||||
|
KPrintf("reason: unknown???\n");
|
||||||
|
|
||||||
|
dump_tf(r);
|
||||||
|
}
|
||||||
|
|
||||||
void handle_undefined_instruction(struct trapframe* tf)
|
void handle_undefined_instruction(struct trapframe* tf)
|
||||||
{
|
{
|
||||||
// unimplemented trap handler
|
// unimplemented trap handler
|
||||||
|
@ -105,61 +139,4 @@ void handle_fiq(void)
|
||||||
{
|
{
|
||||||
xizi_enter_kernel();
|
xizi_enter_kernel();
|
||||||
panic("Unimplemented FIQ\n");
|
panic("Unimplemented FIQ\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
extern void context_switch(struct context**, struct context*);
|
|
||||||
void dabort_handler(struct trapframe* r)
|
|
||||||
{
|
|
||||||
if (xizi_is_in_kernel()) {
|
|
||||||
uint32_t dfs, dfa;
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 0" : "=r"(dfs)::);
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 0" : "=r"(dfa)::);
|
|
||||||
LOG("program counter: 0x%x caused\n", r->pc);
|
|
||||||
LOG("data abort at 0x%x, status 0x%x\n", dfa, dfs);
|
|
||||||
_abort_reason(dfs);
|
|
||||||
dump_tf(r);
|
|
||||||
panic("data abort exception\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
xizi_enter_kernel();
|
|
||||||
|
|
||||||
uint32_t dfs, dfa;
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 0" : "=r"(dfs)::);
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 0" : "=r"(dfa)::);
|
|
||||||
|
|
||||||
ERROR("dabort in user space: %s\n", cur_cpu()->task->name);
|
|
||||||
LOG("program counter: 0x%x caused\n", r->pc);
|
|
||||||
LOG("data abort at 0x%x, status 0x%x\n", dfa, dfs);
|
|
||||||
_abort_reason(dfs);
|
|
||||||
dump_tf(r);
|
|
||||||
sys_exit(cur_cpu()->task);
|
|
||||||
context_switch(&cur_cpu()->task->main_thread.context, cur_cpu()->scheduler);
|
|
||||||
}
|
|
||||||
|
|
||||||
void iabort_handler(struct trapframe* r)
|
|
||||||
{
|
|
||||||
if (xizi_is_in_kernel()) {
|
|
||||||
uint32_t ifs, ifa;
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 1" : "=r"(ifs)::);
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 2" : "=r"(ifa)::);
|
|
||||||
LOG("program counter: 0x%x caused\n", r->pc);
|
|
||||||
LOG("prefetch abort at 0x%x, status 0x%x\n", ifa, ifs);
|
|
||||||
_abort_reason(ifs);
|
|
||||||
dump_tf(r);
|
|
||||||
panic("prefetch abort exception\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
xizi_enter_kernel();
|
|
||||||
|
|
||||||
uint32_t ifs, ifa;
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c5, c0, 1" : "=r"(ifs)::);
|
|
||||||
__asm__ __volatile__("mrc p15, 0, %0, c6, c0, 2" : "=r"(ifa)::);
|
|
||||||
|
|
||||||
ERROR("iabort in user space: %s\n", cur_cpu()->task->name);
|
|
||||||
LOG("program counter: 0x%x(%s) caused\n", r->pc, cur_cpu()->task);
|
|
||||||
LOG("prefetch abort at 0x%x, status 0x%x\n", ifa, ifs);
|
|
||||||
_abort_reason(ifs);
|
|
||||||
dump_tf(r);
|
|
||||||
sys_exit(cur_cpu()->task);
|
|
||||||
context_switch(&cur_cpu()->task->main_thread.context, cur_cpu()->scheduler);
|
|
||||||
}
|
|
|
@ -42,12 +42,13 @@ extern void trap_iabort(void);
|
||||||
extern void trap_dabort(void);
|
extern void trap_dabort(void);
|
||||||
extern void trap_irq_enter(void);
|
extern void trap_irq_enter(void);
|
||||||
extern void trap_undefined_instruction(void);
|
extern void trap_undefined_instruction(void);
|
||||||
|
extern void handle_reserved(void);
|
||||||
|
extern void handle_fiq(void);
|
||||||
|
|
||||||
static struct XiziTrapDriver xizi_trap_driver;
|
static struct XiziTrapDriver xizi_trap_driver;
|
||||||
|
|
||||||
void panic(char* s)
|
void panic(char* s)
|
||||||
{
|
{
|
||||||
xizi_trap_driver.cpu_irq_disable();
|
|
||||||
KPrintf("panic: %s\n", s);
|
KPrintf("panic: %s\n", s);
|
||||||
for (;;)
|
for (;;)
|
||||||
;
|
;
|
||||||
|
@ -55,7 +56,6 @@ void panic(char* s)
|
||||||
|
|
||||||
/* stack for different mode*/
|
/* stack for different mode*/
|
||||||
static char mode_stack_pages[NR_CPU][NR_MODE_STACKS][MODE_STACK_SIZE];
|
static char mode_stack_pages[NR_CPU][NR_MODE_STACKS][MODE_STACK_SIZE];
|
||||||
|
|
||||||
extern uint32_t _vector_jumper;
|
extern uint32_t _vector_jumper;
|
||||||
extern uint32_t _vector_start;
|
extern uint32_t _vector_start;
|
||||||
extern uint32_t _vector_end;
|
extern uint32_t _vector_end;
|
||||||
|
@ -72,19 +72,6 @@ void init_cpu_mode_stacks(int cpu_id)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void handle_reserved(void)
|
|
||||||
{
|
|
||||||
// unimplemented trap handler
|
|
||||||
LOG("Unimplemented Reserved\n");
|
|
||||||
panic("");
|
|
||||||
}
|
|
||||||
|
|
||||||
void handle_fiq(void)
|
|
||||||
{
|
|
||||||
LOG("Unimplemented FIQ\n");
|
|
||||||
panic("");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void _sys_irq_init(int cpu_id)
|
static void _sys_irq_init(int cpu_id)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -101,18 +88,18 @@ static void _sys_irq_init(int cpu_id)
|
||||||
vector_base[5] = (uint32_t)handle_reserved; // Reserved
|
vector_base[5] = (uint32_t)handle_reserved; // Reserved
|
||||||
vector_base[6] = (uint32_t)trap_irq_enter; // IRQ
|
vector_base[6] = (uint32_t)trap_irq_enter; // IRQ
|
||||||
vector_base[7] = (uint32_t)handle_fiq; // FIQ
|
vector_base[7] = (uint32_t)handle_fiq; // FIQ
|
||||||
}
|
|
||||||
|
|
||||||
/* active hardware irq responser */
|
/* active hardware irq responser */
|
||||||
XScuGic_Config* gic_config = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
|
XScuGic_Config* gic_config = XScuGic_LookupConfig(XPAR_PS7_SCUGIC_0_DEVICE_ID);
|
||||||
if (NULL == gic_config) {
|
if (NULL == gic_config) {
|
||||||
ERROR("Error while looking up gic config\n");
|
ERROR("Error while looking up gic config\n");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
int gic_init_status = XScuGic_CfgInitialize(&IntcInstance, gic_config, gic_config->CpuBaseAddress);
|
int gic_init_status = XScuGic_CfgInitialize(&IntcInstance, gic_config, gic_config->CpuBaseAddress);
|
||||||
if (gic_init_status != XST_SUCCESS) {
|
if (gic_init_status != XST_SUCCESS) {
|
||||||
ERROR("Error initializing gic\n");
|
ERROR("Error initializing gic\n");
|
||||||
return;
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
xizi_trap_driver.switch_hw_irqtbl((uint32_t*)&_vector_jumper);
|
xizi_trap_driver.switch_hw_irqtbl((uint32_t*)&_vector_jumper);
|
||||||
|
@ -226,6 +213,6 @@ static struct XiziTrapDriver xizi_trap_driver = {
|
||||||
struct XiziTrapDriver* hardkernel_intr_init(struct TraceTag* hardkernel_tag)
|
struct XiziTrapDriver* hardkernel_intr_init(struct TraceTag* hardkernel_tag)
|
||||||
{
|
{
|
||||||
xizi_trap_driver.sys_irq_init(0);
|
xizi_trap_driver.sys_irq_init(0);
|
||||||
xizi_trap_driver.cpu_irq_enable();
|
xizi_trap_driver.cpu_irq_disable();
|
||||||
return &xizi_trap_driver;
|
return &xizi_trap_driver;
|
||||||
}
|
}
|
|
@ -27,8 +27,8 @@
|
||||||
#include "multicores.h"
|
#include "multicores.h"
|
||||||
|
|
||||||
struct lock_node {
|
struct lock_node {
|
||||||
int cpu_id;
|
|
||||||
struct double_list_node node;
|
struct double_list_node node;
|
||||||
|
int cpu_id;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct double_list_node lock_request_guard;
|
static struct double_list_node lock_request_guard;
|
||||||
|
@ -63,15 +63,18 @@ void _spinlock_unlock(struct spinlock* lock);
|
||||||
|
|
||||||
void spinlock_lock(struct spinlock* lock)
|
void spinlock_lock(struct spinlock* lock)
|
||||||
{
|
{
|
||||||
if (lock->owner_cpu != SPINLOCK_STATE_UNLOCK && lock->owner_cpu == cur_cpuid()) {
|
int cur_cpu_id = cur_cpuid();
|
||||||
|
if (lock->owner_cpu != SPINLOCK_STATE_UNLOCK && lock->owner_cpu == cur_cpu_id) {
|
||||||
ERROR("spinlock %s lock double locked by core %d\n", lock->name, lock->owner_cpu);
|
ERROR("spinlock %s lock double locked by core %d\n", lock->name, lock->owner_cpu);
|
||||||
panic("");
|
panic("");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct double_list_node* p_lock_node = &core_lock_request[cur_cpu_id].node;
|
||||||
_spinlock_lock(&request_lock, SPINLOCK_LOCK_WAITFOREVER);
|
_spinlock_lock(&request_lock, SPINLOCK_LOCK_WAITFOREVER);
|
||||||
doubleListAddOnBack(&core_lock_request[cur_cpuid()].node, &lock_request_guard);
|
doubleListAddOnBack(p_lock_node, &lock_request_guard);
|
||||||
_spinlock_unlock(&request_lock);
|
_spinlock_unlock(&request_lock);
|
||||||
|
|
||||||
while (lock_request_guard.next != &core_lock_request[cur_cpuid()].node)
|
while (lock_request_guard.next != p_lock_node)
|
||||||
;
|
;
|
||||||
|
|
||||||
_spinlock_lock(lock, SPINLOCK_LOCK_WAITFOREVER);
|
_spinlock_lock(lock, SPINLOCK_LOCK_WAITFOREVER);
|
||||||
|
@ -79,15 +82,38 @@ void spinlock_lock(struct spinlock* lock)
|
||||||
|
|
||||||
void spinlock_unlock(struct spinlock* lock)
|
void spinlock_unlock(struct spinlock* lock)
|
||||||
{
|
{
|
||||||
assert(lock_request_guard.next == &core_lock_request[cur_cpuid()].node);
|
struct double_list_node* p_lock_node = &core_lock_request[cur_cpuid()].node;
|
||||||
|
assert(lock_request_guard.next == p_lock_node);
|
||||||
_spinlock_lock(&request_lock, SPINLOCK_LOCK_WAITFOREVER);
|
_spinlock_lock(&request_lock, SPINLOCK_LOCK_WAITFOREVER);
|
||||||
_double_list_del(core_lock_request[cur_cpuid()].node.prev, core_lock_request[cur_cpuid()].node.next);
|
_double_list_del(p_lock_node->prev, p_lock_node->next);
|
||||||
_spinlock_unlock(&request_lock);
|
_spinlock_unlock(&request_lock);
|
||||||
|
|
||||||
_spinlock_unlock(lock);
|
_spinlock_unlock(lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool is_spinlock_locked(struct spinlock* lock)
|
bool spinlock_try_lock(struct spinlock* lock)
|
||||||
{
|
{
|
||||||
return lock->owner_cpu != SPINLOCK_STATE_UNLOCK;
|
int cur_cpu_id = cur_cpuid();
|
||||||
|
if (lock->owner_cpu != SPINLOCK_STATE_UNLOCK && lock->owner_cpu == cur_cpu_id) {
|
||||||
|
ERROR("spinlock %s lock double locked by core %d\n", lock->name, lock->owner_cpu);
|
||||||
|
panic("");
|
||||||
|
}
|
||||||
|
|
||||||
|
struct double_list_node* p_lock_node = &core_lock_request[cur_cpu_id].node;
|
||||||
|
_spinlock_lock(&request_lock, SPINLOCK_LOCK_WAITFOREVER);
|
||||||
|
doubleListAddOnBack(p_lock_node, &lock_request_guard);
|
||||||
|
if (lock_request_guard.next != p_lock_node) {
|
||||||
|
_double_list_del(p_lock_node->prev, p_lock_node->next);
|
||||||
|
_spinlock_unlock(&request_lock);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
_spinlock_unlock(&request_lock);
|
||||||
|
_spinlock_lock(lock, SPINLOCK_LOCK_WAITFOREVER);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool is_spinlock_hold_by_current_cpu(struct spinlock* lock)
|
||||||
|
{
|
||||||
|
return lock->owner_cpu;
|
||||||
}
|
}
|
|
@ -42,4 +42,5 @@ bool module_spinlock_use_intr_init(void);
|
||||||
void spinlock_init(struct spinlock* lock, char* name);
|
void spinlock_init(struct spinlock* lock, char* name);
|
||||||
void spinlock_lock(struct spinlock* lock);
|
void spinlock_lock(struct spinlock* lock);
|
||||||
void spinlock_unlock(struct spinlock* lock);
|
void spinlock_unlock(struct spinlock* lock);
|
||||||
bool is_spinlock_locked(struct spinlock* lock);
|
bool spinlock_try_lock(struct spinlock* lock);
|
||||||
|
bool is_spinlock_hold_by_current_cpu(struct spinlock* lock);
|
|
@ -100,4 +100,7 @@ void panic(char* s);
|
||||||
bool intr_distributer_init(struct IrqDispatcherRightGroup*);
|
bool intr_distributer_init(struct IrqDispatcherRightGroup*);
|
||||||
void intr_irq_dispatch(struct trapframe* tf);
|
void intr_irq_dispatch(struct trapframe* tf);
|
||||||
bool swi_distributer_init(struct SwiDispatcherRightGroup*);
|
bool swi_distributer_init(struct SwiDispatcherRightGroup*);
|
||||||
void software_irq_dispatch(struct trapframe* tf);
|
void software_irq_dispatch(struct trapframe* tf);
|
||||||
|
|
||||||
|
void dabort_reason(struct trapframe* r);
|
||||||
|
void iabort_reason(struct trapframe* r);
|
|
@ -23,7 +23,11 @@ INC_DIR = -I$(KERNEL_ROOT)/services/shell/letter-shell \
|
||||||
-I$(KERNEL_ROOT)/services/boards/$(BOARD) \
|
-I$(KERNEL_ROOT)/services/boards/$(BOARD) \
|
||||||
-I$(KERNEL_ROOT)/services/app
|
-I$(KERNEL_ROOT)/services/app
|
||||||
|
|
||||||
|
ifeq ($(BOARD), imx6q-sabrelite)
|
||||||
all: init test_fs simple_client simple_server shell fs_server test_priority test_irq_hdlr test_irq_send readme.txt | bin
|
all: init test_fs simple_client simple_server shell fs_server test_priority test_irq_hdlr test_irq_send readme.txt | bin
|
||||||
|
else
|
||||||
|
all: init test_fs simple_client simple_server shell fs_server test_priority test_irq_hdlr readme.txt | bin
|
||||||
|
endif
|
||||||
../tools/mkfs/mkfs ./fs.img $^
|
../tools/mkfs/mkfs ./fs.img $^
|
||||||
@mv $(filter-out readme.txt, $^) bin
|
@mv $(filter-out readme.txt, $^) bin
|
||||||
@mv *.o bin
|
@mv *.o bin
|
||||||
|
@ -32,9 +36,11 @@ all: init test_fs simple_client simple_server shell fs_server test_priority test
|
||||||
bin:
|
bin:
|
||||||
@mkdir -p bin
|
@mkdir -p bin
|
||||||
|
|
||||||
|
ifeq ($(BOARD), imx6q-sabrelite)
|
||||||
test_irq_send: test_irq_sender.o usyscall.o libserial.o
|
test_irq_send: test_irq_sender.o usyscall.o libserial.o
|
||||||
@${ld} ${user_ldflags} -e main -o $@ $^ ${board_specs}
|
@${ld} ${user_ldflags} -e main -o $@ $^ ${board_specs}
|
||||||
@${objdump} -S $@ > $@.asm
|
@${objdump} -S $@ > $@.asm
|
||||||
|
endif
|
||||||
|
|
||||||
test_irq_hdlr: test_irq_handler.o libserial.o libipc.o session.o usyscall.o libmem.o
|
test_irq_hdlr: test_irq_handler.o libserial.o libipc.o session.o usyscall.o libmem.o
|
||||||
@${ld} ${user_ldflags} -e main -o $@ $^ ${board_specs}
|
@${ld} ${user_ldflags} -e main -o $@ $^ ${board_specs}
|
||||||
|
|
|
@ -35,7 +35,7 @@ signed short userShellRead(char* data, unsigned short len)
|
||||||
while (length--) {
|
while (length--) {
|
||||||
cur_read = getc();
|
cur_read = getc();
|
||||||
if (cur_read == 0xff) {
|
if (cur_read == 0xff) {
|
||||||
yield();
|
yield(SYS_TASK_YIELD_NO_REASON);
|
||||||
}
|
}
|
||||||
// *data++ = getc();
|
// *data++ = getc();
|
||||||
*data++ = cur_read;
|
*data++ = cur_read;
|
||||||
|
|
|
@ -89,7 +89,7 @@ int main()
|
||||||
mmap(ARM_PERIPHERAL_VIRT_BASE, ARM_PERIPHERAL_BASE, 0x2000, true);
|
mmap(ARM_PERIPHERAL_VIRT_BASE, ARM_PERIPHERAL_BASE, 0x2000, true);
|
||||||
|
|
||||||
printf("%s: Sending soft interrupt\n", prog_name);
|
printf("%s: Sending soft interrupt\n", prog_name);
|
||||||
gic_send_sgi(SW_INTERRUPT_3, 0, kGicSgiFilter_OnlyThisCPU);
|
gic_send_sgi(SW_INTERRUPT_3, 0xF, kGicSgiFilter_UseTargetList);
|
||||||
printf("%s: Soft interrupt send done\n", prog_name);
|
printf("%s: Soft interrupt send done\n", prog_name);
|
||||||
exit();
|
exit();
|
||||||
}
|
}
|
|
@ -53,9 +53,9 @@ int exit()
|
||||||
return syscall(SYSCALL_EXIT, 0, 0, 0, 0);
|
return syscall(SYSCALL_EXIT, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int yield()
|
int yield(task_yield_reason reason)
|
||||||
{
|
{
|
||||||
return syscall(SYSCALL_YIELD, 0, 0, 0, 0);
|
return syscall(SYSCALL_YIELD, (uintptr_t)reason, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kill(int pid)
|
int kill(int pid)
|
||||||
|
|
|
@ -44,6 +44,12 @@ typedef enum {
|
||||||
SYS_STATE_SHOW_CPU_INFO,
|
SYS_STATE_SHOW_CPU_INFO,
|
||||||
} sys_state_option;
|
} sys_state_option;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SYS_TASK_YIELD_NO_REASON = 0x0,
|
||||||
|
SYS_TASK_YIELD_FOREVER = 0x1,
|
||||||
|
SYS_TASK_YIELD_BLOCK_IPC = 0x2,
|
||||||
|
} task_yield_reason;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uintptr_t memblock_start;
|
uintptr_t memblock_start;
|
||||||
|
@ -58,7 +64,7 @@ typedef int (*ipc_write_fn)(struct Session* session, int fd, char* src, int offs
|
||||||
|
|
||||||
int spawn(struct Session* session, int fd, ipc_read_fn ipc_read, ipc_fsize_fn ipc_fsize, char* name, char** argv);
|
int spawn(struct Session* session, int fd, ipc_read_fn ipc_read, ipc_fsize_fn ipc_fsize, char* name, char** argv);
|
||||||
int exit();
|
int exit();
|
||||||
int yield();
|
int yield(task_yield_reason reason);
|
||||||
int kill(int pid);
|
int kill(int pid);
|
||||||
int register_server(char* name);
|
int register_server(char* name);
|
||||||
int session(char* path, int capacity, struct Session* user_session);
|
int session(char* path, int capacity, struct Session* user_session);
|
||||||
|
|
|
@ -53,9 +53,9 @@ int exit()
|
||||||
return syscall(SYSCALL_EXIT, 0, 0, 0, 0);
|
return syscall(SYSCALL_EXIT, 0, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int yield()
|
int yield(task_yield_reason reason)
|
||||||
{
|
{
|
||||||
return syscall(SYSCALL_YIELD, 0, 0, 0, 0);
|
return syscall(SYSCALL_YIELD, (uintptr_t)reason, 0, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int kill(int pid)
|
int kill(int pid)
|
||||||
|
|
|
@ -44,6 +44,12 @@ typedef enum {
|
||||||
SYS_STATE_SHOW_CPU_INFO,
|
SYS_STATE_SHOW_CPU_INFO,
|
||||||
} sys_state_option;
|
} sys_state_option;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SYS_TASK_YIELD_NO_REASON = 0x0,
|
||||||
|
SYS_TASK_YIELD_FOREVER = 0x1,
|
||||||
|
SYS_TASK_YIELD_BLOCK_IPC = 0x2,
|
||||||
|
} task_yield_reason;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uintptr_t memblock_start;
|
uintptr_t memblock_start;
|
||||||
|
@ -58,7 +64,7 @@ typedef int (*ipc_write_fn)(struct Session* session, int fd, char* src, int offs
|
||||||
|
|
||||||
int spawn(struct Session* session, int fd, ipc_read_fn ipc_read, ipc_fsize_fn ipc_fsize, char* name, char** argv);
|
int spawn(struct Session* session, int fd, ipc_read_fn ipc_read, ipc_fsize_fn ipc_fsize, char* name, char** argv);
|
||||||
int exit();
|
int exit();
|
||||||
int yield();
|
int yield(task_yield_reason reason);
|
||||||
int kill(int pid);
|
int kill(int pid);
|
||||||
int register_server(char* name);
|
int register_server(char* name);
|
||||||
int session(char* path, int capacity, struct Session* user_session);
|
int session(char* path, int capacity, struct Session* user_session);
|
||||||
|
|
|
@ -121,7 +121,7 @@ void ipc_msg_send_wait(struct IpcMsg* msg)
|
||||||
msg->header.done = 0;
|
msg->header.done = 0;
|
||||||
while (msg->header.done == 0) {
|
while (msg->header.done == 0) {
|
||||||
/// @todo syscall yield with prio decrease
|
/// @todo syscall yield with prio decrease
|
||||||
yield();
|
yield(SYS_TASK_YIELD_BLOCK_IPC);
|
||||||
}
|
}
|
||||||
assert(msg->header.done == 1);
|
assert(msg->header.done == 1);
|
||||||
}
|
}
|
||||||
|
@ -138,7 +138,7 @@ int ipc_session_wait(struct Session* session)
|
||||||
struct IpcMsg* msg = IPCSESSION_MSG(session);
|
struct IpcMsg* msg = IPCSESSION_MSG(session);
|
||||||
while (msg->header.done == 0) {
|
while (msg->header.done == 0) {
|
||||||
/// @todo syscall yield with prio decrease
|
/// @todo syscall yield with prio decrease
|
||||||
yield();
|
yield(SYS_TASK_YIELD_BLOCK_IPC);
|
||||||
}
|
}
|
||||||
assert(msg->header.done == 1);
|
assert(msg->header.done == 1);
|
||||||
return msg->header.ret_val;
|
return msg->header.ret_val;
|
||||||
|
@ -169,7 +169,7 @@ void ipc_server_loop(struct IpcNode* ipc_node)
|
||||||
/* handle each session */
|
/* handle each session */
|
||||||
for (int i = 0; i < NR_MAX_SESSION; i++) {
|
for (int i = 0; i < NR_MAX_SESSION; i++) {
|
||||||
if (session_list[i].buf == NULL) {
|
if (session_list[i].buf == NULL) {
|
||||||
yield();
|
yield(SYS_TASK_YIELD_NO_REASON);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cur_sess_id = session_list[i].id;
|
cur_sess_id = session_list[i].id;
|
||||||
|
|
|
@ -50,5 +50,6 @@ static inline struct CPU* cur_cpu(void)
|
||||||
struct spinlock whole_kernel_lock;
|
struct spinlock whole_kernel_lock;
|
||||||
|
|
||||||
void xizi_enter_kernel();
|
void xizi_enter_kernel();
|
||||||
|
bool xizi_try_enter_kernel();
|
||||||
void xizi_leave_kernel();
|
void xizi_leave_kernel();
|
||||||
bool xizi_is_in_kernel();
|
bool xizi_is_in_kernel();
|
|
@ -64,6 +64,12 @@ typedef enum {
|
||||||
SYS_STATE_SHOW_CPU_INFO,
|
SYS_STATE_SHOW_CPU_INFO,
|
||||||
} sys_state_option;
|
} sys_state_option;
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
SYS_TASK_YIELD_NO_REASON = 0x0,
|
||||||
|
SYS_TASK_YIELD_FOREVER = 0x1,
|
||||||
|
SYS_TASK_YIELD_BLOCK_IPC = 0x2,
|
||||||
|
} task_yield_reason;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct {
|
struct {
|
||||||
uintptr_t memblock_start;
|
uintptr_t memblock_start;
|
||||||
|
@ -80,7 +86,7 @@ int syscall(int sys_num, uintptr_t param1, uintptr_t param2, uintptr_t param3, u
|
||||||
|
|
||||||
int sys_spawn(char* img_start, char* name, char** argv);
|
int sys_spawn(char* img_start, char* name, char** argv);
|
||||||
int sys_exit(struct TaskMicroDescriptor* ptask);
|
int sys_exit(struct TaskMicroDescriptor* ptask);
|
||||||
int sys_yield();
|
int sys_yield(task_yield_reason reason);
|
||||||
int sys_kill(int id);
|
int sys_kill(int id);
|
||||||
|
|
||||||
int sys_register_as_server(char* name);
|
int sys_register_as_server(char* name);
|
||||||
|
|
|
@ -34,6 +34,7 @@ Modification:
|
||||||
#include "actracer.h"
|
#include "actracer.h"
|
||||||
#include "assert.h"
|
#include "assert.h"
|
||||||
#include "ipc.h"
|
#include "ipc.h"
|
||||||
|
#include "kalloc.h"
|
||||||
#include "mmu_common.h"
|
#include "mmu_common.h"
|
||||||
#include "multicores.h"
|
#include "multicores.h"
|
||||||
#include "share_page.h"
|
#include "share_page.h"
|
||||||
|
@ -52,9 +53,10 @@ static void send_irq_to_user(int irq_num)
|
||||||
struct Session* session = &irq_forward_table[irq_num].session;
|
struct Session* session = &irq_forward_table[irq_num].session;
|
||||||
int len = IPC_ARG_INFO_BASE_OFFSET;
|
int len = IPC_ARG_INFO_BASE_OFFSET;
|
||||||
len += sizeof(struct IpcArgInfo);
|
len += sizeof(struct IpcArgInfo);
|
||||||
|
/* get message space and add session tail */
|
||||||
|
|
||||||
/* add session tail */
|
void* session_kern_vaddr = P2V(xizi_pager.address_translate(&kernel_irq_proxy->pgdir, (uintptr_t)session->buf));
|
||||||
struct IpcMsg* buf = session->buf + session->tail;
|
struct IpcMsg* buf = session_kern_vaddr + session->tail;
|
||||||
memset((void*)buf, 0, len);
|
memset((void*)buf, 0, len);
|
||||||
session->tail = (session->tail + len) % session->capacity;
|
session->tail = (session->tail + len) % session->capacity;
|
||||||
|
|
||||||
|
@ -73,20 +75,13 @@ static void send_irq_to_user(int irq_num)
|
||||||
|
|
||||||
int user_irq_handler(int irq, void* tf, void* arg)
|
int user_irq_handler(int irq, void* tf, void* arg)
|
||||||
{
|
{
|
||||||
static struct MmuCommonDone* p_mmu_driver = NULL;
|
|
||||||
if (p_mmu_driver == NULL) {
|
|
||||||
struct TraceTag mmu_driver_tag;
|
|
||||||
AchieveResourceTag(&mmu_driver_tag, RequireRootTag(), "/hardkernel/mmu-ac-resource");
|
|
||||||
p_mmu_driver = (struct MmuCommonDone*)AchieveResource(&mmu_driver_tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (irq_forward_table[irq].handle_task != NULL) {
|
if (irq_forward_table[irq].handle_task != NULL) {
|
||||||
p_mmu_driver->LoadPgdir((uintptr_t)V2P(kernel_irq_proxy->pgdir.pd_addr));
|
|
||||||
send_irq_to_user(irq);
|
send_irq_to_user(irq);
|
||||||
p_mmu_driver->LoadPgdir((uintptr_t)V2P(cur_cpu()->task->pgdir.pd_addr));
|
|
||||||
|
|
||||||
next_task_emergency = irq_forward_table[irq].handle_task;
|
next_task_emergency = irq_forward_table[irq].handle_task;
|
||||||
xizi_task_manager.task_yield_noschedule(cur_cpu()->task, false);
|
if (cur_cpu()->task != NULL) {
|
||||||
|
xizi_task_manager.task_yield_noschedule(cur_cpu()->task, false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -33,7 +33,7 @@ Modification:
|
||||||
|
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
||||||
int sys_yield()
|
int sys_yield(task_yield_reason reason)
|
||||||
{
|
{
|
||||||
xizi_task_manager.task_yield_noschedule(cur_cpu()->task, false);
|
xizi_task_manager.task_yield_noschedule(cur_cpu()->task, false);
|
||||||
return 0;
|
return 0;
|
||||||
|
|
|
@ -48,7 +48,7 @@ int syscall(int sys_num, uintptr_t param1, uintptr_t param2, uintptr_t param3, u
|
||||||
ret = sys_exit(cur_cpu()->task);
|
ret = sys_exit(cur_cpu()->task);
|
||||||
break;
|
break;
|
||||||
case SYSCALL_YIELD:
|
case SYSCALL_YIELD:
|
||||||
ret = sys_yield();
|
ret = sys_yield((task_yield_reason)param1);
|
||||||
break;
|
break;
|
||||||
case SYSCALL_SERVER:
|
case SYSCALL_SERVER:
|
||||||
ret = sys_register_as_server((char*)param1);
|
ret = sys_register_as_server((char*)param1);
|
||||||
|
|
|
@ -1,3 +1,3 @@
|
||||||
SRC_FILES := task.c scheduler.c
|
SRC_FILES := task.c schedule.c
|
||||||
|
|
||||||
include $(KERNEL_ROOT)/compiler.mk
|
include $(KERNEL_ROOT)/compiler.mk
|
||||||
|
|
|
@ -259,6 +259,7 @@ static void _scheduler(struct SchedulerRightGroup right_group)
|
||||||
cpu->task = next_task;
|
cpu->task = next_task;
|
||||||
p_mmu_driver->LoadPgdir((uintptr_t)V2P(next_task->pgdir.pd_addr));
|
p_mmu_driver->LoadPgdir((uintptr_t)V2P(next_task->pgdir.pd_addr));
|
||||||
context_switch(&cpu->scheduler, next_task->main_thread.context);
|
context_switch(&cpu->scheduler, next_task->main_thread.context);
|
||||||
|
assert(cur_cpu()->task == NULL);
|
||||||
assert(next_task->state != RUNNING);
|
assert(next_task->state != RUNNING);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
|
|
||||||
SRC_FILES := default_irq_handler.c \
|
SRC_FILES := default_irq_handler.c \
|
||||||
clock_irq_handler.c \
|
clock_irq_handler.c \
|
||||||
software_irq_handler.c
|
software_irq_handler.c \
|
||||||
|
abort_handler.c
|
||||||
|
|
||||||
include $(KERNEL_ROOT)/compiler.mk
|
include $(KERNEL_ROOT)/compiler.mk
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
/* Copyright (c) 2006-2018 Frans Kaashoek, Robert Morris, Russ Cox,
|
||||||
|
* Massachusetts Institute of Technology
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
* a copy of this software and associated documentation files (the
|
||||||
|
* "Software"), to deal in the Software without restriction, including
|
||||||
|
* without limitation the rights to use, copy, modify, merge, publish,
|
||||||
|
* distribute, sublicense, and/or sell copies of the Software, and to
|
||||||
|
* permit persons to whom the Software is furnished to do so, subject to
|
||||||
|
* the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be
|
||||||
|
* included in all copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||||
|
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||||
|
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||||
|
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||||
|
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
*/
|
||||||
|
/**
|
||||||
|
* @file abort_handler.c
|
||||||
|
* @brief handle program abort
|
||||||
|
* @version 3.0
|
||||||
|
* @author AIIT XUOS Lab
|
||||||
|
* @date 2023.11.23
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*************************************************
|
||||||
|
File name: abort_handler.c
|
||||||
|
Description: handle program abort
|
||||||
|
Others:
|
||||||
|
History:
|
||||||
|
1. Date: 2023-11-23
|
||||||
|
Author: AIIT XUOS Lab
|
||||||
|
Modification:
|
||||||
|
1. Modify iabort and dabort handler(in dabort_handler() and iabort_handler())
|
||||||
|
*************************************************/
|
||||||
|
#include "core.h"
|
||||||
|
#include "memlayout.h"
|
||||||
|
#include "spinlock.h"
|
||||||
|
#include "trap_common.h"
|
||||||
|
|
||||||
|
#include "assert.h"
|
||||||
|
#include "multicores.h"
|
||||||
|
#include "syscall.h"
|
||||||
|
#include "task.h"
|
||||||
|
|
||||||
|
extern void context_switch(struct context**, struct context*);
|
||||||
|
void dabort_handler(struct trapframe* r)
|
||||||
|
{
|
||||||
|
if (r->pc >= DEV_VRTMEM_BASE && is_spinlock_hold_by_current_cpu(&whole_kernel_lock)) {
|
||||||
|
assert(is_spinlock_hold_by_current_cpu(&whole_kernel_lock));
|
||||||
|
dabort_reason(r);
|
||||||
|
panic("data abort exception\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
xizi_enter_kernel();
|
||||||
|
|
||||||
|
struct TaskMicroDescriptor* cur_task = cur_cpu()->task;
|
||||||
|
ERROR("dabort in user space: %s\n", cur_task->name);
|
||||||
|
dabort_reason(r);
|
||||||
|
sys_exit(cur_task);
|
||||||
|
assert(cur_cpu()->task == NULL);
|
||||||
|
context_switch(&cur_task->main_thread.context, cur_cpu()->scheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
void iabort_handler(struct trapframe* r)
|
||||||
|
{
|
||||||
|
if (r->pc >= DEV_VRTMEM_BASE && is_spinlock_hold_by_current_cpu(&whole_kernel_lock)) {
|
||||||
|
assert(is_spinlock_hold_by_current_cpu(&whole_kernel_lock));
|
||||||
|
iabort_reason(r);
|
||||||
|
panic("kernel prefetch abort exception\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
xizi_enter_kernel();
|
||||||
|
|
||||||
|
struct TaskMicroDescriptor* cur_task = cur_cpu()->task;
|
||||||
|
ERROR("iabort in user space: %s\n", cur_task->name);
|
||||||
|
iabort_reason(r);
|
||||||
|
sys_exit(cur_task);
|
||||||
|
assert(cur_cpu()->task == NULL);
|
||||||
|
context_switch(&cur_task->main_thread.context, cur_cpu()->scheduler);
|
||||||
|
}
|
|
@ -66,9 +66,8 @@ void intr_irq_dispatch(struct trapframe* tf)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TaskMicroDescriptor* current_task = cur_cpu()->task;
|
struct TaskMicroDescriptor* current_task = cur_cpu()->task;
|
||||||
if (LIKELY(current_task != NULL)) {
|
assert(current_task != NULL);
|
||||||
current_task->main_thread.trapframe = tf;
|
current_task->main_thread.trapframe = tf;
|
||||||
}
|
|
||||||
|
|
||||||
unsigned cpu = p_intr_driver->hw_cur_int_cpu(int_info);
|
unsigned cpu = p_intr_driver->hw_cur_int_cpu(int_info);
|
||||||
unsigned irq = p_intr_driver->hw_cur_int_num(int_info);
|
unsigned irq = p_intr_driver->hw_cur_int_num(int_info);
|
||||||
|
@ -86,7 +85,7 @@ void intr_irq_dispatch(struct trapframe* tf)
|
||||||
p_intr_driver->curr_int[cpu] = 0;
|
p_intr_driver->curr_int[cpu] = 0;
|
||||||
p_intr_driver->hw_after_irq(int_info);
|
p_intr_driver->hw_after_irq(int_info);
|
||||||
|
|
||||||
if ((cur_cpu()->task == NULL && current_task != NULL) || current_task->state != RUNNING) {
|
if (cur_cpu()->task == NULL || current_task->state != RUNNING) {
|
||||||
cur_cpu()->task = NULL;
|
cur_cpu()->task = NULL;
|
||||||
context_switch(¤t_task->main_thread.context, cur_cpu()->scheduler);
|
context_switch(¤t_task->main_thread.context, cur_cpu()->scheduler);
|
||||||
}
|
}
|
||||||
|
@ -102,13 +101,18 @@ void xizi_enter_kernel()
|
||||||
spinlock_lock(&whole_kernel_lock);
|
spinlock_lock(&whole_kernel_lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool xizi_try_enter_kernel()
|
||||||
|
{
|
||||||
|
p_intr_driver->cpu_irq_disable();
|
||||||
|
if (spinlock_try_lock(&whole_kernel_lock)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
void xizi_leave_kernel()
|
void xizi_leave_kernel()
|
||||||
{
|
{
|
||||||
spinlock_unlock(&whole_kernel_lock);
|
spinlock_unlock(&whole_kernel_lock);
|
||||||
p_intr_driver->cpu_irq_enable();
|
p_intr_driver->cpu_irq_enable();
|
||||||
}
|
|
||||||
|
|
||||||
bool xizi_is_in_kernel()
|
|
||||||
{
|
|
||||||
return is_spinlock_locked(&whole_kernel_lock);
|
|
||||||
}
|
}
|
|
@ -71,10 +71,10 @@ void software_irq_dispatch(struct trapframe* tf)
|
||||||
cur_cpu()->task = NULL;
|
cur_cpu()->task = NULL;
|
||||||
context_switch(&cur_task->main_thread.context, cur_cpu()->scheduler);
|
context_switch(&cur_task->main_thread.context, cur_cpu()->scheduler);
|
||||||
}
|
}
|
||||||
assert(cur_task == cur_cpu()->task);
|
|
||||||
if (syscall_num == SYSCALL_EXIT) {
|
if (syscall_num == SYSCALL_EXIT) {
|
||||||
panic("Exit reaches");
|
panic("Exit reaches");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
assert(cur_task == cur_cpu()->task);
|
||||||
xizi_leave_kernel();
|
xizi_leave_kernel();
|
||||||
}
|
}
|
Loading…
Reference in New Issue