whatcanGOwrong
This commit is contained in:
@@ -0,0 +1,267 @@
|
||||
package kconfig
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cilium/ebpf/btf"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
)
|
||||
|
||||
// Find find a kconfig file on the host.
|
||||
// It first reads from /boot/config- of the current running kernel and tries
|
||||
// /proc/config.gz if nothing was found in /boot.
|
||||
// If none of the file provide a kconfig, it returns an error.
|
||||
func Find() (*os.File, error) {
|
||||
kernelRelease, err := internal.KernelRelease()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot get kernel release: %w", err)
|
||||
}
|
||||
|
||||
path := "/boot/config-" + kernelRelease
|
||||
f, err := os.Open(path)
|
||||
if err == nil {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
f, err = os.Open("/proc/config.gz")
|
||||
if err == nil {
|
||||
return f, nil
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("neither %s nor /proc/config.gz provide a kconfig", path)
|
||||
}
|
||||
|
||||
// Parse parses the kconfig file for which a reader is given.
|
||||
// All the CONFIG_* which are in filter and which are set set will be
|
||||
// put in the returned map as key with their corresponding value as map value.
|
||||
// If filter is nil, no filtering will occur.
|
||||
// If the kconfig file is not valid, error will be returned.
|
||||
func Parse(source io.ReaderAt, filter map[string]struct{}) (map[string]string, error) {
|
||||
var r io.Reader
|
||||
zr, err := gzip.NewReader(io.NewSectionReader(source, 0, math.MaxInt64))
|
||||
if err != nil {
|
||||
r = io.NewSectionReader(source, 0, math.MaxInt64)
|
||||
} else {
|
||||
// Source is gzip compressed, transparently decompress.
|
||||
r = zr
|
||||
}
|
||||
|
||||
ret := make(map[string]string, len(filter))
|
||||
|
||||
s := bufio.NewScanner(r)
|
||||
|
||||
for s.Scan() {
|
||||
line := s.Bytes()
|
||||
err = processKconfigLine(line, ret, filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("cannot parse line: %w", err)
|
||||
}
|
||||
|
||||
if filter != nil && len(ret) == len(filter) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, fmt.Errorf("cannot parse: %w", err)
|
||||
}
|
||||
|
||||
if zr != nil {
|
||||
return ret, zr.Close()
|
||||
}
|
||||
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
// Golang translation of libbpf bpf_object__process_kconfig_line():
|
||||
// https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/libbpf.c#L1874
|
||||
// It does the same checks but does not put the data inside the BPF map.
|
||||
func processKconfigLine(line []byte, m map[string]string, filter map[string]struct{}) error {
|
||||
// Ignore empty lines and "# CONFIG_* is not set".
|
||||
if !bytes.HasPrefix(line, []byte("CONFIG_")) {
|
||||
return nil
|
||||
}
|
||||
|
||||
key, value, found := bytes.Cut(line, []byte{'='})
|
||||
if !found {
|
||||
return fmt.Errorf("line %q does not contain separator '='", line)
|
||||
}
|
||||
|
||||
if len(value) == 0 {
|
||||
return fmt.Errorf("line %q has no value", line)
|
||||
}
|
||||
|
||||
if filter != nil {
|
||||
// NB: map[string(key)] gets special optimisation help from the compiler
|
||||
// and doesn't allocate. Don't turn this into a variable.
|
||||
_, ok := filter[string(key)]
|
||||
if !ok {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// This can seem odd, but libbpf only sets the value the first time the key is
|
||||
// met:
|
||||
// https://github.com/torvalds/linux/blob/0d85b27b0cc6/tools/lib/bpf/libbpf.c#L1906-L1908
|
||||
_, ok := m[string(key)]
|
||||
if !ok {
|
||||
m[string(key)] = string(value)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// PutValue translates the value given as parameter depending on the BTF
|
||||
// type, the translated value is then written to the byte array.
|
||||
func PutValue(data []byte, typ btf.Type, value string) error {
|
||||
typ = btf.UnderlyingType(typ)
|
||||
|
||||
switch value {
|
||||
case "y", "n", "m":
|
||||
return putValueTri(data, typ, value)
|
||||
default:
|
||||
if strings.HasPrefix(value, `"`) {
|
||||
return putValueString(data, typ, value)
|
||||
}
|
||||
return putValueNumber(data, typ, value)
|
||||
}
|
||||
}
|
||||
|
||||
// Golang translation of libbpf_tristate enum:
|
||||
// https://github.com/libbpf/libbpf/blob/fbd60dbff51c870f5e80a17c4f2fd639eb80af90/src/bpf_helpers.h#L169
|
||||
type triState int
|
||||
|
||||
const (
|
||||
TriNo triState = 0
|
||||
TriYes triState = 1
|
||||
TriModule triState = 2
|
||||
)
|
||||
|
||||
func putValueTri(data []byte, typ btf.Type, value string) error {
|
||||
switch v := typ.(type) {
|
||||
case *btf.Int:
|
||||
if v.Encoding != btf.Bool {
|
||||
return fmt.Errorf("cannot add tri value, expected btf.Bool, got: %v", v.Encoding)
|
||||
}
|
||||
|
||||
if v.Size != 1 {
|
||||
return fmt.Errorf("cannot add tri value, expected size of 1 byte, got: %d", v.Size)
|
||||
}
|
||||
|
||||
switch value {
|
||||
case "y":
|
||||
data[0] = 1
|
||||
case "n":
|
||||
data[0] = 0
|
||||
default:
|
||||
return fmt.Errorf("cannot use %q for btf.Bool", value)
|
||||
}
|
||||
case *btf.Enum:
|
||||
if v.Name != "libbpf_tristate" {
|
||||
return fmt.Errorf("cannot use enum %q, only libbpf_tristate is supported", v.Name)
|
||||
}
|
||||
|
||||
var tri triState
|
||||
switch value {
|
||||
case "y":
|
||||
tri = TriYes
|
||||
case "m":
|
||||
tri = TriModule
|
||||
case "n":
|
||||
tri = TriNo
|
||||
default:
|
||||
return fmt.Errorf("value %q is not support for libbpf_tristate", value)
|
||||
}
|
||||
|
||||
internal.NativeEndian.PutUint64(data, uint64(tri))
|
||||
default:
|
||||
return fmt.Errorf("cannot add number value, expected btf.Int or btf.Enum, got: %T", v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func putValueString(data []byte, typ btf.Type, value string) error {
|
||||
array, ok := typ.(*btf.Array)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot add string value, expected btf.Array, got %T", array)
|
||||
}
|
||||
|
||||
contentType, ok := btf.UnderlyingType(array.Type).(*btf.Int)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot add string value, expected array of btf.Int, got %T", contentType)
|
||||
}
|
||||
|
||||
// Any Int, which is not bool, of one byte could be used to store char:
|
||||
// https://github.com/torvalds/linux/blob/1a5304fecee5/tools/lib/bpf/libbpf.c#L3637-L3638
|
||||
if contentType.Size != 1 && contentType.Encoding != btf.Bool {
|
||||
return fmt.Errorf("cannot add string value, expected array of btf.Int of size 1, got array of btf.Int of size: %v", contentType.Size)
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(value, `"`) || !strings.HasSuffix(value, `"`) {
|
||||
return fmt.Errorf(`value %q must start and finish with '"'`, value)
|
||||
}
|
||||
|
||||
str := strings.Trim(value, `"`)
|
||||
|
||||
// We need to trim string if the bpf array is smaller.
|
||||
if uint32(len(str)) >= array.Nelems {
|
||||
str = str[:array.Nelems]
|
||||
}
|
||||
|
||||
// Write the string content to .kconfig.
|
||||
copy(data, str)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func putValueNumber(data []byte, typ btf.Type, value string) error {
|
||||
integer, ok := typ.(*btf.Int)
|
||||
if !ok {
|
||||
return fmt.Errorf("cannot add number value, expected *btf.Int, got: %T", integer)
|
||||
}
|
||||
|
||||
size := integer.Size
|
||||
sizeInBits := size * 8
|
||||
|
||||
var n uint64
|
||||
var err error
|
||||
if integer.Encoding == btf.Signed {
|
||||
parsed, e := strconv.ParseInt(value, 0, int(sizeInBits))
|
||||
|
||||
n = uint64(parsed)
|
||||
err = e
|
||||
} else {
|
||||
parsed, e := strconv.ParseUint(value, 0, int(sizeInBits))
|
||||
|
||||
n = uint64(parsed)
|
||||
err = e
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot parse value: %w", err)
|
||||
}
|
||||
|
||||
switch size {
|
||||
case 1:
|
||||
data[0] = byte(n)
|
||||
case 2:
|
||||
internal.NativeEndian.PutUint16(data, uint16(n))
|
||||
case 4:
|
||||
internal.NativeEndian.PutUint32(data, uint32(n))
|
||||
case 8:
|
||||
internal.NativeEndian.PutUint64(data, uint64(n))
|
||||
default:
|
||||
return fmt.Errorf("size (%d) is not valid, expected: 1, 2, 4 or 8", size)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
package kconfig
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/cilium/ebpf/btf"
|
||||
"github.com/cilium/ebpf/internal"
|
||||
|
||||
qt "github.com/frankban/quicktest"
|
||||
)
|
||||
|
||||
func BenchmarkParse(b *testing.B) {
|
||||
f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := Parse(f, nil)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkParseFiltered(b *testing.B) {
|
||||
f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz")
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
b.ReportAllocs()
|
||||
b.ResetTimer()
|
||||
|
||||
// CONFIG_ARCH_USE_MEMTEST is the last CONFIG_ in the file.
|
||||
// So, we will easily be able to see how many allocated bytes the filtering
|
||||
// permits reducing compared to unfiltered benchmark.
|
||||
filter := map[string]struct{}{"CONFIG_ARCH_USE_MEMTEST": {}}
|
||||
|
||||
for n := 0; n < b.N; n++ {
|
||||
_, err := Parse(f, filter)
|
||||
if err != nil {
|
||||
b.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestParse(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.Open("testdata/test.kconfig")
|
||||
if err != nil {
|
||||
t.Fatal("Error reading /testdata/test.kconfig: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
config, err := Parse(f, nil)
|
||||
if err != nil {
|
||||
t.Fatal("Error parsing kconfig: ", err)
|
||||
}
|
||||
|
||||
expected := map[string]string{
|
||||
"CONFIG_TRISTATE": "m",
|
||||
"CONFIG_BOOL": "y",
|
||||
"CONFIG_CHAR": "100",
|
||||
"CONFIG_USHORT": "30000",
|
||||
"CONFIG_INT": "123456",
|
||||
"CONFIG_ULONG": "0xDEADBEEFC0DE",
|
||||
"CONFIG_STR": `"abracad"`,
|
||||
"CONFIG_FOO": `"foo"`,
|
||||
}
|
||||
qt.Assert(t, config, qt.DeepEquals, expected)
|
||||
}
|
||||
|
||||
func TestParseFiltered(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.Open("testdata/test.kconfig")
|
||||
if err != nil {
|
||||
t.Fatal("Error reading /testdata/test.kconfig: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
filter := map[string]struct{}{"CONFIG_FOO": {}}
|
||||
|
||||
config, err := Parse(f, filter)
|
||||
if err != nil {
|
||||
t.Fatal("Error parsing gzipped kconfig: ", err)
|
||||
}
|
||||
|
||||
expected := map[string]string{"CONFIG_FOO": `"foo"`}
|
||||
qt.Assert(t, config, qt.DeepEquals, expected)
|
||||
}
|
||||
|
||||
func TestParseGzipped(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz")
|
||||
if err != nil {
|
||||
t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
_, err = Parse(f, nil)
|
||||
if err != nil {
|
||||
t.Fatal("Error parsing gzipped kconfig: ", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseGzippedFiltered(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
f, err := os.Open("testdata/config-6.2.15-300.fc38.x86_64.gz")
|
||||
if err != nil {
|
||||
t.Fatal("Error reading /testdata/config-6.2.15-300.fc38.x86_64.gz: ", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
filter := map[string]struct{}{"CONFIG_HZ": {}}
|
||||
|
||||
config, err := Parse(f, filter)
|
||||
if err != nil {
|
||||
t.Fatal("Error parsing gzipped kconfig: ", err)
|
||||
}
|
||||
|
||||
expected := map[string]string{"CONFIG_HZ": "1000"}
|
||||
qt.Assert(t, config, qt.DeepEquals, expected)
|
||||
}
|
||||
|
||||
func TestProcessKconfigBadLine(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
m := make(map[string]string)
|
||||
|
||||
err := processKconfigLine([]byte("CONFIG_FOO"), m, nil)
|
||||
qt.Assert(t, err, qt.IsNotNil, qt.Commentf("line has no '='"))
|
||||
|
||||
err = processKconfigLine([]byte("CONFIG_FOO="), m, nil)
|
||||
qt.Assert(t, err, qt.IsNotNil, qt.Commentf("line has no value"))
|
||||
}
|
||||
|
||||
func TestPutValue(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
type testCase struct {
|
||||
typ btf.Type
|
||||
value string
|
||||
expected any
|
||||
comment string
|
||||
}
|
||||
|
||||
cases := []testCase{
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Bool,
|
||||
},
|
||||
value: "n",
|
||||
expected: int8(0),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Bool,
|
||||
},
|
||||
value: "y",
|
||||
expected: int8(1),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Bool,
|
||||
},
|
||||
value: "foo",
|
||||
comment: "Bad value",
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{},
|
||||
comment: "Encoding is not Bool",
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Encoding: btf.Bool,
|
||||
},
|
||||
comment: "Size is not 1",
|
||||
},
|
||||
{
|
||||
typ: &btf.Enum{
|
||||
Name: "libbpf_tristate",
|
||||
},
|
||||
value: "y",
|
||||
expected: int64(TriYes),
|
||||
},
|
||||
{
|
||||
typ: &btf.Enum{
|
||||
Name: "libbpf_tristate",
|
||||
},
|
||||
value: "n",
|
||||
expected: int64(TriNo),
|
||||
},
|
||||
{
|
||||
typ: &btf.Enum{
|
||||
Name: "libbpf_tristate",
|
||||
},
|
||||
value: "m",
|
||||
expected: int64(TriModule),
|
||||
},
|
||||
{
|
||||
typ: &btf.Enum{
|
||||
Name: "libbpf_tristate",
|
||||
},
|
||||
value: "foo",
|
||||
comment: "Bad value",
|
||||
},
|
||||
{
|
||||
typ: &btf.Enum{
|
||||
Name: "error",
|
||||
},
|
||||
comment: "Enum name is wrong",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{},
|
||||
value: "y",
|
||||
comment: "Type is not btf.Int",
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 1,
|
||||
},
|
||||
value: "255",
|
||||
expected: uint8(255),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 2,
|
||||
},
|
||||
value: "0xcafe",
|
||||
expected: uint16(0xcafe),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 2,
|
||||
},
|
||||
value: "0755",
|
||||
expected: uint16(0755),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 4,
|
||||
Encoding: btf.Signed,
|
||||
},
|
||||
value: "-2147483648",
|
||||
expected: int32(-2147483648),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 4,
|
||||
Encoding: btf.Signed,
|
||||
},
|
||||
value: "+2147483647",
|
||||
expected: int32(+2147483647),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 4,
|
||||
},
|
||||
value: "0xcafec0de",
|
||||
expected: uint32(0xcafec0de),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 8,
|
||||
Encoding: btf.Signed,
|
||||
},
|
||||
value: "+1000000000000",
|
||||
expected: int64(1000000000000),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 8,
|
||||
},
|
||||
value: "1000000000000",
|
||||
expected: uint64(1000000000000),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 1,
|
||||
},
|
||||
value: "foo",
|
||||
comment: "Value is not an int",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{},
|
||||
value: "1",
|
||||
comment: "Type is not btf.Int",
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{
|
||||
Size: 16,
|
||||
},
|
||||
value: "1",
|
||||
comment: "Size is wrong",
|
||||
},
|
||||
{
|
||||
typ: &btf.Typedef{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
},
|
||||
},
|
||||
value: "1",
|
||||
expected: uint8(1),
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Char,
|
||||
},
|
||||
Nelems: 6,
|
||||
},
|
||||
value: `"foobar"`,
|
||||
expected: []byte("foobar"),
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Unsigned,
|
||||
},
|
||||
Nelems: 3,
|
||||
},
|
||||
value: `"foobar"`,
|
||||
expected: []byte("foo"),
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Signed,
|
||||
},
|
||||
Nelems: 2,
|
||||
},
|
||||
value: `"42"`,
|
||||
expected: []byte("42"),
|
||||
},
|
||||
{
|
||||
typ: &btf.Int{},
|
||||
value: `"foo"`,
|
||||
comment: "Type is not btf.Array",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{},
|
||||
value: `"foo"`,
|
||||
comment: "Type is not btf.Array of btf.Int",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Bool,
|
||||
},
|
||||
},
|
||||
comment: "Type is not btf.Array of btf.Int of size 1 which is not btf.Bool",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 4,
|
||||
Encoding: btf.Char,
|
||||
},
|
||||
},
|
||||
value: `"foo"`,
|
||||
comment: "Type is not btf.Array of btf.Char of size 1",
|
||||
},
|
||||
{
|
||||
typ: &btf.Array{
|
||||
Type: &btf.Int{
|
||||
Size: 1,
|
||||
Encoding: btf.Char,
|
||||
},
|
||||
},
|
||||
value: `"foo`,
|
||||
comment: `Value does not start and end with '"'`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
if len(c.comment) > 0 {
|
||||
err := PutValue(make([]byte, 0), c.typ, c.value)
|
||||
|
||||
qt.Assert(t, err, qt.IsNotNil, qt.Commentf(c.comment))
|
||||
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
err := binary.Write(&buf, internal.NativeEndian, c.expected)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
expected := buf.Bytes()
|
||||
data := make([]byte, len(expected))
|
||||
err = PutValue(data, c.typ, c.value)
|
||||
|
||||
qt.Assert(t, err, qt.IsNil)
|
||||
|
||||
qt.Assert(t, data, qt.DeepEquals, expected)
|
||||
}
|
||||
}
|
||||
go/pkg/mod/github.com/cilium/ebpf@v0.11.0/internal/kconfig/testdata/config-6.2.15-300.fc38.x86_64.gz
Vendored
BIN
Binary file not shown.
+11
@@ -0,0 +1,11 @@
|
||||
CONFIG_TRISTATE=m
|
||||
# CONFIG_IS_NOT_SET is not set
|
||||
CONFIG_BOOL=y
|
||||
CONFIG_CHAR=100
|
||||
|
||||
CONFIG_USHORT=30000
|
||||
CONFIG_INT=123456
|
||||
CONFIG_ULONG=0xDEADBEEFC0DE
|
||||
CONFIG_STR="abracad"
|
||||
CONFIG_FOO="foo"
|
||||
CONFIG_FOO="bar"
|
||||
Reference in New Issue
Block a user