develop PCI device by python on QEMU ARM model

13 篇文章 0 订阅

QEMU devices are developed by C. I hope to develop a PCI device by python. So PCI device is half C and half python. C part is interface to QEMU, python part is device self. And we need call python functions from C. This topic is on my previous blog.

Add this device into ARM model on QEMU. This topic is on my previous blog.

So Here is the necessary steps to run python device model on QEMU.

QEMU ARM model: virt-2.7

PCI device: edu

1. edu code modification:

diff --git a/hw/misc/edu.c b/hw/misc/edu.c
index 91af452..c624fd4 100644
--- a/hw/misc/edu.c
+++ b/hw/misc/edu.c
@@ -30,6 +30,15 @@
 #include "qemu/main-loop.h" /* iothread mutex */
 #include "qapi/visitor.h"
 
+#include "edu_c2p_pci.h"
+
+#define DEBUG_EDU
+#ifdef DEBUG_EDU
+# define EDU_DPRINTF(format, ...)       printf(format, ## __VA_ARGS__)
+#else
+# define EDU_DPRINTF(format, ...)       do { } while (0)
+#endif
+
 #define TYPE_PCI_EDU_DEVICE "edu"
 #define EDU(obj)        OBJECT_CHECK(EduState, obj, TYPE_PCI_EDU_DEVICE)
 
@@ -397,6 +406,22 @@ static void edu_instance_init(Object *obj)
                     edu_obj_uint64, NULL, &edu->dma_mask, NULL);
 }
 
+static uint32_t edu_pci_read_config(PCIDevice *d, uint32_t address, int len)
+{
+    uint32_t val = 0;
+#if 0
+    if (pci_is_express_downstream_port(d) &&
+        ranges_overlap(address, len, d->exp.exp_cap + PCI_EXP_LNKSTA, 2)) {
+        pcie_sync_bridge_lnk(d);
+    }
+#endif
+    //memcpy(&val, d->config + address, len);
+    val=edu_c2p_pci_configure_space_read(address,len);
+    memcpy(d->config + address,&val, len);
+    EDU_DPRINTF("%s, PCI device is %s, address is 0x%x,val is 0x%x\n",__func__,d->name,address,val);
+    return le32_to_cpu(val);
+}
+
 static void edu_class_init(ObjectClass *class, void *data)
 {
     DeviceClass *dc = DEVICE_CLASS(class);
@@ -408,6 +433,7 @@ static void edu_class_init(ObjectClass *class, void *data)
     k->device_id = 0x11e8;
     k->revision = 0x10;
     k->class_id = PCI_CLASS_OTHERS;
+    k->config_read = edu_pci_read_config;
     set_bit(DEVICE_CATEGORY_MISC, dc->categories);
 }
 

edu_pci_read_config() is called when u-boot access edu's configure space. it should return configure space register value to u-boot. For PCI device, QEMU has default PCI configure space read/write functions. However, the configure space registers are in C language model. Here I implement the configure space registers by python. So I need implement configure space read function by myself and hook it to PCI class interface.

In edu_pci_read_config(), you can see I called edu_c2p_pci_configure_space_read(). this function is C to python interface.

And you can see I copy register value(from python) to C language model(d->config) as:

memcpy(d->config + address,&val, len);

Why? because the QEMU monitor can only show C language model now. If I modify the monitor to access python model, I don't need this step.

2. C to Python code:

c-call-python.c



#include "c-call-python.h"



PyObject* c_call_py_init(char* path,char* file)
{
	PyObject *pName, *pModule=NULL;
	Py_Initialize();

	PySys_SetPath(path);  // path to the module to import
	pName = PyString_FromString(file);
	if(pName==NULL){
		printf("module name is NULL\n");
	}
	pModule = PyImport_Import(pName);
	if(pModule==NULL){
		printf("can't import python file\n");
	}
	return pModule;
}

PyObject* c2p_parse_arg(struct c2p_argv* argv)
{
	PyObject *pValue=NULL;
	switch(argv->type)
	{
	case C2P_ARG_TYPE_INT:
		pValue = PyInt_FromLong(*(int *)(argv->value));
		break;
	case C2P_ARG_TYPE_STR:
		pValue = PyString_FromString((char*)(argv->value));
		break;
	default:
		break;
	}
	return pValue;	
}
PyObject* c2p_parse_args(struct c2p_param* param)
{
	PyObject *pythonArgument=NULL;
	PyObject *pValue=NULL;
	struct c2p_argv* argv=NULL;
	int i;
	if(param->argc == 0) return NULL;
	pythonArgument = PyTuple_New(param->argc);
	argv=param->argv;
	for(i=0;i<param->argc;i++){
		pValue = c2p_parse_arg(&argv[i]);
                if (pValue == NULL) {
                        return NULL;
                }
                PyTuple_SetItem(pythonArgument, i, pValue);
	}
	return pythonArgument;
}
PyObject* c_call_py(void* pyobj,char* func,struct c2p_param* param)
{
	PyObject *pResult=NULL, *pFunc=NULL;
	PyObject *pythonArgument=NULL;
	pFunc = PyObject_GetAttrString(pyobj, func);
	if(pFunc==NULL || !PyCallable_Check(pFunc)){
		return NULL;
	}
	pythonArgument = c2p_parse_args(param);
	if(pythonArgument==NULL) return NULL;
	pResult = PyObject_CallObject(pFunc, pythonArgument);
	return pResult;
}

#ifdef TEST_C_CALL_PYTHON
int c_call_python(int argc, char *argv[])
{
    int ret=0;
    PyObject *pName, *pModule, *pDict, *pFunc;
    PyObject *pArgs, *pValue;

    Py_Initialize();

    PySys_SetPath("./");  // path to the module to import
    pName = PyString_FromString(argv[1]);

    pModule = PyImport_Import(pName);
    if (pModule != NULL) {
        PyObject *pythonArgument;
        pythonArgument = PyTuple_New(1);
        pValue = PyString_FromString(argv[3]);

        if (pValue == NULL) {
            ret=-1;
            goto out;
        }
        PyTuple_SetItem(pythonArgument, 0, pValue);
        pFunc = PyObject_GetAttrString(pModule, argv[2]);
        if (pFunc && PyCallable_Check(pFunc)) {
            pValue = PyObject_CallObject(pFunc, pythonArgument);
            if (pValue != NULL) {
                printf("Value returuend from the function %s", PyString_AsString(pValue));
            } else {
                PyErr_Print();
		ret= -1;
            }
        } else {
            if (PyErr_Occurred())
                PyErr_Print();
            fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
            ret=-1;
        }
    }
    else {
        PyErr_Print();
        fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
        ret=-1;
    }
out:
    Py_Finalize();
    return ret;
}


int main(int argc,char** argv)
{
	uint32_t reg;
	if(argc < 3){
		printf("command line is: c-python [python-file-name] [python-function-name]\n");
		return -1;
	}
	//c_call_python(argc,argv);
	reg=c2p_pci_configure_space_read(0x0,4);
	printf("reg value is 0x%x\n",reg);
#if 0
	printf("c calls python %s\n",argv[1]);
	Py_Initialize();
	PySys_SetPath("./");

	const char* kModuleName = argv[1];//"mymain";
	PyObject* module_name = PyString_FromString(kModuleName);
	PyObject* module = PyImport_Import(module_name);

	PyObject* dic = PyModule_GetDict(module);
	const char* kFuncName = argv[2];//"main";
	PyObject* main_func = PyDict_GetItemString(dic, kFuncName);
	assert(PyCallable_Check(main_func));

	PyObject* main_args = PyTuple_New(1);
	PyObject* main_args_0 = PyString_FromString("Hello, Python!");
	PyTuple_SetItem(main_args, 0, main_args_0);
	PyObject_CallObject(main_func, main_args);
#endif
	Py_Finalize();
	return 0;
}
#endif

c-call-python.h

#ifndef __C_CALL_PYTHON_H__
#define __C_CALL_PYTHON_H__
#define C2P_ARG_TYPE_INT	0
#define C2P_ARG_TYPE_STR	1

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "/usr/include/python2.7/Python.h"

struct c2p_argv{
	int type;
	void* value;
};
struct c2p_param{
	int argc;
	struct c2p_argv* argv;
};

PyObject* c_call_py_init(char* path,char* file);
PyObject* c_call_py(void* pyobj,char* func,struct c2p_param* param);
PyObject* c2p_parse_arg(struct c2p_argv* argv);
PyObject* c2p_parse_args(struct c2p_param* param);
#endif

3. EDU C to Python interface

edu_c2p.c

#include "c-call-python.h"
#include "edu_c2p_pci.h"
static PyObject* pModule_PCI;
static PyObject* edu_c2p_find_pci_module(void)
{
#if 1
	char path[8];
	char name[64];
	memset(path,0,8);
	memset(name,0,64);
	strcpy(path,"./");
	strcpy(name,"qemu_pci_config_space");
#else
	char* path="./";
	char* name="qemu_pci_config_space";
#endif
	if(pModule_PCI==NULL)
		pModule_PCI=c_call_py_init(path,name);
	return pModule_PCI;
}
uint32_t edu_c2p_pci_configure_space_read(uint32_t addr, int len)
{
	PyObject* pResult=NULL, *pModule=NULL;
	char py_func[64];
	struct c2p_argv argv[2];
	struct c2p_param param;
	pModule=edu_c2p_find_pci_module();
	if(pModule==NULL) return 0;

	memset(py_func,0,64);
	strcpy(py_func,"pci_config_space_read");
	argv[0].type = C2P_ARG_TYPE_INT;
	argv[0].value =(void*)&addr;
	argv[1].type = C2P_ARG_TYPE_INT;
	argv[1].value =(void*)&len;
	param.argc = 2;
	param.argv=argv;
	
	pResult=c_call_py(pModule,py_func,&param);
	return PyInt_AsLong(pResult);
}

edu_c2p_pci.h

#ifndef __EDU_C2P_PCI_H__
#define __EDU_C2P_PCI_H__

uint32_t edu_c2p_pci_configure_space_read(uint32_t addr, int len);
#endif

Put all the code at the same folder with edu.c(qemu/hw/misc).

4. add edu to ARM model -- qemu/default-configs/arm-softmmu.mak

diff --git a/default-configs/arm-softmmu.mak b/default-configs/arm-softmmu.mak
index 613d19a..a1a5cba 100644
--- a/default-configs/arm-softmmu.mak
+++ b/default-configs/arm-softmmu.mak
@@ -3,6 +3,7 @@
 CONFIG_PCI=y
 CONFIG_PCI_DEVICES=y
 CONFIG_PCI_TESTDEV=y
+CONFIG_EDU=y
 CONFIG_VGA=y
 CONFIG_NAND=y
 CONFIG_ECC=y

5. add all code into Makefile -- qemu/hw/misc/Makefile.objs

diff --git a/hw/misc/Makefile.objs b/hw/misc/Makefile.objs
index c71e07a..724ecc9 100644
--- a/hw/misc/Makefile.objs
+++ b/hw/misc/Makefile.objs
@@ -7,6 +7,9 @@ common-obj-$(CONFIG_SGA) += sga.o
 common-obj-$(CONFIG_ISA_TESTDEV) += pc-testdev.o
 common-obj-$(CONFIG_PCI_TESTDEV) += pci-testdev.o
 common-obj-$(CONFIG_EDU) += edu.o
+common-obj-$(CONFIG_EDU) += c-call-python.o
+common-obj-$(CONFIG_EDU) += edu_c2p.o
+
 common-obj-$(CONFIG_PCA9552) += pca9552.o
 
 common-obj-y += unimp.o

6. add python lib into the Makefile--qemu/configure

chunxie@chunxie-VirtualBox:~/qemu/qemu$ git diff configure | tee diff.txt
diff --git a/configure b/configure
index 5b183c2..85cf8de 100755
--- a/configure
+++ b/configure
@@ -972,6 +972,8 @@ for opt do
   ;;
   --python=*) python="$optarg"
   ;;
+  --python_lib=*) python_lib="$optarg"
+  ;;
   --gcov=*) gcov_tool="$optarg"
   ;;
   --smbd=*) smbd="$optarg"
@@ -2779,7 +2781,9 @@ if test "$gtk" != "no"; then
     fi
 fi
 
-
+if test "$python_lib" = "yes"; then
+	libs_softmmu="$libs_softmmu -lpython2.7"
+fi
 ##########################################
 # GNUTLS probe

OK, Now we can build and install QEMU. Refer to my previous blog about it.

7. Add python model : qemu_pci_config_space.py

import sys

edu_config_space={0x0:0x11e81234, 0x4:0,0x8:0x00ff0010, 0xc:0x00,0x10:0xFFF00000,0x14:0,0x18:0,0x1c:0,0x20:0,0x24:0,0x30:0}
def pci_config_space_read(addr,len):
	print("python:addr is %x, len is %d" % (addr,len))
	byte_offset=addr % 4
	print("python:byte offset is %d" % byte_offset)
	if byte_offset > 0:
		reg_offset = addr/4 
		reg_offset *= 4
	else:
		reg_offset = addr
	print("python:reg_offset is 0x%x" % reg_offset)
	mask = 0xFFFFFFFF
	mask = mask >> ((4-len)*8)
	print("python:len mask is 0x%x" % mask) 
	value = edu_config_space[reg_offset]
	value = value >> (byte_offset*8)
	value = value & mask
	print("python:value is 0x%x" % value)
	return value

if __name__ == "__main__":
	pci_config_space_read(0,2)
	pci_config_space_read(2,2)
	pci_config_space_read(0xb,1)
	pci_config_space_read(0xa,2)

Now, we can try it.

chunxie@chunxie-VirtualBox:~/uboot-mainline/u-boot$ qemu-system-arm -M virt-2.7 -nographic -device edu -m 512 -kernel u-boot -monitor telnet:127.0.0.1:4444,server,nowait
pci_register_bar
pci_register_bar
pci_register_bar
efi-virtio.rom: ROM id 1af41000 / PCI id 1af41000
pci_register_bar
pci_register_bar


U-Boot 2019.07-rc1-00456-g82da478 (May 14 2019 - 18:10:43 +0800)

DRAM:  512 MiB
WARNING: Caches not enabled
Flash: 128 MiB
*** Warning - bad CRC, using default environment

In:    pl011@9000000
Out:   pl011@9000000
Err:   pl011@9000000
Net:   No ethernet found.
pci_default_read_config:PCI dev is gpex-root,addr is 0x0,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0xe,val is 0x1
pci_default_read_config:PCI dev is gpex-root,addr is 0x2,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0x8,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x2c,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x0,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xe,val is 0x1
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x2,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x8,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x2c,val is 0x4
python:addr is 0, len is 2
python:byte offset is 0
python:reg_offset is 0x0
python:len mask is 0xffff
python:value is 0x1234
edu_pci_read_config, PCI device is edu, address is 0x0,val is 0x1234
python:addr is e, len is 1
python:byte offset is 2
python:reg_offset is 0xc
python:len mask is 0xff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0xe,val is 0x0
python:addr is 2, len is 2
python:byte offset is 2
python:reg_offset is 0x0
python:len mask is 0xffff
python:value is 0x11e8
edu_pci_read_config, PCI device is edu, address is 0x2,val is 0x11e8
python:addr is 8, len is 4
python:byte offset is 0
python:reg_offset is 0x8
python:len mask is 0xffffffff
python:value is 0xff0010
edu_pci_read_config, PCI device is edu, address is 0x8,val is 0xff0010
python:addr is 2c, len is 4
python:byte offset is 0
python:reg_offset is 0x2c
python:len mask is 0xffffffff
edu_pci_read_config, PCI device is edu, address is 0x2c,val is 0xffffffff
pci_default_read_config:PCI dev is gpex-root,addr is 0xa,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0x4,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0x10,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x14,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x18,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x1c,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x20,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0x24,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0xe,val is 0x1
pci_default_read_config:PCI dev is gpex-root,addr is 0x30,val is 0x4
pci_default_read_config:PCI dev is gpex-root,addr is 0xa,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xa,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x4,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x10,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x14,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x18,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x1c,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x20,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x24,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xe,val is 0x1
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x30,val is 0x4
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xa,val is 0x2
python:addr is a, len is 2
python:byte offset is 2
python:reg_offset is 0x8
python:len mask is 0xffff
python:value is 0xff
edu_pci_read_config, PCI device is edu, address is 0xa,val is 0xff
python:addr is 4, len is 2
python:byte offset is 0
python:reg_offset is 0x4
python:len mask is 0xffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x4,val is 0x0
python:addr is 10, len is 4
python:byte offset is 0
python:reg_offset is 0x10
python:len mask is 0xffffffff
python:value is 0xfff00000
edu_pci_read_config, PCI device is edu, address is 0x10,val is 0xfff00000
python:addr is 14, len is 4
python:byte offset is 0
python:reg_offset is 0x14
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x14,val is 0x0
python:addr is 18, len is 4
python:byte offset is 0
python:reg_offset is 0x18
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x18,val is 0x0
python:addr is 1c, len is 4
python:byte offset is 0
python:reg_offset is 0x1c
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x1c,val is 0x0
python:addr is 20, len is 4
python:byte offset is 0
python:reg_offset is 0x20
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x20,val is 0x0
python:addr is 24, len is 4
python:byte offset is 0
python:reg_offset is 0x24
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x24,val is 0x0
python:addr is e, len is 1
python:byte offset is 2
python:reg_offset is 0xc
python:len mask is 0xff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0xe,val is 0x0
python:addr is 30, len is 4
python:byte offset is 0
python:reg_offset is 0x30
python:len mask is 0xffffffff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0x30,val is 0x0
python:addr is a, len is 2
python:byte offset is 2
python:reg_offset is 0x8
python:len mask is 0xffff
python:value is 0xff
edu_pci_read_config, PCI device is edu, address is 0xa,val is 0xff
Hit any key to stop autoboot:  0 
=> pci
Scanning PCI devices on bus 0
BusDevFun  VendorId   DeviceId   Device Class       Sub-Class
_____________________________________________________________
00.00.00   pci_default_read_config:PCI dev is gpex-root,addr is 0x0,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0x2,val is 0x2
pci_default_read_config:PCI dev is gpex-root,addr is 0xb,val is 0x1
pci_default_read_config:PCI dev is gpex-root,addr is 0xa,val is 0x1
0x1b36     0x0008     Bridge device           0x00
00.01.00   pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x0,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0x2,val is 0x2
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xb,val is 0x1
pci_default_read_config:PCI dev is virtio-net-pci,addr is 0xa,val is 0x1
0x1af4     0x1000     Network controller      0x00
00.02.00   python:addr is 0, len is 2
python:byte offset is 0
python:reg_offset is 0x0
python:len mask is 0xffff
python:value is 0x1234
edu_pci_read_config, PCI device is edu, address is 0x0,val is 0x1234
python:addr is 2, len is 2
python:byte offset is 2
python:reg_offset is 0x0
python:len mask is 0xffff
python:value is 0x11e8
edu_pci_read_config, PCI device is edu, address is 0x2,val is 0x11e8
python:addr is b, len is 1
python:byte offset is 3
python:reg_offset is 0x8
python:len mask is 0xff
python:value is 0x0
edu_pci_read_config, PCI device is edu, address is 0xb,val is 0x0
python:addr is a, len is 1
python:byte offset is 2
python:reg_offset is 0x8
python:len mask is 0xff
python:value is 0xff
edu_pci_read_config, PCI device is edu, address is 0xa,val is 0xff
0x1234     0x11e8     Build before PCI Rev2.0 0xff
=>

From the log, you can see python model was accessed and it print out log. So it works.

We can also check the device status from monitor:

chunxie@chunxie-VirtualBox:~/uboot-mainline/u-boot$ telnet 127.0.0.1 4444
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
QEMU 4.0.50 monitor - type 'help' for more information
(qemu) info pci
  Bus  0, device   0, function 0:
    Host bridge: PCI device 1b36:0008
      PCI subsystem 1af4:1100
      id ""
  Bus  0, device   1, function 0:
    Ethernet controller: PCI device 1af4:1000
      PCI subsystem 1af4:0001
      IRQ 0.
      BAR0: I/O at 0x1000 [0x101f].
      BAR1: 32 bit memory at 0x10000000 [0x10000fff].
      BAR4: 64 bit prefetchable memory at 0x10800000 [0x10ffffff].
      BAR6: 32 bit memory at 0xffffffffffffffff [0x0003fffe].
      id ""
  Bus  0, device   2, function 0:
    Class 0255: PCI device 1234:11e8
      PCI subsystem ffff:ffff
      IRQ 0.
      BAR0: 32 bit memory at 0x11100000 [0x111fffff].
      id ""
(qemu)

Look, we can see PCI device 1234:11e8, that is our python device. Do you still remember we did a copy from python model to C model of configure space?

OK, that's all. End

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值