Skip to content

Commit

Permalink
kernel: invoke sseInit and initFakeFS from rt0 to handle <autogenerat…
Browse files Browse the repository at this point in the history
…ed> function wrappers of Go 1.17

In Go 1.17, when invoking Go functions from asm, <autogenerated>
function wrappers are generated to handle the conversion from the
old Go ABI (abi0) which is stack based to the new internal
register-based Go ABI (abi1).

The problem is that the <autogenerated> function wrapper assumes
that we are already running in a fully initialized Go environment,
with support for SSE instructions and with a configured %fs segment
register to handle TLS of G.

For context, see the autogenerated function wrapper which is invoked
when calling the Go kernel.preinit function from the asm rt0 function.

	Dump of assembler code for function github.com/icexin/eggos/kernel.preinit<autogenerated>:
		0x00000000002bbe40 <+0>:	sub    $0x18,%rsp
		0x00000000002bbe44 <+4>:	mov    %rbp,0x10(%rsp)
		0x00000000002bbe49 <+9>:	lea    0x10(%rsp),%rbp
		0x00000000002bbe4e <+14>:	mov    0x20(%rsp),%rax
		0x00000000002bbe53 <+19>:	mov    0x28(%rsp),%rbx
	=> 0x00000000002bbe58 <+24>:	xorps  %xmm15,%xmm15
		0x00000000002bbe5c <+28>:	mov    %fs:0xfffffffffffffff8,%r14
		0x00000000002bbe65 <+37>:	call   0x2b6c40 <github.com/icexin/eggos/kernel.preinit>
		0x00000000002bbe6a <+42>:	mov    0x10(%rsp),%rbp
		0x00000000002bbe6f <+47>:	add    $0x18,%rsp
		0x00000000002bbe73 <+51>:	ret

As visible in the disassembly of kernel.preinit<autogenerated>,
the XMM15 register is set to zero using XOR (see offset +24),
which requires SSE instructions to be initialized.

Also, on offset +28, the R14 register is moved into TLS by
using the %fs segment register. R14 store G in Go 1.17.

To handle this case, we need to initialize %fs to a valid memory
location; we use a fake memory location, as no Go routines are
running at this point, and later on %fs will be set when the first
goroutine is started by thread0Init.

Note, this is just a preliminary work to better understand what
we must support to handle Go 1.17. We are most likely _not_ going
to use this code for the final commit to eggos. This is just to
get more intuition into the problem domain.

Updates icexin#100.
  • Loading branch information
mewmew committed Feb 12, 2022
1 parent 4892e84 commit 8cdbd4f
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
35 changes: 35 additions & 0 deletions kernel/asm_amd64.s
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@ TEXT ·rt0(SB), NOSPLIT, $0-0
SUBQ $0x10, SP
MOVQ DI, 0(SP)
MOVQ SI, 8(SP)

// NOTE: the SSE instruction set must be initialized prior to invoking the
// first Go function, since any transition from asm -> Go will be handled by
// an autogenerated Go function wrapper for the given function (e.g.
// kernel.preinit<autogenerated>). The autogenerated function invokes
// `xorps %xmm15, %xmm15` which crashes the machine unless the FPU has been
// initialized.
CALL ·sseInit(SB)

// NOTE: the fs segment register must be set prior to invoking the first Go
// function, since any transition from asm -> Go will be handled by an
// autogenerated Go function wrapper for the given function (e.g.
// kernel.preinit<autogenerated>). The autogenerated function invokes
// `%fs:0xfffffffffffffff8,%r14` which crashes the machine unless the fs
// segment register has been set.
CALL ·initFakeFS(SB)

CALL ·preinit(SB)
INT $3

Expand All @@ -27,6 +44,24 @@ TEXT ·go_entry(SB), NOSPLIT, $0
ADDQ $8, SP
JMP _rt0_amd64_linux(SB)

// initFakeFS initializes a valid fake %fs segment register as required by the
// <autogenerated> function wrappers of the Go 1.17 calling convention
// (see icexin/eggos#100) when invoked before kernel.thread0Init.
TEXT ·initFakeFS(SB), NOSPLIT, $0
// HACK: just use a memory pointer on the stack that we are unlikely to
// overwrite untill we run thread0Init, at which point, %fs will be set to
// its real value.
SUBQ $100000, SP // TODO: figure out a better way to handle %fs, use a memory address that will never be used before by other instructions before thread0Init.

MOVL $0xc0000100, CX // _MSR_FS_BASE
MOVL SP, AX // low, just to get a valid address to.
//ADDL $0x3, AX // low, set lower 2 bits to prevent EIO error
MOVL $0x0, DX // high
WRMSR

ADDQ $100000, SP
RET

// sseInit initializes the SSE instruction set.
TEXT ·sseInit(SB), NOSPLIT, $0
MOVL CR0, AX
Expand Down
2 changes: 1 addition & 1 deletion kernel/simd.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func cpuid(fn, cx uint32) (eax, ebx, ecx, edx uint32)

//go:nosplit
func simdInit() {
sseInit()
//sseInit() // NOTE: invoked from asm (instead of Go) to support <autogenerated> function wrappers introduced in Go 1.17.

// init for avx
// first check avx function
Expand Down

0 comments on commit 8cdbd4f

Please sign in to comment.