diff --git a/src/gopheros/kernel/hal/multiboot/multiboot.go b/src/gopheros/kernel/hal/multiboot/multiboot.go index f3c3586..77ee31f 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot.go @@ -1,6 +1,10 @@ package multiboot -import "unsafe" +import ( + "reflect" + "strings" + "unsafe" +) type tagType uint32 @@ -137,7 +141,8 @@ const ( ) var ( - infoData uintptr + infoData uintptr + cmdLineKV map[string]string ) // MemRegionVisitor defies a visitor function that gets invoked by VisitMemRegions @@ -224,6 +229,39 @@ func GetFramebufferInfo() *FramebufferInfo { return info } +// GetBootCmdLine returns the command line key-value pairs passed to the +// kernel. This function must only be invoked after bootstrapping the memory +// allocator. +func GetBootCmdLine() map[string]string { + if cmdLineKV != nil { + return cmdLineKV + } + + cmdLineKV = make(map[string]string) + + curPtr, size := findTagByType(tagBootCmdLine) + if size != 0 { + // The command line is a C-style NULL-terminated string + cmdLine := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{ + Len: int(size - 1), + Cap: int(size - 1), + Data: curPtr, + })) + pairs := strings.Fields(string(cmdLine)) + for _, pair := range pairs { + kv := strings.Split(pair, "=") + switch len(kv) { + case 2: // foo=bar + cmdLineKV[kv[0]] = kv[1] + case 1: // nofoo + cmdLineKV[kv[0]] = kv[0] + } + } + } + + return cmdLineKV +} + // findTagByType scans the multiboot info data looking for the start of of the // specified type. It returns a pointer to the tag contents start offset and // the content length exluding the tag header. diff --git a/src/gopheros/kernel/hal/multiboot/multiboot_test.go b/src/gopheros/kernel/hal/multiboot/multiboot_test.go index 6ef35de..25a8861 100644 --- a/src/gopheros/kernel/hal/multiboot/multiboot_test.go +++ b/src/gopheros/kernel/hal/multiboot/multiboot_test.go @@ -1,6 +1,7 @@ package multiboot import ( + "reflect" "testing" "unsafe" ) @@ -170,6 +171,26 @@ func TestGetFramebufferInfo(t *testing.T) { } } +func TestGetBootCmdLine(t *testing.T) { + SetInfoPtr(uintptr(unsafe.Pointer(&multibootInfoTestData[0]))) + + expKV := map[string]string{ + "param1": "param1", // "param1" (no value) + "param2": "value2", // "param2=value2" + } + + kv := GetBootCmdLine() + if !reflect.DeepEqual(kv, expKV) { + t.Errorf("expected to get: %v; got %v", expKV, kv) + } + + // Second call should return the memoized data + cmdLineKV["foo"] = "bar" + if kv2 := GetBootCmdLine(); !reflect.DeepEqual(kv2, kv) { + t.Error("expected second call to GetBootCmdLine() to return the memoized KV pairs") + } +} + var ( emptyInfoData = []byte{ 0, 0, 0, 0, // size @@ -181,7 +202,7 @@ var ( // A dump of multiboot data when running under qemu. multibootInfoTestData = []byte{ 0xb8, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, - 0x70, 0x61, 0x72, 0x61, 0x6d, 0x31, 0x3d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x31, 0x20, 0x70, 0x61, + 0x70, 0x61, 0x72, 0x61, 0x6d, 0x31, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x70, 0x61, 0x72, 0x61, 0x6d, 0x32, 0x3d, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x32, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x24, 0x00, 0x00, 0x00, 0x47, 0x52, 0x55, 0x42, 0x20, 0x32, 0x2e, 0x30, 0x32, 0x7e, 0x62, 0x65, 0x74, 0x61, 0x32, 0x2d, 0x33, 0x36, 0x75, 0x62, 0x75, 0x6e, 0x74, 0x75,