new development tools
This commit is contained in:
@ -29,6 +29,7 @@ import (
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -43,6 +44,7 @@ type kernconfig struct {
|
||||
}
|
||||
|
||||
type kernel struct {
|
||||
CodeFile string
|
||||
Systab string
|
||||
Config kernconfig
|
||||
Ramfiles map[string]string
|
||||
@ -89,9 +91,9 @@ func (bf *buildfile) UnmarshalJSON(s []byte) error {
|
||||
b.Projects = adjust(b.Projects)
|
||||
b.Libs = adjust(b.Libs)
|
||||
b.Cflags = adjust(b.Cflags)
|
||||
b.SourceFiles = adjust(b.SourceFiles)
|
||||
b.SourceFilesCmd = adjust(b.SourceFilesCmd)
|
||||
b.ObjectFiles = adjust(b.ObjectFiles)
|
||||
b.SourceFiles = b.SourceFiles
|
||||
b.SourceFilesCmd = b.SourceFilesCmd
|
||||
b.ObjectFiles = b.ObjectFiles
|
||||
b.Include = adjust(b.Include)
|
||||
b.Install = fromRoot(b.Install)
|
||||
for i, e := range b.Env {
|
||||
@ -138,6 +140,15 @@ func failOn(err error) {
|
||||
}
|
||||
}
|
||||
|
||||
func isValueInList(value string, list []string) bool {
|
||||
for _, v := range list {
|
||||
if v == value {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func adjust(s []string) []string {
|
||||
for i, v := range s {
|
||||
s[i] = fromRoot(v)
|
||||
@ -145,6 +156,18 @@ func adjust(s []string) []string {
|
||||
return s
|
||||
}
|
||||
|
||||
func buildEnv(b *build) func(string) string{
|
||||
return func(v string) string {
|
||||
search := v + "="
|
||||
for _, s := range b.Env {
|
||||
if strings.Index(s, search) == 0 {
|
||||
return strings.Replace(s, search, "", 1)
|
||||
}
|
||||
}
|
||||
return os.Getenv(v)
|
||||
}
|
||||
}
|
||||
|
||||
// return the given absolute path as an absolute path rooted at the jehanne tree.
|
||||
func fromRoot(p string) string {
|
||||
p = os.ExpandEnv(p)
|
||||
@ -179,6 +202,40 @@ func sh(cmd *exec.Cmd) {
|
||||
failOn(shell.Run())
|
||||
}
|
||||
|
||||
func mergeKernel(k *kernel, defaults *kernel) *kernel {
|
||||
if k == nil {
|
||||
return defaults
|
||||
}
|
||||
if defaults == nil {
|
||||
return k
|
||||
}
|
||||
|
||||
// The custom kernel Code will be added after the default from includes
|
||||
// so that it has a chance to change de default behaviour.
|
||||
k.Config.Code = append(defaults.Config.Code, k.Config.Code...)
|
||||
|
||||
k.Config.Dev = append(k.Config.Dev, defaults.Config.Dev...)
|
||||
k.Config.Ip = append(k.Config.Ip, defaults.Config.Ip...)
|
||||
k.Config.Link = append(k.Config.Link, defaults.Config.Link...)
|
||||
k.Config.Sd = append(k.Config.Sd, defaults.Config.Sd...)
|
||||
k.Config.Uart = append(k.Config.Uart, defaults.Config.Uart...)
|
||||
k.Config.VGA = append(k.Config.VGA, defaults.Config.VGA...)
|
||||
|
||||
if k.CodeFile == "" {
|
||||
k.CodeFile = defaults.CodeFile
|
||||
}
|
||||
if k.Systab == "" {
|
||||
k.Systab = defaults.Systab
|
||||
}
|
||||
for name, path := range defaults.Ramfiles {
|
||||
if _, ok := k.Ramfiles[name]; ok == false {
|
||||
k.Ramfiles[name] = path
|
||||
}
|
||||
}
|
||||
|
||||
return k
|
||||
}
|
||||
|
||||
func include(f string, b *build) {
|
||||
if b.jsons[f] {
|
||||
return
|
||||
@ -203,6 +260,7 @@ func include(f string, b *build) {
|
||||
b.SourceFilesCmd = append(b.SourceFilesCmd, build.SourceFilesCmd...)
|
||||
b.Program += build.Program
|
||||
b.Library += build.Library
|
||||
b.Kernel = mergeKernel(b.Kernel, build.Kernel)
|
||||
if build.Install != "" {
|
||||
if b.Install != "" {
|
||||
log.Fatalf("In file %s (target %s) included by %s (target %s): redefined Install.", f, n, build.path, build.name)
|
||||
@ -244,7 +302,16 @@ func process(f string, r []*regexp.Regexp) []build {
|
||||
d, err := ioutil.ReadFile(f)
|
||||
failOn(err)
|
||||
failOn(json.Unmarshal(d, &builds))
|
||||
for n, build := range builds {
|
||||
|
||||
// Sort keys alphabetically (GoLang does not preserve the JSON order)
|
||||
var keys []string
|
||||
for n := range builds {
|
||||
keys = append(keys, n)
|
||||
}
|
||||
sort.Strings(keys)
|
||||
|
||||
for _, n := range keys {
|
||||
build := builds[n]
|
||||
build.name = n
|
||||
build.jsons = make(map[string]bool)
|
||||
skip := true
|
||||
@ -281,8 +348,15 @@ func buildkernel(b *build) {
|
||||
if b.Kernel == nil {
|
||||
return
|
||||
}
|
||||
envFunc := buildEnv(b)
|
||||
for name, path := range b.Kernel.Ramfiles {
|
||||
b.Kernel.Ramfiles[name] = os.Expand(path, envFunc);
|
||||
}
|
||||
codebuf := confcode(b.path, b.Kernel)
|
||||
failOn(ioutil.WriteFile(b.name+".c", codebuf, 0666))
|
||||
if b.Kernel.CodeFile == "" {
|
||||
log.Fatalf("Missing Kernel.CodeFile in %v\n", b.path)
|
||||
}
|
||||
failOn(ioutil.WriteFile(b.Kernel.CodeFile, codebuf, 0666))
|
||||
}
|
||||
|
||||
func wrapInQuote(args []string) []string {
|
||||
@ -297,24 +371,57 @@ func wrapInQuote(args []string) []string {
|
||||
return res
|
||||
}
|
||||
|
||||
func convertLibPathsToArgs(b *build) []string {
|
||||
libLocations := make([]string, 0)
|
||||
args := make([]string, 0)
|
||||
defaultLibLocation := fromRoot("/arch/$ARCH/lib")
|
||||
for _, lib := range b.Libs {
|
||||
ldir := filepath.Dir(lib)
|
||||
if ldir != defaultLibLocation {
|
||||
if !isValueInList(ldir, libLocations) {
|
||||
libLocations = append(libLocations, ldir)
|
||||
args = append(args, "-L", ldir)
|
||||
}
|
||||
}
|
||||
lib = strings.Replace(lib, ldir + "/lib", "-l", 1)
|
||||
lib = strings.Replace(lib, ".a", "", 1)
|
||||
args = append(args, lib)
|
||||
}
|
||||
return args
|
||||
}
|
||||
|
||||
func compile(b *build) {
|
||||
log.Printf("Building %s\n", b.name)
|
||||
// N.B. Plan 9 has a very well defined include structure, just three things:
|
||||
// /amd64/include, /sys/include, .
|
||||
args := b.Cflags
|
||||
args := b.SourceFiles
|
||||
args = append(args, b.Cflags...)
|
||||
if !isValueInList("-c", b.Cflags) {
|
||||
args = append(args, convertLibPathsToArgs(b)...)
|
||||
args = append(args, b.Oflags...)
|
||||
}
|
||||
if len(b.SourceFilesCmd) > 0 {
|
||||
for _, i := range b.SourceFilesCmd {
|
||||
cmd := exec.Command(tools["cc"], append(args, i)...)
|
||||
largs := make([]string, 3)
|
||||
largs[0] = i
|
||||
largs[1] = "-o"
|
||||
largs[2] = strings.Replace(filepath.Base(i), filepath.Ext(i), "", 1)
|
||||
cmd := exec.Command(tools["cc"], append(largs, args...)...)
|
||||
run(b, *shellhack, cmd)
|
||||
}
|
||||
return
|
||||
}
|
||||
args = append(args, b.SourceFiles...)
|
||||
if !isValueInList("-c", b.Cflags) {
|
||||
args = append(args, "-o", b.Program)
|
||||
}
|
||||
cmd := exec.Command(tools["cc"], args...)
|
||||
run(b, *shellhack, cmd)
|
||||
}
|
||||
|
||||
func link(b *build) {
|
||||
if !isValueInList("-c", b.Cflags) {
|
||||
return
|
||||
}
|
||||
log.Printf("Linking %s\n", b.name)
|
||||
if len(b.SourceFilesCmd) > 0 {
|
||||
for _, n := range b.SourceFilesCmd {
|
||||
@ -329,7 +436,6 @@ func link(b *build) {
|
||||
o := f[:len(f)] + ".o"
|
||||
args := []string{"-o", n, o}
|
||||
args = append(args, b.Oflags...)
|
||||
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
|
||||
args = append(args, b.Libs...)
|
||||
run(b, *shellhack, exec.Command(tools["ld"], args...))
|
||||
}
|
||||
@ -338,7 +444,6 @@ func link(b *build) {
|
||||
args := []string{"-o", b.Program}
|
||||
args = append(args, b.ObjectFiles...)
|
||||
args = append(args, b.Oflags...)
|
||||
args = append(args, "-L", fromRoot("/arch/$ARCH/lib"))
|
||||
args = append(args, b.Libs...)
|
||||
run(b, *shellhack, exec.Command(tools["ld"], args...))
|
||||
}
|
||||
@ -412,12 +517,12 @@ func projects(b *build, r []*regexp.Regexp) {
|
||||
for _, v := range b.Projects {
|
||||
f, _ := findBuildfile(v)
|
||||
log.Printf("Doing %s\n", f)
|
||||
project(f, r)
|
||||
project(f, r, b)
|
||||
}
|
||||
}
|
||||
|
||||
// assumes we are in the wd of the project.
|
||||
func project(bf string, which []*regexp.Regexp) {
|
||||
func project(bf string, which []*regexp.Regexp, container *build) {
|
||||
cwd, err := os.Getwd()
|
||||
failOn(err)
|
||||
debug("Start new project cwd is %v", cwd)
|
||||
@ -430,11 +535,25 @@ func project(bf string, which []*regexp.Regexp) {
|
||||
debug("Processing %v: %d target", root, len(builds))
|
||||
for _, b := range builds {
|
||||
debug("Processing %v: %v", b.name, b)
|
||||
if container != nil {
|
||||
b.Env = append(container.Env, b.Env...)
|
||||
}
|
||||
projects(&b, regexpAll)
|
||||
for _, c := range b.Pre {
|
||||
// this is a hack: we just pass the command through as an exec.Cmd
|
||||
run(&b, true, exec.Command(c))
|
||||
}
|
||||
envFunc := buildEnv(&b);
|
||||
b.Program = os.Expand(b.Program, envFunc)
|
||||
for i, s := range b.SourceFiles {
|
||||
b.SourceFiles[i] = fromRoot(os.Expand(s, envFunc));
|
||||
}
|
||||
for i, s := range b.SourceFilesCmd {
|
||||
b.SourceFilesCmd[i] = fromRoot(os.Expand(s, envFunc));
|
||||
}
|
||||
for i, s := range b.ObjectFiles {
|
||||
b.ObjectFiles[i] = fromRoot(os.Expand(s, envFunc));
|
||||
}
|
||||
buildkernel(&b)
|
||||
if len(b.SourceFiles) > 0 || len(b.SourceFilesCmd) > 0 {
|
||||
compile(&b)
|
||||
@ -511,7 +630,7 @@ func main() {
|
||||
re = append(re, rx)
|
||||
}
|
||||
}
|
||||
project(bf, re)
|
||||
project(bf, re, nil)
|
||||
}
|
||||
|
||||
func findTools(toolprefix string) {
|
||||
@ -520,7 +639,10 @@ func findTools(toolprefix string) {
|
||||
if x := os.Getenv(strings.ToUpper(k)); x != "" {
|
||||
v = x
|
||||
}
|
||||
v, err = exec.LookPath(toolprefix + v)
|
||||
if v != "sh" {
|
||||
v = toolprefix + v;
|
||||
}
|
||||
v, err = exec.LookPath(v)
|
||||
failOn(err)
|
||||
tools[k] = v
|
||||
}
|
||||
|
@ -98,20 +98,6 @@ VGAcur* vgacur[] = {
|
||||
nil,
|
||||
};
|
||||
|
||||
Physseg physseg[8] = {
|
||||
{
|
||||
.attr = SG_SHARED,
|
||||
.name = "shared",
|
||||
.size = SEGMAXPG,
|
||||
},
|
||||
{
|
||||
.attr = SG_BSS,
|
||||
.name = "memory",
|
||||
.size = SEGMAXPG,
|
||||
},
|
||||
};
|
||||
int nphysseg = 8;
|
||||
|
||||
{{ range .Config.Code }}{{ . }}
|
||||
{{ end }}
|
||||
|
||||
|
197
src/jehanne/cmd/fetch/fetch.go
Normal file
197
src/jehanne/cmd/fetch/fetch.go
Normal file
@ -0,0 +1,197 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"bytes"
|
||||
"compress/bzip2"
|
||||
"compress/gzip"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"hash"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
dirPermissions = 0755
|
||||
)
|
||||
|
||||
type Fetch struct {
|
||||
Upstream string
|
||||
Digest map[string]string
|
||||
Compress string
|
||||
RemovePrefix bool
|
||||
Exclude []string
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
j, err := ioutil.ReadFile("fetch.json")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
fetches := make(map[string]Fetch)
|
||||
if err := json.Unmarshal(j, &fetches); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
for name, f := range(fetches) {
|
||||
if _, err := os.Stat(name); err == nil {
|
||||
log.Printf("Fetch: skip %v (already present)", name)
|
||||
} else {
|
||||
log.Printf("Fetch: %v from %v", name, f.Upstream)
|
||||
if err := do(&f, name); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func do(f *Fetch, name string) error {
|
||||
fname := fetch(f)
|
||||
s, err := os.Open(fname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer os.Remove(fname)
|
||||
|
||||
os.MkdirAll(name, dirPermissions)
|
||||
|
||||
var unZ io.Reader
|
||||
switch f.Compress {
|
||||
case "gzip":
|
||||
unZ, err = gzip.NewReader(s)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case "bzip2":
|
||||
unZ = bzip2.NewReader(s)
|
||||
default:
|
||||
unZ = s
|
||||
}
|
||||
|
||||
ar := tar.NewReader(unZ)
|
||||
h, err := ar.Next()
|
||||
untar:
|
||||
for ; err == nil; h, err = ar.Next() {
|
||||
n := h.Name
|
||||
if f.RemovePrefix {
|
||||
n = strings.SplitN(n, "/", 2)[1]
|
||||
}
|
||||
for _, ex := range f.Exclude {
|
||||
if strings.HasPrefix(n, ex) {
|
||||
continue untar
|
||||
}
|
||||
}
|
||||
n = path.Join(name, n)
|
||||
if h.FileInfo().IsDir() {
|
||||
os.MkdirAll(n, dirPermissions)
|
||||
continue
|
||||
}
|
||||
os.MkdirAll(path.Dir(n), dirPermissions)
|
||||
out, err := os.Create(n)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
continue
|
||||
}
|
||||
|
||||
if n, err := io.Copy(out, ar); n != h.Size || err != nil {
|
||||
return err
|
||||
}
|
||||
out.Close()
|
||||
if err := os.Chmod(n, h.FileInfo().Mode()); err != nil {
|
||||
return err;
|
||||
}
|
||||
}
|
||||
if err != io.EOF {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type match struct {
|
||||
hash.Hash
|
||||
Good []byte
|
||||
Name string
|
||||
}
|
||||
|
||||
func (m match) OK() bool {
|
||||
return bytes.Equal(m.Good, m.Hash.Sum(nil))
|
||||
}
|
||||
|
||||
func fetch(v *Fetch) string {
|
||||
if len(v.Digest) == 0 {
|
||||
log.Fatal("no checksums specifed")
|
||||
}
|
||||
|
||||
f, err := ioutil.TempFile("", "cmdVendor")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
req, err := http.NewRequest("GET", v.Upstream, nil)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
client := &http.Client{
|
||||
Transport: &http.Transport{
|
||||
Proxy: http.ProxyFromEnvironment,
|
||||
DisableCompression: true,
|
||||
},
|
||||
}
|
||||
res, err := client.Do(req)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer res.Body.Close()
|
||||
|
||||
var digests []match
|
||||
for k, v := range v.Digest {
|
||||
g, err := hex.DecodeString(v)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
switch k {
|
||||
case "sha224":
|
||||
digests = append(digests, match{sha256.New224(), g, k})
|
||||
case "sha256":
|
||||
digests = append(digests, match{sha256.New(), g, k})
|
||||
case "sha384":
|
||||
digests = append(digests, match{sha512.New384(), g, k})
|
||||
case "sha512":
|
||||
digests = append(digests, match{sha512.New(), g, k})
|
||||
}
|
||||
}
|
||||
ws := make([]io.Writer, len(digests))
|
||||
for i := range digests {
|
||||
ws[i] = digests[i]
|
||||
}
|
||||
w := io.MultiWriter(ws...)
|
||||
|
||||
if _, err := io.Copy(f, io.TeeReader(res.Body, w)); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, h := range digests {
|
||||
if !h.OK() {
|
||||
log.Fatalf("mismatched %q hash\n\tWanted %x\n\tGot %x\n", h.Name, h.Good, h.Hash.Sum(nil))
|
||||
}
|
||||
}
|
||||
return f.Name()
|
||||
}
|
||||
|
||||
func run(exe string, arg ...string) error {
|
||||
cmd := exec.Command(exe, arg...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
return cmd.Run()
|
||||
}
|
374
src/jehanne/cmd/ksyscalls/ksyscalls.go
Normal file
374
src/jehanne/cmd/ksyscalls/ksyscalls.go
Normal file
@ -0,0 +1,374 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
type SyscallConf struct {
|
||||
Ret []string
|
||||
Args []string
|
||||
Name string
|
||||
Id uint32
|
||||
}
|
||||
|
||||
type Sysconf struct {
|
||||
Syscalls []SyscallConf
|
||||
}
|
||||
|
||||
func usage(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
fmt.Fprint(os.Stderr, "Usage: ksyscalls path/to/sysconf.json\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
type SyscallWrapper struct {
|
||||
Id uint32
|
||||
Name string
|
||||
Vars []string
|
||||
CommonCode string
|
||||
ExecCode string
|
||||
EntryPrint string
|
||||
ExitPrint string
|
||||
SysRetField string
|
||||
DefaultRet string
|
||||
}
|
||||
|
||||
type KernelCode struct {
|
||||
Externs []string
|
||||
Wrappers []SyscallWrapper
|
||||
}
|
||||
|
||||
func uregArg(index int) string{
|
||||
switch(index){
|
||||
case 0:
|
||||
return "di";
|
||||
case 1:
|
||||
return "si";
|
||||
case 2:
|
||||
return "dx";
|
||||
case 3:
|
||||
return "r10";
|
||||
case 4:
|
||||
return "r8";
|
||||
case 5:
|
||||
return "r9";
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func sysret(t string) string {
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return "i"
|
||||
case "long", "int64_t":
|
||||
return "vl"
|
||||
case "uintptr_t", "void":
|
||||
return "p"
|
||||
case "void*", "char*", "char**", "uint8_t*", "int32_t*", "uint64_t*", "int64_t*":
|
||||
return "v"
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func formatArg(i int, t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%d\", a%d);\n", i)
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#ux\", a%d);\n", i)
|
||||
case "long", "int64_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%lld\", a%d);\n", i)
|
||||
case "unsigned long", "uint64_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#lud\", a%d);\n", i)
|
||||
case "void*", "uint8_t*", "const void*", "const uint8_t*":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p\", a%d);\n", i)
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p(%%d)\", a%d, a%d);\n", i, i)
|
||||
case "const char*", "char*":
|
||||
return fmt.Sprintf("\tfmtuserstring(fmt, a%d);\n", i)
|
||||
case "const char**", "char**":
|
||||
return fmt.Sprintf("\tfmtuserstringlist(fmt, a%d);\n", i);
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func formatRet(t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%d\", ret->%s);\n", sysret(t))
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#ux\", ret->%s);\n", sysret(t))
|
||||
case "long", "int64_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%lld\", ret->%s);\n", sysret(t))
|
||||
case "unsigned long", "uint64_t", "void":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#llud\", ret->%s);\n", sysret(t))
|
||||
case "void*", "uintptr_t", "const void*", "const uintptr_t":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p\", ret->%s);\n", sysret(t))
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return fmt.Sprintf("\tfmtprint(fmt, \" %%#p(%%d)\", ret->%s, *ret->%s);\n", sysret(t), sysret(t))
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
}
|
||||
|
||||
func generateKernelCode(calls []SyscallConf){
|
||||
code := new(KernelCode)
|
||||
|
||||
for _, call := range(calls) {
|
||||
/* extern definitions */
|
||||
ext := "extern " + call.Ret[0] + " sys" + call.Name + "("
|
||||
if len(call.Args) == 0 {
|
||||
ext += "void";
|
||||
} else {
|
||||
for i, a := range(call.Args){
|
||||
if i > 0 {
|
||||
ext += ", "
|
||||
}
|
||||
ext += fmt.Sprintf("%s", a)
|
||||
}
|
||||
}
|
||||
ext += ");\n"
|
||||
|
||||
wcall := new(SyscallWrapper)
|
||||
wcall.Id = call.Id
|
||||
wcall.Name = call.Name
|
||||
wcall.SysRetField = sysret(call.Ret[0])
|
||||
wcall.DefaultRet = fmt.Sprintf("ret.%s = (%s)-1;", wcall.SysRetField, call.Ret[0])
|
||||
|
||||
for i, a := range(call.Args){
|
||||
wcall.Vars = append(wcall.Vars, fmt.Sprintf("%s a%v;", a, i))
|
||||
wcall.CommonCode += fmt.Sprintf("\ta%v = (%s)ureg->%s;\n", i, a, uregArg(i))
|
||||
}
|
||||
wcall.ExecCode += "\tret->" + wcall.SysRetField + " = sys" + call.Name + "("
|
||||
for i, _ := range(call.Args){
|
||||
if i > 0 {
|
||||
wcall.ExecCode += ", "
|
||||
}
|
||||
wcall.ExecCode += fmt.Sprintf("a%v", i)
|
||||
}
|
||||
wcall.ExecCode += ");"
|
||||
|
||||
if call.Name == "pwrite"{
|
||||
wcall.EntryPrint += formatArg(0, call.Args[0])
|
||||
wcall.EntryPrint += "\tfmtrwdata(fmt, (char*)a1, MIN(a2, 64));\n"
|
||||
wcall.EntryPrint += formatArg(2, call.Args[2])
|
||||
wcall.EntryPrint += formatArg(3, call.Args[3])
|
||||
} else {
|
||||
for i, a := range(call.Args){
|
||||
wcall.EntryPrint += formatArg(i, a)
|
||||
}
|
||||
}
|
||||
|
||||
wcall.ExitPrint += formatRet(call.Ret[0])
|
||||
if call.Name == "pread"{
|
||||
wcall.ExitPrint += fmt.Sprintf("\tfmtrwdata(fmt, (char*)ureg->%s, MIN(ureg->%s, 64));\n", uregArg(1), uregArg(2))
|
||||
}
|
||||
|
||||
code.Wrappers = append(code.Wrappers, *wcall)
|
||||
code.Externs = append(code.Externs, ext)
|
||||
}
|
||||
|
||||
tmpl, err := template.New("systab.c").Parse(`/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* automatically generated by ksyscalls */
|
||||
#include "u.h"
|
||||
#include "../port/lib.h"
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../port/error.h"
|
||||
#include "ureg.h"
|
||||
|
||||
extern void fmtrwdata(Fmt* f, char* a, int n);
|
||||
extern void fmtuserstring(Fmt* f, const char* a);
|
||||
extern void fmtuserstringlist(Fmt* f, const char** argv);
|
||||
|
||||
{{ range .Externs }}{{.}}{{ end }}
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
wrap_{{ .Name }}(ScRet* ret, Ureg* ureg)
|
||||
{
|
||||
{{ range .Vars }}{{.}}
|
||||
{{ end }}
|
||||
{{ .CommonCode }}
|
||||
{{ .ExecCode }}
|
||||
}
|
||||
{{ end }}
|
||||
int nsyscall = {{len .Wrappers}};
|
||||
|
||||
ScRet
|
||||
default_syscall_ret(int syscall)
|
||||
{
|
||||
static ScRet zero;
|
||||
ScRet ret = zero;
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
{{ .DefaultRet }}
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
ret.vl = -1;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
char*
|
||||
syscall_name(int syscall)
|
||||
{
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
return "{{ .Name }}";
|
||||
{{ end }}
|
||||
default:
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
dispatch_syscall(int syscall, Ureg* ureg, ScRet* ret)
|
||||
{
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
wrap_{{ .Name }}(ret, ureg);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("dispatch_syscall: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
}
|
||||
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
enter_{{ .Name }}(Fmt* fmt, Ureg* ureg)
|
||||
{
|
||||
{{ range .Vars }}{{.}}
|
||||
{{ end }}
|
||||
{{ .CommonCode }}
|
||||
fmtprint(fmt, "{{ .Name }} %#p > ", ureg->ip);
|
||||
{{ .EntryPrint }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
char*
|
||||
syscallfmt(int syscall, Ureg* ureg)
|
||||
{
|
||||
Fmt fmt;
|
||||
fmtstrinit(&fmt);
|
||||
fmtprint(&fmt, "%d %s ", up->pid, up->text);
|
||||
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
enter_{{ .Name }}(&fmt, ureg);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("syscallfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
|
||||
return fmtstrflush(&fmt);
|
||||
}
|
||||
|
||||
{{ range .Wrappers }}
|
||||
static void
|
||||
exit_{{ .Name }}(Fmt* fmt, Ureg* ureg, ScRet* ret)
|
||||
{
|
||||
fmtprint(fmt, "{{ .Name }} %#p < ", ureg->ip);
|
||||
{{ .ExitPrint }}
|
||||
}
|
||||
{{ end }}
|
||||
|
||||
char*
|
||||
sysretfmt(int syscall, Ureg* ureg, ScRet* ret, uint64_t start, uint64_t stop)
|
||||
{
|
||||
Fmt fmt;
|
||||
fmtstrinit(&fmt);
|
||||
fmtprint(&fmt, "%d %s ", up->pid, up->text);
|
||||
|
||||
switch(syscall){
|
||||
{{ range .Wrappers }}case {{ .Id }}:
|
||||
exit_{{ .Name }}(&fmt, ureg, ret);
|
||||
break;
|
||||
{{ end }}
|
||||
default:
|
||||
panic("sysretfmt: bad sys call number %d pc %#p\n", syscall, ureg->ip);
|
||||
}
|
||||
|
||||
if(0 > ret->vl){
|
||||
fmtprint(&fmt, " %s %#llud %#llud\n", up->syserrstr, start, stop-start);
|
||||
} else {
|
||||
fmtprint(&fmt, " \"\" %#llud %#llud\n", start, stop-start);
|
||||
}
|
||||
|
||||
return fmtstrflush(&fmt);
|
||||
}
|
||||
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 1 {
|
||||
usage("no path to sysconf.json")
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(flag.Arg(0))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var sysconf Sysconf
|
||||
err = json.Unmarshal(buf, &sysconf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
generateKernelCode(sysconf.Syscalls)
|
||||
|
||||
}
|
@ -258,9 +258,9 @@ const(
|
||||
#include "mem.h"
|
||||
#include "dat.h"
|
||||
#include "fns.h"
|
||||
#include "../../libc/9syscall/sys.h"
|
||||
#include <9syscall/sys.h>
|
||||
|
||||
{{ range . }}extern void {{ .Sysname }}(Ar0*, ...);
|
||||
{{ range . }}extern void {{ .Sysname }}(ScRet*, ScArg, ScArg, ScArg, ScArg, ScArg, ScArg);
|
||||
{{ end }}
|
||||
Systab systab[] = {
|
||||
{{ range . }}[{{ .Define }}] { "{{ .Name }}", {{ .Sysname }}, {{ .Fudge }} },
|
||||
|
291
src/jehanne/cmd/usyscalls/usyscalls.go
Normal file
291
src/jehanne/cmd/usyscalls/usyscalls.go
Normal file
@ -0,0 +1,291 @@
|
||||
/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
const gplHeader string = `/*
|
||||
* This file is part of Jehanne.
|
||||
*
|
||||
* Copyright (C) 2016 Giacomo Tesio <giacomo@tesio.it>
|
||||
*
|
||||
* Jehanne is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 3 of the License.
|
||||
*
|
||||
* Jehanne is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Jehanne. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
/* automatically generated by usyscalls */
|
||||
`
|
||||
|
||||
type SyscallConf struct {
|
||||
Ret []string
|
||||
Args []string
|
||||
Name string
|
||||
Id uint32
|
||||
}
|
||||
|
||||
type Sysconf struct {
|
||||
Syscalls []SyscallConf
|
||||
}
|
||||
|
||||
type SyscallWrapper struct {
|
||||
Id uint32
|
||||
Name string
|
||||
FuncArgs string
|
||||
MacroArgs string
|
||||
VarValues []string
|
||||
Vars []string
|
||||
AsmArgs string
|
||||
AsmClobbers string
|
||||
RetType string
|
||||
}
|
||||
|
||||
type HeaderCode struct {
|
||||
Wrappers []SyscallWrapper
|
||||
}
|
||||
|
||||
|
||||
func usage(msg string) {
|
||||
fmt.Fprint(os.Stderr, msg)
|
||||
fmt.Fprint(os.Stderr, "Usage: usyscalls header|code path/to/sysconf.json\n")
|
||||
flag.PrintDefaults()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func argTypeName(t string) string{
|
||||
switch(t){
|
||||
case "int", "int32_t":
|
||||
return "i"
|
||||
case "unsigned int", "uint32_t":
|
||||
/* unsigned int is reserved for flags */
|
||||
return "ui"
|
||||
case "long", "int64_t":
|
||||
return "l"
|
||||
case "unsigned long", "uint64_t":
|
||||
return "ul"
|
||||
case "void*", "uint8_t*", "const void*", "const uint8_t*":
|
||||
return "p"
|
||||
case "int32_t*", "int*", "const int32_t*", "const int*":
|
||||
return "p"
|
||||
case "const char*", "char*":
|
||||
return "p"
|
||||
case "const char**", "char**":
|
||||
return "p"
|
||||
}
|
||||
return " [?? " + t + "]"
|
||||
|
||||
}
|
||||
|
||||
func argRegister(index int, t string) string{
|
||||
prefix := ""
|
||||
switch(t){
|
||||
case "int", "unsigned int", "uint32_t", "int32_t":
|
||||
prefix = "e"
|
||||
default:
|
||||
prefix = "r"
|
||||
}
|
||||
switch(index){
|
||||
case 0:
|
||||
return prefix + "di";
|
||||
case 1:
|
||||
return prefix + "si";
|
||||
case 2:
|
||||
return prefix + "dx";
|
||||
case 3:
|
||||
return "r10";
|
||||
case 4:
|
||||
return "r8";
|
||||
case 5:
|
||||
return "r9";
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func getHeaderData(calls []SyscallConf) *HeaderCode {
|
||||
code := new(HeaderCode)
|
||||
for _, call := range(calls) {
|
||||
wcall := new(SyscallWrapper)
|
||||
wcall.Id = call.Id
|
||||
wcall.Name = call.Name
|
||||
wcall.RetType = call.Ret[0]
|
||||
|
||||
clobberMemory := false
|
||||
wcall.AsmClobbers = "\"cc\", \"rcx\", \"r11\""
|
||||
wcall.AsmArgs = fmt.Sprintf("\"0\"(%d)", wcall.Id)
|
||||
for i, a := range(call.Args){
|
||||
if i > 0 {
|
||||
wcall.FuncArgs += ", "
|
||||
wcall.MacroArgs += ", "
|
||||
}
|
||||
if strings.HasSuffix(a, "*") && !strings.HasPrefix(a, "const"){
|
||||
clobberMemory = true
|
||||
}
|
||||
wcall.FuncArgs += fmt.Sprintf("%s a%d", a, i)
|
||||
wcall.MacroArgs += fmt.Sprintf("/* %s */ a%d", a, i)
|
||||
wcall.VarValues = append(wcall.VarValues, fmt.Sprintf("_sysargs[%d].%s = (a%d); \\\n\t", i, argTypeName(a), i))
|
||||
wcall.Vars = append(wcall.Vars, fmt.Sprintf("register %s __r%d asm(\"%s\") = _sysargs[%d].%s; \\\n\t", a, i, argRegister(i, a), i, argTypeName(a)))
|
||||
wcall.AsmArgs += fmt.Sprintf(", \"r\"(__r%d)", i)
|
||||
}
|
||||
if clobberMemory {
|
||||
wcall.AsmClobbers += ", \"memory\""
|
||||
}
|
||||
code.Wrappers = append(code.Wrappers, *wcall)
|
||||
}
|
||||
|
||||
return code
|
||||
}
|
||||
|
||||
func generateSyscallTable(calls []SyscallConf){
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("tab.c").Parse(`
|
||||
{{ range .Wrappers }}"{{ .Name }}", (int(*)()) {{ .Name }},
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateLibcCode(calls []SyscallConf){
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("syscalls.c").Parse(gplHeader + `
|
||||
#define PORTABLE_SYSCALLS
|
||||
#include <u.h>
|
||||
|
||||
{{ range .Wrappers }}
|
||||
{{ .RetType }}
|
||||
{{ .Name }}({{ .FuncArgs }})
|
||||
{
|
||||
register {{ .RetType }} __ret asm ("rax");
|
||||
__asm__ __volatile__ (
|
||||
"movq %%rcx, %%r10" "\n\t"
|
||||
"movq ${{ .Id }}, %%rax" "\n\t"
|
||||
"syscall"
|
||||
: "=r" (__ret)
|
||||
: /* args are ready */
|
||||
: {{ .AsmClobbers }}
|
||||
);
|
||||
return __ret;
|
||||
}
|
||||
{{ end }}
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func generateSyscallHeader(calls []SyscallConf){
|
||||
funcMap := template.FuncMap{
|
||||
"title": strings.Title,
|
||||
}
|
||||
code := getHeaderData(calls)
|
||||
tmpl, err := template.New("syscall.h").Funcs(funcMap).Parse(gplHeader + `
|
||||
typedef enum Syscalls
|
||||
{
|
||||
{{ range .Wrappers }} Sys{{ .Name|title }} = {{ .Id }},
|
||||
{{ end }}} Syscalls;
|
||||
|
||||
#ifndef KERNEL
|
||||
{{ range .Wrappers }}
|
||||
#define sys_{{ .Name }}({{ .MacroArgs }}) ({ \
|
||||
{{ range .VarValues }}{{.}}{{end}}{{ range .Vars }}{{.}}{{end}}register {{ .RetType }} __ret asm ("rax"); \
|
||||
__asm__ __volatile__ ( \
|
||||
"syscall" \
|
||||
: "=&r" (__ret) \
|
||||
: {{ .AsmArgs }} \
|
||||
: {{ .AsmClobbers }} \
|
||||
); \
|
||||
__ret; })
|
||||
{{ end }}
|
||||
|
||||
#ifdef PORTABLE_SYSCALLS
|
||||
|
||||
{{ range .Wrappers }}extern {{ .RetType }} {{ .Name }}({{ .FuncArgs }});
|
||||
{{ end }}
|
||||
extern int32_t read(int, void*, int32_t);
|
||||
extern int32_t write(int, const void*, int32_t);
|
||||
|
||||
#else
|
||||
|
||||
{{ range .Wrappers }}# define {{ .Name }}(...) sys_{{ .Name }}(__VA_ARGS__)
|
||||
{{ end }}
|
||||
#define read(fd, buf, size) pread(fd, buf, size, -1)
|
||||
#define write(fd, buf, size) pwrite(fd, buf, size, -1)
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
`)
|
||||
err = tmpl.Execute(os.Stdout, code)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
flag.Parse()
|
||||
|
||||
if flag.NArg() != 2 {
|
||||
usage("no path to sysconf.json")
|
||||
}
|
||||
|
||||
buf, err := ioutil.ReadFile(flag.Arg(1))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
var sysconf Sysconf
|
||||
err = json.Unmarshal(buf, &sysconf)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
mode := flag.Arg(0)
|
||||
switch(mode){
|
||||
case "header":
|
||||
generateSyscallHeader(sysconf.Syscalls)
|
||||
break
|
||||
case "code":
|
||||
generateLibcCode(sysconf.Syscalls)
|
||||
break
|
||||
case "tab":
|
||||
generateSyscallTable(sysconf.Syscalls)
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user