Skip to content

Commit

Permalink
migrate assembly files to avo
Browse files Browse the repository at this point in the history
Assembly is tricky, it should be generated.
This change adds an avo-based generator that handles the common parts and subtle
differences of our 386 vs amd64 implementations.

asm/ is its own module so that we don't add avo dependencies to vmw-guestinfo
module, and its downstream consumers.
  • Loading branch information
sigma authored and frapposelli committed Oct 6, 2021
1 parent 560fab2 commit 1edadea
Show file tree
Hide file tree
Showing 12 changed files with 368 additions and 98 deletions.
94 changes: 94 additions & 0 deletions asm/gen.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// Copyright 2020 Google, Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package asm

import (
"flag"
"go/types"
"log"
"os"

"github.com/mmcloughlin/avo/build"
"github.com/mmcloughlin/avo/gotypes"
"github.com/mmcloughlin/avo/ir"
)

// CLI utilities for ASM generator

// AvoContext is an extended context for Avo that conveys information about
// the target architecture. In particular this allows some level of
// abstraction over registers.
type AvoContext struct {
*build.Context
Arch string
}

// Missing instructions from avo.

// INL is the INL instruction.
func (ctx *AvoContext) INL() {
ctx.Instruction(&ir.Instruction{Opcode: "INL"})
}

// INSB is the INSB instruction.
func (ctx *AvoContext) INSB() {
ctx.Instruction(&ir.Instruction{Opcode: "INSB"})
}

// OUTSB is the OUTSB instruction.
func (ctx *AvoContext) OUTSB() {
ctx.Instruction(&ir.Instruction{Opcode: "OUTSB"})
}

// REP is the REP instruction.
func (ctx *AvoContext) REP() {
ctx.Instruction(&ir.Instruction{Opcode: "REP"})
}

// AvoMainFunc is the type of functions that can be used with GenAsm.
type AvoMainFunc func(ctx *AvoContext) error

// GenAsm is our main entry point for ASM code generation.
// It augments avo flags with a "-arch", the content of which is
// exposed in AvoContext. That value also drives the alignments in
// the generated code.
func GenAsm(f AvoMainFunc) {
var arch string

// reset the flagset to allow NewFlags to work.
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
var flags = build.NewFlags(flag.CommandLine)
flag.CommandLine.StringVar(&arch, "arch", "amd64", "arch to use")

flag.Parse()

// This is needed to get proper alignment of the return values.
// amd64 is the global default in avo, but not modifying it might
// result in misalignment of the stack.
gotypes.Sizes = types.SizesFor("gc", arch)

cfg := flags.Config()

ctx := &AvoContext{
Context: build.NewContext(),
Arch: arch,
}

if err := f(ctx); err != nil {
log.Fatal(err)
}

os.Exit(build.Main(cfg, ctx.Context))
}
5 changes: 5 additions & 0 deletions asm/go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module github.com/vmware/vmw-guestinfo/asm

go 1.12

require github.com/mmcloughlin/avo v0.0.0-20200201205037-fb157e1de836
12 changes: 12 additions & 0 deletions asm/go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
github.com/mmcloughlin/avo v0.0.0-20200201205037-fb157e1de836 h1:knhFIJojRvDc83PlhHV2u9hn1co3QEwZ6ZHwle3KtHs=
github.com/mmcloughlin/avo v0.0.0-20200201205037-fb157e1de836/go.mod h1:L0u9qfRMLNBO97u6pPukRp6ncoQz0Q25W69fvtht3vA=
golang.org/x/arch v0.0.0-20190909030613-46d78d1859ac/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e h1:nOOVVcLC+/3MeovP40q5lCiWmP1Z1DaN8yn8ngU63hw=
golang.org/x/tools v0.0.0-20190914235951-31e00f45c22e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
157 changes: 157 additions & 0 deletions bdoor/asm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
// Copyright 2016-2017 VMware, Inc. All Rights Reserved.
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build ignore

package main

import (
"fmt"
"strings"

. "github.com/mmcloughlin/avo/build"
"github.com/mmcloughlin/avo/reg"

"github.com/vmware/vmw-guestinfo/asm"
)

// Doc of the golang plan9 assembler
// http://p9.nyx.link/labs/sys/doc/asm.html
//
// A good primer of how to write golang with some plan9 flavored assembly
// http://www.doxsey.net/blog/go-and-assembly
//
// Some x86 references
// http://www.eecg.toronto.edu/~amza/www.mindsec.com/files/x86regs.html
// https://cseweb.ucsd.edu/classes/sp10/cse141/pdf/02/S01_x86_64.key.pdf
// https://en.wikibooks.org/wiki/X86_Assembly/Other_Instructions
//
// (This one is invaluable. Has a working example of how a standard function
// call looks on the stack with the associated assembly.)
// https://www.recurse.com/blog/7-understanding-c-by-learning-assembly
//
// Reference with raw form of the Opcode
// http://x86.renejeschke.de/html/file_module_x86_id_139.html
//
// Massive x86_64 reference
// http://ref.x86asm.net/coder64.html#xED

type config struct {
wordSize uint8
registers map[string]reg.Register
}

func setup(ctx *asm.AvoContext) (*config, error) {
switch ctx.Arch {
case "amd64":
return &config{
wordSize: 64,
registers: map[string]reg.Register{
"ax": reg.RAX,
"bx": reg.RBX,
"cx": reg.RCX,
"dx": reg.RDX,
"si": reg.RSI,
"di": reg.RDI,
"bp": reg.RBP,
},
}, nil
case "386":
return &config{
wordSize: 32,
registers: map[string]reg.Register{
"ax": reg.EAX,
"bx": reg.EBX,
"cx": reg.ECX,
"dx": reg.EDX,
"si": reg.ESI,
"di": reg.EDI,
"bp": reg.EBP,
},
}, nil
default:
return nil, fmt.Errorf("unsupported architecture: %s", ctx.Arch)
}
}

func code(ctx *asm.AvoContext) error {
cfg, err := setup(ctx)
if err != nil {
return err
}

// for all the following functions, we use those 7 registers as both
// input and output.
regs := []string{"ax", "bx", "cx", "dx", "si", "di", "bp"}

funcs := []struct {
name string
body func()
}{
{"bdoor_inout", func() {
ctx.Comment("IN to DX from AX")
ctx.INL()
}},
{"bdoor_hbout", func() {
ctx.CLD()
ctx.REP()
ctx.OUTSB()
}},
{"bdoor_hbin", func() {
ctx.CLD()
ctx.REP()
ctx.INSB()
}},
{"bdoor_inout_test", func() {}},
}

for _, f := range funcs {
// generate function signature. By convention we use:
// - $REG as input
// - and ret$REG as output.
proto := fmt.Sprintf(
"func(%s uint%d) (%s uint%d)",
strings.Join(regs, ","),
cfg.wordSize,
"ret"+strings.Join(regs, ",ret"),
cfg.wordSize,
)

ctx.Function(f.name)
ctx.Attributes(NOSPLIT | WRAPPER)
ctx.SignatureExpr(proto)

// load all registers
for _, r := range regs {
ctx.Load(ctx.Param(r), cfg.registers[r])
}

// execute backdoor function
f.body()

// store all registers
for _, r := range regs {
ctx.Store(cfg.registers[r], ctx.Return("ret"+r))
}

ctx.RET()
}

return nil
}

func main() {
asm.GenAsm(code)
}
3 changes: 3 additions & 0 deletions bdoor/bdoor.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.

//go:generate go run asm.go -out bdoor_amd64.s -arch amd64
//go:generate go run asm.go -out bdoor_386.s -arch 386

package bdoor

const (
Expand Down
Loading

0 comments on commit 1edadea

Please sign in to comment.