github.com/icexin/eggos@v0.4.2-0.20220216025428-78b167e4f349/kernel/thread.go (about)

     1  package kernel
     2  
     3  import (
     4  	"unsafe"
     5  
     6  	"github.com/icexin/eggos/kernel/mm"
     7  	"github.com/icexin/eggos/kernel/sys"
     8  )
     9  
    10  const (
    11  	_NTHREDS = 20
    12  
    13  	_FLAGS_IF        = 0x200
    14  	_FLAGS_IOPL_USER = 0x3000
    15  
    16  	_RPL_USER = 3
    17  
    18  	_THREAD_STACK_SIZE         = 32 << 10
    19  	_THREAD_STACK_GUARD_OFFSET = 1 << 10
    20  
    21  	_CLONE_IDLE = 0x8000000000000000
    22  )
    23  
    24  const (
    25  	UNUSED = iota
    26  	INITING
    27  	SLEEPING
    28  	RUNNABLE
    29  	RUNNING
    30  	EXIT
    31  )
    32  
    33  const (
    34  	_TSS_ESP0 = 1
    35  	_TSS_SS0  = 2
    36  )
    37  
    38  var (
    39  	threads    [_NTHREDS]Thread
    40  	scheduler  *context
    41  	idleThread threadptr
    42  )
    43  
    44  //go:notinheap
    45  type context struct {
    46  	r15 uintptr
    47  	r14 uintptr
    48  	r13 uintptr
    49  	r12 uintptr
    50  	r11 uintptr
    51  	bx  uintptr
    52  	bp  uintptr
    53  	ip  uintptr
    54  }
    55  
    56  // position of threadTLS and fpstate must be synced with trap.s and syscall.s
    57  type Thread struct {
    58  	// store thread tls, the pointer to Thread
    59  	threadTLS [4]uintptr
    60  
    61  	// the state of fpu
    62  	fpstate uintptr
    63  
    64  	kstack uintptr
    65  	stack  uintptr
    66  	tf     *trapFrame
    67  
    68  	context *context
    69  	id      int
    70  	state   int
    71  	counter int64
    72  
    73  	// sysmon 会调用usleep,进而调用sleepon,如果sleepKey是个指针会触发gcWriteBarrier
    74  	// 而sysmon没有P,会导致空指针
    75  	sleepKey uintptr
    76  	// for sleep timeout
    77  	timerKey uintptr
    78  
    79  	// store goroutine tls
    80  	fsBase uintptr
    81  
    82  	// 用于保存需要转发的系统调用栈帧
    83  	systf trapFrame
    84  }
    85  
    86  //go:nosplit
    87  func allocThread() *Thread {
    88  	var t *Thread
    89  	for i := 0; i < _NTHREDS; i++ {
    90  		tt := &threads[i]
    91  		if tt.state == UNUSED {
    92  			t = tt
    93  			t.id = i
    94  			break
    95  		}
    96  	}
    97  	if t == nil {
    98  		throw("no thread slot available")
    99  	}
   100  
   101  	t.state = INITING
   102  	t.kstack = allocThreadStack()
   103  	t.fpstate = mm.Alloc()
   104  	t.threadTLS[0] = uintptr(unsafe.Pointer(t))
   105  	return t
   106  }
   107  
   108  //go:nosplit
   109  func allocThreadStack() uintptr {
   110  	stack := mm.Mmap(0, _THREAD_STACK_SIZE)
   111  	stack += _THREAD_STACK_SIZE - _THREAD_STACK_GUARD_OFFSET
   112  	return stack
   113  }
   114  
   115  type threadptr uintptr
   116  
   117  //go:nosplit
   118  func (t threadptr) ptr() *Thread {
   119  	return (*Thread)(unsafe.Pointer(t))
   120  }
   121  
   122  //go:nosplit
   123  func setFS(addr uintptr) {
   124  	wrmsr(_MSR_FS_BASE, addr)
   125  }
   126  
   127  //go:nosplit
   128  func setGS(addr uintptr) {
   129  	wrmsr(_MSR_GS_BASE, addr)
   130  }
   131  
   132  //go:nosplit
   133  func Mythread() *Thread
   134  
   135  //go:nosplit
   136  func setMythread(t *Thread) {
   137  	switchThreadContext(t)
   138  }
   139  
   140  //go:nosplit
   141  func switchThreadContext(t *Thread) {
   142  	// set go tls base address
   143  	if t.fsBase != 0 {
   144  		setFS(t.fsBase)
   145  	}
   146  	// set current thread base address
   147  	setGS(uintptr(unsafe.Pointer(&t.threadTLS)))
   148  
   149  	// use current thread esp0 in tss
   150  	setTssSP0(t.kstack)
   151  }
   152  
   153  //go:nosplit
   154  func thread0Init() {
   155  	t := allocThread()
   156  	t.stack = allocThreadStack()
   157  
   158  	sp := t.kstack
   159  
   160  	// for trap frame
   161  	sp -= unsafe.Sizeof(trapFrame{})
   162  	tf := (*trapFrame)(unsafe.Pointer(sp))
   163  
   164  	// Because trapret restore fpstate
   165  	// we need a valid fpstate here
   166  	sys.Fxsave(t.fpstate)
   167  	tf.SS = _UDATA_IDX<<3 | _RPL_USER
   168  	tf.SP = t.stack
   169  	// enable interrupt and io port
   170  	// TODO: enable interrupt
   171  	tf.FLAGS = _FLAGS_IF | _FLAGS_IOPL_USER
   172  	// tf.FLAGS = _FLAGS_IF
   173  	tf.CS = _UCODE_IDX<<3 | _RPL_USER
   174  	tf.IP = sys.FuncPC(thread0)
   175  	t.tf = tf
   176  
   177  	// for context
   178  	sp -= unsafe.Sizeof(*t.context)
   179  	ctx := (*context)(unsafe.Pointer(sp))
   180  	ctx.ip = sys.FuncPC(trapret)
   181  	t.context = ctx
   182  
   183  	t.state = RUNNABLE
   184  }
   185  
   186  //go:nosplit
   187  func ksysClone(pc, stack, flags uintptr) uintptr
   188  
   189  //go:nosplit
   190  func ksysYield()
   191  
   192  // thread0 is the first thread
   193  //go:nosplit
   194  func thread0() {
   195  	// jump to go rt0
   196  	go_entry()
   197  	panic("main return")
   198  }
   199  
   200  // run when after main init
   201  func idleInit() {
   202  	// thread0 clone idle thread
   203  	stack := mm.SysMmap(0, _THREAD_STACK_SIZE) +
   204  		_THREAD_STACK_SIZE - _THREAD_STACK_GUARD_OFFSET
   205  
   206  	tid := ksysClone(sys.FuncPC(idle), stack, _CLONE_IDLE)
   207  	idleThread = (threadptr)(unsafe.Pointer(&threads[tid]))
   208  }
   209  
   210  //go:nosplit
   211  func idle() {
   212  	for {
   213  		if sys.CS() != 8 {
   214  			throw("bad cs in idle thread")
   215  		}
   216  		sys.Hlt()
   217  		ksysYield()
   218  	}
   219  }
   220  
   221  //go:nosplit
   222  func clone(pc, usp, flags, tls uintptr) int {
   223  	my := Mythread()
   224  	chld := allocThread()
   225  
   226  	sp := chld.kstack
   227  	// for trap frame
   228  	sp -= unsafe.Sizeof(trapFrame{})
   229  	tf := (*trapFrame)(unsafe.Pointer(sp))
   230  	*tf = *my.tf
   231  
   232  	// copy fpstate
   233  	fpsrc := (*[512]byte)(unsafe.Pointer(my.fpstate))
   234  	fpdst := (*[512]byte)(unsafe.Pointer(chld.fpstate))
   235  	*fpdst = *fpsrc
   236  
   237  	tf.SP = usp
   238  	tf.IP = pc
   239  	tf.AX = 0
   240  	// idle thread running on ring0 which rely on HLT ins
   241  	if flags&_CLONE_IDLE != 0 {
   242  		tf.CS = _KCODE_IDX << 3
   243  		tf.SS = _KDATA_IDX << 3
   244  	}
   245  
   246  	// for context
   247  	sp -= unsafe.Sizeof(context{})
   248  	ctx := (*context)(unsafe.Pointer(sp))
   249  	ctx.ip = sys.FuncPC(trapret)
   250  
   251  	chld.context = ctx
   252  	// *(*uintptr)(unsafe.Pointer(&chld.context)) = sp
   253  	chld.tf = tf
   254  	chld.stack = usp
   255  	chld.fsBase = tls
   256  	chld.state = RUNNABLE
   257  	return chld.id
   258  }
   259  
   260  //go:nosplit
   261  func exit() {
   262  	t := Mythread()
   263  	t.state = EXIT
   264  	Yield()
   265  	// TODO: handle thread exit in scheduler
   266  }
   267  
   268  //go:nosplit
   269  func threadInit() {
   270  	thread0Init()
   271  }
   272  
   273  //go:nosplit
   274  func swtch(old **context, _new *context)
   275  
   276  //go:nosplit
   277  func schedule() {
   278  	var t *Thread
   279  	var idx int
   280  	for {
   281  		t = pickup(&idx)
   282  		switchto(t)
   283  	}
   284  }
   285  
   286  // pickup selects the next runnable thread
   287  //go:nosplit
   288  func pickup(pidx *int) *Thread {
   289  	curr := *pidx
   290  	if traptask != 0 && traptask.ptr().state == RUNNABLE {
   291  		return traptask.ptr()
   292  	}
   293  	if syscalltask != 0 && syscalltask.ptr().state == RUNNABLE {
   294  		return syscalltask.ptr()
   295  	}
   296  
   297  	var t *Thread
   298  	for i := 0; i < _NTHREDS; i++ {
   299  		idx := (curr + i + 1) % _NTHREDS
   300  		*pidx = idx
   301  		tt := &threads[idx]
   302  		if tt.state == RUNNABLE && tt != idleThread.ptr() {
   303  			t = tt
   304  			break
   305  		}
   306  	}
   307  	if t == nil {
   308  		t = idleThread.ptr()
   309  	}
   310  	if t == nil {
   311  		throw("no runnable thread")
   312  	}
   313  	return t
   314  }
   315  
   316  // switchto switch thread context from scheduler to t
   317  //go:nosplit
   318  func switchto(t *Thread) {
   319  	begin := nanosecond()
   320  	// assert that interrupt is enabled
   321  	// TODO: enable check
   322  	if t.tf != nil && t.tf.FLAGS&_FLAGS_IF == 0 {
   323  		throw("bad eflags")
   324  	}
   325  	setMythread(t)
   326  	t.state = RUNNING
   327  
   328  	if t == idleThread.ptr() && t.tf.CS != 8 {
   329  		throw("bad idle cs")
   330  
   331  	}
   332  	swtch(&scheduler, t.context)
   333  	used := nanosecond() - begin
   334  	t.counter += used
   335  }
   336  
   337  func ThreadStat(stat *[_NTHREDS]int64) {
   338  	for i := 0; i < _NTHREDS; i++ {
   339  		stat[i] = threads[i].counter
   340  	}
   341  }
   342  
   343  //go:nosplit
   344  func Sched() {
   345  	my := Mythread()
   346  	swtch(&my.context, scheduler)
   347  }
   348  
   349  //go:nosplit
   350  func Yield() {
   351  	my := Mythread()
   352  	my.state = RUNNABLE
   353  	swtch(&my.context, scheduler)
   354  }