diff options
| author | guo <[email protected]> | 2022-12-03 12:07:44 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2022-12-03 12:07:44 +0800 |
| commit | ecf2d8215961ca7c539132b62a72df6072cc3e14 (patch) | |
| tree | 59005f0f74f2593c0435059264f624c862a89f55 /components | |
| parent | aaf5462c6d4ae9466a0a8cd2a4bb00cfe1fef2f2 (diff) | |
sync branch rt-smart. (#6641)
* Synchronize the code of the rt mart branch to the master branch.
* TTY device
* Add lwP code from rt-smart
* Add vnode in DFS, but DFS will be re-write for rt-smart
* There are three libcpu for rt-smart:
* arm/cortex-a, arm/aarch64
* riscv64
Co-authored-by: Rbb666 <[email protected]>
Co-authored-by: zhkag <[email protected]>
Diffstat (limited to 'components')
199 files changed, 40922 insertions, 3722 deletions
diff --git a/components/dfs/Kconfig b/components/dfs/Kconfig index 28cb892e3e..527c3ba77a 100644 --- a/components/dfs/Kconfig +++ b/components/dfs/Kconfig @@ -135,17 +135,26 @@ if RT_USING_DFS config RT_USING_DFS_DEVFS bool "Using devfs for device objects" - default n + default y config RT_USING_DFS_ROMFS bool "Enable ReadOnly file system on flash" default n + config RT_USING_DFS_CROMFS + bool "Enable readonly compressed file system on flash" + default n + # select PKG_USING_ZLIB + config RT_USING_DFS_RAMFS bool "Enable RAM file system" select RT_USING_MEMHEAP default n + config RT_USING_DFS_TMPFS + bool "Enable TMP file system" + default n + config RT_USING_DFS_NFS bool "Using NFS v3 client file system" depends on RT_USING_LWIP diff --git a/components/dfs/filesystems/devfs/devfs.c b/components/dfs/filesystems/devfs/devfs.c index 8e58fb9f53..5181a95391 100644 --- a/components/dfs/filesystems/devfs/devfs.c +++ b/components/dfs/filesystems/devfs/devfs.c @@ -7,7 +7,7 @@ * Date Author Notes * 2018-02-11 Bernard Ignore O_CREAT flag in open. */ - +#include <rthw.h> #include <rtthread.h> #include <rtdevice.h> @@ -37,7 +37,7 @@ int dfs_device_fs_ioctl(struct dfs_fd *file, int cmd, void *args) RT_ASSERT(file != RT_NULL); /* get device handler */ - dev_id = (rt_device_t)file->data; + dev_id = (rt_device_t)file->vnode->data; RT_ASSERT(dev_id != RT_NULL); /* close device handler */ @@ -56,7 +56,7 @@ int dfs_device_fs_read(struct dfs_fd *file, void *buf, size_t count) RT_ASSERT(file != RT_NULL); /* get device handler */ - dev_id = (rt_device_t)file->data; + dev_id = (rt_device_t)file->vnode->data; RT_ASSERT(dev_id != RT_NULL); /* read device data */ @@ -74,7 +74,7 @@ int dfs_device_fs_write(struct dfs_fd *file, const void *buf, size_t count) RT_ASSERT(file != RT_NULL); /* get device handler */ - dev_id = (rt_device_t)file->data; + dev_id = (rt_device_t)file->vnode->data; RT_ASSERT(dev_id != RT_NULL); /* read device data */ @@ -90,12 +90,18 @@ int dfs_device_fs_close(struct dfs_fd *file) rt_device_t dev_id; RT_ASSERT(file != RT_NULL); + RT_ASSERT(file->vnode->ref_count > 0); + + if (file->vnode->ref_count > 1) + { + return 0; + } - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) { struct device_dirent *root_dirent; - root_dirent = (struct device_dirent *)file->data; + root_dirent = (struct device_dirent *)file->vnode->data; RT_ASSERT(root_dirent != RT_NULL); /* release dirent */ @@ -104,14 +110,14 @@ int dfs_device_fs_close(struct dfs_fd *file) } /* get device handler */ - dev_id = (rt_device_t)file->data; + dev_id = (rt_device_t)file->vnode->data; RT_ASSERT(dev_id != RT_NULL); /* close device handler */ result = rt_device_close(dev_id); if (result == RT_EOK) { - file->data = RT_NULL; + file->vnode->data = RT_NULL; return RT_EOK; } @@ -124,8 +130,14 @@ int dfs_device_fs_open(struct dfs_fd *file) rt_err_t result; rt_device_t device; + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + file->pos = 0; + return 0; + } /* open root directory */ - if ((file->path[0] == '/') && (file->path[1] == '\0') && + if ((file->vnode->path[0] == '/') && (file->vnode->path[1] == '\0') && (file->flags & O_DIRECTORY)) { struct rt_object *object; @@ -174,29 +186,45 @@ int dfs_device_fs_open(struct dfs_fd *file) } /* set data */ - file->data = root_dirent; + file->vnode->data = root_dirent; return RT_EOK; } +#ifdef RT_USING_DEV_BUS + else if (file->flags & O_CREAT) + { + if (!(file->flags & O_DIRECTORY)) + { + return -ENOSYS; + } + /* regester bus device */ + if (rt_device_bus_create(&file->vnode->path[1], 0) == RT_NULL) + { + return -EEXIST; + } + } +#endif - device = rt_device_find(&file->path[1]); + device = rt_device_find(&file->vnode->path[1]); if (device == RT_NULL) + { return -ENODEV; + } #ifdef RT_USING_POSIX_DEVIO if (device->fops) { /* use device fops */ - file->fops = device->fops; - file->data = (void *)device; + file->vnode->fops = device->fops; + file->vnode->data = (void *)device; /* use fops */ - if (file->fops->open) + if (file->vnode->fops->open) { - result = file->fops->open(file); + result = file->vnode->fops->open(file); if (result == RT_EOK || result == -RT_ENOSYS) { - file->type = FT_DEVICE; + file->vnode->type = FT_DEVICE; return 0; } } @@ -207,17 +235,36 @@ int dfs_device_fs_open(struct dfs_fd *file) result = rt_device_open(device, RT_DEVICE_OFLAG_RDWR); if (result == RT_EOK || result == -RT_ENOSYS) { - file->data = device; - file->type = FT_DEVICE; + file->vnode->data = device; + file->vnode->type = FT_DEVICE; return RT_EOK; } } - file->data = RT_NULL; + file->vnode->data = RT_NULL; /* open device failed. */ return -EIO; } +int dfs_device_fs_unlink(struct dfs_filesystem *fs, const char *path) +{ +#ifdef RT_USING_DEV_BUS + rt_device_t dev_id; + + dev_id = rt_device_find(&path[1]); + if (dev_id == RT_NULL) + { + return -1; + } + if (dev_id->type != RT_Device_Class_Bus) + { + return -1; + } + rt_device_bus_destroy(dev_id); +#endif + return RT_EOK; +} + int dfs_device_fs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st) { /* stat root directory */ @@ -253,6 +300,8 @@ int dfs_device_fs_stat(struct dfs_filesystem *fs, const char *path, struct stat st->st_mode |= S_IFBLK; else if (dev_id->type == RT_Device_Class_Pipe) st->st_mode |= S_IFIFO; + else if (dev_id->type == RT_Device_Class_Bus) + st->st_mode |= S_IFDIR; else st->st_mode |= S_IFREG; @@ -273,7 +322,7 @@ int dfs_device_fs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t co struct dirent *d; struct device_dirent *root_dirent; - root_dirent = (struct device_dirent *)file->data; + root_dirent = (struct device_dirent *)file->vnode->data; RT_ASSERT(root_dirent != RT_NULL); /* make integer count */ @@ -287,7 +336,14 @@ int dfs_device_fs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t co object = (rt_object_t)root_dirent->devices[root_dirent->read_index + index]; d = dirp + index; - d->d_type = DT_REG; + if ((((rt_device_t)object)->type) == RT_Device_Class_Bus) + { + d->d_type = DT_DIR; + } + else + { + d->d_type = DT_REG; + } d->d_namlen = RT_NAME_MAX; d->d_reclen = (rt_uint16_t)sizeof(struct dirent); rt_strncpy(d->d_name, object->name, RT_NAME_MAX); @@ -327,7 +383,7 @@ static const struct dfs_filesystem_ops _device_fs = RT_NULL, /*unmount*/ RT_NULL, /*mkfs*/ RT_NULL, /*statfs*/ - RT_NULL, /*unlink*/ + dfs_device_fs_unlink, dfs_device_fs_stat, RT_NULL, /*rename*/ }; diff --git a/components/dfs/filesystems/elmfat/dfs_elm.c b/components/dfs/filesystems/elmfat/dfs_elm.c index adcb3e0ac5..f32ab53285 100644 --- a/components/dfs/filesystems/elmfat/dfs_elm.c +++ b/components/dfs/filesystems/elmfat/dfs_elm.c @@ -191,7 +191,7 @@ int dfs_elm_unmount(struct dfs_filesystem *fs) return RT_EOK; } -int dfs_elm_mkfs(rt_device_t dev_id) +int dfs_elm_mkfs(rt_device_t dev_id, const char *fs_name) { #define FSM_STATUS_INIT 0 #define FSM_STATUS_USE_TEMP_DRIVER 1 @@ -330,9 +330,21 @@ int dfs_elm_open(struct dfs_fd *file) #if (FF_VOLUMES > 1) int vol; - struct dfs_filesystem *fs = (struct dfs_filesystem *)file->data; + struct dfs_filesystem *fs = file->vnode->fs; extern int elm_get_vol(FATFS * fat); + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + if (file->vnode->type == FT_DIRECTORY + && !(file->flags & O_DIRECTORY)) + { + return -ENOENT; + } + file->pos = 0; + return 0; + } + if (fs == NULL) return -ENOENT; @@ -344,9 +356,9 @@ int dfs_elm_open(struct dfs_fd *file) if (drivers_fn == RT_NULL) return -ENOMEM; - rt_snprintf(drivers_fn, 256, "%d:%s", vol, file->path); + rt_snprintf(drivers_fn, 256, "%d:%s", vol, file->vnode->path); #else - drivers_fn = file->path; + drivers_fn = file->vnode->path; #endif if (file->flags & O_DIRECTORY) @@ -423,7 +435,8 @@ int dfs_elm_open(struct dfs_fd *file) if (result == FR_OK) { file->pos = fd->fptr; - file->size = f_size(fd); + file->vnode->size = f_size(fd); + file->vnode->type = FT_REGULAR; file->data = fd; if (file->flags & O_APPEND) @@ -448,10 +461,15 @@ int dfs_elm_close(struct dfs_fd *file) { FRESULT result; + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return 0; + } result = FR_OK; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) { - DIR *dir; + DIR *dir = RT_NULL; dir = (DIR *)(file->data); RT_ASSERT(dir != RT_NULL); @@ -459,9 +477,9 @@ int dfs_elm_close(struct dfs_fd *file) /* release memory */ rt_free(dir); } - else if (file->type == FT_REGULAR) + else if (file->vnode->type == FT_REGULAR) { - FIL *fd; + FIL *fd = RT_NULL; fd = (FIL *)(file->data); RT_ASSERT(fd != RT_NULL); @@ -504,6 +522,10 @@ int dfs_elm_ioctl(struct dfs_fd *file, int cmd, void *args) fd->fptr = fptr; return elm_result_to_dfs(result); } + case F_GETLK: + return 0; + case F_SETLK: + return 0; } return -ENOSYS; } @@ -514,7 +536,7 @@ int dfs_elm_read(struct dfs_fd *file, void *buf, size_t len) FRESULT result; UINT byte_read; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) { return -EISDIR; } @@ -537,7 +559,7 @@ int dfs_elm_write(struct dfs_fd *file, const void *buf, size_t len) FRESULT result; UINT byte_write; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) { return -EISDIR; } @@ -548,7 +570,7 @@ int dfs_elm_write(struct dfs_fd *file, const void *buf, size_t len) result = f_write(fd, buf, len, &byte_write); /* update position and file size */ file->pos = fd->fptr; - file->size = f_size(fd); + file->vnode->size = f_size(fd); if (result == FR_OK) return byte_write; @@ -567,10 +589,10 @@ int dfs_elm_flush(struct dfs_fd *file) return elm_result_to_dfs(result); } -int dfs_elm_lseek(struct dfs_fd *file, rt_off_t offset) +int dfs_elm_lseek(struct dfs_fd *file, off_t offset) { FRESULT result = FR_OK; - if (file->type == FT_REGULAR) + if (file->vnode->type == FT_REGULAR) { FIL *fd; @@ -586,10 +608,10 @@ int dfs_elm_lseek(struct dfs_fd *file, rt_off_t offset) return fd->fptr; } } - else if (file->type == FT_DIRECTORY) + else if (file->vnode->type == FT_DIRECTORY) { /* which is a directory */ - DIR *dir; + DIR *dir = RT_NULL; dir = (DIR *)(file->data); RT_ASSERT(dir != RT_NULL); diff --git a/components/dfs/filesystems/nfs/dfs_nfs.c b/components/dfs/filesystems/nfs/dfs_nfs.c index 284d8d42af..5b16875c29 100644 --- a/components/dfs/filesystems/nfs/dfs_nfs.c +++ b/components/dfs/filesystems/nfs/dfs_nfs.c @@ -559,12 +559,11 @@ int nfs_read(struct dfs_fd *file, void *buf, size_t count) nfs_file *fd; nfs_filesystem *nfs; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) return -EISDIR; - - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->vnode->data)); nfs = (struct nfs_filesystem *)(dfs_nfs->data); fd = (nfs_file *)(nfs->data); RT_ASSERT(fd != NULL); @@ -629,11 +628,11 @@ int nfs_write(struct dfs_fd *file, const void *buf, size_t count) nfs_file *fd; nfs_filesystem *nfs; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) return -EISDIR; - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->vnode->data)); nfs = (struct nfs_filesystem *)(dfs_nfs->data); fd = (nfs_file *)(nfs->data); RT_ASSERT(fd != NULL); @@ -676,11 +675,10 @@ int nfs_write(struct dfs_fd *file, const void *buf, size_t count) file->pos = fd->offset; /* update file size */ if (fd->size < fd->offset) fd->size = fd->offset; - file->size = fd->size; + file->vnode->size = fd->size; } xdr_free((xdrproc_t)xdr_WRITE3res, (char *)&res); - } - while (count > 0); + } while (count > 0); xdr_free((xdrproc_t)xdr_WRITE3res, (char *)&res); @@ -692,11 +690,11 @@ int nfs_lseek(struct dfs_fd *file, off_t offset) nfs_file *fd; nfs_filesystem *nfs; - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) return -EISDIR; - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->vnode->data)); nfs = (struct nfs_filesystem *)(dfs_nfs->data); fd = (nfs_file *)(nfs->data); RT_ASSERT(fd != NULL); @@ -714,11 +712,18 @@ int nfs_lseek(struct dfs_fd *file, off_t offset) int nfs_close(struct dfs_fd *file) { nfs_filesystem *nfs; - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->vnode->data)); + + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return 0; + } + nfs = (struct nfs_filesystem *)(dfs_nfs->data); - if (file->type == FT_DIRECTORY) + if (file->vnode->type == FT_DIRECTORY) { struct nfs_dir *dir; @@ -727,7 +732,7 @@ int nfs_close(struct dfs_fd *file) xdr_free((xdrproc_t)xdr_READDIR3res, (char *)&dir->res); rt_free(dir); } - else if (file->type == FT_REGULAR) + else if (file->vnode->type == FT_REGULAR) { struct nfs_file *fd; @@ -744,24 +749,42 @@ int nfs_close(struct dfs_fd *file) int nfs_open(struct dfs_fd *file) { nfs_filesystem *nfs; - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = file->vnode->fs; nfs = (struct nfs_filesystem *)(dfs_nfs->data); RT_ASSERT(nfs != NULL); + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + if (file->vnode->type == FT_DIRECTORY + && !(file->flags & O_DIRECTORY)) + { + return -ENOENT; + } + file->pos = 0; + return 0; + } + if (file->flags & O_DIRECTORY) { nfs_dir *dir; if (file->flags & O_CREAT) { - if (nfs_mkdir(nfs, file->path, 0755) < 0) + if (nfs_mkdir(nfs, file->vnode->path, 0755) < 0) + { return -EAGAIN; + } } /* open directory */ - dir = nfs_opendir(nfs, file->path); - if (dir == NULL) return -ENOENT; + dir = nfs_opendir(nfs, file->vnode->path); + if (dir == NULL) + { + return -ENOENT; + } + file->vnode->type = FT_DIRECTORY; nfs->data = dir; } else @@ -772,8 +795,10 @@ int nfs_open(struct dfs_fd *file) /* create file */ if (file->flags & O_CREAT) { - if (nfs_create(nfs, file->path, 0664) < 0) + if (nfs_create(nfs, file->vnode->path, 0664) < 0) + { return -EAGAIN; + } } /* open file (get file handle ) */ @@ -781,7 +806,7 @@ int nfs_open(struct dfs_fd *file) if (fp == NULL) return -ENOMEM; - handle = get_handle(nfs, file->path); + handle = get_handle(nfs, file->vnode->path); if (handle == NULL) { rt_free(fp); @@ -805,7 +830,8 @@ int nfs_open(struct dfs_fd *file) /* set private file */ nfs->data = fp; - file->size = fp->size; + file->vnode->size = fp->size; + file->vnode->type = FT_REGULAR; } return 0; @@ -1085,9 +1111,8 @@ int nfs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t count) nfs_filesystem *nfs; char *name; - - RT_ASSERT(file->data != NULL); - struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->data)); + RT_ASSERT(file->vnode->data != NULL); + struct dfs_filesystem *dfs_nfs = ((struct dfs_filesystem *)(file->vnode->data)); nfs = (struct nfs_filesystem *)(dfs_nfs->data); dir = (nfs_dir *)(nfs->data); RT_ASSERT(dir != NULL); diff --git a/components/dfs/filesystems/nfs/rpc/types.h b/components/dfs/filesystems/nfs/rpc/types.h index 043bfab896..9c4ccd3838 100644 --- a/components/dfs/filesystems/nfs/rpc/types.h +++ b/components/dfs/filesystems/nfs/rpc/types.h @@ -47,7 +47,15 @@ #include <string.h> #include <stdint.h> + +#ifndef RT_USING_MINILIBC +typedef unsigned int u_int; +typedef unsigned char u_char; +typedef unsigned long u_long; +#else #include <sys/types.h> +#include <stdint.h> +#endif typedef long long int64_t; typedef unsigned long long uint64_t; @@ -55,6 +63,14 @@ typedef unsigned long long uint64_t; typedef int bool_t; typedef int enum_t; +#if !defined(RT_USING_NEWLIB) && !defined(RT_USING_MUSL) +typedef unsigned long dev_t; +#endif + +#if !defined(RT_USING_NEWLIB) && !defined(RT_USING_MINILIBC) && !defined(RT_USING_MUSL) +typedef rt_int32_t ssize_t; +#endif + /* This needs to be changed to uint32_t in the future */ typedef unsigned long rpcprog_t; typedef unsigned long rpcvers_t; diff --git a/components/dfs/filesystems/ramfs/dfs_ramfs.c b/components/dfs/filesystems/ramfs/dfs_ramfs.c index 781626633f..f31b76f0e2 100644 --- a/components/dfs/filesystems/ramfs/dfs_ramfs.c +++ b/components/dfs/filesystems/ramfs/dfs_ramfs.c @@ -97,13 +97,13 @@ int dfs_ramfs_read(struct dfs_fd *file, void *buf, size_t count) rt_size_t length; struct ramfs_dirent *dirent; - dirent = (struct ramfs_dirent *)file->data; + dirent = (struct ramfs_dirent *)file->vnode->data; RT_ASSERT(dirent != NULL); - if (count < file->size - file->pos) + if (count < file->vnode->size - file->pos) length = count; else - length = file->size - file->pos; + length = file->vnode->size - file->pos; if (length > 0) rt_memcpy(buf, &(dirent->data[file->pos]), length); @@ -119,13 +119,13 @@ int dfs_ramfs_write(struct dfs_fd *fd, const void *buf, size_t count) struct ramfs_dirent *dirent; struct dfs_ramfs *ramfs; - dirent = (struct ramfs_dirent *)fd->data; + dirent = (struct ramfs_dirent *)fd->vnode->data; RT_ASSERT(dirent != NULL); ramfs = dirent->fs; RT_ASSERT(ramfs != NULL); - if (count + fd->pos > fd->size) + if (count + fd->pos > fd->vnode->size) { rt_uint8_t *ptr; ptr = rt_memheap_realloc(&(ramfs->memheap), dirent->data, fd->pos + count); @@ -139,7 +139,7 @@ int dfs_ramfs_write(struct dfs_fd *fd, const void *buf, size_t count) /* update dirent and file size */ dirent->data = ptr; dirent->size = fd->pos + count; - fd->size = dirent->size; + fd->vnode->size = dirent->size; } if (count > 0) @@ -153,7 +153,7 @@ int dfs_ramfs_write(struct dfs_fd *fd, const void *buf, size_t count) int dfs_ramfs_lseek(struct dfs_fd *file, off_t offset) { - if (offset <= (off_t)file->size) + if (offset <= (off_t)file->vnode->size) { file->pos = offset; @@ -165,7 +165,13 @@ int dfs_ramfs_lseek(struct dfs_fd *file, off_t offset) int dfs_ramfs_close(struct dfs_fd *file) { - file->data = NULL; + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return 0; + } + + file->vnode->data = NULL; return RT_EOK; } @@ -177,7 +183,19 @@ int dfs_ramfs_open(struct dfs_fd *file) struct ramfs_dirent *dirent; struct dfs_filesystem *fs; - fs = (struct dfs_filesystem *)file->data; + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + if (file->vnode->type == FT_DIRECTORY + && !(file->flags & O_DIRECTORY)) + { + return -ENOENT; + } + file->pos = 0; + return 0; + } + + fs = file->vnode->fs; ramfs = (struct dfs_ramfs *)fs->data; RT_ASSERT(ramfs != NULL); @@ -190,7 +208,7 @@ int dfs_ramfs_open(struct dfs_fd *file) } /* open directory */ - dirent = dfs_ramfs_lookup(ramfs, file->path, &size); + dirent = dfs_ramfs_lookup(ramfs, file->vnode->path, &size); if (dirent == NULL) return -ENOENT; if (dirent == &(ramfs->root)) /* it's root directory */ @@ -200,10 +218,11 @@ int dfs_ramfs_open(struct dfs_fd *file) return -ENOENT; } } + file->vnode->type = FT_DIRECTORY; } else { - dirent = dfs_ramfs_lookup(ramfs, file->path, &size); + dirent = dfs_ramfs_lookup(ramfs, file->vnode->path, &size); if (dirent == &(ramfs->root)) /* it's root directory */ { return -ENOENT; @@ -225,15 +244,18 @@ int dfs_ramfs_open(struct dfs_fd *file) } /* remove '/' separator */ - name_ptr = file->path; + name_ptr = file->vnode->path; while (*name_ptr == '/' && *name_ptr) - name_ptr ++; + { + name_ptr++; + } strncpy(dirent->name, name_ptr, RAMFS_NAME_MAX); rt_list_init(&(dirent->list)); dirent->data = NULL; dirent->size = 0; dirent->fs = ramfs; + file->vnode->type = FT_DIRECTORY; /* add to the root directory */ rt_list_insert_after(&(ramfs->root.list), &(dirent->list)); @@ -256,12 +278,16 @@ int dfs_ramfs_open(struct dfs_fd *file) } } - file->data = dirent; - file->size = dirent->size; + file->vnode->data = dirent; + file->vnode->size = dirent->size; if (file->flags & O_APPEND) - file->pos = file->size; + { + file->pos = file->vnode->size; + } else + { file->pos = 0; + } return 0; } @@ -299,7 +325,7 @@ int dfs_ramfs_getdents(struct dfs_fd *file, struct ramfs_dirent *dirent; struct dfs_ramfs *ramfs; - dirent = (struct ramfs_dirent *)file->data; + dirent = (struct ramfs_dirent *)file->vnode->data; ramfs = dirent->fs; RT_ASSERT(ramfs != RT_NULL); diff --git a/components/dfs/filesystems/romfs/dfs_romfs.c b/components/dfs/filesystems/romfs/dfs_romfs.c index 981e5f2ee8..bdce2a8bb2 100644 --- a/components/dfs/filesystems/romfs/dfs_romfs.c +++ b/components/dfs/filesystems/romfs/dfs_romfs.c @@ -34,7 +34,28 @@ int dfs_romfs_unmount(struct dfs_filesystem *fs) int dfs_romfs_ioctl(struct dfs_fd *file, int cmd, void *args) { - return -EIO; + int ret = RT_EOK; + struct romfs_dirent *dirent; + + dirent = (struct romfs_dirent *)file->vnode->data; + RT_ASSERT(dirent != NULL); + + switch (cmd) + { + case RT_FIOGETADDR: + { + *(rt_ubase_t*)args = (rt_ubase_t)dirent->data; + break; + } + case RT_FIOFTRUNCATE: + { + break; + } + default: + ret = -RT_EINVAL; + break; + } + return ret; } rt_inline int check_dirent(struct romfs_dirent *dirent) @@ -112,9 +133,6 @@ struct romfs_dirent *dfs_romfs_lookup(struct romfs_dirent *root_dirent, const ch else { /* return file dirent */ - if (subpath != NULL) - break; /* not the end of path */ - return &dirent[index]; } } @@ -133,7 +151,7 @@ int dfs_romfs_read(struct dfs_fd *file, void *buf, size_t count) rt_size_t length; struct romfs_dirent *dirent; - dirent = (struct romfs_dirent *)file->data; + dirent = (struct romfs_dirent *)file->vnode->data; RT_ASSERT(dirent != NULL); if (check_dirent(dirent) != 0) @@ -141,10 +159,10 @@ int dfs_romfs_read(struct dfs_fd *file, void *buf, size_t count) return -EIO; } - if (count < file->size - file->pos) + if (count < file->vnode->size - file->pos) length = count; else - length = file->size - file->pos; + length = file->vnode->size - file->pos; if (length > 0) rt_memcpy(buf, &(dirent->data[file->pos]), length); @@ -157,7 +175,7 @@ int dfs_romfs_read(struct dfs_fd *file, void *buf, size_t count) int dfs_romfs_lseek(struct dfs_fd *file, off_t offset) { - if (offset >= 0 && (rt_size_t)offset <= file->size) + if (offset <= file->vnode->size) { file->pos = offset; return file->pos; @@ -168,7 +186,12 @@ int dfs_romfs_lseek(struct dfs_fd *file, off_t offset) int dfs_romfs_close(struct dfs_fd *file) { - file->data = NULL; + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return RT_EOK; + } + file->vnode->data = NULL; return RT_EOK; } @@ -179,34 +202,63 @@ int dfs_romfs_open(struct dfs_fd *file) struct romfs_dirent *root_dirent; struct dfs_filesystem *fs; - fs = (struct dfs_filesystem *)file->data; + if (file->flags & (O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_RDWR)) + { + return -EINVAL; + } + + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + if (file->vnode->type == FT_DIRECTORY + && !(file->flags & O_DIRECTORY)) + { + return -ENOENT; + } + file->pos = 0; + return 0; + } + + fs = file->vnode->fs; root_dirent = (struct romfs_dirent *)fs->data; if (check_dirent(root_dirent) != 0) + { return -EIO; + } if (file->flags & (O_CREAT | O_WRONLY | O_APPEND | O_TRUNC | O_RDWR)) + { return -EINVAL; + } - dirent = dfs_romfs_lookup(root_dirent, file->path, &size); + dirent = dfs_romfs_lookup(root_dirent, file->vnode->path, &size); if (dirent == NULL) + { return -ENOENT; + } /* entry is a directory file type */ if (dirent->type == ROMFS_DIRENT_DIR) { if (!(file->flags & O_DIRECTORY)) + { return -ENOENT; + } + file->vnode->type = FT_DIRECTORY; } else { /* entry is a file, but open it as a directory */ if (file->flags & O_DIRECTORY) + { return -ENOENT; + } + file->vnode->type = FT_REGULAR; } - file->data = dirent; - file->size = size; + file->vnode->data = dirent; + file->vnode->size = size; file->pos = 0; return RT_EOK; @@ -222,7 +274,9 @@ int dfs_romfs_stat(struct dfs_filesystem *fs, const char *path, struct stat *st) dirent = dfs_romfs_lookup(root_dirent, path, &size); if (dirent == NULL) + { return -ENOENT; + } st->st_dev = 0; st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | @@ -248,9 +302,11 @@ int dfs_romfs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t count) struct dirent *d; struct romfs_dirent *dirent, *sub_dirent; - dirent = (struct romfs_dirent *)file->data; + dirent = (struct romfs_dirent *)file->vnode->data; if (check_dirent(dirent) != 0) + { return -EIO; + } RT_ASSERT(dirent->type == ROMFS_DIRENT_DIR); /* enter directory */ @@ -259,10 +315,12 @@ int dfs_romfs_getdents(struct dfs_fd *file, struct dirent *dirp, uint32_t count) /* make integer count */ count = (count / sizeof(struct dirent)); if (count == 0) + { return -EINVAL; + } index = 0; - for (index = 0; index < count && (rt_size_t)file->pos < file->size; index ++) + for (index = 0; index < count && file->pos < file->vnode->size; index++) { d = dirp + index; diff --git a/components/dfs/filesystems/tmpfs/SConscript b/components/dfs/filesystems/tmpfs/SConscript new file mode 100644 index 0000000000..c94a9bcff0 --- /dev/null +++ b/components/dfs/filesystems/tmpfs/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('Filesystem', src, depend = ['RT_USING_DFS', 'RT_USING_DFS_TMPFS'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/dfs/filesystems/tmpfs/dfs_tmpfs.c b/components/dfs/filesystems/tmpfs/dfs_tmpfs.c new file mode 100644 index 0000000000..a2eebe00f3 --- /dev/null +++ b/components/dfs/filesystems/tmpfs/dfs_tmpfs.c @@ -0,0 +1,657 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 flybreak the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <dfs.h> +#include <dfs_fs.h> +#include <dfs_file.h> + +#ifdef RT_USING_LWP +#include <lwp.h> +#include <lwp_user_mm.h> +#endif + +#include "dfs_tmpfs.h" + +#define DBG_TAG "tmpfs" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +static int _path_separate(const char *path, char *parent_path, char *file_name) +{ + const char *path_p, *path_q; + + RT_ASSERT(path[0] == '/'); + + file_name[0] = '\0'; + path_p = path_q = &path[1]; +__next_dir: + while (*path_q != '/' && *path_q != '\0') + { + path_q++; + } + if (path_q != path_p) /*sub dir*/ + { + if (*path_q != '\0') + { + path_q++; + path_p = path_q; + goto __next_dir; + } + else /* Last level dir */ + { + rt_memcpy(parent_path, path, path_p - path - 1); + parent_path[path_p - path - 1] = '\0'; + rt_memcpy(file_name, path_p, path_q - path_p); + file_name[path_q - path_p] = '\0'; + } + } + if (parent_path[0] == 0) + { + parent_path[0] = '/'; + parent_path[1] = '\0'; + } + LOG_D("parent_path: %s", parent_path); + LOG_D("file_name: %s", file_name); + + return 0; +} + +static int _get_subdir(const char *path, char *name) +{ + const char *subpath = path; + while (*subpath == '/' && *subpath) + subpath ++; + while (*subpath != '/' && *subpath) + { + *name = *subpath; + name ++; + subpath ++; + } + return 0; +} + +static int _free_subdir(struct tmpfs_file *dfile) +{ + struct tmpfs_file *file; + rt_list_t *list, *temp_list; + + RT_DEFINE_SPINLOCK(lock); + + RT_ASSERT(dfile->type == TMPFS_TYPE_DIR); + + rt_list_for_each_safe(list, temp_list, &dfile->subdirs) + { + file = rt_list_entry(list, struct tmpfs_file, sibling); + if (file->type == TMPFS_TYPE_DIR) + { + _free_subdir(file); + } + if (file->data != NULL) + { + /* TODO: fix for rt-smart */ + rt_free(file->data); + } + rt_hw_spin_lock(&lock); + rt_list_remove(&(file->sibling)); + rt_hw_spin_unlock(&lock); + rt_free(file); + } + return 0; +} + +int dfs_tmpfs_mount(struct dfs_filesystem *fs, + unsigned long rwflag, + const void *data) +{ + struct tmpfs_sb *superblock; + + superblock = rt_calloc(1, sizeof(struct tmpfs_sb)); + superblock->df_size = sizeof(struct tmpfs_sb); + superblock->magic = TMPFS_MAGIC; + rt_list_init(&superblock->sibling); + + superblock->root.name[0] = '/'; + superblock->root.sb = superblock; + superblock->root.type = TMPFS_TYPE_DIR; + rt_list_init(&superblock->root.sibling); + rt_list_init(&superblock->root.subdirs); + + fs->data = superblock; + + return RT_EOK; +} + +int dfs_tmpfs_unmount(struct dfs_filesystem *fs) +{ + struct tmpfs_sb *superblock; + + superblock = (struct tmpfs_sb *)fs->data; + RT_ASSERT(superblock != NULL); + + _free_subdir(&(superblock->root)); + rt_free(superblock); + + fs->data = NULL; + + return RT_EOK; +} + +int dfs_tmpfs_statfs(struct dfs_filesystem *fs, struct statfs *buf) +{ + struct tmpfs_sb *superblock; + + superblock = (struct tmpfs_sb *)fs->data; + RT_ASSERT(superblock != NULL); + RT_ASSERT(buf != NULL); + + buf->f_bsize = 512; + buf->f_blocks = (superblock->df_size + 511) / 512; + buf->f_bfree = 1; + + return RT_EOK; +} + +int dfs_tmpfs_ioctl(struct dfs_fd *file, int cmd, void *args) +{ + struct tmpfs_file *d_file; + struct tmpfs_sb *superblock; + + d_file = (struct tmpfs_file *)file->vnode->data; + RT_ASSERT(d_file != NULL); + + superblock = d_file->sb; + RT_ASSERT(superblock != NULL); + + switch (cmd) + { +#ifdef RT_USING_LWP + case RT_FIOMMAP2: + { + struct dfs_mmap2_args *mmap2 = (struct dfs_mmap2_args *)args; + if (mmap2) + { + if (mmap2->length > file->vnode->size) + { + return -RT_ENOMEM; + } + + LOG_D("tmpfile mmap ptr:%x , size:%d\n", d_file->data, mmap2->length); + mmap2->ret = lwp_map_user_phy(lwp_self(), RT_NULL, d_file->data, mmap2->length, 0); + } + return RT_EOK; + break; + } +#endif + default: + break; + } + return -EIO; +} + +struct tmpfs_file *dfs_tmpfs_lookup(struct tmpfs_sb *superblock, + const char *path, + rt_size_t *size) +{ + const char *subpath, *curpath, *filename = RT_NULL; + char subdir_name[TMPFS_NAME_MAX]; + struct tmpfs_file *file, *curfile; + rt_list_t *list; + + RT_DEFINE_SPINLOCK(lock); + + subpath = path; + while (*subpath == '/' && *subpath) + subpath ++; + if (! *subpath) /* is root directory */ + { + *size = 0; + return &(superblock->root); + } + + curpath = subpath; + curfile = &superblock->root; + +find_subpath: + while (*subpath != '/' && *subpath) + subpath ++; + + if (! *subpath) /* is last directory */ + filename = curpath; + else + subpath ++; /* skip '/' */ + + memset(subdir_name, 0, TMPFS_NAME_MAX); + _get_subdir(curpath, subdir_name); + + rt_hw_spin_lock(&lock); + + rt_list_for_each(list, &curfile->subdirs) + { + file = rt_list_entry(list, struct tmpfs_file, sibling); + if (filename) /* find file */ + { + if (rt_strcmp(file->name, filename) == 0) + { + *size = file->size; + + rt_hw_spin_unlock(&lock); + return file; + } + } + else if (rt_strcmp(file->name, subdir_name) == 0) + { + *size = file->size; + curpath = subpath; + curfile = file; + rt_hw_spin_unlock(&lock); + goto find_subpath; + } + } + rt_hw_spin_unlock(&lock); + /* not found */ + return NULL; +} + +int dfs_tmpfs_read(struct dfs_fd *file, void *buf, size_t count) +{ + rt_size_t length; + struct tmpfs_file *d_file; + + d_file = (struct tmpfs_file *)file->vnode->data; + RT_ASSERT(d_file != NULL); + + if (count < file->vnode->size - file->pos) + length = count; + else + length = file->vnode->size - file->pos; + + if (length > 0) + memcpy(buf, &(d_file->data[file->pos]), length); + + /* update file current position */ + file->pos += length; + + return length; +} + + +int dfs_tmpfs_write(struct dfs_fd *fd, const void *buf, size_t count) +{ + struct tmpfs_file *d_file; + struct tmpfs_sb *superblock; + + d_file = (struct tmpfs_file *)fd->vnode->data; + RT_ASSERT(d_file != NULL); + + superblock = d_file->sb; + RT_ASSERT(superblock != NULL); + + if (count + fd->pos > fd->vnode->size) + { + rt_uint8_t *ptr; + ptr = rt_realloc(d_file->data, fd->pos + count); + if (ptr == NULL) + { + rt_set_errno(-ENOMEM); + return 0; + } + + superblock->df_size += (fd->pos - d_file->size + count); + /* update d_file and file size */ + d_file->data = ptr; + d_file->size = fd->pos + count; + fd->vnode->size = d_file->size; + LOG_D("tmpfile ptr:%x, size:%d", ptr, d_file->size); + } + + if (count > 0) + memcpy(d_file->data + fd->pos, buf, count); + + /* update file current position */ + fd->pos += count; + + return count; +} + +int dfs_tmpfs_lseek(struct dfs_fd *file, off_t offset) +{ + if (offset <= (off_t)file->vnode->size) + { + file->pos = offset; + + return file->pos; + } + + return -EIO; +} + +int dfs_tmpfs_close(struct dfs_fd *file) +{ + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + return 0; + } + + file->vnode->data = NULL; + + return RT_EOK; +} + +int dfs_tmpfs_open(struct dfs_fd *file) +{ + rt_size_t size; + struct tmpfs_sb *superblock; + struct tmpfs_file *d_file, *p_file; + struct dfs_filesystem *fs; + char parent_path[DFS_PATH_MAX],file_name[TMPFS_NAME_MAX]; + + RT_DEFINE_SPINLOCK(lock); + + RT_ASSERT(file->vnode->ref_count > 0); + if (file->vnode->ref_count > 1) + { + if (file->vnode->type == FT_DIRECTORY + && !(file->flags & O_DIRECTORY)) + { + return -ENOENT; + } + file->pos = 0; + return 0; + } + + fs = file->vnode->fs; + + superblock = (struct tmpfs_sb *)fs->data; + RT_ASSERT(superblock != NULL); + + /* find file */ + d_file = dfs_tmpfs_lookup(superblock, file->vnode->path, &size); + if (d_file == NULL && !(file->flags & O_CREAT)) + return -ENOENT; + + /* Creates a new file. */ + if (file->flags & O_CREAT) + { + if (d_file != NULL) + return -EEXIST; + + /* find parent file */ + _path_separate(file->vnode->path, parent_path, file_name); + if (file_name[0] == '\0') /* it's root dir */ + return -ENOENT; + + /* open parent directory */ + p_file = dfs_tmpfs_lookup(superblock, parent_path, &size); + if (p_file == NULL) + return -ENOENT; + + /* create a file entry */ + d_file = (struct tmpfs_file *)rt_calloc(1, sizeof(struct tmpfs_file)); + if (d_file == NULL) + { + return -ENOMEM; + } + superblock->df_size += sizeof(struct tmpfs_file); + + strncpy(d_file->name, file_name, TMPFS_NAME_MAX); + + rt_list_init(&(d_file->subdirs)); + rt_list_init(&(d_file->sibling)); + d_file->data = NULL; + d_file->size = 0; + d_file->sb = superblock; + if (file->flags & O_DIRECTORY) + { + d_file->type = TMPFS_TYPE_DIR; + } + else + { + d_file->type = TMPFS_TYPE_FILE; + } + rt_hw_spin_lock(&lock); + rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling)); + rt_hw_spin_unlock(&lock); + } + /* Creates a new file. + * If the file is existing, it is truncated and overwritten. + */ + if (file->flags & O_TRUNC) + { + d_file->size = 0; + if (d_file->data != NULL) + { + /* ToDo: fix for rt-smart. */ + rt_free(d_file->data); + d_file->data = NULL; + } + } + + /* fill file */ + if (d_file->type == TMPFS_TYPE_DIR) + { + if (file->flags & O_DIRECTORY) + file->vnode->type = FT_DIRECTORY; + else + return -ENOMEM; + } + else + { + if (file->flags & O_DIRECTORY) + { + return -ENOMEM; + } + file->vnode->type = FT_DEVICE; + } + + file->vnode->data = d_file; + file->vnode->size = d_file->size; + if (file->flags & O_APPEND) + { + file->pos = file->vnode->size; + } + else + { + file->pos = 0; + } + + return 0; +} + +int dfs_tmpfs_stat(struct dfs_filesystem *fs, + const char *path, + struct stat *st) +{ + rt_size_t size; + struct tmpfs_file *d_file; + struct tmpfs_sb *superblock; + + superblock = (struct tmpfs_sb *)fs->data; + d_file = dfs_tmpfs_lookup(superblock, path, &size); + + if (d_file == NULL) + return -ENOENT; + + st->st_dev = 0; + st->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | + S_IWUSR | S_IWGRP | S_IWOTH; + if (d_file->type == TMPFS_TYPE_DIR) + { + st->st_mode &= ~S_IFREG; + st->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + } + + st->st_size = d_file->size; + st->st_mtime = 0; + + return RT_EOK; +} + +int dfs_tmpfs_getdents(struct dfs_fd *file, + struct dirent *dirp, + uint32_t count) +{ + rt_size_t index, end; + struct dirent *d; + struct tmpfs_file *d_file, *n_file; + rt_list_t *list; + struct tmpfs_sb *superblock; + + d_file = (struct tmpfs_file *)file->vnode->data; + + superblock = d_file->sb; + RT_ASSERT(superblock != RT_NULL); + + /* make integer count */ + count = (count / sizeof(struct dirent)); + if (count == 0) + return -EINVAL; + + end = file->pos + count; + index = 0; + count = 0; + + rt_list_for_each(list, &d_file->subdirs) + { + n_file = rt_list_entry(list, struct tmpfs_file, sibling); + if (index >= (rt_size_t)file->pos) + { + d = dirp + count; + if (d_file->type == TMPFS_TYPE_FILE) + { + d->d_type = DT_REG; + } + if (d_file->type == TMPFS_TYPE_DIR) + { + d->d_type = DT_DIR; + } + d->d_namlen = RT_NAME_MAX; + d->d_reclen = (rt_uint16_t)sizeof(struct dirent); + rt_strncpy(d->d_name, n_file->name, TMPFS_NAME_MAX); + + count += 1; + file->pos += 1; + } + index += 1; + if (index >= end) + { + break; + } + } + + return count * sizeof(struct dirent); +} + +int dfs_tmpfs_unlink(struct dfs_filesystem *fs, const char *path) +{ + rt_size_t size; + struct tmpfs_sb *superblock; + struct tmpfs_file *d_file; + + RT_DEFINE_SPINLOCK(lock); + + superblock = (struct tmpfs_sb *)fs->data; + RT_ASSERT(superblock != NULL); + + d_file = dfs_tmpfs_lookup(superblock, path, &size); + if (d_file == NULL) + return -ENOENT; + + rt_hw_spin_lock(&lock); + rt_list_remove(&(d_file->sibling)); + rt_hw_spin_unlock(&lock); + + if (d_file->data != NULL) + rt_free(d_file->data); + rt_free(d_file); + + return RT_EOK; +} + +int dfs_tmpfs_rename(struct dfs_filesystem *fs, + const char *oldpath, + const char *newpath) +{ + struct tmpfs_file *d_file, *p_file; + struct tmpfs_sb *superblock; + rt_size_t size; + char parent_path[DFS_PATH_MAX],file_name[TMPFS_NAME_MAX]; + + RT_DEFINE_SPINLOCK(lock); + + superblock = (struct tmpfs_sb *)fs->data; + RT_ASSERT(superblock != NULL); + + d_file = dfs_tmpfs_lookup(superblock, newpath, &size); + if (d_file != NULL) + return -EEXIST; + + d_file = dfs_tmpfs_lookup(superblock, oldpath, &size); + if (d_file == NULL) + return -ENOENT; + + /* find parent file */ + _path_separate(newpath, parent_path, file_name); + if (file_name[0] == '\0') /* it's root dir */ + return -ENOENT; + /* open parent directory */ + p_file = dfs_tmpfs_lookup(superblock, parent_path, &size); + RT_ASSERT(p_file != NULL); + + rt_hw_spin_lock(&lock); + rt_list_remove(&(d_file->sibling)); + rt_hw_spin_unlock(&lock); + + strncpy(d_file->name, file_name, TMPFS_NAME_MAX); + + rt_hw_spin_lock(&lock); + rt_list_insert_after(&(p_file->subdirs), &(d_file->sibling)); + rt_hw_spin_unlock(&lock); + + return RT_EOK; +} + +static const struct dfs_file_ops _tmp_fops = +{ + dfs_tmpfs_open, + dfs_tmpfs_close, + dfs_tmpfs_ioctl, + dfs_tmpfs_read, + dfs_tmpfs_write, + NULL, /* flush */ + dfs_tmpfs_lseek, + dfs_tmpfs_getdents, +}; + +static const struct dfs_filesystem_ops _tmpfs = +{ + "tmp", + DFS_FS_FLAG_DEFAULT, + &_tmp_fops, + + dfs_tmpfs_mount, + dfs_tmpfs_unmount, + NULL, /* mkfs */ + dfs_tmpfs_statfs, + + dfs_tmpfs_unlink, + dfs_tmpfs_stat, + dfs_tmpfs_rename, +}; + +int dfs_tmpfs_init(void) +{ + /* register tmp file system */ + dfs_register(&_tmpfs); + + return 0; +} diff --git a/components/dfs/filesystems/tmpfs/dfs_tmpfs.h b/components/dfs/filesystems/tmpfs/dfs_tmpfs.h new file mode 100644 index 0000000000..37911bfd1b --- /dev/null +++ b/components/dfs/filesystems/tmpfs/dfs_tmpfs.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-24 flybreak the first version + */ + +#ifndef __DFS_TMPFS_H__ +#define __DFS_TMPFS_H__ + +#include <rtthread.h> + +#define TMPFS_NAME_MAX 32 +#define TMPFS_MAGIC 0x0B0B0B0B + +#define TMPFS_TYPE_FILE 0x00 +#define TMPFS_TYPE_DIR 0x01 + +struct tmpfs_sb; + +struct tmpfs_file +{ + rt_uint32_t type; /* file type */ + char name[TMPFS_NAME_MAX]; /* file name */ + rt_list_t subdirs; /* file subdir list */ + rt_list_t sibling; /* file sibling list */ + struct tmpfs_sb *sb; /* superblock ptr */ + rt_uint8_t *data; /* file date ptr */ + rt_size_t size; /* file size */ +}; + + +struct tmpfs_sb +{ + rt_uint32_t magic; /* TMPFS_MAGIC */ + struct tmpfs_file root; /* root dir */ + rt_size_t df_size; /* df size */ + rt_list_t sibling; /* sb sibling list */ +}; + +int dfs_tmpfs_init(void); + +#endif + diff --git a/components/dfs/include/dfs.h b/components/dfs/include/dfs.h index 678ff0e377..b2575fbf4d 100644 --- a/components/dfs/include/dfs.h +++ b/components/dfs/include/dfs.h @@ -37,8 +37,8 @@ extern "C" { /* * skip stdin/stdout/stderr normally */ -#ifndef DFS_FD_OFFSET -#define DFS_FD_OFFSET 3 +#ifndef DFS_STDIO_OFFSET +#define DFS_STDIO_OFFSET 3 #endif #ifndef DFS_PATH_MAX @@ -87,11 +87,27 @@ struct dfs_fdtable *dfs_fdtable_get(void); void dfs_lock(void); void dfs_unlock(void); +void dfs_fd_lock(void); +void dfs_fd_unlock(void); + +void dfs_fm_lock(void); +void dfs_fm_unlock(void); + #ifdef DFS_USING_POSIX /* FD APIs */ +int fdt_fd_new(struct dfs_fdtable *fdt); +struct dfs_fd *fdt_fd_get(struct dfs_fdtable* fdt, int fd); +void fdt_fd_release(struct dfs_fdtable* fdt, int fd); int fd_new(void); +int fd_associate(struct dfs_fdtable *fdt, int fd, struct dfs_fd *file); struct dfs_fd *fd_get(int fd); -void fd_put(struct dfs_fd *fd); +int fd_get_fd_index(struct dfs_fd *file); +void fd_release(int fd); + +void fd_init(struct dfs_fd *fd); + +struct dfs_fdtable *dfs_fdtable_get(void); +struct dfs_fdtable *dfs_fdtable_get_global(void); #endif /* DFS_USING_POSIX */ #ifdef __cplusplus diff --git a/components/dfs/include/dfs_file.h b/components/dfs/include/dfs_file.h index df1013d69c..7258127331 100644 --- a/components/dfs/include/dfs_file.h +++ b/components/dfs/include/dfs_file.h @@ -36,24 +36,47 @@ struct dfs_file_ops /* file descriptor */ #define DFS_FD_MAGIC 0xfdfd -struct dfs_fd + +struct dfs_fnode { - uint16_t magic; /* file descriptor magic number */ uint16_t type; /* Type (regular or socket) */ char *path; /* Name (below mount point) */ + char *fullpath; /* Full path is hash key */ int ref_count; /* Descriptor reference count */ + rt_list_t list; /* The node of vnode hash table */ struct dfs_filesystem *fs; const struct dfs_file_ops *fops; + uint32_t flags; /* self flags, is dir etc.. */ - uint32_t flags; /* Descriptor flags */ size_t size; /* Size in bytes */ + void *data; /* Specific file system data */ +}; + +struct dfs_fd +{ + uint16_t magic; /* file descriptor magic number */ + uint32_t flags; /* Descriptor flags */ + int ref_count; /* Descriptor reference count */ off_t pos; /* Current file position */ + struct dfs_fnode *vnode; /* file node struct */ + void *data; /* Specific fd data */ +}; - void *data; /* Specific file system data */ +struct dfs_mmap2_args +{ + void *addr; + size_t length; + int prot; + int flags; + off_t pgoffset; + + void *ret; }; +void dfs_fnode_mgr_init(void); +int dfs_file_is_open(const char *pathname); int dfs_file_open(struct dfs_fd *fd, const char *path, int flags); int dfs_file_close(struct dfs_fd *fd); int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args); @@ -67,9 +90,12 @@ int dfs_file_lseek(struct dfs_fd *fd, off_t offset); int dfs_file_stat(const char *path, struct stat *buf); int dfs_file_rename(const char *oldpath, const char *newpath); int dfs_file_ftruncate(struct dfs_fd *fd, off_t length); +int dfs_file_mmap2(struct dfs_fd *fd, struct dfs_mmap2_args *mmap2); /* 0x5254 is just a magic number to make these relatively unique ("RT") */ -#define RT_FIOFTRUNCATE 0x52540000U +#define RT_FIOFTRUNCATE 0x52540000U +#define RT_FIOGETADDR 0x52540001U +#define RT_FIOMMAP2 0x52540002U #ifdef __cplusplus } diff --git a/components/dfs/include/dfs_fs.h b/components/dfs/include/dfs_fs.h index 0a13040c21..985017afe0 100644 --- a/components/dfs/include/dfs_fs.h +++ b/components/dfs/include/dfs_fs.h @@ -37,7 +37,7 @@ struct dfs_filesystem_ops int (*unmount) (struct dfs_filesystem *fs); /* make a file system */ - int (*mkfs) (rt_device_t devid); + int (*mkfs) (rt_device_t dev_id, const char *fs_name); int (*statfs) (struct dfs_filesystem *fs, struct statfs *buf); int (*unlink) (struct dfs_filesystem *fs, const char *pathname); diff --git a/components/dfs/src/dfs.c b/components/dfs/src/dfs.c index f1d7571c0c..f3f9c2354b 100644 --- a/components/dfs/src/dfs.c +++ b/components/dfs/src/dfs.c @@ -28,12 +28,14 @@ struct dfs_filesystem filesystem_table[DFS_FILESYSTEMS_MAX]; /* device filesystem lock */ static struct rt_mutex fslock; +static struct rt_mutex fdlock; #ifdef DFS_USING_WORKDIR char working_directory[DFS_PATH_MAX] = {"/"}; #endif static struct dfs_fdtable _fdtab; +static int fd_alloc(struct dfs_fdtable *fdt, int startfd); /** * @addtogroup DFS @@ -54,6 +56,9 @@ int dfs_init(void) return 0; } + /* init vnode hash table */ + dfs_fnode_mgr_init(); + /* clear filesystem operations table */ rt_memset((void *)filesystem_operation_table, 0, sizeof(filesystem_operation_table)); /* clear filesystem table */ @@ -63,6 +68,7 @@ int dfs_init(void) /* create device filesystem lock */ rt_mutex_init(&fslock, "fslock", RT_IPC_FLAG_PRIO); + rt_mutex_init(&fdlock, "fdlock", RT_IPC_FLAG_PRIO); #ifdef DFS_USING_WORKDIR /* set current working directory */ @@ -70,6 +76,13 @@ int dfs_init(void) working_directory[0] = '/'; #endif +#ifdef RT_USING_DFS_TMPFS + { + extern int dfs_tmpfs_init(void); + dfs_tmpfs_init(); + } +#endif + #ifdef RT_USING_DFS_DEVFS { extern int devfs_init(void); @@ -79,6 +92,13 @@ int dfs_init(void) dfs_mount(NULL, "/dev", "devfs", 0, 0); } +#if defined(RT_USING_DEV_BUS) && defined(RT_USING_DFS_TMPFS) + mkdir("/dev/shm", 0x777); + if (dfs_mount(RT_NULL, "/dev/shm", "tmp", 0, 0) != 0) + { + rt_kprintf("Dir /dev/shm mount failed!\n"); + } +#endif #endif init_ok = RT_TRUE; @@ -107,6 +127,21 @@ void dfs_lock(void) } } +void dfs_fd_lock(void) +{ + rt_err_t result = -RT_EBUSY; + + while (result == -RT_EBUSY) + { + result = rt_mutex_take(&fdlock, RT_WAITING_FOREVER); + } + + if (result != RT_EOK) + { + RT_ASSERT(0); + } +} + /** * this function will lock device file system. * @@ -118,51 +153,95 @@ void dfs_unlock(void) } #ifdef DFS_USING_POSIX -static int fd_alloc(struct dfs_fdtable *fdt, int startfd) + +void dfs_fd_unlock(void) { - int idx; + rt_mutex_release(&fdlock); +} - /* find an empty fd entry */ - for (idx = startfd; idx < (int)fdt->maxfd; idx++) +static int fd_slot_expand(struct dfs_fdtable *fdt, int fd) +{ + int nr; + int index; + struct dfs_fd **fds = NULL; + + if (fd < fdt->maxfd) { - if (fdt->fds[idx] == RT_NULL) - break; - if (fdt->fds[idx]->ref_count == 0) - break; + return fd; + } + if (fd >= DFS_FD_MAX) + { + return -1; } - /* allocate a larger FD container */ - if (idx == (int)fdt->maxfd && fdt->maxfd < DFS_FD_MAX) + nr = ((fd + 4) & ~3); + if (nr > DFS_FD_MAX) { - int cnt, index; - struct dfs_fd **fds; + nr = DFS_FD_MAX; + } + fds = (struct dfs_fd **)rt_realloc(fdt->fds, nr * sizeof(struct dfs_fd *)); + if (!fds) + { + return -1; + } - /* increase the number of FD with 4 step length */ - cnt = fdt->maxfd + 4; - cnt = cnt > DFS_FD_MAX ? DFS_FD_MAX : cnt; + /* clean the new allocated fds */ + for (index = fdt->maxfd; index < nr; index++) + { + fds[index] = NULL; + } + fdt->fds = fds; + fdt->maxfd = nr; - fds = (struct dfs_fd **)rt_realloc(fdt->fds, cnt * sizeof(struct dfs_fd *)); - if (fds == NULL) goto __exit; /* return fdt->maxfd */ + return fd; +} + +static int fd_slot_alloc(struct dfs_fdtable *fdt, int startfd) +{ + int idx; - /* clean the new allocated fds */ - for (index = (int)fdt->maxfd; index < cnt; index ++) + /* find an empty fd slot */ + for (idx = startfd; idx < (int)fdt->maxfd; idx++) + { + if (fdt->fds[idx] == RT_NULL) { - fds[index] = NULL; + return idx; } + } - fdt->fds = fds; - fdt->maxfd = cnt; + idx = fdt->maxfd; + if (idx < startfd) + { + idx = startfd; } + if (fd_slot_expand(fdt, idx) < 0) + { + return -1; + } + return idx; +} +static int fd_alloc(struct dfs_fdtable *fdt, int startfd) +{ + int idx; + struct dfs_fd *fd = NULL; + + idx = fd_slot_alloc(fdt, startfd); /* allocate 'struct dfs_fd' */ - if (idx < (int)fdt->maxfd && fdt->fds[idx] == RT_NULL) + if (idx < 0) { - fdt->fds[idx] = (struct dfs_fd *)rt_calloc(1, sizeof(struct dfs_fd)); - if (fdt->fds[idx] == RT_NULL) - idx = fdt->maxfd; + return -1; } + fd = (struct dfs_fd *)rt_calloc(1, sizeof(struct dfs_fd)); + if (!fd) + { + return -1; + } + fd->ref_count = 1; + fd->magic = DFS_FD_MAGIC; + fd->vnode = NULL; + fdt->fds[idx] = fd; -__exit: return idx; } @@ -172,34 +251,32 @@ __exit: * * @return -1 on failed or the allocated file descriptor. */ -int fd_new(void) +int fdt_fd_new(struct dfs_fdtable *fdt) { - struct dfs_fd *d; int idx; - struct dfs_fdtable *fdt; - fdt = dfs_fdtable_get(); /* lock filesystem */ - dfs_lock(); + dfs_fd_lock(); /* find an empty fd entry */ - idx = fd_alloc(fdt, 0); + idx = fd_alloc(fdt, DFS_STDIO_OFFSET); /* can't find an empty fd entry */ - if (idx == (int)fdt->maxfd) + if (idx < 0) { - idx = -(1 + DFS_FD_OFFSET); LOG_E("DFS fd new is failed! Could not found an empty fd entry."); - goto __result; } - d = fdt->fds[idx]; - d->ref_count = 1; - d->magic = DFS_FD_MAGIC; + dfs_fd_unlock(); + return idx; +} -__result: - dfs_unlock(); - return idx + DFS_FD_OFFSET; +int fd_new(void) +{ + struct dfs_fdtable *fdt = NULL; + + fdt = dfs_fdtable_get(); + return fdt_fd_new(fdt); } /** @@ -211,69 +288,119 @@ __result: * @return NULL on on this file descriptor or the file descriptor structure * pointer. */ -struct dfs_fd *fd_get(int fd) + +struct dfs_fd *fdt_fd_get(struct dfs_fdtable* fdt, int fd) { struct dfs_fd *d; - struct dfs_fdtable *fdt; -#ifdef RT_USING_POSIX_STDIO - if ((0 <= fd) && (fd <= 2)) - fd = libc_stdio_get_console(); -#endif /* RT_USING_POSIX_STDIO */ - - fdt = dfs_fdtable_get(); - fd = fd - DFS_FD_OFFSET; if (fd < 0 || fd >= (int)fdt->maxfd) + { return NULL; + } - dfs_lock(); + dfs_fd_lock(); d = fdt->fds[fd]; /* check dfs_fd valid or not */ if ((d == NULL) || (d->magic != DFS_FD_MAGIC)) { - dfs_unlock(); + dfs_fd_unlock(); return NULL; } - /* increase the reference count */ - d->ref_count ++; - dfs_unlock(); + dfs_fd_unlock(); return d; } +struct dfs_fd *fd_get(int fd) +{ + struct dfs_fdtable *fdt; + + fdt = dfs_fdtable_get(); + return fdt_fd_get(fdt, fd); +} + /** * @ingroup Fd * * This function will put the file descriptor. */ -void fd_put(struct dfs_fd *fd) +void fdt_fd_release(struct dfs_fdtable* fdt, int fd) { - RT_ASSERT(fd != NULL); + struct dfs_fd *fd_slot = NULL; - dfs_lock(); + RT_ASSERT(fdt != NULL); - fd->ref_count --; + dfs_fd_lock(); - /* clear this fd entry */ - if (fd->ref_count == 0) + if ((fd < 0) || (fd >= fdt->maxfd)) { - int index; - struct dfs_fdtable *fdt; + dfs_fd_unlock(); + return; + } + + fd_slot = fdt->fds[fd]; + if (fd_slot == NULL) + { + dfs_fd_unlock(); + return; + } + fdt->fds[fd] = NULL; - fdt = dfs_fdtable_get(); - for (index = 0; index < (int)fdt->maxfd; index ++) + /* check fd */ + RT_ASSERT(fd_slot->magic == DFS_FD_MAGIC); + + fd_slot->ref_count--; + + /* clear this fd entry */ + if (fd_slot->ref_count == 0) + { + struct dfs_fnode *vnode = fd_slot->vnode; + if (vnode) { - if (fdt->fds[index] == fd) - { - rt_free(fd); - fdt->fds[index] = 0; - break; - } + vnode->ref_count--; } + rt_free(fd_slot); } - dfs_unlock(); + dfs_fd_unlock(); +} + +void fd_release(int fd) +{ + struct dfs_fdtable *fdt; + + fdt = dfs_fdtable_get(); + fdt_fd_release(fdt, fd); +} + +int sys_dup(int oldfd) +{ + int newfd = -1; + struct dfs_fdtable *fdt = NULL; + + dfs_fd_lock(); + /* check old fd */ + fdt = dfs_fdtable_get(); + if ((oldfd < 0) || (oldfd >= fdt->maxfd)) + { + goto exit; + } + if (!fdt->fds[oldfd]) + { + goto exit; + } + /* get a new fd */ + newfd = fd_slot_alloc(fdt, DFS_STDIO_OFFSET); + if (newfd >= 0) + { + fdt->fds[newfd] = fdt->fds[oldfd]; + /* inc ref_count */ + fdt->fds[newfd]->ref_count++; + } +exit: + dfs_fd_unlock(); + return newfd; } #endif /* DFS_USING_POSIX */ @@ -320,9 +447,9 @@ int fd_is_open(const char *pathname) for (index = 0; index < fdt->maxfd; index++) { fd = fdt->fds[index]; - if (fd == NULL || fd->fops == NULL || fd->path == NULL) continue; + if (fd == NULL || fd->vnode->fops == NULL || fd->vnode->path == NULL) continue; - if (fd->fs == fs && strcmp(fd->path, mountpath) == 0) + if (fd->vnode->fs == fs && strcmp(fd->vnode->path, mountpath) == 0) { /* found file in file descriptor table */ rt_free(fullpath); @@ -339,6 +466,139 @@ int fd_is_open(const char *pathname) return -1; } +int sys_dup2(int oldfd, int newfd) +{ + struct dfs_fdtable *fdt = NULL; + int ret = 0; + int retfd = -1; + + dfs_fd_lock(); + /* check old fd */ + fdt = dfs_fdtable_get(); + if ((oldfd < 0) || (oldfd >= fdt->maxfd)) + { + goto exit; + } + if (!fdt->fds[oldfd]) + { + goto exit; + } + if (newfd < 0) + { + goto exit; + } + if (newfd >= fdt->maxfd) + { + newfd = fd_slot_expand(fdt, newfd); + if (newfd < 0) + { + goto exit; + } + } + if (fdt->fds[newfd] == fdt->fds[oldfd]) + { + /* ok, return newfd */ + retfd = newfd; + goto exit; + } + + if (fdt->fds[newfd]) + { + ret = dfs_file_close(fdt->fds[newfd]); + if (ret < 0) + { + goto exit; + } + fd_release(newfd); + } + + fdt->fds[newfd] = fdt->fds[oldfd]; + /* inc ref_count */ + fdt->fds[newfd]->ref_count++; + retfd = newfd; +exit: + dfs_fd_unlock(); + return retfd; +} + +static int fd_get_fd_index_form_fdt(struct dfs_fdtable *fdt, struct dfs_fd *file) +{ + int fd = -1; + + if (file == RT_NULL) + { + return -1; + } + + dfs_fd_lock(); + + for(int index = 0; index < (int)fdt->maxfd; index++) + { + if(fdt->fds[index] == file) + { + fd = index; + break; + } + } + + dfs_fd_unlock(); + + return fd; +} + +int fd_get_fd_index(struct dfs_fd *file) +{ + struct dfs_fdtable *fdt; + + fdt = dfs_fdtable_get(); + return fd_get_fd_index_form_fdt(fdt, file); +} + +int fd_associate(struct dfs_fdtable *fdt, int fd, struct dfs_fd *file) +{ + int retfd = -1; + + if (!file) + { + return retfd; + } + if (!fdt) + { + return retfd; + } + + dfs_fd_lock(); + /* check old fd */ + if ((fd < 0) || (fd >= fdt->maxfd)) + { + goto exit; + } + + if (fdt->fds[fd]) + { + goto exit; + } + /* inc ref_count */ + file->ref_count++; + fdt->fds[fd] = file; + retfd = fd; +exit: + dfs_fd_unlock(); + return retfd; +} + +void fd_init(struct dfs_fd *fd) +{ + if (fd) + { + fd->magic = DFS_FD_MAGIC; + fd->ref_count = 1; + fd->pos = 0; + fd->vnode = NULL; + fd->data = NULL; + } +} + /** * this function will return a sub-path name under directory. * @@ -383,7 +643,13 @@ char *dfs_normalize_path(const char *directory, const char *filename) #ifdef DFS_USING_WORKDIR if (directory == NULL) /* shall use working directory */ + { +#ifdef RT_USING_LWP + directory = lwp_getcwd(); +#else directory = &working_directory[0]; +#endif + } #else if ((directory == NULL) && (filename[0] != '/')) { @@ -422,14 +688,14 @@ char *dfs_normalize_path(const char *directory, const char *filename) if (c == '.') { - if (!src[1]) src ++; /* '.' and ends */ + if (!src[1]) src++; /* '.' and ends */ else if (src[1] == '/') { /* './' case */ src += 2; while ((*src == '/') && (*src != '\0')) - src ++; + src++; continue; } else if (src[1] == '.') @@ -446,7 +712,7 @@ char *dfs_normalize_path(const char *directory, const char *filename) src += 3; while ((*src == '/') && (*src != '\0')) - src ++; + src++; goto up_one; } } @@ -454,15 +720,15 @@ char *dfs_normalize_path(const char *directory, const char *filename) /* copy up the next '/' and erase all '/' */ while ((c = *src++) != '\0' && c != '/') - *dst ++ = c; + *dst++ = c; if (c == '/') { - *dst ++ = '/'; + *dst++ = '/'; while (c == '/') c = *src++; - src --; + src--; } else if (!c) break; @@ -470,20 +736,25 @@ char *dfs_normalize_path(const char *directory, const char *filename) continue; up_one: - dst --; - if (dst < dst0) + /* keep the topmost root directory */ + if (dst - dst0 != 1 || dst[-1] != '/') { - rt_free(fullpath); - return NULL; + dst--; + + if (dst < dst0) + { + rt_free(fullpath); + return NULL; + } } while (dst0 < dst && dst[-1] != '/') - dst --; + dst--; } *dst = '\0'; /* remove '/' in the end of path if exist */ - dst --; + dst--; if ((dst != fullpath) && (*dst == '/')) *dst = '\0'; @@ -519,6 +790,27 @@ struct dfs_fdtable *dfs_fdtable_get(void) return fdt; } +#ifdef RT_USING_LWP +struct dfs_fdtable *dfs_fdtable_get_pid(int pid) +{ + struct rt_lwp *lwp = RT_NULL; + struct dfs_fdtable *fdt = RT_NULL; + + lwp = lwp_from_pid(pid); + if (lwp) + { + fdt = &lwp->fdt; + } + + return fdt; +} +#endif + +struct dfs_fdtable *dfs_fdtable_get_global(void) +{ + return &_fdtab; +} + #ifdef RT_USING_FINSH int list_fd(void) { @@ -532,28 +824,97 @@ int list_fd(void) rt_kprintf("fd type ref magic path\n"); rt_kprintf("-- ------ --- ----- ------\n"); - for (index = 0; index < (int)fd_table->maxfd; index ++) + for (index = 0; index < (int)fd_table->maxfd; index++) { struct dfs_fd *fd = fd_table->fds[index]; - if (fd && fd->fops) + if (fd && fd->vnode->fops) { - rt_kprintf("%2d ", index + DFS_FD_OFFSET); - if (fd->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir"); - else if (fd->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file"); - else if (fd->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket"); - else if (fd->type == FT_USER) rt_kprintf("%-7.7s ", "user"); - else if (fd->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device"); + rt_kprintf("%2d ", index); + if (fd->vnode->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir"); + else if (fd->vnode->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file"); + else if (fd->vnode->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket"); + else if (fd->vnode->type == FT_USER) rt_kprintf("%-7.7s ", "user"); + else if (fd->vnode->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device"); else rt_kprintf("%-8.8s ", "unknown"); - rt_kprintf("%3d ", fd->ref_count); + rt_kprintf("%3d ", fd->vnode->ref_count); rt_kprintf("%04x ", fd->magic); - if (fd->fs && fd->fs->path && rt_strlen(fd->fs->path) > 1) + if (fd->vnode->path) { - rt_kprintf("%s", fd->fs->path); + rt_kprintf("%s\n", fd->vnode->path); } - if (fd->path) + else { - rt_kprintf("%s\n", fd->path); + rt_kprintf("\n"); + } + } + } + dfs_unlock(); + + return 0; +} + +#ifdef RT_USING_LWP +static int lsofp(int pid) +{ + int index; + struct dfs_fdtable *fd_table = RT_NULL; + + if (pid == (-1)) + { + fd_table = dfs_fdtable_get(); + if (!fd_table) return -1; + } + else + { + fd_table = dfs_fdtable_get_pid(pid); + if (!fd_table) + { + rt_kprintf("PID %s is not a applet(lwp)\n", pid); + return -1; + } + } + + rt_kprintf("--- -- ------ ------ ----- ---------- ---------- ---------- ------\n"); + + rt_enter_critical(); + for (index = 0; index < (int)fd_table->maxfd; index++) + { + struct dfs_fd *fd = fd_table->fds[index]; + + if (fd && fd->vnode->fops) + { + if(pid == (-1)) + { + rt_kprintf(" K "); + } + else + { + rt_kprintf("%3d ", pid); + } + + rt_kprintf("%2d ", index); + if (fd->vnode->type == FT_DIRECTORY) rt_kprintf("%-7.7s ", "dir"); + else if (fd->vnode->type == FT_REGULAR) rt_kprintf("%-7.7s ", "file"); + else if (fd->vnode->type == FT_SOCKET) rt_kprintf("%-7.7s ", "socket"); + else if (fd->vnode->type == FT_USER) rt_kprintf("%-7.7s ", "user"); + else if (fd->vnode->type == FT_DEVICE) rt_kprintf("%-7.7s ", "device"); + else rt_kprintf("%-8.8s ", "unknown"); + rt_kprintf("%6d ", fd->vnode->ref_count); + rt_kprintf("%04x 0x%.8x ", fd->magic, (int)(size_t)fd->vnode); + + if(fd->vnode == RT_NULL) + { + rt_kprintf("0x%.8x 0x%.8x ", (int)0x00000000, (int)(size_t)fd); + } + else + { + rt_kprintf("0x%.8x 0x%.8x ", (int)(size_t)(fd->vnode->data), (int)(size_t)fd); + } + + if (fd->vnode->path) + { + rt_kprintf("%s \n", fd->vnode->path); } else { @@ -561,10 +922,50 @@ int list_fd(void) } } } - dfs_unlock(); + rt_exit_critical(); + + return 0; +} + +int lsof(int argc, char *argv[]) +{ + rt_kprintf("PID fd type fd-ref magic vnode vnode/data addr path \n"); + + if (argc == 1) + { + struct rt_list_node *node, *list; + struct lwp_avl_struct *pids = lwp_get_pid_ary(); + + lsofp(-1); + + for (int index = 0; index < RT_LWP_MAX_NR; index++) + { + struct rt_lwp *lwp = (struct rt_lwp *)pids[index].data; + + if (lwp) + { + list = &lwp->t_grp; + for (node = list->next; node != list; node = node->next) + { + lsofp(lwp_to_pid(lwp)); + } + } + } + } + else if (argc == 3) + { + if (argv[1][0] == '-' && argv[1][1] == 'p') + { + int pid = atoi(argv[2]); + lsofp(pid); + } + } return 0; } -#endif /* RT_USING_FINSH */ +MSH_CMD_EXPORT(lsof, list open files); +#endif /* RT_USING_LWP */ +#endif /*@}*/ + diff --git a/components/dfs/src/dfs_file.c b/components/dfs/src/dfs_file.c index a798bd3c59..1f6f0d1e29 100644 --- a/components/dfs/src/dfs_file.c +++ b/components/dfs/src/dfs_file.c @@ -14,6 +14,78 @@ #include <dfs.h> #include <dfs_file.h> #include <dfs_private.h> +#include <unistd.h> + +#define DFS_FNODE_HASH_NR 128 + +struct dfs_fnode_mgr +{ + struct rt_mutex lock; + rt_list_t head[DFS_FNODE_HASH_NR]; +}; + +static struct dfs_fnode_mgr dfs_fm; + +void dfs_fm_lock(void) +{ + rt_mutex_take(&dfs_fm.lock, RT_WAITING_FOREVER); +} + +void dfs_fm_unlock(void) +{ + rt_mutex_release(&dfs_fm.lock); +} + +void dfs_fnode_mgr_init(void) +{ + int i = 0; + + rt_mutex_init(&dfs_fm.lock, "dfs_mgr", RT_IPC_FLAG_PRIO); + for (i = 0; i < DFS_FNODE_HASH_NR; i++) + { + rt_list_init(&dfs_fm.head[i]); + } +} + +/* BKDR Hash Function */ +static unsigned int bkdr_hash(const char *str) +{ + unsigned int seed = 131; // 31 131 1313 13131 131313 etc.. + unsigned int hash = 0; + + while (*str) + { + hash = hash * seed + (*str++); + } + + return (hash % DFS_FNODE_HASH_NR); +} + +static struct dfs_fnode *dfs_fnode_find(const char *path, rt_list_t **hash_head) +{ + struct dfs_fnode *vnode = NULL; + int hash = bkdr_hash(path); + rt_list_t *hh; + + hh = dfs_fm.head[hash].next; + + if (hash_head) + { + *hash_head = &dfs_fm.head[hash]; + } + + while (hh != &dfs_fm.head[hash]) + { + vnode = rt_container_of(hh, struct dfs_fnode, list); + if (rt_strcmp(path, vnode->fullpath) == 0) + { + /* found */ + return vnode; + } + hh = hh->next; + } + return NULL; +} /** * @addtogroup FileApi @@ -22,6 +94,34 @@ /*@{*/ /** + * This function will return whether this file has been opend. + * + * @param pathname the file path name. + * + * @return 0 on file has been open successfully, -1 on open failed. + */ +int dfs_file_is_open(const char *pathname) +{ + char *fullpath = NULL; + struct dfs_fnode *vnode = NULL; + int ret = 0; + + fullpath = dfs_normalize_path(NULL, pathname); + + dfs_fm_lock(); + vnode = dfs_fnode_find(fullpath, NULL); + if (vnode) + { + ret = 1; + } + dfs_fm_unlock(); + + rt_free(fullpath); + return ret; +} + + +/** * this function will open a file which specified by path with specified flags. * * @param fd the file descriptor pointer to return the corresponding result. @@ -35,6 +135,8 @@ int dfs_file_open(struct dfs_fd *fd, const char *path, int flags) struct dfs_filesystem *fs; char *fullpath; int result; + struct dfs_fnode *vnode = NULL; + rt_list_t *hash_head; /* parameter check */ if (fd == NULL) @@ -49,56 +151,101 @@ int dfs_file_open(struct dfs_fd *fd, const char *path, int flags) LOG_D("open file:%s", fullpath); - /* find filesystem */ - fs = dfs_filesystem_lookup(fullpath); - if (fs == NULL) + dfs_fm_lock(); + /* vnode find */ + vnode = dfs_fnode_find(fullpath, &hash_head); + if (vnode) { + vnode->ref_count++; + fd->pos = 0; + fd->vnode = vnode; + dfs_fm_unlock(); rt_free(fullpath); /* release path */ - - return -ENOENT; } + else + { + /* find filesystem */ + fs = dfs_filesystem_lookup(fullpath); + if (fs == NULL) + { + dfs_fm_unlock(); + rt_free(fullpath); /* release path */ + return -ENOENT; + } + + vnode = rt_calloc(1, sizeof(struct dfs_fnode)); + if (!vnode) + { + dfs_fm_unlock(); + rt_free(fullpath); /* release path */ + return -ENOMEM; + } + vnode->ref_count = 1; - LOG_D("open in filesystem:%s", fs->ops->name); - fd->fs = fs; /* set file system */ - fd->fops = fs->ops->fops; /* set file ops */ + LOG_D("open in filesystem:%s", fs->ops->name); + vnode->fs = fs; /* set file system */ + vnode->fops = fs->ops->fops; /* set file ops */ - /* initialize the fd item */ - fd->type = FT_REGULAR; - fd->flags = flags; - fd->size = 0; - fd->pos = 0; - fd->data = fs; + /* initialize the fd item */ + vnode->type = FT_REGULAR; + vnode->flags = 0; - if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH)) - { - if (dfs_subdir(fs->path, fullpath) == NULL) - fd->path = rt_strdup("/"); + if (!(fs->ops->flags & DFS_FS_FLAG_FULLPATH)) + { + if (dfs_subdir(fs->path, fullpath) == NULL) + vnode->path = rt_strdup("/"); + else + vnode->path = rt_strdup(dfs_subdir(fs->path, fullpath)); + LOG_D("Actual file path: %s", vnode->path); + } else - fd->path = rt_strdup(dfs_subdir(fs->path, fullpath)); - rt_free(fullpath); - LOG_D("Actual file path: %s", fd->path); - } - else - { - fd->path = fullpath; - } + { + vnode->path = fullpath; + } + vnode->fullpath = fullpath; - /* specific file system open routine */ - if (fd->fops->open == NULL) - { - /* clear fd */ - rt_free(fd->path); - fd->path = NULL; + /* specific file system open routine */ + if (vnode->fops->open == NULL) + { + dfs_fm_unlock(); + /* clear fd */ + if (vnode->path != vnode->fullpath) + { + rt_free(vnode->fullpath); + } + rt_free(vnode->path); + rt_free(vnode); - return -ENOSYS; + return -ENOSYS; + } + + fd->pos = 0; + fd->vnode = vnode; + + /* insert vnode to hash */ + rt_list_insert_after(hash_head, &vnode->list); } - if ((result = fd->fops->open(fd)) < 0) + fd->flags = flags; + + if ((result = vnode->fops->open(fd)) < 0) { - /* clear fd */ - rt_free(fd->path); - fd->path = NULL; + vnode->ref_count--; + if (vnode->ref_count == 0) + { + /* remove from hash */ + rt_list_remove(&vnode->list); + /* clear fd */ + if (vnode->path != vnode->fullpath) + { + rt_free(vnode->fullpath); + } + rt_free(vnode->path); + fd->vnode = NULL; + rt_free(vnode); + } + dfs_fm_unlock(); LOG_D("%s open failed", fullpath); return result; @@ -107,9 +254,10 @@ int dfs_file_open(struct dfs_fd *fd, const char *path, int flags) fd->flags |= DFS_F_OPEN; if (flags & O_DIRECTORY) { - fd->type = FT_DIRECTORY; + fd->vnode->type = FT_DIRECTORY; fd->flags |= DFS_F_DIRECTORY; } + dfs_fm_unlock(); LOG_D("open successful"); return 0; @@ -124,20 +272,52 @@ int dfs_file_open(struct dfs_fd *fd, const char *path, int flags) */ int dfs_file_close(struct dfs_fd *fd) { + struct dfs_fnode *vnode = NULL; int result = 0; if (fd == NULL) + { return -ENXIO; + } - if (fd->fops->close != NULL) - result = fd->fops->close(fd); + if (fd->ref_count == 1) + { + dfs_fm_lock(); + vnode = fd->vnode; - /* close fd error, return */ - if (result < 0) - return result; + if (vnode->ref_count <= 0) + { + dfs_fm_unlock(); + return -ENXIO; + } - rt_free(fd->path); - fd->path = NULL; + if (vnode->fops->close != NULL) + { + result = vnode->fops->close(fd); + } + + /* close fd error, return */ + if (result < 0) + { + dfs_fm_unlock(); + return result; + } + + if (vnode->ref_count == 1) + { + /* remove from hash */ + rt_list_remove(&vnode->list); + fd->vnode = NULL; + + if (vnode->path != vnode->fullpath) + { + rt_free(vnode->fullpath); + } + rt_free(vnode->path); + rt_free(vnode); + } + dfs_fm_unlock(); + } return result; } @@ -154,10 +334,12 @@ int dfs_file_close(struct dfs_fd *fd) int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args) { if (fd == NULL) + { return -EINVAL; + } /* regular file system fd */ - if (fd->type == FT_REGULAR) + if (fd->vnode->type == FT_REGULAR || fd->vnode->type == FT_DEVICE) { switch (cmd) { @@ -176,8 +358,10 @@ int dfs_file_ioctl(struct dfs_fd *fd, int cmd, void *args) } } - if (fd->fops->ioctl != NULL) - return fd->fops->ioctl(fd, cmd, args); + if (fd->vnode->fops->ioctl != NULL) + { + return fd->vnode->fops->ioctl(fd, cmd, args); + } return -ENOSYS; } @@ -197,13 +381,19 @@ int dfs_file_read(struct dfs_fd *fd, void *buf, size_t len) int result = 0; if (fd == NULL) + { return -EINVAL; + } - if (fd->fops->read == NULL) + if (fd->vnode->fops->read == NULL) + { return -ENOSYS; + } - if ((result = fd->fops->read(fd, buf, len)) < 0) + if ((result = fd->vnode->fops->read(fd, buf, len)) < 0) + { fd->flags |= DFS_F_EOF; + } return result; } @@ -220,11 +410,20 @@ int dfs_file_read(struct dfs_fd *fd, void *buf, size_t len) int dfs_file_getdents(struct dfs_fd *fd, struct dirent *dirp, size_t nbytes) { /* parameter check */ - if (fd == NULL || fd->type != FT_DIRECTORY) + if (fd == NULL) + { + return -EINVAL; + } + + if (fd->vnode->type != FT_DIRECTORY) + { return -EINVAL; + } - if (fd->fops->getdents != NULL) - return fd->fops->getdents(fd, dirp, nbytes); + if (fd->vnode->fops->getdents != NULL) + { + return fd->vnode->fops->getdents(fd, dirp, nbytes); + } return -ENOSYS; } @@ -249,17 +448,17 @@ int dfs_file_unlink(const char *path) return -EINVAL; } - /* get filesystem */ - if ((fs = dfs_filesystem_lookup(fullpath)) == NULL) + /* Check whether file is already open */ + if (dfs_file_is_open(fullpath)) { - result = -ENOENT; + result = -EBUSY; goto __exit; } - /* Check whether file is already open */ - if (fd_is_open(fullpath) == 0) + /* get filesystem */ + if ((fs = dfs_filesystem_lookup(fullpath)) == NULL) { - result = -EBUSY; + result = -ENOENT; goto __exit; } @@ -294,12 +493,16 @@ __exit: int dfs_file_write(struct dfs_fd *fd, const void *buf, size_t len) { if (fd == NULL) + { return -EINVAL; + } - if (fd->fops->write == NULL) + if (fd->vnode->fops->write == NULL) + { return -ENOSYS; + } - return fd->fops->write(fd, buf, len); + return fd->vnode->fops->write(fd, buf, len); } /** @@ -314,10 +517,10 @@ int dfs_file_flush(struct dfs_fd *fd) if (fd == NULL) return -EINVAL; - if (fd->fops->flush == NULL) + if (fd->vnode->fops->flush == NULL) return -ENOSYS; - return fd->fops->flush(fd); + return fd->vnode->fops->flush(fd); } /** @@ -335,10 +538,10 @@ int dfs_file_lseek(struct dfs_fd *fd, off_t offset) if (fd == NULL) return -EINVAL; - if (fd->fops->lseek == NULL) + if (fd->vnode->fops->lseek == NULL) return -ENOSYS; - result = fd->fops->lseek(fd, offset); + result = fd->vnode->fops->lseek(fd, offset); /* update current position */ if (result >= 0) @@ -425,11 +628,10 @@ int dfs_file_stat(const char *path, struct stat *buf) */ int dfs_file_rename(const char *oldpath, const char *newpath) { - int result; - struct dfs_filesystem *oldfs, *newfs; - char *oldfullpath, *newfullpath; + int result = RT_EOK; + struct dfs_filesystem *oldfs = NULL, *newfs = NULL; + char *oldfullpath = NULL, *newfullpath = NULL; - result = RT_EOK; newfullpath = NULL; oldfullpath = NULL; @@ -440,6 +642,12 @@ int dfs_file_rename(const char *oldpath, const char *newpath) goto __exit; } + if (dfs_file_is_open((const char *)oldfullpath)) + { + result = -EBUSY; + goto __exit; + } + newfullpath = dfs_normalize_path(NULL, newpath); if (newfullpath == NULL) { @@ -473,8 +681,14 @@ int dfs_file_rename(const char *oldpath, const char *newpath) } __exit: - rt_free(oldfullpath); - rt_free(newfullpath); + if (oldfullpath) + { + rt_free(oldfullpath); + } + if (newfullpath) + { + rt_free(newfullpath); + } /* not at same file system, return EXDEV */ return result; @@ -494,28 +708,52 @@ int dfs_file_ftruncate(struct dfs_fd *fd, off_t length) int result; /* fd is null or not a regular file system fd, or length is invalid */ - if (fd == NULL || fd->type != FT_REGULAR || length < 0) + if (fd == NULL || fd->vnode->type != FT_REGULAR || length < 0) return -EINVAL; - if (fd->fops->ioctl == NULL) + if (fd->vnode->fops->ioctl == NULL) return -ENOSYS; - result = fd->fops->ioctl(fd, RT_FIOFTRUNCATE, (void*)&length); + result = fd->vnode->fops->ioctl(fd, RT_FIOFTRUNCATE, (void*)&length); /* update current size */ if (result == 0) - fd->size = length; + fd->vnode->size = length; return result; } +int dfs_file_mmap2(struct dfs_fd *fd, struct dfs_mmap2_args *mmap2) +{ + int ret = 0; + + if (fd && mmap2) + { + if (fd->vnode->type != FT_DEVICE || !fd->vnode->fops->ioctl) + { + rt_set_errno(EINVAL); + } + else if (fd->vnode->type == FT_DEVICE && fd->vnode->fops->ioctl) + { + ret = fd->vnode->fops->ioctl(fd, RT_FIOMMAP2, mmap2); + if (ret != 0) + { + ret = ret > 0? ret : -ret; + rt_set_errno(ret); + } + } + } + + return ret; +} + #ifdef RT_USING_FINSH #include <finsh.h> -static struct dfs_fd fd; -static struct dirent dirent; void ls(const char *pathname) { + struct dfs_fd fd; + struct dirent dirent; struct stat stat; int length; char *fullpath, *path; @@ -537,6 +775,7 @@ void ls(const char *pathname) path = (char *)pathname; } + fd_init(&fd); /* list directory */ if (dfs_file_open(&fd, path, O_DIRECTORY) == 0) { @@ -595,9 +834,11 @@ FINSH_FUNCTION_EXPORT(rm, remove files or directories); void cat(const char *filename) { - uint32_t length; + struct dfs_fd fd; + int length = 0; char buffer[81]; + fd_init(&fd); if (dfs_file_open(&fd, filename, O_RDONLY) < 0) { rt_kprintf("Open %s failed\n", filename); @@ -607,15 +848,15 @@ void cat(const char *filename) do { - rt_memset(buffer, 0, sizeof(buffer)); - length = dfs_file_read(&fd, buffer, sizeof(buffer) - 1); + rt_memset(buffer, 0x0, sizeof(buffer)); + length = dfs_file_read(&fd, (void *)buffer, sizeof(buffer) - 1); if (length > 0) { - rt_kprintf("%s", buffer); + buffer[length] = '\0'; + rt_device_t out_device = rt_console_get_device(); + rt_device_write(out_device, 0, (void *)buffer, sizeof(buffer)); } - } - while (length > 0); - rt_kprintf("\n"); + } while (length > 0); dfs_file_close(&fd); } @@ -625,6 +866,7 @@ FINSH_FUNCTION_EXPORT(cat, print file); #define BUF_SZ 4096 static void copyfile(const char *src, const char *dst) { + struct dfs_fd fd; struct dfs_fd src_fd; rt_uint8_t *block_ptr; rt_int32_t read_bytes; @@ -637,6 +879,7 @@ static void copyfile(const char *src, const char *dst) return; } + fd_init(&src_fd); if (dfs_file_open(&src_fd, src, O_RDONLY) < 0) { rt_free(block_ptr); @@ -644,6 +887,7 @@ static void copyfile(const char *src, const char *dst) return; } + fd_init(&fd); if (dfs_file_open(&fd, dst, O_WRONLY | O_CREAT) < 0) { rt_free(block_ptr); diff --git a/components/dfs/src/dfs_fs.c b/components/dfs/src/dfs_fs.c index 2abad9dfeb..7724afc7bc 100644 --- a/components/dfs/src/dfs_fs.c +++ b/components/dfs/src/dfs_fs.c @@ -241,7 +241,7 @@ int dfs_mount(const char *device_name, for (ops = &filesystem_operation_table[0]; ops < &filesystem_operation_table[DFS_FILESYSTEM_TYPES_MAX]; ops++) - if ((*ops != NULL) && (strcmp((*ops)->name, filesystemtype) == 0)) + if ((*ops != NULL) && (strncmp((*ops)->name, filesystemtype, strlen((*ops)->name)) == 0)) break; dfs_unlock(); @@ -273,6 +273,7 @@ int dfs_mount(const char *device_name, { struct dfs_fd fd; + fd_init(&fd); if (dfs_file_open(&fd, fullpath, O_RDONLY | O_DIRECTORY) < 0) { rt_free(fullpath); @@ -312,6 +313,9 @@ int dfs_mount(const char *device_name, fs->path = fullpath; fs->ops = *ops; fs->dev_id = dev_id; + /* For UFS, record the real filesystem name */ + fs->data = (void *) filesystemtype; + /* release filesystem_table lock */ dfs_unlock(); @@ -447,7 +451,8 @@ int dfs_mkfs(const char *fs_name, const char *device_name) for (index = 0; index < DFS_FILESYSTEM_TYPES_MAX; index ++) { if (filesystem_operation_table[index] != NULL && - strcmp(filesystem_operation_table[index]->name, fs_name) == 0) + strncmp(filesystem_operation_table[index]->name, fs_name, + strlen(filesystem_operation_table[index]->name)) == 0) break; } dfs_unlock(); @@ -463,7 +468,7 @@ int dfs_mkfs(const char *fs_name, const char *device_name) return -1; } - return ops->mkfs(dev_id); + return ops->mkfs(dev_id, fs_name); } LOG_E("File system (%s) was not found.", fs_name); @@ -490,6 +495,7 @@ int dfs_statfs(const char *path, struct statfs *buffer) return fs->ops->statfs(fs, buffer); } + rt_set_errno(-ENOSYS); return -1; } @@ -625,7 +631,10 @@ int df(const char *path) result = dfs_statfs(path ? path : NULL, &buffer); if (result != 0) { - rt_kprintf("dfs_statfs failed.\n"); + if (rt_get_errno() == -ENOSYS) + rt_kprintf("The function is not implemented.\n"); + else + rt_kprintf("statfs failed: errno=%d.\n", rt_get_errno()); return -1; } diff --git a/components/dfs/src/dfs_posix.c b/components/dfs/src/dfs_posix.c index ed0647121d..e3efa5bed6 100644 --- a/components/dfs/src/dfs_posix.c +++ b/components/dfs/src/dfs_posix.c @@ -14,6 +14,16 @@ #include <dfs_private.h> #include <sys/errno.h> +#ifdef RT_USING_LWP +#include <lwp.h> +#endif + +/** + * @addtogroup FsPosixApi + */ + +/*@{*/ + /** * this function is a POSIX compliant version, which will open a file and * return a file descriptor according specified flags. @@ -42,17 +52,13 @@ int open(const char *file, int flags, ...) if (result < 0) { /* release the ref-count of fd */ - fd_put(d); - fd_put(d); + fd_release(fd); rt_set_errno(result); return -1; } - /* release the ref-count of fd */ - fd_put(d); - return fd; } RTM_EXPORT(open); @@ -94,7 +100,6 @@ int close(int fd) } result = dfs_file_close(d); - fd_put(d); if (result < 0) { @@ -103,7 +108,7 @@ int close(int fd) return -1; } - fd_put(d); + fd_release(fd); return 0; } @@ -141,15 +146,11 @@ ssize_t read(int fd, void *buf, size_t len) result = dfs_file_read(d, buf, len); if (result < 0) { - fd_put(d); rt_set_errno(result); return -1; } - /* release the ref-count of fd */ - fd_put(d); - return result; } RTM_EXPORT(read); @@ -185,15 +186,11 @@ ssize_t write(int fd, const void *buf, size_t len) result = dfs_file_write(d, buf, len); if (result < 0) { - fd_put(d); rt_set_errno(result); return -1; } - /* release the ref-count of fd */ - fd_put(d); - return result; } RTM_EXPORT(write); @@ -231,11 +228,10 @@ off_t lseek(int fd, off_t offset, int whence) break; case SEEK_END: - offset += d->size; + offset += d->vnode->size; break; default: - fd_put(d); rt_set_errno(-EINVAL); return -1; @@ -243,24 +239,17 @@ off_t lseek(int fd, off_t offset, int whence) if (offset < 0) { - fd_put(d); rt_set_errno(-EINVAL); return -1; } - if(offset != d->pos) + result = dfs_file_lseek(d, offset); + if (result < 0) { - result = dfs_file_lseek(d, offset); - if (result < 0) - { - fd_put(d); - rt_set_errno(result); + rt_set_errno(result); - return -1; - } + return -1; } - /* release the ref-count of fd */ - fd_put(d); return offset; } @@ -369,17 +358,15 @@ int fstat(int fildes, struct stat *buf) buf->st_mode = S_IFREG | S_IRUSR | S_IRGRP | S_IROTH | S_IWUSR | S_IWGRP | S_IWOTH; - if (d->type == FT_DIRECTORY) + if (d->vnode->type == FT_DIRECTORY) { buf->st_mode &= ~S_IFREG; buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; } - buf->st_size = d->size; + buf->st_size = d->vnode->size; buf->st_mtime = 0; - fd_put(d); - return RT_EOK; } RTM_EXPORT(fstat); @@ -409,7 +396,6 @@ int fsync(int fildes) ret = dfs_file_flush(d); - fd_put(d); return ret; } RTM_EXPORT(fsync); @@ -443,7 +429,6 @@ int fcntl(int fildes, int cmd, ...) va_end(ap); ret = dfs_file_ioctl(d, cmd, arg); - fd_put(d); } else ret = -EBADF; @@ -508,7 +493,6 @@ int ftruncate(int fd, off_t length) if (length < 0) { - fd_put(d); rt_set_errno(-EINVAL); return -1; @@ -516,15 +500,11 @@ int ftruncate(int fd, off_t length) result = dfs_file_ftruncate(d, length); if (result < 0) { - fd_put(d); rt_set_errno(result); return -1; } - /* release the ref-count of fd */ - fd_put(d); - return 0; } RTM_EXPORT(ftruncate); @@ -582,16 +562,14 @@ int mkdir(const char *path, mode_t mode) if (result < 0) { - fd_put(d); - fd_put(d); + fd_release(fd); rt_set_errno(result); return -1; } dfs_file_close(d); - fd_put(d); - fd_put(d); + fd_release(fd); return 0; } @@ -653,7 +631,7 @@ DIR *opendir(const char *name) if (t == NULL) { dfs_file_close(d); - fd_put(d); + fd_release(fd); } else { @@ -661,14 +639,12 @@ DIR *opendir(const char *name) t->fd = fd; } - fd_put(d); return t; } /* open failed */ - fd_put(d); - fd_put(d); + fd_release(fd); rt_set_errno(result); return NULL; @@ -711,7 +687,6 @@ struct dirent *readdir(DIR *d) sizeof(d->buf) - 1); if (result <= 0) { - fd_put(fd); rt_set_errno(result); return NULL; @@ -721,8 +696,6 @@ struct dirent *readdir(DIR *d) d->cur = 0; /* current entry index */ } - fd_put(fd); - return (struct dirent *)(d->buf + d->cur); } RTM_EXPORT(readdir); @@ -749,7 +722,6 @@ long telldir(DIR *d) } result = fd->pos - d->num + d->cur; - fd_put(fd); return result; } @@ -777,7 +749,6 @@ void seekdir(DIR *d, long offset) /* seek to the offset position of directory */ if (dfs_file_lseek(fd, offset) >= 0) d->num = d->cur = 0; - fd_put(fd); } RTM_EXPORT(seekdir); @@ -802,7 +773,6 @@ void rewinddir(DIR *d) /* seek to the beginning of directory */ if (dfs_file_lseek(fd, 0) >= 0) d->num = d->cur = 0; - fd_put(fd); } RTM_EXPORT(rewinddir); @@ -828,9 +798,8 @@ int closedir(DIR *d) } result = dfs_file_close(fd); - fd_put(fd); + fd_release(d->fd); - fd_put(fd); rt_free(d); if (result < 0) @@ -861,7 +830,9 @@ int chdir(const char *path) if (path == NULL) { dfs_lock(); +#ifdef DFS_USING_WORKDIR rt_kprintf("%s\n", working_directory); +#endif dfs_unlock(); return 0; @@ -895,9 +866,12 @@ int chdir(const char *path) /* close directory stream */ closedir(d); - +#ifdef RT_USING_LWP /* copy full path to working directory */ - strncpy(working_directory, fullpath, DFS_PATH_MAX); + lwp_setcwd(fullpath); +#else + rt_strncpy(working_directory, fullpath, DFS_PATH_MAX); +#endif /* release normalize directory path name */ rt_free(fullpath); @@ -930,6 +904,31 @@ int access(const char *path, int amode) /* ignore R_OK,W_OK,X_OK condition */ return 0; } +/** + * this function is a POSIX compliant version, which will set current + * working directory. + * + * @param buf the current directory. + * + * @return null. + */ +void setcwd(char *buf) +{ +#ifdef DFS_USING_WORKDIR + dfs_lock(); +#ifdef RT_USING_LWP + lwp_setcwd(buf); +#else + rt_strncpy(working_directory, buf, DFS_PATH_MAX); +#endif + dfs_unlock(); +#else + rt_kprintf(NO_WORKING_DIR); +#endif + + return ; +} +RTM_EXPORT(setcwd); /** * this function is a POSIX compliant version, which will return current @@ -943,8 +942,22 @@ int access(const char *path, int amode) char *getcwd(char *buf, size_t size) { #ifdef DFS_USING_WORKDIR + char *dir_buf = RT_NULL; + dfs_lock(); - strncpy(buf, working_directory, size); + +#ifdef RT_USING_LWP + dir_buf = lwp_getcwd(); +#else + dir_buf = &working_directory[0]; +#endif + + /* copy to buf parameter */ + if (buf) + { + rt_strncpy(buf, dir_buf, size); + } + dfs_unlock(); #else rt_kprintf(NO_WORKING_DIR); @@ -953,3 +966,5 @@ char *getcwd(char *buf, size_t size) return buf; } RTM_EXPORT(getcwd); + +/* @} */ diff --git a/components/drivers/Kconfig b/components/drivers/Kconfig index af4132f4e2..da0211683f 100755 --- a/components/drivers/Kconfig +++ b/components/drivers/Kconfig @@ -4,6 +4,10 @@ config RT_USING_DEVICE_IPC bool "Using device drivers IPC" default y +config RT_UNAMED_PIPE_NUMBER + int "The number of unamed pipe" + default 64 + if RT_USING_DEVICE_IPC config RT_USING_SYSTEM_WORKQUEUE bool "Using system default workqueue" @@ -45,6 +49,17 @@ menuconfig RT_USING_SERIAL default 64 endif +config RT_USING_TTY + bool "Using TTY SYSTEM" + depends on RT_USING_LWP + default y + +if RT_USING_TTY + config RT_TTY_DEBUG + bool "Using TTY DEBUG" + default n +endif + config RT_USING_CAN bool "Using CAN device drivers" default n @@ -86,6 +101,12 @@ if RT_USING_CPUTIME default y depends on ARCH_ARM_CORTEX_M0 || ARCH_ARM_CORTEX_M3 || ARCH_ARM_CORTEX_M4 || ARCH_ARM_CORTEX_M7 select PKG_USING_PERF_COUNTER + config RT_USING_CPUTIME_RISCV + bool "Use rdtime instructions for CPU time" + default y + depends on ARCH_RISCV64 + help + Some RISCV64 MCU Use rdtime instructions read CPU time. endif config RT_USING_I2C @@ -123,7 +144,19 @@ config RT_USING_ADC config RT_USING_DAC bool "Using DAC device drivers" default n - + +config RT_USING_NULL + bool "Using NULL device drivers" + default n + +config RT_USING_ZERO + bool "Using ZERO device drivers" + default n + +config RT_USING_RANDOM + bool "Using RANDOM device drivers" + default n + config RT_USING_PWM bool "Using PWM device drivers" default n @@ -182,6 +215,19 @@ config RT_USING_PM endif endif +config RT_USING_FDT + bool "Using fdt interface for device drivers" + default n + if RT_USING_FDT + config RT_USING_FDTLIB + bool "Using fdt lib for device drivers" + default y + config FDT_USING_DEBUG + bool "Using fdt debug function " + default n + + endif + config RT_USING_RTC bool "Using RTC device drivers" default n @@ -322,13 +368,8 @@ config RT_USING_SENSOR if RT_USING_SENSOR config RT_USING_SENSOR_CMD - bool "Enable sensor commands" - select PKG_USING_RT_VSNPRINTF_FULL + bool "Using Sensor cmd" default y - - config RT_USING_SENSOR_DOUBLE_FLOAT - bool "Using double floating as sensor data type" - default n endif config RT_USING_TOUCH @@ -340,6 +381,10 @@ config RT_USING_TOUCH default n endif +config RT_USING_LCD + bool "Using LCD graphic drivers" + default n + menuconfig RT_USING_HWCRYPTO bool "Using Hardware Crypto drivers" default n @@ -520,6 +565,11 @@ if RT_USING_INPUT_CAPTURE default 100 endif +config RT_USING_DEV_BUS + bool "Using Device Bus device drivers" + default y if RT_USING_LWP + default n if !RT_USING_LWP + menuconfig RT_USING_WIFI bool "Using Wi-Fi framework" default n @@ -671,6 +721,50 @@ menuconfig RT_USING_WIFI endif endif +menuconfig RT_USING_VIRTIO + bool "Using VirtIO device drivers" + default n + + if RT_USING_VIRTIO + choice + prompt "VirtIO Version" + default RT_USING_VIRTIO10 + + config RT_USING_VIRTIO10 + bool "VirtIO v1.0" + endchoice + + config RT_USING_VIRTIO_MMIO_ALIGN + bool "Using VirtIO MMIO alignment" + default y + + config RT_USING_VIRTIO_BLK + bool "Using VirtIO BLK" + default y + + config RT_USING_VIRTIO_NET + bool "Using VirtIO NET" + default y + + menuconfig RT_USING_VIRTIO_CONSOLE + bool "Using VirtIO Console" + default y + + if RT_USING_VIRTIO_CONSOLE + config RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR + int "Max number of port in VirtIO Console" + default 4 + endif + + config RT_USING_VIRTIO_GPU + bool "Using VirtIO GPU" + default y + + config RT_USING_VIRTIO_INPUT + bool "Using VirtIO Input" + default y + endif + menu "Using USB" config RT_USING_USB bool diff --git a/components/drivers/cputime/SConscript b/components/drivers/cputime/SConscript index 57c0b44fa8..e1b447e16b 100644 --- a/components/drivers/cputime/SConscript +++ b/components/drivers/cputime/SConscript @@ -9,6 +9,9 @@ cputime.c if GetDepend('RT_USING_CPUTIME_CORTEXM'): src += ['cputime_cortexm.c'] +if GetDepend('RT_USING_CPUTIME_RISCV'): + src += ['cputime_riscv.c'] + group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_CPUTIME'], CPPPATH = CPPPATH) Return('group') diff --git a/components/drivers/cputime/cputime.c b/components/drivers/cputime/cputime.c index 490f5725c1..5fe0b07cb1 100644 --- a/components/drivers/cputime/cputime.c +++ b/components/drivers/cputime/cputime.c @@ -51,11 +51,11 @@ uint64_t clock_cpu_gettime(void) * * @return the microsecond */ -uint32_t clock_cpu_microsecond(uint32_t cpu_tick) +uint64_t clock_cpu_microsecond(uint64_t cpu_tick) { float unit = clock_cpu_getres(); - return (uint32_t)((cpu_tick * unit) / 1000); + return (uint64_t)((cpu_tick * unit) / 1000); } /** @@ -66,11 +66,11 @@ uint32_t clock_cpu_microsecond(uint32_t cpu_tick) * * @return the millisecond */ -uint32_t clock_cpu_millisecond(uint32_t cpu_tick) +uint64_t clock_cpu_millisecond(uint64_t cpu_tick) { float unit = clock_cpu_getres(); - return (uint32_t)((cpu_tick * unit) / (1000 * 1000)); + return (uint64_t)((cpu_tick * unit) / (1000 * 1000)); } /** diff --git a/components/drivers/cputime/cputime_riscv.c b/components/drivers/cputime/cputime_riscv.c new file mode 100644 index 0000000000..7e327fc2d4 --- /dev/null +++ b/components/drivers/cputime/cputime_riscv.c @@ -0,0 +1,39 @@ +#include <rthw.h> +#include <rtdevice.h> +#include <rtthread.h> + +#include <board.h> + + +#define TIMER_FREQ (24000000) + +/* Use Cycle counter of Data Watchpoint and Trace Register for CPU time */ + +static float riscv_cputime_getres(void) +{ + float ret = 1000 * 1000 * 1000; + + ret = ret / TIMER_FREQ; + return ret; +} + +static uint64_t riscv_cputime_gettime(void) +{ + uint64_t time_elapsed; + __asm__ __volatile__( + "rdtime %0" + : "=r"(time_elapsed)); + return time_elapsed; +} + +const static struct rt_clock_cputime_ops _riscv_ops = + { + riscv_cputime_getres, + riscv_cputime_gettime}; + +int riscv_cputime_init(void) +{ + clock_cpu_setops(&_riscv_ops); + return 0; +} +INIT_BOARD_EXPORT(riscv_cputime_init); diff --git a/components/drivers/fdt/README.md b/components/drivers/fdt/README.md new file mode 100644 index 0000000000..d8a049c8fc --- /dev/null +++ b/components/drivers/fdt/README.md @@ -0,0 +1,41 @@ +# fdt + +## 1、介绍 +fdt基于libfdt进行封装,可实现在内存或文件系统中加载dtb设备树,对内存中的设备树修改、解析,并转换为设备节点树,通过该节点树开发者可通过设备树信息开发驱动。 + +### 1.1 目录结构 +| 名称 | 说明 | +| ---- | ---- | +| docs | 文档目录 | +| examples | 例子目录,并有相应的一些说明 | +| inc | 头文件目录 | +| src | 源代码目录 | + +### 1.2 许可证 +fdt package 遵循 GPL-3.0 许可,详见 LICENSE 文件。 + +### 1.3 依赖 +- RT-Thread 3.0+ + +## 2、如何打开 fdt +使用 fdt package 需要在 RT-Thread 的包管理器中选择它,具体路径如下: + +``` +RT-Thread online packages + tools packages ---> + [*] Device Tree package in RT-Thread +``` + +## 3、使用 fdt +在打开 fdt package 后,当进行 bsp 编译时,它会被加入到 bsp 工程中进行编译。 + +* 完整的 API 手册可以访问这个[链接](docs/api.md) +* 更多文档位于 [`/docs`](/docs) 下,使用前 **务必查看** + +## 4、注意事项 +如果发生`libfdt`库冲突,在package管理菜单中取消选择`Enable libfdt` + +## 5、联系方式 + +* 维护:GuEe-GUI +* 主页:https://github.com/GuEe-GUI/fdt
\ No newline at end of file diff --git a/components/drivers/fdt/SConscript b/components/drivers/fdt/SConscript new file mode 100644 index 0000000000..d916b2dfbc --- /dev/null +++ b/components/drivers/fdt/SConscript @@ -0,0 +1,16 @@ +# RT-Thread building script for bridge + +import os +from building import * + +cwd = GetCurrentDir() +objs = [] +list = os.listdir(cwd) + +if GetDepend('RT_USING_FDT'): + for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/components/drivers/fdt/docs/README.md b/components/drivers/fdt/docs/README.md new file mode 100644 index 0000000000..f57e0a8e75 --- /dev/null +++ b/components/drivers/fdt/docs/README.md @@ -0,0 +1,13 @@ +# 文档 + +## 软件包地址 + +- https://gitee.com/GuEe_GUI/fdt + +## 文档列表 + +|文件名 |描述| +|:----- |:----| +|[examples.md](examples.md) |示例程序| +|[version.md](version.md) |版本信息| +|[api.md](api.md) |API 说明| diff --git a/components/drivers/fdt/docs/api.md b/components/drivers/fdt/docs/api.md new file mode 100644 index 0000000000..13471de06f --- /dev/null +++ b/components/drivers/fdt/docs/api.md @@ -0,0 +1,434 @@ +# fdt load API + +## 从文件系统上读取设备树 +```c +void *fdt_load_from_fs(char *dtb_filename) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_filename | 设备树文件名 | +| **返回** | **描述** | +|void* | 设备树在内存上的地址 | + +## 从内存上读取设备树 +```c +void *fdt_load_from_memory(void *dtb_ptr, rt_bool_t is_clone) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_ptr | 设备树在内存上的内存地址 | +|is_clone | 是否克隆到新的内存区域 | +| **返回** | **描述** | +|void* | 设备树在内存上的地址 | + +# fdt set API + +## 设置Linux启动参数 +```c +rt_size_t fdt_set_linux_cmdline(void *fdt, char *cmdline) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树指针 | +|cmdline | 启动参数 | +| **返回** | **描述** | +|rt_size_t | 修改设备树后设备树的大小 | + +## 设置Linux init ramdisk +```c +rt_size_t fdt_set_linux_initrd(void *fdt, rt_uint64_t initrd_addr, rt_size_t initrd_size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树指针 | +|initrd_addr | init ramdisk 内存地址 | +|initrd_size | init 大小 | +| **返回** | **描述** | +|rt_size_t | 修改设备树后设备树的大小 | + +## 设置节点属性值 +```c +rt_size_t fdt_set_dtb_property(void *fdt, char *pathname, char *property_name, rt_uint32_t *cells, rt_size_t cells_size); +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树指针 | +|pathname | 节点路径 | +|property_name | 属性名称 | +|cells | 属性值地址 | +|cells_size | 属性值长度 | +| **返回** | **描述** | +|rt_size_t | 修改设备树后设备树的大小 | + +## 添加保留内存 +```c +rt_size_t fdt_add_dtb_memreserve(void *fdt, rt_uint64_t address, rt_uint64_t size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树指针 | +|address | 保留内存起始地址 | +|size | 保留内存大小 | +| **返回** | **描述** | +|rt_size_t | 修改设备树后设备树的大小 | + +## 删除保留内存 +```c +rt_size_t fdt_del_dtb_memreserve(void *fdt, rt_uint64_t address) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树指针 | +|address | 保留内存起始地址 | +| **返回** | **描述** | +|rt_size_t | 修改设备树后设备树的大小 | + +# fdt get API + +## 获取设备树软件包执行状态 +```c +rt_err_t fdt_get_exec_status() +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|无 | 无参数 | +| **返回** | **描述** | +|FDT_RET_NO_MEMORY | 内存不足 | +|FDT_RET_NO_LOADED | 设备树还未加载进内存 | +|FDT_RET_GET_OK | 执行成功 | +|FDT_RET_GET_EMPTY | 读取数据为空 | + +## 将原始设备树转换为设备节点树 +```c +struct dtb_node *fdt_get_dtb_list(void *fdt) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|fdt | 设备树在内存上的地址 | +| **返回** | **描述** | +|struct dtb_node * | 设备节点树头指针 | +## 释放设备节点树内存 +```c +void fdt_free_dtb_list(struct dtb_node *dtb_node_head) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node_head | 设备节点树头节点 | +| **返回** | **描述** | +|无返回值 | 无描述 | + +示例:加载设备树 + +```c +#include <rtthread.h> +#include <fdt.h> + +int main() +{ + void *fdt = fdt_load_from_fs("sample.dtb"); + if (fdt != RT_NULL) + { + struct dtb_node *dtb_node_list = fdt_get_dtb_list(fdt); + + if (dtb_node_list != RT_NULL) + { + /* load dtb node list OK */ + + /* free dtb_node_list here */ + fdt_free_dtb_list(dtb_node_list); + } + + rt_free(fdt); + } + + return 0; +} +``` + +## 打印设备树信息 +```c +void fdt_get_dts_dump(struct dtb_node *dtb_node_head) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node_head | 设备节点树头节点 | +| **返回** | **描述** | +|无返回值 | 无描述 | + +## 遍历设备节点树并使用程序定义的回调函数 +```c +void fdt_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (*callback(struct dtb_node *dtb_node))) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node_head | 设备节点树头节点 | +|callback | 程序定义的回调函数 | +| **返回** | **描述** | +|无返回值 | 无描述 | + +示例:遍历设备树节点,并打印每个节点名称 + +```c +#include <rtthread.h> +#include <fdt.h> + +void callback(struct dtb_node *node) +{ + rt_kprintf("this node's name is %s\n", node->name); +} + +int main() +{ + /* loaded dtb_node */ + extern struct dtb_node *dtb_node_list; + + if (dtb_node_list != RT_NULL) + { + fdt_get_enum_dtb_node(dtb_node_list, callback); + } + + return 0; +} +``` + +## 通过节点名称查找节点 +```c +struct dtb_node *fdt_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename) +``` +```c +struct dtb_node *fdt_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|nodename | 查找节点的名称 | +| **返回** | **描述** | +|struct dtb_node * | 返回查找的节点 | +|RT_NULL | 未找到为RT_NULL | + +## 通过节点路径查找节点 +```c +struct dtb_node *fdt_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|pathname | 查找节点的路径 | +| **返回** | **描述** | +|struct dtb_node * | 返回查找的节点 | +|RT_NULL | 未找到为RT_NULL | + +## 通过节点phandle值查找节点 +```c +struct dtb_node *fdt_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle) +``` +```c +struct dtb_node *fdt_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|handle | 查找节点的phandle值 | +| **返回** | **描述** | +|struct dtb_node * | 返回查找的节点 | +|RT_NULL | 未找到为RT_NULL | + +DFS和BFS是用于区分不同深度节点的搜索方法,时间复杂度和空间复杂度都较高,建议使用路径查找。 + +## 读取节点属性值的单位 +```c +void fdt_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|addr_cells | 返回的地址块的单位(u32)大小 | +|size_cells | 返回的尺寸块的单位(u32)大小 | +| **返回** | **描述** | +|无返回值 | 无描述 | + + +## 读取节点属性值 +```c +void *fdt_get_dtb_node_property(struct dtb_node *dtb_node, const char *property_name, rt_size_t *property_size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|property_name | 属性名称 | +|property_size | 属性大小 | +| **返回** | **描述** | +|void * | 无描述 | +|RT_NULL | 该设备树没有该属性 | + +读取的值为在设备树中存储的值,CPU小端模式下可能需要使用其他API进行转换才是正确的值 + +## 读取预留内存信息 +```c +struct dtb_memreserve *fdt_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|memreserve_size | 返回的内存信息数组长度 | +| **返回** | **描述** | +|struct dtb_memreserve *| 内存预留信息数组的内存地址 | +|RT_NULL | 该设备树没有预留内存信息 | + +## 读取节点的状态 +```c +rt_bool_t fdt_get_dtb_node_status(struct dtb_node *dtb_node) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +| **返回** | **描述** | +|RT_FALSE | 状态为禁用 | +|RT_TRUE | 状态为使用 | + +## 用于参数为字符串列表的函数 +```c +fdt_string_list(string, ...) +``` +| 参数 | 描述 | +|:------------------|:------------------------------------| +|string | 字符串集合 | + +## 读取节点compatible标志匹配 +```c +rt_bool_t fdt_get_dtb_node_compatible_match(struct dtb_node *dtb_node, char **compatibles) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|dtb_node | 设备节点树节点 | +|compatibles | 所有要匹配的字符串列表 | +| **返回** | **描述** | +|RT_FALSE | 匹配失败 | +|RT_TRUE | 匹配成功 | + +## 读取属性值中的字符串 +```c +char *fdt_get_dtb_string_list_value(void *value, int size, int index) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|value | 节点属性的内存地址 | +|size | 节点属性的尺寸 | +|index | 要读取字符串的索引 | +| **返回** | **描述** | +|char * | 字符串的内存地址 | +|RT_NULL | 该索引没有字符串 | + +## 读取值为字符串属性下一个字符串 +```c +char *fdt_get_dtb_string_list_value_next(void *value, void *end) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|value | 节点属性的内存地址 | +|end | 节点属性的结束地址 | +| **返回** | **描述** | +|char * | 字符串的内存地址 | +|RT_NULL | 没有下一个字符串 | + +## 读取值为u32属性的值 +```c +rt_uint32_t fdt_get_dtb_cell_value(void *value) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|value | 节点属性的内存地址 | +| **返回** | **描述** | +|rt_uint32_t | 该值小端的值 | + +## 读取值为u8属性的值 +```c +rt_uint8_t fdt_get_dtb_byte_value(void *value) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|value | 节点属性的内存地址 | +| **返回** | **描述** | +|rt_uint8_t | 该值小端的值 | + +# fdt foreach API + +## 遍历属性中字符串宏 +```c +for_each_property_string(node_ptr, property_name, str, size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|node_ptr | 设备节点树节点 | +|property_name | 节点属性名称 | +|str | 每次遍历到的字符串 | +|size | 用于保存节点属性的尺寸 | + +## 遍历属性中u32宏 +```c +for_each_property_cell(node_ptr, property_name, value, list, size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|node_ptr | 设备节点树节点 | +|property_name | 节点属性名称 | +|value | 每次遍历到的值 | +|list | 用于保存节点属性的值内存地址 | +|size | 用于保存节点属性的尺寸 | + +## 遍历属性中u8宏 +```c +for_each_property_byte(node_ptr, property_name, value, list, size) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|node_ptr | 设备节点树节点 | +|property_name | 节点属性名称 | +|value | 每次遍历到的值 | +|list | 用于保存节点属性的值内存地址 | +|size | 用于保存节点属性的尺寸 | + +## 遍历子节点宏 +```c +for_each_node_child(node_ptr) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|node_ptr | 设备节点树节点 | + +## 遍历兄弟节点宏 +```c +for_each_node_sibling(node_ptr) +``` + +| 参数 | 描述 | +|:------------------|:------------------------------------| +|node_ptr | 设备节点树节点 | diff --git a/components/drivers/fdt/docs/examples.md b/components/drivers/fdt/docs/examples.md new file mode 100644 index 0000000000..aa3e655987 --- /dev/null +++ b/components/drivers/fdt/docs/examples.md @@ -0,0 +1,81 @@ +# fdt示例 # + +在`examples`文件夹中存放`bcm2711-rpi-4-b.dtb`和`vexpress-v2p-ca9.dtb`可供测试,如果系统可以从bootloader或其他方式获取到bsp本身的dtb,也可以通过修改示例程序进行测试 + +## fdt_dump +```bash +fdt_dump vexpress-v2p-ca9.dtb +``` + +#### 示例结果 #### +```bash +/dts-v1/; + +/ { + model = "V2P-CA9"; + arm,hbi = <0x191>; + arm,vexpress,site = <0xf>; + compatible = "arm,vexpress,v2p-ca9", "arm,vexpress"; + interrupt-parent = <0x1>; + #address-cells = <0x1>; + #size-cells = <0x1>; + + chosen { + }; + + aliases { + serial0 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000"; + serial1 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@a000"; + serial2 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@b000"; + serial3 = "/smb@4000000/motherboard/iofpga@7,00000000/uart@c000"; + i2c0 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@16000"; + i2c1 = "/smb@4000000/motherboard/iofpga@7,00000000/i2c@2000"; + }; + +...... 省略 + + hsb@e0000000 { + compatible = "simple-bus"; + #address-cells = <0x1>; + #size-cells = <0x1>; + ranges = <0x0 0xe0000000 0x20000000>; + #interrupt-cells = <0x1>; + interrupt-map-mask = <0x0 0x3>; + interrupt-map = <0x0 0x0 0x1 0x0 0x24 0x4 0x0 0x1 0x1 0x0 0x25 0x4 0x0 0x2 0x1 0x0 0x26 0x4 0x0 0x3 0x1 0x0 0x27 0x4>; + }; +}; +``` + +## fdt_test +```bash +fdt_test +``` + +#### 示例结果 #### +```bash +name = uart@9000 +reg = <0x9000,0x1000>; +compatible = "arm,pl011","arm,primecell"; + +name = cpus +path = /cpus/cpu@0/ +path = /cpus/cpu@1/ +path = /cpus/cpu@2/ +path = /cpus/cpu@3/ + +name = user1, lable = v2m:green:user1 +name = user2, lable = v2m:green:user2 +name = user3, lable = v2m:green:user3 +name = user4, lable = v2m:green:user4 +name = user5, lable = v2m:green:user5 +name = user6, lable = v2m:green:user6 +name = user7, lable = v2m:green:user7 +name = user8, lable = v2m:green:user8 + +/memreserve/ 0x0000000000000000 0x0000000000001000; + +phandle = <0x9> +name = bt_pins +path = /soc/gpio@7e200000/bt_pins/ +brcm,pins = [2d 00] +```
\ No newline at end of file diff --git a/components/drivers/fdt/docs/version.md b/components/drivers/fdt/docs/version.md new file mode 100644 index 0000000000..3c3499d91a --- /dev/null +++ b/components/drivers/fdt/docs/version.md @@ -0,0 +1,7 @@ +# 版本和修订 # + +| Date | Version | Author | Note | +| -------- | :-----: | :---- | :---- | +| 2021-9-1 | v0.0.1 | GuEe-GUI | 初始版本 | +| 2021-11-11 | v1.0.0 | GuEe-GUI | API确定版本 | +| | | | |
\ No newline at end of file diff --git a/components/drivers/fdt/examples/SConscript b/components/drivers/fdt/examples/SConscript new file mode 100644 index 0000000000..30867a1de3 --- /dev/null +++ b/components/drivers/fdt/examples/SConscript @@ -0,0 +1,10 @@ + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('FDT', src, depend = ['FDT_USING_DEBUG'], CPPPATH = CPPPATH) + +Return('group')
\ No newline at end of file diff --git a/components/drivers/fdt/examples/fdt_test.c b/components/drivers/fdt/examples/fdt_test.c new file mode 100644 index 0000000000..4bf2879256 --- /dev/null +++ b/components/drivers/fdt/examples/fdt_test.c @@ -0,0 +1,143 @@ +#include <rtthread.h> +#include "dtb_node.h" + +int dtb_test() +{ + void *fdt; + + if ((fdt = dtb_node_load_from_fs("vexpress-v2p-ca9.dtb")) != RT_NULL) + { + struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt); + if (dtb_node_list != RT_NULL) + { + struct dtb_node *serial0 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/iofpga@7,00000000/uart@9000"); + struct dtb_node *cpu = dtb_node_get_dtb_node_by_path(dtb_node_list, "/cpus"); + struct dtb_node *user1 = dtb_node_get_dtb_node_by_path(dtb_node_list, "/smb@4000000/motherboard/leds/user1"); + + if (serial0 != RT_NULL) + { + int property_size; + rt_uint32_t u32_value; + rt_uint32_t *u32_ptr; + char *str_ptr; + + rt_kprintf("name = %s\n", serial0->name); + + rt_kputs("reg = <"); + for_each_property_cell(serial0, "reg", u32_value, u32_ptr, property_size) + { + rt_kprintf("0x%x,", u32_value); + } + rt_kputs("\b>;\n"); + + rt_kputs("compatible = "); + for_each_property_string(serial0, "compatible", str_ptr, property_size) + { + rt_kprintf("\"%s\",", str_ptr); + } + rt_kputs("\b;\n"); + } + + if (cpu != RT_NULL) + { + struct dtb_node *cpu_child = cpu; + + rt_kprintf("\nname = %s\n", cpu->name); + + for_each_node_child(cpu_child) + { + rt_kprintf("path = %s\n", cpu_child->path); + } + } + + if (user1 != RT_NULL) + { + struct dtb_node *user = user1; + + rt_kprintf("\nname = %s, lable = %s\n", user1->name, dtb_node_get_dtb_node_property(user1, "label", RT_NULL)); + + for_each_node_sibling(user) + { + rt_kprintf("name = %s, lable = %s\n", user->name, dtb_node_get_dtb_node_property(user, "label", RT_NULL)); + } + } + } + dtb_node_free_dtb_list(dtb_node_list); + } + + if ((fdt = dtb_node_load_from_fs("bcm2711-rpi-4-b.dtb")) != RT_NULL) + { + struct dtb_node *dtb_node_list = dtb_node_get_dtb_list(fdt); + if (dtb_node_list != RT_NULL) + { + struct dtb_node *bt_pins; + int memreserve_size; + struct dtb_memreserve *memreserve; + + bt_pins = dtb_node_get_dtb_node_by_name_DFS(dtb_node_list, "bt_pins"); + bt_pins = dtb_node_get_dtb_node_by_name_BFS(dtb_node_list, bt_pins->name); + bt_pins = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node_list, bt_pins->handle); + bt_pins = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node_list, bt_pins->handle); + + memreserve = dtb_node_get_dtb_memreserve(bt_pins, &memreserve_size); + if (memreserve_size > 0) + { + int i; + for (i = 0; i < memreserve_size; ++i) + { + rt_kputs("\n/memreserve/\t"); + if (IN_64BITS_MODE) + { + rt_kprintf("0x%016x 0x%016x;", memreserve[i].address, memreserve[i].size); + } + else + { + rt_kprintf("0x%08x%08x 0x%08x%08x;", memreserve[i].address, memreserve[i].size); + } + } + } + + if (bt_pins != RT_NULL) + { + int property_size; + rt_uint8_t u8_value; + rt_uint8_t *u8_ptr; + + rt_kprintf("\n\nphandle = <0x%x>\n", bt_pins->handle); + rt_kprintf("name = %s\n", bt_pins->name); + rt_kprintf("path = %s\n", bt_pins->path); + + rt_kputs("brcm,pins = ["); + for_each_property_byte(bt_pins, "brcm,pins", u8_value, u8_ptr, property_size) + { + rt_kprintf("%02x ", u8_value); + } + rt_kputs("\b]\n"); + } + } + dtb_node_free_dtb_list(dtb_node_list); + } + return 0; +} +MSH_CMD_EXPORT(dtb_test, dtb API test); + +int dtb_dump(int argc, char** argv) +{ + struct dtb_node *dtb_root_node = get_dtb_node_head(); + if(argc == 1) + { + if (dtb_root_node != RT_NULL) + { + dtb_node_get_dts_dump(dtb_root_node); + } + } + else + { + rt_kprintf("invailed parameter\n"); + } + + return 0; +} +MSH_CMD_EXPORT(dtb_dump, dtb dump from mem); + + diff --git a/components/drivers/fdt/inc/dtb_node.h b/components/drivers/fdt/inc/dtb_node.h new file mode 100644 index 0000000000..be304a48c8 --- /dev/null +++ b/components/drivers/fdt/inc/dtb_node.h @@ -0,0 +1,379 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#ifndef _DTB_NODE_H__ +#define _DTB_NODE_H__ + +#include "libfdt_env.h" +#include <rtthread.h> + +//#define RT_DTB_DEBUG +#ifdef RT_DTB_DEBUG +#define debug(fmt, args...) rt_kprintf(fmt, ##args) +#else +#define debug(fmt, args...) +#endif + +#define ERR_PTR(err) ((void *)((long)(err))) +#define PTR_ERR(ptr) ((long)(ptr)) +#define IS_ERR(ptr) ((unsigned long)(ptr) > (unsigned long)(-1000)) + +#define DEV_ROOT_NODE_ADDR_CELLS_DEFAULT 2 +#define DEV_ROOT_NODE_SIZE_CELLS_DEFAULT 1 +/* will be optimized to only u64 or u32 by gcc */ +#define IN_64BITS_MODE (sizeof(void *) == 8) + +#define FDT_ROOT_ADDR_CELLS_DEFAULT 1 +#define FDT_ROOT_SIZE_CELLS_DEFAULT 1 + +#define FDT_DTB_ALL_NODES_PATH_SIZE (32 * 1024) +#define FDT_DTB_PAD_SIZE 1024 + +#define FDT_RET_NO_MEMORY 2 +#define FDT_RET_NO_LOADED 1 +#define FDT_RET_GET_OK 0 +#define FDT_RET_GET_EMPTY (-1) + +typedef uint32_t phandle; + +struct dtb_memreserve +{ + uintptr_t address; + size_t size; +}; + +struct dtb_header +{ + char root, zero; /* "/" */ + struct dtb_memreserve *memreserve; + size_t memreserve_sz; +}; + +struct dtb_property +{ + const char *name; + int size; + void *value; + + struct dtb_property *next; +}; + +struct dtb_node +{ + union + { + const char *name; + const struct dtb_header *header; + }; + const char *path; + phandle handle; + + struct dtb_property *properties; + struct dtb_node *parent; + struct dtb_node *child; + struct dtb_node *sibling; +}; + +#define FDT_MAX_PHANDLE_ARGS 16 + +/** + * struct dtb_node_phandle_args - structure to hold phandle and arguments + * + * This is used when decoding a phandle in a device tree property. Typically + * these look like this: + * + * wibble { + * phandle = <5>; + * }; + * + * ... + * some-prop = <&wibble 1 2 3> + * + * Here &node is the phandle of the node 'wibble', i.e. 5. There are three + * arguments: 1, 2, 3. + * + * So when decoding the phandle in some-prop, np will point to wibble, + * args_count will be 3 and the three arguments will be in args. + * + * @np: Node that the phandle refers to + * @args_count: Number of arguments + * @args: Argument values + */ +struct fdt_phandle_args +{ + struct dtb_node *np; + int args_count; + uint32_t args[FDT_MAX_PHANDLE_ARGS]; +}; + +/* + * A single signal can be specified via a range of minimal and maximal values + * with a typical value, that lies somewhere inbetween. + */ +struct timing_entry +{ + uint32_t min; + uint32_t typ; + uint32_t max; +}; + +void *get_fdt_blob(void); +struct dtb_node *get_dtb_node_head(void); +rt_bool_t dtb_node_active(void); +int device_tree_setup(void *mem_addr); + +void *dtb_node_load_from_fs(char *dtb_filename); +void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone); + +size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline); +size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size); + +size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size); +size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size); +size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address); + +int dtb_node_get_exec_status(); +struct dtb_node *dtb_node_get_dtb_list(void *fdt); +void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head); +void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head); +void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void(callback(struct dtb_node *dtb_node))); + +struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename); +struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename); +struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname); +struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle); +struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle); +void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells); +struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size); +uint8_t dtb_node_get_dtb_byte_value(void *value); + +char *dtb_node_get_dtb_string_list_value(void *value, int size, int index); +char *dtb_node_get_dtb_string_list_value_next(void *value, void *end); +uint32_t dtb_node_get_dtb_cell_value(void *value); + +rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node); +rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles); + +/*dtb_node_access.c */ +int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp); +uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def); +int dtb_node_read_u32_index(const struct dtb_node *node, const char *propname, int index, + uint32_t *outp); +uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index, + uint32_t def); +int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname, + uint32_t *out_values, size_t sz); +int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname, + int index, uint32_t *outp); +int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def); +int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp); +uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def); + +int dtb_node_n_addr_cells(const struct dtb_node *dn); +int dtb_node_n_size_cells(const struct dtb_node *dn); +int dtb_node_simple_addr_cells(const struct dtb_node *np); +int dtb_node_simple_size_cells(const struct dtb_node *np); + +struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev); +struct dtb_node *dtb_node_find_node_by_phandle(phandle handle); +struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible); +void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size); +struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size); +const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from, const char *propname, const void *propval, int proplen); +struct dtb_node *dtb_node_find_node_opts_by_path(const char *path, + const char **opts); + +static inline struct dtb_node *dtb_node_find_node_by_path(const char *path) +{ + return dtb_node_find_node_opts_by_path(path, NULL); +} + +rt_bool_t dtb_node_device_is_available(const struct dtb_node *device); +struct dtb_node *dtb_node_get_parent(const struct dtb_node *node); +int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname, + const char *string); +int dtb_node_property_read_string_helper(const struct dtb_node *dn, + const char *propname, const char **out_strs, + size_t sz, int skip); +/** + * of_property_read_string_index() - Find and read a string from a multiple + * strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @index: index of the string in the list of strings + * @out_string: pointer to null terminated return string, modified only if + * return value is 0. + * + * Search for a property in a device tree node and retrieve a null + * terminated string value (pointer to data, not a copy) in the list of strings + * contained in that property. + * Returns 0 on success, -EINVAL if the property does not exist, -ENODATA if + * property does not have a value, and -EILSEQ if the string is not + * null-terminated within the length of the property data. + * + * The out_string pointer is modified only if a valid string can be decoded. + */ +static inline int dtb_node_property_read_string_index(const struct dtb_node *dn, + const char *propname, + int index, const char **output) +{ + int rc = dtb_node_property_read_string_helper(dn, propname, output, 1, index); + return rc < 0 ? rc : 0; +} + +/** + * of_property_count_strings() - Find and return the number of strings from a + * multiple strings property. + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * + * Search for a property in a device tree node and retrieve the number of null + * terminated string contain in it. Returns the number of strings on + * success, -EINVAL if the property does not exist, -ENODATA if property + * does not have a value, and -EILSEQ if the string is not null-terminated + * within the length of the property data. + */ +static inline int dtb_node_property_count_strings(const struct dtb_node *dn, + const char *propname) +{ + return dtb_node_property_read_string_helper(dn, propname, NULL, 0, 0); +} + +struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn, + const char *phandle_name, int index); +int dtb_node_parse_phandle_with_args(const struct dtb_node *dn, + const char *list_name, const char *cells_name, + int index, struct fdt_phandle_args *out_args); +int dtb_node_count_phandle_with_args(const struct dtb_node *dn, + const char *list_name, const char *cells_name); + +/* dtb_node_addr.c */ +const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index, + uint64_t *size, unsigned int *flags); + +#define dtb_node_string_list(string, ...) ((char *[]){string, ##__VA_ARGS__, NULL}) + +#define for_each_property_string(node_ptr, property_name, str, size) \ + for (str = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \ + size += (typeof(size))(size_t)str; \ + str && *str; \ + str = dtb_node_get_dtb_string_list_value_next((void *)str, (void *)(size_t)size)) + +#define for_each_property_cell(node_ptr, property_name, value, list, size) \ + for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \ + value = dtb_node_get_dtb_cell_value(list), \ + size /= sizeof(uint32_t); \ + size > 0; \ + value = dtb_node_get_dtb_cell_value(++list), --size) + +#define for_each_property_byte(node_ptr, property_name, value, list, size) \ + for (list = dtb_node_get_dtb_node_property_value(node_ptr, property_name, &size), \ + value = dtb_node_get_dtb_byte_value(list); \ + size > 0; \ + value = dtb_node_get_dtb_byte_value(++list), --size) + +#define for_each_node_child(node_ptr) \ + for (node_ptr = (node_ptr ? node_ptr->child : NULL); \ + node_ptr != NULL; \ + node_ptr = node_ptr->sibling) + +#define for_each_node_sibling(node_ptr) \ + for (node_ptr = (node_ptr ? node_ptr->sibling : NULL); \ + node_ptr != NULL; \ + node_ptr = node_ptr->sibling) + +#define for_each_of_allnodes_from(from, dn) \ + for (dn = dtb_node_find_all_nodes(from); dn; dn = dtb_node_find_all_nodes(dn)) + +#define for_each_of_allnodes(dn) for_each_of_allnodes_from(NULL, dn) + +#define dtb_node_get(x) (x) +static inline void dtb_node_put(const struct dtb_node *np) +{ +} + +/* Helper to read a big number; size is in cells (not bytes) */ +static inline uint64_t dtb_node_read_number(const uint32_t *cell, int size) +{ + uint64_t r = 0; + while (size--) + r = (r << 32) | fdt32_to_cpu(*(cell++)); + return r; +} + +/** + * ofnode_valid() - check if an ofnode is valid + * + * @return true if the reference contains a valid ofnode, RT_FALSE if it is NULL + */ +static inline rt_bool_t dtb_node_valid(const struct dtb_node *node) +{ + if (dtb_node_active()) + return node != NULL; + return RT_FALSE; +} + +/*dtb_base.c */ +rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname); +const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep); +const char *dtb_node_read_string(const struct dtb_node *node, const char *propname); +const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name); +int dtb_node_read_u32_array(const struct dtb_node *node, const char *propname, + uint32_t *out_values, size_t sz); +struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node); +struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node); +struct dtb_node *dtb_node_get_parent(const struct dtb_node *node); +const char *dtb_node_get_name(const struct dtb_node *node); +struct dtb_node *dtb_node_get_by_phandle(uint phandle); +int dtb_node_read_size(const struct dtb_node *node, const char *propname); +size_t dtb_node_get_addr_index(const struct dtb_node *node, int index); +size_t dtb_node_get_addr(const struct dtb_node *node); +int dtb_node_stringlist_search(const struct dtb_node *node, const char *property, + const char *string); +int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index, + const char **outp); +int dtb_node_read_string_count(const struct dtb_node *node, const char *property); +struct dtb_node *dtb_node_path(const char *path); +const char *dtb_node_get_chosen_prop(const char *name); +struct dtb_node *dtb_node_get_chosen_node(const char *name); +const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp); +rt_bool_t dtb_node_is_available(const struct dtb_node *node); +size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property, + size_t *sizep); +const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname, size_t sz); +int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num); +int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len, + const void *value); +int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value); +int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value); +int dtb_node_irq_get(struct dtb_node *dev, int index); +int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name); +int dtb_node_irq_count(struct dtb_node *dev); + +/** + * dtb_node_for_each_subnode() - iterate over all subnodes of a parent + * + * @node: child node (ofnode, lvalue) + * @parent: parent node (ofnode) + * + * This is a wrapper around a for loop and is used like so: + * + * ofnode node; + * + * dtb_node_for_each_subnode(node, parent) { + * Use node + * ... + * } + * + * Note that this is implemented as a macro and @node is used as + * iterator in the loop. The parent variable can be a constant or even a + * literal. + */ +#define dtb_node_for_each_subnode(node, parent) \ + for (node = dtb_node_first_subnode(parent); \ + dtb_node_valid(node); \ + node = dtb_node_next_subnode(node)) + +#endif /* RT_FDT_H__ */ diff --git a/components/drivers/fdt/libfdt/SConscript b/components/drivers/fdt/libfdt/SConscript new file mode 100644 index 0000000000..638a457bfe --- /dev/null +++ b/components/drivers/fdt/libfdt/SConscript @@ -0,0 +1,10 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + +CPPPATH = [cwd] + +group = DefineGroup('FDT', src, depend = ['RT_USING_FDTLIB'], CPPPATH = CPPPATH) + +Return('group')
\ No newline at end of file diff --git a/components/drivers/fdt/libfdt/fdt.c b/components/drivers/fdt/libfdt/fdt.c new file mode 100644 index 0000000000..1d4c606985 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt.c @@ -0,0 +1,249 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +int fdt_check_header(const void *fdt) +{ + if (fdt_magic(fdt) == FDT_MAGIC) { + /* Complete tree */ + if (fdt_version(fdt) < FDT_FIRST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + if (fdt_last_comp_version(fdt) > FDT_LAST_SUPPORTED_VERSION) + return -FDT_ERR_BADVERSION; + } else if (fdt_magic(fdt) == FDT_SW_MAGIC) { + /* Unfinished sequential-write blob */ + if (fdt_size_dt_struct(fdt) == 0) + return -FDT_ERR_BADSTATE; + } else { + return -FDT_ERR_BADMAGIC; + } + + return 0; +} + +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int len) +{ + unsigned absoffset = offset + fdt_off_dt_struct(fdt); + + if ((absoffset < offset) + || ((absoffset + len) < absoffset) + || (absoffset + len) > fdt_totalsize(fdt)) + return NULL; + + if (fdt_version(fdt) >= 0x11) + if (((offset + len) < offset) + || ((offset + len) > fdt_size_dt_struct(fdt))) + return NULL; + + return _fdt_offset_ptr(fdt, offset); +} + +uint32_t fdt_next_tag(const void *fdt, int startoffset, int *nextoffset) +{ + const fdt32_t *tagp, *lenp; + uint32_t tag; + int offset = startoffset; + const char *p; + + *nextoffset = -FDT_ERR_TRUNCATED; + tagp = fdt_offset_ptr(fdt, offset, FDT_TAGSIZE); + if (!tagp) + return FDT_END; /* premature end */ + tag = fdt32_to_cpu(*tagp); + offset += FDT_TAGSIZE; + + *nextoffset = -FDT_ERR_BADSTRUCTURE; + switch (tag) { + case FDT_BEGIN_NODE: + /* skip name */ + do { + p = fdt_offset_ptr(fdt, offset++, 1); + } while (p && (*p != '\0')); + if (!p) + return FDT_END; /* premature end */ + break; + + case FDT_PROP: + lenp = fdt_offset_ptr(fdt, offset, sizeof(*lenp)); + if (!lenp) + return FDT_END; /* premature end */ + /* skip-name offset, length and value */ + offset += sizeof(struct fdt_property) - FDT_TAGSIZE + + fdt32_to_cpu(*lenp); + break; + + case FDT_END: + case FDT_END_NODE: + case FDT_NOP: + break; + + default: + return FDT_END; + } + + if (!fdt_offset_ptr(fdt, startoffset, offset - startoffset)) + return FDT_END; /* premature end */ + + *nextoffset = FDT_TAGALIGN(offset); + return tag; +} + +int _fdt_check_node_offset(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_BEGIN_NODE)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int _fdt_check_prop_offset(const void *fdt, int offset) +{ + if ((offset < 0) || (offset % FDT_TAGSIZE) + || (fdt_next_tag(fdt, offset, &offset) != FDT_PROP)) + return -FDT_ERR_BADOFFSET; + + return offset; +} + +int fdt_next_node(const void *fdt, int offset, int *depth) +{ + int nextoffset = 0; + uint32_t tag; + + if (offset >= 0) + if ((nextoffset = _fdt_check_node_offset(fdt, offset)) < 0) + return nextoffset; + + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_PROP: + case FDT_NOP: + break; + + case FDT_BEGIN_NODE: + if (depth) + (*depth)++; + break; + + case FDT_END_NODE: + if (depth && ((--(*depth)) < 0)) + return nextoffset; + break; + + case FDT_END: + if ((nextoffset >= 0) + || ((nextoffset == -FDT_ERR_TRUNCATED) && !depth)) + return -FDT_ERR_NOTFOUND; + else + return nextoffset; + } + } while (tag != FDT_BEGIN_NODE); + + return offset; +} + +int fdt_first_subnode(const void *fdt, int offset) +{ + int depth = 0; + + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth != 1) + return -FDT_ERR_NOTFOUND; + + return offset; +} + +int fdt_next_subnode(const void *fdt, int offset) +{ + int depth = 1; + + /* + * With respect to the parent, the depth of the next subnode will be + * the same as the last. + */ + do { + offset = fdt_next_node(fdt, offset, &depth); + if (offset < 0 || depth < 1) + return -FDT_ERR_NOTFOUND; + } while (depth > 1); + + return offset; +} + +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s) +{ + int len = strlen(s) + 1; + const char *last = strtab + tabsize - len; + const char *p; + + for (p = strtab; p <= last; p++) + if (memcmp(p, s, len) == 0) + return p; + return NULL; +} + +int fdt_move(const void *fdt, void *buf, int bufsize) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_totalsize(fdt) > bufsize) + return -FDT_ERR_NOSPACE; + + memmove(buf, fdt, fdt_totalsize(fdt)); + return 0; +} diff --git a/components/drivers/fdt/libfdt/fdt.h b/components/drivers/fdt/libfdt/fdt.h new file mode 100644 index 0000000000..526aedb515 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt.h @@ -0,0 +1,111 @@ +#ifndef _FDT_H +#define _FDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef __ASSEMBLY__ + +struct fdt_header { + fdt32_t magic; /* magic word FDT_MAGIC */ + fdt32_t totalsize; /* total size of DT block */ + fdt32_t off_dt_struct; /* offset to structure */ + fdt32_t off_dt_strings; /* offset to strings */ + fdt32_t off_mem_rsvmap; /* offset to memory reserve map */ + fdt32_t version; /* format version */ + fdt32_t last_comp_version; /* last compatible version */ + + /* version 2 fields below */ + fdt32_t boot_cpuid_phys; /* Which physical CPU id we're + booting on */ + /* version 3 fields below */ + fdt32_t size_dt_strings; /* size of the strings block */ + + /* version 17 fields below */ + fdt32_t size_dt_struct; /* size of the structure block */ +}; + +struct fdt_reserve_entry { + fdt64_t address; + fdt64_t size; +}; + +struct fdt_node_header { + fdt32_t tag; + char name[0]; +}; + +struct fdt_property { + fdt32_t tag; + fdt32_t len; + fdt32_t nameoff; + char data[0]; +}; + +#endif /* !__ASSEMBLY */ + +#define FDT_MAGIC 0xd00dfeed /* 4: version, 4: total size */ +#define FDT_TAGSIZE sizeof(fdt32_t) + +#define FDT_BEGIN_NODE 0x1 /* Start node: full name */ +#define FDT_END_NODE 0x2 /* End node */ +#define FDT_PROP 0x3 /* Property: name off, + size, content */ +#define FDT_NOP 0x4 /* nop */ +#define FDT_END 0x9 + +#define FDT_V1_SIZE (7*sizeof(fdt32_t)) +#define FDT_V2_SIZE (FDT_V1_SIZE + sizeof(fdt32_t)) +#define FDT_V3_SIZE (FDT_V2_SIZE + sizeof(fdt32_t)) +#define FDT_V16_SIZE FDT_V3_SIZE +#define FDT_V17_SIZE (FDT_V16_SIZE + sizeof(fdt32_t)) + +#endif /* _FDT_H */ diff --git a/components/drivers/fdt/libfdt/fdt_addresses.c b/components/drivers/fdt/libfdt/fdt_addresses.c new file mode 100644 index 0000000000..a8f2ad5ebc --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_addresses.c @@ -0,0 +1,99 @@ +// SPDX-License-Identifier: (GPL-2.0-or-later OR BSD-2-Clause) +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2014 David Gibson <[email protected]> + * Copyright (C) 2018 embedded brains GmbH + */ +#include "libfdt_env.h" +#include <fdt.h> +#include <libfdt.h> +#include "libfdt_internal.h" + +static int fdt_cells(const void *fdt, int nodeoffset, const char *name) +{ + const fdt32_t *c; + uint32_t val; + int len; + + c = fdt_getprop(fdt, nodeoffset, name, &len); + if (!c) + return len; + + if (len != sizeof(*c)) + return -FDT_ERR_BADNCELLS; + + val = fdt32_to_cpu(*c); + if (val > FDT_MAX_NCELLS) + return -FDT_ERR_BADNCELLS; + + return (int)val; +} + +int fdt_address_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#address-cells"); + if (val == 0) + return -FDT_ERR_BADNCELLS; + if (val == -FDT_ERR_NOTFOUND) + return 2; + return val; +} + +int fdt_size_cells(const void *fdt, int nodeoffset) +{ + int val; + + val = fdt_cells(fdt, nodeoffset, "#size-cells"); + if (val == -FDT_ERR_NOTFOUND) + return 1; + return val; +} + +/* This function assumes that [address|size]_cells is 1 or 2 */ +int fdt_appendprop_addrrange(void *fdt, int parent, int nodeoffset, + const char *name, uint64_t addr, uint64_t size) +{ + int addr_cells, size_cells, ret; + uint8_t data[sizeof(fdt64_t) * 2], *prop; + + ret = fdt_address_cells(fdt, parent); + if (ret < 0) + return ret; + addr_cells = ret; + + ret = fdt_size_cells(fdt, parent); + if (ret < 0) + return ret; + size_cells = ret; + + /* check validity of address */ + prop = data; + if (addr_cells == 1) { + if ((addr > UINT32_MAX) || ((UINT32_MAX + 1 - addr) < size)) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)addr); + } else if (addr_cells == 2) { + fdt64_st(prop, addr); + } else { + return -FDT_ERR_BADNCELLS; + } + + /* check validity of size */ + prop += addr_cells * sizeof(fdt32_t); + if (size_cells == 1) { + if (size > UINT32_MAX) + return -FDT_ERR_BADVALUE; + + fdt32_st(prop, (uint32_t)size); + } else if (size_cells == 2) { + fdt64_st(prop, size); + } else { + return -FDT_ERR_BADNCELLS; + } + + return fdt_appendprop(fdt, nodeoffset, name, data, + (addr_cells + size_cells) * sizeof(fdt32_t)); +} diff --git a/components/drivers/fdt/libfdt/fdt_empty_tree.c b/components/drivers/fdt/libfdt/fdt_empty_tree.c new file mode 100644 index 0000000000..26ab2d3641 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_empty_tree.c @@ -0,0 +1,84 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2012 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" + +#include "fdt.h" +#include "libfdt.h" + +#include "libfdt_internal.h" + +int fdt_create_empty_tree(void *buf, int bufsize) +{ + int err; + + err = fdt_create(buf, bufsize); + if (err) + return err; + + err = fdt_finish_reservemap(buf); + if (err) + return err; + + err = fdt_begin_node(buf, ""); + if (err) + return err; + + err = fdt_end_node(buf); + if (err) + return err; + + err = fdt_finish(buf); + if (err) + return err; + + return fdt_open_into(buf, buf, bufsize); +} + diff --git a/components/drivers/fdt/libfdt/fdt_ro.c b/components/drivers/fdt/libfdt/fdt_ro.c new file mode 100644 index 0000000000..984e8fe227 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_ro.c @@ -0,0 +1,701 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +static int _fdt_nodename_eq(const void *fdt, int offset, + const char *s, int len) +{ + const char *p = fdt_offset_ptr(fdt, offset + FDT_TAGSIZE, len+1); + + if (! p) + /* short match */ + return 0; + + if (memcmp(p, s, len) != 0) + return 0; + + if (p[len] == '\0') + return 1; + else if (!memchr(s, '@', len) && (p[len] == '@')) + return 1; + else + return 0; +} + +const char *fdt_string(const void *fdt, int stroffset) +{ + return (const char *)fdt + fdt_off_dt_strings(fdt) + stroffset; +} + +static int _fdt_string_eq(const void *fdt, int stroffset, + const char *s, int len) +{ + const char *p = fdt_string(fdt, stroffset); + + return (strlen(p) == len) && (memcmp(p, s, len) == 0); +} + +uint32_t fdt_get_max_phandle(const void *fdt) +{ + uint32_t max_phandle = 0; + int offset; + + for (offset = fdt_next_node(fdt, -1, NULL);; + offset = fdt_next_node(fdt, offset, NULL)) { + uint32_t phandle; + + if (offset == -FDT_ERR_NOTFOUND) + return max_phandle; + + if (offset < 0) + return (uint32_t)-1; + + phandle = fdt_get_phandle(fdt, offset); + if (phandle == (uint32_t)-1) + continue; + + if (phandle > max_phandle) + max_phandle = phandle; + } + + return 0; +} + +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size) +{ + FDT_CHECK_HEADER(fdt); + *address = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->address); + *size = fdt64_to_cpu(_fdt_mem_rsv(fdt, n)->size); + return 0; +} + +int fdt_num_mem_rsv(const void *fdt) +{ + int i = 0; + + while (fdt64_to_cpu(_fdt_mem_rsv(fdt, i)->size) != 0) + i++; + return i; +} + +static int _nextprop(const void *fdt, int offset) +{ + uint32_t tag; + int nextoffset; + + do { + tag = fdt_next_tag(fdt, offset, &nextoffset); + + switch (tag) { + case FDT_END: + if (nextoffset >= 0) + return -FDT_ERR_BADSTRUCTURE; + else + return nextoffset; + + case FDT_PROP: + return offset; + } + offset = nextoffset; + } while (tag == FDT_NOP); + + return -FDT_ERR_NOTFOUND; +} + +int fdt_subnode_offset_namelen(const void *fdt, int offset, + const char *name, int namelen) +{ + int depth; + + FDT_CHECK_HEADER(fdt); + + for (depth = 0; + (offset >= 0) && (depth >= 0); + offset = fdt_next_node(fdt, offset, &depth)) + if ((depth == 1) + && _fdt_nodename_eq(fdt, offset, name, namelen)) + return offset; + + if (depth < 0) + return -FDT_ERR_NOTFOUND; + return offset; /* error */ +} + +int fdt_subnode_offset(const void *fdt, int parentoffset, + const char *name) +{ + return fdt_subnode_offset_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen) +{ + const char *end = path + namelen; + const char *p = path; + int offset = 0; + + FDT_CHECK_HEADER(fdt); + + /* see if we have an alias */ + if (*path != '/') { + const char *q = memchr(path, '/', end - p); + + if (!q) + q = end; + + p = fdt_get_alias_namelen(fdt, p, q - p); + if (!p) + return -FDT_ERR_BADPATH; + offset = fdt_path_offset(fdt, p); + + p = q; + } + + while (p < end) { + const char *q; + + while (*p == '/') { + p++; + if (p == end) + return offset; + } + q = memchr(p, '/', end - p); + if (! q) + q = end; + + offset = fdt_subnode_offset_namelen(fdt, offset, p, q-p); + if (offset < 0) + return offset; + + p = q; + } + + return offset; +} + +int fdt_path_offset(const void *fdt, const char *path) +{ + return fdt_path_offset_namelen(fdt, path, strlen(path)); +} + +const char *fdt_get_name(const void *fdt, int nodeoffset, int *len) +{ + const struct fdt_node_header *nh = _fdt_offset_ptr(fdt, nodeoffset); + int err; + + if (((err = fdt_check_header(fdt)) != 0) + || ((err = _fdt_check_node_offset(fdt, nodeoffset)) < 0)) + goto fail; + + if (len) + *len = strlen(nh->name); + + return nh->name; + + fail: + if (len) + *len = err; + return NULL; +} + +int fdt_first_property_offset(const void *fdt, int nodeoffset) +{ + int offset; + + if ((offset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return offset; + + return _nextprop(fdt, offset); +} + +int fdt_next_property_offset(const void *fdt, int offset) +{ + if ((offset = _fdt_check_prop_offset(fdt, offset)) < 0) + return offset; + + return _nextprop(fdt, offset); +} + +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp) +{ + int err; + const struct fdt_property *prop; + + if ((err = _fdt_check_prop_offset(fdt, offset)) < 0) { + if (lenp) + *lenp = err; + return NULL; + } + + prop = _fdt_offset_ptr(fdt, offset); + + if (lenp) + *lenp = fdt32_to_cpu(prop->len); + + return prop; +} + +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int offset, + const char *name, + int namelen, int *lenp) +{ + for (offset = fdt_first_property_offset(fdt, offset); + (offset >= 0); + (offset = fdt_next_property_offset(fdt, offset))) { + const struct fdt_property *prop; + + if (!(prop = fdt_get_property_by_offset(fdt, offset, lenp))) { + offset = -FDT_ERR_INTERNAL; + break; + } + if (_fdt_string_eq(fdt, fdt32_to_cpu(prop->nameoff), + name, namelen)) + return prop; + } + + if (lenp) + *lenp = offset; + return NULL; +} + +const struct fdt_property *fdt_get_property(const void *fdt, + int nodeoffset, + const char *name, int *lenp) +{ + return fdt_get_property_namelen(fdt, nodeoffset, name, + strlen(name), lenp); +} + +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_namelen(fdt, nodeoffset, name, namelen, lenp); + if (! prop) + return NULL; + + return prop->data; +} + +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp) +{ + const struct fdt_property *prop; + + prop = fdt_get_property_by_offset(fdt, offset, lenp); + if (!prop) + return NULL; + if (namep) + *namep = fdt_string(fdt, fdt32_to_cpu(prop->nameoff)); + return prop->data; +} + +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return fdt_getprop_namelen(fdt, nodeoffset, name, strlen(name), lenp); +} + +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset) +{ + const fdt32_t *php; + int len; + + /* FIXME: This is a bit sub-optimal, since we potentially scan + * over all the properties twice. */ + php = fdt_getprop(fdt, nodeoffset, "phandle", &len); + if (!php || (len != sizeof(*php))) { + php = fdt_getprop(fdt, nodeoffset, "linux,phandle", &len); + if (!php || (len != sizeof(*php))) + return 0; + } + + return fdt32_to_cpu(*php); +} + +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen) +{ + int aliasoffset; + + aliasoffset = fdt_path_offset(fdt, "/aliases"); + if (aliasoffset < 0) + return NULL; + + return fdt_getprop_namelen(fdt, aliasoffset, name, namelen, NULL); +} + +const char *fdt_get_alias(const void *fdt, const char *name) +{ + return fdt_get_alias_namelen(fdt, name, strlen(name)); +} + +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen) +{ + int pdepth = 0, p = 0; + int offset, depth, namelen; + const char *name; + + FDT_CHECK_HEADER(fdt); + + if (buflen < 2) + return -FDT_ERR_NOSPACE; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + while (pdepth > depth) { + do { + p--; + } while (buf[p-1] != '/'); + pdepth--; + } + + if (pdepth >= depth) { + name = fdt_get_name(fdt, offset, &namelen); + if (!name) + return namelen; + if ((p + namelen + 1) <= buflen) { + memcpy(buf + p, name, namelen); + p += namelen; + buf[p++] = '/'; + pdepth++; + } + } + + if (offset == nodeoffset) { + if (pdepth < (depth + 1)) + return -FDT_ERR_NOSPACE; + + if (p > 1) /* special case so that root path is "/", not "" */ + p--; + buf[p] = '\0'; + return 0; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth) +{ + int offset, depth; + int supernodeoffset = -FDT_ERR_INTERNAL; + + FDT_CHECK_HEADER(fdt); + + if (supernodedepth < 0) + return -FDT_ERR_NOTFOUND; + + for (offset = 0, depth = 0; + (offset >= 0) && (offset <= nodeoffset); + offset = fdt_next_node(fdt, offset, &depth)) { + if (depth == supernodedepth) + supernodeoffset = offset; + + if (offset == nodeoffset) { + if (nodedepth) + *nodedepth = depth; + + if (supernodedepth > depth) + return -FDT_ERR_NOTFOUND; + else + return supernodeoffset; + } + } + + if ((offset == -FDT_ERR_NOTFOUND) || (offset >= 0)) + return -FDT_ERR_BADOFFSET; + else if (offset == -FDT_ERR_BADOFFSET) + return -FDT_ERR_BADSTRUCTURE; + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_depth(const void *fdt, int nodeoffset) +{ + int nodedepth; + int err; + + err = fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, &nodedepth); + if (err) + return (err < 0) ? err : -FDT_ERR_INTERNAL; + return nodedepth; +} + +int fdt_parent_offset(const void *fdt, int nodeoffset) +{ + int nodedepth = fdt_node_depth(fdt, nodeoffset); + + if (nodedepth < 0) + return nodedepth; + return fdt_supernode_atdepth_offset(fdt, nodeoffset, + nodedepth - 1, NULL); +} + +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen) +{ + int offset; + const void *val; + int len; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_getprop(), then if that didn't + * find what we want, we scan over them again making our way + * to the next node. Still it's the easiest to implement + * approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + val = fdt_getprop(fdt, offset, propname, &len); + if (val && (len == proplen) + && (memcmp(val, propval, len) == 0)) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle) +{ + int offset; + + if ((phandle == 0) || (phandle == -1)) + return -FDT_ERR_BADPHANDLE; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we + * potentially scan each property of a node in + * fdt_get_phandle(), then if that didn't find what + * we want, we scan over them again making our way to the next + * node. Still it's the easiest to implement approach; + * performance can come later. */ + for (offset = fdt_next_node(fdt, -1, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + if (fdt_get_phandle(fdt, offset) == phandle) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} + +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str) +{ + int len = strlen(str); + const char *p; + + while (listlen >= len) { + if (memcmp(str, strlist, len+1) == 0) + return 1; + p = memchr(strlist, '\0', listlen); + if (!p) + return 0; /* malformed strlist.. */ + listlen -= (p-strlist) + 1; + strlist = p + 1; + } + return 0; +} + +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property) +{ + const char *list, *end; + int length, count = 0; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + list += length; + count++; + } + + return count; +} + +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string) +{ + int length, len, idx = 0; + const char *list, *end; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) + return length; + + len = strlen(string) + 1; + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) + return -FDT_ERR_BADVALUE; + + if (length == len && memcmp(list, string, length) == 0) + return idx; + + list += length; + idx++; + } + + return -FDT_ERR_NOTFOUND; +} + +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int idx, + int *lenp) +{ + const char *list, *end; + int length; + + list = fdt_getprop(fdt, nodeoffset, property, &length); + if (!list) { + if (lenp) + *lenp = length; + + return NULL; + } + + end = list + length; + + while (list < end) { + length = strnlen(list, end - list) + 1; + + /* Abort if the last string isn't properly NUL-terminated. */ + if (list + length > end) { + if (lenp) + *lenp = -FDT_ERR_BADVALUE; + + return NULL; + } + + if (idx == 0) { + if (lenp) + *lenp = length - 1; + + return list; + } + + list += length; + idx--; + } + + if (lenp) + *lenp = -FDT_ERR_NOTFOUND; + + return NULL; +} + +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible) +{ + const void *prop; + int len; + + prop = fdt_getprop(fdt, nodeoffset, "compatible", &len); + if (!prop) + return len; + + return !fdt_stringlist_contains(prop, len, compatible); +} + +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible) +{ + int offset, err; + + FDT_CHECK_HEADER(fdt); + + /* FIXME: The algorithm here is pretty horrible: we scan each + * property of a node in fdt_node_check_compatible(), then if + * that didn't find what we want, we scan over them again + * making our way to the next node. Still it's the easiest to + * implement approach; performance can come later. */ + for (offset = fdt_next_node(fdt, startoffset, NULL); + offset >= 0; + offset = fdt_next_node(fdt, offset, NULL)) { + err = fdt_node_check_compatible(fdt, offset, compatible); + if ((err < 0) && (err != -FDT_ERR_NOTFOUND)) + return err; + else if (err == 0) + return offset; + } + + return offset; /* error from fdt_next_node() */ +} diff --git a/components/drivers/fdt/libfdt/fdt_rw.c b/components/drivers/fdt/libfdt/fdt_rw.c new file mode 100644 index 0000000000..4406471b74 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_rw.c @@ -0,0 +1,489 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +static int _fdt_blocks_misordered(const void *fdt, + int mem_rsv_size, int struct_size) +{ + return (fdt_off_mem_rsvmap(fdt) < FDT_ALIGN(sizeof(struct fdt_header), 8)) + || (fdt_off_dt_struct(fdt) < + (fdt_off_mem_rsvmap(fdt) + mem_rsv_size)) + || (fdt_off_dt_strings(fdt) < + (fdt_off_dt_struct(fdt) + struct_size)) + || (fdt_totalsize(fdt) < + (fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt))); +} + +static int _fdt_rw_check_header(void *fdt) +{ + FDT_CHECK_HEADER(fdt); + + if (fdt_version(fdt) < 17) + return -FDT_ERR_BADVERSION; + if (_fdt_blocks_misordered(fdt, sizeof(struct fdt_reserve_entry), + fdt_size_dt_struct(fdt))) + return -FDT_ERR_BADLAYOUT; + if (fdt_version(fdt) > 17) + fdt_set_version(fdt, 17); + + return 0; +} + +#define FDT_RW_CHECK_HEADER(fdt) \ + { \ + int __err; \ + if ((__err = _fdt_rw_check_header(fdt)) != 0) \ + return __err; \ + } + +static inline int _fdt_data_size(void *fdt) +{ + return fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); +} + +static int _fdt_splice(void *fdt, void *splicepoint, int oldlen, int newlen) +{ + char *p = splicepoint; + char *end = (char *)fdt + _fdt_data_size(fdt); + + if (((p + oldlen) < p) || ((p + oldlen) > end)) + return -FDT_ERR_BADOFFSET; + if ((p < (char *)fdt) || ((end - oldlen + newlen) < (char *)fdt)) + return -FDT_ERR_BADOFFSET; + if ((end - oldlen + newlen) > ((char *)fdt + fdt_totalsize(fdt))) + return -FDT_ERR_NOSPACE; + memmove(p + newlen, p + oldlen, end - p - oldlen); + return 0; +} + +static int _fdt_splice_mem_rsv(void *fdt, struct fdt_reserve_entry *p, + int oldn, int newn) +{ + int delta = (newn - oldn) * sizeof(*p); + int err; + err = _fdt_splice(fdt, p, oldn * sizeof(*p), newn * sizeof(*p)); + if (err) + return err; + fdt_set_off_dt_struct(fdt, fdt_off_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_struct(void *fdt, void *p, + int oldlen, int newlen) +{ + int delta = newlen - oldlen; + int err; + + if ((err = _fdt_splice(fdt, p, oldlen, newlen))) + return err; + + fdt_set_size_dt_struct(fdt, fdt_size_dt_struct(fdt) + delta); + fdt_set_off_dt_strings(fdt, fdt_off_dt_strings(fdt) + delta); + return 0; +} + +static int _fdt_splice_string(void *fdt, int newlen) +{ + void *p = (char *)fdt + + fdt_off_dt_strings(fdt) + fdt_size_dt_strings(fdt); + int err; + + if ((err = _fdt_splice(fdt, p, 0, newlen))) + return err; + + fdt_set_size_dt_strings(fdt, fdt_size_dt_strings(fdt) + newlen); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_off_dt_strings(fdt); + const char *p; + char *new; + int len = strlen(s) + 1; + int err; + + p = _fdt_find_string(strtab, fdt_size_dt_strings(fdt), s); + if (p) + /* found it */ + return (p - strtab); + + new = strtab + fdt_size_dt_strings(fdt); + err = _fdt_splice_string(fdt, len); + if (err) + return err; + + memcpy(new, s, len); + return (new - strtab); +} + +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size) +{ + struct fdt_reserve_entry *re; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + re = _fdt_mem_rsv_w(fdt, fdt_num_mem_rsv(fdt)); + err = _fdt_splice_mem_rsv(fdt, re, 0, 1); + if (err) + return err; + + re->address = cpu_to_fdt64(address); + re->size = cpu_to_fdt64(size); + return 0; +} + +int fdt_del_mem_rsv(void *fdt, int n) +{ + struct fdt_reserve_entry *re = _fdt_mem_rsv_w(fdt, n); + + FDT_RW_CHECK_HEADER(fdt); + + if (n >= fdt_num_mem_rsv(fdt)) + return -FDT_ERR_NOTFOUND; + + return _fdt_splice_mem_rsv(fdt, re, 1, 0); +} + +static int _fdt_resize_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int oldlen; + int err; + + *prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (! (*prop)) + return oldlen; + + if ((err = _fdt_splice_struct(fdt, (*prop)->data, FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(len)))) + return err; + + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +static int _fdt_add_property(void *fdt, int nodeoffset, const char *name, + int len, struct fdt_property **prop) +{ + int proplen; + int nextoffset; + int namestroff; + int err; + + if ((nextoffset = _fdt_check_node_offset(fdt, nodeoffset)) < 0) + return nextoffset; + + namestroff = _fdt_find_add_string(fdt, name); + if (namestroff < 0) + return namestroff; + + *prop = _fdt_offset_ptr_w(fdt, nextoffset); + proplen = sizeof(**prop) + FDT_TAGALIGN(len); + + err = _fdt_splice_struct(fdt, *prop, 0, proplen); + if (err) + return err; + + (*prop)->tag = cpu_to_fdt32(FDT_PROP); + (*prop)->nameoff = cpu_to_fdt32(namestroff); + (*prop)->len = cpu_to_fdt32(len); + return 0; +} + +int fdt_set_name(void *fdt, int nodeoffset, const char *name) +{ + char *namep; + int oldlen, newlen; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + namep = (char *)(uintptr_t)fdt_get_name(fdt, nodeoffset, &oldlen); + if (!namep) + return oldlen; + + newlen = strlen(name); + + err = _fdt_splice_struct(fdt, namep, FDT_TAGALIGN(oldlen+1), + FDT_TAGALIGN(newlen+1)); + if (err) + return err; + + memcpy(namep, name, newlen+1); + return 0; +} + +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err; + + FDT_RW_CHECK_HEADER(fdt); + + err = _fdt_resize_property(fdt, nodeoffset, name, len, &prop); + if (err == -FDT_ERR_NOTFOUND) + err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + + if (len) + memcpy(prop->data, val, len); + return 0; +} + +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + struct fdt_property *prop; + int err, oldlen, newlen; + + FDT_RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &oldlen); + if (prop) { + newlen = len + oldlen; + err = _fdt_splice_struct(fdt, prop->data, + FDT_TAGALIGN(oldlen), + FDT_TAGALIGN(newlen)); + if (err) + return err; + prop->len = cpu_to_fdt32(newlen); + memcpy(prop->data + oldlen, val, len); + } else { + err = _fdt_add_property(fdt, nodeoffset, name, len, &prop); + if (err) + return err; + memcpy(prop->data, val, len); + } + return 0; +} + +int fdt_delprop(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len, proplen; + + FDT_RW_CHECK_HEADER(fdt); + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + proplen = sizeof(*prop) + FDT_TAGALIGN(len); + return _fdt_splice_struct(fdt, prop, proplen, 0); +} + +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen) +{ + struct fdt_node_header *nh; + int offset, nextoffset; + int nodelen; + int err; + uint32_t tag; + fdt32_t *endtag; + + FDT_RW_CHECK_HEADER(fdt); + + offset = fdt_subnode_offset_namelen(fdt, parentoffset, name, namelen); + if (offset >= 0) + return -FDT_ERR_EXISTS; + else if (offset != -FDT_ERR_NOTFOUND) + return offset; + + /* Try to place the new node after the parent's properties */ + fdt_next_tag(fdt, parentoffset, &nextoffset); /* skip the BEGIN_NODE */ + do { + offset = nextoffset; + tag = fdt_next_tag(fdt, offset, &nextoffset); + } while ((tag == FDT_PROP) || (tag == FDT_NOP)); + + nh = _fdt_offset_ptr_w(fdt, offset); + nodelen = sizeof(*nh) + FDT_TAGALIGN(namelen+1) + FDT_TAGSIZE; + + err = _fdt_splice_struct(fdt, nh, 0, nodelen); + if (err) + return err; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memset(nh->name, 0, FDT_TAGALIGN(namelen+1)); + memcpy(nh->name, name, namelen); + endtag = (fdt32_t *)((char *)nh + nodelen - FDT_TAGSIZE); + *endtag = cpu_to_fdt32(FDT_END_NODE); + + return offset; +} + +int fdt_add_subnode(void *fdt, int parentoffset, const char *name) +{ + return fdt_add_subnode_namelen(fdt, parentoffset, name, strlen(name)); +} + +int fdt_del_node(void *fdt, int nodeoffset) +{ + int endoffset; + + FDT_RW_CHECK_HEADER(fdt); + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + return _fdt_splice_struct(fdt, _fdt_offset_ptr_w(fdt, nodeoffset), + endoffset - nodeoffset, 0); +} + +static void _fdt_packblocks(const char *old, char *new, + int mem_rsv_size, int struct_size) +{ + int mem_rsv_off, struct_off, strings_off; + + mem_rsv_off = FDT_ALIGN(sizeof(struct fdt_header), 8); + struct_off = mem_rsv_off + mem_rsv_size; + strings_off = struct_off + struct_size; + + memmove(new + mem_rsv_off, old + fdt_off_mem_rsvmap(old), mem_rsv_size); + fdt_set_off_mem_rsvmap(new, mem_rsv_off); + + memmove(new + struct_off, old + fdt_off_dt_struct(old), struct_size); + fdt_set_off_dt_struct(new, struct_off); + fdt_set_size_dt_struct(new, struct_size); + + memmove(new + strings_off, old + fdt_off_dt_strings(old), + fdt_size_dt_strings(old)); + fdt_set_off_dt_strings(new, strings_off); + fdt_set_size_dt_strings(new, fdt_size_dt_strings(old)); +} + +int fdt_open_into(const void *fdt, void *buf, int bufsize) +{ + int err; + int mem_rsv_size, struct_size; + int newsize; + const char *fdtstart = fdt; + const char *fdtend = fdtstart + fdt_totalsize(fdt); + char *tmp; + + FDT_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + + if (fdt_version(fdt) >= 17) { + struct_size = fdt_size_dt_struct(fdt); + } else { + struct_size = 0; + while (fdt_next_tag(fdt, struct_size, &struct_size) != FDT_END) + ; + if (struct_size < 0) + return struct_size; + } + + if (!_fdt_blocks_misordered(fdt, mem_rsv_size, struct_size)) { + /* no further work necessary */ + err = fdt_move(fdt, buf, bufsize); + if (err) + return err; + fdt_set_version(buf, 17); + fdt_set_size_dt_struct(buf, struct_size); + fdt_set_totalsize(buf, bufsize); + return 0; + } + + /* Need to reorder */ + newsize = FDT_ALIGN(sizeof(struct fdt_header), 8) + mem_rsv_size + + struct_size + fdt_size_dt_strings(fdt); + + if (bufsize < newsize) + return -FDT_ERR_NOSPACE; + + /* First attempt to build converted tree at beginning of buffer */ + tmp = buf; + /* But if that overlaps with the old tree... */ + if (((tmp + newsize) > fdtstart) && (tmp < fdtend)) { + /* Try right after the old tree instead */ + tmp = (char *)(uintptr_t)fdtend; + if ((tmp + newsize) > ((char *)buf + bufsize)) + return -FDT_ERR_NOSPACE; + } + + _fdt_packblocks(fdt, tmp, mem_rsv_size, struct_size); + memmove(buf, tmp, newsize); + + fdt_set_magic(buf, FDT_MAGIC); + fdt_set_totalsize(buf, bufsize); + fdt_set_version(buf, 17); + fdt_set_last_comp_version(buf, 16); + fdt_set_boot_cpuid_phys(buf, fdt_boot_cpuid_phys(fdt)); + + return 0; +} + +int fdt_pack(void *fdt) +{ + int mem_rsv_size; + + FDT_RW_CHECK_HEADER(fdt); + + mem_rsv_size = (fdt_num_mem_rsv(fdt)+1) + * sizeof(struct fdt_reserve_entry); + _fdt_packblocks(fdt, fdt, mem_rsv_size, fdt_size_dt_struct(fdt)); + fdt_set_totalsize(fdt, _fdt_data_size(fdt)); + + return 0; +} diff --git a/components/drivers/fdt/libfdt/fdt_strerror.c b/components/drivers/fdt/libfdt/fdt_strerror.c new file mode 100644 index 0000000000..eda657ff07 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_strerror.c @@ -0,0 +1,100 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +struct fdt_errtabent { + const char *str; +}; + +#define FDT_ERRTABENT(val) \ + [(val)] = { .str = #val, } + +static struct fdt_errtabent fdt_errtable[] = { + FDT_ERRTABENT(FDT_ERR_NOTFOUND), + FDT_ERRTABENT(FDT_ERR_EXISTS), + FDT_ERRTABENT(FDT_ERR_NOSPACE), + + FDT_ERRTABENT(FDT_ERR_BADOFFSET), + FDT_ERRTABENT(FDT_ERR_BADPATH), + FDT_ERRTABENT(FDT_ERR_BADPHANDLE), + FDT_ERRTABENT(FDT_ERR_BADSTATE), + + FDT_ERRTABENT(FDT_ERR_TRUNCATED), + FDT_ERRTABENT(FDT_ERR_BADMAGIC), + FDT_ERRTABENT(FDT_ERR_BADVERSION), + FDT_ERRTABENT(FDT_ERR_BADSTRUCTURE), + FDT_ERRTABENT(FDT_ERR_BADLAYOUT), + FDT_ERRTABENT(FDT_ERR_INTERNAL), + FDT_ERRTABENT(FDT_ERR_BADNCELLS), + FDT_ERRTABENT(FDT_ERR_BADVALUE), + FDT_ERRTABENT(FDT_ERR_BADOVERLAY), + FDT_ERRTABENT(FDT_ERR_NOPHANDLES), +}; +#define FDT_ERRTABSIZE (sizeof(fdt_errtable) / sizeof(fdt_errtable[0])) + +const char *fdt_strerror(int errval) +{ + if (errval > 0) + return "<valid offset/length>"; + else if (errval == 0) + return "<no error>"; + else if (errval > -FDT_ERRTABSIZE) { + const char *s = fdt_errtable[-errval].str; + + if (s) + return s; + } + + return "<unknown error>"; +} diff --git a/components/drivers/fdt/libfdt/fdt_sw.c b/components/drivers/fdt/libfdt/fdt_sw.c new file mode 100644 index 0000000000..5d6de1b1b2 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_sw.c @@ -0,0 +1,286 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +static int _fdt_sw_check_header(void *fdt) +{ + if (fdt_magic(fdt) != FDT_SW_MAGIC) + return -FDT_ERR_BADMAGIC; + /* FIXME: should check more details about the header state */ + return 0; +} + +#define FDT_SW_CHECK_HEADER(fdt) \ + { \ + int err; \ + if ((err = _fdt_sw_check_header(fdt)) != 0) \ + return err; \ + } + +static void *_fdt_grab_space(void *fdt, size_t len) +{ + int offset = fdt_size_dt_struct(fdt); + int spaceleft; + + spaceleft = fdt_totalsize(fdt) - fdt_off_dt_struct(fdt) + - fdt_size_dt_strings(fdt); + + if ((offset + len < offset) || (offset + len > spaceleft)) + return NULL; + + fdt_set_size_dt_struct(fdt, offset + len); + return _fdt_offset_ptr_w(fdt, offset); +} + +int fdt_create(void *buf, int bufsize) +{ + void *fdt = buf; + + if (bufsize < sizeof(struct fdt_header)) + return -FDT_ERR_NOSPACE; + + memset(buf, 0, bufsize); + + fdt_set_magic(fdt, FDT_SW_MAGIC); + fdt_set_version(fdt, FDT_LAST_SUPPORTED_VERSION); + fdt_set_last_comp_version(fdt, FDT_FIRST_SUPPORTED_VERSION); + fdt_set_totalsize(fdt, bufsize); + + fdt_set_off_mem_rsvmap(fdt, FDT_ALIGN(sizeof(struct fdt_header), + sizeof(struct fdt_reserve_entry))); + fdt_set_off_dt_struct(fdt, fdt_off_mem_rsvmap(fdt)); + fdt_set_off_dt_strings(fdt, bufsize); + + return 0; +} + +int fdt_resize(void *fdt, void *buf, int bufsize) +{ + size_t headsize, tailsize; + char *oldtail, *newtail; + + FDT_SW_CHECK_HEADER(fdt); + + headsize = fdt_off_dt_struct(fdt); + tailsize = fdt_size_dt_strings(fdt); + + if ((headsize + tailsize) > bufsize) + return -FDT_ERR_NOSPACE; + + oldtail = (char *)fdt + fdt_totalsize(fdt) - tailsize; + newtail = (char *)buf + bufsize - tailsize; + + /* Two cases to avoid clobbering data if the old and new + * buffers partially overlap */ + if (buf <= fdt) { + memmove(buf, fdt, headsize); + memmove(newtail, oldtail, tailsize); + } else { + memmove(newtail, oldtail, tailsize); + memmove(buf, fdt, headsize); + } + + fdt_set_off_dt_strings(buf, bufsize); + fdt_set_totalsize(buf, bufsize); + + return 0; +} + +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size) +{ + struct fdt_reserve_entry *re; + int offset; + + FDT_SW_CHECK_HEADER(fdt); + + if (fdt_size_dt_struct(fdt)) + return -FDT_ERR_BADSTATE; + + offset = fdt_off_dt_struct(fdt); + if ((offset + sizeof(*re)) > fdt_totalsize(fdt)) + return -FDT_ERR_NOSPACE; + + re = (struct fdt_reserve_entry *)((char *)fdt + offset); + re->address = cpu_to_fdt64(addr); + re->size = cpu_to_fdt64(size); + + fdt_set_off_dt_struct(fdt, offset + sizeof(*re)); + + return 0; +} + +int fdt_finish_reservemap(void *fdt) +{ + return fdt_add_reservemap_entry(fdt, 0, 0); +} + +int fdt_begin_node(void *fdt, const char *name) +{ + struct fdt_node_header *nh; + int namelen = strlen(name) + 1; + + FDT_SW_CHECK_HEADER(fdt); + + nh = _fdt_grab_space(fdt, sizeof(*nh) + FDT_TAGALIGN(namelen)); + if (! nh) + return -FDT_ERR_NOSPACE; + + nh->tag = cpu_to_fdt32(FDT_BEGIN_NODE); + memcpy(nh->name, name, namelen); + return 0; +} + +int fdt_end_node(void *fdt) +{ + fdt32_t *en; + + FDT_SW_CHECK_HEADER(fdt); + + en = _fdt_grab_space(fdt, FDT_TAGSIZE); + if (! en) + return -FDT_ERR_NOSPACE; + + *en = cpu_to_fdt32(FDT_END_NODE); + return 0; +} + +static int _fdt_find_add_string(void *fdt, const char *s) +{ + char *strtab = (char *)fdt + fdt_totalsize(fdt); + const char *p; + int strtabsize = fdt_size_dt_strings(fdt); + int len = strlen(s) + 1; + int struct_top, offset; + + p = _fdt_find_string(strtab - strtabsize, strtabsize, s); + if (p) + return p - strtab; + + /* Add it */ + offset = -strtabsize - len; + struct_top = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + if (fdt_totalsize(fdt) + offset < struct_top) + return 0; /* no more room :( */ + + memcpy(strtab + offset, s, len); + fdt_set_size_dt_strings(fdt, strtabsize + len); + return offset; +} + +int fdt_property(void *fdt, const char *name, const void *val, int len) +{ + struct fdt_property *prop; + int nameoff; + + FDT_SW_CHECK_HEADER(fdt); + + nameoff = _fdt_find_add_string(fdt, name); + if (nameoff == 0) + return -FDT_ERR_NOSPACE; + + prop = _fdt_grab_space(fdt, sizeof(*prop) + FDT_TAGALIGN(len)); + if (! prop) + return -FDT_ERR_NOSPACE; + + prop->tag = cpu_to_fdt32(FDT_PROP); + prop->nameoff = cpu_to_fdt32(nameoff); + prop->len = cpu_to_fdt32(len); + memcpy(prop->data, val, len); + return 0; +} + +int fdt_finish(void *fdt) +{ + char *p = (char *)fdt; + fdt32_t *end; + int oldstroffset, newstroffset; + uint32_t tag; + int offset, nextoffset; + + FDT_SW_CHECK_HEADER(fdt); + + /* Add terminator */ + end = _fdt_grab_space(fdt, sizeof(*end)); + if (! end) + return -FDT_ERR_NOSPACE; + *end = cpu_to_fdt32(FDT_END); + + /* Relocate the string table */ + oldstroffset = fdt_totalsize(fdt) - fdt_size_dt_strings(fdt); + newstroffset = fdt_off_dt_struct(fdt) + fdt_size_dt_struct(fdt); + memmove(p + newstroffset, p + oldstroffset, fdt_size_dt_strings(fdt)); + fdt_set_off_dt_strings(fdt, newstroffset); + + /* Walk the structure, correcting string offsets */ + offset = 0; + while ((tag = fdt_next_tag(fdt, offset, &nextoffset)) != FDT_END) { + if (tag == FDT_PROP) { + struct fdt_property *prop = + _fdt_offset_ptr_w(fdt, offset); + int nameoff; + + nameoff = fdt32_to_cpu(prop->nameoff); + nameoff += fdt_size_dt_strings(fdt); + prop->nameoff = cpu_to_fdt32(nameoff); + } + offset = nextoffset; + } + if (nextoffset < 0) + return nextoffset; + + /* Finally, adjust the header */ + fdt_set_totalsize(fdt, newstroffset + fdt_size_dt_strings(fdt)); + fdt_set_magic(fdt, FDT_MAGIC); + return 0; +} diff --git a/components/drivers/fdt/libfdt/fdt_wip.c b/components/drivers/fdt/libfdt/fdt_wip.c new file mode 100644 index 0000000000..a9f2d66679 --- /dev/null +++ b/components/drivers/fdt/libfdt/fdt_wip.c @@ -0,0 +1,137 @@ +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "libfdt_env.h" +#include "fdt.h" +#include "libfdt.h" +#include "libfdt_internal.h" + +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len) +{ + void *propval; + int proplen; + + propval = fdt_getprop_namelen_w(fdt, nodeoffset, name, namelen, + &proplen); + if (!propval) + return proplen; + + if (proplen < (len + idx)) + return -FDT_ERR_NOSPACE; + + memcpy((char *)propval + idx, val, len); + return 0; +} + +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len) +{ + const void *propval; + int proplen; + + propval = fdt_getprop(fdt, nodeoffset, name, &proplen); + if (! propval) + return proplen; + + if (proplen != len) + return -FDT_ERR_NOSPACE; + + return fdt_setprop_inplace_namelen_partial(fdt, nodeoffset, name, + strlen(name), 0, + val, len); +} + +static void _fdt_nop_region(void *start, int len) +{ + fdt32_t *p; + + for (p = start; (char *)p < ((char *)start + len); p++) + *p = cpu_to_fdt32(FDT_NOP); +} + +int fdt_nop_property(void *fdt, int nodeoffset, const char *name) +{ + struct fdt_property *prop; + int len; + + prop = fdt_get_property_w(fdt, nodeoffset, name, &len); + if (! prop) + return len; + + _fdt_nop_region(prop, len + sizeof(*prop)); + + return 0; +} + +int _fdt_node_end_offset(void *fdt, int offset) +{ + int depth = 0; + + while ((offset >= 0) && (depth >= 0)) + offset = fdt_next_node(fdt, offset, &depth); + + return offset; +} + +int fdt_nop_node(void *fdt, int nodeoffset) +{ + int endoffset; + + endoffset = _fdt_node_end_offset(fdt, nodeoffset); + if (endoffset < 0) + return endoffset; + + _fdt_nop_region(fdt_offset_ptr_w(fdt, nodeoffset, 0), + endoffset - nodeoffset); + return 0; +} diff --git a/components/drivers/fdt/libfdt/libfdt.h b/components/drivers/fdt/libfdt/libfdt.h new file mode 100644 index 0000000000..05de18d721 --- /dev/null +++ b/components/drivers/fdt/libfdt/libfdt.h @@ -0,0 +1,1876 @@ +#ifndef _LIBFDT_H +#define _LIBFDT_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include "libfdt_env.h" +#include "fdt.h" + +#define FDT_FIRST_SUPPORTED_VERSION 0x10 +#define FDT_LAST_SUPPORTED_VERSION 0x11 + +/* Error codes: informative error codes */ +#define FDT_ERR_NOTFOUND 1 + /* FDT_ERR_NOTFOUND: The requested node or property does not exist */ +#define FDT_ERR_EXISTS 2 + /* FDT_ERR_EXISTS: Attempted to create a node or property which + * already exists */ +#define FDT_ERR_NOSPACE 3 + /* FDT_ERR_NOSPACE: Operation needed to expand the device + * tree, but its buffer did not have sufficient space to + * contain the expanded tree. Use fdt_open_into() to move the + * device tree to a buffer with more space. */ + +/* Error codes: codes for bad parameters */ +#define FDT_ERR_BADOFFSET 4 + /* FDT_ERR_BADOFFSET: Function was passed a structure block + * offset which is out-of-bounds, or which points to an + * unsuitable part of the structure for the operation. */ +#define FDT_ERR_BADPATH 5 + /* FDT_ERR_BADPATH: Function was passed a badly formatted path + * (e.g. missing a leading / for a function which requires an + * absolute path) */ +#define FDT_ERR_BADPHANDLE 6 + /* FDT_ERR_BADPHANDLE: Function was passed an invalid phandle. + * This can be caused either by an invalid phandle property + * length, or the phandle value was either 0 or -1, which are + * not permitted. */ +#define FDT_ERR_BADSTATE 7 + /* FDT_ERR_BADSTATE: Function was passed an incomplete device + * tree created by the sequential-write functions, which is + * not sufficiently complete for the requested operation. */ + +/* Error codes: codes for bad device tree blobs */ +#define FDT_ERR_TRUNCATED 8 + /* FDT_ERR_TRUNCATED: Structure block of the given device tree + * ends without an FDT_END tag. */ +#define FDT_ERR_BADMAGIC 9 + /* FDT_ERR_BADMAGIC: Given "device tree" appears not to be a + * device tree at all - it is missing the flattened device + * tree magic number. */ +#define FDT_ERR_BADVERSION 10 + /* FDT_ERR_BADVERSION: Given device tree has a version which + * can't be handled by the requested operation. For + * read-write functions, this may mean that fdt_open_into() is + * required to convert the tree to the expected version. */ +#define FDT_ERR_BADSTRUCTURE 11 + /* FDT_ERR_BADSTRUCTURE: Given device tree has a corrupt + * structure block or other serious error (e.g. misnested + * nodes, or subnodes preceding properties). */ +#define FDT_ERR_BADLAYOUT 12 + /* FDT_ERR_BADLAYOUT: For read-write functions, the given + * device tree has it's sub-blocks in an order that the + * function can't handle (memory reserve map, then structure, + * then strings). Use fdt_open_into() to reorganize the tree + * into a form suitable for the read-write operations. */ + +/* "Can't happen" error indicating a bug in libfdt */ +#define FDT_ERR_INTERNAL 13 + /* FDT_ERR_INTERNAL: libfdt has failed an internal assertion. + * Should never be returned, if it is, it indicates a bug in + * libfdt itself. */ + +/* Errors in device tree content */ +#define FDT_ERR_BADNCELLS 14 + /* FDT_ERR_BADNCELLS: Device tree has a #address-cells, #size-cells + * or similar property with a bad format or value */ + +#define FDT_ERR_BADVALUE 15 + /* FDT_ERR_BADVALUE: Device tree has a property with an unexpected + * value. For example: a property expected to contain a string list + * is not NUL-terminated within the length of its value. */ + +#define FDT_ERR_BADOVERLAY 16 + /* FDT_ERR_BADOVERLAY: The device tree overlay, while + * correctly structured, cannot be applied due to some + * unexpected or missing value, property or node. */ + +#define FDT_ERR_NOPHANDLES 17 + /* FDT_ERR_NOPHANDLES: The device tree doesn't have any + * phandle available anymore without causing an overflow */ + +#define FDT_ERR_MAX 17 + +/**********************************************************************/ +/* Low-level functions (you probably don't need these) */ +/**********************************************************************/ + +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_offset_ptr(const void *fdt, int offset, unsigned int checklen); +#endif +static inline void *fdt_offset_ptr_w(void *fdt, int offset, int checklen) +{ + return (void *)(uintptr_t)fdt_offset_ptr(fdt, offset, checklen); +} + +uint32_t fdt_next_tag(const void *fdt, int offset, int *nextoffset); + +static inline void fdt32_st(void *property, uint32_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 24; + bp[1] = (value >> 16) & 0xff; + bp[2] = (value >> 8) & 0xff; + bp[3] = value & 0xff; +} + +static inline void fdt64_st(void *property, uint64_t value) +{ + uint8_t *bp = (uint8_t *)property; + + bp[0] = value >> 56; + bp[1] = (value >> 48) & 0xff; + bp[2] = (value >> 40) & 0xff; + bp[3] = (value >> 32) & 0xff; + bp[4] = (value >> 24) & 0xff; + bp[5] = (value >> 16) & 0xff; + bp[6] = (value >> 8) & 0xff; + bp[7] = value & 0xff; +} + +/**********************************************************************/ +/* Traversal functions */ +/**********************************************************************/ + +int fdt_next_node(const void *fdt, int offset, int *depth); + +/** + * fdt_first_subnode() - get offset of first direct subnode + * + * @fdt: FDT blob + * @offset: Offset of node to check + * @return offset of first subnode, or -FDT_ERR_NOTFOUND if there is none + */ +int fdt_first_subnode(const void *fdt, int offset); + +/** + * fdt_next_subnode() - get offset of next direct subnode + * + * After first calling fdt_first_subnode(), call this function repeatedly to + * get direct subnodes of a parent node. + * + * @fdt: FDT blob + * @offset: Offset of previous subnode + * @return offset of next subnode, or -FDT_ERR_NOTFOUND if there are no more + * subnodes + */ +int fdt_next_subnode(const void *fdt, int offset); + +/** + * fdt_for_each_subnode - iterate over all subnodes of a parent + * + * @node: child node (int, lvalue) + * @fdt: FDT blob (const void *) + * @parent: parent node (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_subnode(node, fdt, parent) { + * Use node + * ... + * } + * + * if ((node < 0) && (node != -FDT_ERR_NOT_FOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and @node is used as + * iterator in the loop. The parent variable be constant or even a + * literal. + * + */ +#define fdt_for_each_subnode(node, fdt, parent) \ + for (node = fdt_first_subnode(fdt, parent); \ + node >= 0; \ + node = fdt_next_subnode(fdt, node)) + +/**********************************************************************/ +/* General functions */ +/**********************************************************************/ +#define fdt_get_header(fdt, field) \ + (fdt32_to_cpu(((const struct fdt_header *)(fdt))->field)) +#define fdt_magic(fdt) (fdt_get_header(fdt, magic)) +#define fdt_totalsize(fdt) (fdt_get_header(fdt, totalsize)) +#define fdt_off_dt_struct(fdt) (fdt_get_header(fdt, off_dt_struct)) +#define fdt_off_dt_strings(fdt) (fdt_get_header(fdt, off_dt_strings)) +#define fdt_off_mem_rsvmap(fdt) (fdt_get_header(fdt, off_mem_rsvmap)) +#define fdt_version(fdt) (fdt_get_header(fdt, version)) +#define fdt_last_comp_version(fdt) (fdt_get_header(fdt, last_comp_version)) +#define fdt_boot_cpuid_phys(fdt) (fdt_get_header(fdt, boot_cpuid_phys)) +#define fdt_size_dt_strings(fdt) (fdt_get_header(fdt, size_dt_strings)) +#define fdt_size_dt_struct(fdt) (fdt_get_header(fdt, size_dt_struct)) + +#define __fdt_set_hdr(name) \ + static inline void fdt_set_##name(void *fdt, uint32_t val) \ + { \ + struct fdt_header *fdth = (struct fdt_header *)fdt; \ + fdth->name = cpu_to_fdt32(val); \ + } +__fdt_set_hdr(magic); +__fdt_set_hdr(totalsize); +__fdt_set_hdr(off_dt_struct); +__fdt_set_hdr(off_dt_strings); +__fdt_set_hdr(off_mem_rsvmap); +__fdt_set_hdr(version); +__fdt_set_hdr(last_comp_version); +__fdt_set_hdr(boot_cpuid_phys); +__fdt_set_hdr(size_dt_strings); +__fdt_set_hdr(size_dt_struct); +#undef __fdt_set_hdr + +/** + * fdt_check_header - sanity check a device tree or possible device tree + * @fdt: pointer to data which might be a flattened device tree + * + * fdt_check_header() checks that the given buffer contains what + * appears to be a flattened device tree with sane information in its + * header. + * + * returns: + * 0, if the buffer appears to contain a valid device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings, as above + */ +int fdt_check_header(const void *fdt); + +/** + * fdt_move - move a device tree around in memory + * @fdt: pointer to the device tree to move + * @buf: pointer to memory where the device is to be moved + * @bufsize: size of the memory space at buf + * + * fdt_move() relocates, if possible, the device tree blob located at + * fdt to the buffer at buf of size bufsize. The buffer may overlap + * with the existing device tree blob at fdt. Therefore, + * fdt_move(fdt, fdt, fdt_totalsize(fdt)) + * should always succeed. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, bufsize is insufficient to contain the device tree + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_move(const void *fdt, void *buf, int bufsize); + +/**********************************************************************/ +/* Read-only functions */ +/**********************************************************************/ + +/** + * fdt_string - retrieve a string from the strings block of a device tree + * @fdt: pointer to the device tree blob + * @stroffset: offset of the string within the strings block (native endian) + * + * fdt_string() retrieves a pointer to a single string from the + * strings block of the device tree blob at fdt. + * + * returns: + * a pointer to the string, on success + * NULL, if stroffset is out of bounds + */ +const char *fdt_string(const void *fdt, int stroffset); + +/** + * fdt_get_max_phandle - retrieves the highest phandle in a tree + * @fdt: pointer to the device tree blob + * + * fdt_get_max_phandle retrieves the highest phandle in the given + * device tree. This will ignore badly formatted phandles, or phandles + * with a value of 0 or -1. + * + * returns: + * the highest phandle on success + * 0, if no phandle was found in the device tree + * -1, if an error occurred + */ +uint32_t fdt_get_max_phandle(const void *fdt); + +/** + * fdt_num_mem_rsv - retrieve the number of memory reserve map entries + * @fdt: pointer to the device tree blob + * + * Returns the number of entries in the device tree blob's memory + * reservation map. This does not include the terminating 0,0 entry + * or any other (0,0) entries reserved for expansion. + * + * returns: + * the number of entries + */ +int fdt_num_mem_rsv(const void *fdt); + +/** + * fdt_get_mem_rsv - retrieve one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: pointers to 64-bit variables + * + * On success, *address and *size will contain the address and size of + * the n-th reserve map entry from the device tree blob, in + * native-endian format. + * + * returns: + * 0, on success + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_get_mem_rsv(const void *fdt, int n, uint64_t *address, uint64_t *size); + +/** + * fdt_subnode_offset_namelen - find a subnode based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_subnode_offset(), but only examine the first + * namelen characters of name for matching the subnode name. This is + * useful for finding subnodes based on a portion of a larger string, + * such as a full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_subnode_offset_namelen(const void *fdt, int parentoffset, + const char *name, int namelen); +#endif +/** + * fdt_subnode_offset - find a subnode of a given node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_subnode_offset() finds a subnode of the node at structure block + * offset parentoffset with the given name. name may include a unit + * address, in which case fdt_subnode_offset() will find the subnode + * with that unit address, or the unit address may be omitted, in + * which case fdt_subnode_offset() will find an arbitrary subnode + * whose name excluding unit address matches the given name. + * + * returns: + * structure block offset of the requested subnode (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_subnode_offset(const void *fdt, int parentoffset, const char *name); + +/** + * fdt_path_offset_namelen - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * @namelen: number of characters of path to consider + * + * Identical to fdt_path_offset(), but only consider the first namelen + * characters of path as the path name. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_path_offset_namelen(const void *fdt, const char *path, int namelen); +#endif + +/** + * fdt_path_offset - find a tree node by its full path + * @fdt: pointer to the device tree blob + * @path: full path of the node to locate + * + * fdt_path_offset() finds a node of a given path in the device tree. + * Each path component may omit the unit address portion, but the + * results of this are undefined if any such path component is + * ambiguous (that is if there are multiple nodes at the relevant + * level matching the given component, differentiated only by unit + * address). + * + * returns: + * structure block offset of the node with the requested path (>=0), on + * success + * -FDT_ERR_BADPATH, given path does not begin with '/' or is invalid + * -FDT_ERR_NOTFOUND, if the requested node does not exist + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_path_offset(const void *fdt, const char *path); + +/** + * fdt_get_name - retrieve the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the starting node + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_name() retrieves the name (including unit address) of the + * device tree node at structure block offset nodeoffset. If lenp is + * non-NULL, the length of this name is also returned, in the integer + * pointed to by lenp. + * + * returns: + * pointer to the node's name, on success + * If lenp is non-NULL, *lenp contains the length of that name + * (>=0) + * NULL, on error + * if lenp is non-NULL *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +const char *fdt_get_name(const void *fdt, int nodeoffset, int *lenp); + +/** + * fdt_first_property_offset - find the offset of a node's first property + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * + * fdt_first_property_offset() finds the first property of the node at + * the given structure block offset. + * + * returns: + * structure block offset of the property (>=0), on success + * -FDT_ERR_NOTFOUND, if the requested node has no properties + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_first_property_offset(const void *fdt, int nodeoffset); + +/** + * fdt_next_property_offset - step through a node's properties + * @fdt: pointer to the device tree blob + * @offset: structure block offset of a property + * + * fdt_next_property_offset() finds the property immediately after the + * one at the given structure block offset. This will be a property + * of the same node as the given property. + * + * returns: + * structure block offset of the next property (>=0), on success + * -FDT_ERR_NOTFOUND, if the given property is the last in its node + * -FDT_ERR_BADOFFSET, if nodeoffset did not point to an FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_next_property_offset(const void *fdt, int offset); + +/** + * fdt_for_each_property_offset - iterate over all properties of a node + * + * @property_offset: property offset (int, lvalue) + * @fdt: FDT blob (const void *) + * @node: node offset (int) + * + * This is actually a wrapper around a for loop and would be used like so: + * + * fdt_for_each_property_offset(property, fdt, node) { + * Use property + * ... + * } + * + * if ((property < 0) && (property != -FDT_ERR_NOT_FOUND)) { + * Error handling + * } + * + * Note that this is implemented as a macro and property is used as + * iterator in the loop. The node variable can be constant or even a + * literal. + */ +#define fdt_for_each_property_offset(property, fdt, node) \ + for (property = fdt_first_property_offset(fdt, node); \ + property >= 0; \ + property = fdt_next_property_offset(fdt, property)) + +/** + * fdt_get_property_by_offset - retrieve the property at a given offset + * @fdt: pointer to the device tree blob + * @offset: offset of the property to retrieve + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property_by_offset() retrieves a pointer to the + * fdt_property structure within the device tree blob at the given + * offset. If lenp is non-NULL, the length of the property value is + * also returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property_by_offset(const void *fdt, + int offset, + int *lenp); + +/** + * fdt_get_property_namelen - find a property based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_get_property(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const struct fdt_property *fdt_get_property_namelen(const void *fdt, + int nodeoffset, + const char *name, + int namelen, int *lenp); +#endif + +/** + * fdt_get_property - find a given property in a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_get_property() retrieves a pointer to the fdt_property + * structure within the device tree blob corresponding to the property + * named 'name' of the node at offset nodeoffset. If lenp is + * non-NULL, the length of the property value is also returned, in the + * integer pointed to by lenp. + * + * returns: + * pointer to the structure representing the property + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const struct fdt_property *fdt_get_property(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline struct fdt_property *fdt_get_property_w(void *fdt, int nodeoffset, + const char *name, + int *lenp) +{ + return (struct fdt_property *)(uintptr_t) + fdt_get_property(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_getprop_by_offset - retrieve the value of a property at a given offset + * @fdt: pointer to the device tree blob + * @ffset: offset of the property to read + * @namep: pointer to a string variable (will be overwritten) or NULL + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop_by_offset() retrieves a pointer to the value of the + * property at structure block offset 'offset' (this will be a pointer + * to within the device blob itself, not a copy of the value). If + * lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. If namep is non-NULL, + * the property's namne will also be returned in the char * pointed to + * by namep (this will be a pointer to within the device tree's string + * block, not a new copy of the name). + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * if namep is non-NULL *namep contiains a pointer to the property + * name. + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_PROP tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* This function is not useful in Python */ +const void *fdt_getprop_by_offset(const void *fdt, int offset, + const char **namep, int *lenp); +#endif + +/** + * fdt_getprop_namelen - get property value based on substring + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @namelen: number of characters of name to consider + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * Identical to fdt_getprop(), but only examine the first namelen + * characters of name for matching the property name. + */ +#ifndef SWIG /* Not available in Python */ +const void *fdt_getprop_namelen(const void *fdt, int nodeoffset, + const char *name, int namelen, int *lenp); +static inline void *fdt_getprop_namelen_w(void *fdt, int nodeoffset, + const char *name, int namelen, + int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop_namelen(fdt, nodeoffset, name, + namelen, lenp); +} +#endif + +/** + * fdt_getprop - retrieve the value of a given property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to find + * @name: name of the property to find + * @lenp: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_getprop() retrieves a pointer to the value of the property + * named 'name' of the node at offset nodeoffset (this will be a + * pointer to within the device blob itself, not a copy of the value). + * If lenp is non-NULL, the length of the property value is also + * returned, in the integer pointed to by lenp. + * + * returns: + * pointer to the property's value + * if lenp is non-NULL, *lenp contains the length of the property + * value (>=0) + * NULL, on error + * if lenp is non-NULL, *lenp contains an error code (<0): + * -FDT_ERR_NOTFOUND, node does not have named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE + * tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +const void *fdt_getprop(const void *fdt, int nodeoffset, + const char *name, int *lenp); +static inline void *fdt_getprop_w(void *fdt, int nodeoffset, + const char *name, int *lenp) +{ + return (void *)(uintptr_t)fdt_getprop(fdt, nodeoffset, name, lenp); +} + +/** + * fdt_get_phandle - retrieve the phandle of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of the node + * + * fdt_get_phandle() retrieves the phandle of the device tree node at + * structure block offset nodeoffset. + * + * returns: + * the phandle of the node at nodeoffset, on success (!= 0, != -1) + * 0, if the node has no phandle, or another error occurs + */ +uint32_t fdt_get_phandle(const void *fdt, int nodeoffset); + +/** + * fdt_get_alias_namelen - get alias based on substring + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * @namelen: number of characters of name to consider + * + * Identical to fdt_get_alias(), but only examine the first namelen + * characters of name for matching the alias name. + */ +#ifndef SWIG /* Not available in Python */ +const char *fdt_get_alias_namelen(const void *fdt, + const char *name, int namelen); +#endif + +/** + * fdt_get_alias - retrieve the path referenced by a given alias + * @fdt: pointer to the device tree blob + * @name: name of the alias th look up + * + * fdt_get_alias() retrieves the value of a given alias. That is, the + * value of the property named 'name' in the node /aliases. + * + * returns: + * a pointer to the expansion of the alias named 'name', if it exists + * NULL, if the given alias or the /aliases node does not exist + */ +const char *fdt_get_alias(const void *fdt, const char *name); + +/** + * fdt_get_path - determine the full path of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose path to find + * @buf: character buffer to contain the returned path (will be overwritten) + * @buflen: size of the character buffer at buf + * + * fdt_get_path() computes the full path of the node at offset + * nodeoffset, and records that path in the buffer at buf. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * 0, on success + * buf contains the absolute path of the node at + * nodeoffset, as a NUL-terminated string. + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOSPACE, the path of the given node is longer than (bufsize-1) + * characters and will not fit in the given buffer. + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_get_path(const void *fdt, int nodeoffset, char *buf, int buflen); + +/** + * fdt_supernode_atdepth_offset - find a specific ancestor of a node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * @supernodedepth: depth of the ancestor to find + * @nodedepth: pointer to an integer variable (will be overwritten) or NULL + * + * fdt_supernode_atdepth_offset() finds an ancestor of the given node + * at a specific depth from the root (where the root itself has depth + * 0, its immediate subnodes depth 1 and so forth). So + * fdt_supernode_atdepth_offset(fdt, nodeoffset, 0, NULL); + * will always return 0, the offset of the root node. If the node at + * nodeoffset has depth D, then: + * fdt_supernode_atdepth_offset(fdt, nodeoffset, D, NULL); + * will return nodeoffset itself. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * structure block offset of the node at node offset's ancestor + * of depth supernodedepth (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_NOTFOUND, supernodedepth was greater than the depth of + * nodeoffset + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_supernode_atdepth_offset(const void *fdt, int nodeoffset, + int supernodedepth, int *nodedepth); + +/** + * fdt_node_depth - find the depth of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_node_depth() finds the depth of a given node. The root node + * has depth 0, its immediate subnodes depth 1 and so forth. + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset. + * + * returns: + * depth of the node at nodeoffset (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_depth(const void *fdt, int nodeoffset); + +/** + * fdt_parent_offset - find the parent of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose parent to find + * + * fdt_parent_offset() locates the parent node of a given node (that + * is, it finds the offset of the node which contains the node at + * nodeoffset as a subnode). + * + * NOTE: This function is expensive, as it must scan the device tree + * structure from the start to nodeoffset, *twice*. + * + * returns: + * structure block offset of the parent of the node at nodeoffset + * (>=0), on success + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_parent_offset(const void *fdt, int nodeoffset); + +/** + * fdt_node_offset_by_prop_value - find nodes with a given property value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @propname: property name to check + * @propval: property value to search for + * @proplen: length of the value in propval + * + * fdt_node_offset_by_prop_value() returns the offset of the first + * node after startoffset, which has a property named propname whose + * value is of length proplen and has value equal to propval; or if + * startoffset is -1, the very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_prop_value(fdt, -1, propname, + * propval, proplen); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_prop_value(fdt, offset, propname, + * propval, proplen); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_prop_value(const void *fdt, int startoffset, + const char *propname, + const void *propval, int proplen); + +/** + * fdt_node_offset_by_phandle - find the node with a given phandle + * @fdt: pointer to the device tree blob + * @phandle: phandle value + * + * fdt_node_offset_by_phandle() returns the offset of the node + * which has the given phandle value. If there is more than one node + * in the tree with the given phandle (an invalid tree), results are + * undefined. + * + * returns: + * structure block offset of the located node (>= 0), on success + * -FDT_ERR_NOTFOUND, no node with that phandle exists + * -FDT_ERR_BADPHANDLE, given phandle value was invalid (0 or -1) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle); + +/** + * fdt_node_check_compatible: check a node's compatible property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @compatible: string to match against + * + * + * fdt_node_check_compatible() returns 0 if the given node contains a + * 'compatible' property with the given string as one of its elements, + * it returns non-zero otherwise, or on error. + * + * returns: + * 0, if the node has a 'compatible' property listing the given string + * 1, if the node has a 'compatible' property, but it does not list + * the given string + * -FDT_ERR_NOTFOUND, if the given node has no 'compatible' property + * -FDT_ERR_BADOFFSET, if nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_check_compatible(const void *fdt, int nodeoffset, + const char *compatible); + +/** + * fdt_node_offset_by_compatible - find nodes with a given 'compatible' value + * @fdt: pointer to the device tree blob + * @startoffset: only find nodes after this offset + * @compatible: 'compatible' string to match against + * + * fdt_node_offset_by_compatible() returns the offset of the first + * node after startoffset, which has a 'compatible' property which + * lists the given compatible string; or if startoffset is -1, the + * very first such node in the tree. + * + * To iterate through all nodes matching the criterion, the following + * idiom can be used: + * offset = fdt_node_offset_by_compatible(fdt, -1, compatible); + * while (offset != -FDT_ERR_NOTFOUND) { + * // other code here + * offset = fdt_node_offset_by_compatible(fdt, offset, compatible); + * } + * + * Note the -1 in the first call to the function, if 0 is used here + * instead, the function will never locate the root node, even if it + * matches the criterion. + * + * returns: + * structure block offset of the located node (>= 0, >startoffset), + * on success + * -FDT_ERR_NOTFOUND, no node matching the criterion exists in the + * tree after startoffset + * -FDT_ERR_BADOFFSET, nodeoffset does not refer to a BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, standard meanings + */ +int fdt_node_offset_by_compatible(const void *fdt, int startoffset, + const char *compatible); + +/** + * fdt_stringlist_contains - check a string list property for a string + * @strlist: Property containing a list of strings to check + * @listlen: Length of property + * @str: String to search for + * + * This is a utility function provided for convenience. The list contains + * one or more strings, each terminated by \0, as is found in a device tree + * "compatible" property. + * + * @return: 1 if the string is found in the list, 0 not found, or invalid list + */ +int fdt_stringlist_contains(const char *strlist, int listlen, const char *str); + +/** + * fdt_stringlist_count - count the number of strings in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @return: + * the number of strings in the given property + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +int fdt_stringlist_count(const void *fdt, int nodeoffset, const char *property); + +/** + * fdt_stringlist_search - find a string in a string list and return its index + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @string: string to look up in the string list + * + * Note that it is possible for this function to succeed on property values + * that are not NUL-terminated. That's because the function will stop after + * finding the first occurrence of @string. This can for example happen with + * small-valued cell properties, such as #address-cells, when searching for + * the empty string. + * + * @return: + * the index of the string in the list of strings + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist or does not contain + * the given string + */ +int fdt_stringlist_search(const void *fdt, int nodeoffset, const char *property, + const char *string); + +/** + * fdt_stringlist_get() - obtain the string at a given index in a string list + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of a tree node + * @property: name of the property containing the string list + * @index: index of the string to return + * @lenp: return location for the string length or an error code on failure + * + * Note that this will successfully extract strings from properties with + * non-NUL-terminated values. For example on small-valued cell properties + * this function will return the empty string. + * + * If non-NULL, the length of the string (on success) or a negative error-code + * (on failure) will be stored in the integer pointer to by lenp. + * + * @return: + * A pointer to the string at the given index in the string list or NULL on + * failure. On success the length of the string will be stored in the memory + * location pointed to by the lenp parameter, if non-NULL. On failure one of + * the following negative error codes will be returned in the lenp parameter + * (if non-NULL): + * -FDT_ERR_BADVALUE if the property value is not NUL-terminated + * -FDT_ERR_NOTFOUND if the property does not exist + */ +const char *fdt_stringlist_get(const void *fdt, int nodeoffset, + const char *property, int index, + int *lenp); + +/**********************************************************************/ +/* Read-only functions (addressing related) */ +/**********************************************************************/ + +/** + * FDT_MAX_NCELLS - maximum value for #address-cells and #size-cells + * + * This is the maximum value for #address-cells, #size-cells and + * similar properties that will be processed by libfdt. IEE1275 + * requires that OF implementations handle values up to 4. + * Implementations may support larger values, but in practice higher + * values aren't used. + */ +#define FDT_MAX_NCELLS 4 + +/** + * fdt_address_cells - retrieve address size for a bus represented in the tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address size for + * + * When the node has a valid #address-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #address-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_address_cells(const void *fdt, int nodeoffset); + +/** + * fdt_size_cells - retrieve address range size for a bus represented in the + * tree + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to find the address range size for + * + * When the node has a valid #size-cells property, returns its value. + * + * returns: + * 0 <= n < FDT_MAX_NCELLS, on success + * 2, if the node has no #address-cells property + * -FDT_ERR_BADNCELLS, if the node has a badly formatted or invalid + * #size-cells property + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_size_cells(const void *fdt, int nodeoffset); + + +/**********************************************************************/ +/* Write-in-place functions */ +/**********************************************************************/ + +/** + * fdt_setprop_inplace_namelen_partial - change a property's value, + * but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @namelen: number of characters of name to consider + * @idx: index of the property to change in the array + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * Identical to fdt_setprop_inplace(), but modifies the given property + * starting from the given index, and using only the first characters + * of the name. It is useful when you want to manipulate only one value of + * an array and you have a string that doesn't end with \0. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace_namelen_partial(void *fdt, int nodeoffset, + const char *name, int namelen, + uint32_t idx, const void *val, + int len); +#endif + +/** + * fdt_setprop_inplace - change a property's value, but not its size + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to replace the property value with + * @len: length of the property value + * + * fdt_setprop_inplace() replaces the value of a given property with + * the data in val, of length len. This function cannot change the + * size of a property, and so will only work if len is equal to the + * current length of the property. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if len is not equal to the property's current length + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#ifndef SWIG /* Not available in Python */ +int fdt_setprop_inplace(void *fdt, int nodeoffset, const char *name, + const void *val, int len); +#endif + +/** + * fdt_setprop_inplace_u32 - change the value of a 32-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to replace the property with + * + * fdt_setprop_inplace_u32() replaces the value of a given property + * with the 32-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 4. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 4 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_u64 - change the value of a 64-bit integer property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to replace the property with + * + * fdt_setprop_inplace_u64() replaces the value of a given property + * with the 64-bit integer value in val, converting val to big-endian + * if necessary. This function cannot change the size of a property, + * and so will only work if the property already exists and has length + * 8. + * + * This function will alter only the bytes in the blob which contain + * the given property value, and will not alter or move any other part + * of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, if the property's length is not equal to 8 + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_inplace_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop_inplace(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_inplace_cell - change the value of a single-cell property + * + * This is an alternative name for fdt_setprop_inplace_u32() + */ +static inline int fdt_setprop_inplace_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_setprop_inplace_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_nop_property - replace a property with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_nop_property() will replace a given property's representation + * in the blob with FDT_NOP tags, effectively removing it from the + * tree. + * + * This function will alter only the bytes in the blob which contain + * the property, and will not alter or move any other part of the + * tree. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_property(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_nop_node - replace a node (subtree) with nop tags + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_nop_node() will replace a given node's representation in the + * blob, including all its subnodes, if any, with FDT_NOP tags, + * effectively removing it from the tree. + * + * This function will alter only the bytes in the blob which contain + * the node and its properties and subnodes, and will not alter or + * move any other part of the tree. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_nop_node(void *fdt, int nodeoffset); + +/**********************************************************************/ +/* Sequential write functions */ +/**********************************************************************/ + +int fdt_create(void *buf, int bufsize); +int fdt_resize(void *fdt, void *buf, int bufsize); +int fdt_add_reservemap_entry(void *fdt, uint64_t addr, uint64_t size); +int fdt_finish_reservemap(void *fdt); +int fdt_begin_node(void *fdt, const char *name); +int fdt_property(void *fdt, const char *name, const void *val, int len); +static inline int fdt_property_u32(void *fdt, const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_u64(void *fdt, const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_property(fdt, name, &tmp, sizeof(tmp)); +} +static inline int fdt_property_cell(void *fdt, const char *name, uint32_t val) +{ + return fdt_property_u32(fdt, name, val); +} +#define fdt_property_string(fdt, name, str) \ + fdt_property(fdt, name, str, strlen(str)+1) +int fdt_end_node(void *fdt); +int fdt_finish(void *fdt); + +/**********************************************************************/ +/* Read-write functions */ +/**********************************************************************/ + +int fdt_create_empty_tree(void *buf, int bufsize); +int fdt_open_into(const void *fdt, void *buf, int bufsize); +int fdt_pack(void *fdt); + +/** + * fdt_add_mem_rsv - add one memory reserve map entry + * @fdt: pointer to the device tree blob + * @address, @size: 64-bit values (native endian) + * + * Adds a reserve map entry to the given blob reserving a region at + * address address of length size. + * + * This function will insert data into the reserve map and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new reservation entry + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_add_mem_rsv(void *fdt, uint64_t address, uint64_t size); + +/** + * fdt_del_mem_rsv - remove a memory reserve map entry + * @fdt: pointer to the device tree blob + * @n: entry to remove + * + * fdt_del_mem_rsv() removes the n-th memory reserve map entry from + * the blob. + * + * This function will delete data from the reservation table and will + * therefore change the indexes of some entries in the table. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, there is no entry of the given index (i.e. there + * are less than n+1 reserve map entries) + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_mem_rsv(void *fdt, int n); + +/** + * fdt_set_name - change the name of a given node + * @fdt: pointer to the device tree blob + * @nodeoffset: structure block offset of a node + * @name: name to give the node + * + * fdt_set_name() replaces the name (including unit address, if any) + * of the given node with the given string. NOTE: this function can't + * efficiently check if the new name is unique amongst the given + * node's siblings; results are undefined if this function is invoked + * with a name equal to one of the given node's siblings. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob + * to contain the new name + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, standard meanings + */ +int fdt_set_name(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_setprop - create or change a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: pointer to data to set the property value to + * @len: length of the property value + * + * fdt_setprop() sets the value of the named property in the given + * node to the given value and length, creating the property if it + * does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_setprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_setprop_u32 - set a property to a 32-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value for the property (native endian) + * + * fdt_setprop_u32() sets the value of the named property in the given + * node to the given 32-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u32(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_u64 - set a property to a 64-bit integer + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value for the property (native endian) + * + * fdt_setprop_u64() sets the value of the named property in the given + * node to the given 64-bit integer value (converting to big-endian if + * necessary), or creates a new property with that value if it does + * not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_setprop_u64(void *fdt, int nodeoffset, const char *name, + uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_setprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_setprop_cell - set a property to a single cell value + * + * This is an alternative name for fdt_setprop_u32() + */ +static inline int fdt_setprop_cell(void *fdt, int nodeoffset, const char *name, + uint32_t val) +{ + return fdt_setprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_setprop_string - set a property to a string value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value for the property + * + * fdt_setprop_string() sets the value of the named property in the + * given node to the given string value (using the length of the + * string to determine the new length of the property), or creates a + * new property with that value if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_string(fdt, nodeoffset, name, str) \ + fdt_setprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + + +/** + * fdt_setprop_empty - set a property to an empty value + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * + * fdt_setprop_empty() sets the value of the named property in the + * given node to an empty (zero length) value, or creates a new empty + * property if it does not already exist. + * + * This function may insert or delete data from the blob, and will + * therefore change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_setprop_empty(fdt, nodeoffset, name) \ + fdt_setprop((fdt), (nodeoffset), (name), NULL, 0) + +/** + * fdt_appendprop - append to or create a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to append to + * @val: pointer to data to append to the property value + * @len: length of the data to append to the property value + * + * fdt_appendprop() appends the value to the named property in the + * given node, creating the property if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_appendprop(void *fdt, int nodeoffset, const char *name, + const void *val, int len); + +/** + * fdt_appendprop_u32 - append a 32-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 32-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u32() appends the given 32-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u32(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + fdt32_t tmp = cpu_to_fdt32(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_u64 - append a 64-bit integer value to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @val: 64-bit integer value to append to the property (native endian) + * + * fdt_appendprop_u64() appends the given 64-bit integer value + * (converting to big-endian if necessary) to the value of the named + * property in the given node, or creates a new property with that + * value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +static inline int fdt_appendprop_u64(void *fdt, int nodeoffset, + const char *name, uint64_t val) +{ + fdt64_t tmp = cpu_to_fdt64(val); + return fdt_appendprop(fdt, nodeoffset, name, &tmp, sizeof(tmp)); +} + +/** + * fdt_appendprop_cell - append a single cell value to a property + * + * This is an alternative name for fdt_appendprop_u32() + */ +static inline int fdt_appendprop_cell(void *fdt, int nodeoffset, + const char *name, uint32_t val) +{ + return fdt_appendprop_u32(fdt, nodeoffset, name, val); +} + +/** + * fdt_appendprop_string - append a string to a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to change + * @name: name of the property to change + * @str: string value to append to the property + * + * fdt_appendprop_string() appends the given string to the value of + * the named property in the given node, or creates a new property + * with that value if it does not already exist. + * + * This function may insert data into the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there is insufficient free space in the blob to + * contain the new property value + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_TRUNCATED, standard meanings + */ +#define fdt_appendprop_string(fdt, nodeoffset, name, str) \ + fdt_appendprop((fdt), (nodeoffset), (name), (str), strlen(str)+1) + +/** + * fdt_delprop - delete a property + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node whose property to nop + * @name: name of the property to nop + * + * fdt_del_property() will delete the given property. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_NOTFOUND, node does not have the named property + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_delprop(void *fdt, int nodeoffset, const char *name); + +/** + * fdt_add_subnode_namelen - creates a new node based on substring + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * @namelen: number of characters of name to consider + * + * Identical to fdt_add_subnode(), but use only the first namelen + * characters of name as the name of the new node. This is useful for + * creating subnodes based on a portion of a larger string, such as a + * full path. + */ +#ifndef SWIG /* Not available in Python */ +int fdt_add_subnode_namelen(void *fdt, int parentoffset, + const char *name, int namelen); +#endif + +/** + * fdt_add_subnode - creates a new node + * @fdt: pointer to the device tree blob + * @parentoffset: structure block offset of a node + * @name: name of the subnode to locate + * + * fdt_add_subnode() creates a new node as a subnode of the node at + * structure block offset parentoffset, with the given name (which + * should include the unit address, if any). + * + * This function will insert data into the blob, and will therefore + * change the offsets of some existing nodes. + + * returns: + * structure block offset of the created nodeequested subnode (>=0), on + * success + * -FDT_ERR_NOTFOUND, if the requested subnode does not exist + * -FDT_ERR_BADOFFSET, if parentoffset did not point to an FDT_BEGIN_NODE + * tag + * -FDT_ERR_EXISTS, if the node at parentoffset already has a subnode of + * the given name + * -FDT_ERR_NOSPACE, if there is insufficient free space in the + * blob to contain the new node + * -FDT_ERR_NOSPACE + * -FDT_ERR_BADLAYOUT + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings. + */ +int fdt_add_subnode(void *fdt, int parentoffset, const char *name); + +/** + * fdt_del_node - delete a node (subtree) + * @fdt: pointer to the device tree blob + * @nodeoffset: offset of the node to nop + * + * fdt_del_node() will remove the given node, including all its + * subnodes if any, from the blob. + * + * This function will delete data from the blob, and will therefore + * change the offsets of some existing nodes. + * + * returns: + * 0, on success + * -FDT_ERR_BADOFFSET, nodeoffset did not point to FDT_BEGIN_NODE tag + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_del_node(void *fdt, int nodeoffset); + +/** + * fdt_overlay_apply - Applies a DT overlay on a base DT + * @fdt: pointer to the base device tree blob + * @fdto: pointer to the device tree overlay blob + * + * fdt_overlay_apply() will apply the given device tree overlay on the + * given base device tree. + * + * Expect the base device tree to be modified, even if the function + * returns an error. + * + * returns: + * 0, on success + * -FDT_ERR_NOSPACE, there's not enough space in the base device tree + * -FDT_ERR_NOTFOUND, the overlay points to some inexistant nodes or + * properties in the base DT + * -FDT_ERR_BADPHANDLE, + * -FDT_ERR_BADOVERLAY, + * -FDT_ERR_NOPHANDLES, + * -FDT_ERR_INTERNAL, + * -FDT_ERR_BADLAYOUT, + * -FDT_ERR_BADMAGIC, + * -FDT_ERR_BADOFFSET, + * -FDT_ERR_BADPATH, + * -FDT_ERR_BADVERSION, + * -FDT_ERR_BADSTRUCTURE, + * -FDT_ERR_BADSTATE, + * -FDT_ERR_TRUNCATED, standard meanings + */ +int fdt_overlay_apply(void *fdt, void *fdto); + +/**********************************************************************/ +/* Debugging / informational functions */ +/**********************************************************************/ + +const char *fdt_strerror(int errval); + +#endif /* _LIBFDT_H */ diff --git a/components/drivers/fdt/libfdt/libfdt_env.h b/components/drivers/fdt/libfdt/libfdt_env.h new file mode 100644 index 0000000000..f4a6b6063b --- /dev/null +++ b/components/drivers/fdt/libfdt/libfdt_env.h @@ -0,0 +1,113 @@ +#ifndef _LIBFDT_ENV_H +#define _LIBFDT_ENV_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * Copyright 2012 Kim Phillips, Freescale Semiconductor. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <stddef.h> +#include <stdint.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __CHECKER__ +#define FDT_FORCE __attribute__((force)) +#define FDT_BITWISE __attribute__((bitwise)) +#else +#define FDT_FORCE +#define FDT_BITWISE +#endif + +typedef uint16_t FDT_BITWISE fdt16_t; +typedef uint32_t FDT_BITWISE fdt32_t; +typedef uint64_t FDT_BITWISE fdt64_t; +typedef uint64_t unaligned_fdt64_t __attribute__((aligned(4))); + +#define EXTRACT_BYTE(x, n) ((unsigned long long)((uint8_t *)&x)[n]) +#define CPU_TO_FDT16(x) ((EXTRACT_BYTE(x, 0) << 8) | EXTRACT_BYTE(x, 1)) +#define CPU_TO_FDT32(x) ((EXTRACT_BYTE(x, 0) << 24) | (EXTRACT_BYTE(x, 1) << 16) | \ + (EXTRACT_BYTE(x, 2) << 8) | EXTRACT_BYTE(x, 3)) +#define CPU_TO_FDT64(x) ((EXTRACT_BYTE(x, 0) << 56) | (EXTRACT_BYTE(x, 1) << 48) | \ + (EXTRACT_BYTE(x, 2) << 40) | (EXTRACT_BYTE(x, 3) << 32) | \ + (EXTRACT_BYTE(x, 4) << 24) | (EXTRACT_BYTE(x, 5) << 16) | \ + (EXTRACT_BYTE(x, 6) << 8) | EXTRACT_BYTE(x, 7)) + +static inline uint16_t fdt16_to_cpu(fdt16_t x) +{ + return (FDT_FORCE uint16_t)CPU_TO_FDT16(x); +} +static inline fdt16_t cpu_to_fdt16(uint16_t x) +{ + return (FDT_FORCE fdt16_t)CPU_TO_FDT16(x); +} + +static inline uint32_t fdt32_to_cpu(fdt32_t x) +{ + return (FDT_FORCE uint32_t)CPU_TO_FDT32(x); +} +static inline fdt32_t cpu_to_fdt32(uint32_t x) +{ + return (FDT_FORCE fdt32_t)CPU_TO_FDT32(x); +} + +static inline uint64_t fdt64_to_cpu(fdt64_t x) +{ + return (FDT_FORCE uint64_t)CPU_TO_FDT64(x); +} +static inline fdt64_t cpu_to_fdt64(uint64_t x) +{ + return (FDT_FORCE fdt64_t)CPU_TO_FDT64(x); +} +#undef CPU_TO_FDT64 +#undef CPU_TO_FDT32 +#undef CPU_TO_FDT16 +#undef EXTRACT_BYTE + +#endif /* _LIBFDT_ENV_H */ diff --git a/components/drivers/fdt/libfdt/libfdt_internal.h b/components/drivers/fdt/libfdt/libfdt_internal.h new file mode 100644 index 0000000000..5cb817b365 --- /dev/null +++ b/components/drivers/fdt/libfdt/libfdt_internal.h @@ -0,0 +1,95 @@ +#ifndef _LIBFDT_INTERNAL_H +#define _LIBFDT_INTERNAL_H +/* + * libfdt - Flat Device Tree manipulation + * Copyright (C) 2006 David Gibson, IBM Corporation. + * + * libfdt is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * + * a) This library 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; either version 2 of the + * License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the Free + * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, + * MA 02110-1301 USA + * + * Alternatively, + * + * b) Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * + * 1. Redistributions of source code must retain the above + * copyright notice, this list of conditions and the following + * disclaimer. + * 2. Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, + * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +#include "fdt.h" + +#define FDT_ALIGN(x, a) (((x) + (a) - 1) & ~((a) - 1)) +#define FDT_TAGALIGN(x) (FDT_ALIGN((x), FDT_TAGSIZE)) + +#define FDT_CHECK_HEADER(fdt) \ + { \ + int __err; \ + if ((__err = fdt_check_header(fdt)) != 0) \ + return __err; \ + } + +int _fdt_check_node_offset(const void *fdt, int offset); +int _fdt_check_prop_offset(const void *fdt, int offset); +const char *_fdt_find_string(const char *strtab, int tabsize, const char *s); +int _fdt_node_end_offset(void *fdt, int nodeoffset); + +static inline const void *_fdt_offset_ptr(const void *fdt, int offset) +{ + return (const char *)fdt + fdt_off_dt_struct(fdt) + offset; +} + +static inline void *_fdt_offset_ptr_w(void *fdt, int offset) +{ + return (void *)(uintptr_t)_fdt_offset_ptr(fdt, offset); +} + +static inline const struct fdt_reserve_entry *_fdt_mem_rsv(const void *fdt, int n) +{ + const struct fdt_reserve_entry *rsv_table = + (const struct fdt_reserve_entry *) + ((const char *)fdt + fdt_off_mem_rsvmap(fdt)); + + return rsv_table + n; +} +static inline struct fdt_reserve_entry *_fdt_mem_rsv_w(void *fdt, int n) +{ + return (void *)(uintptr_t)_fdt_mem_rsv(fdt, n); +} + +#define FDT_SW_MAGIC (~FDT_MAGIC) + +#endif /* _LIBFDT_INTERNAL_H */ diff --git a/components/drivers/fdt/src/SConscript b/components/drivers/fdt/src/SConscript new file mode 100644 index 0000000000..74ae118929 --- /dev/null +++ b/components/drivers/fdt/src/SConscript @@ -0,0 +1,17 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +list = os.listdir(cwd) +CPPPATH = [cwd + '/../inc' , cwd + '/libfdt'] +objs = [] + +group = DefineGroup('FDT', src, depend = ['RT_USING_FDT'], CPPPATH = CPPPATH) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) +objs = objs + group + +Return('objs')
\ No newline at end of file diff --git a/components/drivers/fdt/src/dtb_access.c b/components/drivers/fdt/src/dtb_access.c new file mode 100644 index 0000000000..71de2989db --- /dev/null +++ b/components/drivers/fdt/src/dtb_access.c @@ -0,0 +1,650 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#define _GNU_SOURCE + +#include "libfdt.h" +#include "dtb_node.h" + +/* "/aliaes" node */ +static struct dtb_node *fdt_aliases; + +/** + * of_find_property_value_of_size() - find property of given size + * + * Search for a property in a device node and validate the requested size. + * + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @len: requested length of property value + * + * @return the property value on success, -EINVAL if the property does not + * exist, -ENODATA if property does not have a value, and -EOVERFLOW if the + * property data isn't large enough. + */ +static void *dtb_node_find_property_value_of_size(const struct dtb_node *dn, + const char *propname, uint32_t len) +{ + struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL); + + if (!prop) + return ERR_PTR(-EINVAL); + if (!prop->value) + return ERR_PTR(-ENODATA); + if (len > prop->size) + return ERR_PTR(-EOVERFLOW); + return prop->value; +} + +int dtb_node_read_u32(const struct dtb_node *dn, const char *propname, uint32_t *outp) +{ + const uint32_t *val; + + debug("%s: %s: \n", __func__, propname); + if (!dn) + return -EINVAL; + val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp)); + if (IS_ERR(val)) + { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = fdt32_to_cpu(*val); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + +uint32_t dtb_node_read_u32_default(const struct dtb_node *node, const char *propname, uint32_t def) +{ + dtb_node_read_u32(node, propname, &def); + + return def; +} + +int dtb_node_read_u32_array(const struct dtb_node *dn, const char *propname, + uint32_t *out_values, size_t sz) +{ + const uint32_t *val; + + debug("%s: %s: ", __func__, propname); + val = dtb_node_find_property_value_of_size(dn, propname, + sz * sizeof(*out_values)); + + if (IS_ERR(val)) + return PTR_ERR(val); + + debug("size %zd, val:%d\n", sz, *val); + while (sz--) + *out_values++ = fdt32_to_cpu(*val++); + + return 0; +} + +uint32_t dtb_node_read_u32_index_default(const struct dtb_node *node, const char *propname, int index, + uint32_t def) +{ + RT_ASSERT(dtb_node_valid(node)); + dtb_node_read_u32_index(node, propname, index, &def); + + return def; +} + +int dtb_node_read_s32_default(const struct dtb_node *node, const char *propname, int32_t def) +{ + RT_ASSERT(dtb_node_valid(node)); + dtb_node_read_u32(node, propname, (uint32_t *)&def); + + return def; +} + +int dtb_node_read_u32_index(const struct dtb_node *dn, const char *propname, + int index, uint32_t *outp) +{ + const uint32_t *val; + + debug("%s: %s: ", __func__, propname); + if (!dn) + return -EINVAL; + + val = dtb_node_find_property_value_of_size(dn, propname, + sizeof(*outp) * (index + 1)); + if (IS_ERR(val)) + { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = fdt32_to_cpu(val[index]); + debug("%#x (%d)\n", *outp, *outp); + + return 0; +} + +int dtb_node_read_u64(const struct dtb_node *dn, const char *propname, uint64_t *outp) +{ + const uint64_t *val; + + debug("%s: %s: ", __func__, propname); + if (!dn) + return -EINVAL; + val = dtb_node_find_property_value_of_size(dn, propname, sizeof(*outp)); + if (IS_ERR(val)) + { + debug("(not found)\n"); + return PTR_ERR(val); + } + + *outp = fdt64_to_cpu(*val); + debug("%#llx (%lld)\n", (unsigned long long)*outp, + (unsigned long long)*outp); + + return 0; +} + +uint64_t dtb_node_read_u64_default(const struct dtb_node *node, const char *propname, uint64_t def) +{ + RT_ASSERT(dtb_node_valid(node)); + dtb_node_read_u64(node, propname, &def); + + return def; +} + +int dtb_node_n_addr_cells(const struct dtb_node *dn) +{ + const uint32_t *ip; + + do + { + if (dn->parent) + dn = dn->parent; + ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL); + if (ip) + return fdt32_to_cpu(*ip); + } while (dn->parent); + + /* No #address-cells property for the root node */ + return DEV_ROOT_NODE_ADDR_CELLS_DEFAULT; +} + +int dtb_node_n_size_cells(const struct dtb_node *dn) +{ + const uint32_t *ip; + + do + { + if (dn->parent) + dn = dn->parent; + ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL); + if (ip) + return fdt32_to_cpu(*ip); + } while (dn->parent); + + /* No #size-cells property for the root node */ + return DEV_ROOT_NODE_SIZE_CELLS_DEFAULT; +} + +int dtb_node_simple_addr_cells(const struct dtb_node *dn) +{ + const uint32_t *ip; + + ip = dtb_node_get_dtb_node_property_value(dn, "#address-cells", NULL); + if (ip) + return fdt32_to_cpu(*ip); + + /* Return a default of 2 to match fdt_address_cells()*/ + return 2; +} + +int dtb_node_simple_size_cells(const struct dtb_node *dn) +{ + const uint32_t *ip; + + ip = dtb_node_get_dtb_node_property_value(dn, "#size-cells", NULL); + if (ip) + return fdt32_to_cpu(*ip); + + /* Return a default of 2 to match fdt_size_cells()*/ + return 2; +} + +struct dtb_property *dtb_node_get_dtb_node_property(const struct dtb_node *dtb_node, const char *property_name, int *property_size) +{ + struct dtb_property *dtb_property = NULL; + + if (dtb_node != NULL && property_name != NULL) + { + dtb_property = dtb_node->properties; + + while (dtb_property != NULL) + { + if (!strcmp(dtb_property->name, property_name)) + { + if (property_size != NULL) + { + *property_size = dtb_property->size; + } + return dtb_property; + } + dtb_property = dtb_property->next; + } + } + + return dtb_property; +} + +#define for_each_property_of_node(dn, pp) \ + for (pp = dn->properties; pp != NULL; pp = pp->next) + +struct dtb_node *dtb_node_find_node_opts_by_path(const char *path, + const char **opts) +{ + struct dtb_node *np = NULL; + struct dtb_property *pp; + const char *separator = strchr(path, ':'); + + if (opts) + *opts = separator ? separator + 1 : NULL; + + if (strcmp(path, "/") == 0) + return dtb_node_get(get_dtb_node_head()); + + /* The path could begin with an alias */ + if (*path != '/') + { + int len; + const char *p = separator; + + if (!p) + p = strchrnul(path, '/'); + len = p - path; + + /* of_aliases must not be NULL */ + if (!fdt_aliases) + return NULL; + + for_each_property_of_node(fdt_aliases, pp) + { + if (strlen(pp->name) == len && !strncmp(pp->name, path, + len)) + { + np = dtb_node_find_node_by_path(pp->value); + break; + } + } + if (!np) + return NULL; + path = p; + } + + /* Step down the tree matching path components */ + if (!np) + np = dtb_node_get(get_dtb_node_head()); + while (np && *path == '/') + { + struct dtb_node *tmp = np; + + path++; /* Increment past '/' delimiter */ + np = dtb_node_get_dtb_node_by_path(np, path); + dtb_node_put(tmp); + path = strchrnul(path, '/'); + if (separator && separator < path) + break; + } + + return np; +} + +struct dtb_node *dtb_node_find_compatible_node(struct dtb_node *from, const char *compatible) +{ + struct dtb_node *dn; + + for_each_of_allnodes_from(from, dn) + { + if (dtb_node_get_dtb_node_compatible_match(dn, compatible) && + dtb_node_get(dn)) + break; + } + dtb_node_put(from); + + return dn; +} + +void *dtb_node_get_dtb_node_property_value(const struct dtb_node *dtb_node, const char *property_name, int *property_size) +{ + struct dtb_property *dtb_property = dtb_node_get_dtb_node_property(dtb_node, property_name, NULL); + + if (!dtb_property || !dtb_property->value || !dtb_property->size) + { + return NULL; + } + + if (property_size != NULL) + { + *property_size = dtb_property->size; + } + + return dtb_property->value; +} + +const struct dtb_node *dtb_node_find_node_by_prop_value(struct dtb_node *from, + const char *propname, + const void *propval, int proplen) +{ + struct dtb_node *np; + void *value; + for_each_of_allnodes_from(from, np) + { + value = dtb_node_get_dtb_node_property_value(np, propname, &proplen); + if (!memcmp(value, propval, proplen) && dtb_node_get(np)) + break; + } + dtb_node_put(from); + + return np; +} + +struct dtb_node *dtb_node_find_all_nodes(const struct dtb_node *prev) +{ + const struct dtb_node *dn; + + if (!prev) + { + dn = get_dtb_node_head(); + } + else if (prev->child) + { + dn = prev->child; + } + else + { + /* + * Walk back up looking for a sibling, or the end of the + * structure + */ + dn = prev; + while (dn->parent && !dn->sibling) + dn = dn->parent; + dn = dn->sibling; /* Might be null at the end of the tree */ + } + + return (struct dtb_node *)dn; +} + +rt_bool_t dtb_node_device_is_available(const struct dtb_node *device) +{ + const char *status; + int statlen; + + if (!device) + return RT_FALSE; + + status = dtb_node_get_dtb_node_property_value(device, "status", &statlen); + if (status == NULL) + return RT_TRUE; + + if (statlen > 0) + { + if (!strcmp(status, "okay")) + return RT_TRUE; + } + + return RT_FALSE; +} + +struct dtb_node *dtb_node_get_parent(const struct dtb_node *node) +{ + const struct dtb_node *dn; + + if (!node) + return NULL; + + dn = dtb_node_get(node->parent); + + return (struct dtb_node *)dn; +} + +struct dtb_node *dtb_node_find_node_by_phandle(phandle handle) +{ + struct dtb_node *dn; + + if (!handle) + return NULL; + + for_each_of_allnodes(dn) if (dn->handle == handle) break; + (void)dtb_node_get(dn); + + return dn; +} + +int dtb_node_property_match_string(const struct dtb_node *dn, const char *propname, + const char *string) +{ + const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL); + size_t l; + int i; + const char *p, *end; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + + p = prop->value; + end = p + prop->size; + + for (i = 0; p < end; i++, p += l) + { + l = strnlen(p, end - p) + 1; + if (p + l > end) + return -EILSEQ; + debug("comparing %s with %s\n", string, p); + if (strcmp(string, p) == 0) + return i; /* Found it; return index */ + } + return -ENODATA; +} + +/** + * of_property_read_string_helper() - Utility helper for parsing string properties + * @np: device node from which the property value is to be read. + * @propname: name of the property to be searched. + * @out_strs: output array of string pointers. + * @sz: number of array elements to read. + * @skip: Number of strings to skip over at beginning of list. + * + * Don't call this function directly. It is a utility helper for the + * of_property_read_string*() family of functions. + */ +int dtb_node_property_read_string_helper(const struct dtb_node *dn, + const char *propname, const char **out_strs, + size_t sz, int skip) +{ + const struct dtb_property *prop = dtb_node_get_dtb_node_property(dn, propname, NULL); + int l = 0, i = 0; + const char *p, *end; + + if (!prop) + return -EINVAL; + if (!prop->value) + return -ENODATA; + p = prop->value; + end = p + prop->size; + + for (i = 0; p < end && (!out_strs || i < skip + sz); i++, p += l) + { + l = strnlen(p, end - p) + 1; + if (p + l > end) + return -EILSEQ; + if (out_strs && i >= skip) + *out_strs++ = p; + } + i -= skip; + return i <= 0 ? -ENODATA : i; +} + +static int __dtb_node_parse_phandle_with_args(const struct dtb_node *dn, + const char *list_name, + const char *cells_name, + int cell_count, int index, + struct fdt_phandle_args *out_args) +{ + const uint32_t *list, *list_end; + int rc = 0, cur_index = 0; + uint32_t count = 0; + struct dtb_node *node = NULL; + phandle phandle; + int size; + + /* Retrieve the phandle list property */ + list = dtb_node_get_dtb_node_property_value(dn, list_name, &size); + if (!list) + return -ENOENT; + list_end = list + size / sizeof(*list); + + /* Loop over the phandles until all the requested entry is found */ + while (list < list_end) + { + rc = -EINVAL; + count = 0; + + /* + * If phandle is 0, then it is an empty entry with no + * arguments. Skip forward to the next entry. + */ + phandle = fdt32_to_cpu(*(list++)); + if (phandle) + { + /* + * Find the provider node and parse the #*-cells + * property to determine the argument length. + * + * This is not needed if the cell count is hard-coded + * (i.e. cells_name not set, but cell_count is set), + * except when we're going to return the found node + * below. + */ + if (cells_name || cur_index == index) + { + node = dtb_node_find_node_by_phandle(phandle); + if (!node) + { + debug("%s: could not find phandle\n", + dn->path); + goto err; + } + } + + if (cells_name) + { + if (dtb_node_read_u32(node, cells_name, &count)) + { + debug("%s: could not get %s for %s\n", + dn->path, cells_name, + node->path); + goto err; + } + } + else + { + count = cell_count; + } + + /* + * Make sure that the arguments actually fit in the + * remaining property data length + */ + if (list + count > list_end) + { + debug("%s: arguments longer than property\n", + dn->path); + goto err; + } + } + + /* + * All of the error cases above bail out of the loop, so at + * this point, the parsing is successful. If the requested + * index matches, then fill the out_args structure and return, + * or return -ENOENT for an empty entry. + */ + rc = -ENOENT; + if (cur_index == index) + { + if (!phandle) + goto err; + + if (out_args) + { + int i; + if (count > FDT_MAX_PHANDLE_ARGS) + count = FDT_MAX_PHANDLE_ARGS; + out_args->np = node; + out_args->args_count = count; + for (i = 0; i < count; i++) + out_args->args[i] = + fdt32_to_cpu(*(list++)); + } + else + { + dtb_node_put(node); + } + + /* Found it! return success */ + return 0; + } + + dtb_node_put(node); + node = NULL; + list += count; + cur_index++; + } + + /* + * Unlock node before returning result; will be one of: + * -ENOENT : index is for empty phandle + * -EINVAL : parsing error on data + * [1..n] : Number of phandle (count mode; when index = -1) + */ + rc = index < 0 ? cur_index : -ENOENT; +err: + if (node) + dtb_node_put(node); + return rc; +} + +struct dtb_node *dtb_node_parse_phandle(const struct dtb_node *dn, + const char *phandle_name, int index) +{ + struct fdt_phandle_args args; + + if (index < 0) + return NULL; + + if (__dtb_node_parse_phandle_with_args(dn, phandle_name, NULL, 0, index, + &args)) + return NULL; + + return args.np; +} + +int dtb_node_parse_phandle_with_args(const struct dtb_node *dn, + const char *list_name, const char *cells_name, + int index, struct fdt_phandle_args *out_args) +{ + if (index < 0) + return -EINVAL; + + return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0, + index, out_args); +} + +int dtb_node_count_phandle_with_args(const struct dtb_node *dn, + const char *list_name, const char *cells_name) +{ + return __dtb_node_parse_phandle_with_args(dn, list_name, cells_name, 0, + -1, NULL); +} diff --git a/components/drivers/fdt/src/dtb_addr.c b/components/drivers/fdt/src/dtb_addr.c new file mode 100644 index 0000000000..e4a296ea7f --- /dev/null +++ b/components/drivers/fdt/src/dtb_addr.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "libfdt.h" +#include "dtb_node.h" + +/* Max address size we deal with */ +#define FDT_MAX_ADDR_CELLS 4 +#define FDT_CHECK_ADDR_COUNT(na) ((na) > 0 && (na) <= FDT_MAX_ADDR_CELLS) +#define FDT_CHECK_COUNTS(na, ns) (FDT_CHECK_ADDR_COUNT(na) && (ns) > 0) + +static void dtb_node_default_count_cells(const struct dtb_node *dn, + int *addrc, int *sizec) +{ + if (addrc) + *addrc = dtb_node_n_addr_cells(dn); + if (sizec) + *sizec = dtb_node_n_size_cells(dn); +} + +const uint32_t *dtb_node_get_address(const struct dtb_node *dev, int index, + uint64_t *size, unsigned int *flags) +{ + const uint32_t *prop; + int psize; + struct dtb_node *parent; + int onesize, i, na, ns; + + /* Get parent */ + parent = dtb_node_get_parent(dev); + if (parent == NULL) + return NULL; + + dtb_node_default_count_cells(dev, &na, &ns); + if (!FDT_CHECK_ADDR_COUNT(na)) + return NULL; + + /* Get "reg" or "assigned-addresses" property */ + prop = dtb_node_get_dtb_node_property_value(dev, "reg", &psize); + if (prop == NULL) + return NULL; + psize /= 4; + + onesize = na + ns; + for (i = 0; psize >= onesize; psize -= onesize, prop += onesize, i++) + if (i == index) + { + if (size) + *size = dtb_node_read_number(prop + na, ns); + if (flags) + *flags = 0x200; + return prop; + } + return NULL; +}
\ No newline at end of file diff --git a/components/drivers/fdt/src/dtb_base.c b/components/drivers/fdt/src/dtb_base.c new file mode 100644 index 0000000000..d89e5dad9e --- /dev/null +++ b/components/drivers/fdt/src/dtb_base.c @@ -0,0 +1,537 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "dtb_node.h" +#include "libfdt.h" +#include "libfdt_env.h" +#include <ctype.h> + +static const char *_parse_integer_fixup_radix(const char *s, unsigned int *base) +{ + if (*base == 0) + { + if (s[0] == '0') + { + if (tolower(s[1]) == 'x' && isxdigit(s[2])) + *base = 16; + else + *base = 8; + } + else + *base = 10; + } + if (*base == 16 && s[0] == '0' && tolower(s[1]) == 'x') + s += 2; + return s; +} + +unsigned long simple_strtoul(const char *cp, char **endp, + unsigned int base) +{ + unsigned long result = 0; + unsigned long value; + + cp = _parse_integer_fixup_radix(cp, &base); + + while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp) + ? toupper(*cp) : *cp)-'A'+10) < base) + { + result = result*base + value; + cp++; + } + + if (endp) + *endp = (char *)cp; + + return result; +} + +int strict_strtoul(const char *cp, unsigned int base, unsigned long *res) +{ + char *tail; + unsigned long val; + size_t len; + + *res = 0; + len = strlen(cp); + if (len == 0) + return -EINVAL; + + val = simple_strtoul(cp, &tail, base); + if (tail == cp) + return -EINVAL; + + if ((*tail == '\0') || + ((len == (size_t)(tail - cp) + 1) && (*tail == '\n'))) + { + *res = val; + return 0; + } + + return -EINVAL; +} + +long simple_strtol(const char *cp, char **endp, unsigned int base) +{ + if (*cp == '-') + return -simple_strtoul(cp + 1, endp, base); + + return simple_strtoul(cp, endp, base); +} + +rt_bool_t dtb_node_read_bool(const struct dtb_node *node, const char *propname) +{ + const void *prop; + + RT_ASSERT(dtb_node_valid(node)); + debug("%s: %s: ", __func__, propname); + + prop = dtb_node_get_property(node, propname, NULL); + + debug("%s\n", prop ? "true" : "false"); + + return prop ? RT_TRUE : RT_FALSE; +} + +const void *dtb_node_read_prop(const struct dtb_node *node, const char *propname, int *sizep) +{ + const char *val = NULL; + int len; + + RT_ASSERT(dtb_node_valid(node)); + debug("%s: %s: ", __func__, propname); + + struct dtb_property *prop = dtb_node_get_dtb_node_property(node, propname, &len); + + if (prop) + { + val = prop->value; + len = prop->size; + } + + if (!val) + { + debug("<not found>\n"); + if (sizep) + *sizep = -FDT_ERR_NOTFOUND; + return NULL; + } + if (sizep) + *sizep = len; + + return val; +} + +const char *dtb_node_read_string(const struct dtb_node *node, const char *propname) +{ + const char *str; + int len; + + str = dtb_node_read_prop(node, propname, &len); + if (!str) + return NULL; + + if (strnlen(str, len) >= len) + { + debug("<invalid>\n"); + return NULL; + } + debug("%s\n", str); + + return str; +} + +const struct dtb_node *dtb_node_find_subnode(const struct dtb_node *node, const char *subnode_name) +{ + const struct dtb_node *subnode; + + RT_ASSERT(dtb_node_valid(node)); + debug("%s: %s: ", __func__, subnode_name); + + for (node = node->child; node; node = node->sibling) + { + if (!strcmp(subnode_name, node->name)) + break; + } + subnode = node; + + debug("%s\n", dtb_node_valid(subnode) ?\ + dtb_node_get_name(subnode) : "<none>"); + + return subnode; +} + +struct dtb_node *dtb_node_first_subnode(const struct dtb_node *node) +{ + RT_ASSERT(dtb_node_valid(node)); + + return node->child; +} + +struct dtb_node *dtb_node_next_subnode(const struct dtb_node *node) +{ + RT_ASSERT(dtb_node_valid(node)); + + return node->sibling; +} + +const char *dtb_node_get_name(const struct dtb_node *node) +{ + if (!dtb_node_valid(node)) + { + debug("%s node not valid\n", __func__); + return NULL; + } + + return strrchr(node->path, '/') + 1; + +} + +struct dtb_node *dtb_node_get_by_phandle(uint phandle) +{ + if (dtb_node_active()) + return dtb_node_find_node_by_phandle(phandle); + return NULL; +} + +int dtb_node_read_size(const struct dtb_node *node, const char *propname) +{ + struct dtb_property *prop = dtb_node_get_dtb_node_property( node, propname, NULL); + + if (prop) + return prop->size; + + return -EINVAL; +} + +size_t dtb_node_get_addr_index(const struct dtb_node *node, int index) +{ + int na; + size_t size; + + const uint32_t *prop_val; + uint flags; + + prop_val = dtb_node_get_address(node, index, + (uint64_t *)&size, &flags); + if (!prop_val) + return -1; + + na = dtb_node_n_addr_cells(node); + + return dtb_node_read_number(prop_val, na); +} + +size_t dtb_node_get_addr(const struct dtb_node *node) +{ + return dtb_node_get_addr_index(node, 0); +} + +int dtb_node_stringlist_search(const struct dtb_node *node, const char *property, + const char *string) +{ + return dtb_node_property_match_string(node, property, string); +} + +int dtb_node_read_string_index(const struct dtb_node *node, const char *property, int index, + const char **outp) +{ + return dtb_node_property_read_string_index(node, property, index, outp); +} + +int dtb_node_read_string_count(const struct dtb_node *node, const char *property) +{ + return dtb_node_property_count_strings(node, property); +} + +struct dtb_node *dtb_node_path(const char *path) +{ + if (dtb_node_active()) + return dtb_node_find_node_by_path(path); + return NULL; +} + +const char *dtb_node_get_chosen_prop(const char *name) +{ + const struct dtb_node *chosen_node; + + chosen_node = (const struct dtb_node *)dtb_node_path("/chosen"); + + return dtb_node_read_string(chosen_node, name); +} + +struct dtb_node *dtb_node_get_chosen_node(const char *name) +{ + const char *prop; + + prop = dtb_node_get_chosen_prop(name); + if (!prop) + return NULL; + + return dtb_node_path(prop); +} + +const void *dtb_node_get_property(const struct dtb_node *node, const char *propname, int *lenp) +{ + return dtb_node_get_dtb_node_property_value(node, propname, lenp); +} + +rt_bool_t dtb_node_is_available(const struct dtb_node *node) +{ + return dtb_node_device_is_available(node); +} + +size_t dtb_node_get_addr_size(const struct dtb_node *node, const char *property, + size_t *sizep) +{ + int na, ns; + int psize; + const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, property, &psize); + + if (!prop) + return -1; + na = dtb_node_n_addr_cells(node); + ns = dtb_node_n_size_cells(node); + *sizep = dtb_node_read_number(prop + na, ns); + + return dtb_node_read_number(prop, na); +} + +const uint8_t *dtb_node_read_u8_array_ptr(const struct dtb_node *node, const char *propname, + size_t sz) +{ + int psize; + const uint32_t *prop = dtb_node_get_dtb_node_property_value(node, propname, &psize); + + if (!prop || sz != psize) + return NULL; + return (uint8_t *)prop; +} + +int dtb_node_find_all_compatible_node(const struct dtb_node *from, const char *compatible, struct dtb_node **node_table, int max_num, int *node_num) +{ + const struct dtb_node *dn; + int num = 0; + for_each_of_allnodes_from(from, dn) + { + if (dtb_node_get_dtb_node_compatible_match(dn, compatible) && + dtb_node_get(dn)) + { + + num++; + *node_table = (struct dtb_node *)dn; + node_table++; + if (num >= max_num) + { + break; + } + } + } + *node_num = num; + dtb_node_put(from); + + return 0; +} + +int dtb_node_write_prop(const struct dtb_node *node, const char *propname, int len, + const void *value) +{ + struct dtb_property *pp; + struct dtb_property *pp_last = NULL; + struct dtb_property *new; + + if (!dtb_node_active()) + return -ENOSYS; + + if (!node) + return -EINVAL; + + for (pp = node->properties; pp; pp = pp->next) + { + if (strcmp(pp->name, propname) == 0) + { + /* Property exists -> change value */ + pp->value = (void *)value; + pp->size = len; + return 0; + } + pp_last = pp; + } + + if (!pp_last) + return -ENOENT; + + /* Property does not exist -> append new property */ + new = malloc(sizeof(struct dtb_property)); + if (!new) + return -ENOMEM; + + new->name = strdup(propname); + if (!new->name) + { + free(new); + return -ENOMEM; + } + + new->value = (void *)value; + new->size = len; + new->next = NULL; + + pp_last->next = new; + + return 0; +} + +int dtb_node_write_string(const struct dtb_node *node, const char *propname, const char *value) +{ + if (!dtb_node_active()) + return -ENOSYS; + + RT_ASSERT(dtb_node_valid(node)); + + debug("%s: %s = %s", __func__, propname, value); + + return dtb_node_write_prop(node, propname, strlen(value) + 1, value); +} + +int dtb_node_set_enabled(const struct dtb_node *node, rt_bool_t value) +{ + if (!dtb_node_active()) + return -ENOSYS; + + RT_ASSERT(dtb_node_valid(node)); + + if (value) + return dtb_node_write_string(node, "status", "okay"); + else + return dtb_node_write_string(node, "status", "disable"); +} + +/** + * dtb_node_irq_find_parent - Given a device node, find its interrupt parent node + * @child: pointer to device node + * + * Returns a pointer to the interrupt parent node, or NULL if the interrupt + * parent could not be determined. + */ +static struct dtb_node *dtb_node_irq_find_parent(struct dtb_node *child) +{ + struct dtb_node *p; + phandle parent; + + if (!dtb_node_get(child)) + return NULL; + do + { + if (dtb_node_read_u32_array(child, "interrupt-parent", &parent, 1)) + { + p = dtb_node_get_parent(child); + } + else + { + p = dtb_node_get_by_phandle(parent); + } + dtb_node_put(child); + child = p; + } while (p && dtb_node_get_property(p, "#interrupt-cells", NULL) == NULL); + + return p; +} + +int dtb_node_irq_get(struct dtb_node *dev, int index) +{ + int rc = 0; + struct fdt_phandle_args out_irq; + struct dtb_node *p; + uint32_t intsize; + int res, i; + + p = dtb_node_irq_find_parent(dev); + if (p == NULL) + return -EINVAL; + /* Get size of interrupt specifier */ + if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1)) + { + res = -EINVAL; + goto out; + } + + debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize); + + /* Copy intspec into irq structure */ + out_irq.np = p; + out_irq.args_count = intsize; + for (i = 0; i < intsize; i++) + { + res = dtb_node_read_u32_index(dev, "interrupts", + (index * 3 + i), + out_irq.args + i); + if (res) + goto out; + } + rc = out_irq.args[1]; + out: + dtb_node_put(p); + return rc; + +} + +/** + * dtb_node_irq_get_byname - Decode a node's IRQ and return it as a Linux IRQ number + * @dev: pointer to device tree node + * @name: IRQ name + * + * Returns Linux IRQ number on success, or 0 on the IRQ mapping failure, or + * -EPROBE_DEFER if the IRQ domain is not yet created, or error code in case + * of any other failure. + */ +int dtb_node_irq_get_byname(struct dtb_node *dev, const char *name) +{ + int index; + + if (!name) + return -EINVAL; + + index = dtb_node_stringlist_search(dev, "interrupt-names", name); + if (index < 0) + return index; + + return dtb_node_irq_get(dev, index); +} + +/** + * dtb_node_irq_count - Count the number of IRQs a node uses + * @dev: pointer to device tree node + */ +int dtb_node_irq_count(struct dtb_node *device) +{ + struct dtb_node *p; + uint32_t intsize; + int nr = 0, res = 0; + + p = dtb_node_irq_find_parent(device); + if (p == NULL) + return -EINVAL; + + /* Get size of interrupt specifier */ + if (dtb_node_read_u32_array(p, "#interrupt-cells", &intsize, 1)) + { + res = -EINVAL; + goto out; + } + + debug(" path:%s, parent=%pOF, intsize=%d\n", p->path,p, intsize); + + res = dtb_node_read_size(device, "interrupts"); + if (res < 0) + { + goto out; + } + nr = res / (intsize * 4); + out: + dtb_node_put(p); + + return nr; +}
\ No newline at end of file diff --git a/components/drivers/fdt/src/dtb_get.c b/components/drivers/fdt/src/dtb_get.c new file mode 100644 index 0000000000..df1b5f9341 --- /dev/null +++ b/components/drivers/fdt/src/dtb_get.c @@ -0,0 +1,817 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "libfdt.h" +#include "dtb_node.h" + +static struct +{ + const char *ptr; + const char *end; + char *cur; +} paths_buf = {NULL, NULL}; +static void *current_fdt; + +int fdt_exec_status = FDT_RET_GET_OK; + +int dtb_node_get_exec_status() +{ + return fdt_exec_status; +} + +static int _dtb_node_get_dtb_properties_list(struct dtb_property *dtb_property, off_t node_off) +{ + /* caller alrealy checked current_fdt */ + off_t property_off = fdt_first_property_offset(current_fdt, node_off); + struct fdt_property *fdt_property; + + if (property_off < 0) + { + return FDT_RET_GET_EMPTY; + } + + for (;;) + { + fdt_property = (struct fdt_property *)fdt_get_property_by_offset(current_fdt, property_off, &dtb_property->size); + if (fdt_property != NULL) + { + dtb_property->name = fdt_string(current_fdt, fdt32_to_cpu(fdt_property->nameoff)); + dtb_property->value = fdt_property->data; + dtb_property->size = fdt32_to_cpu(fdt_property->len); + } + + property_off = fdt_next_property_offset(current_fdt, property_off); + if (property_off >= 0) + { + dtb_property->next = (struct dtb_property *)malloc(sizeof(struct dtb_property)); + if (dtb_property->next == NULL) + { + return FDT_RET_NO_MEMORY; + } + dtb_property = dtb_property->next; + } + else + { + dtb_property->next = NULL; + break; + } + } + + return FDT_RET_GET_OK; +} + +static int _dtb_node_get_dtb_nodes_list(struct dtb_node *dtb_node_head, struct dtb_node *dtb_node, const char *pathname) +{ + off_t root_off; + off_t node_off; + int pathname_sz; + int node_name_sz; + + /* caller alrealy checked current_fdt */ + if ((root_off = fdt_path_offset(current_fdt, pathname)) >= 0) + { + pathname_sz = strlen(pathname); + node_off = fdt_first_subnode(current_fdt, root_off); + + if (node_off < 0) + { + return FDT_RET_GET_EMPTY; + } + + for (;;) + { + dtb_node->parent = dtb_node_head; + dtb_node->sibling = NULL; + dtb_node->name = fdt_get_name(current_fdt, node_off, &node_name_sz); + + /* parent_path + name + '/' + '\0' */ + if (paths_buf.cur + pathname_sz + node_name_sz + 2 < paths_buf.end) + { + dtb_node->path = (const char *)paths_buf.cur; + strncpy(paths_buf.cur, pathname, pathname_sz); + paths_buf.cur += pathname_sz; + strncpy(paths_buf.cur, (char *)dtb_node->name, node_name_sz); + paths_buf.cur += node_name_sz; + *paths_buf.cur++ = '/'; + *paths_buf.cur++ = '\0'; + } + else + { + dtb_node->path = NULL; + rt_kprintf("\033[31m\rERROR: `FDT_DTB_ALL_NODES_PATH_SIZE' = %d bytes is configured too low.\033[0m\n", FDT_DTB_ALL_NODES_PATH_SIZE); + return FDT_RET_NO_MEMORY; + } + + dtb_node->handle = fdt_get_phandle(current_fdt, node_off); + dtb_node->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property)); + dtb_node->child = (struct dtb_node *)malloc(sizeof(struct dtb_node)); + + if (dtb_node->properties == NULL || dtb_node->child == NULL) + { + return FDT_RET_NO_MEMORY; + } + + fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node->properties, node_off); + if (fdt_exec_status == FDT_RET_GET_EMPTY) + { + free(dtb_node->properties); + dtb_node->properties = NULL; + } + else if (fdt_exec_status != FDT_RET_GET_OK) + { + return fdt_exec_status; + } + + fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node, dtb_node->child, dtb_node->path); + if (fdt_exec_status == FDT_RET_GET_EMPTY) + { + free(dtb_node->child); + dtb_node->child = NULL; + } + else if (fdt_exec_status != FDT_RET_GET_OK) + { + return fdt_exec_status; + } + + node_off = fdt_next_subnode(current_fdt, node_off); + if (node_off >= 0) + { + dtb_node->sibling = (struct dtb_node *)malloc(sizeof(struct dtb_node)); + if (dtb_node->sibling == NULL) + { + return FDT_RET_NO_MEMORY; + } + dtb_node = dtb_node->sibling; + } + else + { + break; + } + } + } + + return FDT_RET_GET_OK; +} + +struct dtb_node *dtb_node_get_dtb_list(void *fdt) +{ + int root_off; + struct dtb_node *dtb_node_head = NULL; + + if (fdt == NULL) + { + fdt_exec_status = FDT_RET_NO_LOADED; + goto fail; + } + + current_fdt = fdt; + + if ((dtb_node_head = (struct dtb_node *)malloc(sizeof(struct dtb_node))) == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto fail; + } + + if (paths_buf.ptr == NULL) + { + paths_buf.ptr = malloc(FDT_DTB_ALL_NODES_PATH_SIZE); + if (paths_buf.ptr == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto fail; + } + paths_buf.cur = (char *)paths_buf.ptr; + paths_buf.end = (char *)(paths_buf.ptr + FDT_DTB_ALL_NODES_PATH_SIZE); + } + + root_off = fdt_path_offset(fdt, "/"); + + if ((dtb_node_head->header = malloc(sizeof(struct dtb_header))) == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto fail; + } + else + { + ((struct dtb_header *)dtb_node_head->header)->root = '/'; + ((struct dtb_header *)dtb_node_head->header)->zero = '\0'; + ((struct dtb_header *)dtb_node_head->header)->memreserve_sz = fdt_num_mem_rsv(fdt); + + if (dtb_node_head->header->memreserve_sz > 0) + { + int i; + int memreserve_sz = dtb_node_head->header->memreserve_sz; + uint32_t off_mem_rsvmap = fdt_off_mem_rsvmap(fdt); + struct fdt_reserve_entry *rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap); + + ((struct dtb_header *)dtb_node_head->header)->memreserve = malloc(sizeof(struct dtb_memreserve) * memreserve_sz); + if (dtb_node_head->header->memreserve == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto fail; + } + for (i = 0; i < memreserve_sz; ++i) + { + ((struct dtb_header *)dtb_node_head->header)->memreserve[i].address = fdt64_to_cpu(rsvmap[i].address); + ((struct dtb_header *)dtb_node_head->header)->memreserve[i].size = fdt64_to_cpu(rsvmap[i].size); + } + } + else + { + ((struct dtb_header *)dtb_node_head->header)->memreserve = NULL; + } + } + + dtb_node_head->path = paths_buf.ptr; + *paths_buf.cur++ = '/'; + *paths_buf.cur++ = '\0'; + dtb_node_head->parent = NULL; + dtb_node_head->sibling = NULL; + + dtb_node_head->handle = fdt_get_phandle(fdt, root_off); + dtb_node_head->properties = (struct dtb_property *)malloc(sizeof(struct dtb_property)); + dtb_node_head->child = (struct dtb_node *)malloc(sizeof(struct dtb_node)); + + if (dtb_node_head->properties == NULL || dtb_node_head->child == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto fail; + } + + if ((fdt_exec_status = _dtb_node_get_dtb_properties_list(dtb_node_head->properties, root_off)) != FDT_RET_GET_OK) + { + goto fail; + } + + if ((fdt_exec_status = _dtb_node_get_dtb_nodes_list(dtb_node_head, dtb_node_head->child, dtb_node_head->path)) != FDT_RET_GET_OK) + { + goto fail; + } + + /* paths_buf.ptr addr save in the dtb_node_head's path */ + paths_buf.ptr = NULL; + paths_buf.cur = NULL; + + return dtb_node_head; + +fail: + if (dtb_node_head != NULL) + { + dtb_node_free_dtb_list(dtb_node_head); + } + + return NULL; +} + +static void _dtb_node_free_dtb_node(struct dtb_node *dtb_node) +{ + struct dtb_node *dtb_node_last; + struct dtb_property *dtb_property; + struct dtb_property *dtb_property_last; + + while (dtb_node != NULL) + { + dtb_property = dtb_node->properties; + while (dtb_property != NULL) + { + dtb_property_last = dtb_property; + dtb_property = dtb_property->next; + free(dtb_property_last); + } + + _dtb_node_free_dtb_node(dtb_node->child); + + dtb_node_last = dtb_node; + dtb_node = dtb_node->sibling; + free(dtb_node_last); + } +} + +void dtb_node_free_dtb_list(struct dtb_node *dtb_node_head) +{ + if (dtb_node_head == NULL) + { + return; + } + + /* only root node can free all path buffer */ + if (dtb_node_head->parent == NULL || (dtb_node_head->path != NULL && !strcmp(dtb_node_head->path, "/"))) + { + if (dtb_node_head->path != NULL) + { + free((void *)dtb_node_head->path); + } + if (dtb_node_head->header != NULL) + { + if (dtb_node_head->header->memreserve != NULL) + { + free((void *)dtb_node_head->header->memreserve); + } + free((void *)dtb_node_head->header); + } + } + + _dtb_node_free_dtb_node(dtb_node_head); +} + +static void _dtb_node_printf_depth(int depth) +{ + int i = depth; + while (i --> 0) + { + rt_kputs("\t"); + } +} + +rt_bool_t _dtb_node_test_string_list(const void *value, int size) +{ + const char *str = value; + const char *str_start, *str_end; + + if (size == 0) + { + return RT_FALSE; + } + + /* string end with '\0' */ + if (str[size - 1] != '\0') + { + return RT_FALSE; + } + + /* get string list end */ + str_end = str + size; + + while (str < str_end) + { + str_start = str; + /* before string list end, not '\0' and a printable characters */ + while (str < str_end && *str && ((unsigned char)*str >= ' ' && (unsigned char)*str <= '~')) + { + ++str; + } + + /* not zero, or not increased */ + if (*str != '\0' || str == str_start) + { + return RT_FALSE; + } + + /* next string */ + ++str; + } + + return RT_TRUE; +} + +static void _dtb_node_printf_dtb_node_info(struct dtb_node *dtb_node) +{ + static int depth = 0; + struct dtb_property *dtb_property; + + while (dtb_node != NULL) + { + rt_kputs("\n"); + _dtb_node_printf_depth(depth); + rt_kputs(dtb_node->name); + rt_kputs(" {\n"); + ++depth; + + dtb_property = dtb_node->properties; + while (dtb_property != NULL) + { + _dtb_node_printf_depth(depth); + + rt_kputs(dtb_property->name); + + if (dtb_property->size > 0) + { + int size = dtb_property->size; + char *value = dtb_property->value; + + rt_kputs(" = "); + if (_dtb_node_test_string_list(value, size) == RT_TRUE) + { + /* print string list */ + char *str = value; + do + { + rt_kprintf("\"%s\"", str); + str += strlen(str) + 1; + rt_kputs(", "); + } while (str < value + size); + rt_kputs("\b\b"); + } + else if ((size % 4) == 0) + { + /* print addr and size cell */ + int i; + fdt32_t *cell = (fdt32_t *)value; + + rt_kputs("<"); + for (i = 0, size /= 4; i < size; ++i) + { + rt_kprintf("0x%x ", fdt32_to_cpu(cell[i])); + } + rt_kputs("\b>"); + } + else + { + /* print bytes array */ + int i; + uint8_t *byte = (uint8_t *)value; + + rt_kputs("["); + for (i = 0; i < size; ++i) + { + rt_kprintf("%02x ", *byte++); + } + rt_kputs("\b]"); + } + } + rt_kputs(";\n"); + dtb_property = dtb_property->next; + } + + _dtb_node_printf_dtb_node_info(dtb_node->child); + dtb_node = dtb_node->sibling; + + --depth; + _dtb_node_printf_depth(depth); + rt_kputs("};\n"); + } +} + +void dtb_node_get_dts_dump(struct dtb_node *dtb_node_head) +{ + if (dtb_node_head != NULL) + { + int i = dtb_node_head->header->memreserve_sz; + + rt_kputs("/dts-v1/;\n"); + while (i --> 0) + { + rt_kprintf("\n/memreserve/\t0x%lx 0x%zx;", dtb_node_head->header->memreserve[i].address, dtb_node_head->header->memreserve[i].size); + } + + _dtb_node_printf_dtb_node_info(dtb_node_head); + } +} + +static void _dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node, void (callback(struct dtb_node *dtb_node))) +{ + while (dtb_node != NULL) + { + callback(dtb_node); + _dtb_node_get_enum_dtb_node(dtb_node->child, callback); + dtb_node = dtb_node->sibling; + } +} + +void dtb_node_get_enum_dtb_node(struct dtb_node *dtb_node_head, void (callback(struct dtb_node *dtb_node))) +{ + if (dtb_node_head == NULL || callback == NULL) + { + return; + } + + _dtb_node_get_enum_dtb_node(dtb_node_head, callback); +} + +struct dtb_node *dtb_node_get_dtb_node_by_name_DFS(struct dtb_node *dtb_node, const char *nodename) +{ + struct dtb_node *dtb_node_child; + + while (dtb_node != NULL) + { + if (!strcmp(nodename, dtb_node->name)) + { + return dtb_node; + } + + dtb_node_child = dtb_node_get_dtb_node_by_name_DFS(dtb_node->child, nodename); + + if (dtb_node_child != NULL) + { + return dtb_node_child; + } + dtb_node = dtb_node->sibling; + } + + return NULL; +} + +struct dtb_node *dtb_node_get_dtb_node_by_name_BFS(struct dtb_node *dtb_node, const char *nodename) +{ + if (dtb_node != NULL) + { + struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node; + + while (dtb_node != NULL) + { + if (!strcmp(nodename, dtb_node->name)) + { + return dtb_node; + } + dtb_node = dtb_node->sibling; + } + + dtb_node = dtb_node_head; + + while (dtb_node != NULL) + { + dtb_node_child = dtb_node_get_dtb_node_by_name_BFS(dtb_node->child, nodename); + + if (dtb_node_child != NULL) + { + return dtb_node_child; + } + dtb_node = dtb_node->sibling; + } + } + + return NULL; +} + +struct dtb_node *dtb_node_get_dtb_node_by_path(struct dtb_node *dtb_node, const char *pathname) +{ + int i = 0; + char *node_name; + char *pathname_clone; + int pathname_sz; + + if (pathname == NULL || dtb_node == NULL) + { + return NULL; + } + + /* skip '/' */ + if (*pathname == '/') + { + ++pathname; + } + + /* root not have sibling, so skip */ + if (dtb_node->parent == NULL || !strcmp(dtb_node->path, "/")) + { + dtb_node = dtb_node->child; + } + + pathname_sz = strlen(pathname) + 1; + pathname_clone = malloc(pathname_sz); + if (pathname_clone == NULL) + { + return NULL; + } + strncpy(pathname_clone, pathname, pathname_sz); + node_name = pathname_clone; + + while (pathname_clone[i]) + { + if (pathname_clone[i] == '/') + { + /* set an end of name that can used to strcmp */ + pathname_clone[i] = '\0'; + + while (dtb_node != NULL) + { + if (!strcmp(dtb_node->name, node_name)) + { + break; + } + dtb_node = dtb_node->sibling; + } + if (dtb_node == NULL) + { + free(pathname_clone); + return NULL; + } + dtb_node = dtb_node->child; + node_name = &pathname_clone[i + 1]; + } + ++i; + } + + /* + * found the end node and it's name: + * (pathname[pathname_sz - 1]) is '\0' + * (&pathname_clone[i] - node_name) is the node_name's length + */ + node_name = &((char *)pathname)[(pathname_sz - 1) - (&pathname_clone[i] - node_name)]; + free(pathname_clone); + + while (dtb_node != NULL) + { + if (!strcmp(dtb_node->name, node_name)) + { + return dtb_node; + } + dtb_node = dtb_node->sibling; + } + + return NULL; +} + +struct dtb_node *dtb_node_get_dtb_node_by_phandle_DFS(struct dtb_node *dtb_node, phandle handle) +{ + struct dtb_node *dtb_node_child; + + while (dtb_node != NULL) + { + if (dtb_node->handle == handle) + { + return dtb_node; + } + + dtb_node_child = dtb_node_get_dtb_node_by_phandle_DFS(dtb_node->child, handle); + + if (dtb_node_child != NULL) + { + return dtb_node_child; + } + dtb_node = dtb_node->sibling; + } + + return NULL; +} + +struct dtb_node *dtb_node_get_dtb_node_by_phandle_BFS(struct dtb_node *dtb_node, phandle handle) +{ + if (dtb_node != NULL) + { + struct dtb_node *dtb_node_child, *dtb_node_head = dtb_node; + + while (dtb_node != NULL) + { + if (dtb_node->handle == handle) + { + return dtb_node; + } + dtb_node = dtb_node->sibling; + } + + dtb_node = dtb_node_head; + + while (dtb_node != NULL) + { + dtb_node_child = dtb_node_get_dtb_node_by_phandle_BFS(dtb_node->child, handle); + + if (dtb_node_child != NULL) + { + return dtb_node_child; + } + dtb_node = dtb_node->sibling; + } + } + + return NULL; +} + +void dtb_node_get_dtb_node_cells(struct dtb_node *dtb_node, int *addr_cells, int *size_cells) +{ + if (dtb_node != NULL && addr_cells != NULL && size_cells != NULL) + { + struct dtb_property *dtb_property; + *addr_cells = -1; + *size_cells = -1; + + /* if couldn't found, check parent */ + while ((dtb_node = dtb_node->parent) != NULL) + { + dtb_property = dtb_node->properties; + while (dtb_property != NULL) + { + if (!strcmp(dtb_property->name, "#address-cells")) + { + *addr_cells = fdt32_to_cpu(*(int *)dtb_property->value); + } + else if (!strcmp(dtb_property->name, "#size-cells")) + { + *size_cells = fdt32_to_cpu(*(int *)dtb_property->value); + } + if (*addr_cells != -1 && *size_cells != -1) + { + return; + } + dtb_property = dtb_property->next; + } + } + + if (*addr_cells == -1) + { + *addr_cells = FDT_ROOT_ADDR_CELLS_DEFAULT; + } + if (*size_cells == -1) + { + *size_cells = FDT_ROOT_SIZE_CELLS_DEFAULT; + } + } +} + +struct dtb_memreserve *dtb_node_get_dtb_memreserve(struct dtb_node *dtb_node, int *memreserve_size) +{ + if (dtb_node != NULL && memreserve_size != NULL) + { + struct dtb_node *dtb_node_root = dtb_node; + + while (dtb_node_root != NULL) + { + if (!strcmp(dtb_node_root->path, "/")) + { + break; + } + dtb_node_root = dtb_node_root->parent; + } + + *memreserve_size = dtb_node_root->header->memreserve_sz; + + return dtb_node_root->header->memreserve; + } + return NULL; +} + +rt_bool_t dtb_node_get_dtb_node_status(const struct dtb_node *dtb_node) +{ + if (dtb_node != NULL) + { + char *status = dtb_node_get_dtb_node_property_value(dtb_node, "status", NULL); + if (status != NULL) + { + return (!strcmp(status, "okay") || !strcmp(status, "ok")) ? RT_TRUE : RT_FALSE; + } + + return RT_TRUE; + } + + return RT_FALSE; +} + +rt_bool_t dtb_node_get_dtb_node_compatible_match(const struct dtb_node *dtb_node, const char *compatibles) +{ + if (dtb_node != NULL) + { + if (compatibles != NULL) + { + char *str_ptr; + int prop_sz; + + for_each_property_string(dtb_node, "compatible", str_ptr, prop_sz) + { + if (!strcmp(compatibles, str_ptr)) + { + return RT_TRUE; + } + } + } + } + + return RT_FALSE; +} + +char *dtb_node_get_dtb_string_list_value(void *value, int size, int index) +{ + int i = 0; + char *str = value; + + if (str != NULL) + { + do + { + if (i++ == index) + { + return str; + } + str += strlen(str) + 1; + } while (str < (char *)value + size); + } + + return NULL; +} + +char *dtb_node_get_dtb_string_list_value_next(void *value, void *end) +{ + char *str = value; + + if (str != NULL) + { + str += strlen(str) + 1; + if (str < (char *)end) + { + return str; + } + } + + return NULL; +} + +uint32_t dtb_node_get_dtb_cell_value(void *value) +{ + return fdt32_to_cpu(*(fdt32_t *)value); +} + +uint8_t dtb_node_get_dtb_byte_value(void *value) +{ + return *(uint8_t *)value; +} diff --git a/components/drivers/fdt/src/dtb_head.c b/components/drivers/fdt/src/dtb_head.c new file mode 100644 index 0000000000..7fe661943d --- /dev/null +++ b/components/drivers/fdt/src/dtb_head.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "dtb_node.h" + +static void *dtb_root = NULL; +static struct dtb_node *dtb_node_list = NULL; +void *get_fdt_blob(void) +{ + return dtb_root; +} + +struct dtb_node *get_dtb_node_head(void) +{ + return dtb_node_list; +} + +rt_bool_t dtb_node_active(void) +{ + return dtb_node_list != NULL; +} + +int device_tree_setup(void *mem_addr) +{ + if(mem_addr) + { + if ((dtb_root = dtb_node_load_from_memory(mem_addr,1)) != NULL) + { + dtb_node_list = dtb_node_get_dtb_list(dtb_root); + if (dtb_node_list != NULL) + { + return -1; + } + } + return 0; + } + + return -1; +}
\ No newline at end of file diff --git a/components/drivers/fdt/src/dtb_load.c b/components/drivers/fdt/src/dtb_load.c new file mode 100644 index 0000000000..a6c6dca6f9 --- /dev/null +++ b/components/drivers/fdt/src/dtb_load.c @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "libfdt.h" +#include <unistd.h> +#include "dtb_node.h" + +extern int fdt_exec_status; + +rt_bool_t dtb_node_check(void *fdt) +{ + return fdt_check_header(fdt) == 0 ? RT_TRUE : RT_FALSE; +} + +void *dtb_node_load_from_fs(char *dtb_filename) +{ + void *fdt = NULL; + size_t dtb_sz; + int fd = -1; + + if (dtb_filename == NULL) + { + fdt_exec_status = FDT_RET_GET_EMPTY; + goto end; + } + + fd = open(dtb_filename, O_RDONLY, 0); + + if (fd == -1) + { + rt_kprintf("File `%s' not found.\n", dtb_filename); + fdt_exec_status = FDT_RET_GET_EMPTY; + goto end; + } + + dtb_sz = lseek(fd, 0, SEEK_END); + + if (dtb_sz > 0) + { + if ((fdt = (struct fdt_header *)malloc(sizeof(uint8_t) * dtb_sz)) == NULL) + { + fdt_exec_status = FDT_RET_NO_MEMORY; + goto end; + } + + lseek(fd, 0, SEEK_SET); + read(fd, fdt, sizeof(uint8_t) * dtb_sz); + + if (dtb_node_check(fdt) == RT_FALSE) + { + free(fdt); + } + } + +end: + if (fd != -1) + { + close(fd); + } + + return fdt; +} + +void *dtb_node_load_from_memory(void *dtb_ptr, rt_bool_t is_clone) +{ + void *fdt = NULL; + + if (dtb_ptr == NULL) + { + fdt_exec_status = FDT_RET_GET_EMPTY; + goto end; + } + + if (dtb_node_check(dtb_ptr) == RT_FALSE) + { + fdt_exec_status = FDT_RET_GET_EMPTY; + fdt = NULL; + goto end; + } + + if (is_clone) + { + size_t dtb_sz = fdt_totalsize(dtb_ptr); + if (dtb_sz > 0) + { + if ((fdt = malloc(dtb_sz)) != NULL) + { + memcpy(fdt, dtb_ptr, dtb_sz); + } + else + { + fdt_exec_status = FDT_RET_NO_MEMORY; + } + } + } + else + { + fdt = dtb_ptr; + } + +end: + return fdt; +} diff --git a/components/drivers/fdt/src/dtb_set.c b/components/drivers/fdt/src/dtb_set.c new file mode 100644 index 0000000000..28fe2b5fde --- /dev/null +++ b/components/drivers/fdt/src/dtb_set.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + */ +#include "libfdt.h" +#include "dtb_node.h" + +static off_t dtb_node_find_and_add_subnode(void *fdt, char* name) +{ + off_t chosen_offset = 0; + + chosen_offset = fdt_subnode_offset(fdt, 0, name); + + if (chosen_offset == -FDT_ERR_NOTFOUND) + { + chosen_offset = fdt_add_subnode(fdt, 0, name); + } + + return chosen_offset; +} + +size_t dtb_node_set_linux_cmdline(void *fdt, char *cmdline) +{ + off_t chosen_offset; + size_t cmdline_size; + + if (cmdline == NULL || fdt == NULL) + { + goto end; + } + + chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen"); + cmdline_size = strlen(cmdline); + + /* install bootargs */ + if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS) + { + if (fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size) < 0) + { + fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE); + fdt_setprop(fdt, chosen_offset, "bootargs", cmdline, cmdline_size); + } + } + +end: + return fdt_totalsize(fdt); +} + +size_t dtb_node_set_linux_initrd(void *fdt, uint64_t initrd_addr, size_t initrd_size) +{ + uint64_t addr, size_ptr; + off_t chosen_offset; + int i; + + if (fdt == NULL) + { + goto end; + } + + chosen_offset = dtb_node_find_and_add_subnode(fdt, "chosen"); + + /* update the entry */ + for (i = fdt_num_mem_rsv(fdt) - 1; i >= 0; --i) + { + fdt_get_mem_rsv(fdt, i, &addr, &size_ptr); + if (addr == initrd_addr) + { + fdt_del_mem_rsv(fdt, i); + break; + } + } + + /* add the memory */ + if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0) + { + /* move the memory */ + fdt_open_into(fdt, fdt, fdt_totalsize(fdt) + FDT_DTB_PAD_SIZE); + if (fdt_add_mem_rsv(fdt, initrd_addr, initrd_size) < 0) + { + goto end; + } + } + + /* install initrd */ + if (chosen_offset >= 0 || chosen_offset == -FDT_ERR_EXISTS) + { + chosen_offset = fdt_path_offset(fdt, "/chosen"); + + if (IN_64BITS_MODE) + { + fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-start", initrd_addr); + fdt_setprop_u64(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size); + } + else + { + fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-start", initrd_addr); + fdt_setprop_u32(fdt, chosen_offset, "linux,initrd-end", initrd_addr + initrd_size); + } + } + +end: + return fdt_totalsize(fdt); +} + +size_t dtb_node_set_dtb_property(void *fdt, char *pathname, char *property_name, uint32_t *cells, size_t cells_size) +{ + int node_off; + + if (fdt == NULL) + { + goto end; + } + + node_off = fdt_path_offset(fdt, pathname); + + if (node_off >= 0 && cells_size != 0) + { + fdt_setprop(fdt, node_off, property_name, cells, cells_size); + } + +end: + return fdt_totalsize(fdt); +} + +size_t dtb_node_add_dtb_memreserve(void *fdt, uint64_t address, uint64_t size) +{ + if (fdt == NULL) + { + goto end; + } + + fdt_add_mem_rsv(fdt, address, size); + +end: + return fdt_totalsize(fdt); +} + +size_t dtb_node_del_dtb_memreserve(void *fdt, uint64_t address) +{ + int i; + int num_mem_rsvmap; + uint32_t off_mem_rsvmap; + struct fdt_reserve_entry *rsvmap; + + if (fdt == NULL) + { + goto end; + } + + num_mem_rsvmap = fdt_num_mem_rsv(fdt); + off_mem_rsvmap = fdt_off_mem_rsvmap(fdt); + rsvmap = (struct fdt_reserve_entry *)((char *)fdt + off_mem_rsvmap); + + for (i = 0; i < num_mem_rsvmap; ++i) + { + if (address == fdt64_to_cpu(rsvmap[i].address)) + { + fdt_del_mem_rsv(fdt, i); + break; + } + } + +end: + return fdt_totalsize(fdt); +} diff --git a/components/drivers/include/drivers/cputime.h b/components/drivers/include/drivers/cputime.h index ec98f3152e..bcc339ca35 100644 --- a/components/drivers/include/drivers/cputime.h +++ b/components/drivers/include/drivers/cputime.h @@ -22,8 +22,8 @@ struct rt_clock_cputime_ops float clock_cpu_getres(void); uint64_t clock_cpu_gettime(void); -uint32_t clock_cpu_microsecond(uint32_t cpu_tick); -uint32_t clock_cpu_millisecond(uint32_t cpu_tick); +uint64_t clock_cpu_microsecond(uint64_t cpu_tick); +uint64_t clock_cpu_millisecond(uint64_t cpu_tick); int clock_cpu_setops(const struct rt_clock_cputime_ops *ops); diff --git a/components/drivers/include/drivers/gpt.h b/components/drivers/include/drivers/gpt.h new file mode 100644 index 0000000000..c1bdcb732a --- /dev/null +++ b/components/drivers/include/drivers/gpt.h @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + */ +#ifndef __GPT_H +#define __GPT_H + +#include <rtthread.h> +#include <stdint.h> + +typedef struct +{ + uint8_t b[16]; /* GUID 16 bytes*/ +} guid_t; + +#define MSDOS_MBR_SIGNATURE 0xaa55 +#define EFI_PMBR_OSTYPE_EFI 0xEF +#define EFI_PMBR_OSTYPE_EFI_GPT 0xEE + +#define GPT_MBR_PROTECTIVE 1 +#define GPT_MBR_HYBRID 2 + +#define GPT_HEADER_SIGNATURE 0x5452415020494645ULL +#define GPT_HEADER_REVISION_V1 0x00010000 +#define GPT_PRIMARY_PARTITION_TABLE_LBA 1 + +typedef guid_t gpt_guid_t __attribute__ ((aligned (4))); +#define EFI_GUID(a, b, c, d...) (gpt_guid_t){ { \ + (a) & 0xff, ((a) >> 8) & 0xff, ((a) >> 16) & 0xff, ((a) >> 24) & 0xff, \ + (b) & 0xff, ((b) >> 8) & 0xff, \ + (c) & 0xff, ((c) >> 8) & 0xff, d } } + +#define NULL_GUID \ + EFI_GUID(0x00000000, 0x0000, 0x0000,\ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00) +#define PARTITION_SYSTEM_GUID \ + EFI_GUID( 0xC12A7328, 0xF81F, 0x11d2, \ + 0xBA, 0x4B, 0x00, 0xA0, 0xC9, 0x3E, 0xC9, 0x3B) +#define LEGACY_MBR_PARTITION_GUID \ + EFI_GUID( 0x024DEE41, 0x33E7, 0x11d3, \ + 0x9D, 0x69, 0x00, 0x08, 0xC7, 0x81, 0xF3, 0x9F) +#define PARTITION_MSFT_RESERVED_GUID \ + EFI_GUID( 0xE3C9E316, 0x0B5C, 0x4DB8, \ + 0x81, 0x7D, 0xF9, 0x2D, 0xF0, 0x02, 0x15, 0xAE) +#define PARTITION_BASIC_DATA_GUID \ + EFI_GUID( 0xEBD0A0A2, 0xB9E5, 0x4433, \ + 0x87, 0xC0, 0x68, 0xB6, 0xB7, 0x26, 0x99, 0xC7) +#define PARTITION_LINUX_RAID_GUID \ + EFI_GUID( 0xa19d880f, 0x05fc, 0x4d3b, \ + 0xa0, 0x06, 0x74, 0x3f, 0x0f, 0x84, 0x91, 0x1e) +#define PARTITION_LINUX_SWAP_GUID \ + EFI_GUID( 0x0657fd6d, 0xa4ab, 0x43c4, \ + 0x84, 0xe5, 0x09, 0x33, 0xc8, 0x4b, 0x4f, 0x4f) +#define PARTITION_LINUX_LVM_GUID \ + EFI_GUID( 0xe6d6d379, 0xf507, 0x44c2, \ + 0xa2, 0x3c, 0x23, 0x8f, 0x2a, 0x3d, 0xf9, 0x28) +#pragma pack(push, 1) +typedef struct _gpt_header +{ + uint64_t signature; + uint32_t revision; + uint32_t header_size; + uint32_t header_crc32; + uint32_t reserved1; + uint64_t start_lba; /*GPT head start sector*/ + uint64_t alternate_lba; /*GPT head alternate sector*/ + uint64_t first_usable_lba; + uint64_t last_usable_lba; + gpt_guid_t disk_guid; + uint64_t partition_entry_lba; + uint32_t num_partition_entries; + uint32_t sizeof_partition_entry; + uint32_t partition_entry_array_crc32; + + /* The rest of the logical block is reserved by UEFI and must be zero. + * EFI standard handles this by: + * + * uint8_t reserved2[ BlockSize - 92 ]; + */ +} gpt_header; + +typedef struct _gpt_entry_attributes +{ + uint64_t required_to_function:1; + uint64_t reserved:47; + uint64_t type_guid_specific:16; +} gpt_entry_attributes; + +typedef struct _gpt_entry +{ + gpt_guid_t partition_type_guid; + gpt_guid_t unique_partition_guid; + uint64_t starting_lba; + uint64_t ending_lba; + gpt_entry_attributes attributes; + uint16_t partition_name[72/sizeof(uint16_t)]; +} gpt_entry; + +typedef struct _gpt_mbr_record +{ + uint8_t boot_indicator; /* unused by EFI, set to 0x80 for bootable */ + uint8_t start_head; /* unused by EFI, pt start in CHS */ + uint8_t start_sector; /* unused by EFI, pt start in CHS */ + uint8_t start_track; + uint8_t os_type; /* EFI and legacy non-EFI OS types */ + uint8_t end_head; /* unused by EFI, pt end in CHS */ + uint8_t end_sector; /* unused by EFI, pt end in CHS */ + uint8_t end_track; /* unused by EFI, pt end in CHS */ + uint32_t starting_lba; /* used by EFI - start addr of the on disk pt */ + uint32_t size_in_lba; /* used by EFI - size of pt in LBA */ +} gpt_mbr_record; + + +typedef struct _legacy_mbr +{ + uint8_t boot_code[440]; + uint32_t unique_mbr_signature; + uint16_t unknown; + gpt_mbr_record partition_record[4]; + uint16_t signature; +} legacy_mbr; +#pragma pack(pop) + +int check_gpt(struct rt_mmcsd_card *card); +int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex); +void gpt_free(void); +#endif /*__GPT_H*/ diff --git a/components/drivers/include/drivers/lcd.h b/components/drivers/include/drivers/lcd.h new file mode 100644 index 0000000000..74d291feac --- /dev/null +++ b/components/drivers/include/drivers/lcd.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-03-05 bernard first version. + */ + +#ifndef RT_LCD_H__ +#define RT_LCD_H__ + +/* ioctls + 0x46 is 'F' */ + +#define FBIOGET_VSCREENINFO 0x4600 +#define FBIOPUT_VSCREENINFO 0x4601 +#define FBIOGET_FSCREENINFO 0x4602 +#define FBIOGETCMAP 0x4604 +#define FBIOPUTCMAP 0x4605 +#define FBIOPAN_DISPLAY 0x4606 +#define FBIO_CURSOR 0x4608 +/* #define FBIOGET_MONITORSPEC 0x460C */ +/* #define FBIOPUT_MONITORSPEC 0x460D */ +/* #define FBIOSWITCH_MONIBIT 0x460E */ + +#define FBIOGET_CON2FBMAP 0x460F +#define FBIOPUT_CON2FBMAP 0x4610 +#define FBIOBLANK 0x4611 /* arg: 0 or vesa level + 1 */ +#define FBIOGET_VBLANK 0x4612 +#define FBIO_ALLOC 0x4613 +#define FBIO_FREE 0x4614 +#define FBIOGET_GLYPH 0x4615 +#define FBIOGET_HWCINFO 0x4616 +#define FBIOPUT_MODEINFO 0x4617 +#define FBIOGET_DISPINFO 0x4618 +#define FBIO_WAITFORVSYNC 0x4620 + +struct fb_bitfield +{ + uint32_t offset; /* beginning of bitfield */ + uint32_t length; /* length of bitfield */ + uint32_t msb_right; /* != 0 : Most significant bit is */ + /* right */ +}; + +struct fb_var_screeninfo +{ + uint32_t xres; /* visible resolution */ + uint32_t yres; + uint32_t xres_virtual; /* virtual resolution */ + uint32_t yres_virtual; + uint32_t xoffset; /* offset from virtual to visible */ + uint32_t yoffset; /* resolution */ + + uint32_t bits_per_pixel; /* guess what */ + uint32_t grayscale; /* 0 = color, 1 = grayscale, */ + /* >1 = FOURCC */ + struct fb_bitfield red; /* bitfield in fb mem if true color, */ + struct fb_bitfield green; /* else only length is significant */ + struct fb_bitfield blue; + struct fb_bitfield transp; /* transparency */ + + uint32_t nonstd; /* != 0 Non standard pixel format */ + + uint32_t activate; /* see FB_ACTIVATE_* */ + + uint32_t height; /* height of picture in mm */ + uint32_t width; /* width of picture in mm */ + + uint32_t accel_flags; /* (OBSOLETE) see fb_info.flags */ + + /* Timing: All values in pixclocks, except pixclock (of course) */ + uint32_t pixclock; /* pixel clock in ps (pico seconds) */ + uint32_t left_margin; /* time from sync to picture */ + uint32_t right_margin; /* time from picture to sync */ + uint32_t upper_margin; /* time from sync to picture */ + uint32_t lower_margin; + uint32_t hsync_len; /* length of horizontal sync */ + uint32_t vsync_len; /* length of vertical sync */ + uint32_t sync; /* see FB_SYNC_* */ + uint32_t vmode; /* see FB_VMODE_* */ + uint32_t rotate; /* angle we rotate counter clockwise */ + uint32_t colorspace; /* colorspace for FOURCC-based modes */ + uint32_t reserved[4]; /* Reserved for future compatibility */ +}; + +struct fb_fix_screeninfo +{ + char id[16]; /* identification string eg "TT Builtin" */ + unsigned long smem_start; /* Start of frame buffer mem */ + /* (physical address) */ + uint32_t smem_len; /* Length of frame buffer mem */ + uint32_t type; /* see FB_TYPE_* */ + uint32_t type_aux; /* Interleave for interleaved Planes */ + uint32_t visual; /* see FB_VISUAL_* */ + uint16_t xpanstep; /* zero if no hardware panning */ + uint16_t ypanstep; /* zero if no hardware panning */ + uint16_t ywrapstep; /* zero if no hardware ywrap */ + uint32_t line_length; /* length of a line in bytes */ + unsigned long mmio_start; /* Start of Memory Mapped I/O */ + /* (physical address) */ + uint32_t mmio_len; /* Length of Memory Mapped I/O */ + uint32_t accel; /* Indicate to driver which */ + /* specific chip/card we have */ + uint16_t capabilities; /* see FB_CAP_* */ + uint16_t reserved[2]; /* Reserved for future compatibility */ +}; + +#endif diff --git a/components/drivers/include/drivers/mmcsd_card.h b/components/drivers/include/drivers/mmcsd_card.h index 8a49978a17..f877e60069 100644 --- a/components/drivers/include/drivers/mmcsd_card.h +++ b/components/drivers/include/drivers/mmcsd_card.h @@ -141,6 +141,7 @@ struct rt_mmcsd_card { rt_uint32_t max_data_rate; /* max data transfer rate */ rt_uint32_t card_capacity; /* card capacity, unit:KB */ rt_uint32_t card_blksize; /* card block size */ + rt_uint32_t card_sec_cnt; /* card sector count*/ rt_uint32_t erase_size; /* erase size in sectors */ rt_uint16_t card_type; #define CARD_TYPE_MMC 0 /* MMC card */ @@ -152,7 +153,8 @@ struct rt_mmcsd_card { #define CARD_FLAG_HIGHSPEED (1 << 0) /* SDIO bus speed 50MHz */ #define CARD_FLAG_SDHC (1 << 1) /* SDHC card */ #define CARD_FLAG_SDXC (1 << 2) /* SDXC card */ - +#define CARD_FLAG_HIGHSPEED_DDR (1 << 3) /*HIGH SPEED DDR*/ +#define CARD_FLAG_HS200 (1 << 4) /* BUS SPEED 200mHz*/ struct rt_sd_scr scr; struct rt_mmcsd_csd csd; rt_uint32_t hs_max_data_rate; /* max data transfer rate in high speed mode */ diff --git a/components/drivers/include/drivers/mmcsd_cmd.h b/components/drivers/include/drivers/mmcsd_cmd.h index 2993fe5406..2bd7b93ee1 100644 --- a/components/drivers/include/drivers/mmcsd_cmd.h +++ b/components/drivers/include/drivers/mmcsd_cmd.h @@ -37,7 +37,8 @@ extern "C" { #define SET_BLOCKLEN 16 /* ac [31:0] block len R1 */ #define READ_SINGLE_BLOCK 17 /* adtc [31:0] data addr R1 */ #define READ_MULTIPLE_BLOCK 18 /* adtc [31:0] data addr R1 */ - +#define SEND_TUNING_BLOCK 19 /* adtc R1 */ +#define SEND_TUNING_BLOCK_HS200 21 /* adtc R1*/ /* class 3 */ #define WRITE_DAT_UNTIL_STOP 20 /* adtc [31:0] data addr R1 */ diff --git a/components/drivers/include/drivers/mmcsd_core.h b/components/drivers/include/drivers/mmcsd_core.h index 51263d35f2..04a6ffde2b 100644 --- a/components/drivers/include/drivers/mmcsd_core.h +++ b/components/drivers/include/drivers/mmcsd_core.h @@ -26,7 +26,8 @@ extern "C" { #define mmcsd_dbg(fmt, ...) #endif -struct rt_mmcsd_data { +struct rt_mmcsd_data +{ rt_uint32_t blksize; rt_uint32_t blks; rt_uint32_t *buf; @@ -43,9 +44,15 @@ struct rt_mmcsd_data { rt_uint32_t timeout_ns; rt_uint32_t timeout_clks; + + void *sg; /* scatter list */ + rt_uint16_t sg_len; /* size of scatter list */ + rt_int16_t sg_count; /* mapped sg entries */ + rt_ubase_t host_cookie; /* host driver private data */ }; -struct rt_mmcsd_cmd { +struct rt_mmcsd_cmd +{ rt_uint32_t cmd_code; rt_uint32_t arg; rt_uint32_t resp[4]; @@ -94,15 +101,20 @@ struct rt_mmcsd_cmd { rt_int32_t retries; /* max number of retries */ rt_int32_t err; + unsigned int busy_timeout; /* busy detect timeout in ms */ struct rt_mmcsd_data *data; struct rt_mmcsd_req *mrq; /* associated request */ }; -struct rt_mmcsd_req { +struct rt_mmcsd_req +{ struct rt_mmcsd_data *data; struct rt_mmcsd_cmd *cmd; struct rt_mmcsd_cmd *stop; + struct rt_mmcsd_cmd *sbc; /* SET_BLOCK_COUNT for multiblock */ + /* Allow other commands during this ongoing data transfer or busy wait */ + int cap_cmd_during_tfr; }; /*the following is response bit*/ @@ -208,6 +220,7 @@ rt_inline rt_uint32_t __rt_fls(rt_uint32_t val) #define MMCSD_HOST_PLUGED 0 #define MMCSD_HOST_UNPLUGED 1 +rt_int32_t mmcsd_excute_tuning(struct rt_mmcsd_card *card); int mmcsd_wait_cd_changed(rt_int32_t timeout); void mmcsd_host_lock(struct rt_mmcsd_host *host); void mmcsd_host_unlock(struct rt_mmcsd_host *host); @@ -226,14 +239,18 @@ void mmcsd_set_chip_select(struct rt_mmcsd_host *host, rt_int32_t mode); void mmcsd_set_clock(struct rt_mmcsd_host *host, rt_uint32_t clk); void mmcsd_set_bus_mode(struct rt_mmcsd_host *host, rt_uint32_t mode); void mmcsd_set_bus_width(struct rt_mmcsd_host *host, rt_uint32_t width); +void mmcsd_set_timing(struct rt_mmcsd_host *host, rt_uint32_t timing); void mmcsd_set_data_timeout(struct rt_mmcsd_data *data, const struct rt_mmcsd_card *card); rt_uint32_t mmcsd_select_voltage(struct rt_mmcsd_host *host, rt_uint32_t ocr); void mmcsd_change(struct rt_mmcsd_host *host); void mmcsd_detect(void *param); +void mmcsd_host_init(struct rt_mmcsd_host *host); struct rt_mmcsd_host *mmcsd_alloc_host(void); void mmcsd_free_host(struct rt_mmcsd_host *host); int rt_mmcsd_core_init(void); +int rt_mmcsd_blk_init(void); +rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count); rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card); void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card); diff --git a/components/drivers/include/drivers/mmcsd_host.h b/components/drivers/include/drivers/mmcsd_host.h index 44f7b1c916..774f515c8a 100644 --- a/components/drivers/include/drivers/mmcsd_host.h +++ b/components/drivers/include/drivers/mmcsd_host.h @@ -17,7 +17,8 @@ extern "C" { #endif -struct rt_mmcsd_io_cfg { +struct rt_mmcsd_io_cfg +{ rt_uint32_t clock; /* clock rate */ rt_uint16_t vdd; @@ -45,21 +46,50 @@ struct rt_mmcsd_io_cfg { #define MMCSD_BUS_WIDTH_1 0 #define MMCSD_BUS_WIDTH_4 2 #define MMCSD_BUS_WIDTH_8 3 -#define MMCSD_DDR_BUS_WIDTH_4 4 -#define MMCSD_DDR_BUS_WIDTH_8 5 + + unsigned char timing; /* timing specification used */ + +#define MMCSD_TIMING_LEGACY 0 +#define MMCSD_TIMING_MMC_HS 1 +#define MMCSD_TIMING_SD_HS 2 +#define MMCSD_TIMING_UHS_SDR12 3 +#define MMCSD_TIMING_UHS_SDR25 4 +#define MMCSD_TIMING_UHS_SDR50 5 +#define MMCSD_TIMING_UHS_SDR104 6 +#define MMCSD_TIMING_UHS_DDR50 7 +#define MMCSD_TIMING_MMC_DDR52 8 +#define MMCSD_TIMING_MMC_HS200 9 +#define MMCSD_TIMING_MMC_HS400 10 + + unsigned char drv_type; /* driver type (A, B, C, D) */ + +#define MMCSD_SET_DRIVER_TYPE_B 0 +#define MMCSD_SET_DRIVER_TYPE_A 1 +#define MMCSD_SET_DRIVER_TYPE_C 2 +#define MMCSD_SET_DRIVER_TYPE_D 3 + + unsigned char signal_voltage; + +#define MMCSD_SIGNAL_VOLTAGE_330 0 +#define MMCSD_SIGNAL_VOLTAGE_180 1 +#define MMCSD_SIGNAL_VOLTAGE_120 2 }; struct rt_mmcsd_host; struct rt_mmcsd_req; -struct rt_mmcsd_host_ops { +struct rt_mmcsd_host_ops +{ void (*request)(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req); void (*set_iocfg)(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg); rt_int32_t (*get_card_status)(struct rt_mmcsd_host *host); void (*enable_sdio_irq)(struct rt_mmcsd_host *host, rt_int32_t en); + rt_int32_t (*execute_tuning)(struct rt_mmcsd_host *host, rt_int32_t opcode); }; -struct rt_mmcsd_host { +struct rt_mmcsd_host +{ + char name[RT_NAME_MAX]; struct rt_mmcsd_card *card; const struct rt_mmcsd_host_ops *ops; rt_uint32_t freq_min; @@ -90,10 +120,15 @@ struct rt_mmcsd_host { #define MMCSD_HOST_IS_SPI (1 << 3) #define controller_is_spi(host) (host->flags & MMCSD_HOST_IS_SPI) #define MMCSD_SUP_SDIO_IRQ (1 << 4) /* support signal pending SDIO IRQs */ -#define MMCSD_SUP_HIGHSPEED (1 << 5) /* support high speed */ -#define MMCSD_SUP_HIGHSPEED_DDR (1 << 6) /* support high speed(DDR) */ -#define MMCSD_SUP_HIGHSPEED_HS200 (1 << 7) /* support high speed HS200 */ -#define MMCSD_SUP_HIGHSPEED_HS400 (1 << 8) /* support high speed HS400 */ +#define MMCSD_SUP_HIGHSPEED (1 << 5) /* support high speed SDR*/ +#define MMCSD_SUP_DDR_3V3 (1 << 6) +#define MMCSD_SUP_DDR_1V8 (1 << 7) +#define MMCSD_SUP_DDR_1V2 (1 << 8) +#define MMCSD_SUP_HIGHSPEED_DDR (MMCSD_SUP_DDR_3V3 | MMCSD_SUP_DDR_1V8 | MMCSD_SUP_DDR_1V2)/* HIGH SPEED DDR*/ +#define MMCSD_SUP_HS200_1V8 (1 << 9) +#define MMCSD_SUP_HS200_1V2 (1 << 10) +#define MMCSD_SUP_HS200 (MMCSD_SUP_HS200_1V2 | MMCSD_SUP_HS200_1V8) /* hs200 sdr */ +#define MMCSD_SUP_NONREMOVABLE (1 << 11) rt_uint32_t max_seg_size; /* maximum size of one dma segment */ rt_uint32_t max_dma_segs; /* maximum number of dma segments in one request */ diff --git a/components/drivers/include/drivers/rt_dev_bus.h b/components/drivers/include/drivers/rt_dev_bus.h new file mode 100644 index 0000000000..9fe8af893c --- /dev/null +++ b/components/drivers/include/drivers/rt_dev_bus.h @@ -0,0 +1,18 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-13 flybreak the first version + */ + +#ifndef __RT_DEV_BUS_H__ +#define __RT_DEV_BUS_H__ +#include <rtthread.h> + +rt_device_t rt_device_bus_create(char *name, int attach_size); +rt_err_t rt_device_bus_destroy(rt_device_t dev); + +#endif /* __RT_BUS_H__ */ diff --git a/components/drivers/include/drivers/serial.h b/components/drivers/include/drivers/serial.h index 3b0d4c80da..1d0b9afe5c 100644 --- a/components/drivers/include/drivers/serial.h +++ b/components/drivers/include/drivers/serial.h @@ -150,6 +150,8 @@ struct rt_serial_device void *serial_rx; void *serial_tx; + + struct rt_device_notify rx_notify; }; typedef struct rt_serial_device rt_serial_t; diff --git a/components/drivers/include/drivers/serial_v2.h b/components/drivers/include/drivers/serial_v2.h index a449372e8a..b1020aaf99 100644 --- a/components/drivers/include/drivers/serial_v2.h +++ b/components/drivers/include/drivers/serial_v2.h @@ -158,6 +158,8 @@ struct rt_serial_device void *serial_rx; void *serial_tx; + + struct rt_device_notify rx_notify; }; /** diff --git a/components/drivers/include/ipc/pipe.h b/components/drivers/include/ipc/pipe.h index 08b9f266c8..5f64506011 100644 --- a/components/drivers/include/ipc/pipe.h +++ b/components/drivers/include/ipc/pipe.h @@ -20,16 +20,18 @@ struct rt_pipe_device { struct rt_device parent; rt_bool_t is_named; +#ifdef RT_USING_POSIX_DEVIO + int pipeno; /* for unamed pipe */ +#endif /* ring buffer in pipe device */ struct rt_ringbuffer *fifo; rt_uint16_t bufsz; - rt_uint8_t readers; - rt_uint8_t writers; - rt_wqueue_t reader_queue; rt_wqueue_t writer_queue; + int writer; + int reader; struct rt_mutex lock; }; diff --git a/components/drivers/include/ipc/waitqueue.h b/components/drivers/include/ipc/waitqueue.h index fbf5f93142..0727810696 100644 --- a/components/drivers/include/ipc/waitqueue.h +++ b/components/drivers/include/ipc/waitqueue.h @@ -43,6 +43,8 @@ rt_inline void rt_wqueue_init(rt_wqueue_t *queue) void rt_wqueue_add(rt_wqueue_t *queue, struct rt_wqueue_node *node); void rt_wqueue_remove(struct rt_wqueue_node *node); int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int timeout); +int rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int timeout); +int rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int timeout); void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key); #define DEFINE_WAIT_FUNC(name, function) \ diff --git a/components/drivers/include/rtdevice.h b/components/drivers/include/rtdevice.h index 18c3ef9cd4..c7a61c1994 100644 --- a/components/drivers/include/rtdevice.h +++ b/components/drivers/include/rtdevice.h @@ -157,6 +157,10 @@ extern "C" { #include "drivers/touch.h" #endif +#ifdef RT_USING_DEV_BUS +#include "drivers/rt_dev_bus.h" +#endif + #ifdef __cplusplus } #endif diff --git a/components/drivers/ipc/completion.c b/components/drivers/ipc/completion.c index 84dfb740b1..3f7644216b 100644 --- a/components/drivers/ipc/completion.c +++ b/components/drivers/ipc/completion.c @@ -80,11 +80,14 @@ rt_err_t rt_completion_wait(struct rt_completion *completion, thread->error = RT_EOK; /* suspend thread */ - rt_thread_suspend(thread); + rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); /* add to suspended list */ rt_list_insert_before(&(completion->suspended_list), &(thread->tlist)); + /* current context checking */ + RT_DEBUG_NOT_IN_INTERRUPT; + /* start timer */ if (timeout > 0) { diff --git a/components/drivers/ipc/dataqueue.c b/components/drivers/ipc/dataqueue.c index 4b1a14add9..6b53962384 100644 --- a/components/drivers/ipc/dataqueue.c +++ b/components/drivers/ipc/dataqueue.c @@ -119,7 +119,7 @@ rt_err_t rt_data_queue_push(struct rt_data_queue *queue, thread->error = RT_EOK; /* suspend thread on the push list */ - rt_thread_suspend(thread); + rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist)); /* start timer */ if (timeout > 0) @@ -237,7 +237,7 @@ rt_err_t rt_data_queue_pop(struct rt_data_queue *queue, thread->error = RT_EOK; /* suspend thread on the pop list */ - rt_thread_suspend(thread); + rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist)); /* start timer */ if (timeout > 0) diff --git a/components/drivers/ipc/pipe.c b/components/drivers/ipc/pipe.c index 5483ffa549..7567a73482 100644 --- a/components/drivers/ipc/pipe.c +++ b/components/drivers/ipc/pipe.c @@ -19,6 +19,28 @@ #include <poll.h> #include <sys/ioctl.h> #include <dfs_file.h> +#include <resource_id.h> + +/* check RT_UNAMED_PIPE_NUMBER */ + +#ifndef RT_UNAMED_PIPE_NUMBER +#define RT_UNAMED_PIPE_NUMBER 64 +#endif + +#define BITS(x) _BITS(x) +#define _BITS(x) (sizeof(#x) - 1) + +struct check_rt_unamed_pipe_number +{ + /* -4 for "pipe" prefix */ + /* -1 for '\0' postfix */ + char _check[RT_NAME_MAX - 4 - 1 - BITS(RT_UNAMED_PIPE_NUMBER)]; +}; + +/* check end */ + +static void *resoure_id[RT_UNAMED_PIPE_NUMBER]; +static resource_id_t id_mgr = RESOURCE_ID_INIT(RT_UNAMED_PIPE_NUMBER, resoure_id); /** * @brief This function will open a pipe. @@ -33,16 +55,26 @@ static int pipe_fops_open(struct dfs_fd *fd) { int rc = 0; - rt_device_t device; rt_pipe_t *pipe; - pipe = (rt_pipe_t *)fd->data; - if (!pipe) return -1; + pipe = (rt_pipe_t *)fd->vnode->data; + if (!pipe) + { + return -1; + } + + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); - device = &(pipe->parent); - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); + if ((fd->flags & O_RDONLY) == O_RDONLY) + { + pipe->reader = 1; + } - if (device->ref_count == 0) + if ((fd->flags & O_WRONLY) == O_WRONLY) + { + pipe->writer = 1; + } + if (fd->vnode->ref_count == 1) { pipe->fifo = rt_ringbuffer_create(pipe->bufsz); if (pipe->fifo == RT_NULL) @@ -52,23 +84,8 @@ static int pipe_fops_open(struct dfs_fd *fd) } } - switch (fd->flags & O_ACCMODE) - { - case O_RDONLY: - pipe->readers ++; - break; - case O_WRONLY: - pipe->writers ++; - break; - case O_RDWR: - pipe->readers ++; - pipe->writers ++; - break; - } - device->ref_count ++; - __exit: - rt_mutex_release(&(pipe->lock)); + rt_mutex_release(&pipe->lock); return rc; } @@ -87,47 +104,41 @@ static int pipe_fops_close(struct dfs_fd *fd) rt_device_t device; rt_pipe_t *pipe; - pipe = (rt_pipe_t *)fd->data; - if (!pipe) return -1; - - device = &(pipe->parent); - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); - - switch (fd->flags & O_ACCMODE) + pipe = (rt_pipe_t *)fd->vnode->data; + if (!pipe) { - case O_RDONLY: - pipe->readers --; - break; - case O_WRONLY: - pipe->writers --; - break; - case O_RDWR: - pipe->readers --; - pipe->writers --; - break; + return -1; } - if (pipe->writers == 0) + device = &pipe->parent; + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); + + if ((fd->flags & O_RDONLY) == O_RDONLY) { - rt_wqueue_wakeup(&(pipe->reader_queue), (void *)(POLLIN | POLLERR | POLLHUP)); + pipe->reader = 0; } - if (pipe->readers == 0) + if ((fd->flags & O_WRONLY) == O_WRONLY) { - rt_wqueue_wakeup(&(pipe->writer_queue), (void *)(POLLOUT | POLLERR | POLLHUP)); + pipe->writer = 0; + while (!rt_list_isempty(&pipe->reader_queue.waiting_list)) + { + rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN); + } } - if (device->ref_count == 1) + if (fd->vnode->ref_count == 1) { if (pipe->fifo != RT_NULL) + { rt_ringbuffer_destroy(pipe->fifo); + } pipe->fifo = RT_NULL; } - device->ref_count --; - rt_mutex_release(&(pipe->lock)); + rt_mutex_release(&pipe->lock); - if (device->ref_count == 0 && pipe->is_named == RT_FALSE) + if (fd->vnode->ref_count == 1 && pipe->is_named == RT_FALSE) { /* delete the unamed pipe */ rt_pipe_delete(device->parent.name); @@ -158,15 +169,15 @@ static int pipe_fops_ioctl(struct dfs_fd *fd, int cmd, void *args) rt_pipe_t *pipe; int ret = 0; - pipe = (rt_pipe_t *)fd->data; + pipe = (rt_pipe_t *)fd->vnode->data; switch (cmd) { case FIONREAD: - *((int *)args) = rt_ringbuffer_data_len(pipe->fifo); + *((int*)args) = rt_ringbuffer_data_len(pipe->fifo); break; case FIONWRITE: - *((int *)args) = rt_ringbuffer_space_len(pipe->fifo); + *((int*)args) = rt_ringbuffer_space_len(pipe->fifo); break; default: ret = -EINVAL; @@ -194,24 +205,16 @@ static int pipe_fops_read(struct dfs_fd *fd, void *buf, size_t count) int len = 0; rt_pipe_t *pipe; - pipe = (rt_pipe_t *)fd->data; + pipe = (rt_pipe_t *)fd->vnode->data; /* no process has the pipe open for writing, return end-of-file */ - if (pipe->writers == 0) - return 0; - - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); while (1) { - if (pipe->writers == 0) - { - goto out; - } - len = rt_ringbuffer_get(pipe->fifo, buf, count); - if (len > 0) + if (len > 0 || pipe->writer == 0) { break; } @@ -224,14 +227,14 @@ static int pipe_fops_read(struct dfs_fd *fd, void *buf, size_t count) } rt_mutex_release(&pipe->lock); - rt_wqueue_wakeup(&(pipe->writer_queue), (void *)POLLOUT); - rt_wqueue_wait(&(pipe->reader_queue), 0, -1); - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); + rt_wqueue_wakeup(&pipe->writer_queue, (void*)POLLOUT); + rt_wqueue_wait(&pipe->reader_queue, 0, -1); + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); } } /* wakeup writer */ - rt_wqueue_wakeup(&(pipe->writer_queue), (void *)POLLOUT); + rt_wqueue_wakeup(&pipe->writer_queue, (void*)POLLOUT); out: rt_mutex_release(&pipe->lock); @@ -259,29 +262,18 @@ static int pipe_fops_write(struct dfs_fd *fd, const void *buf, size_t count) int ret = 0; uint8_t *pbuf; - pipe = (rt_pipe_t *)fd->data; - - if (pipe->readers == 0) - { - ret = -EPIPE; - goto out; - } + pipe = (rt_pipe_t *)fd->vnode->data; if (count == 0) + { return 0; + } - pbuf = (uint8_t *)buf; + pbuf = (uint8_t*)buf; rt_mutex_take(&pipe->lock, -1); while (1) { - if (pipe->readers == 0) - { - if (ret == 0) - ret = -EPIPE; - break; - } - len = rt_ringbuffer_put(pipe->fifo, pbuf, count - ret); ret += len; pbuf += len; @@ -305,19 +297,18 @@ static int pipe_fops_write(struct dfs_fd *fd, const void *buf, size_t count) } rt_mutex_release(&pipe->lock); - rt_wqueue_wakeup(&(pipe->reader_queue), (void *)POLLIN); + rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN); /* pipe full, waiting on suspended write list */ - rt_wqueue_wait(&(pipe->writer_queue), 0, -1); + rt_wqueue_wait(&pipe->writer_queue, 0, -1); rt_mutex_take(&pipe->lock, -1); } rt_mutex_release(&pipe->lock); if (wakeup) { - rt_wqueue_wakeup(&(pipe->reader_queue), (void *)POLLIN); + rt_wqueue_wakeup(&pipe->reader_queue, (void*)POLLIN); } -out: return ret; } @@ -339,10 +330,10 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req) int mask = 0; rt_pipe_t *pipe; int mode = 0; - pipe = (rt_pipe_t *)fd->data; + pipe = (rt_pipe_t *)fd->vnode->data; - rt_poll_add(&(pipe->reader_queue), req); - rt_poll_add(&(pipe->writer_queue), req); + rt_poll_add(&pipe->reader_queue, req); + rt_poll_add(&pipe->writer_queue, req); switch (fd->flags & O_ACCMODE) { @@ -363,10 +354,6 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req) { mask |= POLLIN; } - if (pipe->writers == 0) - { - mask |= POLLHUP; - } } if (mode & 2) @@ -375,10 +362,6 @@ static int pipe_fops_poll(struct dfs_fd *fd, rt_pollreq_t *req) { mask |= POLLOUT; } - if (pipe->readers == 0) - { - mask |= POLLERR; - } } return mask; @@ -410,7 +393,7 @@ static const struct dfs_file_ops pipe_fops = * When the return value is -RT_EINVAL, it means the device handle is empty. * When the return value is -RT_ENOMEM, it means insufficient memory allocation failed. */ -static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag) +rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag) { rt_pipe_t *pipe = (rt_pipe_t *)device; rt_err_t ret = RT_EOK; @@ -421,7 +404,7 @@ static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag) goto __exit; } - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); if (pipe->fifo == RT_NULL) { @@ -432,7 +415,7 @@ static rt_err_t rt_pipe_open(rt_device_t device, rt_uint16_t oflag) } } - rt_mutex_release(&(pipe->lock)); + rt_mutex_release(&pipe->lock); __exit: return ret; @@ -447,20 +430,20 @@ __exit: * When the return value is RT_EOK, the operation is successful. * When the return value is -RT_EINVAL, it means the device handle is empty. */ -static rt_err_t rt_pipe_close(rt_device_t device) +rt_err_t rt_pipe_close(rt_device_t device) { rt_pipe_t *pipe = (rt_pipe_t *)device; - if (device == RT_NULL) return -RT_EINVAL; - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); - - if (device->ref_count == 1) + if (device == RT_NULL) { - rt_ringbuffer_destroy(pipe->fifo); - pipe->fifo = RT_NULL; + return -RT_EINVAL; } + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); + + rt_ringbuffer_destroy(pipe->fifo); + pipe->fifo = RT_NULL; - rt_mutex_release(&(pipe->lock)); + rt_mutex_release(&pipe->lock); return RT_EOK; } @@ -479,7 +462,7 @@ static rt_err_t rt_pipe_close(rt_device_t device) * @return Return the length of data read. * When the return value is 0, it means the pipe device handle is empty or the count is 0. */ -static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count) +rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt_size_t count) { uint8_t *pbuf; rt_size_t read_bytes = 0; @@ -487,18 +470,24 @@ static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt if (device == RT_NULL) { - rt_set_errno(EINVAL); + rt_set_errno(-EINVAL); + return 0; + } + if (count == 0) + { return 0; } - if (count == 0) return 0; - pbuf = (uint8_t *)buffer; - rt_mutex_take(&(pipe->lock), RT_WAITING_FOREVER); + pbuf = (uint8_t*)buffer; + rt_mutex_take(&pipe->lock, RT_WAITING_FOREVER); while (read_bytes < count) { - int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], (rt_uint16_t)(count - read_bytes)); - if (len <= 0) break; + int len = rt_ringbuffer_get(pipe->fifo, &pbuf[read_bytes], count - read_bytes); + if (len <= 0) + { + break; + } read_bytes += len; } @@ -521,7 +510,7 @@ static rt_size_t rt_pipe_read(rt_device_t device, rt_off_t pos, void *buffer, rt * @return Return the length of data written. * When the return value is 0, it means the pipe device handle is empty or the count is 0. */ -static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count) +rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buffer, rt_size_t count) { uint8_t *pbuf; rt_size_t write_bytes = 0; @@ -529,18 +518,24 @@ static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buf if (device == RT_NULL) { - rt_set_errno(EINVAL); + rt_set_errno(-EINVAL); + return 0; + } + if (count == 0) + { return 0; } - if (count == 0) return 0; - pbuf = (uint8_t *)buffer; + pbuf = (uint8_t*)buffer; rt_mutex_take(&pipe->lock, -1); while (write_bytes < count) { - int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], (rt_uint16_t)(count - write_bytes)); - if (len <= 0) break; + int len = rt_ringbuffer_put(pipe->fifo, &pbuf[write_bytes], count - write_bytes); + if (len <= 0) + { + break; + } write_bytes += len; } @@ -560,7 +555,7 @@ static rt_size_t rt_pipe_write(rt_device_t device, rt_off_t pos, const void *buf * * @return Always return RT_EOK. */ -static rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args) +rt_err_t rt_pipe_control(rt_device_t dev, int cmd, void *args) { return RT_EOK; } @@ -599,14 +594,19 @@ rt_pipe_t *rt_pipe_create(const char *name, int bufsz) rt_memset(pipe, 0, sizeof(rt_pipe_t)); pipe->is_named = RT_TRUE; /* initialize as a named pipe */ - rt_mutex_init(&(pipe->lock), name, RT_IPC_FLAG_PRIO); - rt_wqueue_init(&(pipe->reader_queue)); - rt_wqueue_init(&(pipe->writer_queue)); +#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE) + pipe->pipeno = -1; +#endif + rt_mutex_init(&pipe->lock, name, RT_IPC_FLAG_FIFO); + rt_wqueue_init(&pipe->reader_queue); + rt_wqueue_init(&pipe->writer_queue); + pipe->writer = 0; + pipe->reader = 0; RT_ASSERT(bufsz < 0xFFFF); pipe->bufsz = bufsz; - dev = &(pipe->parent); + dev = &pipe->parent; dev->type = RT_Device_Class_Pipe; #ifdef RT_USING_DEVICE_OPS dev->ops = &pipe_ops; @@ -622,8 +622,12 @@ rt_pipe_t *rt_pipe_create(const char *name, int bufsz) dev->rx_indicate = RT_NULL; dev->tx_complete = RT_NULL; - if (rt_device_register(&(pipe->parent), name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE) != 0) + if (rt_device_register(&pipe->parent, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE) != 0) { + rt_mutex_detach(&pipe->lock); +#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE) + resource_id_put(&id_mgr, pipe->pipeno); +#endif rt_free(pipe); return RT_NULL; } @@ -657,14 +661,12 @@ int rt_pipe_delete(const char *name) { rt_pipe_t *pipe; - if (device->ref_count != 0) - { - return -RT_EBUSY; - } - pipe = (rt_pipe_t *)device; - rt_mutex_detach(&(pipe->lock)); + rt_mutex_detach(&pipe->lock); +#if defined(RT_USING_POSIX_DEVIO) && defined(RT_USING_POSIX_PIPE) + resource_id_put(&id_mgr, pipe->pipeno); +#endif rt_device_unregister(device); /* close fifo ringbuffer */ @@ -677,12 +679,12 @@ int rt_pipe_delete(const char *name) } else { - result = -RT_EINVAL; + result = -ENODEV; } } else { - result = -RT_EINVAL; + result = -ENODEV; } return result; @@ -702,19 +704,26 @@ int rt_pipe_delete(const char *name) int pipe(int fildes[2]) { rt_pipe_t *pipe; - char dname[RT_NAME_MAX]; - char dev_name[RT_NAME_MAX * 4]; - static int pipeno = 0; + char dname[8]; + char dev_name[32]; + int pipeno = 0; - rt_snprintf(dname, sizeof(dname), "pipe%d", pipeno++); + pipeno = resource_id_get(&id_mgr); + if (pipeno == -1) + { + return -1; + } + rt_snprintf(dname, sizeof(dname), "pipe%d", pipeno); pipe = rt_pipe_create(dname, RT_USING_POSIX_PIPE_SIZE); if (pipe == RT_NULL) { + resource_id_put(&id_mgr, pipeno); return -1; } pipe->is_named = RT_FALSE; /* unamed pipe */ + pipe->pipeno = pipeno; rt_snprintf(dev_name, sizeof(dev_name), "/dev/%s", dname); fildes[0] = open(dev_name, O_RDONLY, 0); if (fildes[0] < 0) diff --git a/components/drivers/ipc/waitqueue.c b/components/drivers/ipc/waitqueue.c index 97b5075638..25ebc71f68 100644 --- a/components/drivers/ipc/waitqueue.c +++ b/components/drivers/ipc/waitqueue.c @@ -118,13 +118,14 @@ void rt_wqueue_wakeup(rt_wqueue_t *queue, void *key) * * @return Return 0 if the thread is woken up. */ -int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec) +static int _rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec, int suspend_flag) { int tick; rt_thread_t tid = rt_thread_self(); rt_timer_t tmr = &(tid->thread_timer); struct rt_wqueue_node __wait; rt_base_t level; + rt_err_t ret; /* current context checking */ RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE); @@ -150,8 +151,14 @@ int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec) goto __exit_wakeup; } + ret = rt_thread_suspend_with_flag(tid, suspend_flag); + if (ret != RT_EOK) + { + rt_hw_interrupt_enable(level); + /* suspend failed */ + return -RT_EINTR; + } rt_wqueue_add(queue, &__wait); - rt_thread_suspend(tid); /* start timer */ if (tick != RT_WAITING_FOREVER) @@ -176,3 +183,18 @@ __exit_wakeup: return tid->error; } + +int rt_wqueue_wait(rt_wqueue_t *queue, int condition, int msec) +{ + return _rt_wqueue_wait(queue, condition, msec, RT_UNINTERRUPTIBLE); +} + +int rt_wqueue_wait_killable(rt_wqueue_t *queue, int condition, int msec) +{ + return _rt_wqueue_wait(queue, condition, msec, RT_KILLABLE); +} + +int rt_wqueue_wait_interruptible(rt_wqueue_t *queue, int condition, int msec) +{ + return _rt_wqueue_wait(queue, condition, msec, RT_INTERRUPTIBLE); +} diff --git a/components/drivers/ipc/workqueue.c b/components/drivers/ipc/workqueue.c index 3dae192d0d..d9b98929c8 100644 --- a/components/drivers/ipc/workqueue.c +++ b/components/drivers/ipc/workqueue.c @@ -65,7 +65,7 @@ static void _workqueue_thread_entry(void *parameter) if (rt_list_isempty(&(queue->work_list))) { /* no software timer exist, suspend self. */ - rt_thread_suspend(rt_thread_self()); + rt_thread_suspend_with_flag(rt_thread_self(), RT_UNINTERRUPTIBLE); rt_hw_interrupt_enable(level); rt_schedule(); continue; @@ -116,7 +116,7 @@ static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue, /* whether the workqueue is doing work */ if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) + ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK)) { /* resume work thread */ rt_thread_resume(queue->work_thread); @@ -198,7 +198,7 @@ static void _delayed_work_timeout_handler(void *parameter) } /* whether the workqueue is doing work */ if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) + ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK)) { /* resume work thread */ rt_thread_resume(queue->work_thread); @@ -356,7 +356,7 @@ rt_err_t rt_workqueue_urgent_work(struct rt_workqueue *queue, struct rt_work *wo rt_list_insert_after(&queue->work_list, &(work->list)); /* whether the workqueue is doing work */ if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) + ((queue->work_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_STAT_MASK)) { /* resume work thread */ rt_thread_resume(queue->work_thread); diff --git a/components/drivers/misc/SConscript b/components/drivers/misc/SConscript index 72058eeeca..b0d0354cf4 100644 --- a/components/drivers/misc/SConscript +++ b/components/drivers/misc/SConscript @@ -23,6 +23,18 @@ if GetDepend(['RT_USING_PULSE_ENCODER']): if GetDepend(['RT_USING_INPUT_CAPTURE']): src = src + ['rt_inputcapture.c'] +if GetDepend(['RT_USING_DEV_BUS']): + src = src + ['rt_dev_bus.c'] + +if GetDepend(['RT_USING_NULL']): + src = src + ['rt_null.c'] + +if GetDepend(['RT_USING_ZERO']): + src = src + ['rt_zero.c'] + +if GetDepend(['RT_USING_RANDOM']): + src = src + ['rt_random.c'] + if len(src): group = DefineGroup('DeviceDrivers', src, depend = [''], CPPPATH = CPPPATH) diff --git a/components/drivers/misc/rt_dev_bus.c b/components/drivers/misc/rt_dev_bus.c new file mode 100644 index 0000000000..5e858d1781 --- /dev/null +++ b/components/drivers/misc/rt_dev_bus.c @@ -0,0 +1,80 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-10-13 flybreak the first version + */ + +#include <rtthread.h> +#include <rtdevice.h> + +#include <string.h> +#include <stdlib.h> + +#define DBG_TAG "dev_bus" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#if defined(RT_USING_POSIX_DEVIO) +#include <unistd.h> +#include <fcntl.h> +#include <poll.h> +#include <sys/ioctl.h> +#include <dfs_file.h> + +static int bus_fops_open(struct dfs_fd *fd) +{ + LOG_D("bus fops open"); + return 0; +} + +static int bus_fops_close(struct dfs_fd *fd) +{ + LOG_D("bus fops close"); + return 0; +} + +static const struct dfs_file_ops bus_fops = +{ + bus_fops_open, + bus_fops_close, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, +}; +#endif + +rt_device_t rt_device_bus_create(char *name, int attach_size) +{ + rt_err_t result = RT_EOK; + rt_device_t dev = rt_device_create(RT_Device_Class_Bus, 0); + + result = rt_device_register(dev, name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE); + if (result < 0) + { + rt_kprintf("dev bus [%s] register failed!, ret=%d\n", name, result); + return RT_NULL; + } +#if defined(RT_USING_POSIX_DEVIO) + dev->fops = &bus_fops; +#endif + + LOG_D("bus create"); + return dev; +} + +rt_err_t rt_device_bus_destroy(rt_device_t dev) +{ + rt_device_unregister(dev); + dev->parent.type = RT_Object_Class_Device; + rt_device_destroy(dev); + LOG_D("bus destroy"); + return RT_EOK; +} diff --git a/components/drivers/misc/rt_null.c b/components/drivers/misc/rt_null.c new file mode 100644 index 0000000000..3e4cf6f3da --- /dev/null +++ b/components/drivers/misc/rt_null.c @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd + * + * Change Logs: + * Date Author Notes + * 2020-12-03 quanzhao the first version + */ + +#include <time.h> +#include <string.h> +#include <rtthread.h> + +static struct rt_device null_dev; + +static rt_size_t null_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + return 0; +} + +static rt_size_t null_write (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + return size; +} + +static rt_err_t null_control (rt_device_t dev, int cmd, void *args) +{ + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops null_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + null_read, + null_write, + null_control +}; +#endif + +int null_device_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + + if (init_ok) + { + return 0; + } + RT_ASSERT(!rt_device_find("null")); + null_dev.type = RT_Device_Class_Miscellaneous; + +#ifdef RT_USING_DEVICE_OPS + null_dev.ops = &null_ops; +#else + null_dev.init = RT_NULL; + null_dev.open = RT_NULL; + null_dev.close = RT_NULL; + null_dev.read = null_read; + null_dev.write = null_write; + null_dev.control = null_control; +#endif + + /* no private */ + null_dev.user_data = RT_NULL; + + rt_device_register(&null_dev, "null", RT_DEVICE_FLAG_RDWR); + + init_ok = RT_TRUE; + + return 0; +} +INIT_DEVICE_EXPORT(null_device_init); + diff --git a/components/drivers/misc/rt_random.c b/components/drivers/misc/rt_random.c new file mode 100644 index 0000000000..1ea0480b7c --- /dev/null +++ b/components/drivers/misc/rt_random.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd + * + * Change Logs: + * Date Author Notes + * 2020-12-18 quanzhao the first version + */ + +#include <time.h> +#include <string.h> +#include <rtthread.h> + +static struct rt_device random_dev; +static unsigned long seed; + +static rt_uint16_t calc_random(void) +{ + seed = 214013L * seed + 2531011L; + return (seed >> 16) & 0x7FFF; /* return bits 16~30 */ +} + +static rt_size_t random_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_uint16_t rand = calc_random(); + ssize_t ret = sizeof(rand); + rt_memcpy(buffer, &rand, ret); + return ret; +} + +static rt_size_t random_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + ssize_t ret = sizeof(seed); + rt_memcpy(&seed, buffer, ret); + return ret; +} + +static rt_err_t random_control(rt_device_t dev, int cmd, void *args) +{ + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops random_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + random_read, + random_write, + random_control +}; +#endif + +int random_device_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + + if (init_ok) + { + return 0; + } + RT_ASSERT(!rt_device_find("random")); + random_dev.type = RT_Device_Class_Miscellaneous; + +#ifdef RT_USING_DEVICE_OPS + random_dev.ops = &random_ops; +#else + random_dev.init = RT_NULL; + random_dev.open = RT_NULL; + random_dev.close = RT_NULL; + random_dev.read = random_read; + random_dev.write = random_write; + random_dev.control = random_control; +#endif + + /* no private */ + random_dev.user_data = RT_NULL; + + rt_device_register(&random_dev, "random", RT_DEVICE_FLAG_RDWR); + + init_ok = RT_TRUE; + + return 0; +} +INIT_DEVICE_EXPORT(random_device_init); + +static struct rt_device urandom_dev; +static unsigned long useed; + +static rt_uint16_t calc_urandom(void) +{ + useed = 214013L * useed + 2531011L; + return (useed >> 16) & 0x7FFF; /* return bits 16~30 */ +} + +static rt_size_t random_uread(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_uint16_t rand = calc_urandom(); + ssize_t ret = sizeof(rand); + rt_memcpy(buffer, &rand, ret); + return ret; +} + +static rt_size_t random_uwrite(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + ssize_t ret = sizeof(useed); + rt_memcpy(&useed, buffer, ret); + return ret; +} + +static rt_err_t random_ucontrol(rt_device_t dev, int cmd, void *args) +{ + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops urandom_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + random_uread, + random_uwrite, + random_ucontrol +}; +#endif + +int urandom_device_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + + if (init_ok) + { + return 0; + } + RT_ASSERT(!rt_device_find("urandom")); + urandom_dev.type = RT_Device_Class_Miscellaneous; + +#ifdef RT_USING_DEVICE_OPS + urandom_dev.ops = &urandom_ops; +#else + urandom_dev.init = RT_NULL; + urandom_dev.open = RT_NULL; + urandom_dev.close = RT_NULL; + urandom_dev.read = random_uread; + urandom_dev.write = random_uwrite; + urandom_dev.control = random_ucontrol; +#endif + + /* no private */ + urandom_dev.user_data = RT_NULL; + + rt_device_register(&urandom_dev, "urandom", RT_DEVICE_FLAG_RDWR); + + init_ok = RT_TRUE; + + return 0; +} +INIT_DEVICE_EXPORT(urandom_device_init); diff --git a/components/drivers/misc/rt_zero.c b/components/drivers/misc/rt_zero.c new file mode 100644 index 0000000000..f2657a2360 --- /dev/null +++ b/components/drivers/misc/rt_zero.c @@ -0,0 +1,75 @@ +/* + * Copyright (c) 2011-2020, Shanghai Real-Thread Electronic Technology Co.,Ltd + * + * Change Logs: + * Date Author Notes + * 2020-12-03 quanzhao the first version + */ + +#include <time.h> +#include <string.h> +#include <rtthread.h> + +static struct rt_device zero_dev; + +static rt_size_t zero_read (rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_memset(buffer, 0, size); + return size; +} + +static rt_size_t zero_write (rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + return size; +} + +static rt_err_t zero_control (rt_device_t dev, int cmd, void *args) +{ + return RT_EOK; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops zero_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + zero_read, + zero_write, + zero_control +}; +#endif + +int zero_device_init(void) +{ + static rt_bool_t init_ok = RT_FALSE; + + if (init_ok) + { + return 0; + } + RT_ASSERT(!rt_device_find("zero")); + zero_dev.type = RT_Device_Class_Miscellaneous; + +#ifdef RT_USING_DEVICE_OPS + zero_dev.ops = &zero_ops; +#else + zero_dev.init = RT_NULL; + zero_dev.open = RT_NULL; + zero_dev.close = RT_NULL; + zero_dev.read = zero_read; + zero_dev.write = zero_write; + zero_dev.control = zero_control; +#endif + + /* no private */ + zero_dev.user_data = RT_NULL; + + rt_device_register(&zero_dev, "zero", RT_DEVICE_FLAG_RDWR); + + init_ok = RT_TRUE; + + return 0; +} +INIT_DEVICE_EXPORT(zero_device_init); + diff --git a/components/drivers/sdio/SConscript b/components/drivers/sdio/SConscript index 95dcfa2578..d5b6eb1cb9 100644 --- a/components/drivers/sdio/SConscript +++ b/components/drivers/sdio/SConscript @@ -7,6 +7,7 @@ block_dev.c mmcsd_core.c sd.c sdio.c +gpt.c mmc.c """) diff --git a/components/drivers/sdio/block_dev.c b/components/drivers/sdio/block_dev.c index 522fa753db..70159af30c 100644 --- a/components/drivers/sdio/block_dev.c +++ b/components/drivers/sdio/block_dev.c @@ -12,6 +12,7 @@ #include <dfs_fs.h> #include <drivers/mmcsd_core.h> +#include <drivers/gpt.h> #define DBG_TAG "SDIO" #ifdef RT_SDIO_DEBUG @@ -21,6 +22,7 @@ #endif /* RT_SDIO_DEBUG */ #include <rtdbg.h> +static rt_list_t blk_devices = RT_LIST_OBJECT_INIT(blk_devices); #define BLK_MIN(a, b) ((a) < (b) ? (a) : (b)) @@ -37,6 +39,67 @@ struct mmcsd_blk_device #ifndef RT_MMCSD_MAX_PARTITION #define RT_MMCSD_MAX_PARTITION 16 #endif +#define RT_GPT_PARTITION_MAX 128 + +static int __send_status(struct rt_mmcsd_card *card, rt_uint32_t *status, unsigned retries) +{ + int err; + struct rt_mmcsd_cmd cmd; + + cmd.busy_timeout = 0; + cmd.cmd_code = SEND_STATUS; + cmd.arg = card->rca << 16; + cmd.flags = RESP_R1 | CMD_AC; + err = mmcsd_send_cmd(card->host, &cmd, retries); + if (err) + return err; + + if (status) + *status = cmd.resp[0]; + + return 0; +} + +static int card_busy_detect(struct rt_mmcsd_card *card, unsigned int timeout_ms, + rt_uint32_t *resp_errs) +{ + int timeout = rt_tick_from_millisecond(timeout_ms); + int err = 0; + rt_uint32_t status; + rt_tick_t start; + + start = rt_tick_get(); + do + { + rt_bool_t out = (int)(rt_tick_get() - start) > timeout; + + err = __send_status(card, &status, 5); + if (err) + { + LOG_E("error %d requesting status", err); + return err; + } + + /* Accumulate any response error bits seen */ + if (resp_errs) + *resp_errs |= status; + + if (out) + { + LOG_E("wait card busy timeout"); + return -RT_ETIMEOUT; + } + /* + * Some cards mishandle the status bits, + * so make sure to check both the busy + * indication and the card state. + */ + } + while (!(status & R1_READY_FOR_DATA) || + (R1_CURRENT_STATE(status) == 7)); + + return err; +} rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card) { @@ -73,7 +136,7 @@ rt_int32_t mmcsd_num_wr_blocks(struct rt_mmcsd_card *card) timeout_us = data.timeout_ns / 1000; timeout_us += data.timeout_clks * 1000 / - (card->host->io_cfg.clock / 1000); + (card->host->io_cfg.clock / 1000); if (timeout_us > 100000) { @@ -148,44 +211,29 @@ static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card, w_cmd = WRITE_BLOCK; } + if (!controller_is_spi(card->host) && (card->flags & 0x8000)) + { + /* last request is WRITE,need check busy */ + card_busy_detect(card, 10000, RT_NULL); + } + if (!dir) { cmd.cmd_code = r_cmd; data.flags |= DATA_DIR_READ; + card->flags &= 0x7fff; } else { cmd.cmd_code = w_cmd; data.flags |= DATA_DIR_WRITE; + card->flags |= 0x8000; } mmcsd_set_data_timeout(&data, card); data.buf = buf; - mmcsd_send_request(host, &req); - if (!controller_is_spi(card->host) && dir != 0) - { - do - { - rt_int32_t err; - - cmd.cmd_code = SEND_STATUS; - cmd.arg = card->rca << 16; - cmd.flags = RESP_R1 | CMD_AC; - err = mmcsd_send_cmd(card->host, &cmd, 5); - if (err) - { - LOG_E("error %d requesting status", err); - break; - } - /* - * Some cards mishandle the status bits, - * so make sure to check both the busy - * indication and the card state. - */ - } while (!(cmd.resp[0] & R1_READY_FOR_DATA) || - (R1_CURRENT_STATE(cmd.resp[0]) == 7)); - } + mmcsd_send_request(host, &req); mmcsd_host_unlock(host); @@ -193,7 +241,7 @@ static rt_err_t rt_mmcsd_req_blk(struct rt_mmcsd_card *card, { LOG_E("mmcsd request blocks error"); LOG_E("%d,%d,%d, 0x%08x,0x%08x", - cmd.err, data.err, stop.err, data.flags, sector); + cmd.err, data.err, stop.err, data.flags, sector); return -RT_ERROR; } @@ -224,6 +272,8 @@ static rt_err_t rt_mmcsd_control(rt_device_t dev, int cmd, void *args) case RT_DEVICE_CTRL_BLK_GETGEOME: rt_memcpy(args, &blk_dev->geometry, sizeof(struct rt_device_blk_geometry)); break; + case RT_DEVICE_CTRL_BLK_PARTITION: + rt_memcpy(args, &blk_dev->part, sizeof(struct dfs_partition)); default: break; } @@ -253,7 +303,7 @@ static rt_size_t rt_mmcsd_read(rt_device_t dev, while (remain_size) { req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size; - err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, rd_ptr, req_size, 0); + err = rt_mmcsd_req_blk(blk_dev->card, pos + offset, rd_ptr, req_size, 0); if (err) break; offset += req_size; @@ -294,7 +344,7 @@ static rt_size_t rt_mmcsd_write(rt_device_t dev, while (remain_size) { req_size = (remain_size > blk_dev->max_req_size) ? blk_dev->max_req_size : remain_size; - err = rt_mmcsd_req_blk(blk_dev->card, part->offset + pos + offset, wr_ptr, req_size, 1); + err = rt_mmcsd_req_blk(blk_dev->card, pos + offset, wr_ptr, req_size, 1); if (err) break; offset += req_size; @@ -338,6 +388,19 @@ static rt_int32_t mmcsd_set_blksize(struct rt_mmcsd_card *card) return 0; } +rt_int32_t read_lba(struct rt_mmcsd_card *card, size_t lba, uint8_t *buffer, size_t count) +{ + rt_uint8_t status = 0; + + status = mmcsd_set_blksize(card); + if (status) + { + return status; + } + rt_thread_mdelay(1); + status = rt_mmcsd_req_blk(card, lba, buffer, count, 0); + return status; +} #ifdef RT_USING_DEVICE_OPS const static struct rt_device_ops mmcsd_blk_ops = @@ -351,44 +414,31 @@ const static struct rt_device_ops mmcsd_blk_ops = }; #endif - -static struct mmcsd_blk_device * rt_mmcsd_create_blkdev(struct rt_mmcsd_card *card, const char* dname, struct dfs_partition* psPart) +rt_int32_t gpt_device_probe(struct rt_mmcsd_card *card) { - struct mmcsd_blk_device *blk_dev; - char sname[12]; + rt_int32_t err = RT_EOK; + rt_uint8_t i, status; + char dname[10]; + char sname[16]; + struct mmcsd_blk_device *blk_dev = RT_NULL; blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); if (!blk_dev) { LOG_E("mmcsd:malloc memory failed!"); - return RT_NULL; + return -1; } - if (psPart != RT_NULL) - { - rt_memcpy(&blk_dev->part, psPart, sizeof(struct dfs_partition)); - blk_dev->geometry.sector_count = psPart->size; - } - else - { - blk_dev->part.offset = 0; - blk_dev->part.size = 0; - blk_dev->geometry.sector_count = card->card_capacity * (1024 / 512); - } - - blk_dev->geometry.bytes_per_sector = 1<<9; - blk_dev->geometry.block_size = card->card_blksize; - - rt_snprintf(sname, sizeof(sname), "sem_%s", dname); - blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); - blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * card->host->max_seg_size) >> 9, (card->host->max_blk_count * card->host->max_blk_size) >> 9); - + blk_dev->part.offset = 0; + blk_dev->part.size = 0; + rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0); + blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); /* register mmcsd device */ - blk_dev->dev.type = RT_Device_Class_Block; + blk_dev->dev.type = RT_Device_Class_Block; #ifdef RT_USING_DEVICE_OPS blk_dev->dev.ops = &mmcsd_blk_ops; #else @@ -399,42 +449,100 @@ static struct mmcsd_blk_device * rt_mmcsd_create_blkdev(struct rt_mmcsd_card *ca blk_dev->dev.write = rt_mmcsd_write; blk_dev->dev.control = rt_mmcsd_control; #endif + blk_dev->card = card; + + blk_dev->geometry.bytes_per_sector = 1 << 9; + blk_dev->geometry.block_size = card->card_blksize; + blk_dev->geometry.sector_count = + card->card_capacity * (1024 / 512); + blk_dev->dev.user_data = blk_dev; - blk_dev->card = card; + rt_device_register(&(blk_dev->dev), card->host->name, + RT_DEVICE_FLAG_RDWR); + rt_list_insert_after(&blk_devices, &blk_dev->list); + + for (i = 0; i < RT_GPT_PARTITION_MAX; i++) + { + blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); + if (!blk_dev) + { + LOG_E("mmcsd:malloc memory failed!"); + break; + } + blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * + card->host->max_seg_size) >> 9, + (card->host->max_blk_count * + card->host->max_blk_size) >> 9); - rt_device_register(&blk_dev->dev, dname, - RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE | RT_DEVICE_FLAG_STANDALONE); + /* get the first partition */ + status = gpt_get_partition_param(card, &blk_dev->part, i); + if (status == RT_EOK) + { + rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); + rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); + blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); + + /* register mmcsd device */ + blk_dev->dev.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + blk_dev->dev.ops = &mmcsd_blk_ops; +#else + blk_dev->dev.init = rt_mmcsd_init; + blk_dev->dev.open = rt_mmcsd_open; + blk_dev->dev.close = rt_mmcsd_close; + blk_dev->dev.read = rt_mmcsd_read; + blk_dev->dev.write = rt_mmcsd_write; + blk_dev->dev.control = rt_mmcsd_control; +#endif + blk_dev->card = card; - /* Insert to list. */ - rt_list_insert_after(&card->blk_devices, &blk_dev->list); + blk_dev->geometry.bytes_per_sector = 1 << 9; + blk_dev->geometry.block_size = card->card_blksize; + blk_dev->geometry.sector_count = blk_dev->part.size; + + blk_dev->dev.user_data = blk_dev; + + rt_device_register(&(blk_dev->dev), dname, + RT_DEVICE_FLAG_RDWR); + rt_list_insert_after(&blk_devices, &blk_dev->list); + } + else + { + rt_free(blk_dev); + blk_dev = RT_NULL; + break; + } #ifdef RT_USING_DFS_MNTTABLE - if ( blk_dev ) - { - LOG_I("try to mount file system!"); - /* try to mount file system on this block device */ - dfs_mount_device(&(blk_dev->dev)); - } + if (blk_dev) + { + LOG_I("try to mount file system!"); + /* try to mount file system on this block device */ + dfs_mount_device(&(blk_dev->dev)); + } #endif + } + gpt_free(); - return blk_dev; + return err; } -rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) +rt_int32_t mbr_device_probe(struct rt_mmcsd_card *card) { rt_int32_t err = 0; - rt_err_t status; + rt_uint8_t i, status; rt_uint8_t *sector; + char dname[10]; + char sname[16]; + struct mmcsd_blk_device *blk_dev = RT_NULL; err = mmcsd_set_blksize(card); - if(err) + if (err) { return err; } - - LOG_D("probe mmcsd block device!"); - + rt_thread_mdelay(1); /* get the first sector to read partition table */ sector = (rt_uint8_t *)rt_malloc(SECTOR_SIZE); if (sector == RT_NULL) @@ -447,43 +555,104 @@ rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) status = rt_mmcsd_req_blk(card, 0, sector, 1, 0); if (status == RT_EOK) { - rt_uint8_t i; - char dname[8]; - struct dfs_partition part; - struct mmcsd_blk_device *blk_dev = RT_NULL; - rt_int32_t host_id = card->host->id; + blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); + if (!blk_dev) + { + LOG_E("mmcsd:malloc memory failed!"); + return -1; + } + blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * + card->host->max_seg_size) >> 9, + (card->host->max_blk_count * + card->host->max_blk_size) >> 9); + blk_dev->part.offset = 0; + blk_dev->part.size = 0; + rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, 0); + blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); + /* register mmcsd device */ + blk_dev->dev.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + blk_dev->dev.ops = &mmcsd_blk_ops; +#else + blk_dev->dev.init = rt_mmcsd_init; + blk_dev->dev.open = rt_mmcsd_open; + blk_dev->dev.close = rt_mmcsd_close; + blk_dev->dev.read = rt_mmcsd_read; + blk_dev->dev.write = rt_mmcsd_write; + blk_dev->dev.control = rt_mmcsd_control; +#endif + blk_dev->card = card; - /* Initial blk_device link-list. */ - rt_list_init(&card->blk_devices); + blk_dev->geometry.bytes_per_sector = 1 << 9; + blk_dev->geometry.block_size = card->card_blksize; + blk_dev->geometry.sector_count = + card->card_capacity * (1024 / 512); + blk_dev->dev.user_data = blk_dev; + + rt_device_register(&(blk_dev->dev), card->host->name, + RT_DEVICE_FLAG_RDWR); + rt_list_insert_after(&blk_devices, &blk_dev->list); for (i = 0; i < RT_MMCSD_MAX_PARTITION; i++) { - /* Get the first partition */ - status = dfs_filesystem_get_partition(&part, sector, i); + blk_dev = rt_calloc(1, sizeof(struct mmcsd_blk_device)); + if (!blk_dev) + { + LOG_E("mmcsd:malloc memory failed!"); + break; + } + blk_dev->max_req_size = BLK_MIN((card->host->max_dma_segs * + card->host->max_seg_size) >> 9, + (card->host->max_blk_count * + card->host->max_blk_size) >> 9); + + /* get the first partition */ + status = dfs_filesystem_get_partition(&blk_dev->part, sector, i); if (status == RT_EOK) { - /* Given name is with allocated host id and its partition index. */ - rt_snprintf(dname, sizeof(dname), "sd%dp%d", host_id, i); - blk_dev = rt_mmcsd_create_blkdev(card, (const char*)dname, &part); - if ( blk_dev == RT_NULL ) - { - err = -RT_ENOMEM; - goto exit_rt_mmcsd_blk_probe; - } + rt_snprintf(dname, sizeof(dname) - 1, "%s%d", card->host->name, i); + rt_snprintf(sname, sizeof(sname) - 1, "sem_%s%d", card->host->name, i + 1); + blk_dev->part.lock = rt_sem_create(sname, 1, RT_IPC_FLAG_FIFO); + + /* register mmcsd device */ + blk_dev->dev.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + blk_dev->dev.ops = &mmcsd_blk_ops; +#else + blk_dev->dev.init = rt_mmcsd_init; + blk_dev->dev.open = rt_mmcsd_open; + blk_dev->dev.close = rt_mmcsd_close; + blk_dev->dev.read = rt_mmcsd_read; + blk_dev->dev.write = rt_mmcsd_write; + blk_dev->dev.control = rt_mmcsd_control; +#endif + blk_dev->card = card; + + blk_dev->geometry.bytes_per_sector = 1 << 9; + blk_dev->geometry.block_size = card->card_blksize; + blk_dev->geometry.sector_count = blk_dev->part.size; + + blk_dev->dev.user_data = blk_dev; + + rt_device_register(&(blk_dev->dev), dname, + RT_DEVICE_FLAG_RDWR); + rt_list_insert_after(&blk_devices, &blk_dev->list); } else { + rt_free(blk_dev); + blk_dev = RT_NULL; break; } - } - /* Always create the super node, given name is with allocated host id. */ - rt_snprintf(dname, sizeof(dname), "sd%d", host_id); - blk_dev = rt_mmcsd_create_blkdev(card, (const char*)dname, RT_NULL); - if ( blk_dev == RT_NULL ) - { - err = -RT_ENOMEM; - goto exit_rt_mmcsd_blk_probe; +#ifdef RT_USING_DFS_MNTTABLE + if (blk_dev) + { + LOG_I("try to mount file system!"); + /* try to mount file system on this block device */ + dfs_mount_device(&(blk_dev->dev)); + } +#endif } } else @@ -492,42 +661,45 @@ rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) err = -RT_ERROR; } -exit_rt_mmcsd_blk_probe: - /* release sector buffer */ rt_free(sector); return err; + } -void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card) +rt_int32_t rt_mmcsd_blk_probe(struct rt_mmcsd_card *card) { - rt_list_t *l, *n; - struct mmcsd_blk_device *blk_dev; + uint32_t err = 0; - if(card == RT_NULL) + LOG_D("probe mmcsd block device!"); + if (check_gpt(card) != 0) { - LOG_E("card is null!"); - return; + err = gpt_device_probe(card); } - - if(rt_list_isempty(&card->blk_devices)) + else { - LOG_E("card blk_devices is empty!"); - return; + err = mbr_device_probe(card); } + return err; +} - for (l = (&card->blk_devices)->next, n = l->next; l != &card->blk_devices; l = n, n=n->next) +void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card) +{ + rt_list_t *l, *n; + struct mmcsd_blk_device *blk_dev; + + for (l = (&blk_devices)->next, n = l->next; l != &blk_devices; l = n, n = n->next) { blk_dev = (struct mmcsd_blk_device *)rt_list_entry(l, struct mmcsd_blk_device, list); if (blk_dev->card == card) { /* unmount file system */ - const char * mounted_path = dfs_filesystem_get_mounted_path(&(blk_dev->dev)); + const char *mounted_path = dfs_filesystem_get_mounted_path(&(blk_dev->dev)); if (mounted_path) { - dfs_unmount(mounted_path); - LOG_D("unmount file system %s for device %s.\r\n", mounted_path, blk_dev->dev.parent.name); + dfs_unmount(mounted_path); + LOG_D("unmount file system %s for device %s.\r\n", mounted_path, blk_dev->dev.parent.name); } rt_sem_delete(blk_dev->part.lock); rt_device_unregister(&blk_dev->dev); @@ -536,3 +708,15 @@ void rt_mmcsd_blk_remove(struct rt_mmcsd_card *card) } } } + +/* + * This function will initialize block device on the mmc/sd. + * + * @deprecated since 2.1.0, this function does not need to be invoked + * in the system initialization. + */ +int rt_mmcsd_blk_init(void) +{ + /* nothing */ + return 0; +} diff --git a/components/drivers/sdio/gpt.c b/components/drivers/sdio/gpt.c new file mode 100644 index 0000000000..19bc1ad453 --- /dev/null +++ b/components/drivers/sdio/gpt.c @@ -0,0 +1,561 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-05-05 linzhenxing first version + */ +#include <rtthread.h> +#include <dfs_fs.h> +#include <drivers/gpt.h> +#include <drivers/mmcsd_core.h> + +#define DBG_TAG "GPT" +#ifdef RT_SDIO_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_SDIO_DEBUG */ +#include <rtdbg.h> + +#define min(a, b) a < b ? a : b +static int force_gpt = 0; +static gpt_header *_gpt; +static gpt_entry *_ptes; +#define GPT_TYPE 1 +#define MBR_TYPE 0 + +static inline int efi_guidcmp (gpt_guid_t left, gpt_guid_t right) +{ + return rt_memcmp(&left, &right, sizeof (gpt_guid_t)); +} + +static uint32_t last_lba(struct rt_mmcsd_card *card) +{ + RT_ASSERT(card != RT_NULL); + return (card->card_sec_cnt) - 1; +} + +static inline int pmbr_part_valid(gpt_mbr_record *part) +{ + if (part->os_type != EFI_PMBR_OSTYPE_EFI_GPT) + { + goto invalid; + } + + /* set to 0x00000001 (i.e., the LBA of the GPT Partition Header) */ + if ((uint32_t)(part->starting_lba) != GPT_PRIMARY_PARTITION_TABLE_LBA) + { + goto invalid; + } + + return GPT_MBR_PROTECTIVE; +invalid: + return 0; +} + +/* +* +* return ret +* ret = 0, invalid mbr +* ret = 1, protect mbr +* ret = 2, hybrid mbr +*/ +int is_pmbr_valid(legacy_mbr *mbr, uint64_t total_sectors) +{ + uint32_t sz = 0; + int i, part = 0, ret = 0; /* invalid by default */ + + if (!mbr || (uint16_t)(mbr->signature) != MSDOS_MBR_SIGNATURE) + { + goto done; + } + + for (i = 0; i < 4; i++) + { + ret = pmbr_part_valid(&mbr->partition_record[i]); + if (ret == GPT_MBR_PROTECTIVE) + { + part = i; + /* + * Ok, we at least know that there's a protective MBR, + * now check if there are other partition types for + * hybrid MBR. + */ + goto check_hybrid; + } + } + + if (ret != GPT_MBR_PROTECTIVE) + { + goto done; + } + +check_hybrid: + for (i = 0; i < 4; i++) + { + if ((mbr->partition_record[i].os_type != + EFI_PMBR_OSTYPE_EFI_GPT) && + (mbr->partition_record[i].os_type != 0x00)) + { + ret = GPT_MBR_HYBRID; + } + + } + + /* + * Protective MBRs take up the lesser of the whole disk + * or 2 TiB (32bit LBA), ignoring the rest of the disk. + * Some partitioning programs, nonetheless, choose to set + * the size to the maximum 32-bit limitation, disregarding + * the disk size. + * + * Hybrid MBRs do not necessarily comply with this. + * + * Consider a bad value here to be a warning to support dd'ing + * an image from a smaller disk to a larger disk. + */ + if (ret == GPT_MBR_PROTECTIVE) + { + sz = (uint32_t)(mbr->partition_record[part].size_in_lba); + if (sz != (uint32_t) total_sectors - 1 && sz != 0xFFFFFFFF) + { + LOG_I("GPT: mbr size in lba (%u) different than whole disk (%u).", + sz, min(total_sectors - 1, 0xFFFFFFFF)); + } + } + +done: + return ret; + +} + +static gpt_entry *alloc_read_gpt_entries(struct rt_mmcsd_card *card, gpt_header *gpt) +{ + size_t count; + gpt_entry *pte; + + if (!gpt) + { + return RT_NULL; + } + + count = (size_t)(gpt->num_partition_entries) * (gpt->sizeof_partition_entry); + if (!count) + { + return RT_NULL; + } + + pte = rt_malloc(count); + if (!pte) + return RT_NULL; + + if (read_lba(card, (size_t)(gpt->partition_entry_lba),(uint8_t *)pte, count/512) != RT_EOK) + { + rt_free(pte); + return RT_NULL; + } + return pte; + +} + +static gpt_header *alloc_read_gpt_header(struct rt_mmcsd_card *card, size_t lba) +{ + gpt_header *gpt; + void *buf; + + buf = rt_malloc(512); + if (!buf) + { + return RT_NULL; + } + + if (read_lba(card, lba, (uint8_t *)buf, 1) != RT_EOK) + { + rt_free(buf); + return RT_NULL; + } + gpt = (gpt_header *)buf; + + return gpt; +} + +static int is_gpt_valid(struct rt_mmcsd_card *card, size_t lba, gpt_header **gpt, gpt_entry **ptes) +{ + size_t lastlba; + + if (!ptes) + { + return 0; + } + + if (!(*gpt = alloc_read_gpt_header(card, lba))) + { + return 0; + } + + /* Check the GUID Partition Table signature */ + if ((uint64_t)((*gpt)->signature) != GPT_HEADER_SIGNATURE) + { + LOG_E("GUID Partition Table Header signature is wrong:" + "%ld != %ld",(uint64_t)((*gpt)->signature),(uint64_t)GPT_HEADER_SIGNATURE); + goto fail; + } + + /* Check the GUID Partition Table header size is too small */ + if ((uint32_t)((*gpt)->header_size) < sizeof(gpt_header)) + { + LOG_E("GUID Partition Table Header size is too small: %u < %zu", + (uint32_t)((*gpt)->header_size),sizeof(gpt_header)); + goto fail; + } + + /* Check that the start_lba entry points to the LBA that contains + * the GUID Partition Table */ + if ((uint64_t)((*gpt)->start_lba) != lba) + { + LOG_E("GPT start_lba incorrect: %ld != %ld", + (uint64_t)((*gpt)->start_lba), + (uint64_t)lba); + goto fail; + } + + /* Check the first_usable_lba and last_usable_lba are + * within the disk. + */ + lastlba = last_lba(card); + if ((uint64_t)((*gpt)->first_usable_lba) > lastlba) + { + LOG_E("GPT: first_usable_lba incorrect: %ld > %ld", + ((uint64_t)((*gpt)->first_usable_lba)), + (size_t)lastlba); + goto fail; + } + + if ((uint64_t)((*gpt)->last_usable_lba) > lastlba) + { + LOG_E("GPT: last_usable_lba incorrect: %ld > %ld", + (uint64_t)((*gpt)->last_usable_lba), + (size_t)lastlba); + goto fail; + } + + if ((uint64_t)((*gpt)->last_usable_lba) < (uint64_t)((*gpt)->first_usable_lba)) + { + LOG_E("GPT: last_usable_lba incorrect: %ld > %ld", + (uint64_t)((*gpt)->last_usable_lba), + (uint64_t)((*gpt)->first_usable_lba)); + goto fail; + } + /* Check that sizeof_partition_entry has the correct value */ + if ((uint32_t)((*gpt)->sizeof_partition_entry) != sizeof(gpt_entry)) { + LOG_E("GUID Partition Entry Size check failed."); + goto fail; + } + + if (!(*ptes = alloc_read_gpt_entries(card, *gpt))) + { + goto fail; + } + + /* We're done, all's well */ + return 1; + + fail: + rt_free(*gpt); + *gpt = RT_NULL; + return 0; +} + +/** + * is_pte_valid() - tests one PTE for validity + * pte:pte to check + * lastlba: last lba of the disk + * + * Description: returns 1 if valid, 0 on error. + */ +static inline int is_pte_valid(const gpt_entry *pte, const size_t lastlba) +{ + if ((!efi_guidcmp(pte->partition_type_guid, NULL_GUID)) || + (uint64_t)(pte->starting_lba) > lastlba || + (uint64_t)(pte->ending_lba) > lastlba) + { + return 0; + } + + return 1; +} + +/** + * compare_gpts() - Search disk for valid GPT headers and PTEs + * pgpt: primary GPT header + * agpt: alternate GPT header + * lastlba: last LBA number + * + * Description: Returns nothing. Sanity checks pgpt and agpt fields + * and prints warnings on discrepancies. + * + */ +static void compare_gpts(gpt_header *pgpt, gpt_header *agpt, size_t lastlba) +{ + int error_found = 0; + if (!pgpt || !agpt) + { + return; + } + + if ((uint64_t)(pgpt->start_lba) != (uint64_t)(agpt->alternate_lba)) + { + LOG_I("GPT:Primary header LBA != Alt. header alternate_lba"); + LOG_I("GPT:%lld != %lld", + (uint64_t)(pgpt->start_lba), + (uint64_t)(agpt->alternate_lba)); + error_found++; + } + + if ((uint64_t)(pgpt->alternate_lba) != (uint64_t)(agpt->start_lba)) + { + LOG_I("GPT:Primary header alternate_lba != Alt. header start_lba"); + LOG_I("GPT:%lld != %lld", + (uint64_t)(pgpt->alternate_lba), + (uint64_t)(agpt->start_lba)); + error_found++; + } + + if ((uint64_t)(pgpt->first_usable_lba) != (uint64_t)(agpt->first_usable_lba)) + { + LOG_I("GPT:first_usable_lbas don't match."); + LOG_I("GPT:%lld != %lld", + (uint64_t)(pgpt->first_usable_lba), + (uint64_t)(agpt->first_usable_lba)); + error_found++; + } + + if ((uint64_t)(pgpt->last_usable_lba) != (uint64_t)(agpt->last_usable_lba)) + { + LOG_I("GPT:last_usable_lbas don't match."); + LOG_I("GPT:%lld != %lld", + (uint64_t)(pgpt->last_usable_lba), + (uint64_t)(agpt->last_usable_lba)); + error_found++; + } + + if (efi_guidcmp(pgpt->disk_guid, agpt->disk_guid)) + { + LOG_I("GPT:disk_guids don't match."); + error_found++; + } + + if ((pgpt->num_partition_entries) != (agpt->num_partition_entries)) + { + LOG_I("GPT:num_partition_entries don't match: " + "0x%x != 0x%x", + (pgpt->num_partition_entries), + (agpt->num_partition_entries)); + error_found++; + } + + if ((pgpt->sizeof_partition_entry) != (agpt->sizeof_partition_entry)) + { + LOG_I("GPT:sizeof_partition_entry values don't match: " + "0x%x != 0x%x", + (pgpt->sizeof_partition_entry), + (agpt->sizeof_partition_entry)); + error_found++; + } + + if ((pgpt->partition_entry_array_crc32) != (agpt->partition_entry_array_crc32)) + { + LOG_I("GPT:partition_entry_array_crc32 values don't match: " + "0x%x != 0x%x", + (pgpt->partition_entry_array_crc32), + (agpt->partition_entry_array_crc32)); + error_found++; + } + + if ((pgpt->alternate_lba) != lastlba) + { + LOG_I("GPT:Primary header thinks Alt. header is not at the end of the disk."); + LOG_I("GPT:%lld != %lld", + (uint64_t)(pgpt->alternate_lba), + (size_t)lastlba); + error_found++; + } + + if ((agpt->start_lba) != lastlba) + { + LOG_I("GPT:Alternate GPT header not at the end of the disk."); + LOG_I("GPT:%lld != %lld", + (uint64_t)(agpt->start_lba), + (size_t)lastlba); + error_found++; + } + + if (error_found) + { + LOG_I("GPT: Use GNU Parted to correct GPT errors."); + } + return; +} + +/** + * find_valid_gpt() - Search disk for valid GPT headers and PTEs + * state: disk parsed partitions + * gpt: GPT header ptr, filled on return. + * ptes: PTEs ptr, filled on return. + * + * Description: Returns 1 if valid, 0 on error. + * If valid, returns pointers to newly allocated GPT header and PTEs. + * Validity depends on PMBR being valid (or being overridden by the + * 'gpt' kernel command line option) and finding either the Primary + * GPT header and PTEs valid, or the Alternate GPT header and PTEs + * valid. If the Primary GPT header is not valid, the Alternate GPT header + * is not checked unless the 'gpt' kernel command line option is passed. + * This protects against devices which misreport their size, and forces + * the user to decide to use the Alternate GPT. + */ +static int find_valid_gpt(struct rt_mmcsd_card *card, gpt_header **gpt, + gpt_entry **ptes) +{ + int good_pgpt = 0, good_agpt = 0, good_pmbr = 0; + gpt_header *pgpt = RT_NULL, *agpt = RT_NULL; + gpt_entry *pptes = RT_NULL, *aptes = RT_NULL; + legacy_mbr *legacymbr; + size_t total_sectors = last_lba(card) + 1; + size_t lastlba; + int status = 0; + + if (!ptes) + { + return 0; + } + + lastlba = last_lba(card); + if (!force_gpt) + { + /* This will be added to the EFI Spec. per Intel after v1.02. */ + legacymbr = rt_malloc(512); + if (!legacymbr) + { + goto fail; + } + + status = read_lba(card, 0, (uint8_t *)legacymbr, 1); + if (status) + { + LOG_I("status:%d", status); + goto fail; + } + + good_pmbr = is_pmbr_valid(legacymbr, total_sectors); + rt_free(legacymbr); + + if (!good_pmbr) + { + goto fail; + } + + rt_kprintf("Device has a %s MBR\n", + good_pmbr == GPT_MBR_PROTECTIVE ? + "protective" : "hybrid"); + } + + good_pgpt = is_gpt_valid(card, GPT_PRIMARY_PARTITION_TABLE_LBA, + &pgpt, &pptes); + if (good_pgpt) + { + good_agpt = is_gpt_valid(card, (pgpt->alternate_lba), &agpt, &aptes); + if (!good_agpt && force_gpt) + { + good_agpt = is_gpt_valid(card, lastlba, &agpt, &aptes); + } + + /* The obviously unsuccessful case */ + if (!good_pgpt && !good_agpt) + { + goto fail; + } + + compare_gpts(pgpt, agpt, lastlba); + + /* The good cases */ + if (good_pgpt) + { + *gpt = pgpt; + *ptes = pptes; + rt_free(agpt); + rt_free(aptes); + if (!good_agpt) + { + LOG_D("Alternate GPT is invalid, using primary GPT."); + } + return 1; + } + else if (good_agpt) + { + *gpt = agpt; + *ptes = aptes; + rt_free(pgpt); + rt_free(pptes); + LOG_D("Primary GPT is invalid, using alternate GPT."); + return 1; + } + } + + fail: + rt_free(pgpt); + rt_free(agpt); + rt_free(pptes); + rt_free(aptes); + *gpt = RT_NULL; + *ptes = RT_NULL; + return 0; +} + +int check_gpt(struct rt_mmcsd_card *card) +{ + if (!find_valid_gpt(card, &_gpt, &_ptes) || !_gpt || !_ptes) + { + rt_free(_gpt); + rt_free(_ptes); + return MBR_TYPE; + } + return GPT_TYPE; +} + +int gpt_get_partition_param(struct rt_mmcsd_card *card, struct dfs_partition *part, uint32_t pindex) +{ + if (!is_pte_valid(&_ptes[pindex], last_lba(card))) + { + return -1; + } + + part->offset = (off_t)(_ptes[pindex].starting_lba); + part->size = (_ptes[pindex].ending_lba) - (_ptes[pindex].starting_lba) + 1ULL; + + rt_kprintf("found part[%d], begin(sector): %d, end(sector):%d size: ", + pindex, _ptes[pindex].starting_lba, _ptes[pindex].ending_lba); + + if ((part->size >> 11) == 0) + { + rt_kprintf("%d%s", part->size >> 1, "KB\n"); /* KB */ + } + else + { + unsigned int part_size; + part_size = part->size >> 11; /* MB */ + if ((part_size >> 10) == 0) + rt_kprintf("%d.%d%s", part_size, (part->size >> 1) & 0x3FF, "MB\n"); + else + rt_kprintf("%d.%d%s", part_size >> 10, part_size & 0x3FF, "GB\n"); + } + return 0; +} + +void gpt_free(void) +{ + rt_free(_ptes); + rt_free(_gpt); +} diff --git a/components/drivers/sdio/mmc.c b/components/drivers/sdio/mmc.c index 0365de8ed7..32a9a204d8 100644 --- a/components/drivers/sdio/mmc.c +++ b/components/drivers/sdio/mmc.c @@ -46,17 +46,17 @@ rt_inline rt_uint32_t GET_BITS(rt_uint32_t *resp, rt_uint32_t start, rt_uint32_t size) { - const rt_int32_t __size = size; - const rt_uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; - const rt_int32_t __off = 3 - ((start) / 32); - const rt_int32_t __shft = (start) & 31; - rt_uint32_t __res; + const rt_int32_t __size = size; + const rt_uint32_t __mask = (__size < 32 ? 1 << __size : 0) - 1; + const rt_int32_t __off = 3 - ((start) / 32); + const rt_int32_t __shft = (start) & 31; + rt_uint32_t __res; - __res = resp[__off] >> __shft; - if (__size + __shft > 32) - __res |= resp[__off-1] << ((32 - __shft) % 32); + __res = resp[__off] >> __shft; + if (__size + __shft > 32) + __res |= resp[__off - 1] << ((32 - __shft) % 32); - return __res & __mask; + return __res & __mask; } /* @@ -64,50 +64,52 @@ rt_inline rt_uint32_t GET_BITS(rt_uint32_t *resp, */ static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card) { - rt_uint32_t a, b; - struct rt_mmcsd_csd *csd = &card->csd; - rt_uint32_t *resp = card->resp_csd; + rt_uint32_t a, b; + struct rt_mmcsd_csd *csd = &card->csd; + rt_uint32_t *resp = card->resp_csd; - /* - * We only understand CSD structure v1.1 and v1.2. - * v1.2 has extra information in bits 15, 11 and 10. - * We also support eMMC v4.4 & v4.41. - */ - csd->csd_structure = GET_BITS(resp, 126, 2); - if (csd->csd_structure == 0) { + /* + * We only understand CSD structure v1.1 and v1.2. + * v1.2 has extra information in bits 15, 11 and 10. + * We also support eMMC v4.4 & v4.41. + */ + csd->csd_structure = GET_BITS(resp, 126, 2); + if (csd->csd_structure == 0) + { LOG_E("unrecognised CSD structure version %d!", csd->csd_structure); return -RT_ERROR; - } + } - csd->taac = GET_BITS(resp, 112, 8); - csd->nsac = GET_BITS(resp, 104, 8); - csd->tran_speed = GET_BITS(resp, 96, 8); - csd->card_cmd_class = GET_BITS(resp, 84, 12); - csd->rd_blk_len = GET_BITS(resp, 80, 4); - csd->rd_blk_part = GET_BITS(resp, 79, 1); - csd->wr_blk_misalign = GET_BITS(resp, 78, 1); - csd->rd_blk_misalign = GET_BITS(resp, 77, 1); - csd->dsr_imp = GET_BITS(resp, 76, 1); - csd->c_size = GET_BITS(resp, 62, 12); - csd->c_size_mult = GET_BITS(resp, 47, 3); - csd->r2w_factor = GET_BITS(resp, 26, 3); - csd->wr_blk_len = GET_BITS(resp, 22, 4); - csd->wr_blk_partial = GET_BITS(resp, 21, 1); - csd->csd_crc = GET_BITS(resp, 1, 7); + csd->taac = GET_BITS(resp, 112, 8); + csd->nsac = GET_BITS(resp, 104, 8); + csd->tran_speed = GET_BITS(resp, 96, 8); + csd->card_cmd_class = GET_BITS(resp, 84, 12); + csd->rd_blk_len = GET_BITS(resp, 80, 4); + csd->rd_blk_part = GET_BITS(resp, 79, 1); + csd->wr_blk_misalign = GET_BITS(resp, 78, 1); + csd->rd_blk_misalign = GET_BITS(resp, 77, 1); + csd->dsr_imp = GET_BITS(resp, 76, 1); + csd->c_size = GET_BITS(resp, 62, 12); + csd->c_size_mult = GET_BITS(resp, 47, 3); + csd->r2w_factor = GET_BITS(resp, 26, 3); + csd->wr_blk_len = GET_BITS(resp, 22, 4); + csd->wr_blk_partial = GET_BITS(resp, 21, 1); + csd->csd_crc = GET_BITS(resp, 1, 7); - card->card_blksize = 1 << csd->rd_blk_len; - card->tacc_clks = csd->nsac * 100; - card->tacc_ns = (tacc_uint[csd->taac&0x07] * tacc_value[(csd->taac&0x78)>>3] + 9) / 10; - card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3]; - if (csd->wr_blk_len >= 9) { + card->card_blksize = 1 << csd->rd_blk_len; + card->tacc_clks = csd->nsac * 100; + card->tacc_ns = (tacc_uint[csd->taac & 0x07] * tacc_value[(csd->taac & 0x78) >> 3] + 9) / 10; + card->max_data_rate = tran_unit[csd->tran_speed & 0x07] * tran_value[(csd->tran_speed & 0x78) >> 3]; + if (csd->wr_blk_len >= 9) + { a = GET_BITS(resp, 42, 5); b = GET_BITS(resp, 37, 5); card->erase_size = (a + 1) * (b + 1); card->erase_size <<= csd->wr_blk_len - 9; - } + } - return 0; + return 0; } /* @@ -115,66 +117,66 @@ static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card) */ static int mmc_get_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t **new_ext_csd) { - void *ext_csd; - struct rt_mmcsd_req req; - struct rt_mmcsd_cmd cmd; - struct rt_mmcsd_data data; - - *new_ext_csd = RT_NULL; + void *ext_csd; + struct rt_mmcsd_req req; + struct rt_mmcsd_cmd cmd; + struct rt_mmcsd_data data; - if (GET_BITS(card->resp_csd, 122, 4) < 4) - return 0; + *new_ext_csd = RT_NULL; + if (GET_BITS(card->resp_csd, 122, 4) < 4) + return 0; - /* - * As the ext_csd is so large and mostly unused, we don't store the - * raw block in mmc_card. - */ - ext_csd = rt_malloc(512); - if (!ext_csd) { - LOG_E("alloc memory failed when get ext csd!"); - return -RT_ENOMEM; - } + /* + * As the ext_csd is so large and mostly unused, we don't store the + * raw block in mmc_card. + */ + ext_csd = rt_malloc(512); + if (!ext_csd) + { + LOG_E("alloc memory failed when get ext csd!"); + return -RT_ENOMEM; + } - rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); - rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); - rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); + rt_memset(&req, 0, sizeof(struct rt_mmcsd_req)); + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + rt_memset(&data, 0, sizeof(struct rt_mmcsd_data)); - req.cmd = &cmd; - req.data = &data; + req.cmd = &cmd; + req.data = &data; - cmd.cmd_code = SEND_EXT_CSD; - cmd.arg = 0; + cmd.cmd_code = SEND_EXT_CSD; + cmd.arg = 0; - /* NOTE HACK: the RESP_SPI_R1 is always correct here, but we - * rely on callers to never use this with "native" calls for reading - * CSD or CID. Native versions of those commands use the R2 type, - * not R1 plus a data block. - */ - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; + /* NOTE HACK: the RESP_SPI_R1 is always correct here, but we + * rely on callers to never use this with "native" calls for reading + * CSD or CID. Native versions of those commands use the R2 type, + * not R1 plus a data block. + */ + cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_ADTC; - data.blksize = 512; - data.blks = 1; - data.flags = DATA_DIR_READ; - data.buf = ext_csd; + data.blksize = 512; + data.blks = 1; + data.flags = DATA_DIR_READ; + data.buf = ext_csd; - /* - * Some cards require longer data read timeout than indicated in CSD. - * Address this by setting the read timeout to a "reasonably high" - * value. For the cards tested, 300ms has proven enough. If necessary, - * this value can be increased if other problematic cards require this. - */ - data.timeout_ns = 300000000; - data.timeout_clks = 0; + /* + * Some cards require longer data read timeout than indicated in CSD. + * Address this by setting the read timeout to a "reasonably high" + * value. For the cards tested, 300ms has proven enough. If necessary, + * this value can be increased if other problematic cards require this. + */ + data.timeout_ns = 300000000; + data.timeout_clks = 0; - mmcsd_send_request(card->host, &req); + mmcsd_send_request(card->host, &req); - if (cmd.err) - return cmd.err; - if (data.err) - return data.err; + if (cmd.err) + return cmd.err; + if (data.err) + return data.err; - *new_ext_csd = ext_csd; - return 0; + *new_ext_csd = ext_csd; + return 0; } /* @@ -182,24 +184,39 @@ static int mmc_get_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t **new_ext_csd) */ static int mmc_parse_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd) { - rt_uint64_t card_capacity = 0; - - if(card == RT_NULL || ext_csd == RT_NULL) - { - LOG_E("emmc parse ext csd fail, invaild args"); - return -1; - } + rt_uint64_t card_capacity = 0; + struct rt_mmcsd_host *host; + if (card == RT_NULL || ext_csd == RT_NULL) + { + LOG_E("emmc parse ext csd fail, invaild args"); + return -1; + } - card->flags |= CARD_FLAG_HIGHSPEED; - card->hs_max_data_rate = 52000000; + host = card->host; + if (host->flags & MMCSD_SUP_HS200) + { + card->flags |= CARD_FLAG_HS200; + card->hs_max_data_rate = 200000000; + } + else if (host->flags & MMCSD_SUP_HIGHSPEED_DDR) + { + card->flags |= CARD_FLAG_HIGHSPEED_DDR; + card->hs_max_data_rate = 52000000; + } + else + { + card->flags |= CARD_FLAG_HIGHSPEED; + card->hs_max_data_rate = 52000000; + } - card_capacity = *((rt_uint32_t *)&ext_csd[EXT_CSD_SEC_CNT]); - card_capacity *= card->card_blksize; - card_capacity >>= 10; /* unit:KB */ - card->card_capacity = (rt_uint32_t)card_capacity; - LOG_I("emmc card capacity %d KB.", card->card_capacity); + card_capacity = *((rt_uint32_t *)&ext_csd[EXT_CSD_SEC_CNT]); + card->card_sec_cnt = card_capacity; + card_capacity *= card->card_blksize; + card_capacity >>= 10; /* unit:KB */ + card->card_capacity = card_capacity; + LOG_I("emmc card capacity %d KB, card sec count:%d.", card->card_capacity, card->card_sec_cnt); - return 0; + return 0; } /** @@ -214,72 +231,73 @@ static int mmc_parse_ext_csd(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd) static int mmc_switch(struct rt_mmcsd_card *card, rt_uint8_t set, rt_uint8_t index, rt_uint8_t value) { - int err; - struct rt_mmcsd_host *host = card->host; - struct rt_mmcsd_cmd cmd = {0}; + int err; + struct rt_mmcsd_host *host = card->host; + struct rt_mmcsd_cmd cmd = {0}; - cmd.cmd_code = SWITCH; - cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | - (index << 16) | (value << 8) | set; - cmd.flags = RESP_SPI_R1 | RESP_R1 | CMD_AC; + cmd.cmd_code = SWITCH; + cmd.arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | (value << 8) | set; + cmd.flags = RESP_R1B | CMD_AC; - err = mmcsd_send_cmd(host, &cmd, 3); - if (err) - return err; + err = mmcsd_send_cmd(host, &cmd, 3); + if (err) + return err; - return 0; + return 0; } static int mmc_compare_ext_csds(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd, rt_uint32_t bus_width) { - rt_uint8_t *bw_ext_csd; - int err; + rt_uint8_t *bw_ext_csd; + int err; - if (bus_width == MMCSD_BUS_WIDTH_1) - return 0; + if (bus_width == MMCSD_BUS_WIDTH_1) + return 0; - err = mmc_get_ext_csd(card, &bw_ext_csd); + err = mmc_get_ext_csd(card, &bw_ext_csd); - if (err || bw_ext_csd == RT_NULL) { - err = -RT_ERROR; - goto out; - } + if (err || bw_ext_csd == RT_NULL) + { + err = -RT_ERROR; + goto out; + } - /* only compare read only fields */ - err = !((ext_csd[EXT_CSD_PARTITION_SUPPORT] == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && - (ext_csd[EXT_CSD_ERASED_MEM_CONT] == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && - (ext_csd[EXT_CSD_REV] == bw_ext_csd[EXT_CSD_REV]) && - (ext_csd[EXT_CSD_STRUCTURE] == bw_ext_csd[EXT_CSD_STRUCTURE]) && - (ext_csd[EXT_CSD_CARD_TYPE] == bw_ext_csd[EXT_CSD_CARD_TYPE]) && - (ext_csd[EXT_CSD_S_A_TIMEOUT] == bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && - (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && - (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && - (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && - (ext_csd[EXT_CSD_SEC_TRIM_MULT] == bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && - (ext_csd[EXT_CSD_SEC_ERASE_MULT] == bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && - (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && - (ext_csd[EXT_CSD_TRIM_MULT] == bw_ext_csd[EXT_CSD_TRIM_MULT]) && - (ext_csd[EXT_CSD_SEC_CNT + 0] == bw_ext_csd[EXT_CSD_SEC_CNT + 0]) && - (ext_csd[EXT_CSD_SEC_CNT + 1] == bw_ext_csd[EXT_CSD_SEC_CNT + 1]) && - (ext_csd[EXT_CSD_SEC_CNT + 2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && - (ext_csd[EXT_CSD_SEC_CNT + 3] == bw_ext_csd[EXT_CSD_SEC_CNT + 3]) && - (ext_csd[EXT_CSD_PWR_CL_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_52_195]) && - (ext_csd[EXT_CSD_PWR_CL_26_195] == bw_ext_csd[EXT_CSD_PWR_CL_26_195]) && - (ext_csd[EXT_CSD_PWR_CL_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_52_360]) && - (ext_csd[EXT_CSD_PWR_CL_26_360] == bw_ext_csd[EXT_CSD_PWR_CL_26_360]) && - (ext_csd[EXT_CSD_PWR_CL_200_195] == bw_ext_csd[EXT_CSD_PWR_CL_200_195]) && - (ext_csd[EXT_CSD_PWR_CL_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_200_360]) && - (ext_csd[EXT_CSD_PWR_CL_DDR_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && - (ext_csd[EXT_CSD_PWR_CL_DDR_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) && - (ext_csd[EXT_CSD_PWR_CL_DDR_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360])); + /* only compare read only fields */ + err = !((ext_csd[EXT_CSD_PARTITION_SUPPORT] == bw_ext_csd[EXT_CSD_PARTITION_SUPPORT]) && + (ext_csd[EXT_CSD_ERASED_MEM_CONT] == bw_ext_csd[EXT_CSD_ERASED_MEM_CONT]) && + (ext_csd[EXT_CSD_REV] == bw_ext_csd[EXT_CSD_REV]) && + (ext_csd[EXT_CSD_STRUCTURE] == bw_ext_csd[EXT_CSD_STRUCTURE]) && + (ext_csd[EXT_CSD_CARD_TYPE] == bw_ext_csd[EXT_CSD_CARD_TYPE]) && + (ext_csd[EXT_CSD_S_A_TIMEOUT] == bw_ext_csd[EXT_CSD_S_A_TIMEOUT]) && + (ext_csd[EXT_CSD_HC_WP_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_WP_GRP_SIZE]) && + (ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT] == bw_ext_csd[EXT_CSD_ERASE_TIMEOUT_MULT]) && + (ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE] == bw_ext_csd[EXT_CSD_HC_ERASE_GRP_SIZE]) && + (ext_csd[EXT_CSD_SEC_TRIM_MULT] == bw_ext_csd[EXT_CSD_SEC_TRIM_MULT]) && + (ext_csd[EXT_CSD_SEC_ERASE_MULT] == bw_ext_csd[EXT_CSD_SEC_ERASE_MULT]) && + (ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT] == bw_ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) && + (ext_csd[EXT_CSD_TRIM_MULT] == bw_ext_csd[EXT_CSD_TRIM_MULT]) && + (ext_csd[EXT_CSD_SEC_CNT + 0] == bw_ext_csd[EXT_CSD_SEC_CNT + 0]) && + (ext_csd[EXT_CSD_SEC_CNT + 1] == bw_ext_csd[EXT_CSD_SEC_CNT + 1]) && + (ext_csd[EXT_CSD_SEC_CNT + 2] == bw_ext_csd[EXT_CSD_SEC_CNT + 2]) && + (ext_csd[EXT_CSD_SEC_CNT + 3] == bw_ext_csd[EXT_CSD_SEC_CNT + 3]) && + (ext_csd[EXT_CSD_PWR_CL_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_52_195]) && + (ext_csd[EXT_CSD_PWR_CL_26_195] == bw_ext_csd[EXT_CSD_PWR_CL_26_195]) && + (ext_csd[EXT_CSD_PWR_CL_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_52_360]) && + (ext_csd[EXT_CSD_PWR_CL_26_360] == bw_ext_csd[EXT_CSD_PWR_CL_26_360]) && + (ext_csd[EXT_CSD_PWR_CL_200_195] == bw_ext_csd[EXT_CSD_PWR_CL_200_195]) && + (ext_csd[EXT_CSD_PWR_CL_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_200_360]) && + (ext_csd[EXT_CSD_PWR_CL_DDR_52_195] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_195]) && + (ext_csd[EXT_CSD_PWR_CL_DDR_52_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_52_360]) && + (ext_csd[EXT_CSD_PWR_CL_DDR_200_360] == bw_ext_csd[EXT_CSD_PWR_CL_DDR_200_360])); - if (err) - err = -RT_ERROR; + if (err) + err = -RT_ERROR; out: - rt_free(bw_ext_csd); - return err; + rt_free(bw_ext_csd); + return err; } /* @@ -289,104 +307,103 @@ out: */ static int mmc_select_bus_width(struct rt_mmcsd_card *card, rt_uint8_t *ext_csd) { - rt_uint32_t ext_csd_bits[] = { - EXT_CSD_DDR_BUS_WIDTH_8, - EXT_CSD_DDR_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_8, - EXT_CSD_BUS_WIDTH_4, - EXT_CSD_BUS_WIDTH_1 - }; - rt_uint32_t bus_widths[] = { - MMCSD_DDR_BUS_WIDTH_8, - MMCSD_DDR_BUS_WIDTH_4, - MMCSD_BUS_WIDTH_8, - MMCSD_BUS_WIDTH_4, - MMCSD_BUS_WIDTH_1 - }; - struct rt_mmcsd_host *host = card->host; - unsigned idx, trys, bus_width = 0; - int err = 0; + rt_uint32_t ext_csd_bits[][2] = + { + {EXT_CSD_BUS_WIDTH_8, EXT_CSD_DDR_BUS_WIDTH_8}, + {EXT_CSD_BUS_WIDTH_4, EXT_CSD_DDR_BUS_WIDTH_4}, + {EXT_CSD_BUS_WIDTH_1, EXT_CSD_BUS_WIDTH_1}, + }; + rt_uint32_t bus_widths[] = + { + MMCSD_BUS_WIDTH_8, + MMCSD_BUS_WIDTH_4, + MMCSD_BUS_WIDTH_1 + }; + struct rt_mmcsd_host *host = card->host; + unsigned idx, bus_width = 0; + int err = 0, ddr = 0; - if (GET_BITS(card->resp_csd, 122, 4) < 4) - return 0; + if (GET_BITS(card->resp_csd, 122, 4) < 4) + return 0; - /* - * Unlike SD, MMC cards dont have a configuration register to notify - * supported bus width. So bus test command should be run to identify - * the supported bus width or compare the ext csd values of current - * bus width and ext csd values of 1 bit mode read earlier. - */ - for (idx = 0; idx < sizeof(ext_csd_bits)/sizeof(rt_uint32_t); idx++) { + if (card->flags & CARD_FLAG_HIGHSPEED_DDR) + { + ddr = 2; + } /* - * Host is capable of 8bit transfer, then switch - * the device to work in 8bit transfer mode. If the - * mmc switch command returns error then switch to - * 4bit transfer mode. On success set the corresponding - * bus width on the host. Meanwhile, mmc core would - * bail out early if corresponding bus capable wasn't - * set by drivers. + * Unlike SD, MMC cards dont have a configuration register to notify + * supported bus width. So bus test command should be run to identify + * the supported bus width or compare the ext csd values of current + * bus width and ext csd values of 1 bit mode read earlier. */ - if ((!((host->flags & MMCSD_BUSWIDTH_8) && - (host->flags & MMCSD_SUP_HIGHSPEED_DDR)) && - ext_csd_bits[idx] == EXT_CSD_DDR_BUS_WIDTH_8) || - (!((host->flags & MMCSD_BUSWIDTH_4) && - (host->flags & MMCSD_SUP_HIGHSPEED_DDR)) && - ext_csd_bits[idx] == EXT_CSD_DDR_BUS_WIDTH_4) || - (!(host->flags & MMCSD_BUSWIDTH_8) && - ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_8) || - (!(host->flags & MMCSD_BUSWIDTH_4) && - ext_csd_bits[idx] == EXT_CSD_BUS_WIDTH_4)) - continue; - - if (host->flags & MMCSD_SUP_HIGHSPEED_DDR) + for (idx = 0; idx < sizeof(bus_widths) / sizeof(rt_uint32_t); idx++) { - /* HS_TIMING must be set to "0x1" before setting BUS_WIDTH for dual-data-rate(DDR) operation */ - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS); - if (err) - LOG_E("switch to speed mode width hight speed failed!"); - } + /* + * Host is capable of 8bit transfer, then switch + * the device to work in 8bit transfer mode. If the + * mmc switch command returns error then switch to + * 4bit transfer mode. On success set the corresponding + * bus width on the host. Meanwhile, mmc core would + * bail out early if corresponding bus capable wasn't + * set by drivers. + */ + bus_width = bus_widths[idx]; + if (bus_width == MMCSD_BUS_WIDTH_1) + { + ddr = 0; + } - err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, - EXT_CSD_BUS_WIDTH, - ext_csd_bits[idx]); - if (err) - continue; + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ext_csd_bits[idx][0]); + + if (err) + continue; - bus_width = bus_widths[idx]; - for(trys = 0; trys < 5; trys++){ mmcsd_set_bus_width(host, bus_width); - rt_thread_mdelay(10); err = mmc_compare_ext_csds(card, ext_csd, bus_width); - if(!err) + if (!err) + { break; + } + else + { + switch (ext_csd_bits[idx][0]) + { + case 0: + LOG_E("switch to bus width 1 bit failed!"); + break; + case 1: + LOG_E("switch to bus width 4 bit failed!"); + break; + case 2: + LOG_E("switch to bus width 8 bit failed!"); + break; + default: + break; + } + } } - if (!err) { - err = bus_width; - break; - } else { - switch(ext_csd_bits[idx]){ - case EXT_CSD_DDR_BUS_WIDTH_8: - LOG_E("switch to bus width DDR 8 bit failed!"); - break; - case EXT_CSD_DDR_BUS_WIDTH_4: - LOG_E("switch to bus width DDR 4 bit failed!"); - break; - case EXT_CSD_BUS_WIDTH_8: - LOG_E("switch to bus width 8 bit failed!"); - break; - case EXT_CSD_BUS_WIDTH_4: - LOG_E("switch to bus width 4 bit failed!"); - break; - case EXT_CSD_BUS_WIDTH_1: - LOG_E("switch to bus width 1 bit failed!"); - break; - default: - break; - } + + if (!err && ddr) + { + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, + ext_csd_bits[idx][1]); } - } - return err; + if (!err) + { + if (card->flags & (CARD_FLAG_HIGHSPEED | CARD_FLAG_HIGHSPEED_DDR)) + { + + err = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, + 1); + } + } + + return err; } rt_err_t mmc_send_op_cond(struct rt_mmcsd_host *host, rt_uint32_t ocr, rt_uint32_t *rocr) @@ -401,55 +418,98 @@ rt_err_t mmc_send_op_cond(struct rt_mmcsd_host *host, cmd.arg = controller_is_spi(host) ? 0 : ocr; cmd.flags = RESP_SPI_R1 | RESP_R3 | CMD_BCR; - for (i = 100; i; i--) { - err = mmcsd_send_cmd(host, &cmd, 3); - if (err) - break; + for (i = 100; i; i--) + { + err = mmcsd_send_cmd(host, &cmd, 3); + if (err) + break; - /* if we're just probing, do a single pass */ - if (ocr == 0) - break; + /* if we're just probing, do a single pass */ + if (ocr == 0) + break; - /* otherwise wait until reset completes */ - if (controller_is_spi(host)) { - if (!(cmd.resp[0] & R1_SPI_IDLE)) - break; - } else { - if (cmd.resp[0] & CARD_BUSY) - break; - } + /* otherwise wait until reset completes */ + if (controller_is_spi(host)) + { + if (!(cmd.resp[0] & R1_SPI_IDLE)) + break; + } + else + { + if (cmd.resp[0] & CARD_BUSY) + break; + } - err = -RT_ETIMEOUT; + err = -RT_ETIMEOUT; - rt_thread_mdelay(10); //delay 10ms + rt_thread_mdelay(10); //delay 10ms } - if (rocr && !controller_is_spi(host)) - *rocr = cmd.resp[0]; + if (rocr && !controller_is_spi(host)) + *rocr = cmd.resp[0]; - return err; + return err; } static rt_err_t mmc_set_card_addr(struct rt_mmcsd_host *host, rt_uint32_t rca) { - rt_err_t err; - struct rt_mmcsd_cmd cmd; + rt_err_t err; + struct rt_mmcsd_cmd cmd; - rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); + rt_memset(&cmd, 0, sizeof(struct rt_mmcsd_cmd)); - cmd.cmd_code = SET_RELATIVE_ADDR; - cmd.arg = rca << 16; - cmd.flags = RESP_R1 | CMD_AC; + cmd.cmd_code = SET_RELATIVE_ADDR; + cmd.arg = rca << 16; + cmd.flags = RESP_R1 | CMD_AC; - err = mmcsd_send_cmd(host, &cmd, 3); - if (err) - return err; + err = mmcsd_send_cmd(host, &cmd, 3); + if (err) + return err; + + return 0; +} + +static int mmc_select_hs200(struct rt_mmcsd_card *card) +{ + int ret; - return 0; + ret = mmc_switch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_TIMING_HS200); + if (ret) + return ret; + + mmcsd_set_timing(card->host, MMCSD_TIMING_MMC_HS200); + mmcsd_set_clock(card->host, 200000000); + + ret = mmcsd_excute_tuning(card); + + return ret; +} + +static int mmc_select_timing(struct rt_mmcsd_card *card) +{ + int ret = 0; + + if (card->flags & CARD_FLAG_HS200) + { + ret = mmc_select_hs200(card); + } + else if (card->flags & CARD_FLAG_HIGHSPEED_DDR) + { + mmcsd_set_timing(card->host, MMCSD_TIMING_MMC_DDR52); + mmcsd_set_clock(card->host, card->hs_max_data_rate); + } + else + { + mmcsd_set_timing(card->host, MMCSD_TIMING_UHS_SDR50); + mmcsd_set_clock(card->host, card->hs_max_data_rate); + } + + return ret; } static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host, - rt_uint32_t ocr) + rt_uint32_t ocr) { rt_int32_t err; rt_uint32_t resp[4]; @@ -463,7 +523,7 @@ static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host, /* The extra bit indicates that we support high capacity */ err = mmc_send_op_cond(host, ocr | (1 << 30), &rocr); if (err) - goto err; + goto err; if (controller_is_spi(host)) { @@ -526,35 +586,47 @@ static rt_int32_t mmcsd_mmc_init_card(struct rt_mmcsd_host *host, err = mmc_get_ext_csd(card, &ext_csd); if (err) - goto err1; + goto err1; err = mmc_parse_ext_csd(card, ext_csd); if (err) - goto err1; + goto err1; /* If doing byte addressing, check if required to do sector * addressing. Handle the case of <2GB cards needing sector * addressing. See section 8.1 JEDEC Standard JED84-A441; * ocr register has bit 30 set for sector addressing. */ - if (!(card->flags & CARD_FLAG_SDHC) && (rocr & (1<<30))) + if (!(card->flags & CARD_FLAG_SDHC) && (rocr & (1 << 30))) card->flags |= CARD_FLAG_SDHC; /* set bus speed */ - if (card->flags & CARD_FLAG_HIGHSPEED) + if (card->flags & (CARD_FLAG_HIGHSPEED | CARD_FLAG_HIGHSPEED_DDR)) max_data_rate = card->hs_max_data_rate; else max_data_rate = card->max_data_rate; - mmcsd_set_clock(host, max_data_rate); + /*switch bus width and bus mode*/ + err = mmc_select_bus_width(card, ext_csd); + if (err) + { + LOG_E("mmc select buswidth fail"); + goto err0; + } - /*switch bus width*/ - mmc_select_bus_width(card, ext_csd); + err = mmc_select_timing(card); + if (err) + { + LOG_E("mmc select timing fail"); + goto err0; + } host->card = card; rt_free(ext_csd); return 0; +err0: + rt_free(ext_csd); err1: rt_free(card); err: diff --git a/components/drivers/sdio/mmcsd_core.c b/components/drivers/sdio/mmcsd_core.c index 179368026d..f896901804 100644 --- a/components/drivers/sdio/mmcsd_core.c +++ b/components/drivers/sdio/mmcsd_core.c @@ -13,6 +13,7 @@ #include <drivers/sd.h> #include <drivers/mmc.h> #include <drivers/sdio.h> +#include <string.h> #define DBG_TAG "SDIO" #ifdef RT_SDIO_DEBUG @@ -40,7 +41,6 @@ static struct rt_mailbox mmcsd_detect_mb; static rt_uint32_t mmcsd_detect_mb_pool[4]; static struct rt_mailbox mmcsd_hotpluge_mb; static rt_uint32_t mmcsd_hotpluge_mb_pool[4]; -static rt_uint32_t allocated_host_num = 0; void mmcsd_host_lock(struct rt_mmcsd_host *host) { @@ -59,7 +59,8 @@ void mmcsd_req_complete(struct rt_mmcsd_host *host) void mmcsd_send_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) { - do { + do + { req->cmd->retries--; req->cmd->err = 0; req->cmd->mrq = req; @@ -79,7 +80,8 @@ void mmcsd_send_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req) rt_sem_take(&host->sem_ack, RT_WAITING_FOREVER); - } while(req->cmd->err && (req->cmd->retries > 0)); + } + while (req->cmd->err && (req->cmd->retries > 0)); } @@ -243,7 +245,7 @@ rt_int32_t mmcsd_get_cid(struct rt_mmcsd_host *host, rt_uint32_t *cid) return -RT_ERROR; } - for (i = 0;i < 4;i++) + for (i = 0; i < 4; i++) cid[i] = buf[i]; rt_free(buf); @@ -274,7 +276,7 @@ rt_int32_t mmcsd_get_csd(struct rt_mmcsd_card *card, rt_uint32_t *csd) return 0; } - buf = (rt_uint32_t*)rt_malloc(16); + buf = (rt_uint32_t *)rt_malloc(16); if (!buf) { LOG_E("allocate memory failed!"); @@ -320,7 +322,7 @@ rt_int32_t mmcsd_get_csd(struct rt_mmcsd_card *card, rt_uint32_t *csd) return -RT_ERROR; } - for (i = 0;i < 4;i++) + for (i = 0; i < 4; i++) csd[i] = buf[i]; rt_free(buf); @@ -388,10 +390,10 @@ rt_inline void mmcsd_set_iocfg(struct rt_mmcsd_host *host) struct rt_mmcsd_io_cfg *io_cfg = &host->io_cfg; mmcsd_dbg("clock %uHz busmode %u powermode %u cs %u Vdd %u " - "width %u \n", - io_cfg->clock, io_cfg->bus_mode, - io_cfg->power_mode, io_cfg->chip_select, io_cfg->vdd, - io_cfg->bus_width); + "width %u \n", + io_cfg->clock, io_cfg->bus_mode, + io_cfg->power_mode, io_cfg->chip_select, io_cfg->vdd, + io_cfg->bus_width); host->ops->set_iocfg(host, io_cfg); } @@ -438,6 +440,12 @@ void mmcsd_set_bus_width(struct rt_mmcsd_host *host, rt_uint32_t width) mmcsd_set_iocfg(host); } +void mmcsd_set_timing(struct rt_mmcsd_host *host, rt_uint32_t timing) +{ + host->io_cfg.timing = timing; + mmcsd_set_iocfg(host); +} + void mmcsd_set_data_timeout(struct rt_mmcsd_data *data, const struct rt_mmcsd_card *card) { @@ -475,7 +483,7 @@ void mmcsd_set_data_timeout(struct rt_mmcsd_data *data, timeout_us = data->timeout_ns / 1000; timeout_us += data->timeout_clks * 1000 / - (card->host->io_cfg.clock / 1000); + (card->host->io_cfg.clock / 1000); if (data->flags & DATA_DIR_WRITE) /* @@ -596,7 +604,7 @@ int mmcsd_wait_cd_changed(rt_int32_t timeout) struct rt_mmcsd_host *host; if (rt_mb_recv(&mmcsd_hotpluge_mb, (rt_ubase_t *)&host, timeout) == RT_EOK) { - if(host->card == RT_NULL) + if (host->card == RT_NULL) { return MMCSD_HOST_UNPLUGED; } @@ -667,7 +675,6 @@ void mmcsd_detect(void *param) continue; } mmcsd_host_unlock(host); - rt_mb_send(&mmcsd_hotpluge_mb, (rt_ubase_t)host); } else { @@ -691,6 +698,19 @@ void mmcsd_detect(void *param) } } +void mmcsd_host_init(struct rt_mmcsd_host *host) +{ + rt_memset(host, 0, sizeof(struct rt_mmcsd_host)); + strncpy(host->name, "sd", sizeof(host->name) - 1); + host->max_seg_size = 65535; + host->max_dma_segs = 1; + host->max_blk_size = 512; + host->max_blk_count = 4096; + + rt_mutex_init(&host->bus_lock, "sd_bus_lock", RT_IPC_FLAG_FIFO); + rt_sem_init(&host->sem_ack, "sd_ack", 0, RT_IPC_FLAG_FIFO); +} + struct rt_mmcsd_host *mmcsd_alloc_host(void) { struct rt_mmcsd_host *host; @@ -703,17 +723,7 @@ struct rt_mmcsd_host *mmcsd_alloc_host(void) return RT_NULL; } - rt_memset(host, 0, sizeof(struct rt_mmcsd_host)); - - host->max_seg_size = 65535; - host->max_dma_segs = 1; - host->max_blk_size = 512; - host->max_blk_count = 4096; - host->id = allocated_host_num; - allocated_host_num++; - - rt_mutex_init(&host->bus_lock, "sd_bus_lock", RT_IPC_FLAG_PRIO); - rt_sem_init(&host->sem_ack, "sd_ack", 0, RT_IPC_FLAG_FIFO); + mmcsd_host_init(host); return host; } @@ -725,6 +735,22 @@ void mmcsd_free_host(struct rt_mmcsd_host *host) rt_free(host); } +rt_int32_t mmcsd_excute_tuning(struct rt_mmcsd_card *card) +{ + struct rt_mmcsd_host *host = card->host; + rt_int32_t opcode; + + if (!host->ops->execute_tuning) + return RT_EOK; + + if (card->card_type == CARD_TYPE_MMC) + opcode = SEND_TUNING_BLOCK_HS200; + else + opcode = SEND_TUNING_BLOCK; + + return host->ops->execute_tuning(host, opcode);; +} + int rt_mmcsd_core_init(void) { rt_err_t ret; @@ -732,16 +758,16 @@ int rt_mmcsd_core_init(void) /* initialize detect SD cart thread */ /* initialize mailbox and create detect SD card thread */ ret = rt_mb_init(&mmcsd_detect_mb, "mmcsdmb", - &mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]), - RT_IPC_FLAG_FIFO); + &mmcsd_detect_mb_pool[0], sizeof(mmcsd_detect_mb_pool) / sizeof(mmcsd_detect_mb_pool[0]), + RT_IPC_FLAG_FIFO); RT_ASSERT(ret == RT_EOK); - ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb", - &mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]), - RT_IPC_FLAG_FIFO); + ret = rt_mb_init(&mmcsd_hotpluge_mb, "mmcsdhotplugmb", + &mmcsd_hotpluge_mb_pool[0], sizeof(mmcsd_hotpluge_mb_pool) / sizeof(mmcsd_hotpluge_mb_pool[0]), + RT_IPC_FLAG_FIFO); RT_ASSERT(ret == RT_EOK); - ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL, - &mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20); + ret = rt_thread_init(&mmcsd_detect_thread, "mmcsd_detect", mmcsd_detect, RT_NULL, + &mmcsd_stack[0], RT_MMCSD_STACK_SIZE, RT_MMCSD_THREAD_PREORITY, 20); if (ret == RT_EOK) { rt_thread_startup(&mmcsd_detect_thread); diff --git a/components/drivers/sdio/sd.c b/components/drivers/sdio/sd.c index fe63cb86b5..0f2c823235 100644 --- a/components/drivers/sdio/sd.c +++ b/components/drivers/sdio/sd.c @@ -119,6 +119,7 @@ static rt_int32_t mmcsd_parse_csd(struct rt_mmcsd_card *card) card->card_blksize = 512; card->card_capacity = (csd->c_size + 1) * 512; /* unit:KB */ + card->card_sec_cnt = card->card_capacity * 2; card->tacc_clks = 0; card->tacc_ns = 0; card->max_data_rate = tran_unit[csd->tran_speed&0x07] * tran_value[(csd->tran_speed&0x78)>>3]; diff --git a/components/drivers/sdio/sdio.c b/components/drivers/sdio/sdio.c index 7d500c70a5..5c04277549 100644 --- a/components/drivers/sdio/sdio.c +++ b/components/drivers/sdio/sdio.c @@ -743,11 +743,7 @@ static rt_int32_t sdio_set_highspeed(struct rt_mmcsd_card *card) rt_int32_t ret; rt_uint8_t speed; - if (!(card->host->flags & - (MMCSD_SUP_HIGHSPEED | - MMCSD_SUP_HIGHSPEED_DDR | - MMCSD_SUP_HIGHSPEED_HS200 | - MMCSD_SUP_HIGHSPEED_HS400))) + if (!(card->host->flags & MMCSD_SUP_HIGHSPEED)) return 0; if (!card->cccr.high_speed) diff --git a/components/drivers/serial/serial.c b/components/drivers/serial/serial.c index 25f06fa401..cce06217b5 100644 --- a/components/drivers/serial/serial.c +++ b/components/drivers/serial/serial.c @@ -70,7 +70,7 @@ static int serial_fops_open(struct dfs_fd *fd) rt_uint16_t flags = 0; rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; RT_ASSERT(device != RT_NULL); switch (fd->flags & O_ACCMODE) @@ -104,7 +104,7 @@ static int serial_fops_close(struct dfs_fd *fd) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; rt_device_set_rx_indicate(device, RT_NULL); rt_device_close(device); @@ -118,7 +118,7 @@ static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args) int flags = (int)(rt_base_t)args; int mask = O_NONBLOCK | O_APPEND; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; switch (cmd) { case FIONREAD: @@ -139,12 +139,13 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count) { int size = 0; rt_device_t device; + int wait_ret; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; do { - size = rt_device_read(device, -1, buf, count); + size = rt_device_read(device, -1, buf, count); if (size <= 0) { if (fd->flags & O_NONBLOCK) @@ -153,10 +154,18 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count) break; } - rt_wqueue_wait(&(device->wait_queue), 0, RT_WAITING_FOREVER); + wait_ret = rt_wqueue_wait_interruptible(&(device->wait_queue), 0, RT_WAITING_FOREVER); + if (wait_ret != RT_EOK) + { + break; + } } }while (size <= 0); + if (size < 0) + { + size = 0; + } return size; } @@ -164,7 +173,7 @@ static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; return rt_device_write(device, -1, buf, count); } @@ -175,7 +184,7 @@ static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req) rt_device_t device; struct rt_serial_device *serial; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; RT_ASSERT(device != RT_NULL); serial = (struct rt_serial_device *)device; @@ -590,6 +599,8 @@ static rt_err_t rt_serial_init(struct rt_device *dev) serial->serial_rx = RT_NULL; serial->serial_tx = RT_NULL; + rt_memset(&serial->rx_notify, 0, sizeof(struct rt_device_notify)); + /* apply configuration */ if (serial->ops->configure) result = serial->ops->configure(serial, &serial->config); @@ -1026,7 +1037,19 @@ static rt_err_t rt_serial_control(struct rt_device *dev, serial->ops->configure(serial, (struct serial_configure *) args); } } + break; + case RT_DEVICE_CTRL_NOTIFY_SET: + if (args) + { + rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify)); + } + break; + case RT_DEVICE_CTRL_CONSOLE_OFLAG: + if (args) + { + *(rt_uint16_t*)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM; + } break; #ifdef RT_USING_POSIX_STDIO #ifdef RT_USING_POSIX_TERMIOS @@ -1345,6 +1368,10 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) serial->parent.rx_indicate(&serial->parent, rx_length); } } + if (serial->rx_notify.notify) + { + serial->rx_notify.notify(serial->rx_notify.dev); + } break; } case RT_SERIAL_EVENT_TX_DONE: diff --git a/components/drivers/serial/serial_v2.c b/components/drivers/serial/serial_v2.c index f608999f19..fc03605c1d 100644 --- a/components/drivers/serial/serial_v2.c +++ b/components/drivers/serial/serial_v2.c @@ -49,7 +49,7 @@ static int serial_fops_open(struct dfs_fd *fd) rt_uint16_t flags = 0; rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; RT_ASSERT(device != RT_NULL); switch (fd->flags & O_ACCMODE) @@ -83,7 +83,7 @@ static int serial_fops_close(struct dfs_fd *fd) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; rt_device_set_rx_indicate(device, RT_NULL); rt_device_close(device); @@ -97,7 +97,7 @@ static int serial_fops_ioctl(struct dfs_fd *fd, int cmd, void *args) int flags = (int)(rt_base_t)args; int mask = O_NONBLOCK | O_APPEND; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; switch (cmd) { case FIONREAD: @@ -119,7 +119,7 @@ static int serial_fops_read(struct dfs_fd *fd, void *buf, size_t count) int size = 0; rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; do { @@ -143,7 +143,7 @@ static int serial_fops_write(struct dfs_fd *fd, const void *buf, size_t count) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; return rt_device_write(device, -1, buf, count); } @@ -154,7 +154,7 @@ static int serial_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req) rt_device_t device; struct rt_serial_device *serial; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; RT_ASSERT(device != RT_NULL); serial = (struct rt_serial_device *)device; @@ -889,6 +889,8 @@ static rt_err_t rt_serial_tx_disable(struct rt_device *dev, rt_free(tx_fifo); serial->serial_tx = RT_NULL; + rt_memset(&serial->rx_notify, 0, sizeof(struct rt_device_notify)); + return RT_EOK; } @@ -1138,7 +1140,19 @@ static rt_err_t rt_serial_control(struct rt_device *dev, serial->config = *pconfig; serial->ops->configure(serial, (struct serial_configure *) args); } + break; + case RT_DEVICE_CTRL_NOTIFY_SET: + if (args) + { + rt_memcpy(&serial->rx_notify, args, sizeof(struct rt_device_notify)); + } + break; + case RT_DEVICE_CTRL_CONSOLE_OFLAG: + if (args) + { + *(rt_uint16_t*)args = RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX | RT_DEVICE_FLAG_STREAM; + } break; #ifdef RT_USING_POSIX_STDIO #ifdef RT_USING_POSIX_TERMIOS @@ -1512,6 +1526,11 @@ void rt_hw_serial_isr(struct rt_serial_device *serial, int event) /* Trigger the receiving completion callback */ if (serial->parent.rx_indicate != RT_NULL) serial->parent.rx_indicate(&(serial->parent), rx_length); + + if (serial->rx_notify.notify) + { + serial->rx_notify.notify(serial->rx_notify.dev); + } break; } diff --git a/components/drivers/tty/SConscript b/components/drivers/tty/SConscript new file mode 100644 index 0000000000..3c561f4e46 --- /dev/null +++ b/components/drivers/tty/SConscript @@ -0,0 +1,10 @@ +from building import * + +# The set of source files associated with this SConscript file. +src = Glob('*.c') +cwd = GetCurrentDir() +CPPPATH = [cwd + "/include"] + +group = DefineGroup('tty', src, depend = ['RT_USING_LWP', 'RT_USING_TTY'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/tty/console.c b/components/drivers/tty/console.c new file mode 100644 index 0000000000..dfab484f2e --- /dev/null +++ b/components/drivers/tty/console.c @@ -0,0 +1,325 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#include <rtthread.h> +#include <dfs_file.h> +#include <dfs_fs.h> +#include <tty.h> + +#define DBG_TAG "CONSOLE" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +static struct tty_struct console_driver; + +static void console_rx_notify(struct rt_device *dev) +{ + struct tty_struct *console = NULL; + int len = 0; + int lens = 0; + char ch = 0; + char buf[1024] = {0}; + + console = (struct tty_struct *)dev; + RT_ASSERT(console != RT_NULL); + + while (1) + { + len = rt_device_read(console->driver, -1, &ch, 1); + if (len == 0) + { + break; + } + lens += len; + buf[lens-1] = ch; + if (lens > 1024) + { + break; + } + } + + if (console->ldisc->ops->receive_buf) + { + console->ldisc->ops->receive_buf((struct tty_struct *)console, buf, lens); + } +} + +struct tty_struct *console_tty_get(void) +{ + return &console_driver; +} + +static void iodev_close(struct tty_struct *console) +{ + struct rt_device_notify rx_notify; + + rx_notify.notify = RT_NULL; + rx_notify.dev = RT_NULL; + + /* clear notify */ + rt_device_control(console->driver, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify); + rt_device_close(console->driver); +} + +static rt_err_t iodev_open(struct tty_struct *console) +{ + rt_err_t ret = RT_EOK; + struct rt_device_notify rx_notify; + rt_uint16_t oflags = 0; + + rt_device_control(console->driver, RT_DEVICE_CTRL_CONSOLE_OFLAG, &oflags); + + ret = rt_device_open(console->driver, oflags); + if (ret != RT_EOK) + { + return RT_ERROR; + } + + rx_notify.notify = console_rx_notify; + rx_notify.dev = (struct rt_device *)console; + rt_device_control(console->driver, RT_DEVICE_CTRL_NOTIFY_SET, &rx_notify); + return RT_EOK; +} + +struct rt_device *console_get_iodev(void) +{ + rt_base_t level = 0; + struct rt_device *iodev = RT_NULL; + + level = rt_hw_interrupt_disable(); + iodev = console_driver.driver; + rt_hw_interrupt_enable(level); + return iodev; +} + +struct rt_device *console_set_iodev(struct rt_device *iodev) +{ + rt_base_t level = 0; + struct rt_device *io_before = RT_NULL; + struct tty_struct *console = RT_NULL; + + RT_ASSERT(iodev != RT_NULL); + + console = &console_driver; + + level = rt_hw_interrupt_disable(); + + RT_ASSERT(console->init_flag >= TTY_INIT_FLAG_REGED); + + io_before = console->driver; + + if (iodev == io_before) + { + goto exit; + } + + if (console->init_flag >= TTY_INIT_FLAG_INITED) + { + /* close old device */ + iodev_close(console); + } + + console->driver = iodev; + + if (console->init_flag >= TTY_INIT_FLAG_INITED) + { + rt_err_t ret; + /* open new device */ + ret = iodev_open(console); + RT_ASSERT(ret == RT_EOK); + } + +exit: + rt_hw_interrupt_enable(level); + return io_before; +} + +/* RT-Thread Device Interface */ +/* + * This function initializes console device. + */ +static rt_err_t rt_console_init(struct rt_device *dev) +{ + rt_base_t level = 0; + rt_err_t result = RT_EOK; + struct tty_struct *console = RT_NULL; + + RT_ASSERT(dev != RT_NULL); + + console = (struct tty_struct *)dev; + + level = rt_hw_interrupt_disable(); + + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_REGED); + + result = iodev_open(console); + if (result != RT_EOK) + { + goto exit; + } + + console->init_flag = TTY_INIT_FLAG_INITED; +exit: + rt_hw_interrupt_enable(level); + return result; +} + +static rt_err_t rt_console_open(struct rt_device *dev, rt_uint16_t oflag) +{ + rt_err_t result = RT_EOK; + struct tty_struct *console = RT_NULL; + + RT_ASSERT(dev != RT_NULL); + console = (struct tty_struct *)dev; + RT_ASSERT(console != RT_NULL); + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED); + return result; +} + +static rt_err_t rt_console_close(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + struct tty_struct *console = RT_NULL; + + console = (struct tty_struct *)dev; + RT_ASSERT(console != RT_NULL); + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED); + return result; +} + +static rt_size_t rt_console_read(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + rt_size_t len = 0; + return len; +} + +static rt_size_t rt_console_write(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + rt_size_t len = 0; + struct tty_struct *console = RT_NULL; + + console = (struct tty_struct *)dev; + RT_ASSERT(console != RT_NULL); + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED); + + len = rt_device_write((struct rt_device *)console->driver, -1, buffer, size); + + return len; +} + +static rt_err_t rt_console_control(rt_device_t dev, int cmd, void *args) +{ + rt_size_t len = 0; + struct tty_struct *console = RT_NULL; + + console = (struct tty_struct *)dev; + RT_ASSERT(console != RT_NULL); + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_INITED); + + len = rt_device_control((struct rt_device *)console->driver, cmd, args); + + return len; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops console_ops = +{ + rt_console_init, + rt_console_open, + rt_console_close, + rt_console_read, + rt_console_write, + rt_console_control, +}; +#endif +/* + * console register + */ + static struct dfs_file_ops con_fops; +rt_err_t console_register(const char *name, struct rt_device *iodev) +{ + rt_base_t level = 0; + rt_err_t ret = RT_EOK; + struct rt_device *device = RT_NULL; + struct tty_struct *console = &console_driver; + + level = rt_hw_interrupt_disable(); + RT_ASSERT(console->init_flag == TTY_INIT_FLAG_NONE); + RT_ASSERT(iodev != RT_NULL); + + device = &(console->parent); + + device->type = RT_Device_Class_Char; + +#ifdef RT_USING_DEVICE_OPS + device->ops = &console_ops; +#else + device->init = rt_console_init; + device->open = rt_console_open; + device->close = rt_console_close; + device->read = rt_console_read; + device->write = rt_console_write; + device->control = rt_console_control; +#endif + + + /* register a character device */ + ret = rt_device_register(device, name, 0); + if (ret != RT_EOK) + { + LOG_E("console driver register fail\n"); + goto exit; + } + +#ifdef RT_USING_POSIX_DEVIO + /* set fops */ + console_set_fops(&con_fops); + device->fops = &con_fops; +#endif + console->type = TTY_DRIVER_TYPE_CONSOLE; + console->subtype = SERIAL_TYPE_NORMAL; + console->driver = iodev; + console->head = rt_calloc(1, sizeof(struct tty_node)); + if (!console->head) + { + return -RT_ENOMEM; + } + tty_initstack(console->head); + + rt_mutex_init(&console->lock, "ttylock", RT_IPC_FLAG_PRIO); + console->pgrp = -1; + console->session = -1; + console->foreground = RT_NULL; + rt_wqueue_init(&console->wait_queue); + + tty_ldisc_init(console); + +extern struct termios tty_std_termios; + console->init_termios = tty_std_termios; + console->init_termios.c_cflag = + B9600 | CS8 | CREAD | HUPCL; /* is normally B9600 default... */ + console->init_termios.__c_ispeed = 9600; + console->init_termios.__c_ospeed = 9600; + + console_ldata_init(console); + console->init_flag = TTY_INIT_FLAG_REGED; +exit: + rt_hw_interrupt_enable(level); + return ret; +} diff --git a/components/drivers/tty/include/console.h b/components/drivers/tty/include/console.h new file mode 100644 index 0000000000..779d816d4b --- /dev/null +++ b/components/drivers/tty/include/console.h @@ -0,0 +1,19 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#ifndef __CONSOLE_ +#define __CONSOLE_ +#include <rtthread.h> +#include "tty.h" + +struct tty_struct *console_tty_get(void); +struct rt_device *console_get_iodev(void); +struct rt_device *console_set_iodev(struct rt_device *iodev); +rt_err_t console_register(const char *name, struct rt_device *iodev); +#endif diff --git a/components/drivers/tty/include/tty.h b/components/drivers/tty/include/tty.h new file mode 100644 index 0000000000..2040cc45f3 --- /dev/null +++ b/components/drivers/tty/include/tty.h @@ -0,0 +1,490 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#ifndef __TTY_H__ +#define __TTY_H__ +#include <rthw.h> +#include <rtthread.h> +#include <tty_ldisc.h> +#ifdef RT_USING_LWP +#include <lwp.h> +#endif +#if defined(RT_USING_POSIX_TERMIOS) +#include <poll.h> +#include <termios.h> +#endif + +#ifndef ENOIOCTLCMD +#define ENOIOCTLCMD (515) /* No ioctl command */ +#endif + +#define current lwp_self() +#define __DISABLED_CHAR '\0' + +struct tty_node +{ + struct rt_lwp *lwp; + struct tty_node *next; +}; + +void tty_initstack(struct tty_node *node); +int tty_push(struct tty_node **head, struct rt_lwp *lwp); +struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp); +/* + * When a break, frame error, or parity error happens, these codes are + * stuffed into the flags buffer. + */ +#define TTY_NORMAL 0 +#define TTY_BREAK 1 +#define TTY_FRAME 2 +#define TTY_PARITY 3 +#define TTY_OVERRUN 4 + +#define INTR_CHAR(tty) ((tty)->init_termios.c_cc[VINTR]) +#define QUIT_CHAR(tty) ((tty)->init_termios.c_cc[VQUIT]) +#define ERASE_CHAR(tty) ((tty)->init_termios.c_cc[VERASE]) +#define KILL_CHAR(tty) ((tty)->init_termios.c_cc[VKILL]) +#define EOF_CHAR(tty) ((tty)->init_termios.c_cc[VEOF]) +#define TIME_CHAR(tty) ((tty)->init_termios.c_cc[VTIME]) +#define MIN_CHAR(tty) ((tty)->init_termios.c_cc[VMIN]) +#define SWTC_CHAR(tty) ((tty)->init_termios.c_cc[VSWTC]) +#define START_CHAR(tty) ((tty)->init_termios.c_cc[VSTART]) +#define STOP_CHAR(tty) ((tty)->init_termios.c_cc[VSTOP]) +#define SUSP_CHAR(tty) ((tty)->init_termios.c_cc[VSUSP]) +#define EOL_CHAR(tty) ((tty)->init_termios.c_cc[VEOL]) +#define REPRINT_CHAR(tty) ((tty)->init_termios.c_cc[VREPRINT]) +#define DISCARD_CHAR(tty) ((tty)->init_termios.c_cc[VDISCARD]) +#define WERASE_CHAR(tty) ((tty)->init_termios.c_cc[VWERASE]) +#define LNEXT_CHAR(tty) ((tty)->init_termios.c_cc[VLNEXT]) +#define EOL2_CHAR(tty) ((tty)->init_termios.c_cc[VEOL2]) + +#define _I_FLAG(tty,f) ((tty)->init_termios.c_iflag & (f)) +#define _O_FLAG(tty,f) ((tty)->init_termios.c_oflag & (f)) +#define _C_FLAG(tty,f) ((tty)->init_termios.c_cflag & (f)) +#define _L_FLAG(tty,f) ((tty)->init_termios.c_lflag & (f)) + +#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK) +#define I_BRKINT(tty) _I_FLAG((tty),BRKINT) +#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR) +#define I_PARMRK(tty) _I_FLAG((tty),PARMRK) +#define I_INPCK(tty) _I_FLAG((tty),INPCK) +#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP) +#define I_INLCR(tty) _I_FLAG((tty),INLCR) +#define I_IGNCR(tty) _I_FLAG((tty),IGNCR) +#define I_ICRNL(tty) _I_FLAG((tty),ICRNL) +#define I_IUCLC(tty) _I_FLAG((tty),IUCLC) +#define I_IXON(tty) _I_FLAG((tty),IXON) +#define I_IXANY(tty) _I_FLAG((tty),IXANY) +#define I_IXOFF(tty) _I_FLAG((tty),IXOFF) +#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL) +#define I_IUTF8(tty) _I_FLAG((tty), IUTF8) + +#define O_OPOST(tty) _O_FLAG((tty),OPOST) +#define O_OLCUC(tty) _O_FLAG((tty),OLCUC) +#define O_ONLCR(tty) _O_FLAG((tty),ONLCR) +#define O_OCRNL(tty) _O_FLAG((tty),OCRNL) +#define O_ONOCR(tty) _O_FLAG((tty),ONOCR) +#define O_ONLRET(tty) _O_FLAG((tty),ONLRET) +#define O_OFILL(tty) _O_FLAG((tty),OFILL) +#define O_OFDEL(tty) _O_FLAG((tty),OFDEL) +#define O_NLDLY(tty) _O_FLAG((tty),NLDLY) +#define O_CRDLY(tty) _O_FLAG((tty),CRDLY) +#define O_TABDLY(tty) _O_FLAG((tty),TABDLY) +#define O_BSDLY(tty) _O_FLAG((tty),BSDLY) +#define O_VTDLY(tty) _O_FLAG((tty),VTDLY) +#define O_FFDLY(tty) _O_FLAG((tty),FFDLY) + +#define C_BAUD(tty) _C_FLAG((tty),CBAUD) +#define C_CSIZE(tty) _C_FLAG((tty),CSIZE) +#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB) +#define C_CREAD(tty) _C_FLAG((tty),CREAD) +#define C_PARENB(tty) _C_FLAG((tty),PARENB) +#define C_PARODD(tty) _C_FLAG((tty),PARODD) +#define C_HUPCL(tty) _C_FLAG((tty),HUPCL) +#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL) +#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD) +#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS) + +#define L_ISIG(tty) _L_FLAG((tty),ISIG) +#define L_ICANON(tty) _L_FLAG((tty),ICANON) +#define L_XCASE(tty) _L_FLAG((tty),XCASE) +#define L_ECHO(tty) _L_FLAG((tty),ECHO) +#define L_ECHOE(tty) _L_FLAG((tty),ECHOE) +#define L_ECHOK(tty) _L_FLAG((tty),ECHOK) +#define L_ECHONL(tty) _L_FLAG((tty),ECHONL) +#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH) +#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP) +#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL) +#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT) +#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE) +#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO) +#define L_PENDIN(tty) _L_FLAG((tty),PENDIN) +#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN) +#define L_EXTPROC(tty) _L_FLAG((tty), EXTPROC) + +/* + * Where all of the state associated with a tty is kept while the tty + * is open. Since the termios state should be kept even if the tty + * has been closed --- for things like the baud rate, etc --- it is + * not stored here, but rather a pointer to the real state is stored + * here. Possible the winsize structure should have the same + * treatment, but (1) the default 80x24 is usually right and (2) it's + * most often used by a windowing system, which will set the correct + * size each time the window is created or resized anyway. + * - TYT, 9/14/92 + */ +struct tty_struct +{ + struct rt_device parent; + int type; + int subtype; + int init_flag; + int index; //for pty + int pts_lock; //for pty + + struct tty_struct *other_struct; //for pty + + struct termios init_termios; + struct winsize winsize; + struct rt_mutex lock; + pid_t pgrp; + pid_t session; + struct rt_lwp *foreground; + struct tty_node *head; + + struct tty_ldisc *ldisc; + void *disc_data; + struct rt_device *driver; + + struct rt_wqueue wait_queue; + +#define RT_TTY_BUF 1024 + rt_list_t tty_drivers; +}; + +enum +{ + TTY_INIT_FLAG_NONE = 0, + TTY_INIT_FLAG_ALLOCED, + TTY_INIT_FLAG_REGED, + TTY_INIT_FLAG_INITED, +}; + +#define TTY_DRIVER_TYPE_SYSTEM 0x0001 +#define TTY_DRIVER_TYPE_CONSOLE 0x0002 +#define TTY_DRIVER_TYPE_SERIAL 0x0003 +#define TTY_DRIVER_TYPE_PTY 0x0004 +#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */ +#define TTY_DRIVER_TYPE_SYSCONS 0x0006 + +/* tty magic number */ +#define TTY_MAGIC 0x5401 + +/* + * These bits are used in the flags field of the tty structure. + * + * So that interrupts won't be able to mess up the queues, + * copy_to_cooked must be atomic with respect to itself, as must + * tty->write. Thus, you must use the inline functions set_bit() and + * clear_bit() to make things atomic. + */ +#define TTY_THROTTLED 0 +#define TTY_IO_ERROR 1 +#define TTY_OTHER_CLOSED 2 +#define TTY_EXCLUSIVE 3 +#define TTY_DEBUG 4 +#define TTY_DO_WRITE_WAKEUP 5 +#define TTY_PUSH 6 +#define TTY_CLOSING 7 +#define TTY_DONT_FLIP 8 +#define TTY_HW_COOK_OUT 14 +#define TTY_HW_COOK_IN 15 +#define TTY_PTY_LOCK 16 +#define TTY_NO_WRITE_SPLIT 17 + +/* + * These bits are used in the flags field of the tty structure. + * + * So that interrupts won't be able to mess up the queues, + * copy_to_cooked must be atomic with respect to itself, as must + * tty->write. Thus, you must use the inline functions set_bit() and + * clear_bit() to make things atomic. + */ +#define TTY_THROTTLED 0 +#define TTY_IO_ERROR 1 +#define TTY_OTHER_CLOSED 2 +#define TTY_EXCLUSIVE 3 +#define TTY_DEBUG 4 +#define TTY_DO_WRITE_WAKEUP 5 +#define TTY_PUSH 6 +#define TTY_CLOSING 7 +#define TTY_DONT_FLIP 8 +#define TTY_HW_COOK_OUT 14 +#define TTY_HW_COOK_IN 15 +#define TTY_PTY_LOCK 16 +#define TTY_NO_WRITE_SPLIT 17 + +#define NR_LDISCS 30 + +/* line disciplines */ +#define N_TTY 0 +#define N_SLIP 1 +#define N_MOUSE 2 +#define N_PPP 3 +#define N_STRIP 4 +#define N_AX25 5 +#define N_X25 6 /* X.25 async */ +#define N_6PACK 7 +#define N_MASC 8 /* Reserved for Mobitex module <[email protected]> */ +#define N_R3964 9 /* Reserved for Simatic R3964 module */ +#define N_PROFIBUS_FDL 10 /* Reserved for Profibus */ +#define N_IRDA 11 /* Linux IrDa - http://irda.sourceforge.net/ */ +#define N_SMSBLOCK 12 /* SMS block mode - for talking to GSM data */ + /* cards about SMS messages */ +#define N_HDLC 13 /* synchronous HDLC */ +#define N_SYNC_PPP 14 /* synchronous PPP */ +#define N_HCI 15 /* Bluetooth HCI UART */ +#define N_GIGASET_M101 16 /* Siemens Gigaset M101 serial DECT adapter */ +#define N_SLCAN 17 /* Serial / USB serial CAN Adaptors */ +#define N_PPS 18 /* Pulse per Second */ +#define N_V253 19 /* Codec control over voice modem */ +#define N_CAIF 20 /* CAIF protocol for talking to modems */ +#define N_GSM0710 21 /* GSM 0710 Mux */ +#define N_TI_WL 22 /* for TI's WL BT, FM, GPS combo chips */ +#define N_TRACESINK 23 /* Trace data routing for MIPI P1149.7 */ +#define N_TRACEROUTER 24 /* Trace data routing for MIPI P1149.7 */ +#define N_NCI 25 /* NFC NCI UART */ + +/* Used for packet mode */ +#define TIOCPKT_DATA 0 +#define TIOCPKT_FLUSHREAD 1 +#define TIOCPKT_FLUSHWRITE 2 +#define TIOCPKT_STOP 4 +#define TIOCPKT_START 8 +#define TIOCPKT_NOSTOP 16 +#define TIOCPKT_DOSTOP 32 + +/* tty driver types */ +#define TTY_DRIVER_TYPE_SYSTEM 0x0001 +#define TTY_DRIVER_TYPE_CONSOLE 0x0002 +#define TTY_DRIVER_TYPE_SERIAL 0x0003 +#define TTY_DRIVER_TYPE_PTY 0x0004 +#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */ +#define TTY_DRIVER_TYPE_SYSCONS 0x0006 + +/* pty subtypes */ +#define PTY_TYPE_MASTER 0x0001 +#define PTY_TYPE_SLAVE 0x0002 + +/* serial subtype definitions */ +#define SERIAL_TYPE_NORMAL 1 + +#define max(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a > _b ? _a : _b; }) + +#define min(a, b) ({\ + typeof(a) _a = a;\ + typeof(b) _b = b;\ + _a < _b ? _a : _b; }) + +void tty_set_fops(struct dfs_file_ops *fops); +void console_set_fops(struct dfs_file_ops *fops); +void mutex_lock(rt_mutex_t mutex); +void mutex_unlock(rt_mutex_t mutex); +int __tty_check_change(struct tty_struct *tty, int sig); +int tty_check_change(struct tty_struct *tty); + +rt_inline struct rt_wqueue *wait_queue_get(struct rt_lwp *lwp, struct tty_struct *tty) +{ + if (lwp == RT_NULL) + { + return &tty->wait_queue; + } + return &lwp->wait_queue; +} + +rt_inline struct rt_wqueue *wait_queue_current_get(struct rt_lwp *lwp, struct tty_struct *tty) +{ + return wait_queue_get(lwp, tty); +} + +rt_inline void tty_wakeup_check(struct tty_struct *tty) +{ + struct rt_wqueue *wq = NULL; + + wq = wait_queue_current_get(tty->foreground, tty); + rt_wqueue_wakeup(wq, (void*)POLLIN); +} + +rt_inline int set_bit(int nr,int *addr) +{ + int mask, retval, level; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + level = rt_hw_interrupt_disable(); + retval = (mask & *addr) != 0; + *addr |= mask; + rt_hw_interrupt_enable(level); + return retval; +} + +rt_inline int clear_bit(int nr, int *addr) +{ + int mask, retval, level; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + level = rt_hw_interrupt_disable(); + retval = (mask & *addr) != 0; + *addr &= ~mask; + rt_hw_interrupt_enable(level); + return retval; +} + +rt_inline int test_bit(int nr, int *addr) +{ + int mask; + + addr += nr >> 5; + mask = 1 << (nr & 0x1f); + return ((mask & *addr) != 0); +} + +rt_inline int test_and_clear_bit(int nr, volatile void *addr) +{ + int mask, retval, level; + volatile unsigned int *a = addr; + + a += nr >> 5; + mask = 1 << (nr & 0x1f); + level = rt_hw_interrupt_disable(); + retval = (mask & *a) != 0; + *a &= ~mask; + rt_hw_interrupt_enable(level); + + return retval; +} + +rt_inline unsigned long __ffs(unsigned long word) +{ + int num = 0; + +#if BITS_PER_LONG == 64 + if ((word & 0xffffffff) == 0) + { + num += 32; + word >>= 32; + } +#endif + if ((word & 0xffff) == 0) + { + num += 16; + word >>= 16; + } + if ((word & 0xff) == 0) + { + num += 8; + word >>= 8; + } + if ((word & 0xf) == 0) + { + num += 4; + word >>= 4; + } + if ((word & 0x3) == 0) + { + num += 2; + word >>= 2; + } + if ((word & 0x1) == 0) + { + num += 1; + } + + return num; +} +#define BITS_PER_LONG 32 +#define BITOP_WORD(nr) ((nr) / BITS_PER_LONG) + +/* + * Find the next set bit in a memory region. + */ +rt_inline unsigned long find_next_bit(const unsigned long *addr, unsigned long size, + unsigned long offset) +{ + const unsigned long *p = addr + BITOP_WORD(offset); + unsigned long result = offset & ~(BITS_PER_LONG-1); + unsigned long tmp; + + if (offset >= size) + { + return size; + } + + size -= result; + offset %= BITS_PER_LONG; + if (offset) + { + tmp = *(p++); + tmp &= (~0UL << offset); + if (size < BITS_PER_LONG) + { + goto found_first; + } + + if (tmp) + { + goto found_middle; + } + + size -= BITS_PER_LONG; + result += BITS_PER_LONG; + } + + while (size & ~(BITS_PER_LONG-1)) + { + if ((tmp = *(p++))) + { + goto found_middle; + } + + result += BITS_PER_LONG; + size -= BITS_PER_LONG; + } + + if (!size) + { + return result; + } + + tmp = *p; + +found_first: + tmp &= (~0UL >> (BITS_PER_LONG - size)); + if (tmp == 0UL) /* Are any bits set? */ + { + return result + size; /* Nope. */ + } + +found_middle: + return result + __ffs(tmp); +} + +/*create by tty_ioctl.c*/ +int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *arg); + +/*create by n_tty.c*/ +void console_ldata_init(struct tty_struct *tty); +int n_tty_receive_buf(struct tty_struct *tty, char *cp, int count); +void n_tty_init(void); + +#endif /*__TTY_H__*/ diff --git a/components/drivers/tty/include/tty_ldisc.h b/components/drivers/tty/include/tty_ldisc.h new file mode 100644 index 0000000000..919805f645 --- /dev/null +++ b/components/drivers/tty/include/tty_ldisc.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#ifndef __TTY_LDISC_ +#define __TTY_LDISC_ +#include <rtthread.h> +#include <dfs.h> +#include <dfs_fs.h> +#include <tty.h> +#if defined(RT_USING_POSIX_TERMIOS) +#include <termios.h> +#endif +struct tty_struct; + +struct tty_ldisc_ops +{ + char *name; + int num; + + int (*open) (struct dfs_fd *fd); + int (*close) (struct tty_struct *tty); + int (*ioctl) (struct dfs_fd *fd, int cmd, void *args); + int (*read) (struct dfs_fd *fd, void *buf, size_t count); + int (*write) (struct dfs_fd *fd, const void *buf, size_t count); + int (*flush) (struct dfs_fd *fd); + int (*lseek) (struct dfs_fd *fd, off_t offset); + int (*getdents) (struct dfs_fd *fd, struct dirent *dirp, uint32_t count); + + int (*poll) (struct dfs_fd *fd, struct rt_pollreq *req); + void (*set_termios) (struct tty_struct *tty, struct termios *old); + int (*receive_buf) (struct tty_struct *tty,char *cp, int count); + + int refcount; +}; + +struct tty_ldisc +{ + struct tty_ldisc_ops *ops; + struct tty_struct *tty; +}; + +#define TTY_LDISC_MAGIC 0x5403 + +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc); +void tty_ldisc_kill(struct tty_struct *tty); +void tty_ldisc_init(struct tty_struct *tty); +void tty_ldisc_release(struct tty_struct *tty); +void n_tty_init(void); + +#endif // __TTY_LDISC_ diff --git a/components/drivers/tty/n_tty.c b/components/drivers/tty/n_tty.c new file mode 100644 index 0000000000..658c6c0d9e --- /dev/null +++ b/components/drivers/tty/n_tty.c @@ -0,0 +1,2041 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#include <stddef.h> +#include <ctype.h> +#include <tty.h> +#include <lwp.h> +#if defined(RT_USING_POSIX_TERMIOS) +#include <termios.h> +#endif +#include <sys/errno.h> + +#define DBG_TAG "N_TTY" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +/* number of characters left in xmit buffer before select has we have room */ +#define WAKEUP_CHARS 256 + +/* + * This defines the low- and high-watermarks for throttling and + * unthrottling the TTY driver. These watermarks are used for + * controlling the space in the read buffer. + */ +#define TTY_THRESHOLD_THROTTLE 128 /* now based on remaining room */ +#define TTY_THRESHOLD_UNTHROTTLE 128 + +/* + * Special byte codes used in the echo buffer to represent operations + * or special handling of characters. Bytes in the echo buffer that + * are not part of such special blocks are treated as normal character + * codes. + */ +#define ECHO_OP_START 0xff +#define ECHO_OP_MOVE_BACK_COL 0x80 +#define ECHO_OP_SET_CANON_COL 0x81 +#define ECHO_OP_ERASE_TAB 0x82 + +#define ECHO_COMMIT_WATERMARK 256 +#define ECHO_BLOCK 256 +#define ECHO_DISCARD_WATERMARK RT_TTY_BUF - (ECHO_BLOCK + 32) + +rt_inline void tty_sigaddset(lwp_sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + + if (_LWP_NSIG_WORDS == 1) + { + set->sig[0] |= 1UL << sig; + } + else + { + set->sig[sig / _LWP_NSIG_BPW] |= 1UL << (sig % _LWP_NSIG_BPW); + } +} + +struct n_tty_data +{ + /* producer-published */ + size_t read_head; + size_t commit_head; + size_t canon_head; + size_t echo_head; + size_t echo_commit; + size_t echo_mark; + unsigned long char_map[256]; + + /* non-atomic */ + rt_bool_t no_room; + /* must hold exclusive termios_rwsem to reset these */ + unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1; + unsigned char push:1; + + /* shared by producer and consumer */ + char read_buf[RT_TTY_BUF]; + unsigned long read_flags[RT_TTY_BUF]; + unsigned char echo_buf[RT_TTY_BUF]; + + /* consumer-published */ + size_t read_tail; + size_t line_start; + + /* protected by output lock */ + unsigned int column; + unsigned int canon_column; + size_t echo_tail; + + rt_mutex_t atomic_read_lock; + rt_mutex_t output_lock; +}; + +static inline size_t read_cnt(struct n_tty_data *ldata) +{ + return ldata->read_head - ldata->read_tail; +} + +static inline char read_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->read_buf[i & (RT_TTY_BUF - 1)]; +} + +static inline char *read_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->read_buf[i & (RT_TTY_BUF - 1)]; +} + +static inline unsigned char echo_buf(struct n_tty_data *ldata, size_t i) +{ + return ldata->echo_buf[i & (RT_TTY_BUF - 1)]; +} + +static inline unsigned char *echo_buf_addr(struct n_tty_data *ldata, size_t i) +{ + return &ldata->echo_buf[i & (RT_TTY_BUF - 1)]; +} + +/** + * put_tty_queue - add character to tty + * @c: character + * @ldata: n_tty data + * + * Add a character to the tty read_buf queue. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + */ + +static inline void put_tty_queue(unsigned char c, struct n_tty_data *ldata) +{ + *read_buf_addr(ldata, ldata->read_head) = c; + ldata->read_head++; +} + +/** + * reset_buffer_flags - reset buffer state + * @tty: terminal to reset + * + * Reset the read buffer counters and clear the flags. + * Called from n_tty_open() and n_tty_flush_buffer(). + * + * Locking: caller holds exclusive termios_rwsem + * (or locking is not required) + */ + +static void reset_buffer_flags(struct n_tty_data *ldata) +{ + ldata->read_head = ldata->canon_head = ldata->read_tail = 0; + ldata->echo_head = ldata->echo_tail = ldata->echo_commit = 0; + ldata->commit_head = 0; + ldata->echo_mark = 0; + ldata->line_start = 0; + + ldata->erasing = 0; + rt_memset(ldata->read_flags, 0, RT_TTY_BUF); + ldata->push = 0; +} + +/** + * add_echo_byte - add a byte to the echo buffer + * @c: unicode byte to echo + * @ldata: n_tty data + * + * Add a character or operation byte to the echo buffer. + */ + +static inline void add_echo_byte(unsigned char c, struct n_tty_data *ldata) +{ + *echo_buf_addr(ldata, ldata->echo_head++) = c; +} + +/** + * echo_move_back_col - add operation to move back a column + * @ldata: n_tty data + * + * Add an operation to the echo buffer to move back one column. + */ + +static void echo_move_back_col(struct n_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_MOVE_BACK_COL, ldata); +} + +/** + * echo_set_canon_col - add operation to set the canon column + * @ldata: n_tty data + * + * Add an operation to the echo buffer to set the canon column + * to the current column. + */ + +static void echo_set_canon_col(struct n_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_SET_CANON_COL, ldata); +} + +/** + * echo_erase_tab - add operation to erase a tab + * @num_chars: number of character columns already used + * @after_tab: true if num_chars starts after a previous tab + * @ldata: n_tty data + * + * Add an operation to the echo buffer to erase a tab. + * + * Called by the eraser function, which knows how many character + * columns have been used since either a previous tab or the start + * of input. This information will be used later, along with + * canon column (if applicable), to go back the correct number + * of columns. + */ + +static void echo_erase_tab(unsigned int num_chars, int after_tab, + struct n_tty_data *ldata) +{ + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_ERASE_TAB, ldata); + + /* We only need to know this modulo 8 (tab spacing) */ + num_chars &= 7; + + /* Set the high bit as a flag if num_chars is after a previous tab */ + if (after_tab) + { + num_chars |= 0x80; + } + + add_echo_byte(num_chars, ldata); +} + +/** + * echo_char_raw - echo a character raw + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * This variant does not treat control characters specially. + */ + +static void echo_char_raw(unsigned char c, struct n_tty_data *ldata) +{ + if (c == ECHO_OP_START) + { + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); + } + else + { + add_echo_byte(c, ldata); + } +} + +/** + * echo_char - echo a character + * @c: unicode byte to echo + * @tty: terminal device + * + * Echo user input back onto the screen. This must be called only when + * L_ECHO(tty) is true. Called from the driver receive_buf path. + * + * This variant tags control characters to be echoed as "^X" + * (where X is the letter representing the control char). + */ + +static void echo_char(unsigned char c, struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (c == ECHO_OP_START) + { + add_echo_byte(ECHO_OP_START, ldata); + add_echo_byte(ECHO_OP_START, ldata); + } + else + { + if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') + { + add_echo_byte(ECHO_OP_START, ldata); + } + add_echo_byte(c, ldata); + } +} + +/** + * finish_erasing - complete erase + * @ldata: n_tty data + */ + +static inline void finish_erasing(struct n_tty_data *ldata) +{ + if (ldata->erasing) + { + echo_char_raw('/', ldata); + ldata->erasing = 0; + } +} + +/** + * is_utf8_continuation - utf8 multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character. We use this to correctly compute the on screen size + * of the character when printing + */ + +static inline int is_utf8_continuation(unsigned char c) +{ + return (c & 0xc0) == 0x80; +} + +/** + * is_continuation - multibyte check + * @c: byte to check + * + * Returns true if the utf8 character 'c' is a multibyte continuation + * character and the terminal is in unicode mode. + */ + +static inline int is_continuation(unsigned char c, struct tty_struct *tty) +{ + return I_IUTF8(tty) && is_utf8_continuation(c); +} + +/** + * eraser - handle erase function + * @c: character input + * @tty: terminal device + * + * Perform erase and necessary output when an erase character is + * present in the stream from the driver layer. Handles the complexities + * of UTF-8 multibyte symbols. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + */ + +static void eraser(unsigned char c, struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + enum { KERASE, WERASE, KILL } kill_type; + size_t head = 0; + size_t cnt = 0; + int seen_alnums = 0; + + if (ldata->read_head == ldata->canon_head) + { + /* process_output('\a', tty); */ /* what do you think? */ + return; + } + + if (c == ERASE_CHAR(tty)) + { + kill_type = KERASE; + } + else if (c == WERASE_CHAR(tty)) + { + kill_type = WERASE; + } + else + { + if (!L_ECHO(tty)) + { + ldata->read_head = ldata->canon_head; + return; + } + + if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) + { + ldata->read_head = ldata->canon_head; + finish_erasing(ldata); + echo_char(KILL_CHAR(tty), tty); + /* Add a newline if ECHOK is on and ECHOKE is off. */ + if (L_ECHOK(tty)) + { + echo_char_raw('\n', ldata); + } + return; + } + kill_type = KILL; + } + + seen_alnums = 0; + while (ldata->read_head != ldata->canon_head) + { + head = ldata->read_head; + + /* erase a single possibly multibyte character */ + do + { + head--; + c = read_buf(ldata, head); + } while (is_continuation(c, tty) && head != ldata->canon_head); + + /* do not partially erase */ + if (is_continuation(c, tty)) + { + break; + } + + if (kill_type == WERASE) + { + /* Equivalent to BSD's ALTWERASE. */ + if (isalnum(c) || c == '_') + { + seen_alnums++; + } + else if (seen_alnums) + { + break; + } + } + cnt = ldata->read_head - head; + ldata->read_head = head; + if (L_ECHO(tty)) + { + if (L_ECHOPRT(tty)) + { + if (!ldata->erasing) + { + echo_char_raw('\\', ldata); + ldata->erasing = 1; + } + /* if cnt > 1, output a multi-byte character */ + echo_char(c, tty); + while (--cnt > 0) + { + head++; + echo_char_raw(read_buf(ldata, head), ldata); + echo_move_back_col(ldata); + } + } + else if (kill_type == KERASE && !L_ECHOE(tty)) + { + echo_char(ERASE_CHAR(tty), tty); + } + else if (c == '\t') + { + unsigned int num_chars = 0; + int after_tab = 0; + size_t tail = ldata->read_head; + + /* + * Count the columns used for characters + * since the start of input or after a + * previous tab. + * This info is used to go back the correct + * number of columns. + */ + while (tail != ldata->canon_head) + { + tail--; + c = read_buf(ldata, tail); + if (c == '\t') + { + after_tab = 1; + break; + } + else if (iscntrl(c)) + { + if (L_ECHOCTL(tty)) + { + num_chars += 2; + } + } + else if (!is_continuation(c, tty)) + { + num_chars++; + } + } + echo_erase_tab(num_chars, after_tab, ldata); + } + else + { + if (iscntrl(c) && L_ECHOCTL(tty)) + { + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); + } + if (!iscntrl(c) || L_ECHOCTL(tty)) + { + echo_char_raw('\b', ldata); + echo_char_raw(' ', ldata); + echo_char_raw('\b', ldata); + } + } + } + if (kill_type == KERASE) + { + break; + } + } + if (ldata->read_head == ldata->canon_head && L_ECHO(tty)) + { + finish_erasing(ldata); + } +} + +/** + * isig - handle the ISIG optio + * @sig: signal + * @tty: terminal + * + * Called when a signal is being sent due to terminal input. + * Called from the driver receive_buf path so serialized. + * + * Performs input and output flush if !NOFLSH. In this context, the echo + * buffer is 'output'. The signal is processed first to alert any current + * readers or writers to discontinue and exit their i/o loops. + * + * Locking: ctrl_lock + */ + +static void __isig(int sig, struct tty_struct *tty) +{ + struct rt_lwp *lwp = tty->foreground; + struct tty_ldisc *ld = RT_NULL; + struct termios old_termios; + struct termios *new_termios = get_old_termios(); + + if (lwp) + { + if (sig == SIGTSTP) + { + struct rt_lwp *old_lwp; + + rt_memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios)); + tty->init_termios = *new_termios; + ld = tty->ldisc; + if (ld != RT_NULL) + { + if (ld->ops->set_termios) + { + ld->ops->set_termios(tty, &old_termios); + } + } + tty_sigaddset(&lwp->signal_mask, SIGTTOU); + old_lwp = tty_pop(&tty->head, RT_NULL); + tty->foreground = old_lwp; + } + else + { + lwp_kill(lwp_to_pid(lwp), sig); + } + } +} + +static void isig(int sig, struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (L_NOFLSH(tty)) + { + /* signal only */ + __isig(sig, tty); + + } + else + { /* signal and flush */ + __isig(sig, tty); + + /* clear echo buffer */ + ldata->echo_head = ldata->echo_tail = 0; + ldata->echo_mark = ldata->echo_commit = 0; + + /* clear input buffer */ + reset_buffer_flags(tty->disc_data); + } +} + +/** + * do_output_char - output one character + * @c: character (or partial unicode symbol) + * @tty: terminal device + * @space: space available in tty driver write buffer + * + * This is a helper function that handles one output character + * (including special characters like TAB, CR, LF, etc.), + * doing OPOST processing and putting the results in the + * tty driver's write buffer. + * + * Note that Linux currently ignores TABDLY, CRDLY, VTDLY, FFDLY + * and NLDLY. They simply aren't relevant in the world today. + * If you ever need them, add them here. + * + * Returns the number of bytes of buffer space used or -1 if + * no space left. + * + * Locking: should be called under the output_lock to protect + * the column state and space left in the buffer + */ + +static int do_output_char(unsigned char c, struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + int spaces = 0; + char *ch = RT_NULL; + + switch (c) + { + case '\n': + if (O_ONLRET(tty)) + { + ldata->column = 0; + } + + if (O_ONLCR(tty)) + { + ldata->canon_column = ldata->column = 0; + ch = "\r\n"; + rt_device_write((rt_device_t)tty, -1, ch, 2); + return 2; + } + ldata->canon_column = ldata->column; + break; + case '\r': + if (O_ONOCR(tty) && ldata->column == 0) + { + return 0; + } + + if (O_OCRNL(tty)) + { + c = '\n'; + if (O_ONLRET(tty)) + { + ldata->canon_column = ldata->column = 0; + } + break; + } + ldata->canon_column = ldata->column = 0; + break; + case '\t': + spaces = 8 - (ldata->column & 7); + if (O_TABDLY(tty) == XTABS) + { + ldata->column += spaces; + ch = " "; + rt_device_write((rt_device_t)tty, -1, &ch, spaces); + return spaces; + } + ldata->column += spaces; + break; + case '\b': + if (ldata->column > 0) + { + ldata->column--; + } + ch = "\b \b"; + rt_device_write((rt_device_t)tty, -1, ch, strlen(ch)); + return 1; + default: + if (!iscntrl(c)) + { + if (O_OLCUC(tty)) + { + c = toupper(c); + } + + if (!is_continuation(c, tty)) + { + ldata->column++; + } + } + break; + } + + rt_device_write((rt_device_t)tty, -1, &c, 1); + return 1; +} + +/** + * process_output - output post processor + * @c: character (or partial unicode symbol) + * @tty: terminal device + * + * Output one character with OPOST processing. + * Returns -1 when the output device is full and the character + * must be retried. + * + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) + */ + +static int process_output(unsigned char c, struct tty_struct *tty) +{ + int retval = 0; + + retval = do_output_char(c, tty); + if (retval < 0) + { + return -1; + } + else + { + return 0; + } +} + +/** + * process_output_block - block post processor + * @tty: terminal device + * @buf: character buffer + * @nr: number of bytes to output + * + * Output a block of characters with OPOST processing. + * Returns the number of characters output. + * + * This path is used to speed up block console writes, among other + * things when processing blocks of output data. It handles only + * the simple cases normally found and helps to generate blocks of + * symbols for the console driver and thus improve performance. + * + * Locking: output_lock to protect column state and space left + * (also, this is called from n_tty_write under the + * tty layer write lock) + */ + +static ssize_t process_output_block(struct tty_struct *tty, + const char *buf, unsigned int nr) +{ + struct n_tty_data *ldata = tty->disc_data; + int i = 0; + ssize_t size = 0; + const char *cp = RT_NULL; + + for (i = 0, cp = buf; i < nr; i++, cp++) + { + char c = *cp; + + switch (c) + { + case '\n': + if (O_ONLRET(tty)) + { + ldata->column = 0; + } + + if (O_ONLCR(tty)) + { + goto break_out; + } + ldata->canon_column = ldata->column; + break; + case '\r': + if (O_ONOCR(tty) && ldata->column == 0) + { + goto break_out; + } + + if (O_OCRNL(tty)) + { + goto break_out; + } + + ldata->canon_column = ldata->column = 0; + break; + case '\t': + goto break_out; + case '\b': + if (ldata->column > 0) + { + ldata->column--; + } + break; + default: + if (!iscntrl(c)) + { + if (O_OLCUC(tty)) + { + goto break_out; + } + + if (!is_continuation(c, tty)) + { + ldata->column++; + } + } + break; + } + } +break_out: + size = rt_device_write((rt_device_t)tty, -1, buf, i); + return size; +} + +static size_t __process_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t tail = 0; + unsigned char c = 0; + char ch = 0; + unsigned char num_chars = 0, num_bs = 0; + + tail = ldata->echo_tail; + while (ldata->echo_commit != tail) + { + c = echo_buf(ldata, tail); + if (c == ECHO_OP_START) + { + unsigned char op = 0; + + /* + * If the buffer byte is the start of a multi-byte + * operation, get the next byte, which is either the + * op code or a control character value. + */ + op = echo_buf(ldata, tail + 1); + + switch (op) + { + case ECHO_OP_ERASE_TAB: + num_chars = echo_buf(ldata, tail + 2); + + /* + * Determine how many columns to go back + * in order to erase the tab. + * This depends on the number of columns + * used by other characters within the tab + * area. If this (modulo 8) count is from + * the start of input rather than from a + * previous tab, we offset by canon column. + * Otherwise, tab spacing is normal. + */ + if (!(num_chars & 0x80)) + { + num_chars += ldata->canon_column; + } + num_bs = 8 - (num_chars & 7); + + while (num_bs--) + { + ch = '\b'; + rt_device_write((rt_device_t)tty, -1, &ch, 1); + if (ldata->column > 0) + { + ldata->column--; + } + } + tail += 3; + break; + + case ECHO_OP_SET_CANON_COL: + ldata->canon_column = ldata->column; + tail += 2; + break; + + case ECHO_OP_MOVE_BACK_COL: + if (ldata->column > 0) + { + ldata->column--; + } + tail += 2; + break; + + case ECHO_OP_START: + ch = ECHO_OP_START; + rt_device_write((rt_device_t)tty, -1, &ch, 1); + ldata->column++; + tail += 2; + break; + + default: + /* + * If the op is not a special byte code, + * it is a ctrl char tagged to be echoed + * as "^X" (where X is the letter + * representing the control char). + * Note that we must ensure there is + * enough space for the whole ctrl pair. + * + */ + ch = '^'; + rt_device_write((rt_device_t)tty, -1, &ch, 1); + ch = op ^ 0100; + rt_device_write((rt_device_t)tty, -1, &ch, 1); + ldata->column += 2; + tail += 2; + } + } + else + { + if (O_OPOST(tty)) + { + int retval = do_output_char(c, tty); + if (retval < 0) + { + break; + } + } + else + { + rt_device_write((rt_device_t)tty, -1, &c, 1); + } + tail += 1; + } + } + + /* If the echo buffer is nearly full (so that the possibility exists + * of echo overrun before the next commit), then discard enough + * data at the tail to prevent a subsequent overrun */ + while (ldata->echo_commit - tail >= ECHO_DISCARD_WATERMARK) + { + if (echo_buf(ldata, tail) == ECHO_OP_START) + { + if (echo_buf(ldata, tail + 1) == ECHO_OP_ERASE_TAB) + { + tail += 3; + } + else + { + tail += 2; + } + } + else + { + tail++; + } + } + + ldata->echo_tail = tail; + return 0; +} + +static void commit_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t nr = 0, old = 0; + size_t head = 0; + + head = ldata->echo_head; + ldata->echo_mark = head; + old = ldata->echo_commit - ldata->echo_tail; + + /* Process committed echoes if the accumulated # of bytes + * is over the threshold (and try again each time another + * block is accumulated) */ + nr = head - ldata->echo_tail; + if (nr < ECHO_COMMIT_WATERMARK || (nr % ECHO_BLOCK > old % ECHO_BLOCK)) + { + return; + } + + ldata->echo_commit = head; + __process_echoes(tty); +} + +static void process_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (ldata->echo_mark == ldata->echo_tail) + { + return; + } + + ldata->echo_commit = ldata->echo_mark; + __process_echoes(tty); +} + +/* NB: echo_mark and echo_head should be equivalent here */ +static void flush_echoes(struct tty_struct *tty) +{ + struct n_tty_data *ldata = tty->disc_data; + + if ((!L_ECHO(tty) && !L_ECHONL(tty)) || + ldata->echo_commit == ldata->echo_head) + { + return; + } + + ldata->echo_commit = ldata->echo_head; + __process_echoes(tty); +} +/** + * n_tty_set_termios - termios data changed + * @tty: terminal + * @old: previous data + * + * Called by the tty layer when the user changes termios flags so + * that the line discipline can plan ahead. This function cannot sleep + * and is protected from re-entry by the tty layer. The user is + * guaranteed that this function will not be re-entered or in progress + * when the ldisc is closed. + * + * Locking: Caller holds tty->termios_rwsem + */ + +static void n_tty_set_termios(struct tty_struct *tty, struct termios *old) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (!old || (old->c_lflag ^ tty->init_termios.c_lflag) & (ICANON | EXTPROC)) + { + rt_memset(ldata->read_flags, 0, RT_TTY_BUF); + ldata->line_start = ldata->read_tail; + if (!L_ICANON(tty) || !read_cnt(ldata)) + { + ldata->canon_head = ldata->read_tail; + ldata->push = 0; + } + else + { + set_bit((ldata->read_head - 1) & (RT_TTY_BUF - 1),(int *)ldata->read_flags); + ldata->canon_head = ldata->read_head; + ldata->push = 1; + } + ldata->commit_head = ldata->read_head; + ldata->erasing = 0; + ldata->lnext = 0; + } + + ldata->icanon = (L_ICANON(tty) != 0); + + if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) || + I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) || + I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) || + I_PARMRK(tty)) + { + rt_memset(ldata->char_map, 0, 256); + if (I_IGNCR(tty) || I_ICRNL(tty)) + { + set_bit('\r', (int *)ldata->char_map); + } + + if (I_INLCR(tty)) + { + set_bit('\n', (int *)ldata->char_map); + } + + if (L_ICANON(tty)) + { + set_bit(ERASE_CHAR(tty), (int *)ldata->char_map); + set_bit(KILL_CHAR(tty),(int *) ldata->char_map); + set_bit(EOF_CHAR(tty), (int *)ldata->char_map); + set_bit('\n',(int *) ldata->char_map); + set_bit(EOL_CHAR(tty),(int *) ldata->char_map); + if (L_IEXTEN(tty)) + { + set_bit(WERASE_CHAR(tty), (int *)ldata->char_map); + set_bit(LNEXT_CHAR(tty), (int *)ldata->char_map); + set_bit(EOL2_CHAR(tty), (int *)ldata->char_map); + if (L_ECHO(tty)) + { + set_bit(REPRINT_CHAR(tty), + (int *)ldata->char_map); + } + } + } + if (I_IXON(tty)) + { + set_bit(START_CHAR(tty), (int *)ldata->char_map); + set_bit(STOP_CHAR(tty), (int *)ldata->char_map); + } + if (L_ISIG(tty)) + { + set_bit(INTR_CHAR(tty), (int *)ldata->char_map); + set_bit(QUIT_CHAR(tty), (int *)ldata->char_map); + set_bit(SUSP_CHAR(tty), (int *)ldata->char_map); + } + clear_bit(__DISABLED_CHAR, (int *)ldata->char_map); + ldata->raw = 0; + ldata->real_raw = 0; + } + else + { + ldata->raw = 1; + if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) && + (I_IGNPAR(tty) || !I_INPCK(tty))/* && + (tty->driver->flags & TTY_DRIVER_REAL_RAW)*/) + { + ldata->real_raw = 1; + } + else + { + ldata->real_raw = 0; + } + } +} + +void console_ldata_init(struct tty_struct *tty) +{ + struct n_tty_data *ldata = RT_NULL; + + ldata = rt_malloc(sizeof(struct n_tty_data)); + if (ldata == RT_NULL) + { + LOG_E("console_ldata_init ldata malloc fail"); + return; + } + tty->disc_data = ldata; + reset_buffer_flags(ldata); + ldata->column = 0; + ldata->canon_column = 0; + ldata->no_room = 0; + ldata->lnext = 0; + n_tty_set_termios(tty, RT_NULL); + return; +} + +static int n_tty_open(struct dfs_fd *fd) +{ + int ret = 0; + struct n_tty_data *ldata = RT_NULL; + struct tty_struct *tty = (struct tty_struct*)fd->vnode->data; + + ldata = rt_malloc(sizeof(struct n_tty_data)); + if (ldata == RT_NULL) + { + LOG_E("n_tty_open ldata malloc fail"); + return -1; + } + + ldata->atomic_read_lock = rt_mutex_create("atomic_read_lock",RT_IPC_FLAG_FIFO); + ldata->output_lock = rt_mutex_create("output_lock",RT_IPC_FLAG_FIFO); + tty->disc_data = ldata; + reset_buffer_flags(ldata); + ldata->column = 0; + ldata->canon_column = 0; + ldata->lnext = 0; + n_tty_set_termios(tty, RT_NULL); + return ret; +} + +static inline int input_available_p(struct tty_struct *tty, int poll) +{ + struct n_tty_data *ldata = tty->disc_data; + int amt = poll && !TIME_CHAR(tty) && MIN_CHAR(tty) ? MIN_CHAR(tty) : 1; + + if (ldata->icanon && !L_EXTPROC(tty)) + { + return ldata->canon_head != ldata->read_tail; + } + else + { + return ldata->commit_head - ldata->read_tail >= amt; + } +} + +/** + * copy_from_read_buf - copy read data directly + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function to speed up n_tty_read. It is only called when + * ICANON is off; it copies characters straight from the tty queue to + * user space directly. It can be profitably called twice; once to + * drain the space from the tail pointer to the (physical) end of the + * buffer, and once to drain the space from the (physical) beginning of + * the buffer to head pointer. + * + * Called under the ldata->atomic_read_lock sem + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int copy_from_read_buf(struct tty_struct *tty,char *b,size_t nr) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t n = 0; + rt_bool_t is_eof = 0; + size_t head = ldata->commit_head; + size_t tail = ldata->read_tail & (RT_TTY_BUF - 1); + + n = min(head - ldata->read_tail, RT_TTY_BUF - tail); + n = min(nr, n); + if (n) + { + const char *from = read_buf_addr(ldata, tail); + rt_memcpy(b, from, n); + is_eof = n == 1 && *from == EOF_CHAR(tty); + ldata->read_tail += n; + /* Turn single EOF into zero-length read */ + if (L_EXTPROC(tty) && ldata->icanon && is_eof && + (head == ldata->read_tail)) + { + n = 0; + } + + } + return n; +} + +/** + * canon_copy_from_read_buf - copy read data in canonical mode + * @tty: terminal device + * @b: user data + * @nr: size of data + * + * Helper function for n_tty_read. It is only called when ICANON is on; + * it copies one line of input up to and including the line-delimiting + * character into the user-space buffer. + * + * NB: When termios is changed from non-canonical to canonical mode and + * the read buffer contains data, n_tty_set_termios() simulates an EOF + * push (as if C-d were input) _without_ the DISABLED_CHAR in the buffer. + * This causes data already processed as input to be immediately available + * as input although a newline has not been received. + * + * Called under the atomic_read_lock mutex + * + * n_tty_read()/consumer path: + * caller holds non-exclusive termios_rwsem + * read_tail published + */ + +static int canon_copy_from_read_buf(struct tty_struct *tty, char *b, size_t nr) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t n = 0, size = 0, more = 0, c = 0; + size_t eol = 0; + size_t tail = 0; + int found = 0; + + /* N.B. avoid overrun if nr == 0 */ + if (nr == 0) + { + return 0; + } + + n = min(nr + 1, ldata->canon_head - ldata->read_tail); + + tail = ldata->read_tail & (RT_TTY_BUF - 1); + size = min(tail + n, RT_TTY_BUF); + + eol = find_next_bit(ldata->read_flags, size, tail); + more = n - (size - tail); + if (eol == RT_TTY_BUF && more) + { + /* scan wrapped without finding set bit */ + eol = find_next_bit(ldata->read_flags, more, 0); + found = eol != more; + } + else + { + found = eol != size; + } + + n = eol - tail; + if (n > RT_TTY_BUF) + { + n += RT_TTY_BUF; + } + c = n + found; + + if (!found || read_buf(ldata, eol) != __DISABLED_CHAR) + { + c = min(nr, c); + n = c; + } + + size_t buf_size = RT_TTY_BUF - tail; + const void *from = read_buf_addr(ldata, tail); + if (n > buf_size) + { + rt_memcpy(b, from, buf_size); + b += buf_size; + n -= buf_size; + from = ldata->read_buf; + } + rt_memcpy(b, from, n); + + if (found) + { + clear_bit(eol, (int *)ldata->read_flags); + } + ldata->read_tail = ldata->read_tail + c; + + if (found) + { + if (!ldata->push) + { + ldata->line_start = ldata->read_tail; + } + else + { + ldata->push = 0; + } + } + return n; +} + + +static int n_tty_close(struct tty_struct *tty) +{ + int ret = 0; + struct n_tty_data *ldata = RT_NULL; + struct tty_struct *o_tty = RT_NULL; + + RT_ASSERT(tty != RT_NULL); + if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER) + { + o_tty = tty->other_struct; + } + else + { + o_tty = tty; + } + + ldata = o_tty->disc_data; + rt_free(ldata); + o_tty->disc_data = RT_NULL; + return ret; +} + +static int n_tty_ioctl(struct dfs_fd *fd, int cmd, void *args) +{ + int ret = 0; + struct tty_struct *real_tty = RT_NULL; + struct tty_struct *tty = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER) + { + real_tty = tty->other_struct; + } + else + { + real_tty = tty; + } + + switch(cmd) + { + + default: + ret = n_tty_ioctl_extend(real_tty, cmd, args); + if (ret != -ENOIOCTLCMD) + { + return ret; + } + } + + ret = rt_device_control((rt_device_t)real_tty, cmd, args); + if (ret != -ENOIOCTLCMD) + { + return ret; + } + return ret; +} + +static void +n_tty_receive_signal_char(struct tty_struct *tty, int signal, unsigned char c) +{ + isig(signal, tty); + if (L_ECHO(tty)) + { + echo_char(c, tty); + commit_echoes(tty); + } + else + { + process_echoes(tty); + } + return; +} + +/** + * n_tty_receive_char - perform processing + * @tty: terminal device + * @c: character + * + * Process an individual character of input received from the driver. + * This is serialized with respect to itself by the rules for the + * driver above. + * + * n_tty_receive_buf()/producer path: + * caller holds non-exclusive termios_rwsem + * publishes canon_head if canonical mode is active + * + * Returns 1 if LNEXT was received, else returns 0 + */ + +static int n_tty_receive_char_special(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (I_IXON(tty)) + { + if (c == START_CHAR(tty)) /*ctrl + p realize missing*/ + { + process_echoes(tty); + return 0; + } + if (c == STOP_CHAR(tty)) /*ctrl + s realize missing*/ + { + return 0; + } + } + + if (L_ISIG(tty)) + { + if (c == INTR_CHAR(tty)) /*ctrl + c realize missing*/ + { + n_tty_receive_signal_char(tty, SIGINT, c); + return 0; + } + else if (c == QUIT_CHAR(tty)) /*ctrl + d realize missing*/ + { + n_tty_receive_signal_char(tty, SIGQUIT, c); + return 0; + } + else if (c == SUSP_CHAR(tty)) /*ctrl + z realize missing*/ + { + n_tty_receive_signal_char(tty, SIGTSTP, c); + return 0; + } + } + + if (c == '\r') + { + if (I_IGNCR(tty)) + { + return 0; + } + + if (I_ICRNL(tty)) + { + c = '\n'; + } + } + else if (c == '\n' && I_INLCR(tty)) + { + c = '\r'; + } + + if (ldata->icanon) + { + if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) || + (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) + { + eraser(c, tty); + commit_echoes(tty); + return 0; + } + if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) + { + ldata->lnext = 1; + if (L_ECHO(tty)) + { + finish_erasing(ldata); + if (L_ECHOCTL(tty)) + { + echo_char_raw('^', ldata); + echo_char_raw('\b', ldata); + commit_echoes(tty); + } + } + return 1; + } + if (c == REPRINT_CHAR(tty) && L_ECHO(tty) && L_IEXTEN(tty)) + { + size_t tail = ldata->canon_head; + + finish_erasing(ldata); + echo_char(c, tty); + echo_char_raw('\n', ldata); + while (tail != ldata->read_head) + { + echo_char(read_buf(ldata, tail), tty); + tail++; + } + commit_echoes(tty); + return 0; + } + if (c == '\n') + { + if (L_ECHO(tty) || L_ECHONL(tty)) + { + echo_char_raw('\n', ldata); + commit_echoes(tty); + } + goto handle_newline; + } + if (c == EOF_CHAR(tty)) + { + c = __DISABLED_CHAR; + goto handle_newline; + } + if ((c == EOL_CHAR(tty)) || + (c == EOL2_CHAR(tty) && L_IEXTEN(tty))) + { + /* + * XXX are EOL_CHAR and EOL2_CHAR echoed?!? + */ + if (L_ECHO(tty)) + { + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + { + echo_set_canon_col(ldata); + } + + echo_char(c, tty); + commit_echoes(tty); + } + /* + * XXX does PARMRK doubling happen for + * EOL_CHAR and EOL2_CHAR? + */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + { + put_tty_queue(c, ldata); + } + +handle_newline: + set_bit(ldata->read_head & (RT_TTY_BUF - 1), (int *)ldata->read_flags); + put_tty_queue(c, ldata); + ldata->canon_head = ldata->read_head; + tty_wakeup_check(tty); + return 0; + } + } + + if (L_ECHO(tty)) + { + finish_erasing(ldata); + if (c == '\n') + { + echo_char_raw('\n', ldata); + } + else + { + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + { + echo_set_canon_col(ldata); + } + echo_char(c, tty); + } + commit_echoes(tty); + } + + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + { + put_tty_queue(c, ldata); + } + + put_tty_queue(c, ldata); + return 0; +} + +static inline void n_tty_receive_char_inline(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (L_ECHO(tty)) + { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + { + echo_set_canon_col(ldata); + } + + echo_char(c, tty); + commit_echoes(tty); + } + /* PARMRK doubling check */ + if (c == (unsigned char) '\377' && I_PARMRK(tty)) + { + put_tty_queue(c, ldata); + } + + put_tty_queue(c, ldata); +} + +static void n_tty_receive_char(struct tty_struct *tty, unsigned char c) +{ + n_tty_receive_char_inline(tty, c); +} + +static void n_tty_receive_char_lnext(struct tty_struct *tty, unsigned char c, char flag) +{ + struct n_tty_data *ldata = tty->disc_data; + + ldata->lnext = 0; + if (flag == TTY_NORMAL) + { + if (I_ISTRIP(tty)) + { + c &= 0x7f; + } + + if (I_IUCLC(tty) && L_IEXTEN(tty)) + { + c = tolower(c); + } + + n_tty_receive_char(tty, c); + } + else + { + //n_tty_receive_char_flagged(tty, c, flag); + } + +} + +static void n_tty_receive_buf_real_raw(struct tty_struct *tty, char *cp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + size_t n = 0, head = 0; + + head = ldata->read_head & (RT_TTY_BUF - 1); + n = min(count, RT_TTY_BUF - head); + rt_memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; + cp += n; + count -= n; + + head = ldata->read_head & (RT_TTY_BUF - 1); + n = min(count, RT_TTY_BUF - head); + rt_memcpy(read_buf_addr(ldata, head), cp, n); + ldata->read_head += n; +} + +static void n_tty_receive_buf_raw(struct tty_struct *tty, char *cp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) + { + if (flag == TTY_NORMAL) + { + put_tty_queue(*cp++, ldata); + } + } +} + +static void n_tty_receive_buf_standard(struct tty_struct *tty, char *cp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) + { + char c = *cp++; + + if (I_ISTRIP(tty)) + { + c &= 0x7f; + } + + if (I_IUCLC(tty) && L_IEXTEN(tty)) + { + c = tolower(c); + } + + if (L_EXTPROC(tty)) + { + put_tty_queue(c, ldata); + continue; + } + + if (!test_bit(c, (int *)ldata->char_map)) + { + n_tty_receive_char_inline(tty, c); + } + else if (n_tty_receive_char_special(tty, c) && count) + { + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } +} + +static inline void n_tty_receive_char_fast(struct tty_struct *tty, unsigned char c) +{ + struct n_tty_data *ldata = tty->disc_data; + + if (L_ECHO(tty)) + { + finish_erasing(ldata); + /* Record the column of first canon char. */ + if (ldata->canon_head == ldata->read_head) + { + echo_set_canon_col(ldata); + } + + echo_char(c, tty); + commit_echoes(tty); + } + put_tty_queue(c, ldata); +} + +static void n_tty_receive_buf_fast(struct tty_struct *tty, char *cp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + char flag = TTY_NORMAL; + + while (count--) + { + unsigned char c = *cp++; + + if (!test_bit(c, (int *)ldata->char_map)) + { + n_tty_receive_char_fast(tty, c); + } + else if (n_tty_receive_char_special(tty, c) && count) + { + n_tty_receive_char_lnext(tty, *cp++, flag); + count--; + } + } +} + +static void __receive_buf(struct tty_struct *tty, char *cp, int count) +{ + struct n_tty_data *ldata = tty->disc_data; + rt_bool_t preops = I_ISTRIP(tty) || (I_IUCLC(tty) && L_IEXTEN(tty)); + + if (ldata->real_raw) + { + n_tty_receive_buf_real_raw(tty, cp, count); + } + + else if (ldata->raw || (L_EXTPROC(tty) && !preops)) + { + n_tty_receive_buf_raw(tty, cp, count); + } + else + { + if (!preops && !I_PARMRK(tty)) + { + n_tty_receive_buf_fast(tty, cp, count); + } + else + { + n_tty_receive_buf_standard(tty, cp, count); + } + flush_echoes(tty); + } + + if (ldata->icanon && !L_EXTPROC(tty)) + return; + + /* publish read_head to consumer */ + ldata->commit_head = ldata->read_head; + + if (read_cnt(ldata)) + { + tty_wakeup_check(tty); + } +} + +int n_tty_receive_buf(struct tty_struct *tty,char *cp, int count) +{ + int size = 0; + struct n_tty_data *ldata = tty->disc_data; + int room = 0, n = 0, rcvd = 0, overflow = 0; + + size = count; + while(1) + { + size_t tail = ldata->read_tail; + + room = RT_TTY_BUF - (ldata->read_head - tail); + + if (I_PARMRK(tty)) + { + room = (room +2)/3; + } + room--; + if (room <= 0) + { + overflow = ldata->icanon && ldata->canon_head == tail; + if (overflow && room < 0) + { + ldata->read_head--; + } + + room = overflow; + } + else + { + overflow = 0; + } + + n = min(size, room); + + if (!n) + { + break; + } + + if (!overflow) + { + __receive_buf(tty, cp, n); + } + + cp += n; + size -= n; + rcvd += n; + } + return count - size; +} + +/** + * job_control - check job control + * @tty: tty + * @file: file handle + * + * Perform job control management checks on this file/tty descriptor + * and if appropriate send any needed signals and return a negative + * error code if action should be taken. + * + * Locking: redirected write test is safe + * current->signal->tty check is safe + * ctrl_lock to safely reference tty->pgrp + */ + +static int job_control(struct tty_struct *tty) +{ + return __tty_check_change(tty, SIGTTIN); +} + +static int n_tty_read(struct dfs_fd *fd, void *buf, size_t count) +{ + int level = 0; + char *b = (char *)buf; + struct tty_struct *tty = RT_NULL; + struct rt_lwp *lwp = RT_NULL; + struct rt_wqueue *wq = RT_NULL; + int wait_ret = 0; + int retval = 0; + int c = 0; + + level = rt_hw_interrupt_disable(); + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + c = job_control(tty); + if (c < 0) + { + rt_hw_interrupt_enable(level); + return c; + } + + struct n_tty_data *ldata = tty->disc_data; + + lwp = (struct rt_lwp *)(rt_thread_self()->lwp); + wq = wait_queue_get(lwp, tty); + + while(count) + { + if ((!input_available_p(tty, 0))) + { + if (fd->flags & O_NONBLOCK) + { + retval = -EAGAIN; + break; + } + + wait_ret = rt_wqueue_wait_interruptible(wq, 0, RT_WAITING_FOREVER); + if (wait_ret != 0) + { + break; + } + } + + if (ldata->icanon && !L_EXTPROC(tty)) + { + retval = canon_copy_from_read_buf(tty, b, count); + } + else + { + retval = copy_from_read_buf(tty, b, count); + } + + if (retval >= 1) + { + break; + } + } + rt_hw_interrupt_enable(level); + return retval; +} + +static int n_tty_write(struct dfs_fd *fd, const void *buf, size_t count) +{ + int retval = 0; + char *b = (char *)buf; + int c = 0; + struct tty_struct *tty = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + retval = tty_check_change(tty); + if (retval) + { + return retval; + } + + process_echoes(tty); + retval = count; + while(1) + { + if (O_OPOST(tty)) + { + while (count > 0) + { + ssize_t num = process_output_block(tty, b, count); + if (num < 0) + { + if (num == -EAGAIN) + { + break; + } + + retval = num; + goto break_out; + } + b += num; + count -= num; + if (count == 0) + { + break; + } + + c = *b; + if (process_output(c, tty) < 0) + { + break; + } + + b++; + count--; + } + retval -= count; + } + else + { + while (count > 0) + { + c = rt_device_write((rt_device_t)tty, -1, b, count); + if (c < 0) + { + retval = c; + goto break_out; + } + b += c; + count -= c; + } + retval -= count; + } + + if (!count) + { + break; + } + if (fd->flags & O_NONBLOCK) + { + break; + } + } +break_out: + return retval; +} + +static int n_tty_flush(struct dfs_fd *fd) +{ + return 0; +} + +static int n_tty_lseek(struct dfs_fd *fd, off_t offset) +{ + return 0; +} + +static int n_tty_getdents(struct dfs_fd *fd, struct dirent *dirp, uint32_t count) +{ + return 0; +} + +static int n_tty_poll(struct dfs_fd *fd, struct rt_pollreq *req) +{ + rt_base_t level = 0; + int mask = POLLOUT; + struct tty_struct *tty = RT_NULL; + struct rt_wqueue *wq = RT_NULL; + struct rt_lwp *lwp = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + + RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED); + + lwp = (struct rt_lwp *)(rt_thread_self()->lwp); + wq = wait_queue_get(lwp, tty); + rt_poll_add(wq, req); + level = rt_hw_interrupt_disable(); + + if (input_available_p(tty, 1)) + { + mask |= POLLIN; + } + + rt_hw_interrupt_enable(level); + + return mask; +} + +static struct tty_ldisc_ops n_tty_ops = { + "n_tty", + 0, + n_tty_open, + n_tty_close, + n_tty_ioctl, + n_tty_read, + n_tty_write, + n_tty_flush, + n_tty_lseek, + n_tty_getdents, + n_tty_poll, + n_tty_set_termios, + n_tty_receive_buf, + 0, +}; + +void n_tty_init(void) +{ + tty_register_ldisc(N_TTY, &n_tty_ops); +} diff --git a/components/drivers/tty/pty.c b/components/drivers/tty/pty.c new file mode 100644 index 0000000000..b3e6e7519a --- /dev/null +++ b/components/drivers/tty/pty.c @@ -0,0 +1,374 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#include <rtthread.h> +#include <tty.h> +#include <tty_ldisc.h> + +#define DBG_TAG "PTY" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +#define PTY_PTS_SIZE 10 +static struct tty_struct ptm_driver; +static struct tty_struct pts_drivers[PTY_PTS_SIZE]; +static int pts_index = 0; + +static int pts_register(struct tty_struct *ptm_drv, struct tty_struct *pts_drv, int pts_index); + +/* check free pts device */ +static struct tty_struct *find_freepts(void) +{ + for(int i = 0; i < PTY_PTS_SIZE; i++) + { + if (pts_drivers[i].init_flag == TTY_INIT_FLAG_NONE) + { + pts_drivers[i].init_flag = TTY_INIT_FLAG_ALLOCED; + return &pts_drivers[i]; + } + } + return RT_NULL; +} + +/* Set the lock flag on a pty */ +static int pty_set_lock(struct tty_struct *tty, int *arg) +{ + int val = *arg; + + if (val) + { + tty->pts_lock = val; + } + else + { + tty->pts_lock = val; + } + return 0; +} + +static int pty_get_lock(struct tty_struct *tty, int *arg) +{ + *arg = tty->pts_lock; + return 0; +} + +static int pty_get_index(struct tty_struct *tty, int *arg) +{ + *arg = tty->index; + return 0; +} +/* RT-Thread Device Interface */ +/* + * This function initializes console device. + */ +static rt_err_t pty_device_init(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + int level = 0; + struct tty_struct *tty = RT_NULL; + RT_ASSERT(dev != RT_NULL); + + tty = (struct tty_struct *)dev; + level = rt_hw_interrupt_disable(); + RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_REGED); + tty->init_flag = TTY_INIT_FLAG_INITED; + rt_hw_interrupt_enable(level); + return result; +} + +static rt_err_t pty_device_open(struct rt_device *dev, rt_uint16_t oflag) +{ + rt_err_t result = RT_EOK; + return result; +} + +static rt_err_t pty_device_close(struct rt_device *dev) +{ + rt_err_t result = RT_EOK; + struct tty_struct *tty = (struct tty_struct*)dev; + //struct tty_struct *to = RT_NULL; + + if (tty->subtype == PTY_TYPE_MASTER) + { + // to = tty->other_struct; + // to->init_flag = TTY_INIT_FLAG_NONE; + // to->other_struct = RT_NULL; + // to->foreground = RT_NULL; + // to->index = -1; + // tty_ldisc_kill(to); + // tty->other_struct = RT_NULL; + } + else + { + // to = tty->other_struct; + // to->other_struct = RT_NULL; + // tty->init_flag = TTY_INIT_FLAG_NONE; + // tty->other_struct = RT_NULL; + // tty->foreground = RT_NULL; + // tty->index = -1; + // tty->other_struct = RT_NULL; + // tty_ldisc_kill(tty); + } + + return result; +} + +static rt_size_t pty_device_read(struct rt_device *dev, + rt_off_t pos, + void *buffer, + rt_size_t size) +{ + rt_size_t len = 0; + + return len; +} + +static rt_size_t pty_device_write(struct rt_device *dev, + rt_off_t pos, + const void *buffer, + rt_size_t size) +{ + rt_base_t level = 0; + rt_size_t len = 0; + struct tty_struct *tty = RT_NULL; + struct tty_struct *to = RT_NULL; + + tty = (struct tty_struct *)dev; + RT_ASSERT(tty != RT_NULL); + RT_ASSERT(tty->init_flag == TTY_INIT_FLAG_INITED); + to = tty->other_struct; + level = rt_hw_interrupt_disable(); + + if (to->ldisc->ops->receive_buf) + { + len = to->ldisc->ops->receive_buf(to, (char *)buffer, size); + } + rt_hw_interrupt_enable(level); + + return len; +} + +static rt_err_t pty_device_control(rt_device_t dev, int cmd, void *args) +{ + struct tty_struct *tty = (struct tty_struct *)dev; + + switch (cmd) + { + case TIOCSPTLCK: /* Set PT Lock (disallow slave open) */ + return pty_set_lock(tty, (int *)args); + case TIOCGPTLCK: /* Get PT Lock status */ + return pty_get_lock(tty, (int *)args); + case TIOCGPTN: /* Get PT Number */ + return pty_get_index(tty, (int *)args); + } + + return -ENOIOCTLCMD; +} + +static int ptmx_open(struct dfs_fd *fd) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_struct *pts_drv = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + struct rt_lwp *lwp = RT_NULL; + struct rt_wqueue *wq = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + + pts_drv = find_freepts(); + if (pts_drv == RT_NULL) + { + LOG_E("free pts driver find fail\n"); + return -1; + } + ret = pts_register(tty, pts_drv, pts_index); + if (ret < 0) + { + LOG_E("pts register fail\n"); + rt_free(pts_drv); + return -1; + } + pts_index++; + lwp = (struct rt_lwp *)(rt_thread_self()->lwp); + wq = wait_queue_get(lwp, tty); + pts_drv->wait_queue = *wq; + tty->other_struct = pts_drv; + ld = tty->ldisc; + if (ld->ops->open) + { + ret = ld->ops->open(fd); + } + + return ret; +} +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops pty_device_ops = +{ + pty_device_init, + pty_device_open, + pty_device_close, + pty_device_read, + pty_device_write, + pty_device_control, +}; +#endif /* RT_USING_DEVICE_OPS */ +static struct dfs_file_ops pts_fops; +static struct dfs_file_ops ptmx_fops; +static int pts_register(struct tty_struct *ptm_drv, struct tty_struct *pts_drv, int pts_index) +{ + rt_err_t ret = RT_EOK; + rt_base_t level = 0; + struct rt_device *device = RT_NULL; + char name[20]; + + RT_ASSERT(ptm_drv!=RT_NULL); + level = rt_hw_interrupt_disable(); + + if (pts_drv->init_flag != TTY_INIT_FLAG_ALLOCED) + { + LOG_E("pts%d has been registered\n", pts_index); + ret = (-RT_EBUSY); + goto _exit; + } + + device = &pts_drv->parent; + device->type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + device->ops = &pty_device_ops; +#else + device->init = pty_device_init; + device->open = pty_device_open; + device->close = pty_device_close; + device->read = pty_device_read; + device->write = pty_device_write; + device->control = pty_device_control; +#endif /* RT_USING_DEVICE_OPS */ + + rt_snprintf(name, sizeof(name), "pts%d", pts_index); + ret = rt_device_register(device, name, RT_DEVICE_FLAG_RDWR); + if (ret != RT_EOK) + { + LOG_E("pts%d register failed\n", pts_index); + ret = -RT_EIO; + goto _exit; + } + +#ifdef RT_USING_POSIX_DEVIO + /* set fops */ + tty_set_fops(&pts_fops); + device->fops = &pts_fops; +#endif + + pts_drv->type = TTY_DRIVER_TYPE_PTY; + pts_drv->subtype = PTY_TYPE_SLAVE; + + pts_drv->pgrp = -1; + pts_drv->session = -1; + pts_drv->foreground = RT_NULL; + pts_drv->index = pts_index; + pts_drv->pts_lock = 1; + rt_wqueue_init(&pts_drv->wait_queue); + + tty_ldisc_init(pts_drv); + +extern struct termios tty_std_termios; + pts_drv->init_termios = tty_std_termios; + pts_drv->init_termios.c_cflag = B38400 | CS8 | CREAD; + pts_drv->init_termios.c_lflag |= ICANON; + pts_drv->init_termios.__c_ispeed = 38400; + pts_drv->init_termios.__c_ospeed = 38400; + + pts_drv->other_struct = ptm_drv; + + pts_drv->init_flag = TTY_INIT_FLAG_REGED; +_exit: + rt_hw_interrupt_enable(level); + + return ret; +} + +static int ptmx_register(void) +{ + rt_base_t level = 0; + rt_err_t ret = RT_EOK; + struct rt_device *device = RT_NULL; + struct tty_struct *ptm_drv = &ptm_driver; + + level = rt_hw_interrupt_disable(); + RT_ASSERT(ptm_drv->init_flag == TTY_INIT_FLAG_NONE); + + device = &(ptm_drv->parent); + + device->type = RT_Device_Class_Char; + +#ifdef RT_USING_DEVICE_OPS + device->ops = &pty_device_ops; +#else + device->init = pty_device_init; + device->open = pty_device_open; + device->close = pty_device_close; + device->read = pty_device_read; + device->write = pty_device_write; + device->control = pty_device_control; +#endif /* RT_USING_DEVICE_OPS */ + + ret = rt_device_register(device, "ptmx", RT_DEVICE_FLAG_RDWR); + if (ret != RT_EOK) + { + LOG_E("ptmx register fail\n"); + ret = -RT_EIO; + goto _exit; + } + +#ifdef RT_USING_POSIX_DEVIO + /* set fops */ + tty_set_fops(&ptmx_fops); + ptmx_fops.open = ptmx_open; + device->fops = &ptmx_fops; +#endif + + ptm_drv->type = TTY_DRIVER_TYPE_PTY; + ptm_drv->subtype = PTY_TYPE_MASTER; + ptm_drv->head = rt_calloc(1, sizeof(struct tty_node)); + if (!ptm_drv->head) + { + return -RT_ENOMEM; + } + tty_initstack(ptm_drv->head); + + rt_spin_lock_init(&ptm_drv->spinlock); + ptm_drv->pgrp = -1; + ptm_drv->session = -1; + ptm_drv->foreground = RT_NULL; + rt_wqueue_init(&ptm_drv->wait_queue); + + tty_ldisc_init(ptm_drv); + +extern struct termios tty_std_termios; + ptm_drv->init_termios.c_iflag = 0; + ptm_drv->init_termios.c_oflag = 0; + ptm_drv->init_termios.c_cflag = B38400 | CS8 | CREAD; + ptm_drv->init_termios.c_lflag = 0; + ptm_drv->init_termios.__c_ispeed = 38400; + ptm_drv->init_termios.__c_ospeed = 38400; + + ptm_drv->init_flag = TTY_INIT_FLAG_REGED; + +_exit: + rt_hw_interrupt_enable(level); + + return ret; +} +INIT_DEVICE_EXPORT(ptmx_register); diff --git a/components/drivers/tty/tty.c b/components/drivers/tty/tty.c new file mode 100644 index 0000000000..2a9380f55a --- /dev/null +++ b/components/drivers/tty/tty.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021.12.07 linzhenxing first version + */ +#include <dfs_file.h> +#include <dfs_fs.h> +#include <lwp.h> +#include <rtdevice.h> +#include <rtthread.h> +#include <tty.h> +#include <tty_ldisc.h> + +#if defined(RT_USING_POSIX_DEVIO) +#include <termios.h> +#endif + +#define DBG_TAG "TTY" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +struct termios tty_std_termios = { /* for the benefit of tty drivers */ + .c_iflag = IMAXBEL | IUCLC | INLCR | ICRNL | IGNPAR, + .c_oflag = OPOST, + .c_cflag = B38400 | CS8 | CREAD | HUPCL, + .c_lflag = ISIG | ECHOE | TOSTOP | NOFLSH, + RT_NULL,/* .c_line = N_TTY, */ + .c_cc = INIT_C_CC, + .__c_ispeed = 38400, + .__c_ospeed = 38400 +}; + +void tty_initstack(struct tty_node *node) +{ + node->lwp = RT_NULL; + node->next = node; +} + +static struct tty_node tty_node_cache = { RT_NULL, RT_NULL }; + +static struct tty_node *_tty_node_alloc(void) +{ + struct tty_node *node = tty_node_cache.next; + + if (node == RT_NULL) + { + node = rt_calloc(1, sizeof(struct tty_node)); + } + else + { + tty_node_cache.next = node->next; + } + + return node; +} + +static void _tty_node_free(struct tty_node *node) +{ + node->next = tty_node_cache.next; + tty_node_cache.next = node; +} + +int tty_push(struct tty_node **head, struct rt_lwp *lwp) +{ + struct tty_node *node = _tty_node_alloc(); + + if (!node) + { + return -1; + } + + node->lwp = lwp; + node->next = *head; + *head = node; + + return 0; +} + +struct rt_lwp *tty_pop(struct tty_node **head, struct rt_lwp *target_lwp) +{ + struct tty_node *node; + struct rt_lwp *lwp = RT_NULL; + + if (!head || !*head) + { + return RT_NULL; + } + + node = *head; + + if (target_lwp != RT_NULL && node->lwp != target_lwp) + { + struct tty_node *prev = RT_NULL; + + while (node != RT_NULL && node->lwp != target_lwp) + { + prev = node; + node = node->next; + } + + if (node != RT_NULL) + { + /* prev is impossible equ RT_NULL */ + prev->next = node->next; + lwp = target_lwp; + _tty_node_free(node); + } + } + else + { + lwp = (*head)->lwp; + *head = (*head)->next; + node->lwp = RT_NULL; + _tty_node_free(node); + } + + return lwp; +} + +rt_inline int tty_sigismember(lwp_sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + + if (_LWP_NSIG_WORDS == 1) + { + return 1 & (set->sig[0] >> sig); + } + else + { + return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW)); + } +} + +static int is_ignored(int sig) +{ + return (tty_sigismember(¤t->signal_mask, sig) || + current->signal_handler[sig-1] == SIG_IGN); +} + +/** + * tty_check_change - check for POSIX terminal changes + * @tty: tty to check + * + * If we try to write to, or set the state of, a terminal and we're + * not in the foreground, send a SIGTTOU. If the signal is blocked or + * ignored, go ahead and perform the operation. (POSIX 7.2) + * + * Locking: ctrl_lock + */ + +int __tty_check_change(struct tty_struct *tty, int sig) +{ + pid_t pgrp = 0, tty_pgrp = 0; + struct rt_lwp *lwp = tty->foreground; + int ret = 0; + int level = 0; + + level = rt_hw_interrupt_disable(); + if (current == RT_NULL) + { + rt_hw_interrupt_enable(level); + return 0; + } + + if (current->tty != tty) + { + rt_hw_interrupt_enable(level); + return 0; + } + + pgrp = current->__pgrp; + tty_pgrp = tty->pgrp; + + if (tty_pgrp && (pgrp != tty->pgrp)) + { + if (is_ignored(sig)) + { + if (sig == SIGTTIN) + { + ret = -EIO; + } + } + else + { + if (lwp) + { + lwp_kill(lwp_to_pid(lwp), sig); + } + } + } + rt_hw_interrupt_enable(level); + + if (!tty_pgrp) + { + LOG_D(tty, "sig=%d, tty->pgrp == -1!\n", sig); + } + return ret; +} + +int tty_check_change(struct tty_struct *tty) +{ + return __tty_check_change(tty, SIGTTOU); +} + +static int tty_open(struct dfs_fd *fd) +{ + int ret = 0; + int noctty = 0; + struct tty_struct *tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + ld = tty->ldisc; + if (ld->ops->open) + { + ret = ld->ops->open(fd); + } + noctty = (fd->flags & O_NOCTTY); + + rt_device_t device = (rt_device_t)fd->vnode->data; + if (fd->vnode->ref_count == 1) + { + ret = rt_device_open(device, fd->flags); + } + if (current == RT_NULL) //kernel mode not lwp + { + return ret; + } + + if (!noctty && + current->leader && + !current->tty && + tty->session == -1) + { + current->tty = tty; + current->tty_old_pgrp = 0; + tty->session = current->session; + tty->pgrp = current->__pgrp; + tty->foreground = current; + } + + return ret; +} + +static int tty_close(struct dfs_fd *fd) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + ld = tty->ldisc; + if (ld->ops->close) + { + //ld->ops->close(tty); + } + if (fd->vnode->ref_count == 1) + { + ret = rt_device_close((rt_device_t)tty); + } + return ret; +} + +static int tiocsctty(struct tty_struct *tty, int arg) +{ + if (current->leader && + (current->session == tty->session)) + { + return 0; + } + + /* + * The process must be a session leader and + * not have a controlling tty already. + */ + if (!current->leader || current->tty) + { + return -EPERM; + } + + if (tty->session > 0) + { + LOG_E("this tty have control process\n"); + + } + current->tty = tty; + current->tty_old_pgrp = 0; + tty->session = current->session; + tty->pgrp = current->__pgrp; + tty->foreground = current; + if (tty->type == TTY_DRIVER_TYPE_PTY) + { + tty->other_struct->foreground = current; + } + return 0; +} + +static int tty_ioctl(struct dfs_fd *fd, int cmd, void *args) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_struct *real_tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + + if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER) + { + real_tty = tty->other_struct; + } + else + { + real_tty = tty; + } + switch (cmd) + { + case TIOCSCTTY: + return tiocsctty(real_tty, 1); + } + ld = tty->ldisc; + if (ld->ops->ioctl) + { + ret = ld->ops->ioctl(fd, cmd, args); + } + return ret; +} + +static int tty_read(struct dfs_fd *fd, void *buf, size_t count) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + ld = tty->ldisc; + if (ld->ops->read) + { + ret = ld->ops->read(fd, buf, count); + } + return ret; +} + +static int tty_write(struct dfs_fd *fd, const void *buf, size_t count) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + ld = tty->ldisc; + if (ld->ops->write) + { + ret = ld->ops->write(fd, buf, count); + } + return ret; +} + +static int tty_poll(struct dfs_fd *fd, struct rt_pollreq *req) +{ + int ret = 0; + struct tty_struct *tty = RT_NULL; + struct tty_ldisc *ld = RT_NULL; + + tty = (struct tty_struct *)fd->vnode->data; + RT_ASSERT(tty != RT_NULL); + ld = tty->ldisc; + if (ld->ops->poll) + { + ret = ld->ops->poll(fd, req); + } + return ret; +} + +static const struct dfs_file_ops tty_fops = +{ + tty_open, + tty_close, + tty_ioctl, + tty_read, + tty_write, + RT_NULL, /* flush */ + RT_NULL, /* lseek */ + RT_NULL, /* getdents */ + tty_poll, +}; +static const struct dfs_file_ops console_fops = +{ + tty_open, + tty_close, + tty_ioctl, + tty_read, + tty_write, + RT_NULL, /* flush */ + RT_NULL, /* lseek */ + RT_NULL, /* getdents */ + tty_poll, +}; + +void console_init() +{ + n_tty_init(); +} + +void tty_set_fops(struct dfs_file_ops *fops) +{ + *fops = tty_fops; +} + +void console_set_fops(struct dfs_file_ops *fops) +{ + *fops = console_fops; +} diff --git a/components/drivers/tty/tty_ioctl.c b/components/drivers/tty/tty_ioctl.c new file mode 100644 index 0000000000..f7e0d495da --- /dev/null +++ b/components/drivers/tty/tty_ioctl.c @@ -0,0 +1,109 @@ +#include <stddef.h> +#include <rtthread.h> +#include <tty.h> +#if defined(RT_USING_POSIX_DEVIO) +#include <termios.h> +#endif + +#define DBG_TAG "TTY_IOCTL" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +/* + * Internal flag options for termios setting behavior + */ +#define TERMIOS_FLUSH 1 +#define TERMIOS_WAIT 2 +#define TERMIOS_TERMIO 4 +#define TERMIOS_OLD 8 + +/** + * set_termios - set termios values for a tty + * @tty: terminal device + * @arg: user data + * @opt: option information + * + * Helper function to prepare termios data and run necessary other + * functions before using tty_set_termios to do the actual changes. + * + * Locking: + * Called functions take ldisc and termios_rwsem locks + */ + +static int set_termios(struct tty_struct *tty, void *arg, int opt) +{ + struct termios old_termios; + struct tty_ldisc *ld = RT_NULL; + struct termios *new_termios = (struct termios *)arg; + int level = 0; + int retval = tty_check_change(tty); + + if (retval) + { + return retval; + } + + rt_memcpy(&old_termios, &(tty->init_termios), sizeof(struct termios)); + level = rt_hw_interrupt_disable(); + tty->init_termios = *new_termios; + rt_hw_interrupt_enable(level); + ld = tty->ldisc; + if (ld != NULL) + { + if (ld->ops->set_termios) + { + ld->ops->set_termios(tty, &old_termios); + } + } + return 0; +} + +int n_tty_ioctl_extend(struct tty_struct *tty, int cmd, void *args) +{ + int ret = 0; + void *p = (void *)args; + struct tty_struct *real_tty = RT_NULL; + + if (tty->type == TTY_DRIVER_TYPE_PTY && tty->subtype == PTY_TYPE_MASTER) + { + real_tty = tty->other_struct; + } + else + { + real_tty = tty; + } + + switch(cmd) + { + case TCGETS: + { + struct termios *tio = (struct termios *)p; + if (tio == RT_NULL) + { + return -RT_EINVAL; + } + + rt_memcpy(tio, &real_tty->init_termios, sizeof(real_tty->init_termios)); + return ret; + } + case TCSETSF: + { + return set_termios(real_tty, p, TERMIOS_FLUSH | TERMIOS_WAIT | TERMIOS_OLD); + } + case TCSETSW: + { + return set_termios(real_tty, p, TERMIOS_WAIT | TERMIOS_OLD); + } + case TCSETS: + { + return set_termios(real_tty, p, TERMIOS_OLD); + } + default: + break; + } + return -ENOIOCTLCMD; +} diff --git a/components/drivers/tty/tty_ldisc.c b/components/drivers/tty/tty_ldisc.c new file mode 100644 index 0000000000..f736812414 --- /dev/null +++ b/components/drivers/tty/tty_ldisc.c @@ -0,0 +1,181 @@ +#include <tty.h> +#include <tty_ldisc.h> + +#define DBG_TAG "TTY_LDISC" +#ifdef RT_TTY_DEBUG +#define DBG_LVL DBG_LOG +#else +#define DBG_LVL DBG_INFO +#endif /* RT_TTY_DEBUG */ +#include <rtdbg.h> + +static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; + +static struct tty_ldisc_ops *get_ldops(int disc) +{ + struct tty_ldisc_ops *ldops = RT_NULL; + int level = 0; + level = rt_hw_interrupt_disable(); + ldops = tty_ldiscs[disc]; + if (ldops) + { + ldops->refcount++; + } + rt_hw_interrupt_enable(level); + return ldops; +} + +static void put_ldops(struct tty_ldisc_ops *ldops) +{ + int level = 0; + + level = rt_hw_interrupt_disable(); + ldops->refcount--; + rt_hw_interrupt_enable(level); +} +static struct tty_ldisc *tty_ldisc_get(struct tty_struct *tty, int disc) +{ + struct tty_ldisc *ld = RT_NULL; + struct tty_ldisc_ops *ldops = RT_NULL; + + if (disc < N_TTY || disc >= NR_LDISCS) + { + return RT_NULL; + } + + ldops = get_ldops(disc); + if (ldops == RT_NULL) + { + LOG_E("tty ldisc get error\n"); + return RT_NULL; + } + + ld = rt_malloc(sizeof(struct tty_ldisc)); + if (ld == RT_NULL) + { + ldops->refcount--; + return RT_NULL; + } + + ld->ops = ldops; + ld->tty = tty; + + return ld; +} + +/** + * tty_ldisc_put - release the ldisc + * + * Complement of tty_ldisc_get(). + */ +static void tty_ldisc_put(struct tty_ldisc *ld) +{ + if (ld == RT_NULL) + { + return; + } + + put_ldops(ld->ops); + rt_free(ld); +} + +/** + * tty_ldisc_close - close a line discipline + * @tty: tty we are opening the ldisc on + * @ld: discipline to close + * + * A helper close method. Also a convenient debugging and check + * point. + */ + +static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) +{ + if (ld->ops->close) + { + ld->ops->close(tty); + } +} + +/** + * tty_ldisc_kill - teardown ldisc + * @tty: tty being released + * + * Perform final close of the ldisc and reset tty->ldisc + */ +void tty_ldisc_kill(struct tty_struct *tty) +{ + if (!tty->ldisc) + { + return; + } + + /* + * Now kill off the ldisc + */ + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + /* Force an oops if we mess this up */ + tty->ldisc = NULL; +} + +int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc) +{ + int ret = 0; + int level = 0; + + if (disc < N_TTY || disc >= NR_LDISCS) + { + return -EINVAL; + } + + level = rt_hw_interrupt_disable(); + tty_ldiscs[disc] = new_ldisc; + new_ldisc->num = disc; + new_ldisc->refcount = 0; + rt_hw_interrupt_enable(level); + + return ret; +} + +/** + * tty_ldisc_release - release line discipline + * @tty: tty being shut down (or one end of pty pair) + * + * Called during the final close of a tty or a pty pair in order to shut + * down the line discpline layer. On exit, each tty's ldisc is NULL. + */ + +void tty_ldisc_release(struct tty_struct *tty) +{ + int level = 0; + struct tty_struct *o_tty = tty->other_struct; + + /* + * Shutdown this line discipline. As this is the final close, + * it does not race with the set_ldisc code path. + */ + + level = rt_hw_interrupt_disable(); + tty_ldisc_kill(tty); + if (o_tty) + { + tty_ldisc_kill(o_tty); + } + rt_hw_interrupt_enable(level); + +} + +/** + * tty_ldisc_init - ldisc setup for new tty + * @tty: tty being allocated + * + * Set up the line discipline objects for a newly allocated tty. Note that + * the tty structure is not completely set up when this call is made. + */ + +void tty_ldisc_init(struct tty_struct *tty) +{ + struct tty_ldisc *ld = tty_ldisc_get(tty, N_TTY); + RT_ASSERT(ld != RT_NULL); + tty->ldisc = ld; +} diff --git a/components/drivers/virtio/SConscript b/components/drivers/virtio/SConscript new file mode 100644 index 0000000000..a7af6a017e --- /dev/null +++ b/components/drivers/virtio/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] + +group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_VIRTIO'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/drivers/virtio/virtio.c b/components/drivers/virtio/virtio.c new file mode 100644 index 0000000000..338b03ca94 --- /dev/null +++ b/components/drivers/virtio/virtio.c @@ -0,0 +1,324 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include <rtthread.h> +#include <cpuport.h> + +#include <virtio.h> + +rt_inline void _virtio_dev_check(struct virtio_device *dev) +{ + RT_ASSERT(dev != RT_NULL); + RT_ASSERT(dev->mmio_config != RT_NULL); +} + +void virtio_reset_device(struct virtio_device *dev) +{ + _virtio_dev_check(dev); + + dev->mmio_config->status = 0; +} + +void virtio_status_acknowledge_driver(struct virtio_device *dev) +{ + _virtio_dev_check(dev); + + dev->mmio_config->status |= VIRTIO_STATUS_ACKNOWLEDGE | VIRTIO_STATUS_DRIVER; +} + +void virtio_status_driver_ok(struct virtio_device *dev) +{ + _virtio_dev_check(dev); + + dev->mmio_config->status |= VIRTIO_STATUS_FEATURES_OK | VIRTIO_STATUS_DRIVER_OK; +} + +void virtio_interrupt_ack(struct virtio_device *dev) +{ + rt_uint32_t status; + + _virtio_dev_check(dev); + + status = dev->mmio_config->interrupt_status; + + if (status != 0) + { + dev->mmio_config->interrupt_ack = status; + } +} + +rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit) +{ + _virtio_dev_check(dev); + + return !!(dev->mmio_config->device_features & (1UL << feature_bit)); +} + +rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num) +{ + _virtio_dev_check(dev); + + dev->queues = rt_malloc(sizeof(struct virtq) * queues_num); + + if (dev->queues != RT_NULL) + { + dev->queues_num = queues_num; + + return RT_EOK; + } + + return -RT_ENOMEM; +} + +void virtio_queues_free(struct virtio_device *dev) +{ + if (dev->queues != RT_NULL) + { + dev->queues_num = 0; + rt_free(dev->queues); + } +} + +rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size) +{ + int i; + void *pages; + rt_size_t pages_total_size; + struct virtq *queue; + + _virtio_dev_check(dev); + + RT_ASSERT(dev->mmio_config->queue_num_max > 0); + RT_ASSERT(dev->mmio_config->queue_num_max > queue_index); + /* ring_size is power of 2 */ + RT_ASSERT(ring_size > 0); + RT_ASSERT(((ring_size - 1) & ring_size) == 0); + + queue = &dev->queues[queue_index]; + pages_total_size = VIRTIO_PAGE_ALIGN( + VIRTQ_DESC_TOTAL_SIZE(ring_size) + VIRTQ_AVAIL_TOTAL_SIZE(ring_size)) + VIRTQ_USED_TOTAL_SIZE(ring_size); + + pages = rt_malloc_align(pages_total_size, VIRTIO_PAGE_SIZE); + + if (pages == RT_NULL) + { + return -RT_ENOMEM; + } + + queue->free = rt_malloc(sizeof(rt_bool_t) * ring_size); + + if (queue->free == RT_NULL) + { + rt_free_align(pages); + return -RT_ENOMEM; + } + + rt_memset(pages, 0, pages_total_size); + + dev->mmio_config->guest_page_size = VIRTIO_PAGE_SIZE; + dev->mmio_config->queue_sel = queue_index; + dev->mmio_config->queue_num = ring_size; + dev->mmio_config->queue_align = VIRTIO_PAGE_SIZE; + dev->mmio_config->queue_pfn = VIRTIO_VA2PA(pages) >> VIRTIO_PAGE_SHIFT; + + queue->num = ring_size; + queue->desc = (struct virtq_desc *)((rt_ubase_t)pages); + queue->avail = (struct virtq_avail *)(((rt_ubase_t)pages) + VIRTQ_DESC_TOTAL_SIZE(ring_size)); + queue->used = (struct virtq_used *)VIRTIO_PAGE_ALIGN( + (rt_ubase_t)&queue->avail->ring[ring_size] + VIRTQ_AVAIL_RES_SIZE); + + queue->used_idx = 0; + + /* All descriptors start out unused */ + for (i = 0; i < ring_size; ++i) + { + queue->free[i] = RT_TRUE; + } + + queue->free_count = ring_size; + + return RT_EOK; +} + +void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index) +{ + struct virtq *queue; + + _virtio_dev_check(dev); + + RT_ASSERT(dev->mmio_config->queue_num_max > 0); + RT_ASSERT(dev->mmio_config->queue_num_max > queue_index); + + queue = &dev->queues[queue_index]; + + RT_ASSERT(queue->num > 0); + + rt_free(queue->free); + rt_free_align((void *)queue->desc); + + dev->mmio_config->queue_sel = queue_index; + dev->mmio_config->queue_pfn = RT_NULL; + + queue->num = 0; + queue->desc = RT_NULL; + queue->avail = RT_NULL; + queue->used = RT_NULL; +} + +void virtio_queue_notify(struct virtio_device *dev, rt_uint32_t queue_index) +{ + _virtio_dev_check(dev); + + dev->mmio_config->queue_notify = queue_index; +} + +void virtio_submit_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index) +{ + rt_size_t ring_size; + struct virtq *queue; + + _virtio_dev_check(dev); + + queue = &dev->queues[queue_index]; + ring_size = queue->num; + + /* Tell the device the first index in our chain of descriptors */ + queue->avail->ring[queue->avail->idx % ring_size] = desc_index; + rt_hw_dsb(); + + /* Tell the device another avail ring entry is available */ + queue->avail->idx++; + rt_hw_dsb(); +} + +rt_uint16_t virtio_alloc_desc(struct virtio_device *dev, rt_uint32_t queue_index) +{ + int i; + struct virtq *queue; + + _virtio_dev_check(dev); + + RT_ASSERT(queue_index < dev->queues_num); + + queue = &dev->queues[queue_index]; + + if (queue->free_count > 0) + { + rt_size_t ring_size = queue->num; + + for (i = 0; i < ring_size; ++i) + { + if (queue->free[i]) + { + queue->free[i] = RT_FALSE; + queue->free_count--; + + return (rt_uint16_t)i; + } + } + } + + return VIRTQ_INVALID_DESC_ID; +} + +void virtio_free_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index) +{ + struct virtq *queue; + + _virtio_dev_check(dev); + + queue = &dev->queues[queue_index]; + + RT_ASSERT(queue_index < dev->queues_num); + RT_ASSERT(!queue->free[desc_index]); + + queue->desc[desc_index].addr = 0; + queue->desc[desc_index].len = 0; + queue->desc[desc_index].flags = 0; + queue->desc[desc_index].next = 0; + + queue->free[desc_index] = RT_TRUE; + + queue->free_count++; +} + +rt_err_t virtio_alloc_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t count, + rt_uint16_t *indexs) +{ + int i, j; + + _virtio_dev_check(dev); + + RT_ASSERT(indexs != RT_NULL); + + if (dev->queues[queue_index].free_count < count) + { + return -RT_ERROR; + } + + for (i = 0; i < count; ++i) + { + indexs[i] = virtio_alloc_desc(dev, queue_index); + + if (indexs[i] == VIRTQ_INVALID_DESC_ID) + { + for (j = 0; j < i; ++j) + { + virtio_free_desc(dev, queue_index, indexs[j]); + } + + return -RT_ERROR; + } + } + + return RT_EOK; +} + +void virtio_free_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index) +{ + rt_uint16_t flags, next; + struct virtq_desc *desc; + + _virtio_dev_check(dev); + + desc = &dev->queues[queue_index].desc[0]; + + for (;;) + { + flags = desc[desc_index].flags; + next = desc[desc_index].next; + + virtio_free_desc(dev, queue_index, desc_index); + + if (flags & VIRTQ_DESC_F_NEXT) + { + desc_index = next; + } + else + { + break; + } + } +} + +void virtio_fill_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index, + rt_uint64_t addr, rt_uint32_t len, rt_uint16_t flags, rt_uint16_t next) +{ + struct virtq_desc *desc; + + _virtio_dev_check(dev); + + desc = &dev->queues[queue_index].desc[desc_index]; + + desc->addr = addr; + desc->len = len; + desc->flags = flags; + desc->next = next; +} diff --git a/components/drivers/virtio/virtio.h b/components/drivers/virtio/virtio.h new file mode 100644 index 0000000000..297975601b --- /dev/null +++ b/components/drivers/virtio/virtio.h @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-9-16 GuEe-GUI the first version + * 2021-11-11 GuEe-GUI modify to virtio common interface + */ + +#ifndef __VIRTIO_H__ +#define __VIRTIO_H__ + +#include <rthw.h> +#include <rtdef.h> + +#ifdef RT_USING_LWP +#include <mmu.h> +#include <ioremap.h> +#endif + +#if RT_NAME_MAX < 16 +#error "Please set RT_NAME_MAX to at lest 16" +#endif + +#ifdef RT_USING_VIRTIO10 +#define RT_USING_VIRTIO_VERSION 0x1 +#endif + +#include <virtio_mmio.h> +#include <virtio_queue.h> + +#define VIRTIO_MAGIC_VALUE 0x74726976 /* "virt" */ + +#define VIRTIO_STATUS_ACKNOWLEDGE (1 << 0) +#define VIRTIO_STATUS_DRIVER (1 << 1) +#define VIRTIO_STATUS_DRIVER_OK (1 << 2) +#define VIRTIO_STATUS_FEATURES_OK (1 << 3) +#define VIRTIO_STATUS_NEEDS_RESET (1 << 6) +#define VIRTIO_STATUS_FAILED (1 << 7) + +#define VIRTIO_F_NOTIFY_ON_EMPTY 24 +#define VIRTIO_F_ANY_LAYOUT 27 +#define VIRTIO_F_RING_INDIRECT_DESC 28 +#define VIRTIO_F_RING_EVENT_IDX 29 +#define VIRTIO_F_VERSION_1 32 +#define VIRTIO_F_RING_PACKED 34 + +#ifdef RT_USING_LWP +extern rt_mmu_info mmu_info; +#define VIRTIO_VA2PA(vaddr) ((rt_ubase_t)rt_hw_mmu_v2p(&mmu_info, vaddr)) +#define VIRTIO_PA2VA(paddr) ((rt_ubase_t)rt_ioremap((void *)paddr, ARCH_PAGE_SIZE)) +#else +#define VIRTIO_VA2PA(vaddr) ((rt_ubase_t)vaddr) +#define VIRTIO_PA2VA(paddr) ((rt_ubase_t)paddr) +#endif /* RT_USING_LWP */ +#define VIRTIO_PAGE_SHIFT 12 +#define VIRTIO_PAGE_SIZE (1 << VIRTIO_PAGE_SHIFT) +#define VIRTIO_PAGE_ALIGN(addr) (RT_ALIGN(addr, VIRTIO_PAGE_SIZE)) + +enum +{ + /* virtio 1.0 */ + VIRTIO_DEVICE_ID_INVALID = 0, /* Invalid device */ + VIRTIO_DEVICE_ID_NET = 1, /* Net */ + VIRTIO_DEVICE_ID_BLOCK = 2, /* Block */ + VIRTIO_DEVICE_ID_CONSOLE = 3, /* Console */ + VIRTIO_DEVICE_ID_RNG = 4, /* Rng */ + VIRTIO_DEVICE_ID_BALLOON = 5, /* Balloon */ + VIRTIO_DEVICE_ID_IOMEM = 6, /* IO memory */ + VIRTIO_DEVICE_ID_RPMSG = 7, /* Remote processor messaging */ + VIRTIO_DEVICE_ID_SCSI = 8, /* SCSI */ + VIRTIO_DEVICE_ID_9P = 9, /* 9p console */ + VIRTIO_DEVICE_ID_MAC80211_WLAN = 10, /* Mac80211 wlan */ + VIRTIO_DEVICE_ID_RPROC_SERIAL = 11, /* Remoteproc serial link */ + VIRTIO_DEVICE_ID_CAIF = 12, /* CAIF */ + VIRTIO_DEVICE_ID_MEM_BALLOON = 13, /* Memory balloon */ + VIRTIO_DEVICE_ID_GPU = 16, /* GPU */ + VIRTIO_DEVICE_ID_TIME = 17, /* Timer/clock device */ + VIRTIO_DEVICE_ID_INPUT = 18, /* Input */ + /* virtio 1.1 */ + VIRTIO_DEVICE_ID_SOCKET = 19, /* Socket device */ + VIRTIO_DEVICE_ID_CRYPTO = 20, /* Crypto device */ + VIRTIO_DEVICE_ID_SIG_DIS_MOD = 21, /* Signal Distribution Module */ + VIRTIO_DEVICE_ID_PSTORE = 22, /* Pstore device */ + VIRTIO_DEVICE_ID_IOMMU = 23, /* IOMMU device */ + VIRTIO_DEVICE_ID_MEM = 24, /* Memory device */ + /* virtio 1.2 */ + VIRTIO_DEVICE_ID_AUDIO = 25, /* Audio device */ + VIRTIO_DEVICE_ID_FS = 26, /* File system device */ + VIRTIO_DEVICE_ID_PMEM = 27, /* PMEM device */ + VIRTIO_DEVICE_ID_RPMB = 28, /* RPMB device */ + VIRTIO_DEVICE_ID_MAC80211_HWSIM = 29, /* Mac80211 hwsim wireless simulation device */ + VIRTIO_DEVICE_ID_VIDEO_ENCODER = 30, /* Video encoder device */ + VIRTIO_DEVICE_ID_VIDEO_DECODER = 31, /* Video decoder device */ + VIRTIO_DEVICE_ID_SCMI = 32, /* SCMI device */ + VIRTIO_DEVICE_ID_NITRO_SEC_MOD = 33, /* NitroSecureModule */ + VIRTIO_DEVICE_ID_I2C_ADAPTER = 34, /* I2C adapter */ + VIRTIO_DEVICE_ID_WATCHDOG = 35, /* Watchdog */ + VIRTIO_DEVICE_ID_CAN = 36, /* CAN device */ + VIRTIO_DEVICE_ID_DMABUF = 37, /* Virtio dmabuf */ + VIRTIO_DEVICE_ID_PARAM_SERV = 38, /* Parameter Server */ + VIRTIO_DEVICE_ID_AUDIO_POLICY = 39, /* Audio policy device */ + VIRTIO_DEVICE_ID_BT = 40, /* Bluetooth device */ + VIRTIO_DEVICE_ID_GPIO = 41, /* GPIO device */ + VIRTIO_DEVICE_ID_RDMA = 42, /* RDMA device */ + + VIRTIO_DEVICE_TYPE_SIZE +}; + +struct virtio_device +{ + rt_uint32_t irq; + + struct virtq *queues; + rt_size_t queues_num; + + union + { + rt_ubase_t *mmio_base; + struct virtio_mmio_config *mmio_config; + }; + +#ifdef RT_USING_SMP + struct rt_spinlock spinlock; +#endif + + void *priv; +}; + +typedef rt_err_t (*virtio_device_init_handler)(rt_ubase_t *mmio_base, rt_uint32_t irq); + +void virtio_reset_device(struct virtio_device *dev); +void virtio_status_acknowledge_driver(struct virtio_device *dev); +void virtio_status_driver_ok(struct virtio_device *dev); +void virtio_interrupt_ack(struct virtio_device *dev); +rt_bool_t virtio_has_feature(struct virtio_device *dev, rt_uint32_t feature_bit); + +rt_err_t virtio_queues_alloc(struct virtio_device *dev, rt_size_t queues_num); +void virtio_queues_free(struct virtio_device *dev); +rt_err_t virtio_queue_init(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t ring_size); +void virtio_queue_destroy(struct virtio_device *dev, rt_uint32_t queue_index); +void virtio_queue_notify(struct virtio_device *dev, rt_uint32_t queue_index); + +void virtio_submit_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index); + +rt_uint16_t virtio_alloc_desc(struct virtio_device *dev, rt_uint32_t queue_index); +void virtio_free_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index); +rt_err_t virtio_alloc_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_size_t count, + rt_uint16_t *indexs); +void virtio_free_desc_chain(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index); +void virtio_fill_desc(struct virtio_device *dev, rt_uint32_t queue_index, rt_uint16_t desc_index, + rt_uint64_t addr, rt_uint32_t len, rt_uint16_t flags, rt_uint16_t next); + +#endif /* __VIRTIO_H__ */ diff --git a/components/drivers/virtio/virtio_blk.c b/components/drivers/virtio/virtio_blk.c new file mode 100644 index 0000000000..a568a7008a --- /dev/null +++ b/components/drivers/virtio/virtio_blk.c @@ -0,0 +1,253 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-9-16 GuEe-GUI the first version + * 2021-11-11 GuEe-GUI using virtio common interface + */ + +#include <rthw.h> +#include <rtthread.h> +#include <cpuport.h> + +#ifdef RT_USING_VIRTIO_BLK + +#include <virtio_blk.h> + +static void virtio_blk_rw(struct virtio_blk_device *virtio_blk_dev, rt_off_t pos, void *buffer, rt_size_t count, + int flags) +{ + rt_uint16_t idx[3]; + rt_size_t size = count * virtio_blk_dev->config->blk_size; + struct virtio_device *virtio_dev = &virtio_blk_dev->virtio_dev; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + /* Allocate 3 descriptors */ + while (virtio_alloc_desc_chain(virtio_dev, 0, 3, idx)) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + virtio_blk_dev->info[idx[0]].status = 0xff; + virtio_blk_dev->info[idx[0]].valid = RT_TRUE; + virtio_blk_dev->info[idx[0]].req.type = flags; + virtio_blk_dev->info[idx[0]].req.ioprio = 0; + virtio_blk_dev->info[idx[0]].req.sector = pos * (virtio_blk_dev->config->blk_size / 512); + + flags = flags == VIRTIO_BLK_T_OUT ? 0 : VIRTQ_DESC_F_WRITE; + + virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[0], + VIRTIO_VA2PA(&virtio_blk_dev->info[idx[0]].req), sizeof(struct virtio_blk_req), VIRTQ_DESC_F_NEXT, idx[1]); + + virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[1], + VIRTIO_VA2PA(buffer), size, flags | VIRTQ_DESC_F_NEXT, idx[2]); + + virtio_fill_desc(virtio_dev, VIRTIO_BLK_QUEUE, idx[2], + VIRTIO_VA2PA(&virtio_blk_dev->info[idx[0]].status), sizeof(rt_uint8_t), VIRTQ_DESC_F_WRITE, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_BLK_QUEUE, idx[0]); + + virtio_queue_notify(virtio_dev, VIRTIO_BLK_QUEUE); + + /* Wait for virtio_blk_isr() to done */ + while (virtio_blk_dev->info[idx[0]].valid) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + virtio_free_desc_chain(virtio_dev, VIRTIO_BLK_QUEUE, idx[0]); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +static rt_size_t virtio_blk_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t count) +{ + virtio_blk_rw((struct virtio_blk_device *)dev, pos, buffer, count, VIRTIO_BLK_T_IN); + + return count; +} + +static rt_size_t virtio_blk_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t count) +{ + virtio_blk_rw((struct virtio_blk_device *)dev, pos, (void *)buffer, count, VIRTIO_BLK_T_OUT); + + return count; +} + +static rt_err_t virtio_blk_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct virtio_blk_device *virtio_blk_dev = (struct virtio_blk_device *)dev; + + switch (cmd) + { + case RT_DEVICE_CTRL_BLK_GETGEOME: + { + struct rt_device_blk_geometry *geometry = (struct rt_device_blk_geometry *)args; + + if (geometry == RT_NULL) + { + status = -RT_ERROR; + break; + } + + geometry->bytes_per_sector = VIRTIO_BLK_BYTES_PER_SECTOR; + geometry->block_size = virtio_blk_dev->config->blk_size; + geometry->sector_count = virtio_blk_dev->config->capacity; + } + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_blk_ops = +{ + RT_NULL, + RT_NULL, + RT_NULL, + virtio_blk_read, + virtio_blk_write, + virtio_blk_control +}; +#endif + +static void virtio_blk_isr(int irqno, void *param) +{ + rt_uint32_t id; + struct virtio_blk_device *virtio_blk_dev = (struct virtio_blk_device *)param; + struct virtio_device *virtio_dev = &virtio_blk_dev->virtio_dev; + struct virtq *queue = &virtio_dev->queues[VIRTIO_BLK_QUEUE]; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + virtio_interrupt_ack(virtio_dev); + rt_hw_dsb(); + + /* The device increments disk.used->idx when it adds an entry to the used ring */ + while (queue->used_idx != queue->used->idx) + { + rt_hw_dsb(); + id = queue->used->ring[queue->used_idx % queue->num].id; + + RT_ASSERT(virtio_blk_dev->info[id].status == 0); + + /* Done with buffer */ + virtio_blk_dev->info[id].valid = RT_FALSE; + + queue->used_idx++; + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq) +{ + static int dev_no = 0; + char dev_name[RT_NAME_MAX]; + struct virtio_device *virtio_dev; + struct virtio_blk_device *virtio_blk_dev; + + virtio_blk_dev = rt_malloc(sizeof(struct virtio_blk_device)); + + if (virtio_blk_dev == RT_NULL) + { + return -RT_ENOMEM; + } + + virtio_dev = &virtio_blk_dev->virtio_dev; + virtio_dev->irq = irq; + virtio_dev->mmio_base = mmio_base; + + virtio_blk_dev->config = (struct virtio_blk_config *)virtio_dev->mmio_config->config; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&virtio_dev->spinlock); +#endif + + virtio_reset_device(virtio_dev); + virtio_status_acknowledge_driver(virtio_dev); + + /* Negotiate features */ + virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~( + (1 << VIRTIO_BLK_F_RO) | + (1 << VIRTIO_BLK_F_MQ) | + (1 << VIRTIO_BLK_F_SCSI) | + (1 << VIRTIO_BLK_F_CONFIG_WCE) | + (1 << VIRTIO_F_ANY_LAYOUT) | + (1 << VIRTIO_F_RING_EVENT_IDX) | + (1 << VIRTIO_F_RING_INDIRECT_DESC)); + + /* Tell device that feature negotiation is complete and we're completely ready */ + virtio_status_driver_ok(virtio_dev); + + if (virtio_queues_alloc(virtio_dev, 1) != RT_EOK) + { + goto _alloc_fail; + } + + /* Initialize queue 0 */ + if (virtio_queue_init(virtio_dev, 0, VIRTIO_BLK_QUEUE_RING_SIZE) != RT_EOK) + { + goto _alloc_fail; + } + + virtio_blk_dev->parent.type = RT_Device_Class_Block; +#ifdef RT_USING_DEVICE_OPS + virtio_blk_dev->parent.ops = &virtio_blk_ops; +#else + virtio_blk_dev->parent.init = RT_NULL; + virtio_blk_dev->parent.open = RT_NULL; + virtio_blk_dev->parent.close = RT_NULL; + virtio_blk_dev->parent.read = virtio_blk_read; + virtio_blk_dev->parent.write = virtio_blk_write; + virtio_blk_dev->parent.control = virtio_blk_control; +#endif + + rt_snprintf(dev_name, RT_NAME_MAX, "virtio-blk%d", dev_no++); + + rt_hw_interrupt_install(irq, virtio_blk_isr, virtio_blk_dev, dev_name); + rt_hw_interrupt_umask(irq); + + return rt_device_register((rt_device_t)virtio_blk_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_REMOVABLE); + +_alloc_fail: + + if (virtio_blk_dev != RT_NULL) + { + virtio_queues_free(virtio_dev); + rt_free(virtio_blk_dev); + } + return -RT_ENOMEM; +} +#endif /* RT_USING_VIRTIO_BLK */ diff --git a/components/drivers/virtio/virtio_blk.h b/components/drivers/virtio/virtio_blk.h new file mode 100644 index 0000000000..548bcee4c8 --- /dev/null +++ b/components/drivers/virtio/virtio_blk.h @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-9-16 GuEe-GUI the first version + * 2021-11-11 GuEe-GUI using virtio common interface + */ + +#ifndef __VIRTIO_BLK_H__ +#define __VIRTIO_BLK_H__ + +#include <rtdef.h> + +#include <virtio.h> + +#define VIRTIO_BLK_QUEUE 0 +#define VIRTIO_BLK_BYTES_PER_SECTOR 512 +#define VIRTIO_BLK_QUEUE_RING_SIZE 4 + +#define VIRTIO_BLK_F_RO 5 /* Disk is read-only */ +#define VIRTIO_BLK_F_SCSI 7 /* Supports scsi command passthru */ +#define VIRTIO_BLK_F_CONFIG_WCE 11 /* Writeback mode available in config */ +#define VIRTIO_BLK_F_MQ 12 /* Support more than one vq */ + +#define VIRTIO_BLK_T_IN 0 /* Read the blk */ +#define VIRTIO_BLK_T_OUT 1 /* Write the blk */ +#define VIRTIO_BLK_T_SCSI_CMD 2 +#define VIRTIO_BLK_T_SCSI_CMD_OUT 3 +#define VIRTIO_BLK_T_FLUSH 4 +#define VIRTIO_BLK_T_FLUSH_OUT 5 + +struct virtio_blk_req +{ + rt_uint32_t type; + rt_uint32_t ioprio; + rt_uint64_t sector; +}; + +struct virtio_blk_config +{ + rt_uint64_t capacity; /* The capacity (in 512-byte sectors). */ + rt_uint32_t size_max; /* The maximum segment size (if VIRTIO_BLK_F_SIZE_MAX) */ + rt_uint32_t seg_max; /* The maximum number of segments (if VIRTIO_BLK_F_SEG_MAX) */ + + /* Geometry of the device (if VIRTIO_BLK_F_GEOMETRY) */ + struct virtio_blk_geometry + { + rt_uint16_t cylinders; + rt_uint8_t heads; + rt_uint8_t sectors; + } geometry; + + rt_uint32_t blk_size; /* Block size of device (if VIRTIO_BLK_F_BLK_SIZE) */ + + struct virtio_blk_topology + { + /* # Of logical blocks per physical block (log2) */ + rt_uint8_t physical_block_exp; + /* Offset of first aligned logical block */ + rt_uint8_t alignment_offset; + /* Suggested minimum I/O size in blocks */ + rt_uint16_t min_io_size; + /* Optimal (suggested maximum) I/O size in blocks */ + rt_uint32_t opt_io_size; + } topology; + + rt_uint8_t writeback; + rt_uint8_t unused0; + rt_uint16_t num_queues; + rt_uint32_t max_discard_sectors; + rt_uint32_t max_discard_seg; + rt_uint32_t discard_sector_alignment; + rt_uint32_t max_write_zeroes_sectors; + rt_uint32_t max_write_zeroes_seg; + rt_uint8_t write_zeroes_may_unmap; + rt_uint8_t unused1[3]; + rt_uint32_t max_secure_erase_sectors; + rt_uint32_t max_secure_erase_seg; + rt_uint32_t secure_erase_sector_alignment; +} __attribute__((packed)); + +struct virtio_blk_device +{ + struct rt_device parent; + + struct virtio_device virtio_dev; + + struct virtio_blk_config *config; + + struct + { + rt_bool_t valid; + rt_uint8_t status; + + struct virtio_blk_req req; + + } info[VIRTIO_BLK_QUEUE_RING_SIZE]; +}; + +rt_err_t rt_virtio_blk_init(rt_ubase_t *mmio_base, rt_uint32_t irq); + +#endif /* __VIRTIO_BLK_H__ */ diff --git a/components/drivers/virtio/virtio_console.c b/components/drivers/virtio/virtio_console.c new file mode 100644 index 0000000000..7f1a9b8af5 --- /dev/null +++ b/components/drivers/virtio/virtio_console.c @@ -0,0 +1,778 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <cpuport.h> + +#ifdef RT_USING_VIRTIO_CONSOLE + +#include <virtio_console.h> + +struct port_device +{ + struct rt_device parent; + + rt_list_t node; + rt_uint32_t port_id; + rt_bool_t rx_notify; + rt_bool_t need_destroy; + + struct virtio_console_device *console; + + struct virtq *queue_rx, *queue_tx; + rt_uint32_t queue_rx_index, queue_tx_index; + +#ifdef RT_USING_SMP + struct rt_spinlock spinlock_rx, spinlock_tx; +#endif + + struct rt_device_notify rx_notify_helper; + + struct + { + char rx_char, tx_char; + } info[VIRTIO_CONSOLE_QUEUE_SIZE]; +}; + +static void virtio_console_send_ctrl(struct virtio_console_device *virtio_console_dev, + struct virtio_console_control *ctrl) +{ + rt_uint16_t id; + struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev; + struct virtq *queue_ctrl_tx; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + queue_ctrl_tx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_TX]; + + id = queue_ctrl_tx->avail->idx % queue_ctrl_tx->num; + + rt_memcpy(&virtio_console_dev->info[id].tx_ctrl, ctrl, sizeof(struct virtio_console_control)); + + virtio_free_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id); + + virtio_fill_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id, + virtio_console_dev->info[id].tx_ctrl_paddr, sizeof(struct virtio_console_control), 0, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, id); + + virtio_queue_notify(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX); + + virtio_alloc_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +static rt_err_t virtio_console_port_init(rt_device_t dev); +static rt_err_t virtio_console_port_open(rt_device_t dev, rt_uint16_t oflag); +static rt_err_t virtio_console_port_close(rt_device_t dev); +static rt_size_t virtio_console_port_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size); +static rt_size_t virtio_console_port_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size); +static rt_err_t virtio_console_port_control(rt_device_t dev, int cmd, void *args); + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_console_port_ops = +{ + virtio_console_port_init, + virtio_console_port_open, + virtio_console_port_close, + virtio_console_port_read, + virtio_console_port_write, + virtio_console_port_control +}; +#endif + +static rt_err_t virtio_console_port_create(struct virtio_console_device *virtio_console_dev) +{ + rt_uint32_t port_id; + char dev_name[RT_NAME_MAX]; + struct port_device *port_dev, *prev_port_dev = RT_NULL; + struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev; + + if (virtio_console_dev->port_nr > 0 && !virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + return -RT_ENOSYS; + } + + if (virtio_console_dev->port_nr >= virtio_console_dev->max_port_nr) + { + return -RT_EFULL; + } + + port_id = 0; + + /* The port device list is always ordered, so just find next number for id */ + rt_list_for_each_entry(port_dev, &virtio_console_dev->port_head, node) + { + if (port_dev->port_id != port_id) + { + break; + } + ++port_id; + prev_port_dev = port_dev; + } + + port_dev = rt_malloc(sizeof(struct port_device)); + + if (port_dev == RT_NULL) + { + return -RT_ENOMEM; + } + + port_dev->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + port_dev->parent.ops = &virtio_console_port_ops; +#else + port_dev->parent.init = virtio_console_port_init; + port_dev->parent.open = virtio_console_port_open; + port_dev->parent.close = virtio_console_port_close; + port_dev->parent.read = virtio_console_port_read; + port_dev->parent.write = virtio_console_port_write; + port_dev->parent.control = virtio_console_port_control; +#endif + + port_dev->parent.rx_indicate = RT_NULL; + port_dev->parent.tx_complete = RT_NULL; + + rt_list_init(&port_dev->node); + port_dev->port_id = port_id; + port_dev->need_destroy = RT_FALSE; + port_dev->rx_notify = RT_TRUE; + port_dev->console = virtio_console_dev; + port_dev->queue_rx_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_RX); + port_dev->queue_tx_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_TX); + port_dev->queue_rx = &virtio_dev->queues[port_dev->queue_rx_index]; + port_dev->queue_tx = &virtio_dev->queues[port_dev->queue_tx_index]; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&port_dev->spinlock_rx); + rt_spin_lock_init(&port_dev->spinlock_tx); +#endif + + rt_snprintf(dev_name, RT_NAME_MAX, "vport%dp%d", virtio_console_dev->console_id, port_id); + + if (rt_device_register((rt_device_t)port_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX) != RT_EOK) + { + rt_free(port_dev); + + return -RT_ERROR; + } + + if (prev_port_dev != RT_NULL) + { + rt_list_insert_after(&prev_port_dev->node, &port_dev->node); + } + else + { + /* Port0 */ + rt_list_insert_after(&virtio_console_dev->port_head, &port_dev->node); + } + + virtio_console_dev->port_nr++; + + return RT_EOK; +} + +static void virtio_console_port_destroy(struct virtio_console_device *virtio_console_dev, + struct port_device *port_dev) +{ + struct virtio_console_control set_ctrl; + + set_ctrl.id = port_dev->port_id; + set_ctrl.event = VIRTIO_CONSOLE_PORT_OPEN; + set_ctrl.value = 0; + + virtio_console_send_ctrl(virtio_console_dev, &set_ctrl); + + virtio_console_dev->port_nr--; + + rt_list_remove(&port_dev->node); + + rt_device_unregister((rt_device_t)port_dev); + + rt_free(port_dev); +} + +static rt_err_t virtio_console_port_init(rt_device_t dev) +{ + rt_uint16_t id; + rt_uint16_t idx[VIRTIO_CONSOLE_QUEUE_SIZE]; + rt_uint16_t rx_queue_index, tx_queue_index; + struct port_device *port_dev = (struct port_device *)dev; + struct virtio_console_device *virtio_console_dev = port_dev->console; + struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev; + struct virtq *queue_rx, *queue_tx; + + rx_queue_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_RX); + tx_queue_index = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(port_dev->port_id, VIRTIO_CONSOLE_QUEUE_DATA_TX); + + queue_rx = &virtio_dev->queues[rx_queue_index]; + queue_tx = &virtio_dev->queues[tx_queue_index]; + + virtio_alloc_desc_chain(virtio_dev, rx_queue_index, queue_rx->num, idx); + virtio_alloc_desc_chain(virtio_dev, tx_queue_index, queue_tx->num, idx); + + for (id = 0; id < queue_rx->num; ++id) + { + void *addr = &port_dev->info[id].rx_char; + + virtio_fill_desc(virtio_dev, rx_queue_index, id, + VIRTIO_VA2PA(addr), sizeof(char), VIRTQ_DESC_F_WRITE, 0); + + queue_rx->avail->ring[id] = id; + } + rt_hw_dsb(); + + queue_rx->avail->flags = 0; + queue_rx->avail->idx = queue_rx->num; + + queue_rx->used_idx = queue_rx->used->idx; + + queue_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; + queue_tx->avail->idx = 0; + + virtio_queue_notify(virtio_dev, rx_queue_index); + + if (virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + struct virtio_console_control set_ctrl; + + set_ctrl.id = VIRTIO_CONSOLE_PORT_BAD_ID; + set_ctrl.event = VIRTIO_CONSOLE_DEVICE_READY; + set_ctrl.value = 1; + + virtio_console_send_ctrl(virtio_console_dev, &set_ctrl); + } + + return RT_EOK; +} + +static rt_err_t virtio_console_port_open(rt_device_t dev, rt_uint16_t oflag) +{ + struct port_device *port_dev = (struct port_device *)dev; + + if (port_dev->port_id == 0 && virtio_has_feature(&port_dev->console->virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + /* Port0 is reserve in multiport */ + return -RT_ERROR; + } + + port_dev->rx_notify = RT_TRUE; + + return RT_EOK; +} + +static rt_err_t virtio_console_port_close(rt_device_t dev) +{ + struct port_device *port_dev = (struct port_device *)dev; + + if (port_dev->need_destroy) + { + virtio_console_port_destroy(port_dev->console, port_dev); + + /* + * We released the device memory in virtio_console_port_destroy, + * rt_device_close has not finished yet, make the return value + * to empty so that rt_device_close will not access the device memory. + */ + return -RT_EEMPTY; + } + + port_dev->rx_notify = RT_FALSE; + + return RT_EOK; +} + +static rt_size_t virtio_console_port_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + rt_off_t i = 0; + rt_uint16_t id; + rt_uint32_t len; + struct port_device *port_dev = (struct port_device *)dev; + struct virtio_device *virtio_dev = &port_dev->console->virtio_dev; + rt_uint32_t queue_rx_index = port_dev->queue_rx_index; + struct virtq *queue_rx = port_dev->queue_rx; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_rx); +#endif + + while (i < size) + { + if (queue_rx->used_idx == queue_rx->used->idx) + { + break; + } + rt_hw_dsb(); + + id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id; + len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len; + + if (len > sizeof(char)) + { + rt_kprintf("%s: Receive buffer's size = %u is too big!\n", port_dev->parent.parent.name, len); + len = sizeof(char); + } + + *((char *)buffer + i) = port_dev->info[id].rx_char; + + queue_rx->used_idx++; + + virtio_submit_chain(virtio_dev, queue_rx_index, id); + + virtio_queue_notify(virtio_dev, queue_rx_index); + + i += len; + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level); +#endif + + size = i; + + return size; +} + +static rt_size_t virtio_console_port_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + char ch = 0; + rt_off_t i = 0; + rt_uint16_t id; + struct port_device *port_dev = (struct port_device *)dev; + struct virtio_device *virtio_dev = &port_dev->console->virtio_dev; + rt_uint32_t queue_tx_index = port_dev->queue_tx_index; + struct virtq *queue_tx = port_dev->queue_tx; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_tx); +#endif + + while (i < size || ch == '\r') + { + id = queue_tx->avail->idx % queue_tx->num; + + /* Keep the way until 'new line' are unified */ + if (ch != '\r') + { + ch = *((const char *)buffer + i); + } + else + { + i -= sizeof(char); + } + + port_dev->info[id].tx_char = ch; + + ch = (ch == '\n' ? '\r' : 0); + + virtio_free_desc(virtio_dev, queue_tx_index, id); + + virtio_fill_desc(virtio_dev, queue_tx_index, id, + VIRTIO_VA2PA(&port_dev->info[id].tx_char), sizeof(char), 0, 0); + + virtio_submit_chain(virtio_dev, queue_tx_index, id); + + virtio_queue_notify(virtio_dev, queue_tx_index); + + virtio_alloc_desc(virtio_dev, queue_tx_index); + + i += sizeof(char); + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&port_dev->spinlock_tx, level); +#endif + + return size; +} + +static rt_err_t virtio_console_port_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct port_device *port_dev = (struct port_device *)dev; + + switch (cmd) + { + case RT_DEVICE_CTRL_NOTIFY_SET: + if (args == RT_NULL) + { + status = -RT_ERROR; + break; + } + rt_memcpy(&port_dev->rx_notify_helper, args, sizeof(port_dev->rx_notify_helper)); + break; + case RT_DEVICE_CTRL_CLR_INT: + /* Disable RX */ + port_dev->rx_notify = RT_FALSE; + break; + case RT_DEVICE_CTRL_SET_INT: + /* Enable RX */ + port_dev->rx_notify = RT_TRUE; + break; + case VIRTIO_DEVICE_CTRL_CONSOLE_PORT_DESTROY: + { + port_dev->need_destroy = RT_TRUE; + port_dev->rx_notify = RT_FALSE; + } + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +static rt_err_t virtio_console_init(rt_device_t dev) +{ + struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)dev; + struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev; + + if (virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + rt_uint16_t id; + rt_uint16_t idx[VIRTIO_CONSOLE_QUEUE_SIZE]; + struct virtq *queue_ctrl_rx, *queue_ctrl_tx; + + queue_ctrl_rx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_RX]; + queue_ctrl_tx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_TX]; + + virtio_alloc_desc_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX, queue_ctrl_rx->num, idx); + virtio_alloc_desc_chain(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_TX, queue_ctrl_tx->num, idx); + + for (id = 0; id < queue_ctrl_rx->num; ++id) + { + void *addr = &virtio_console_dev->info[id].rx_ctrl; + + virtio_fill_desc(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX, id, + VIRTIO_VA2PA(addr), sizeof(struct virtio_console_control), VIRTQ_DESC_F_WRITE, 0); + + queue_ctrl_rx->avail->ring[id] = id; + } + rt_hw_dsb(); + + for (id = 0; id < queue_ctrl_tx->num; ++id) + { + virtio_console_dev->info[id].tx_ctrl_paddr = VIRTIO_VA2PA(&virtio_console_dev->info[id].tx_ctrl); + } + + queue_ctrl_rx->avail->flags = 0; + queue_ctrl_rx->avail->idx = queue_ctrl_rx->num; + + queue_ctrl_rx->used_idx = queue_ctrl_rx->used->idx; + + queue_ctrl_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; + queue_ctrl_tx->avail->idx = 0; + + virtio_queue_notify(virtio_dev, VIRTIO_CONSOLE_QUEUE_CTRL_RX); + } + + return virtio_console_port_create(virtio_console_dev); +} + +static rt_err_t virtio_console_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)dev; + + switch (cmd) + { + case VIRTIO_DEVICE_CTRL_CONSOLE_PORT_CREATE: + status = virtio_console_port_create(virtio_console_dev); + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_console_ops = +{ + virtio_console_init, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + virtio_console_control +}; +#endif + +static void virtio_console_isr(int irqno, void *param) +{ + rt_uint32_t id; + rt_uint32_t len; + struct port_device *port_dev; + struct virtio_console_device *virtio_console_dev = (struct virtio_console_device *)param; + struct virtio_device *virtio_dev = &virtio_console_dev->virtio_dev; + const char *dev_name = virtio_console_dev->parent.parent.name; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + virtio_interrupt_ack(virtio_dev); + rt_hw_dsb(); + + do { + struct virtq *queue_rx; + struct virtio_console_control *ctrl, set_ctrl; + + if (!virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + break; + } + + queue_rx = &virtio_dev->queues[VIRTIO_CONSOLE_QUEUE_CTRL_RX]; + + if (queue_rx->used_idx == queue_rx->used->idx) + { + break; + } + rt_hw_dsb(); + + id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id; + len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len; + + queue_rx->used_idx++; + + if (len != sizeof(struct virtio_console_control)) + { + rt_kprintf("%s: Invalid ctrl!\n", dev_name); + break; + } + + ctrl = &virtio_console_dev->info[id].rx_ctrl; + + switch (ctrl->event) + { + case VIRTIO_CONSOLE_PORT_ADD: + { + set_ctrl.id = ctrl->id; + set_ctrl.event = VIRTIO_CONSOLE_PORT_READY; + set_ctrl.value = 1; + + #ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); + #endif + + virtio_console_send_ctrl(virtio_console_dev, &set_ctrl); + + #ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); + #endif + } + break; + case VIRTIO_CONSOLE_PORT_REMOVE: + break; + case VIRTIO_CONSOLE_RESIZE: + break; + case VIRTIO_CONSOLE_PORT_OPEN: + { + set_ctrl.id = ctrl->id; + set_ctrl.event = VIRTIO_CONSOLE_PORT_OPEN; + set_ctrl.value = 1; + + #ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); + #endif + + virtio_console_send_ctrl(virtio_console_dev, &set_ctrl); + + #ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); + #endif + } + break; + case VIRTIO_CONSOLE_PORT_NAME: + break; + default: + rt_kprintf("%s: Unsupport ctrl[id: %d, event: %d, value: %d]!\n", + dev_name, ctrl->id, ctrl->event, ctrl->value); + break; + } + + } while (0); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + + rt_list_for_each_entry(port_dev, &virtio_console_dev->port_head, node) + { + rt_uint32_t queue_rx_index = port_dev->queue_rx_index; + struct virtq *queue_rx = port_dev->queue_rx; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&port_dev->spinlock_rx); +#endif + + if (queue_rx->used_idx != queue_rx->used->idx) + { + rt_hw_dsb(); + + id = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id; + len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len; + + if (port_dev->rx_notify) + { + #ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level); + #endif + /* Will call virtio_console_port_read to inc used_idx */ + + if (port_dev->parent.rx_indicate != RT_NULL) + { + port_dev->parent.rx_indicate(&port_dev->parent, len); + } + + if (port_dev->rx_notify_helper.notify != RT_NULL) + { + port_dev->rx_notify_helper.notify(port_dev->rx_notify_helper.dev); + } + + #ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&port_dev->spinlock_rx); + #endif + } + else + { + queue_rx->used_idx++; + + virtio_submit_chain(virtio_dev, queue_rx_index, id); + + virtio_queue_notify(virtio_dev, queue_rx_index); + } + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&port_dev->spinlock_rx, level); +#endif + } +} + +rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq) +{ + int i; + rt_size_t queues_num; + static int dev_no = 0; + char dev_name[RT_NAME_MAX]; + struct virtio_device *virtio_dev; + struct virtio_console_device *virtio_console_dev; + + RT_ASSERT(RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR > 0); + + virtio_console_dev = rt_malloc(sizeof(struct virtio_console_device)); + + if (virtio_console_dev == RT_NULL) + { + goto _alloc_fail; + } + + virtio_dev = &virtio_console_dev->virtio_dev; + virtio_dev->irq = irq; + virtio_dev->mmio_base = mmio_base; + + virtio_console_dev->config = (struct virtio_console_config *)virtio_dev->mmio_config->config; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&virtio_dev->spinlock); +#endif + + virtio_reset_device(virtio_dev); + virtio_status_acknowledge_driver(virtio_dev); + + virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~( + (1 << VIRTIO_F_RING_EVENT_IDX) | + (1 << VIRTIO_F_RING_INDIRECT_DESC)); + + virtio_status_driver_ok(virtio_dev); + + if (!virtio_has_feature(virtio_dev, VIRTIO_CONSOLE_F_MULTIPORT)) + { + virtio_console_dev->max_port_nr = 1; + queues_num = 2; + } + else + { + if (virtio_console_dev->config->max_nr_ports > RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR) + { + virtio_console_dev->max_port_nr = RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR; + virtio_console_dev->config->max_nr_ports = virtio_console_dev->max_port_nr; + } + else + { + virtio_console_dev->max_port_nr = virtio_console_dev->config->max_nr_ports; + } + + queues_num = VIRTIO_CONSOLE_PORT_QUEUE_INDEX(virtio_console_dev->max_port_nr, VIRTIO_CONSOLE_QUEUE_DATA_RX); + } + + if (virtio_queues_alloc(virtio_dev, queues_num) != RT_EOK) + { + goto _alloc_fail; + } + + for (i = 0; i < virtio_dev->queues_num; ++i) + { + if (virtio_queue_init(virtio_dev, i, VIRTIO_CONSOLE_QUEUE_SIZE) != RT_EOK) + { + for (; i >= 0; --i) + { + virtio_queue_destroy(virtio_dev, i); + } + goto _alloc_fail; + } + } + + virtio_console_dev->parent.type = RT_Device_Class_Char; +#ifdef RT_USING_DEVICE_OPS + virtio_console_dev->parent.ops = &virtio_console_ops; +#else + virtio_console_dev->parent.init = virtio_console_init; + virtio_console_dev->parent.open = RT_NULL; + virtio_console_dev->parent.close = RT_NULL; + virtio_console_dev->parent.read = RT_NULL; + virtio_console_dev->parent.write = RT_NULL; + virtio_console_dev->parent.control = virtio_console_control; +#endif + + virtio_console_dev->parent.rx_indicate = RT_NULL; + virtio_console_dev->parent.tx_complete = RT_NULL; + + virtio_console_dev->console_id = dev_no; + virtio_console_dev->port_nr = 0; + rt_list_init(&virtio_console_dev->port_head); + + rt_snprintf(dev_name, RT_NAME_MAX, "virtio-console%d", dev_no++); + + rt_hw_interrupt_install(irq, virtio_console_isr, virtio_console_dev, dev_name); + rt_hw_interrupt_umask(irq); + + return rt_device_register((rt_device_t)virtio_console_dev, dev_name, RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX); + +_alloc_fail: + + if (virtio_console_dev != RT_NULL) + { + virtio_queues_free(virtio_dev); + rt_free(virtio_console_dev); + } + return -RT_ENOMEM; +} +#endif /* RT_USING_VIRTIO_CONSOLE */ diff --git a/components/drivers/virtio/virtio_console.h b/components/drivers/virtio/virtio_console.h new file mode 100644 index 0000000000..f7d8d97e1d --- /dev/null +++ b/components/drivers/virtio/virtio_console.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_CONSOLE_H__ +#define __VIRTIO_CONSOLE_H__ + +#include <rtdef.h> + +#include <virtio.h> + +#ifndef RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR +#define RT_USING_VIRTIO_CONSOLE_PORT_MAX_NR 4 +#endif + +#define VIRTIO_CONSOLE_QUEUE_DATA_RX 0 +#define VIRTIO_CONSOLE_QUEUE_DATA_TX 1 +#define VIRTIO_CONSOLE_QUEUE_CTRL_RX 2 +#define VIRTIO_CONSOLE_QUEUE_CTRL_TX 3 +#define VIRTIO_CONSOLE_QUEUE_SIZE 64 + +/* Every port has data rx & tx, and port0 has ctrl rx & tx in multiport */ +#define VIRTIO_CONSOLE_PORT_QUEUE_INDEX(id, queue) ((id) * 2 + (!!(id)) * 2 + (queue)) + +#define VIRTIO_CONSOLE_PORT_BAD_ID (~(rt_uint32_t)0) + +#define VIRTIO_CONSOLE_F_SIZE 0 /* Does host provide console size? */ +#define VIRTIO_CONSOLE_F_MULTIPORT 1 /* Does host provide multiple ports? */ +#define VIRTIO_CONSOLE_F_EMERG_WRITE 2 /* Does host support emergency write? */ + +struct virtio_console_config +{ + rt_uint16_t cols; + rt_uint16_t rows; + rt_uint32_t max_nr_ports; + rt_uint32_t emerg_wr; +} __attribute__((packed)); + +struct virtio_console_control +{ + rt_uint32_t id; /* Port number */ + rt_uint16_t event; /* The kind of control event */ + rt_uint16_t value; /* Extra information for the event */ +}; + +enum virtio_console_control_event +{ + VIRTIO_CONSOLE_DEVICE_READY = 0, + VIRTIO_CONSOLE_PORT_ADD, + VIRTIO_CONSOLE_PORT_REMOVE, + VIRTIO_CONSOLE_PORT_READY, + VIRTIO_CONSOLE_CONSOLE_PORT, + VIRTIO_CONSOLE_RESIZE, + VIRTIO_CONSOLE_PORT_OPEN, + VIRTIO_CONSOLE_PORT_NAME, +}; + +struct virtio_console_resize +{ + rt_uint16_t cols; + rt_uint16_t rows; +}; + +struct virtio_console_device +{ + struct rt_device parent; + + struct virtio_device virtio_dev; + + rt_uint32_t console_id; + rt_size_t port_nr; + rt_size_t max_port_nr; + rt_list_t port_head; + struct virtio_console_config *config; + + struct + { + rt_ubase_t tx_ctrl_paddr; + struct virtio_console_control rx_ctrl, tx_ctrl; + } info[VIRTIO_CONSOLE_QUEUE_SIZE]; +}; + +rt_err_t rt_virtio_console_init(rt_ubase_t *mmio_base, rt_uint32_t irq); + +enum +{ + VIRTIO_DEVICE_CTRL_CONSOLE_PORT_CREATE = 0x20, + VIRTIO_DEVICE_CTRL_CONSOLE_PORT_DESTROY, +}; + +#endif /* __VIRTIO_CONSOLE_H__ */ diff --git a/components/drivers/virtio/virtio_gpu.c b/components/drivers/virtio/virtio_gpu.c new file mode 100644 index 0000000000..67ce28a876 --- /dev/null +++ b/components/drivers/virtio/virtio_gpu.c @@ -0,0 +1,934 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <cpuport.h> + +#ifdef RT_USING_VIRTIO_GPU + +#include <virtio_gpu.h> + +static struct virtio_gpu_device *_primary_virtio_gpu_dev = RT_NULL; + +static rt_ubase_t _pixel_format_convert(rt_ubase_t format, rt_bool_t to_virtio_gpu_format) +{ + rt_ubase_t ret = 0; + + if (to_virtio_gpu_format) + { + switch (format) + { + case RTGRAPHIC_PIXEL_FORMAT_RGB888: + ret = VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM; + break; + case RTGRAPHIC_PIXEL_FORMAT_ARGB888: + ret = VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM; + break; + case RTGRAPHIC_PIXEL_FORMAT_ABGR888: + ret = VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM; + break; + default: + break; + } + } + else + { + switch (format) + { + case VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM: + ret = RTGRAPHIC_PIXEL_FORMAT_RGB888; + break; + case VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM: + ret = RTGRAPHIC_PIXEL_FORMAT_ARGB888; + break; + case VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM: + ret = RTGRAPHIC_PIXEL_FORMAT_ABGR888; + break; + default: + break; + } + } + + return ret; +} + +static void virtio_gpu_ctrl_send_command(struct virtio_gpu_device *virtio_gpu_dev, + const void *cmd, rt_size_t cmd_len, void *res, rt_size_t res_len) +{ + rt_uint16_t idx[2]; + void *addr = &virtio_gpu_dev->gpu_request; + void *ret_res = ((rt_uint8_t *)addr + cmd_len); + struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + while (virtio_alloc_desc_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, 2, idx)) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + rt_memcpy(&virtio_gpu_dev->gpu_request, cmd, cmd_len); + + virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0], + VIRTIO_VA2PA(addr), cmd_len, VIRTQ_DESC_F_NEXT, idx[1]); + + virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[1], + VIRTIO_VA2PA(addr) + cmd_len, res_len, VIRTQ_DESC_F_WRITE, 0); + + rt_memset(ret_res, 0, res_len); + + virtio_gpu_dev->info[idx[0]].ctrl_valid = RT_TRUE; + + virtio_submit_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0]); + + virtio_queue_notify(virtio_dev, VIRTIO_GPU_QUEUE_CTRL); + + while (virtio_gpu_dev->info[idx[0]].ctrl_valid) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + virtio_free_desc_chain(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, idx[0]); + + rt_memcpy(res, ret_res, res_len); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +static void virtio_gpu_cursor_send_command(struct virtio_gpu_device *virtio_gpu_dev, + const void *cmd, rt_size_t cmd_len) +{ + rt_uint16_t id; + void *addr; + struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + while ((id = virtio_alloc_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR)) == VIRTQ_INVALID_DESC_ID) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + addr = &virtio_gpu_dev->info[id].cursor_cmd; + virtio_gpu_dev->info[id].cursor_valid = RT_TRUE; + + rt_memcpy(addr, cmd, cmd_len); + + virtio_fill_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id, VIRTIO_VA2PA(addr), cmd_len, 0, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id); + + virtio_queue_notify(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR); + + while (virtio_gpu_dev->info[id].cursor_valid) + { +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + rt_thread_yield(); + +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + } + + virtio_free_desc(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, id); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +static rt_err_t virtio_gpu_create_2d_resource(struct virtio_gpu_device *virtio_gpu_dev, enum virtio_gpu_formats format, + rt_uint32_t *resource_id, rt_uint32_t width, rt_uint32_t height) +{ + struct virtio_gpu_ctrl_hdr res; + struct virtio_gpu_resource_create_2d req; + + *resource_id = ++virtio_gpu_dev->next_resource_id; + + req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_CREATE_2D; + req.resource_id = *resource_id; + req.format = format; + req.width = width; + req.height = height; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_unref_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id) +{ + struct virtio_gpu_ctrl_hdr res; + struct virtio_gpu_resource_unref req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_UNREF; + req.resource_id = resource_id; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_attach_backing_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id, + void *buffer, rt_size_t size) +{ + struct virtio_gpu_ctrl_hdr res; + struct + { + struct virtio_gpu_resource_attach_backing req; + struct virtio_gpu_mem_entry mem; + } req; + + rt_memset(&req, 0, sizeof(req)); + + req.req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING; + req.req.resource_id = resource_id; + req.req.nr_entries = 1; + + req.mem.addr = VIRTIO_VA2PA(buffer); + req.mem.length = size; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_set_scanout(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id, + rt_uint32_t resource_id, rt_uint32_t width, rt_uint32_t height) +{ + struct virtio_gpu_ctrl_hdr res; + struct virtio_gpu_set_scanout req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_SET_SCANOUT; + req.r.x = 0; + req.r.y = 0; + req.r.width = width; + req.r.height = height; + req.scanout_id = scanout_id; + req.resource_id = resource_id; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_flush_resource(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id, + rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height) +{ + struct virtio_gpu_ctrl_hdr res; + struct virtio_gpu_resource_flush req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_RESOURCE_FLUSH; + req.r.x = x; + req.r.y = y; + req.r.width = width; + req.r.height = height; + req.resource_id = resource_id; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_transfer_to_host_2d(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id, + rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height, rt_uint32_t offset) +{ + struct virtio_gpu_ctrl_hdr res; + struct virtio_gpu_transfer_to_host_2d req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D; + req.r.x = x; + req.r.y = y; + req.r.width = width; + req.r.height = height; + req.offset = offset; + req.resource_id = resource_id; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &res, sizeof(res)); + + if (res.type == VIRTIO_GPU_RESP_OK_NODATA) + { + return RT_EOK; + } + + return -RT_ERROR; +} + +static rt_err_t virtio_gpu_gfx_flush_2d(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t resource_id, + rt_uint32_t x, rt_uint32_t y, rt_uint32_t width, rt_uint32_t height) +{ + rt_err_t status = virtio_gpu_transfer_to_host_2d(virtio_gpu_dev, resource_id, x, y, width, height, 0); + + if (status == RT_EOK) + { + status = virtio_gpu_flush_resource(virtio_gpu_dev, resource_id, x, y, width, height); + } + + return status; +} + +static rt_err_t virtio_gpu_update_cursor(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id, + rt_uint32_t resource_id, rt_uint32_t hot_x, rt_uint32_t hot_y) +{ + struct virtio_gpu_update_cursor req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_UPDATE_CURSOR; + req.pos.scanout_id = scanout_id; + req.resource_id = resource_id; + req.hot_x = hot_x; + req.hot_y = hot_y; + + virtio_gpu_cursor_send_command(virtio_gpu_dev, &req, sizeof(req)); + + return RT_EOK; +} + +static rt_err_t virtio_gpu_cursor_move(struct virtio_gpu_device *virtio_gpu_dev, rt_uint32_t scanout_id, + rt_uint32_t resource_id, rt_uint32_t x, rt_uint32_t y) +{ + struct virtio_gpu_update_cursor req; + + rt_memset(&req, 0, sizeof(req)); + + req.hdr.type = VIRTIO_GPU_CMD_MOVE_CURSOR; + req.pos.scanout_id = scanout_id; + req.pos.x = x; + req.pos.y = y; + req.resource_id = resource_id; + + virtio_gpu_cursor_send_command(virtio_gpu_dev, &req, sizeof(req)); + + return RT_EOK; +} + +static rt_err_t virtio_gpu_cursor_set_img(struct virtio_gpu_device *virtio_gpu_dev, void *img) +{ + rt_err_t status; + + rt_memcpy(virtio_gpu_dev->cursor_img, img, VIRTIO_GPU_CURSOR_IMG_SIZE); + + status = virtio_gpu_attach_backing_resource(virtio_gpu_dev, + virtio_gpu_dev->cursor_resource_id, virtio_gpu_dev->cursor_img, VIRTIO_GPU_CURSOR_IMG_SIZE); + + if (status != RT_EOK) + { + return status; + } + + status = virtio_gpu_transfer_to_host_2d(virtio_gpu_dev, virtio_gpu_dev->cursor_resource_id, + 0, 0, VIRTIO_GPU_CURSOR_WIDTH, VIRTIO_GPU_CURSOR_HEIGHT, 0); + + return status; +} + +static rt_err_t virtio_gpu_get_display_info(struct virtio_gpu_device *virtio_gpu_dev) +{ + int i; + struct virtio_gpu_ctrl_hdr req; + struct virtio_gpu_resp_display_info info; + + rt_memset(&req, 0, sizeof(req)); + req.type = VIRTIO_GPU_CMD_GET_DISPLAY_INFO; + + virtio_gpu_ctrl_send_command(virtio_gpu_dev, &req, sizeof(req), &info, sizeof(info)); + + if (info.hdr.type != VIRTIO_GPU_RESP_OK_DISPLAY_INFO) + { + return -RT_ERROR; + } + + for (i = 0; i < VIRTIO_GPU_MAX_SCANOUTS; ++i) + { + if (info.pmodes[i].enabled) + { + if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + rt_memcpy(&virtio_gpu_dev->pmode, &info.pmodes[i], sizeof(virtio_gpu_dev->pmode)); + virtio_gpu_dev->pmode_id = i; + } + } + } + + return RT_EOK; +} + +static rt_err_t virtio_gpu_init(rt_device_t dev) +{ + rt_err_t status; + struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev; + struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev; + struct virtq *queue_ctrl, *queue_cursor; + + queue_ctrl = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CTRL]; + queue_cursor = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CURSOR]; + + queue_ctrl->avail->flags = 0; + queue_cursor->avail->flags = 0; + + status = virtio_gpu_get_display_info(virtio_gpu_dev); + + if (virtio_gpu_dev->pmode_id != VIRTIO_GPU_INVALID_PMODE_ID && _primary_virtio_gpu_dev == RT_NULL) + { + /* This device is ready */ + _primary_virtio_gpu_dev = virtio_gpu_dev; + } + + return status; +} + +static rt_size_t virtio_gpu_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev; + + if (virtio_gpu_dev->framebuffer == RT_NULL || pos + size >= virtio_gpu_dev->smem_len) + { + return 0; + } + + rt_mutex_take(&virtio_gpu_dev->rw_mutex, RT_WAITING_FOREVER); + + rt_memcpy(buffer, (rt_uint8_t *)virtio_gpu_dev->framebuffer + pos, size); + + rt_mutex_release(&virtio_gpu_dev->rw_mutex); + + return size; +} + +static rt_size_t virtio_gpu_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev; + + if (virtio_gpu_dev->framebuffer == RT_NULL || pos + size >= virtio_gpu_dev->smem_len) + { + return 0; + } + + rt_mutex_take(&virtio_gpu_dev->rw_mutex, RT_WAITING_FOREVER); + + rt_memcpy((rt_uint8_t *)virtio_gpu_dev->framebuffer + pos, buffer, size); + + rt_mutex_release(&virtio_gpu_dev->rw_mutex); + + return size; +} + +static rt_err_t virtio_gpu_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)dev; + + switch (cmd) + { + case VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY: + + _primary_virtio_gpu_dev = virtio_gpu_dev; + + return status; + } + + if (args == RT_NULL) + { + return -RT_ERROR; + } + + switch (cmd) + { + case RTGRAPHIC_CTRL_RECT_UPDATE: + { + struct rt_device_rect_info *info = (struct rt_device_rect_info *)args; + + if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + status = -RT_ERROR; + break; + } + + status = virtio_gpu_gfx_flush_2d(virtio_gpu_dev, virtio_gpu_dev->display_resource_id, + info->x, info->y, info->width, info->height); + } + break; + case RTGRAPHIC_CTRL_GET_INFO: + { + struct rt_device_graphic_info *info = (struct rt_device_graphic_info *)args; + + info->pixel_format = _pixel_format_convert((rt_ubase_t)args, RT_FALSE); + info->bits_per_pixel = VIRTIO_GPU_FORMAT_BPP; + info->pitch = virtio_gpu_dev->pmode.r.width * VIRTIO_GPU_FORMAT_PIXEL; + info->width = virtio_gpu_dev->pmode.r.width; + info->height = virtio_gpu_dev->pmode.r.height; + info->framebuffer = virtio_gpu_dev->framebuffer; + info->smem_len = virtio_gpu_dev->smem_len; + } + break; + case VIRTIO_DEVICE_CTRL_GPU_CREATE_2D: + + virtio_gpu_dev->format = _pixel_format_convert((rt_ubase_t)args, RT_TRUE); + + if (virtio_gpu_dev->format == 0 || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + status = -RT_ERROR; + break; + } + + status = virtio_gpu_create_2d_resource(virtio_gpu_dev, virtio_gpu_dev->format, + &virtio_gpu_dev->display_resource_id, virtio_gpu_dev->pmode.r.width, virtio_gpu_dev->pmode.r.height); + + if (status != RT_EOK) + { + break; + } + + virtio_gpu_dev->smem_len = + virtio_gpu_dev->pmode.r.width * virtio_gpu_dev->pmode.r.height * VIRTIO_GPU_FORMAT_PIXEL; + virtio_gpu_dev->smem_len = RT_ALIGN(virtio_gpu_dev->smem_len, VIRTIO_PAGE_SIZE); + virtio_gpu_dev->framebuffer = rt_malloc_align(virtio_gpu_dev->smem_len, VIRTIO_PAGE_SIZE); + + if (virtio_gpu_dev->framebuffer == RT_NULL) + { + virtio_gpu_unref_resource(virtio_gpu_dev, virtio_gpu_dev->display_resource_id); + + status = -RT_ENOMEM; + break; + } + + status = virtio_gpu_attach_backing_resource(virtio_gpu_dev, + virtio_gpu_dev->display_resource_id, virtio_gpu_dev->framebuffer, virtio_gpu_dev->smem_len); + + if (status != RT_EOK) + { + break; + } + + status = virtio_gpu_set_scanout(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->display_resource_id, + virtio_gpu_dev->pmode.r.width, virtio_gpu_dev->pmode.r.height); + + break; + case VIRTIO_DEVICE_CTRL_CURSOR_SETUP: + + if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + status = -RT_ERROR; + break; + } + + rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER); + + status = virtio_gpu_create_2d_resource(virtio_gpu_dev, virtio_gpu_dev->format, + &virtio_gpu_dev->cursor_resource_id, VIRTIO_GPU_CURSOR_WIDTH, VIRTIO_GPU_CURSOR_HEIGHT); + + if (status != RT_EOK) + { + goto _cursor_setup_end; + } + + status = virtio_gpu_cursor_set_img(virtio_gpu_dev, args); + + if (status != RT_EOK) + { + goto _cursor_setup_end; + } + + virtio_gpu_dev->cursor_x = 0; + virtio_gpu_dev->cursor_y = 0; + + status = virtio_gpu_update_cursor(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id, + virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y); + + if (status == RT_EOK) + { + virtio_gpu_dev->cursor_enable = RT_TRUE; + } + +_cursor_setup_end: + rt_mutex_release(&virtio_gpu_dev->ops_mutex); + + break; + case VIRTIO_DEVICE_CTRL_CURSOR_SET_IMG: + + if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || !virtio_gpu_dev->cursor_enable) + { + status = -RT_ERROR; + break; + } + + rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER); + + status = virtio_gpu_cursor_set_img(virtio_gpu_dev, args); + + if (status != RT_EOK) + { + goto _cursor_set_img_end; + } + + status = virtio_gpu_update_cursor(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id, + virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y); + +_cursor_set_img_end: + rt_mutex_release(&virtio_gpu_dev->ops_mutex); + + break; + case VIRTIO_DEVICE_CTRL_CURSOR_MOVE: + + if (virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || !virtio_gpu_dev->cursor_enable) + { + status = -RT_ERROR; + break; + } + + rt_mutex_take(&virtio_gpu_dev->ops_mutex, RT_WAITING_FOREVER); + + virtio_gpu_dev->cursor_x = ((rt_uint32_t *)args)[0]; + virtio_gpu_dev->cursor_y = ((rt_uint32_t *)args)[1]; + + status = virtio_gpu_cursor_move(virtio_gpu_dev, virtio_gpu_dev->pmode_id, virtio_gpu_dev->cursor_resource_id, + virtio_gpu_dev->cursor_x, virtio_gpu_dev->cursor_y); + + rt_mutex_release(&virtio_gpu_dev->ops_mutex); + + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_gpu_ops = +{ + virtio_gpu_init, + RT_NULL, + RT_NULL, + virtio_gpu_read, + virtio_gpu_write, + virtio_gpu_control +}; +#endif + +static void virtio_gpu_set_pixel(const char *pixel, int x, int y) +{ + rt_uint8_t *fb; + struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev; + + if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + return; + } + + fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer; + fb += (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL; + *((rt_uint32_t *)fb) = *((rt_uint32_t *)pixel); +} + +static void virtio_gpu_get_pixel(char *pixel, int x, int y) +{ + rt_uint8_t *fb; + struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev; + + if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID) + { + return; + } + + fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer; + *((rt_uint32_t *)pixel) = *(fb + (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL); +} + +static void virtio_gpu_draw_hline(const char *pixel, int x1, int x2, int y) +{ + int i; + rt_uint8_t *fb; + rt_uint32_t color = *((rt_uint32_t *)pixel); + struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev; + + if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || + x1 < 0 || x2 < 0 || y < 0) + { + return; + } + + if (x1 > x2) + { + x1 ^= x2; + x2 ^= x1; + x1 ^= x2; + } + + fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer; + fb += (y * virtio_gpu_dev->pmode.r.width + x1) * VIRTIO_GPU_FORMAT_PIXEL; + + for (i = x1; i < x2; ++i) + { + *((rt_uint32_t *)fb) = color; + + fb += VIRTIO_GPU_FORMAT_PIXEL; + } +} + +static void virtio_gpu_draw_vline(const char *pixel, int x, int y1, int y2) +{ + int i; + rt_uint8_t *fb; + rt_uint16_t pitch; + rt_uint32_t color = *((rt_uint32_t *)pixel); + struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev; + + if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || + x < 0 || y1 < 0 || y2 < 0) + { + return; + } + + if (y1 > y2) + { + y1 ^= y2; + y2 ^= y1; + y1 ^= y2; + } + + fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer; + fb += (y1 * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL; + + pitch = virtio_gpu_dev->pmode.r.width * VIRTIO_GPU_FORMAT_PIXEL; + + for (i = y1; i < y2; ++i) + { + *((rt_uint32_t *)fb) = color; + + fb += pitch; + } +} + +static void virtio_gpu_blit_line(const char *pixel, int x, int y, rt_size_t size) +{ + int i; + rt_uint8_t *fb; + rt_uint32_t *colors = (rt_uint32_t *)pixel; + struct virtio_gpu_device *virtio_gpu_dev = _primary_virtio_gpu_dev; + + if (virtio_gpu_dev == RT_NULL || virtio_gpu_dev->pmode_id == VIRTIO_GPU_INVALID_PMODE_ID || x < 0 || y < 0) + { + return; + } + + fb = (rt_uint8_t *)virtio_gpu_dev->framebuffer; + fb += (y * virtio_gpu_dev->pmode.r.width + x) * VIRTIO_GPU_FORMAT_PIXEL; + + for (i = 0; i < size; ++i) + { + *((rt_uint32_t *)fb) = *colors++; + fb += VIRTIO_GPU_FORMAT_PIXEL; + } +} + +static struct rt_device_graphic_ops virtio_gpu_graphic_ops = +{ + virtio_gpu_set_pixel, + virtio_gpu_get_pixel, + virtio_gpu_draw_hline, + virtio_gpu_draw_vline, + virtio_gpu_blit_line +}; + +static void virtio_gpu_isr(int irqno, void *param) +{ + rt_uint16_t id; + struct virtio_gpu_device *virtio_gpu_dev = (struct virtio_gpu_device *)param; + struct virtio_device *virtio_dev = &virtio_gpu_dev->virtio_dev; + struct virtq *queue_ctrl = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CTRL]; + struct virtq *queue_cursor = &virtio_dev->queues[VIRTIO_GPU_QUEUE_CURSOR]; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + virtio_interrupt_ack(virtio_dev); + rt_hw_dsb(); + + while (queue_ctrl->used_idx != queue_ctrl->used->idx) + { + rt_hw_dsb(); + id = queue_ctrl->used->ring[queue_ctrl->used_idx % queue_ctrl->num].id; + + virtio_gpu_dev->info[id].ctrl_valid = RT_FALSE; + + queue_ctrl->used_idx++; + } + + while (queue_cursor->used_idx != queue_cursor->used->idx) + { + rt_hw_dsb(); + id = queue_cursor->used->ring[queue_cursor->used_idx % queue_cursor->num].id; + + virtio_gpu_dev->info[id].cursor_valid = RT_FALSE; + + queue_cursor->used_idx++; + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq) +{ + static int dev_no = 0; + char dev_name[RT_NAME_MAX]; + struct virtio_device *virtio_dev; + struct virtio_gpu_device *virtio_gpu_dev; + + virtio_gpu_dev = rt_malloc(sizeof(struct virtio_gpu_device)); + + if (virtio_gpu_dev == RT_NULL) + { + goto _alloc_fail; + } + + virtio_dev = &virtio_gpu_dev->virtio_dev; + virtio_dev->irq = irq; + virtio_dev->mmio_base = mmio_base; + + virtio_gpu_dev->pmode_id = VIRTIO_GPU_INVALID_PMODE_ID; + virtio_gpu_dev->display_resource_id = 0; + virtio_gpu_dev->cursor_resource_id = 0; + virtio_gpu_dev->next_resource_id = 0; + virtio_gpu_dev->framebuffer = RT_NULL; + virtio_gpu_dev->smem_len = 0; + virtio_gpu_dev->cursor_enable = RT_FALSE; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&virtio_dev->spinlock); +#endif + + virtio_reset_device(virtio_dev); + virtio_status_acknowledge_driver(virtio_dev); + + virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~( + (1 << VIRTIO_F_RING_EVENT_IDX) | + (1 << VIRTIO_F_RING_INDIRECT_DESC)); + + virtio_status_driver_ok(virtio_dev); + + if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_GPU_QUEUE_CTRL, VIRTIO_GPU_QUEUE_SIZE) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_GPU_QUEUE_CURSOR, VIRTIO_GPU_QUEUE_SIZE) != RT_EOK) + { + virtio_queue_destroy(virtio_dev, VIRTIO_GPU_QUEUE_CTRL); + + goto _alloc_fail; + } + + virtio_gpu_dev->parent.type = RT_Device_Class_Graphic; +#ifdef RT_USING_DEVICE_OPS + virtio_gpu_dev->parent.ops = &virtio_gpu_ops; +#else + virtio_gpu_dev->parent.init = virtio_gpu_init; + virtio_gpu_dev->parent.open = RT_NULL; + virtio_gpu_dev->parent.close = RT_NULL; + virtio_gpu_dev->parent.read = virtio_gpu_read; + virtio_gpu_dev->parent.write = virtio_gpu_write; + virtio_gpu_dev->parent.control = virtio_gpu_control; +#endif + virtio_gpu_dev->parent.user_data = &virtio_gpu_graphic_ops; + + rt_snprintf(dev_name, RT_NAME_MAX, "virtio-gpu%d", dev_no++); + + rt_mutex_init(&virtio_gpu_dev->rw_mutex, dev_name, RT_IPC_FLAG_PRIO); + rt_mutex_init(&virtio_gpu_dev->ops_mutex, dev_name, RT_IPC_FLAG_PRIO); + + rt_hw_interrupt_install(irq, virtio_gpu_isr, virtio_gpu_dev, dev_name); + rt_hw_interrupt_umask(irq); + + return rt_device_register((rt_device_t)virtio_gpu_dev, dev_name, RT_DEVICE_FLAG_RDWR); + +_alloc_fail: + + if (virtio_gpu_dev != RT_NULL) + { + virtio_queues_free(virtio_dev); + rt_free(virtio_gpu_dev); + } + return -RT_ENOMEM; +} +#endif /* RT_USING_VIRTIO_GPU */ diff --git a/components/drivers/virtio/virtio_gpu.h b/components/drivers/virtio/virtio_gpu.h new file mode 100644 index 0000000000..0c49ecbd2f --- /dev/null +++ b/components/drivers/virtio/virtio_gpu.h @@ -0,0 +1,412 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_GPU_H__ +#define __VIRTIO_GPU_H__ + +#include <rtdef.h> + +#include <virtio.h> + +#define VIRTIO_GPU_QUEUE_CTRL 0 +#define VIRTIO_GPU_QUEUE_CURSOR 1 +#define VIRTIO_GPU_QUEUE_SIZE 32 + +#define VIRTIO_GPU_F_VIRGL 0 /* VIRTIO_GPU_CMD_CTX_*, VIRTIO_GPU_CMD_*_3D */ +#define VIRTIO_GPU_F_EDID 1 /* VIRTIO_GPU_CMD_GET_EDID */ +#define VIRTIO_GPU_F_RESOURCE_UUID 2 /* VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID */ +#define VIRTIO_GPU_F_RESOURCE_BLOB 3 /* VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB */ +#define VIRTIO_GPU_F_CONTEXT_INIT 4 /* VIRTIO_GPU_CMD_CREATE_CONTEXT with context_init and multiple timelines */ + +#define VIRTIO_GPU_EVENT_DISPLAY (1 << 0) + +#define VIRTIO_GPU_FORMAT_BPP 32 +#define VIRTIO_GPU_FORMAT_PIXEL 4 +#define VIRTIO_GPU_CURSOR_WIDTH 64 +#define VIRTIO_GPU_CURSOR_HEIGHT 64 +#define VIRTIO_GPU_CURSOR_IMG_SIZE (VIRTIO_GPU_CURSOR_WIDTH * VIRTIO_GPU_CURSOR_HEIGHT * VIRTIO_GPU_FORMAT_PIXEL) +#define VIRTIO_GPU_INVALID_PMODE_ID RT_UINT32_MAX + +/* GPU control */ + +struct virtio_gpu_config +{ + rt_uint32_t events_read; + rt_uint32_t events_clear; + rt_uint32_t num_scanouts; /* 1 ~ 16 */ + rt_uint32_t reserved; +}; + +enum virtio_gpu_ctrl_type +{ + VIRTIO_GPU_UNDEFINED = 0, + + /* 2d commands */ + VIRTIO_GPU_CMD_GET_DISPLAY_INFO = 0x0100, + VIRTIO_GPU_CMD_RESOURCE_CREATE_2D, + VIRTIO_GPU_CMD_RESOURCE_UNREF, + VIRTIO_GPU_CMD_SET_SCANOUT, + VIRTIO_GPU_CMD_RESOURCE_FLUSH, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_2D, + VIRTIO_GPU_CMD_RESOURCE_ATTACH_BACKING, + VIRTIO_GPU_CMD_RESOURCE_DETACH_BACKING, + VIRTIO_GPU_CMD_GET_CAPSET_INFO, + VIRTIO_GPU_CMD_GET_CAPSET, + VIRTIO_GPU_CMD_GET_EDID, + VIRTIO_GPU_CMD_RESOURCE_ASSIGN_UUID, + VIRTIO_GPU_CMD_RESOURCE_CREATE_BLOB, + VIRTIO_GPU_CMD_SET_SCANOUT_BLOB, + + /* 3d commands */ + VIRTIO_GPU_CMD_CTX_CREATE = 0x0200, + VIRTIO_GPU_CMD_CTX_DESTROY, + VIRTIO_GPU_CMD_CTX_ATTACH_RESOURCE, + VIRTIO_GPU_CMD_CTX_DETACH_RESOURCE, + VIRTIO_GPU_CMD_RESOURCE_CREATE_3D, + VIRTIO_GPU_CMD_TRANSFER_TO_HOST_3D, + VIRTIO_GPU_CMD_TRANSFER_FROM_HOST_3D, + VIRTIO_GPU_CMD_SUBMIT_3D, + VIRTIO_GPU_CMD_RESOURCE_MAP_BLOB, + VIRTIO_GPU_CMD_RESOURCE_UNMAP_BLOB, + + /* cursor commands */ + VIRTIO_GPU_CMD_UPDATE_CURSOR = 0x0300, + VIRTIO_GPU_CMD_MOVE_CURSOR, + + /* success responses */ + VIRTIO_GPU_RESP_OK_NODATA = 0x1100, + VIRTIO_GPU_RESP_OK_DISPLAY_INFO, + VIRTIO_GPU_RESP_OK_CAPSET_INFO, + VIRTIO_GPU_RESP_OK_CAPSET, + VIRTIO_GPU_RESP_OK_EDID, + VIRTIO_GPU_RESP_OK_RESOURCE_UUID, + VIRTIO_GPU_RESP_OK_MAP_INFO, + + /* error responses */ + VIRTIO_GPU_RESP_ERR_UNSPEC = 0x1200, + VIRTIO_GPU_RESP_ERR_OUT_OF_MEMORY, + VIRTIO_GPU_RESP_ERR_INVALID_SCANOUT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_RESOURCE_ID, + VIRTIO_GPU_RESP_ERR_INVALID_CONTEXT_ID, + VIRTIO_GPU_RESP_ERR_INVALID_PARAMETER, +}; + +#define VIRTIO_GPU_FLAG_FENCE (1 << 0) + +struct virtio_gpu_ctrl_hdr +{ + rt_uint32_t type; + rt_uint32_t flags; + rt_uint64_t fence_id; + rt_uint32_t ctx_id; + rt_uint8_t ring_idx; + rt_uint8_t padding[3]; +}; + +#define VIRTIO_GPU_MAX_SCANOUTS 16 + +struct virtio_gpu_rect +{ + rt_uint32_t x; + rt_uint32_t y; + rt_uint32_t width; + rt_uint32_t height; +}; + +struct virtio_gpu_resp_display_info +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_display_one + { + struct virtio_gpu_rect r; + rt_uint32_t enabled; + rt_uint32_t flags; + } pmodes[VIRTIO_GPU_MAX_SCANOUTS]; +}; + +struct virtio_gpu_get_edid +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t scanout; + rt_uint32_t padding; +}; + +struct virtio_gpu_resp_edid +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t size; + rt_uint32_t padding; + rt_uint8_t edid[1024]; +}; + +enum virtio_gpu_formats +{ + VIRTIO_GPU_FORMAT_B8G8R8A8_UNORM = 1, + VIRTIO_GPU_FORMAT_B8G8R8X8_UNORM = 2, + VIRTIO_GPU_FORMAT_A8R8G8B8_UNORM = 3, + VIRTIO_GPU_FORMAT_X8R8G8B8_UNORM = 4, + + VIRTIO_GPU_FORMAT_R8G8B8A8_UNORM = 67, + VIRTIO_GPU_FORMAT_X8B8G8R8_UNORM = 68, + + VIRTIO_GPU_FORMAT_A8B8G8R8_UNORM = 121, + VIRTIO_GPU_FORMAT_R8G8B8X8_UNORM = 134, +}; + +struct virtio_gpu_resource_create_2d +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t format; + rt_uint32_t width; + rt_uint32_t height; +}; + +struct virtio_gpu_resource_unref +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +struct virtio_gpu_set_scanout +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + rt_uint32_t scanout_id; + rt_uint32_t resource_id; +}; + +struct virtio_gpu_resource_flush +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +struct virtio_gpu_transfer_to_host_2d +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + rt_uint64_t offset; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +struct virtio_gpu_resource_attach_backing +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t nr_entries; +}; + +struct virtio_gpu_mem_entry +{ + rt_uint64_t addr; + rt_uint32_t length; + rt_uint32_t padding; +}; + +struct virtio_gpu_resource_detach_backing +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +struct virtio_gpu_get_capset_info +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t capset_index; + rt_uint32_t padding; +}; + +#define VIRTIO_GPU_CAPSET_VIRGL 1 +#define VIRTIO_GPU_CAPSET_VIRGL2 2 +#define VIRTIO_GPU_CAPSET_GFXSTREAM 3 +#define VIRTIO_GPU_CAPSET_VENUS 4 +#define VIRTIO_GPU_CAPSET_CROSS_DOMAIN 5 + +struct virtio_gpu_resp_capset_info +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t capset_id; + rt_uint32_t capset_max_version; + rt_uint32_t capset_max_size; + rt_uint32_t padding; +}; + +struct virtio_gpu_get_capset +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t capset_id; + rt_uint32_t capset_version; +}; + +struct virtio_gpu_resp_capset +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint8_t capset_data[]; +}; + +struct virtio_gpu_resource_assign_uuid +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +struct virtio_gpu_resp_resource_uuid +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint8_t uuid[16]; +}; + +#define VIRTIO_GPU_BLOB_MEM_GUEST 0x0001 +#define VIRTIO_GPU_BLOB_MEM_HOST3D 0x0002 +#define VIRTIO_GPU_BLOB_MEM_HOST3D_GUEST 0x0003 + +#define VIRTIO_GPU_BLOB_FLAG_USE_MAPPABLE 0x0001 +#define VIRTIO_GPU_BLOB_FLAG_USE_SHAREABLE 0x0002 +#define VIRTIO_GPU_BLOB_FLAG_USE_CROSS_DEVICE 0x0004 + +struct virtio_gpu_resource_create_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t blob_mem; + rt_uint32_t blob_flags; + rt_uint32_t nr_entries; + rt_uint64_t blob_id; + rt_uint64_t size; +}; + +struct virtio_gpu_set_scanout_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_rect r; + rt_uint32_t scanout_id; + rt_uint32_t resource_id; + rt_uint32_t width; + rt_uint32_t height; + rt_uint32_t format; + rt_uint32_t padding; + rt_uint32_t strides[4]; + rt_uint32_t offsets[4]; +}; + +#define VIRTIO_GPU_CONTEXT_INIT_CAPSET_ID_MASK 0x000000ff +struct virtio_gpu_ctx_create +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t nlen; + rt_uint32_t context_init; + char debug_name[64]; +}; + +struct virtio_gpu_resource_map_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t padding; + rt_uint64_t offset; +}; + +#define VIRTIO_GPU_MAP_CACHE_MASK 0x0f +#define VIRTIO_GPU_MAP_CACHE_NONE 0x00 +#define VIRTIO_GPU_MAP_CACHE_CACHED 0x01 +#define VIRTIO_GPU_MAP_CACHE_UNCACHED 0x02 +#define VIRTIO_GPU_MAP_CACHE_WC 0x03 + +struct virtio_gpu_resp_map_info +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t map_info; + rt_uint32_t padding; +}; + +struct virtio_gpu_resource_unmap_blob +{ + struct virtio_gpu_ctrl_hdr hdr; + rt_uint32_t resource_id; + rt_uint32_t padding; +}; + +/* GPU cursor */ + +struct virtio_gpu_cursor_pos +{ + rt_uint32_t scanout_id; + rt_uint32_t x; + rt_uint32_t y; + rt_uint32_t padding; +}; + +struct virtio_gpu_update_cursor +{ + struct virtio_gpu_ctrl_hdr hdr; + struct virtio_gpu_cursor_pos pos; + rt_uint32_t resource_id; + rt_uint32_t hot_x; + rt_uint32_t hot_y; + rt_uint32_t padding; +}; + +struct virtio_gpu_device +{ + struct rt_device parent; + + struct virtio_device virtio_dev; + + /* Current display's info */ + struct virtio_gpu_display_one pmode; + enum virtio_gpu_formats format; + rt_uint32_t pmode_id; + rt_uint32_t cursor_x, cursor_y; + rt_uint32_t display_resource_id; + rt_uint32_t cursor_resource_id; + rt_uint32_t next_resource_id; + + /* Display framebuffer */ + struct rt_mutex rw_mutex; + void *framebuffer; + rt_uint32_t smem_len; + + /* Cursor image info */ + rt_bool_t cursor_enable; + struct rt_mutex ops_mutex; + rt_uint8_t cursor_img[VIRTIO_GPU_CURSOR_IMG_SIZE]; + + /* GPU request info */ + struct virtio_gpu_resp_display_info gpu_request; + + struct + { + rt_bool_t ctrl_valid; + rt_bool_t cursor_valid; + + struct virtio_gpu_update_cursor cursor_cmd; + } info[VIRTIO_GPU_QUEUE_SIZE]; +}; + +rt_err_t rt_virtio_gpu_init(rt_ubase_t *mmio_base, rt_uint32_t irq); + +enum +{ + VIRTIO_DEVICE_CTRL_GPU_SET_PRIMARY = 0x20, + VIRTIO_DEVICE_CTRL_GPU_CREATE_2D, + + VIRTIO_DEVICE_CTRL_CURSOR_SETUP, + VIRTIO_DEVICE_CTRL_CURSOR_SET_IMG, + VIRTIO_DEVICE_CTRL_CURSOR_MOVE, +}; + +#endif /* __VIRTIO_GPU_H__ */ diff --git a/components/drivers/virtio/virtio_input.c b/components/drivers/virtio/virtio_input.c new file mode 100644 index 0000000000..8de3d9c83f --- /dev/null +++ b/components/drivers/virtio/virtio_input.c @@ -0,0 +1,449 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <cpuport.h> + +#ifdef RT_USING_VIRTIO_INPUT + +#include <virtio_input.h> + +static void _set_bit(rt_uint32_t nr, volatile rt_ubase_t *addr) +{ + rt_ubase_t mask = BIT_MASK(nr); + rt_ubase_t *p = ((rt_ubase_t *)addr) + BIT_WORD(nr); + + *p |= mask; +} + +static rt_size_t virtio_input_cfg_select(struct virtio_input_device *virtio_input_dev, + rt_uint8_t select, rt_uint8_t subsel) +{ + struct virtio_input_config *config = virtio_input_dev->config; + + rt_hw_dsb(); + config->select = select; + config->subsel = subsel; + rt_hw_dsb(); + + return config->size; +} + +static void virtio_input_cfg_bits(struct virtio_input_device *virtio_input_dev, + rt_uint8_t select, rt_uint8_t subsel, rt_ubase_t *bits, rt_uint32_t bitcount) +{ + int i; + rt_uint32_t bit; + rt_uint8_t bytes; + rt_uint8_t *virtio_bits; + void *config_base = virtio_input_dev->config; + rt_off_t offset = (rt_size_t)&((struct virtio_input_config *)0)->bitmap; + + bytes = virtio_input_cfg_select(virtio_input_dev, select, subsel); + + if (bytes == 0) + { + return; + } + + if (bitcount > bytes * 8) + { + bitcount = bytes * 8; + } + + /* + * Bitmap in virtio config space is a simple stream of bytes, + * with the first byte carrying bits 0-7, second bits 8-15 and + * so on. + */ + virtio_bits = rt_malloc(bytes); + + if (virtio_bits == RT_NULL) + { + return; + } + + for (i = 0; i < bytes; ++i) + { + void *buffer = (void *)virtio_bits + i; + + if (virtio_input_dev->virtio_dev.mmio_config->version == 1) + { + HWREG8(config_base + offset + i) = *((rt_uint8_t *)buffer); + } + else + { + rt_memcpy(config_base + offset + i, buffer, sizeof(rt_uint8_t)); + } + } + + for (bit = 0; bit < bitcount; ++bit) + { + if (virtio_bits[bit / 8] & (1 << (bit % 8))) + { + _set_bit(bit, bits); + } + } + + rt_free(virtio_bits); + + if (select == VIRTIO_INPUT_CFG_EV_BITS) + { + _set_bit(subsel, virtio_input_dev->ev_bit); + } +} + +static rt_err_t virtio_input_init(rt_device_t dev) +{ + int i; + rt_uint16_t idx[VIRTIO_INPUT_QUEUE_MAX_SIZE]; + struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev; + struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev; + struct virtq *queue_event, *queue_status; + + virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_KEY, virtio_input_dev->key_bit, KEY_CNT); + virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_REL, virtio_input_dev->rel_bit, REL_CNT); + virtio_input_cfg_bits(virtio_input_dev, VIRTIO_INPUT_CFG_EV_BITS, EV_ABS, virtio_input_dev->abs_bit, ABS_CNT); + + queue_event = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_EVENT]; + queue_status = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_STATUS]; + + virtio_alloc_desc_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, queue_event->num, idx); + virtio_alloc_desc_chain(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, queue_status->num, idx); + + for (i = 0; i < queue_event->num; ++i) + { + rt_uint16_t id = i; + void *addr = &virtio_input_dev->recv_events[i]; + + virtio_fill_desc(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id, + VIRTIO_VA2PA(addr), sizeof(struct virtio_input_event), VIRTQ_DESC_F_WRITE, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id); + } + rt_hw_dsb(); + + queue_event->avail->flags = 0; + queue_status->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; + + virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT); + + return RT_EOK; +} + +static rt_size_t virtio_input_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev; + + if (buffer == RT_NULL || pos + size >= virtio_input_dev->virtio_dev.queues[VIRTIO_INPUT_QUEUE_EVENT].num) + { + return 0; + } + + rt_mutex_take(&virtio_input_dev->rw_mutex, RT_WAITING_FOREVER); + + rt_memcpy(buffer, &virtio_input_dev->bcst_events[pos], size); + + rt_mutex_release(&virtio_input_dev->rw_mutex); + + return size; +} + +static rt_size_t virtio_input_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev; + + if (buffer == RT_NULL || pos + size >= virtio_input_dev->virtio_dev.queues[VIRTIO_INPUT_QUEUE_EVENT].num) + { + return 0; + } + + rt_mutex_take(&virtio_input_dev->rw_mutex, RT_WAITING_FOREVER); + + rt_memcpy(&virtio_input_dev->bcst_events[pos], buffer, size); + + rt_mutex_release(&virtio_input_dev->rw_mutex); + + return size; +} + +static rt_err_t virtio_input_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)dev; + struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev; + struct virtio_input_config *config = virtio_input_dev->config; + + if (args == RT_NULL) + { + return -RT_ERROR; + } + + switch (cmd) + { + case VIRTIO_DEVICE_CTRL_INPUT_GET_TYPE: + + *(enum virtio_input_type *)args = virtio_input_dev->type; + + break; + case VIRTIO_DEVICE_CTRL_INPUT_BIND_BSCT_HANDLER: + + virtio_input_dev->bsct_handler = args; + + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_X_INFO: + + virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ABS_INFO, VIRTIO_INPUT_ABS_AXIS_X); + rt_memcpy(args, config, sizeof(struct virtio_input_config)); + + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_Y_INFO: + + virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ABS_INFO, VIRTIO_INPUT_ABS_AXIS_Y); + rt_memcpy(args, config, sizeof(struct virtio_input_config)); + + break; + case VIRTIO_DEVICE_CTRL_INPUT_SET_STATUS: + { + rt_uint16_t id; + void *addr; + struct virtq *queue_status = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_STATUS]; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + id = queue_status->avail->idx % queue_status->num; + addr = &virtio_input_dev->xmit_events[id]; + + rt_memcpy(addr, args, sizeof(struct virtio_input_event)); + + virtio_free_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id); + + virtio_fill_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id, + VIRTIO_VA2PA(addr), sizeof(struct virtio_input_event), 0, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, id); + + virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS); + + virtio_alloc_desc(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + } + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_EV_BIT: + + rt_memcpy(args, virtio_input_dev->ev_bit, sizeof(virtio_input_dev->ev_bit)); + + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_KEY_BIT: + + rt_memcpy(args, virtio_input_dev->key_bit, sizeof(virtio_input_dev->key_bit)); + + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_REL_BIT: + + rt_memcpy(args, virtio_input_dev->rel_bit, sizeof(virtio_input_dev->rel_bit)); + + break; + case VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_BIT: + + rt_memcpy(args, virtio_input_dev->abs_bit, sizeof(virtio_input_dev->abs_bit)); + + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_input_ops = +{ + virtio_input_init, + RT_NULL, + RT_NULL, + virtio_input_read, + virtio_input_write, + virtio_input_control +}; +#endif + +static void virtio_input_isr(int irqno, void *param) +{ + struct virtio_input_device *virtio_input_dev = (struct virtio_input_device *)param; + struct virtio_device *virtio_dev = &virtio_input_dev->virtio_dev; + struct virtq *event_queue = &virtio_dev->queues[VIRTIO_INPUT_QUEUE_EVENT]; + const char *dev_name = virtio_input_dev->parent.parent.name; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + virtio_interrupt_ack(virtio_dev); + rt_hw_dsb(); + + while (event_queue->used_idx != event_queue->used->idx) + { + rt_uint16_t id = event_queue->used->ring[event_queue->used_idx % event_queue->num].id; + rt_uint32_t len = event_queue->used->ring[event_queue->used_idx % event_queue->num].len; + + if (len == sizeof(struct virtio_input_event)) + { + struct virtio_input_event *recv_events = &virtio_input_dev->recv_events[id]; + struct virtio_input_event *bcst_events = &virtio_input_dev->bcst_events[id]; + + if (recv_events->type >= EV_SYN && recv_events->type <= EV_ABS) + { + bcst_events->type = recv_events->type; + bcst_events->code = recv_events->code; + bcst_events->value = recv_events->value; + + if (virtio_input_dev->bsct_handler != RT_NULL) + { + virtio_input_dev->bsct_handler(*bcst_events); + } + } + else + { + rt_kprintf("%s: Unsupport event[type: %02x, code: %02x, value: %08x]!\n", + dev_name, recv_events->type, recv_events->code, recv_events->value); + } + } + else + { + rt_kprintf("%s: Invalid event!\n", dev_name); + } + + event_queue->used_idx++; + + virtio_submit_chain(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, id); + + virtio_queue_notify(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT); + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq) +{ + rt_uint32_t flag; + static int dev_no = 0; + char dev_name[RT_NAME_MAX]; + struct virtio_device *virtio_dev; + struct virtio_input_device *virtio_input_dev; + + virtio_input_dev = rt_malloc(sizeof(struct virtio_input_device)); + + if (virtio_input_dev == RT_NULL) + { + goto _alloc_fail; + } + + virtio_dev = &virtio_input_dev->virtio_dev; + virtio_dev->irq = irq; + virtio_dev->mmio_base = mmio_base; + + virtio_input_dev->config = (struct virtio_input_config *)virtio_dev->mmio_config->config; + virtio_input_dev->bsct_handler = RT_NULL; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&virtio_dev->spinlock); +#endif + + virtio_reset_device(virtio_dev); + virtio_status_acknowledge_driver(virtio_dev); + + virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~( + (1 << VIRTIO_F_RING_EVENT_IDX) | + (1 << VIRTIO_F_RING_INDIRECT_DESC)); + + virtio_status_driver_ok(virtio_dev); + + if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT, VIRTIO_INPUT_EVENT_QUEUE_SIZE) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_INPUT_QUEUE_STATUS, VIRTIO_INPUT_STATUS_QUEUE_SIZE) != RT_EOK) + { + virtio_queue_destroy(virtio_dev, VIRTIO_INPUT_QUEUE_EVENT); + + goto _alloc_fail; + } + + virtio_input_cfg_select(virtio_input_dev, VIRTIO_INPUT_CFG_ID_DEVIDS, 0); + + if (virtio_input_dev->config->ids.product == EV_ABS) + { + virtio_input_dev->type = VIRTIO_INPUT_TYPE_TABLET; + + virtio_input_dev->parent.type = RT_Device_Class_Touch; + + flag = RT_DEVICE_FLAG_STANDALONE | RT_DEVICE_FLAG_INT_RX; + } + else + { + if (virtio_input_dev->config->ids.product == EV_KEY) + { + virtio_input_dev->type = VIRTIO_INPUT_TYPE_KEYBOARD; + } + else + { + virtio_input_dev->type = VIRTIO_INPUT_TYPE_MOUSE; + } + + /* Replace it to "KeyBoard" or "Mouse" if support in the future */ + virtio_input_dev->parent.type = RT_Device_Class_Miscellaneous; + + flag = RT_DEVICE_FLAG_RDWR; + } +#ifdef RT_USING_DEVICE_OPS + virtio_input_dev->parent.ops = &virtio_input_ops; +#else + virtio_input_dev->parent.init = virtio_input_init; + virtio_input_dev->parent.open = RT_NULL; + virtio_input_dev->parent.close = RT_NULL; + virtio_input_dev->parent.read = virtio_input_read; + virtio_input_dev->parent.write = virtio_input_write; + virtio_input_dev->parent.control = virtio_input_control; +#endif + + rt_snprintf(dev_name, RT_NAME_MAX, "virtio-input%d", dev_no++); + + rt_mutex_init(&virtio_input_dev->rw_mutex, dev_name, RT_IPC_FLAG_PRIO); + + rt_hw_interrupt_install(irq, virtio_input_isr, virtio_input_dev, dev_name); + rt_hw_interrupt_umask(irq); + + return rt_device_register((rt_device_t)virtio_input_dev, dev_name, flag); + +_alloc_fail: + + if (virtio_input_dev != RT_NULL) + { + virtio_queues_free(virtio_dev); + rt_free(virtio_input_dev); + } + return -RT_ENOMEM; +} +#endif /* RT_USING_VIRTIO_INPUT */ diff --git a/components/drivers/virtio/virtio_input.h b/components/drivers/virtio/virtio_input.h new file mode 100644 index 0000000000..367d0a5d6d --- /dev/null +++ b/components/drivers/virtio/virtio_input.h @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_INPUT_H__ +#define __VIRTIO_INPUT_H__ + +#include <rtdef.h> + +#include <virtio.h> +#include <virtio_input_event_codes.h> + +#define VIRTIO_INPUT_QUEUE_EVENT 0 +#define VIRTIO_INPUT_QUEUE_STATUS 1 +#define VIRTIO_INPUT_EVENT_QUEUE_SIZE 64 +#define VIRTIO_INPUT_STATUS_QUEUE_SIZE 8 +#define VIRTIO_INPUT_QUEUE_MAX_SIZE (VIRTIO_INPUT_EVENT_QUEUE_SIZE > VIRTIO_INPUT_STATUS_QUEUE_SIZE ? \ + VIRTIO_INPUT_EVENT_QUEUE_SIZE : VIRTIO_INPUT_STATUS_QUEUE_SIZE) + +#define VIRTIO_INPUT_ABS_AXIS_X 0 +#define VIRTIO_INPUT_ABS_AXIS_Y 1 + +enum virtio_input_type +{ + VIRTIO_INPUT_TYPE_KEYBOARD, + VIRTIO_INPUT_TYPE_MOUSE, + VIRTIO_INPUT_TYPE_TABLET, + + VIRTIO_INPUT_TYPE_SIZE, +}; + +enum virtio_input_config_select +{ + VIRTIO_INPUT_CFG_UNSET = 0x00, + VIRTIO_INPUT_CFG_ID_NAME = 0x01, + VIRTIO_INPUT_CFG_ID_SERIAL = 0x02, + VIRTIO_INPUT_CFG_ID_DEVIDS = 0x03, + VIRTIO_INPUT_CFG_PROP_BITS = 0x10, + VIRTIO_INPUT_CFG_EV_BITS = 0x11, + VIRTIO_INPUT_CFG_ABS_INFO = 0x12, +}; + +struct virtio_input_absinfo +{ + rt_uint32_t min; /* Minimum value for the axis */ + rt_uint32_t max; /* Maximum value for the axis */ + rt_uint32_t fuzz; /* Fuzz value that is used to filter noise from the event stream */ + rt_uint32_t flat; /* Within this value will be discarded by joydev interface and reported as 0 instead */ + rt_uint32_t res; /* Resolution for the values reported for the axis */ +}; + +struct virtio_input_devids +{ + rt_uint16_t bustype; + rt_uint16_t vendor; + rt_uint16_t product; + rt_uint16_t version; +}; + +struct virtio_input_config +{ + rt_uint8_t select; + rt_uint8_t subsel; + rt_uint8_t size; + rt_uint8_t reserved[5]; + + union + { + char string[128]; + rt_uint8_t bitmap[128]; + struct virtio_input_absinfo abs; + struct virtio_input_devids ids; + }; +} __attribute__((packed)); + +struct virtio_input_event +{ + rt_uint16_t type; + rt_uint16_t code; + rt_uint32_t value; +}; + +#ifdef ARCH_CPU_64BIT +#define BITS_PER_LONG 64 +#else +#define BITS_PER_LONG 32 +#endif +#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG)) +#define BIT_WORD(nr) ((nr) / BITS_PER_LONG) +#define DIV_ROUND_UP(n, d) (((n) + (d) - 1) / (d)) + +#define BITS_PER_BYTE 8 +#define BITS_PER_TYPE(type) (sizeof(type) * BITS_PER_BYTE) +#define BITS_TO_BYTES(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(char)) +#define BITS_TO_LONGS(nr) DIV_ROUND_UP(nr, BITS_PER_TYPE(long)) + +struct virtio_input_device +{ + struct rt_device parent; + + struct virtio_device virtio_dev; + + rt_ubase_t ev_bit[BITS_TO_LONGS(EV_CNT)]; + rt_ubase_t key_bit[BITS_TO_LONGS(KEY_CNT)]; + rt_ubase_t rel_bit[BITS_TO_LONGS(REL_CNT)]; + rt_ubase_t abs_bit[BITS_TO_LONGS(ABS_CNT)]; + + enum virtio_input_type type; + struct virtio_input_config *config; + + /* Broadcast events */ + struct rt_mutex rw_mutex; + void (*bsct_handler)(struct virtio_input_event event); + struct virtio_input_event bcst_events[VIRTIO_INPUT_EVENT_QUEUE_SIZE]; + + /* Receive events */ + struct virtio_input_event recv_events[VIRTIO_INPUT_EVENT_QUEUE_SIZE]; + + /* Transmit status */ + struct virtio_input_event xmit_events[VIRTIO_INPUT_STATUS_QUEUE_SIZE]; +}; + +enum +{ + VIRTIO_DEVICE_CTRL_INPUT_GET_TYPE = 0x20, + VIRTIO_DEVICE_CTRL_INPUT_BIND_BSCT_HANDLER, + VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_X_INFO, + VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_Y_INFO, + VIRTIO_DEVICE_CTRL_INPUT_SET_STATUS, + + VIRTIO_DEVICE_CTRL_INPUT_GET_EV_BIT, + VIRTIO_DEVICE_CTRL_INPUT_GET_KEY_BIT, + VIRTIO_DEVICE_CTRL_INPUT_GET_REL_BIT, + VIRTIO_DEVICE_CTRL_INPUT_GET_ABS_BIT, +}; + +rt_err_t rt_virtio_input_init(rt_ubase_t *mmio_base, rt_uint32_t irq); + +#endif /* __VIRTIO_INPUT_H__ */ diff --git a/components/drivers/virtio/virtio_input_event_codes.h b/components/drivers/virtio/virtio_input_event_codes.h new file mode 100644 index 0000000000..b84723374e --- /dev/null +++ b/components/drivers/virtio/virtio_input_event_codes.h @@ -0,0 +1,932 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_INPUT_EVENT_CODES__ +#define __VIRTIO_INPUT_EVENT_CODES__ + +/* Device properties and quirks */ +#define INPUT_PROP_POINTER 0x00 /* Needs a pointer */ +#define INPUT_PROP_DIRECT 0x01 /* Direct input devices */ +#define INPUT_PROP_BUTTONPAD 0x02 /* Has button(s) under pad */ +#define INPUT_PROP_SEMI_MT 0x03 /* Touch rectangle only */ +#define INPUT_PROP_TOPBUTTONPAD 0x04 /* Softbuttons at top of pad */ +#define INPUT_PROP_POINTING_STICK 0x05 /* Is a pointing stick */ +#define INPUT_PROP_ACCELEROMETER 0x06 /* Has accelerometer */ + +#define INPUT_PROP_MAX 0x1f +#define INPUT_PROP_CNT (INPUT_PROP_MAX + 1) + +/* Event types */ +#define EV_SYN 0x00 /* Synchronization events */ +#define EV_KEY 0x01 /* Keys and buttons type */ +#define EV_REL 0x02 /* Relative axes events */ +#define EV_ABS 0x03 /* Absolute axes events */ +#define EV_MSC 0x04 /* Misc events */ +#define EV_SW 0x05 /* Switch events */ +#define EV_LED 0x11 /* LEDs events */ +#define EV_SND 0x12 /* Sounds events */ +#define EV_REP 0x14 /* Repeat events */ +#define EV_FF 0x15 /* Force feedback events */ +#define EV_PWR 0x16 /* Power management events */ +#define EV_FF_STATUS 0x17 /* Force feedback state */ +#define EV_MAX 0x1f /* Maximum number of events */ +#define EV_CNT (EV_MAX + 1)/* Event count */ + +/* Synchronization events.*/ +#define SYN_REPORT 0 +#define SYN_CONFIG 1 +#define SYN_MT_REPORT 2 +#define SYN_DROPPED 3 +#define SYN_MAX 0xf +#define SYN_CNT (SYN_MAX + 1) + +/* + * Keys and buttons + * + * Most of the keys/buttons are modeled after USB HUT 1.12 + * (see http://www.usb.org/developers/hidpage). + * Abbreviations in the comments: + * AC - Application Control + * AL - Application Launch Button + * SC - System Control + */ +#define KEY_RESERVED 0 +#define KEY_ESC 1 +#define KEY_1 2 +#define KEY_2 3 +#define KEY_3 4 +#define KEY_4 5 +#define KEY_5 6 +#define KEY_6 7 +#define KEY_7 8 +#define KEY_8 9 +#define KEY_9 10 +#define KEY_0 11 +#define KEY_MINUS 12 +#define KEY_EQUAL 13 +#define KEY_BACKSPACE 14 +#define KEY_TAB 15 +#define KEY_Q 16 +#define KEY_W 17 +#define KEY_E 18 +#define KEY_R 19 +#define KEY_T 20 +#define KEY_Y 21 +#define KEY_U 22 +#define KEY_I 23 +#define KEY_O 24 +#define KEY_P 25 +#define KEY_LEFTBRACE 26 +#define KEY_RIGHTBRACE 27 +#define KEY_ENTER 28 +#define KEY_LEFTCTRL 29 +#define KEY_A 30 +#define KEY_S 31 +#define KEY_D 32 +#define KEY_F 33 +#define KEY_G 34 +#define KEY_H 35 +#define KEY_J 36 +#define KEY_K 37 +#define KEY_L 38 +#define KEY_SEMICOLON 39 +#define KEY_APOSTROPHE 40 +#define KEY_GRAVE 41 +#define KEY_LEFTSHIFT 42 +#define KEY_BACKSLASH 43 +#define KEY_Z 44 +#define KEY_X 45 +#define KEY_C 46 +#define KEY_V 47 +#define KEY_B 48 +#define KEY_N 49 +#define KEY_M 50 +#define KEY_COMMA 51 +#define KEY_DOT 52 +#define KEY_SLASH 53 +#define KEY_RIGHTSHIFT 54 +#define KEY_KPASTERISK 55 +#define KEY_LEFTALT 56 +#define KEY_SPACE 57 +#define KEY_CAPSLOCK 58 +#define KEY_F1 59 +#define KEY_F2 60 +#define KEY_F3 61 +#define KEY_F4 62 +#define KEY_F5 63 +#define KEY_F6 64 +#define KEY_F7 65 +#define KEY_F8 66 +#define KEY_F9 67 +#define KEY_F10 68 +#define KEY_NUMLOCK 69 +#define KEY_SCROLLLOCK 70 +#define KEY_KP7 71 +#define KEY_KP8 72 +#define KEY_KP9 73 +#define KEY_KPMINUS 74 +#define KEY_KP4 75 +#define KEY_KP5 76 +#define KEY_KP6 77 +#define KEY_KPPLUS 78 +#define KEY_KP1 79 +#define KEY_KP2 80 +#define KEY_KP3 81 +#define KEY_KP0 82 +#define KEY_KPDOT 83 + +#define KEY_ZENKAKUHANKAKU 85 +#define KEY_102ND 86 +#define KEY_F11 87 +#define KEY_F12 88 +#define KEY_RO 89 +#define KEY_KATAKANA 90 +#define KEY_HIRAGANA 91 +#define KEY_HENKAN 92 +#define KEY_KATAKANAHIRAGANA 93 +#define KEY_MUHENKAN 94 +#define KEY_KPJPCOMMA 95 +#define KEY_KPENTER 96 +#define KEY_RIGHTCTRL 97 +#define KEY_KPSLASH 98 +#define KEY_SYSRQ 99 +#define KEY_RIGHTALT 100 +#define KEY_LINEFEED 101 +#define KEY_HOME 102 +#define KEY_UP 103 +#define KEY_PAGEUP 104 +#define KEY_LEFT 105 +#define KEY_RIGHT 106 +#define KEY_END 107 +#define KEY_DOWN 108 +#define KEY_PAGEDOWN 109 +#define KEY_INSERT 110 +#define KEY_DELETE 111 +#define KEY_MACRO 112 +#define KEY_MUTE 113 +#define KEY_VOLUMEDOWN 114 +#define KEY_VOLUMEUP 115 +#define KEY_POWER 116 /* SC System Power Down */ +#define KEY_KPEQUAL 117 +#define KEY_KPPLUSMINUS 118 +#define KEY_PAUSE 119 +#define KEY_SCALE 120 /* AL Compiz Scale (Expose) */ + +#define KEY_KPCOMMA 121 +#define KEY_HANGEUL 122 +#define KEY_HANGUEL KEY_HANGEUL +#define KEY_HANJA 123 +#define KEY_YEN 124 +#define KEY_LEFTMETA 125 +#define KEY_RIGHTMETA 126 +#define KEY_COMPOSE 127 + +#define KEY_STOP 128 /* AC Stop */ +#define KEY_AGAIN 129 +#define KEY_PROPS 130 /* AC Properties */ +#define KEY_UNDO 131 /* AC Undo */ +#define KEY_FRONT 132 +#define KEY_COPY 133 /* AC Copy */ +#define KEY_OPEN 134 /* AC Open */ +#define KEY_PASTE 135 /* AC Paste */ +#define KEY_FIND 136 /* AC Search */ +#define KEY_CUT 137 /* AC Cut */ +#define KEY_HELP 138 /* AL Integrated Help Center */ +#define KEY_MENU 139 /* Menu (show menu) */ +#define KEY_CALC 140 /* AL Calculator */ +#define KEY_SETUP 141 +#define KEY_SLEEP 142 /* SC System Sleep */ +#define KEY_WAKEUP 143 /* System Wake Up */ +#define KEY_FILE 144 /* AL Local Machine Browser */ +#define KEY_SENDFILE 145 +#define KEY_DELETEFILE 146 +#define KEY_XFER 147 +#define KEY_PROG1 148 +#define KEY_PROG2 149 +#define KEY_WWW 150 /* AL Internet Browser */ +#define KEY_MSDOS 151 +#define KEY_COFFEE 152 /* AL Terminal Lock/Screensaver */ +#define KEY_SCREENLOCK KEY_COFFEE +#define KEY_ROTATE_DISPLAY 153 /* Display orientation for e.g. tablets */ +#define KEY_DIRECTION KEY_ROTATE_DISPLAY +#define KEY_CYCLEWINDOWS 154 +#define KEY_MAIL 155 +#define KEY_BOOKMARKS 156 /* AC Bookmarks */ +#define KEY_COMPUTER 157 +#define KEY_BACK 158 /* AC Back */ +#define KEY_FORWARD 159 /* AC Forward */ +#define KEY_CLOSECD 160 +#define KEY_EJECTCD 161 +#define KEY_EJECTCLOSECD 162 +#define KEY_NEXTSONG 163 +#define KEY_PLAYPAUSE 164 +#define KEY_PREVIOUSSONG 165 +#define KEY_STOPCD 166 +#define KEY_RECORD 167 +#define KEY_REWIND 168 +#define KEY_PHONE 169 /* Media Select Telephone */ +#define KEY_ISO 170 +#define KEY_CONFIG 171 /* AL Consumer Control Configuration */ +#define KEY_HOMEPAGE 172 /* AC Home */ +#define KEY_REFRESH 173 /* AC Refresh */ +#define KEY_EXIT 174 /* AC Exit */ +#define KEY_MOVE 175 +#define KEY_EDIT 176 +#define KEY_SCROLLUP 177 +#define KEY_SCROLLDOWN 178 +#define KEY_KPLEFTPAREN 179 +#define KEY_KPRIGHTPAREN 180 +#define KEY_NEW 181 /* AC New */ +#define KEY_REDO 182 /* AC Redo/Repeat */ + +#define KEY_F13 183 +#define KEY_F14 184 +#define KEY_F15 185 +#define KEY_F16 186 +#define KEY_F17 187 +#define KEY_F18 188 +#define KEY_F19 189 +#define KEY_F20 190 +#define KEY_F21 191 +#define KEY_F22 192 +#define KEY_F23 193 +#define KEY_F24 194 + +#define KEY_PLAYCD 200 +#define KEY_PAUSECD 201 +#define KEY_PROG3 202 +#define KEY_PROG4 203 +#define KEY_ALL_APPLICATIONS 204 /* AC Desktop Show All Applications */ +#define KEY_DASHBOARD KEY_ALL_APPLICATIONS +#define KEY_SUSPEND 205 +#define KEY_CLOSE 206 /* AC Close */ +#define KEY_PLAY 207 +#define KEY_FASTFORWARD 208 +#define KEY_BASSBOOST 209 +#define KEY_PRINT 210 /* AC Print */ +#define KEY_HP 211 +#define KEY_CAMERA 212 +#define KEY_SOUND 213 +#define KEY_QUESTION 214 +#define KEY_EMAIL 215 +#define KEY_CHAT 216 +#define KEY_SEARCH 217 +#define KEY_CONNECT 218 +#define KEY_FINANCE 219 /* AL Checkbook/Finance */ +#define KEY_SPORT 220 +#define KEY_SHOP 221 +#define KEY_ALTERASE 222 +#define KEY_CANCEL 223 /* AC Cancel */ +#define KEY_BRIGHTNESSDOWN 224 +#define KEY_BRIGHTNESSUP 225 +#define KEY_MEDIA 226 + +#define KEY_SWITCHVIDEOMODE 227 /* Cycle between available video outputs (Monitor/LCD/TV-out/etc) */ +#define KEY_KBDILLUMTOGGLE 228 +#define KEY_KBDILLUMDOWN 229 +#define KEY_KBDILLUMUP 230 + +#define KEY_SEND 231 /* AC Send */ +#define KEY_REPLY 232 /* AC Reply */ +#define KEY_FORWARDMAIL 233 /* AC Forward Msg */ +#define KEY_SAVE 234 /* AC Save */ +#define KEY_DOCUMENTS 235 + +#define KEY_BATTERY 236 + +#define KEY_BLUETOOTH 237 +#define KEY_WLAN 238 +#define KEY_UWB 239 + +#define KEY_UNKNOWN 240 + +#define KEY_VIDEO_NEXT 241 /* Drive next video source */ +#define KEY_VIDEO_PREV 242 /* Drive previous video source */ +#define KEY_BRIGHTNESS_CYCLE 243 /* Brightness up, after max is min */ +#define KEY_BRIGHTNESS_AUTO 244 /* Set Auto Brightness: manual brightness control is off, rely on ambient */ +#define KEY_BRIGHTNESS_ZERO KEY_BRIGHTNESS_AUTO +#define KEY_DISPLAY_OFF 245 /* Display device to off state */ + +#define KEY_WWAN 246 /* Wireless WAN (LTE, UMTS, GSM, etc.) */ +#define KEY_WIMAX KEY_WWAN +#define KEY_RFKILL 247 /* Key that controls all radios */ + +#define KEY_MICMUTE 248 /* Mute / unmute the microphone */ + +/* Code 255 is reserved for special needs of AT keyboard driver */ +#define BTN_MISC 0x100 +#define BTN_0 0x100 +#define BTN_1 0x101 +#define BTN_2 0x102 +#define BTN_3 0x103 +#define BTN_4 0x104 +#define BTN_5 0x105 +#define BTN_6 0x106 +#define BTN_7 0x107 +#define BTN_8 0x108 +#define BTN_9 0x109 + +#define BTN_MOUSE 0x110 +#define BTN_LEFT 0x110 +#define BTN_RIGHT 0x111 +#define BTN_MIDDLE 0x112 +#define BTN_SIDE 0x113 +#define BTN_EXTRA 0x114 +#define BTN_FORWARD 0x115 +#define BTN_BACK 0x116 +#define BTN_TASK 0x117 + +#define BTN_JOYSTICK 0x120 +#define BTN_TRIGGER 0x120 +#define BTN_THUMB 0x121 +#define BTN_THUMB2 0x122 +#define BTN_TOP 0x123 +#define BTN_TOP2 0x124 +#define BTN_PINKIE 0x125 +#define BTN_BASE 0x126 +#define BTN_BASE2 0x127 +#define BTN_BASE3 0x128 +#define BTN_BASE4 0x129 +#define BTN_BASE5 0x12a +#define BTN_BASE6 0x12b +#define BTN_DEAD 0x12f + +#define BTN_GAMEPAD 0x130 +#define BTN_SOUTH 0x130 +#define BTN_A BTN_SOUTH +#define BTN_EAST 0x131 +#define BTN_B BTN_EAST +#define BTN_C 0x132 +#define BTN_NORTH 0x133 +#define BTN_X BTN_NORTH +#define BTN_WEST 0x134 +#define BTN_Y BTN_WEST +#define BTN_Z 0x135 +#define BTN_TL 0x136 +#define BTN_TR 0x137 +#define BTN_TL2 0x138 +#define BTN_TR2 0x139 +#define BTN_SELECT 0x13a +#define BTN_START 0x13b +#define BTN_MODE 0x13c +#define BTN_THUMBL 0x13d +#define BTN_THUMBR 0x13e + +#define BTN_DIGI 0x140 +#define BTN_TOOL_PEN 0x140 +#define BTN_TOOL_RUBBER 0x141 +#define BTN_TOOL_BRUSH 0x142 +#define BTN_TOOL_PENCIL 0x143 +#define BTN_TOOL_AIRBRUSH 0x144 +#define BTN_TOOL_FINGER 0x145 +#define BTN_TOOL_MOUSE 0x146 +#define BTN_TOOL_LENS 0x147 +#define BTN_TOOL_QUINTTAP 0x148 /* Five fingers on trackpad */ +#define BTN_STYLUS3 0x149 +#define BTN_TOUCH 0x14a +#define BTN_STYLUS 0x14b +#define BTN_STYLUS2 0x14c +#define BTN_TOOL_DOUBLETAP 0x14d +#define BTN_TOOL_TRIPLETAP 0x14e +#define BTN_TOOL_QUADTAP 0x14f /* Four fingers on trackpad */ + +#define BTN_WHEEL 0x150 +#define BTN_GEAR_DOWN 0x150 +#define BTN_GEAR_UP 0x151 + +#define KEY_OK 0x160 +#define KEY_SELECT 0x161 +#define KEY_GOTO 0x162 +#define KEY_CLEAR 0x163 +#define KEY_POWER2 0x164 +#define KEY_OPTION 0x165 +#define KEY_INFO 0x166 /* AL OEM Features/Tips/Tutorial */ +#define KEY_TIME 0x167 +#define KEY_VENDOR 0x168 +#define KEY_ARCHIVE 0x169 +#define KEY_PROGRAM 0x16a /* Media Select Program Guide */ +#define KEY_CHANNEL 0x16b +#define KEY_FAVORITES 0x16c +#define KEY_EPG 0x16d +#define KEY_PVR 0x16e /* Media Select Home */ +#define KEY_MHP 0x16f +#define KEY_LANGUAGE 0x170 +#define KEY_TITLE 0x171 +#define KEY_SUBTITLE 0x172 +#define KEY_ANGLE 0x173 +#define KEY_FULL_SCREEN 0x174 /* AC View Toggle */ +#define KEY_ZOOM KEY_FULL_SCREEN +#define KEY_MODE 0x175 +#define KEY_KEYBOARD 0x176 +#define KEY_ASPECT_RATIO 0x177 /* HUTRR37: Aspect */ +#define KEY_SCREEN KEY_ASPECT_RATIO +#define KEY_PC 0x178 /* Media Select Computer */ +#define KEY_TV 0x179 /* Media Select TV */ +#define KEY_TV2 0x17a /* Media Select Cable */ +#define KEY_VCR 0x17b /* Media Select VCR */ +#define KEY_VCR2 0x17c /* VCR Plus */ +#define KEY_SAT 0x17d /* Media Select Satellite */ +#define KEY_SAT2 0x17e +#define KEY_CD 0x17f /* Media Select CD */ +#define KEY_TAPE 0x180 /* Media Select Tape */ +#define KEY_RADIO 0x181 +#define KEY_TUNER 0x182 /* Media Select Tuner */ +#define KEY_PLAYER 0x183 +#define KEY_TEXT 0x184 +#define KEY_DVD 0x185 /* Media Select DVD */ +#define KEY_AUX 0x186 +#define KEY_MP3 0x187 +#define KEY_AUDIO 0x188 /* AL Audio Browser */ +#define KEY_VIDEO 0x189 /* AL Movie Browser */ +#define KEY_DIRECTORY 0x18a +#define KEY_LIST 0x18b +#define KEY_MEMO 0x18c /* Media Select Messages */ +#define KEY_CALENDAR 0x18d +#define KEY_RED 0x18e +#define KEY_GREEN 0x18f +#define KEY_YELLOW 0x190 +#define KEY_BLUE 0x191 +#define KEY_CHANNELUP 0x192 /* Channel Increment */ +#define KEY_CHANNELDOWN 0x193 /* Channel Decrement */ +#define KEY_FIRST 0x194 +#define KEY_LAST 0x195 /* Recall Last */ +#define KEY_AB 0x196 +#define KEY_NEXT 0x197 +#define KEY_RESTART 0x198 +#define KEY_SLOW 0x199 +#define KEY_SHUFFLE 0x19a +#define KEY_BREAK 0x19b +#define KEY_PREVIOUS 0x19c +#define KEY_DIGITS 0x19d +#define KEY_TEEN 0x19e +#define KEY_TWEN 0x19f +#define KEY_VIDEOPHONE 0x1a0 /* Media Select Video Phone */ +#define KEY_GAMES 0x1a1 /* Media Select Games */ +#define KEY_ZOOMIN 0x1a2 /* AC Zoom In */ +#define KEY_ZOOMOUT 0x1a3 /* AC Zoom Out */ +#define KEY_ZOOMRESET 0x1a4 /* AC Zoom */ +#define KEY_WORDPROCESSOR 0x1a5 /* AL Word Processor */ +#define KEY_EDITOR 0x1a6 /* AL Text Editor */ +#define KEY_SPREADSHEET 0x1a7 /* AL Spreadsheet */ +#define KEY_GRAPHICSEDITOR 0x1a8 /* AL Graphics Editor */ +#define KEY_PRESENTATION 0x1a9 /* AL Presentation App */ +#define KEY_DATABASE 0x1aa /* AL Database App */ +#define KEY_NEWS 0x1ab /* AL Newsreader */ +#define KEY_VOICEMAIL 0x1ac /* AL Voicemail */ +#define KEY_ADDRESSBOOK 0x1ad /* AL Contacts/Address Book */ +#define KEY_MESSENGER 0x1ae /* AL Instant Messaging */ +#define KEY_DISPLAYTOGGLE 0x1af /* Turn display (LCD) on and off */ +#define KEY_BRIGHTNESS_TOGGLE KEY_DISPLAYTOGGLE +#define KEY_SPELLCHECK 0x1b0 /* AL Spell Check */ +#define KEY_LOGOFF 0x1b1 /* AL Logoff */ + +#define KEY_DOLLAR 0x1b2 +#define KEY_EURO 0x1b3 + +#define KEY_FRAMEBACK 0x1b4 /* Consumer - transport controls */ +#define KEY_FRAMEFORWARD 0x1b5 +#define KEY_CONTEXT_MENU 0x1b6 /* GenDesc - system context menu */ +#define KEY_MEDIA_REPEAT 0x1b7 /* Consumer - transport control */ +#define KEY_10CHANNELSUP 0x1b8 /* 10 channels up (10+) */ +#define KEY_10CHANNELSDOWN 0x1b9 /* 10 channels down (10-) */ +#define KEY_IMAGES 0x1ba /* AL Image Browser */ +#define KEY_NOTIFICATION_CENTER 0x1bc /* Show/hide the notification center */ +#define KEY_PICKUP_PHONE 0x1bd /* Answer incoming call */ +#define KEY_HANGUP_PHONE 0x1be /* Decline incoming call */ + +#define KEY_DEL_EOL 0x1c0 +#define KEY_DEL_EOS 0x1c1 +#define KEY_INS_LINE 0x1c2 +#define KEY_DEL_LINE 0x1c3 + +#define KEY_FN 0x1d0 +#define KEY_FN_ESC 0x1d1 +#define KEY_FN_F1 0x1d2 +#define KEY_FN_F2 0x1d3 +#define KEY_FN_F3 0x1d4 +#define KEY_FN_F4 0x1d5 +#define KEY_FN_F5 0x1d6 +#define KEY_FN_F6 0x1d7 +#define KEY_FN_F7 0x1d8 +#define KEY_FN_F8 0x1d9 +#define KEY_FN_F9 0x1da +#define KEY_FN_F10 0x1db +#define KEY_FN_F11 0x1dc +#define KEY_FN_F12 0x1dd +#define KEY_FN_1 0x1de +#define KEY_FN_2 0x1df +#define KEY_FN_D 0x1e0 +#define KEY_FN_E 0x1e1 +#define KEY_FN_F 0x1e2 +#define KEY_FN_S 0x1e3 +#define KEY_FN_B 0x1e4 +#define KEY_FN_RIGHT_SHIFT 0x1e5 + +#define KEY_BRL_DOT1 0x1f1 +#define KEY_BRL_DOT2 0x1f2 +#define KEY_BRL_DOT3 0x1f3 +#define KEY_BRL_DOT4 0x1f4 +#define KEY_BRL_DOT5 0x1f5 +#define KEY_BRL_DOT6 0x1f6 +#define KEY_BRL_DOT7 0x1f7 +#define KEY_BRL_DOT8 0x1f8 +#define KEY_BRL_DOT9 0x1f9 +#define KEY_BRL_DOT10 0x1fa + +#define KEY_NUMERIC_0 0x200 /* Used by phones, remote controls, and other keypads */ +#define KEY_NUMERIC_1 0x201 +#define KEY_NUMERIC_2 0x202 +#define KEY_NUMERIC_3 0x203 +#define KEY_NUMERIC_4 0x204 +#define KEY_NUMERIC_5 0x205 +#define KEY_NUMERIC_6 0x206 +#define KEY_NUMERIC_7 0x207 +#define KEY_NUMERIC_8 0x208 +#define KEY_NUMERIC_9 0x209 +#define KEY_NUMERIC_STAR 0x20a +#define KEY_NUMERIC_POUND 0x20b +#define KEY_NUMERIC_A 0x20c /* Phone key A - HUT Telephony 0xb9 */ +#define KEY_NUMERIC_B 0x20d +#define KEY_NUMERIC_C 0x20e +#define KEY_NUMERIC_D 0x20f + +#define KEY_CAMERA_FOCUS 0x210 +#define KEY_WPS_BUTTON 0x211 /* WiFi Protected Setup key */ + +#define KEY_TOUCHPAD_TOGGLE 0x212 /* Request switch touchpad on or off */ +#define KEY_TOUCHPAD_ON 0x213 +#define KEY_TOUCHPAD_OFF 0x214 + +#define KEY_CAMERA_ZOOMIN 0x215 +#define KEY_CAMERA_ZOOMOUT 0x216 +#define KEY_CAMERA_UP 0x217 +#define KEY_CAMERA_DOWN 0x218 +#define KEY_CAMERA_LEFT 0x219 +#define KEY_CAMERA_RIGHT 0x21a + +#define KEY_ATTENDANT_ON 0x21b +#define KEY_ATTENDANT_OFF 0x21c +#define KEY_ATTENDANT_TOGGLE 0x21d /* Attendant call on or off */ +#define KEY_LIGHTS_TOGGLE 0x21e /* Reading light on or off */ + +#define BTN_DPAD_UP 0x220 +#define BTN_DPAD_DOWN 0x221 +#define BTN_DPAD_LEFT 0x222 +#define BTN_DPAD_RIGHT 0x223 + +#define KEY_ALS_TOGGLE 0x230 /* Ambient light sensor */ +#define KEY_ROTATE_LOCK_TOGGLE 0x231 /* Display rotation lock */ + +#define KEY_BUTTONCONFIG 0x240 /* AL Button Configuration */ +#define KEY_TASKMANAGER 0x241 /* AL Task/Project Manager */ +#define KEY_JOURNAL 0x242 /* AL Log/Journal/Timecard */ +#define KEY_CONTROLPANEL 0x243 /* AL Control Panel */ +#define KEY_APPSELECT 0x244 /* AL Select Task/Application */ +#define KEY_SCREENSAVER 0x245 /* AL Screen Saver */ +#define KEY_VOICECOMMAND 0x246 /* Listening Voice Command */ +#define KEY_ASSISTANT 0x247 /* AL Context-aware desktop assistant */ +#define KEY_KBD_LAYOUT_NEXT 0x248 /* AC Next Keyboard Layout Select */ +#define KEY_EMOJI_PICKER 0x249 /* Show/hide emoji picker (HUTRR101) */ +#define KEY_DICTATE 0x24a /* Start or Stop Voice Dictation Session (HUTRR99) */ + +#define KEY_BRIGHTNESS_MIN 0x250 /* Set Brightness to Minimum */ +#define KEY_BRIGHTNESS_MAX 0x251 /* Set Brightness to Maximum */ + +#define KEY_KBDINPUTASSIST_PREV 0x260 +#define KEY_KBDINPUTASSIST_NEXT 0x261 +#define KEY_KBDINPUTASSIST_PREVGROUP 0x262 +#define KEY_KBDINPUTASSIST_NEXTGROUP 0x263 +#define KEY_KBDINPUTASSIST_ACCEPT 0x264 +#define KEY_KBDINPUTASSIST_CANCEL 0x265 + +/* Diagonal movement keys */ +#define KEY_RIGHT_UP 0x266 +#define KEY_RIGHT_DOWN 0x267 +#define KEY_LEFT_UP 0x268 +#define KEY_LEFT_DOWN 0x269 + +#define KEY_ROOT_MENU 0x26a /* Show Device's Root Menu */ +/* Show Top Menu of the Media (e.g. DVD) */ +#define KEY_MEDIA_TOP_MENU 0x26b +#define KEY_NUMERIC_11 0x26c +#define KEY_NUMERIC_12 0x26d +/* + * Toggle Audio Description: refers to an audio service that helps blind and + * visually impaired consumers understand the action in a program. Note: in + * some countries this is referred to as "Video Description". + */ +#define KEY_AUDIO_DESC 0x26e +#define KEY_3D_MODE 0x26f +#define KEY_NEXT_FAVORITE 0x270 +#define KEY_STOP_RECORD 0x271 +#define KEY_PAUSE_RECORD 0x272 +#define KEY_VOD 0x273 /* Video on Demand */ +#define KEY_UNMUTE 0x274 +#define KEY_FASTREVERSE 0x275 +#define KEY_SLOWREVERSE 0x276 +/* + * Control a data application associated with the currently viewed channel, + * e.g. teletext or data broadcast application (MHEG, MHP, HbbTV, etc.) + */ +#define KEY_DATA 0x277 +#define KEY_ONSCREEN_KEYBOARD 0x278 +/* Electronic privacy screen control */ +#define KEY_PRIVACY_SCREEN_TOGGLE 0x279 + +/* Select an area of screen to be copied */ +#define KEY_SELECTIVE_SCREENSHOT 0x27a + +/* Move the focus to the next or previous user controllable element within a UI container */ +#define KEY_NEXT_ELEMENT 0x27b +#define KEY_PREVIOUS_ELEMENT 0x27c + +/* Toggle Autopilot engagement */ +#define KEY_AUTOPILOT_ENGAGE_TOGGLE 0x27d + +/* Shortcut Keys */ +#define KEY_MARK_WAYPOINT 0x27e +#define KEY_SOS 0x27f +#define KEY_NAV_CHART 0x280 +#define KEY_FISHING_CHART 0x281 +#define KEY_SINGLE_RANGE_RADAR 0x282 +#define KEY_DUAL_RANGE_RADAR 0x283 +#define KEY_RADAR_OVERLAY 0x284 +#define KEY_TRADITIONAL_SONAR 0x285 +#define KEY_CLEARVU_SONAR 0x286 +#define KEY_SIDEVU_SONAR 0x287 +#define KEY_NAV_INFO 0x288 +#define KEY_BRIGHTNESS_MENU 0x289 + +/* + * Some keyboards have keys which do not have a defined meaning, these keys + * are intended to be programmed / bound to macros by the user. For most + * keyboards with these macro-keys the key-sequence to inject, or action to + * take, is all handled by software on the host side. So from the kernel's + * point of view these are just normal keys. + * + * The KEY_MACRO# codes below are intended for such keys, which may be labeled + * e.g. G1-G18, or S1 - S30. The KEY_MACRO# codes MUST NOT be used for keys + * where the marking on the key does indicate a defined meaning / purpose. + * + * The KEY_MACRO# codes MUST also NOT be used as fallback for when no existing + * KEY_FOO define matches the marking / purpose. In this case a new KEY_FOO + * define MUST be added. + */ +#define KEY_MACRO1 0x290 +#define KEY_MACRO2 0x291 +#define KEY_MACRO3 0x292 +#define KEY_MACRO4 0x293 +#define KEY_MACRO5 0x294 +#define KEY_MACRO6 0x295 +#define KEY_MACRO7 0x296 +#define KEY_MACRO8 0x297 +#define KEY_MACRO9 0x298 +#define KEY_MACRO10 0x299 +#define KEY_MACRO11 0x29a +#define KEY_MACRO12 0x29b +#define KEY_MACRO13 0x29c +#define KEY_MACRO14 0x29d +#define KEY_MACRO15 0x29e +#define KEY_MACRO16 0x29f +#define KEY_MACRO17 0x2a0 +#define KEY_MACRO18 0x2a1 +#define KEY_MACRO19 0x2a2 +#define KEY_MACRO20 0x2a3 +#define KEY_MACRO21 0x2a4 +#define KEY_MACRO22 0x2a5 +#define KEY_MACRO23 0x2a6 +#define KEY_MACRO24 0x2a7 +#define KEY_MACRO25 0x2a8 +#define KEY_MACRO26 0x2a9 +#define KEY_MACRO27 0x2aa +#define KEY_MACRO28 0x2ab +#define KEY_MACRO29 0x2ac +#define KEY_MACRO30 0x2ad + +/* + * Some keyboards with the macro-keys described above have some extra keys + * for controlling the host-side software responsible for the macro handling: + * -A macro recording start/stop key. Note that not all keyboards which emit + * KEY_MACRO_RECORD_START will also emit KEY_MACRO_RECORD_STOP if + * KEY_MACRO_RECORD_STOP is not advertised, then KEY_MACRO_RECORD_START + * should be interpreted as a recording start/stop toggle; + * -Keys for switching between different macro (pre)sets, either a key for + * cycling through the configured presets or keys to directly select a preset. + */ +#define KEY_MACRO_RECORD_START 0x2b0 +#define KEY_MACRO_RECORD_STOP 0x2b1 +#define KEY_MACRO_PRESET_CYCLE 0x2b2 +#define KEY_MACRO_PRESET1 0x2b3 +#define KEY_MACRO_PRESET2 0x2b4 +#define KEY_MACRO_PRESET3 0x2b5 + +/* + * Some keyboards have a buildin LCD panel where the contents are controlled + * by the host. Often these have a number of keys directly below the LCD + * intended for controlling a menu shown on the LCD. These keys often don't + * have any labeling so we just name them KEY_KBD_LCD_MENU# + */ +#define KEY_KBD_LCD_MENU1 0x2b8 +#define KEY_KBD_LCD_MENU2 0x2b9 +#define KEY_KBD_LCD_MENU3 0x2ba +#define KEY_KBD_LCD_MENU4 0x2bb +#define KEY_KBD_LCD_MENU5 0x2bc + +#define BTN_TRIGGER_HAPPY 0x2c0 +#define BTN_TRIGGER_HAPPY1 0x2c0 +#define BTN_TRIGGER_HAPPY2 0x2c1 +#define BTN_TRIGGER_HAPPY3 0x2c2 +#define BTN_TRIGGER_HAPPY4 0x2c3 +#define BTN_TRIGGER_HAPPY5 0x2c4 +#define BTN_TRIGGER_HAPPY6 0x2c5 +#define BTN_TRIGGER_HAPPY7 0x2c6 +#define BTN_TRIGGER_HAPPY8 0x2c7 +#define BTN_TRIGGER_HAPPY9 0x2c8 +#define BTN_TRIGGER_HAPPY10 0x2c9 +#define BTN_TRIGGER_HAPPY11 0x2ca +#define BTN_TRIGGER_HAPPY12 0x2cb +#define BTN_TRIGGER_HAPPY13 0x2cc +#define BTN_TRIGGER_HAPPY14 0x2cd +#define BTN_TRIGGER_HAPPY15 0x2ce +#define BTN_TRIGGER_HAPPY16 0x2cf +#define BTN_TRIGGER_HAPPY17 0x2d0 +#define BTN_TRIGGER_HAPPY18 0x2d1 +#define BTN_TRIGGER_HAPPY19 0x2d2 +#define BTN_TRIGGER_HAPPY20 0x2d3 +#define BTN_TRIGGER_HAPPY21 0x2d4 +#define BTN_TRIGGER_HAPPY22 0x2d5 +#define BTN_TRIGGER_HAPPY23 0x2d6 +#define BTN_TRIGGER_HAPPY24 0x2d7 +#define BTN_TRIGGER_HAPPY25 0x2d8 +#define BTN_TRIGGER_HAPPY26 0x2d9 +#define BTN_TRIGGER_HAPPY27 0x2da +#define BTN_TRIGGER_HAPPY28 0x2db +#define BTN_TRIGGER_HAPPY29 0x2dc +#define BTN_TRIGGER_HAPPY30 0x2dd +#define BTN_TRIGGER_HAPPY31 0x2de +#define BTN_TRIGGER_HAPPY32 0x2df +#define BTN_TRIGGER_HAPPY33 0x2e0 +#define BTN_TRIGGER_HAPPY34 0x2e1 +#define BTN_TRIGGER_HAPPY35 0x2e2 +#define BTN_TRIGGER_HAPPY36 0x2e3 +#define BTN_TRIGGER_HAPPY37 0x2e4 +#define BTN_TRIGGER_HAPPY38 0x2e5 +#define BTN_TRIGGER_HAPPY39 0x2e6 +#define BTN_TRIGGER_HAPPY40 0x2e7 + +/* We avoid low common keys in module aliases so they don't get huge. */ +#define KEY_MIN_INTERESTING KEY_MUTE +#define KEY_MAX 0x2ff +#define KEY_CNT (KEY_MAX + 1) + +/* Relative axes */ +#define REL_X 0x00 +#define REL_Y 0x01 +#define REL_Z 0x02 +#define REL_RX 0x03 +#define REL_RY 0x04 +#define REL_RZ 0x05 +#define REL_HWHEEL 0x06 +#define REL_DIAL 0x07 +#define REL_WHEEL 0x08 +#define REL_MISC 0x09 +/* + * 0x0a is reserved and should not be used in input drivers. + * It was used by HID as REL_MISC + 1 and userspace needs to detect if + * the next REL_* event is correct or is just REL_MISC + n. + * We define here REL_RESERVED so userspace can rely on it and detect + * the situation described above. + */ +#define REL_RESERVED 0x0a +#define REL_WHEEL_HI_RES 0x0b +#define REL_HWHEEL_HI_RES 0x0c +#define REL_MAX 0x0f +#define REL_CNT (REL_MAX + 1) + +/* Absolute axes */ +#define ABS_X 0x00 +#define ABS_Y 0x01 +#define ABS_Z 0x02 +#define ABS_RX 0x03 +#define ABS_RY 0x04 +#define ABS_RZ 0x05 +#define ABS_THROTTLE 0x06 +#define ABS_RUDDER 0x07 +#define ABS_WHEEL 0x08 +#define ABS_GAS 0x09 +#define ABS_BRAKE 0x0a +#define ABS_HAT0X 0x10 +#define ABS_HAT0Y 0x11 +#define ABS_HAT1X 0x12 +#define ABS_HAT1Y 0x13 +#define ABS_HAT2X 0x14 +#define ABS_HAT2Y 0x15 +#define ABS_HAT3X 0x16 +#define ABS_HAT3Y 0x17 +#define ABS_PRESSURE 0x18 +#define ABS_DISTANCE 0x19 +#define ABS_TILT_X 0x1a +#define ABS_TILT_Y 0x1b +#define ABS_TOOL_WIDTH 0x1c + +#define ABS_VOLUME 0x20 + +#define ABS_MISC 0x28 + +/* + * 0x2e is reserved and should not be used in input drivers. + * It was used by HID as ABS_MISC + 6 and userspace needs to detect if + * the next ABS_* event is correct or is just ABS_MISC + n. + * We define here ABS_RESERVED so userspace can rely on it and detect + * the situation described above. + */ +#define ABS_RESERVED 0x2e + +#define ABS_MT_SLOT 0x2f /* MT slot being modified */ +#define ABS_MT_TOUCH_MAJOR 0x30 /* Major axis of touching ellipse */ +#define ABS_MT_TOUCH_MINOR 0x31 /* Minor axis (omit if circular) */ +#define ABS_MT_WIDTH_MAJOR 0x32 /* Major axis of approaching ellipse */ +#define ABS_MT_WIDTH_MINOR 0x33 /* Minor axis (omit if circular) */ +#define ABS_MT_ORIENTATION 0x34 /* Ellipse orientation */ +#define ABS_MT_POSITION_X 0x35 /* Center X touch position */ +#define ABS_MT_POSITION_Y 0x36 /* Center Y touch position */ +#define ABS_MT_TOOL_TYPE 0x37 /* Type of touching device */ +#define ABS_MT_BLOB_ID 0x38 /* Group a set of packets as a blob */ +#define ABS_MT_TRACKING_ID 0x39 /* Unique ID of initiated contact */ +#define ABS_MT_PRESSURE 0x3a /* Pressure on contact area */ +#define ABS_MT_DISTANCE 0x3b /* Contact hover distance */ +#define ABS_MT_TOOL_X 0x3c /* Center X tool position */ +#define ABS_MT_TOOL_Y 0x3d /* Center Y tool position */ + + +#define ABS_MAX 0x3f +#define ABS_CNT (ABS_MAX + 1) + +/* Switch events */ +#define SW_LID 0x00 /* Set = lid shut */ +#define SW_TABLET_MODE 0x01 /* Set = tablet mode */ +#define SW_HEADPHONE_INSERT 0x02 /* Set = inserted */ +#define SW_RFKILL_ALL 0x03 /* rfkill master switch, type "any" set = radio enabled */ +#define SW_RADIO SW_RFKILL_ALL /* deprecated */ +#define SW_MICROPHONE_INSERT 0x04 /* Set = inserted */ +#define SW_DOCK 0x05 /* Set = plugged into dock */ +#define SW_LINEOUT_INSERT 0x06 /* Set = inserted */ +#define SW_JACK_PHYSICAL_INSERT 0x07 /* Set = mechanical switch set */ +#define SW_VIDEOOUT_INSERT 0x08 /* Set = inserted */ +#define SW_CAMERA_LENS_COVER 0x09 /* Set = lens covered */ +#define SW_KEYPAD_SLIDE 0x0a /* Set = keypad slide out */ +#define SW_FRONT_PROXIMITY 0x0b /* Set = front proximity sensor active */ +#define SW_ROTATE_LOCK 0x0c /* Set = rotate locked/disabled */ +#define SW_LINEIN_INSERT 0x0d /* Set = inserted */ +#define SW_MUTE_DEVICE 0x0e /* Set = device disabled */ +#define SW_PEN_INSERTED 0x0f /* Set = pen inserted */ +#define SW_MACHINE_COVER 0x10 /* Set = cover closed */ +#define SW_MAX 0x10 +#define SW_CNT (SW_MAX + 1) + +/* Misc events */ +#define MSC_SERIAL 0x00 +#define MSC_PULSELED 0x01 +#define MSC_GESTURE 0x02 +#define MSC_RAW 0x03 +#define MSC_SCAN 0x04 +#define MSC_TIMESTAMP 0x05 +#define MSC_MAX 0x07 +#define MSC_CNT (MSC_MAX + 1) + +/* LEDs */ +#define LED_NUML 0x00 +#define LED_CAPSL 0x01 +#define LED_SCROLLL 0x02 +#define LED_COMPOSE 0x03 +#define LED_KANA 0x04 +#define LED_SLEEP 0x05 +#define LED_SUSPEND 0x06 +#define LED_MUTE 0x07 +#define LED_MISC 0x08 +#define LED_MAIL 0x09 +#define LED_CHARGING 0x0a +#define LED_MAX 0x0f +#define LED_CNT (LED_MAX + 1) + +/* Autorepeat values */ +#define REP_DELAY 0x00 +#define REP_PERIOD 0x01 +#define REP_MAX 0x01 +#define REP_CNT (REP_MAX + 1) + +/* Sounds */ +#define SND_CLICK 0x00 +#define SND_BELL 0x01 +#define SND_TONE 0x02 +#define SND_MAX 0x07 +#define SND_CNT (SND_MAX + 1) + +#endif /* __VIRTIO_INPUT_EVENT_CODES__ */ diff --git a/components/drivers/virtio/virtio_mmio.h b/components/drivers/virtio/virtio_mmio.h new file mode 100644 index 0000000000..98b92e460c --- /dev/null +++ b/components/drivers/virtio/virtio_mmio.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-9-16 GuEe-GUI the first version + * 2021-11-11 GuEe-GUI modify to virtio common interface + */ + +#ifndef __VIRTIO_MMIO_H__ +#define __VIRTIO_MMIO_H__ + +#include <rtdef.h> + +struct virtio_mmio_config +{ + rt_uint32_t magic; /* [0x00]<RO> Magic value */ + rt_uint32_t version; /* [0x04]<RO> Device version number */ + rt_uint32_t device_id; /* [0x08]<RO> Virtio Subsystem Device ID */ + rt_uint32_t vendor_id; /* [0x0c]<RO> Virtio Subsystem Vendor ID */ + rt_uint32_t device_features; /* [0x10]<RO> Flags representing features the device supports */ + rt_uint32_t device_features_sel; /* [0x14]<WO> Device (host) features word selection. */ + rt_uint32_t res0[2]; /* [0x18] */ + rt_uint32_t driver_features; /* [0x20]<WO> Device features understood and activated by the driver */ + rt_uint32_t driver_features_sel; /* [0x24]<WO> Activated (guest) features word selection */ + rt_uint32_t guest_page_size; /* [0x28]<WO> Guest page size, this value should be a power of 2 */ + rt_uint32_t res1[1]; /* [0x2c] */ + rt_uint32_t queue_sel; /* [0x30]<WO> Virtual queue index */ + rt_uint32_t queue_num_max; /* [0x34]<RO> Maximum virtual queue size */ + rt_uint32_t queue_num; /* [0x38]<WO> Virtual queue size */ + rt_uint32_t queue_align; /* [0x3c]<WO> Used Ring alignment in the virtual queue */ + rt_uint32_t queue_pfn; /* [0x40]<RW> Guest physical page number of the virtual queue */ + rt_uint32_t queue_ready; /* [0x44]<RW> Virtual queue ready bit */ + rt_uint32_t res2[2]; /* [0x48] */ + rt_uint32_t queue_notify; /* [0x50]<WO> Queue notifier */ + rt_uint32_t res3[3]; /* [0x54] */ + rt_uint32_t interrupt_status; /* [0x60]<RO> Interrupt status */ + rt_uint32_t interrupt_ack; /* [0x64]<WO> Interrupt acknowledge */ + rt_uint32_t res4[2]; /* [0x68] */ + rt_uint32_t status; /* [0x70]<RW> Device status */ + rt_uint32_t res5[3]; /* [0x74] */ + rt_uint32_t queue_desc_low; /* [0x80]<WO> Virtual queue’s Descriptor Area 64 bit long physical address */ + rt_uint32_t queue_desc_high; /* [0x84]<WO> */ + rt_uint32_t res6[2]; /* [0x88] */ + rt_uint32_t queue_driver_low; /* [0x90]<WO> Virtual queue’s Driver Area 64 bit long physical address */ + rt_uint32_t queue_driver_high; /* [0x94]<WO> */ + rt_uint32_t res7[2]; /* [0x98] */ + rt_uint32_t queue_device_low; /* [0xa0]<WO> Virtual queue’s Device Area 64 bit long physical address */ + rt_uint32_t queue_device_high; /* [0xa4]<WO> */ + rt_uint32_t res8[21]; /* [0xa8] */ + rt_uint32_t config_generation; /* [0xfc]<RO> Configuration atomicity value */ + rt_uint32_t config[]; /* [0x100+]<RO> Configuration space */ + +/* + * According to the compiler's optimization ways, we should force compiler not + * to optimization here, but it will cause some compilers generate memory access + * instructions fail. So we allow user to choose a toggle of optimize here. + */ +#ifdef RT_USING_VIRTIO_MMIO_ALIGN +} __attribute__((packed)); +#else +}; +#endif + +#endif /* __VIRTIO_MMIO_H__ */ diff --git a/components/drivers/virtio/virtio_net.c b/components/drivers/virtio/virtio_net.c new file mode 100644 index 0000000000..9b44cf6bb2 --- /dev/null +++ b/components/drivers/virtio/virtio_net.c @@ -0,0 +1,314 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#include <rthw.h> +#include <rtthread.h> +#include <cpuport.h> + +#ifdef RT_USING_VIRTIO_NET + +#include <virtio_net.h> + +static rt_err_t virtio_net_tx(rt_device_t dev, struct pbuf *p) +{ + rt_uint16_t id; + struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev; + struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev; + struct virtq *queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX]; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + id = (queue_tx->avail->idx * 2) % queue_tx->num; + + virtio_net_dev->info[id].hdr.flags = 0; + virtio_net_dev->info[id].hdr.gso_type = 0; + virtio_net_dev->info[id].hdr.hdr_len = 0; + virtio_net_dev->info[id].hdr.gso_size = 0; + virtio_net_dev->info[id].hdr.csum_start = 0; + virtio_net_dev->info[id].hdr.csum_offset = 0; + virtio_net_dev->info[id].hdr.num_buffers = 0; + + pbuf_copy_partial(p, virtio_net_dev->info[id].rx_buffer, p->tot_len, 0); + + virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id); + virtio_free_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1); + + virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id, + VIRTIO_VA2PA(&virtio_net_dev->info[id].hdr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT, id + 1); + + virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_TX, id + 1, + VIRTIO_VA2PA(virtio_net_dev->info[id].rx_buffer), p->tot_len, 0, 0); + + virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, id); + + virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_TX); + + virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX); + virtio_alloc_desc(virtio_dev, VIRTIO_NET_QUEUE_TX); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + + return RT_EOK; +} + +static struct pbuf *virtio_net_rx(rt_device_t dev) +{ + rt_uint16_t id; + rt_uint32_t len; + struct pbuf *p = RT_NULL, *new, *ret = RT_NULL; + struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev; + struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev; + struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX]; + + while (queue_rx->used_idx != queue_rx->used->idx) + { +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + id = (queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].id + 1) % queue_rx->num; + len = queue_rx->used->ring[queue_rx->used_idx % queue_rx->num].len - VIRTIO_NET_HDR_SIZE; + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + if (len > VIRTIO_NET_PAYLOAD_MAX_SIZE) + { + rt_kprintf("%s: Receive buffer's size = %u is too big!\n", virtio_net_dev->parent.parent.parent.name, len); + len = VIRTIO_NET_PAYLOAD_MAX_SIZE; + } + + new = pbuf_alloc(PBUF_RAW, len, PBUF_RAM); + + if (p != RT_NULL) + { + p->next = new; + p = p->next; + } + else + { + p = new; + ret = p; + } + + if (p != RT_NULL) + { +#ifdef RT_USING_SMP + level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + rt_memcpy(p->payload, (void *)VIRTIO_PA2VA(queue_rx->desc[id].addr), len); + + queue_rx->used_idx++; + + virtio_submit_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, id - 1); + + virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX); + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif + } + else + { + break; + } + } + + return ret; +} + +static rt_err_t virtio_net_init(rt_device_t dev) +{ + int i; + rt_uint16_t idx[VIRTIO_NET_RTX_QUEUE_SIZE]; + struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev; + struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev; + struct virtq *queue_rx, *queue_tx; + + queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX]; + queue_tx = &virtio_dev->queues[VIRTIO_NET_QUEUE_TX]; + + virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_RX, queue_rx->num, idx); + virtio_alloc_desc_chain(virtio_dev, VIRTIO_NET_QUEUE_TX, queue_tx->num, idx); + + for (i = 0; i < queue_rx->num; ++i) + { + rt_uint16_t id = (i * 2) % queue_rx->num; + void *addr = virtio_net_dev->info[i].tx_buffer; + + /* Descriptor for net_hdr */ + virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id, + VIRTIO_VA2PA(addr), VIRTIO_NET_HDR_SIZE, VIRTQ_DESC_F_NEXT | VIRTQ_DESC_F_WRITE, id + 1); + + /* Descriptor for data */ + virtio_fill_desc(virtio_dev, VIRTIO_NET_QUEUE_RX, id + 1, + VIRTIO_VA2PA(addr) + VIRTIO_NET_HDR_SIZE, VIRTIO_NET_MSS, VIRTQ_DESC_F_WRITE, 0); + + queue_rx->avail->ring[i] = id; + } + rt_hw_dsb(); + + queue_rx->avail->flags = 0; + queue_rx->avail->idx = queue_rx->num; + + queue_rx->used_idx = queue_rx->used->idx; + + queue_tx->avail->flags = VIRTQ_AVAIL_F_NO_INTERRUPT; + queue_tx->avail->idx = 0; + + virtio_queue_notify(virtio_dev, VIRTIO_NET_QUEUE_RX); + + return eth_device_linkchange(&virtio_net_dev->parent, RT_TRUE); +} + +static rt_err_t virtio_net_control(rt_device_t dev, int cmd, void *args) +{ + rt_err_t status = RT_EOK; + struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)dev; + + switch (cmd) + { + case NIOCTL_GADDR: + if (args == RT_NULL) + { + status = -RT_ERROR; + break; + } + + rt_memcpy(args, virtio_net_dev->config->mac, sizeof(virtio_net_dev->config->mac)); + break; + default: + status = -RT_EINVAL; + break; + } + + return status; +} + +#ifdef RT_USING_DEVICE_OPS +const static struct rt_device_ops virtio_net_ops = +{ + virtio_net_init, + RT_NULL, + RT_NULL, + RT_NULL, + RT_NULL, + virtio_net_control +}; +#endif + +static void virtio_net_isr(int irqno, void *param) +{ + struct virtio_net_device *virtio_net_dev = (struct virtio_net_device *)param; + struct virtio_device *virtio_dev = &virtio_net_dev->virtio_dev; + struct virtq *queue_rx = &virtio_dev->queues[VIRTIO_NET_QUEUE_RX]; + +#ifdef RT_USING_SMP + rt_base_t level = rt_spin_lock_irqsave(&virtio_dev->spinlock); +#endif + + virtio_interrupt_ack(virtio_dev); + rt_hw_dsb(); + + if (queue_rx->used_idx != queue_rx->used->idx) + { + rt_hw_dsb(); + + eth_device_ready(&virtio_net_dev->parent); + } + +#ifdef RT_USING_SMP + rt_spin_unlock_irqrestore(&virtio_dev->spinlock, level); +#endif +} + +rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq) +{ + static int dev_no = 0; + char dev_name[RT_NAME_MAX]; + struct virtio_device *virtio_dev; + struct virtio_net_device *virtio_net_dev; + + virtio_net_dev = rt_malloc(sizeof(struct virtio_net_device)); + + if (virtio_net_dev == RT_NULL) + { + goto _alloc_fail; + } + + virtio_dev = &virtio_net_dev->virtio_dev; + virtio_dev->irq = irq; + virtio_dev->mmio_base = mmio_base; + + virtio_net_dev->config = (struct virtio_net_config *)virtio_dev->mmio_config->config; + +#ifdef RT_USING_SMP + rt_spin_lock_init(&virtio_dev->spinlock); +#endif + + virtio_reset_device(virtio_dev); + virtio_status_acknowledge_driver(virtio_dev); + + virtio_dev->mmio_config->driver_features = virtio_dev->mmio_config->device_features & ~( + (1 << VIRTIO_NET_F_CTRL_VQ) | + (1 << VIRTIO_F_RING_EVENT_IDX)); + + virtio_status_driver_ok(virtio_dev); + + if (virtio_queues_alloc(virtio_dev, 2) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_RX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK) + { + goto _alloc_fail; + } + + if (virtio_queue_init(virtio_dev, VIRTIO_NET_QUEUE_TX, VIRTIO_NET_RTX_QUEUE_SIZE) != RT_EOK) + { + virtio_queue_destroy(virtio_dev, VIRTIO_NET_QUEUE_RX); + goto _alloc_fail; + } + + virtio_net_dev->parent.parent.type = RT_Device_Class_NetIf; +#ifdef RT_USING_DEVICE_OPS + virtio_net_dev->parent.parent.ops = &virtio_net_ops; +#else + virtio_net_dev->parent.parent.init = virtio_net_init; + virtio_net_dev->parent.parent.open = RT_NULL; + virtio_net_dev->parent.parent.close = RT_NULL; + virtio_net_dev->parent.parent.read = RT_NULL; + virtio_net_dev->parent.parent.write = RT_NULL; + virtio_net_dev->parent.parent.control = virtio_net_control; +#endif + virtio_net_dev->parent.eth_tx = virtio_net_tx; + virtio_net_dev->parent.eth_rx = virtio_net_rx; + + rt_snprintf(dev_name, RT_NAME_MAX, "virtio-net%d", dev_no++); + + rt_hw_interrupt_install(irq, virtio_net_isr, virtio_net_dev, dev_name); + rt_hw_interrupt_umask(irq); + + return eth_device_init(&virtio_net_dev->parent, dev_name); + +_alloc_fail: + + if (virtio_net_dev != RT_NULL) + { + virtio_queues_free(virtio_dev); + rt_free(virtio_net_dev); + } + return -RT_ENOMEM; +} +#endif /* RT_USING_VIRTIO_NET */ diff --git a/components/drivers/virtio/virtio_net.h b/components/drivers/virtio/virtio_net.h new file mode 100644 index 0000000000..d00c3edb90 --- /dev/null +++ b/components/drivers/virtio/virtio_net.h @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_NET_H__ +#define __VIRTIO_NET_H__ + +#ifdef RT_USING_VIRTIO_NET + +#include <rtdef.h> +#include <netif/ethernetif.h> + +#include <virtio.h> + +#define VIRTIO_NET_QUEUE_RX 0 +#define VIRTIO_NET_QUEUE_TX 1 +#define VIRTIO_NET_RTX_QUEUE_SIZE 16 +#define VIRTIO_NET_RTX_BUF_SIZE 2048 + +#define VIRTIO_NET_F_CSUM 0 /* Host handles pkts w/ partial csum */ +#define VIRTIO_NET_F_GUEST_CSUM 1 /* Guest handles pkts w/ partial csum */ +#define VIRTIO_NET_F_CTRL_GUEST_OFFLOADS 2 /* Dynamic offload configuration */ +#define VIRTIO_NET_F_MTU 3 /* Initial MTU advice */ +#define VIRTIO_NET_F_MAC 5 /* Host has given MAC address */ +#define VIRTIO_NET_F_GUEST_TSO4 7 /* Guest can handle TSOv4 in */ +#define VIRTIO_NET_F_GUEST_TSO6 8 /* Guest can handle TSOv6 in */ +#define VIRTIO_NET_F_GUEST_ECN 9 /* Guest can handle TSO[6] w/ ECN in */ +#define VIRTIO_NET_F_GUEST_UFO 10 /* Guest can handle UFO in */ +#define VIRTIO_NET_F_HOST_TSO4 11 /* Host can handle TSOv4 in */ +#define VIRTIO_NET_F_HOST_TSO6 12 /* Host can handle TSOv6 in */ +#define VIRTIO_NET_F_HOST_ECN 13 /* Host can handle TSO[6] w/ ECN in */ +#define VIRTIO_NET_F_HOST_UFO 14 /* Host can handle UFO in */ +#define VIRTIO_NET_F_MRG_RXBUF 15 /* Host can merge receive buffers. */ +#define VIRTIO_NET_F_STATUS 16 /* virtio_net_config.status available */ +#define VIRTIO_NET_F_CTRL_VQ 17 /* Control channel available */ +#define VIRTIO_NET_F_CTRL_RX 18 /* Control channel RX mode support */ +#define VIRTIO_NET_F_CTRL_VLAN 19 /* Control channel VLAN filtering */ +#define VIRTIO_NET_F_CTRL_RX_EXTRA 20 /* Extra RX mode control support */ +#define VIRTIO_NET_F_GUEST_ANNOUNCE 21 /* Guest can announce device on the network */ +#define VIRTIO_NET_F_MQ 22 /* Device supports Receive Flow Steering */ +#define VIRTIO_NET_F_CTRL_MAC_ADDR 23 /* Set MAC address */ + +#define VIRTIO_NET_F_HASH_REPORT 57 /* Supports hash report */ +#define VIRTIO_NET_F_RSS 60 /* Supports RSS RX steering */ +#define VIRTIO_NET_F_RSC_EXT 61 /* Extended coalescing info */ +#define VIRTIO_NET_F_STANDBY 62 /* Act as standby for another device with the same MAC */ +#define VIRTIO_NET_F_SPEED_DUPLEX 63 /* Device set linkspeed and duplex */ + +#define VIRTIO_NET_S_LINK_UP (1 << 0) +#define VIRTIO_NET_S_ANNOUNCE (1 << 1) + +#define VIRTIO_NET_HDR_F_NEEDS_CSUM 1 +#define VIRTIO_NET_HDR_F_DATA_VALID 2 +#define VIRTIO_NET_HDR_F_RSC_INFO 4 + +#define VIRTIO_NET_HDR_GSO_NONE 0 +#define VIRTIO_NET_HDR_GSO_TCPV4 1 +#define VIRTIO_NET_HDR_GSO_UDP 3 +#define VIRTIO_NET_HDR_GSO_TCPV6 4 +#define VIRTIO_NET_HDR_GSO_ECN 0x80 + +struct virtio_net_hdr +{ + rt_uint8_t flags; + rt_uint8_t gso_type; + rt_uint16_t hdr_len; + rt_uint16_t gso_size; + rt_uint16_t csum_start; + rt_uint16_t csum_offset; + rt_uint16_t num_buffers; +} __attribute__ ((packed)); + +#define VIRTIO_NET_MSS 1514 +#define VIRTIO_NET_HDR_SIZE (sizeof(struct virtio_net_hdr)) +#define VIRTIO_NET_PAYLOAD_MAX_SIZE (VIRTIO_NET_HDR_SIZE + VIRTIO_NET_MSS) + +struct virtio_net_config +{ + rt_uint8_t mac[6]; + rt_uint16_t status; + rt_uint16_t max_virtqueue_pairs; + rt_uint16_t mtu; + rt_uint32_t speed; + rt_uint8_t duplex; + rt_uint8_t rss_max_key_size; + rt_uint16_t rss_max_indirection_table_length; + rt_uint32_t supported_hash_types; +} __attribute__((packed)); + +struct virtio_net_device +{ + struct eth_device parent; + + struct virtio_device virtio_dev; + + struct virtio_net_config *config; + + struct + { + /* Transmit hdr */ + struct virtio_net_hdr hdr; + /* Transmit buffer */ + rt_uint8_t tx_buffer[VIRTIO_NET_PAYLOAD_MAX_SIZE]; + /* Receive buffer */ + rt_uint8_t rx_buffer[VIRTIO_NET_PAYLOAD_MAX_SIZE]; + } info[VIRTIO_NET_RTX_QUEUE_SIZE]; +}; + +rt_err_t rt_virtio_net_init(rt_ubase_t *mmio_base, rt_uint32_t irq); + +#endif /* RT_USING_VIRTIO_NET */ + +#endif /* __VIRTIO_NET_H__ */ diff --git a/components/drivers/virtio/virtio_queue.h b/components/drivers/virtio/virtio_queue.h new file mode 100644 index 0000000000..1e416af131 --- /dev/null +++ b/components/drivers/virtio/virtio_queue.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-11-11 GuEe-GUI the first version + */ + +#ifndef __VIRTIO_QUEUE_H__ +#define __VIRTIO_QUEUE_H__ + +#include <rtdef.h> + +#define VIRTQ_DESC_F_NEXT 1 /* This marks a buffer as continuing via the next field. */ +#define VIRTQ_DESC_F_WRITE 2 /* This marks a buffer as write-only (otherwise read-only). */ +#define VIRTQ_DESC_F_INDIRECT 4 /* This means the buffer contains a list of buffer descriptors. */ + +/* + * The device uses this in used->flags to advise the driver: don't kick me + * when you add a buffer. It's unreliable, so it's simply an optimization. + */ +#define VIRTQ_USED_F_NO_NOTIFY 1 + +/* + * The driver uses this in avail->flags to advise the device: don't + * interrupt me when you consume a buffer. It's unreliable, so it's + * simply an optimization. + */ +#define VIRTQ_AVAIL_F_NO_INTERRUPT 1 + +/* Virtqueue descriptors: 16 bytes. These can chain together via "next". */ +struct virtq_desc +{ + rt_uint64_t addr; /* Address (guest-physical). */ + rt_uint32_t len; /* Length. */ + rt_uint16_t flags; /* The flags as indicated above. */ + rt_uint16_t next; /* We chain unused descriptors via this, too */ +}; + +struct virtq_avail +{ + rt_uint16_t flags; /* Notifications */ + rt_uint16_t idx; /* Where the driver would put the next descriptor entry in the ring (modulo the queue size) */ + rt_uint16_t ring[]; + + /* + * Only if VIRTIO_F_RING_EVENT_IDX + * rt_uint16_t used_event; + */ +}; + +struct virtq_used_elem +{ + rt_uint32_t id; /* Index of start of used descriptor chain. */ + rt_uint32_t len; /* Total length of the descriptor chain which was written to. */ +}; + +struct virtq_used +{ + rt_uint16_t flags; + rt_uint16_t idx; + struct virtq_used_elem ring[]; + + /* + * Only if VIRTIO_F_RING_EVENT_IDX + * rt_uint16_t avail_event; + */ +}; + +struct virtq +{ + rt_uint32_t num; + + struct virtq_desc *desc; + struct virtq_avail *avail; + struct virtq_used *used; + + /* Helper of driver */ + rt_uint16_t used_idx; + rt_bool_t *free; + rt_size_t free_count; +}; + +#define VIRTQ_DESC_TOTAL_SIZE(ring_size) (sizeof(struct virtq_desc) * (ring_size)) +/* flags, idx, used_event + ring * ring_size */ +#define VIRTQ_AVAIL_TOTAL_SIZE(ring_size) (sizeof(rt_uint16_t) * 3 + sizeof(rt_uint16_t) * (ring_size)) +/* flags, idx, avail_event + ring * ring_size */ +#define VIRTQ_USED_TOTAL_SIZE(ring_size) (sizeof(rt_uint16_t) * 3 + sizeof(struct virtq_used_elem) * (ring_size)) + +#define VIRTQ_AVAIL_RES_SIZE (sizeof(rt_uint16_t)) /* used_event */ +#define VIRTQ_USED_RES_SIZE (sizeof(rt_uint16_t)) /* avail_event */ + +#define VIRTQ_INVALID_DESC_ID RT_UINT16_MAX + +#endif /* __VIRTIO_QUEUE_H__ */ diff --git a/components/fal/src/fal_rtt.c b/components/fal/src/fal_rtt.c index 52d276d8c9..98f1c3abe0 100644 --- a/components/fal/src/fal_rtt.c +++ b/components/fal/src/fal_rtt.c @@ -397,13 +397,17 @@ const static struct rt_device_ops char_dev_ops = }; #endif -#ifdef RT_USING_POSIX -#include <dfs_posix.h> +#ifdef RT_USING_POSIX_DEVIO +#include <dfs_file.h> +#include <unistd.h> +#include <stdio.h> /* rename() */ +#include <sys/stat.h> +#include <sys/statfs.h> /* statfs() */ /* RT-Thread device filesystem interface */ static int char_dev_fopen(struct dfs_fd *fd) { - struct fal_char_device *part = (struct fal_char_device *) fd->data; + struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data; assert(part != RT_NULL); @@ -427,7 +431,7 @@ static int char_dev_fopen(struct dfs_fd *fd) static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count) { int ret = 0; - struct fal_char_device *part = (struct fal_char_device *) fd->data; + struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data; assert(part != RT_NULL); @@ -447,7 +451,7 @@ static int char_dev_fread(struct dfs_fd *fd, void *buf, size_t count) static int char_dev_fwrite(struct dfs_fd *fd, const void *buf, size_t count) { int ret = 0; - struct fal_char_device *part = (struct fal_char_device *) fd->data; + struct fal_char_device *part = (struct fal_char_device *) fd->vnode->data; assert(part != RT_NULL); @@ -476,7 +480,7 @@ static const struct dfs_file_ops char_dev_fops = RT_NULL, /* getdents */ RT_NULL, }; -#endif /* defined(RT_USING_POSIX) */ +#endif /* defined(RT_USING_POSIX_DEVIO) */ /** * create RT-Thread char device by specified partition @@ -527,7 +531,7 @@ struct rt_device *fal_char_device_create(const char *parition_name) rt_device_register(RT_DEVICE(char_dev), fal_part->name, RT_DEVICE_FLAG_RDWR); log_i("The FAL char device (%s) created successfully", fal_part->name); -#ifdef RT_USING_POSIX +#ifdef RT_USING_POSIX_DEVIO /* set fops */ char_dev->parent.fops = &char_dev_fops; #endif diff --git a/components/finsh/cmd.c b/components/finsh/cmd.c index 99d601b76d..f2dc3b794e 100644 --- a/components/finsh/cmd.c +++ b/components/finsh/cmd.c @@ -213,7 +213,7 @@ long list_thread(void) #endif /*RT_USING_SMP*/ stat = (thread->stat & RT_THREAD_STAT_MASK); if (stat == RT_THREAD_READY) rt_kprintf(" ready "); - else if (stat == RT_THREAD_SUSPEND) rt_kprintf(" suspend"); + else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) rt_kprintf(" suspend"); else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close "); else if (stat == RT_THREAD_RUNNING) rt_kprintf(" running"); diff --git a/components/finsh/msh.c b/components/finsh/msh.c index 22e2cf5389..884d408ec3 100644 --- a/components/finsh/msh.c +++ b/components/finsh/msh.c @@ -210,7 +210,7 @@ int msh_exec_module(const char *cmd_line, int size) length = cmd_length + 32; /* allocate program name memory */ - pg_name = (char *) rt_malloc(length); + pg_name = (char *) rt_malloc(length + 3); if (pg_name == RT_NULL) return -RT_ENOMEM; @@ -261,7 +261,22 @@ int msh_exec_module(const char *cmd_line, int size) rt_free(pg_name); return ret; } -#endif /* defined(RT_USING_MODULE) && defined(DFS_USING_POSIX) */ + +int system(const char *command) +{ + int ret = -RT_ENOMEM; + char *cmd = rt_strdup(command); + + if (cmd) + { + ret = msh_exec(cmd, rt_strlen(cmd)); + rt_free(cmd); + } + + return ret; +} +RTM_EXPORT(system); +#endif static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp) { @@ -294,16 +309,139 @@ static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp) return 0; } +pid_t exec(char*, int, int, char**); + #if defined(RT_USING_LWP) && defined(DFS_USING_POSIX) -static int _msh_exec_lwp(char *cmd, rt_size_t length) +/* check whether a file of the given path exits */ +static rt_bool_t _msh_lwp_cmd_exists(const char *path) +{ + int fd = -1; + fd = open(path, O_RDONLY, 0); + if (fd < 0) + { + return RT_FALSE; + } + close(fd); + return RT_TRUE; +} + +/* + * search for the file named "pg_name" or "pg_name.elf" at the given directory, + * and return its path. return NULL when not found. + */ +static char *_msh_exec_search_path(const char *path, const char *pg_name) +{ + char *path_buffer = RT_NULL; + ssize_t pg_len = strlen(pg_name); + ssize_t base_len = 0; + + if (path) + { + base_len = strlen(path); + } + + path_buffer = rt_malloc(base_len + pg_len + 6); + if (path_buffer == RT_NULL) + { + return RT_NULL; /* no mem */ + } + + if (base_len > 0) + { + memcpy(path_buffer, path, base_len); + path_buffer[base_len] = '/'; + path_buffer[base_len + 1] = '\0'; + } + else + { + *path_buffer = '\0'; + } + strcat(path_buffer, pg_name); + + if (_msh_lwp_cmd_exists(path_buffer)) + { + return path_buffer; + } + + if (strstr(path_buffer, ".elf") != NULL) + { + goto not_found; + } + + strcat(path_buffer, ".elf"); + if (_msh_lwp_cmd_exists(path_buffer)) + { + return path_buffer; + } + +not_found: + rt_free(path_buffer); + return RT_NULL; +} + +/* + * search for the file named "pg_name" or "pg_name.elf" at each env path, + * and return its path. return NULL when not found. + */ +static char *_msh_exec_search_env(const char *pg_name) +{ + char *result = RT_NULL; + char *exec_path = RT_NULL; + char *search_path = RT_NULL; + char *pos = RT_NULL; + char tmp_ch = '\0'; + + if (!(exec_path = getenv("PATH"))) + { + return RT_NULL; + } + + /* exec path may need to be modified */ + if (!(exec_path = strdup(exec_path))) + { + return RT_NULL; + } + + pos = exec_path; + search_path = exec_path; + + /* walk through the entire exec_path until finding the program wanted + or hitting its end */ + while (1) + { + /* env paths are seperated by ':' */ + if (*pos == ':' || *pos == '\0') + { + tmp_ch = *pos; + *pos = '\0'; + + result = _msh_exec_search_path(search_path, pg_name); + if (result || tmp_ch == '\0') + { + goto ret; + } + + pos++; + search_path = pos; + continue; + } + + pos++; + } + + /* release the duplicated exec_path and return */ +ret: + rt_free(exec_path); + return result; +} + +int _msh_exec_lwp(int debug, char *cmd, rt_size_t length) { int argc; int cmd0_size = 0; char *argv[FINSH_ARG_MAX]; - int fd = -1; char *pg_name; - - extern int exec(char *, int, char **); + int ret; /* find the size of first command */ while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length) @@ -317,20 +455,46 @@ static int _msh_exec_lwp(char *cmd, rt_size_t length) if (argc == 0) return -1; - pg_name = argv[0]; - /* try to open program */ - fd = open(pg_name, O_RDONLY, 0); + /* try to find program in working directory */ + pg_name = _msh_exec_search_path("", argv[0]); + if (pg_name) + { + goto found_program; + } - if (fd < 0) + /* only check these paths when the first argument doesn't contain path + seperator */ + if (strstr(argv[0], "/")) + { return -1; + } + + /* try to find program in /bin */ + pg_name = _msh_exec_search_path("/bin", argv[0]); + if (pg_name) + { + goto found_program; + } + + /* try to find program in dirs registered to env path */ + pg_name = _msh_exec_search_env(argv[0]); + if (pg_name) + { + goto found_program; + } + + /* not found in anywhere */ + return -1; /* found program */ - close(fd); - exec(pg_name, argc, argv); +found_program: + ret = exec(pg_name, debug, argc, argv); + rt_free(pg_name); - return 0; + return ret; } -#endif /* defined(RT_USING_LWP) && defined(DFS_USING_POSIX) */ +#endif + int msh_exec(char *cmd, rt_size_t length) { @@ -370,7 +534,9 @@ int msh_exec(char *cmd, rt_size_t length) #endif /* RT_USING_MODULE */ #ifdef RT_USING_LWP - if (_msh_exec_lwp(cmd, length) == 0) + /* exec from msh_exec , debug = 0*/ + /* _msh_exec_lwp return is pid , <= 0 means failed */ + if (_msh_exec_lwp(0, cmd, length) > 0) { return 0; } @@ -472,6 +638,7 @@ void msh_auto_complete_path(char *path) } else { + int multi = 0; rt_size_t length, min_length; min_length = 0; @@ -483,6 +650,7 @@ void msh_auto_complete_path(char *path) /* matched the prefix string */ if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0) { + multi ++; if (min_length == 0) { min_length = rt_strlen(dirent->d_name); @@ -501,7 +669,7 @@ void msh_auto_complete_path(char *path) if (min_length) { - if (min_length < rt_strlen(full_path)) + if (multi > 1) { /* list the candidate */ rewinddir(dir); @@ -519,6 +687,16 @@ void msh_auto_complete_path(char *path) length = index - path; rt_memcpy(index, full_path, min_length); path[length + min_length] = '\0'; + + /* try to locate folder */ + if (multi == 1) + { + struct stat buffer = {0}; + if ((stat(path, &buffer) == 0) && (S_ISDIR(buffer.st_mode))) + { + strcat(path, "/"); + } + } } } @@ -558,7 +736,7 @@ void msh_auto_complete(char *prefix) ptr --; } -#ifdef RT_USING_MODULE +#if defined(RT_USING_MODULE) || defined(RT_USING_LWP) /* There is a chance that the user want to run the module directly. So * try to complete the file names. If the completed path is not a * module, the system won't crash anyway. */ diff --git a/components/finsh/msh_file.c b/components/finsh/msh_file.c index 9deab4f0bc..04255747bd 100644 --- a/components/finsh/msh_file.c +++ b/components/finsh/msh_file.c @@ -104,7 +104,7 @@ int msh_exec_script(const char *cmd_line, int size) int length; line_buf = (char *) rt_malloc(RT_CONSOLEBUF_SIZE); - if (line_buf == RT_NULL) + if (line_buf == RT_NULL) { close(fd); return -RT_ENOMEM; @@ -480,6 +480,15 @@ static int cmd_mkfs(int argc, char **argv) MSH_CMD_EXPORT_ALIAS(cmd_mkfs, mkfs, format disk with file system); extern struct dfs_filesystem filesystem_table[]; + +/* + * If no argument is specified, display the mount history; + * If there are 3 arguments, mount the filesystem. + * The order of the arguments is: + * argv[1]: device name + * argv[2]: mountpoint path + * argv[3]: filesystem type + */ static int cmd_mount(int argc, char **argv) { if (argc == 1) @@ -519,6 +528,24 @@ static int cmd_mount(int argc, char **argv) return -1; } } + else if (argc == 3) + { + char *path = argv[1]; + char *fstype = argv[2]; + + /* mount a filesystem to the specified directory */ + rt_kprintf("mount (%s) onto %s ... ", fstype, path); + if (dfs_mount(NULL, path, fstype, 0, 0) == 0) + { + rt_kprintf("succeed!\n"); + return 0; + } + else + { + rt_kprintf("failed!\n"); + return -1; + } + } else { rt_kprintf("Usage: mount <device> <mountpoint> <fstype>.\n"); @@ -550,7 +577,7 @@ static int cmd_umount(int argc, char **argv) return 0; } } -MSH_CMD_EXPORT_ALIAS(cmd_umount, umount, Unmount device from file system); +MSH_CMD_EXPORT_ALIAS(cmd_umount, umount, Unmount the mountpoint); extern int df(const char *path); static int cmd_df(int argc, char **argv) diff --git a/components/libc/compilers/common/ctime.c b/components/libc/compilers/common/ctime.c index 64e3c9424c..493b502534 100644 --- a/components/libc/compilers/common/ctime.c +++ b/components/libc/compilers/common/ctime.c @@ -26,6 +26,9 @@ #include <rtthread.h> #include <rthw.h> #include <unistd.h> +#ifdef RT_USING_LWP +#include "lwp.h" +#endif #ifdef RT_USING_POSIX_DELAY #include <delay.h> #endif @@ -376,7 +379,7 @@ int stime(const time_t *t) if (t == RT_NULL) { rt_set_errno(EFAULT); - return -1; + return -RT_ERROR; } tv.tv_sec = *t; @@ -387,7 +390,7 @@ int stime(const time_t *t) else { rt_set_errno(EFAULT); - return -1; + return -RT_ERROR; } } RTM_EXPORT(stime); @@ -401,7 +404,7 @@ time_t timegm(struct tm * const t) if(t == RT_NULL) { rt_set_errno(EFAULT); - return (time_t)-1; + return (time_t)-RT_ERROR; } years = (time_t)t->tm_year - 70; @@ -443,7 +446,7 @@ time_t timegm(struct tm * const t) if (t->tm_year < 70) { rt_set_errno(EINVAL); - return (time_t) -1; + return (time_t) -RT_ERROR; } /* Days since 1970 is 365 * number of years + number of leap years since 1970 */ @@ -497,7 +500,7 @@ int gettimeofday(struct timeval *tv, struct timezone *tz) else { rt_set_errno(EINVAL); - return -1; + return -RT_ERROR; } } RTM_EXPORT(gettimeofday); @@ -518,7 +521,7 @@ int settimeofday(const struct timeval *tv, const struct timezone *tz) else { rt_set_errno(EINVAL); - return -1; + return -RT_ERROR; } } RTM_EXPORT(settimeofday); @@ -526,8 +529,52 @@ RTM_EXPORT(settimeofday); #ifdef RT_USING_POSIX_DELAY int nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { - sleep(rqtp->tv_sec); - ndelay(rqtp->tv_nsec); + if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= NANOSECOND_PER_SECOND) + { + rt_set_errno(EINVAL); + return -RT_ERROR; + } +#ifdef RT_USING_CPUTIME + uint64_t cpu_tick, cpu_tick_old; + cpu_tick_old = clock_cpu_gettime(); + rt_tick_t tick; + float unit = clock_cpu_getres(); + + cpu_tick = (rqtp->tv_sec * NANOSECOND_PER_SECOND + ((uint64_t)rqtp->tv_nsec * NANOSECOND_PER_SECOND) / NANOSECOND_PER_SECOND) / unit; + tick = (unit * cpu_tick) / (NANOSECOND_PER_SECOND / RT_TICK_PER_SECOND); + rt_thread_delay(tick); + + if (rt_get_errno() == -RT_EINTR) + { + if (rmtp) + { + uint64_t rmtp_cpu_tick = cpu_tick_old + cpu_tick - clock_cpu_gettime(); + rmtp->tv_sec = ((int)(rmtp_cpu_tick * unit)) / NANOSECOND_PER_SECOND; + rmtp->tv_nsec = ((int)(rmtp_cpu_tick * unit)) % NANOSECOND_PER_SECOND; + } + rt_set_errno(EINTR); + return -RT_ERROR; + } + else + while (clock_cpu_gettime() - cpu_tick_old < cpu_tick); +#else + rt_tick_t tick, tick_old = rt_tick_get(); + tick = rqtp->tv_sec * RT_TICK_PER_SECOND + ((uint64_t)rqtp->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND; + rt_thread_delay(tick); + + if (rt_get_errno() == -RT_EINTR) + { + if (rmtp) + { + tick = tick_old + tick - rt_tick_get(); + /* get the passed time */ + rmtp->tv_sec = tick / RT_TICK_PER_SECOND; + rmtp->tv_nsec = (tick % RT_TICK_PER_SECOND) * (NANOSECOND_PER_SECOND / RT_TICK_PER_SECOND); + } + rt_set_errno(EINTR); + return -RT_ERROR; + } +#endif return 0; } RTM_EXPORT(nanosleep); @@ -563,7 +610,7 @@ static int _rt_clock_time_system_init(void) _timevalue.tv_sec = 0; rt_hw_interrupt_enable(level); - return -1; + return -RT_ERROR; } INIT_COMPONENT_EXPORT(_rt_clock_time_system_init); #endif /* RT_USING_RTC */ @@ -572,14 +619,14 @@ int clock_getres(clockid_t clockid, struct timespec *res) { #ifndef RT_USING_RTC LOG_W(_WARNING_NO_RTC); - return -1; + return -RT_ERROR; #else int ret = 0; if (res == RT_NULL) { rt_set_errno(EFAULT); - return -1; + return -RT_ERROR; } switch (clockid) @@ -599,7 +646,7 @@ int clock_getres(clockid_t clockid, struct timespec *res) default: res->tv_sec = 0; res->tv_nsec = 0; - ret = -1; + ret = -RT_ERROR; rt_set_errno(EINVAL); break; } @@ -613,14 +660,14 @@ int clock_gettime(clockid_t clockid, struct timespec *tp) { #ifndef RT_USING_RTC LOG_W(_WARNING_NO_RTC); - return -1; + return -RT_ERROR; #else int ret = 0; if (tp == RT_NULL) { rt_set_errno(EFAULT); - return -1; + return -RT_ERROR; } switch (clockid) @@ -656,7 +703,7 @@ int clock_gettime(clockid_t clockid, struct timespec *tp) tp->tv_sec = 0; tp->tv_nsec = 0; rt_set_errno(EINVAL); - ret = -1; + ret = -RT_ERROR; } return ret; @@ -666,20 +713,91 @@ RTM_EXPORT(clock_gettime); int clock_nanosleep(clockid_t clockid, int flags, const struct timespec *rqtp, struct timespec *rmtp) { - if ((clockid != CLOCK_REALTIME) || (rqtp == RT_NULL)) +#ifndef RT_USING_RTC + LOG_W(_WARNING_NO_RTC); + return -RT_ERROR; +#else + if (rqtp->tv_sec < 0 || rqtp->tv_nsec < 0 || rqtp->tv_nsec >= NANOSECOND_PER_SECOND) { rt_set_errno(EINVAL); - return -1; + return -RT_ERROR; } + switch (clockid) + { + case CLOCK_REALTIME: + { + rt_tick_t tick, tick_old = rt_tick_get(); + if (flags & TIMER_ABSTIME == TIMER_ABSTIME) + { + tick = (rqtp->tv_sec - _timevalue.tv_sec) * RT_TICK_PER_SECOND + (rqtp->tv_nsec - _timevalue.tv_usec) * (RT_TICK_PER_SECOND / NANOSECOND_PER_SECOND); + rt_tick_t rt_tick = rt_tick_get(); + tick = tick < rt_tick ? 0 : tick - rt_tick; + } + else + { + tick = rqtp->tv_sec * RT_TICK_PER_SECOND + ((uint64_t)(rqtp->tv_nsec) * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND; + } + rt_thread_delay(tick); + if (rt_get_errno() == -RT_EINTR) + { + if (rmtp) + { + tick = tick_old + tick - rt_tick_get(); + /* get the passed time */ + rmtp->tv_sec = tick / RT_TICK_PER_SECOND; + rmtp->tv_nsec = (tick % RT_TICK_PER_SECOND) * (NANOSECOND_PER_SECOND / RT_TICK_PER_SECOND); + } + rt_set_errno(EINTR); + return -RT_ERROR; + } + } + break; + +#ifdef RT_USING_CPUTIME + case CLOCK_MONOTONIC: + case CLOCK_CPUTIME_ID: + { + uint64_t cpu_tick, cpu_tick_old; + cpu_tick_old = clock_cpu_gettime(); + rt_tick_t tick; + float unit = clock_cpu_getres(); + + cpu_tick = (rqtp->tv_sec * NANOSECOND_PER_SECOND + rqtp->tv_nsec * (NANOSECOND_PER_SECOND / NANOSECOND_PER_SECOND)) / unit; + if (flags & TIMER_ABSTIME == TIMER_ABSTIME) + cpu_tick = cpu_tick < cpu_tick_old ? 0 : cpu_tick - cpu_tick_old; + tick = (unit * cpu_tick) / (NANOSECOND_PER_SECOND / RT_TICK_PER_SECOND); + rt_thread_delay(tick); - return nanosleep(rqtp, rmtp); + if (rt_get_errno() == -RT_EINTR) + { + if (rmtp) + { + uint64_t rmtp_cpu_tick = cpu_tick_old + cpu_tick - clock_cpu_gettime(); + rmtp->tv_sec = ((int)(rmtp_cpu_tick * unit)) / NANOSECOND_PER_SECOND; + rmtp->tv_nsec = ((int)(rmtp_cpu_tick * unit)) % NANOSECOND_PER_SECOND; + } + rt_set_errno(EINTR); + return -RT_ERROR; + } + else + while (clock_cpu_gettime() - cpu_tick_old < cpu_tick); + } + break; +#endif + default: + rt_set_errno(EINVAL); + return -RT_ERROR; + } + return 0; +#endif } +RTM_EXPORT(clock_nanosleep); int clock_settime(clockid_t clockid, const struct timespec *tp) { #ifndef RT_USING_RTC LOG_W(_WARNING_NO_RTC); - return -1; + return -RT_ERROR; #else rt_base_t level; int second; @@ -689,7 +807,7 @@ int clock_settime(clockid_t clockid, const struct timespec *tp) if ((clockid != CLOCK_REALTIME) || (tp == RT_NULL)) { rt_set_errno(EFAULT); - return -1; + return -RT_ERROR; } /* get second */ @@ -713,7 +831,7 @@ int clock_settime(clockid_t clockid, const struct timespec *tp) } } - return -1; + return -RT_ERROR; #endif /* RT_USING_RTC */ } RTM_EXPORT(clock_settime); @@ -764,8 +882,13 @@ struct timer_obj void (*sigev_notify_function)(union sigval val); union sigval val; struct timespec interval; /* Reload value */ + struct timespec value; /* Reload value */ rt_uint32_t reload; /* Reload value in ms */ rt_uint32_t status; + int sigev_signo; +#ifdef RT_USING_LWP + pid_t pid; +#endif }; static void rtthread_timer_wrapper(void *timerobj) @@ -779,10 +902,18 @@ static void rtthread_timer_wrapper(void *timerobj) timer->status = NOT_ACTIVE; } + timer->reload = (timer->interval.tv_sec * RT_TICK_PER_SECOND) + (timer->interval.tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND; + if (timer->reload) + rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_TIME, &(timer->reload)); + +#ifdef RT_USING_LWP + sys_kill(timer->pid, timer->sigev_signo); +#else if(timer->sigev_notify_function != RT_NULL) { (timer->sigev_notify_function)(timer->val); } +#endif } /** @@ -799,7 +930,7 @@ int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) struct timer_obj *timer; char timername[RT_NAME_MAX] = {0}; - if (clockid != CLOCK_MONOTONIC || evp == NULL || + if (clockid > CLOCK_ID_MAX || (evp->sigev_notify != SIGEV_NONE && evp->sigev_notify != SIGEV_SIGNAL)) { @@ -814,9 +945,12 @@ int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) return -RT_ENOMEM; } - RT_ASSERT(evp->sigev_notify_function != RT_NULL); rt_snprintf(timername, RT_NAME_MAX, "psx_tm%02d", num++); num %= 100; + timer->sigev_signo = evp->sigev_signo; +#ifdef RT_USING_LWP + timer->pid = lwp_self()->pid; +#endif timer->sigev_notify_function = evp->sigev_notify_function; timer->val = evp->sigev_value; timer->interval.tv_sec = 0; @@ -833,7 +967,7 @@ int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid) rt_timer_init(&timer->timer, timername, rtthread_timer_wrapper, timer, 0, RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_SOFT_TIMER); } - *timerid = (timer_t)timer; + *timerid = (timer_t)((uintptr_t)timer >> 1); return RT_EOK; } @@ -846,9 +980,9 @@ RTM_EXPORT(timer_create); */ int timer_delete(timer_t timerid) { - struct timer_obj *timer = (struct timer_obj *)timerid; + struct timer_obj *timer = (struct timer_obj *)((uintptr_t)timerid << 1); - if (timer == RT_NULL) + if (timer == RT_NULL || rt_object_get_type(&timer->timer.parent) != RT_Object_Class_Timer) { rt_set_errno(EINVAL); return -RT_ERROR; @@ -859,7 +993,7 @@ int timer_delete(timer_t timerid) timer->status = NOT_ACTIVE; rt_timer_stop(&timer->timer); } - + rt_timer_detach(&timer->timer); rt_free(timer); return RT_EOK; @@ -873,6 +1007,7 @@ RTM_EXPORT(timer_delete); **/ int timer_getoverrun(timer_t timerid) { + struct timer_obj *timer = (struct timer_obj *)((uintptr_t)timerid << 1); rt_set_errno(ENOSYS); return -RT_ERROR; } @@ -884,11 +1019,11 @@ int timer_getoverrun(timer_t timerid) */ int timer_gettime(timer_t timerid, struct itimerspec *its) { - struct timer_obj *timer = (struct timer_obj *)timerid; + struct timer_obj *timer = (struct timer_obj *)((uintptr_t)timerid << 1); rt_tick_t remaining; rt_uint32_t seconds, nanoseconds; - if (timer == NULL) + if (timer == NULL || rt_object_get_type(&timer->timer.parent) != RT_Object_Class_Timer) { rt_set_errno(EINVAL); return -RT_ERROR; @@ -951,24 +1086,20 @@ RTM_EXPORT(timer_gettime); int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspec *ovalue) { - struct timer_obj *timer = (struct timer_obj *)timerid; - + struct timer_obj *timer = (struct timer_obj *)((uintptr_t)timerid << 1); if (timer == NULL || + rt_object_get_type(&timer->timer.parent) != RT_Object_Class_Timer || value->it_interval.tv_nsec < 0 || value->it_interval.tv_nsec >= NANOSECOND_PER_SECOND || + value->it_interval.tv_sec < 0 || value->it_value.tv_nsec < 0 || - value->it_value.tv_nsec >= NANOSECOND_PER_SECOND) + value->it_value.tv_nsec >= NANOSECOND_PER_SECOND || + value->it_value.tv_sec < 0) { rt_set_errno(EINVAL); return -RT_ERROR; } - if (value == NULL || ovalue == NULL) - { - rt_set_errno(EFAULT); - return -RT_ERROR; - } - /* Save time to expire and old reload value. */ if (ovalue != NULL) { @@ -994,9 +1125,25 @@ int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, * RT_TICK_PER_SECOND NANOSECOND_PER_SECOND NANOSECOND_PER_SECOND * */ - timer->reload = (value->it_interval.tv_sec * RT_TICK_PER_SECOND) + (value->it_interval.tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND; + if (flags & TIMER_ABSTIME == TIMER_ABSTIME) + { +#ifndef RT_USING_RTC + LOG_W(_WARNING_NO_RTC); + return -RT_ERROR; +#else + rt_int64_t ts = ((value->it_value.tv_sec - _timevalue.tv_sec) * RT_TICK_PER_SECOND); + rt_int64_t tns = (value->it_value.tv_nsec - _timevalue.tv_usec) * (RT_TICK_PER_SECOND / NANOSECOND_PER_SECOND); + rt_int64_t reload = ts + tns; + rt_tick_t rt_tick = rt_tick_get(); + timer->reload = reload < rt_tick ? 0 : reload - rt_tick; +#endif + } + else + timer->reload = (value->it_value.tv_sec * RT_TICK_PER_SECOND) + value->it_value.tv_nsec * (RT_TICK_PER_SECOND / NANOSECOND_PER_SECOND); timer->interval.tv_sec = value->it_interval.tv_sec; timer->interval.tv_nsec = value->it_interval.tv_nsec; + timer->value.tv_sec = value->it_value.tv_sec; + timer->value.tv_nsec = value->it_value.tv_nsec; if (timer->status == ACTIVE) { @@ -1004,8 +1151,13 @@ int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, } timer->status = ACTIVE; - rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_TIME, (void *)timer->reload); - rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_PERIODIC, RT_NULL); + + if ((value->it_interval.tv_sec == 0) && (value->it_interval.tv_nsec == 0)) + rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_ONESHOT, RT_NULL); + else + rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_PERIODIC, RT_NULL); + + rt_timer_control(&timer->timer, RT_TIMER_CTRL_SET_TIME, &(timer->reload)); rt_timer_start(&timer->timer); return RT_EOK; diff --git a/components/libc/compilers/common/include/sys/signal.h b/components/libc/compilers/common/include/sys/signal.h index 1a3b9c1beb..f2a21baed7 100644 --- a/components/libc/compilers/common/include/sys/signal.h +++ b/components/libc/compilers/common/include/sys/signal.h @@ -24,10 +24,10 @@ extern "C" { #define SIGEV_NONE 1 /* No asynchronous notification shall be delivered */ /* when the event of interest occurs. */ -#define SIGEV_SIGNAL 2 /* A queued signal, with an application defined */ +#define SIGEV_SIGNAL 0 /* A queued signal, with an application defined */ /* value, shall be delivered when the event of */ /* interest occurs. */ -#define SIGEV_THREAD 3 /* A notification function shall be called to */ +#define SIGEV_THREAD 2 /* A notification function shall be called to */ /* perform notification. */ /* Signal Generation and Delivery, P1003.1b-1993, p. 63 @@ -41,9 +41,9 @@ union sigval struct sigevent { - int sigev_notify; /* Notification type */ + union sigval sigev_value; int sigev_signo; /* Signal number */ - union sigval sigev_value; /* Signal value */ + int sigev_notify; /* Notification type */ void (*sigev_notify_function)( union sigval ); /* Notification function */ void *sigev_notify_attributes; /* Notification Attributes, really pthread_attr_t */ diff --git a/components/libc/compilers/common/include/sys/time.h b/components/libc/compilers/common/include/sys/time.h index e3bf3ee4ef..8a1ae60909 100644 --- a/components/libc/compilers/common/include/sys/time.h +++ b/components/libc/compilers/common/include/sys/time.h @@ -99,8 +99,6 @@ time_t time(time_t* t); int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); #endif /* RT_USING_POSIX_DELAY */ -#if defined(RT_USING_POSIX_CLOCK) || defined (RT_USING_POSIX_TIMER) -/* POSIX clock and timer */ #define MILLISECOND_PER_SECOND 1000UL #define MICROSECOND_PER_SECOND 1000000UL #define NANOSECOND_PER_SECOND 1000000000UL @@ -109,6 +107,9 @@ int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); #define MICROSECOND_PER_TICK (MICROSECOND_PER_SECOND / RT_TICK_PER_SECOND) #define NANOSECOND_PER_TICK (NANOSECOND_PER_SECOND / RT_TICK_PER_SECOND) +#if defined(RT_USING_POSIX_CLOCK) || defined (RT_USING_POSIX_TIMER) +/* POSIX clock and timer */ + #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 1 #endif /* CLOCK_REALTIME */ @@ -126,6 +127,12 @@ int nanosleep(const struct timespec *rqtp, struct timespec *rmtp); #ifndef CLOCK_MONOTONIC #define CLOCK_MONOTONIC 4 #endif /* CLOCK_MONOTONIC */ + +#ifdef CLOCK_TAI +#define CLOCK_ID_MAX CLOCK_TAI +#else +#define CLOCK_ID_MAX CLOCK_MONOTONIC +#endif #endif /* defined(RT_USING_POSIX_CLOCK) || defined (RT_USING_POSIX_TIMER) */ #ifdef RT_USING_POSIX_CLOCK @@ -137,7 +144,7 @@ int rt_timespec_to_tick(const struct timespec *time); #endif /* RT_USING_POSIX_CLOCK */ #ifdef RT_USING_POSIX_TIMER -#include "signal.h" +#include <sys/signal.h> int timer_create(clockid_t clockid, struct sigevent *evp, timer_t *timerid); int timer_delete(timer_t timerid); int timer_getoverrun(timer_t timerid); diff --git a/components/libc/compilers/musl/fcntl.h b/components/libc/compilers/musl/fcntl.h new file mode 100644 index 0000000000..05d466f848 --- /dev/null +++ b/components/libc/compilers/musl/fcntl.h @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-09-02 Meco Man First version + */ + +#ifndef __FCNTL_H__ +#define __FCNTL_H__ + +#include_next <fcntl.h> + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0x200000 +#endif + +#ifndef O_BINARY +#define O_BINARY 0x10000 +#endif + +#endif diff --git a/components/libc/posix/io/mman/mman.c b/components/libc/posix/io/mman/mman.c index de76bdf314..69a86d9bab 100644 --- a/components/libc/posix/io/mman/mman.c +++ b/components/libc/posix/io/mman/mman.c @@ -13,6 +13,7 @@ #include <rtthread.h> #include <unistd.h> +#include <stdlib.h> #include <sys/stat.h> #include <sys/statfs.h> #include <sys/errno.h> diff --git a/components/libc/posix/io/poll/poll.c b/components/libc/posix/io/poll/poll.c index 5067817449..9fcf0f2063 100644 --- a/components/libc/posix/io/poll/poll.c +++ b/components/libc/posix/io/poll/poll.c @@ -89,20 +89,22 @@ static int poll_wait_timeout(struct rt_poll_table *pt, int msec) if (timeout != 0 && !pt->triggered) { - rt_thread_suspend(thread); - if (timeout > 0) + if (rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE) == RT_EOK) { - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &timeout); - rt_timer_start(&(thread->thread_timer)); - } + if (timeout > 0) + { + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } - rt_hw_interrupt_enable(level); + rt_hw_interrupt_enable(level); - rt_schedule(); + rt_schedule(); - level = rt_hw_interrupt_disable(); + level = rt_hw_interrupt_disable(); + } } ret = !pt->triggered; @@ -126,23 +128,21 @@ static int do_pollfd(struct pollfd *pollfd, rt_pollreq_t *req) if (f) { mask = POLLMASK_DEFAULT; - if (f->fops->poll) + if (f->vnode->fops->poll) { req->_key = pollfd->events | POLLERR | POLLHUP; - mask = f->fops->poll(f, req); + mask = f->vnode->fops->poll(f, req); /* dealwith the device return error -1*/ if (mask < 0) { - fd_put(f); pollfd->revents = 0; return mask; } } /* Mask out unneeded events. */ mask &= pollfd->events | POLLERR | POLLHUP; - fd_put(f); } } pollfd->revents = mask; diff --git a/components/libc/posix/io/stdio/libc.c b/components/libc/posix/io/stdio/libc.c index 3c4e9d6e0b..4d3ac6dd8a 100644 --- a/components/libc/posix/io/stdio/libc.c +++ b/components/libc/posix/io/stdio/libc.c @@ -20,6 +20,8 @@ #define STDIO_DEVICE_NAME_MAX 32 +int sys_dup2(int oldfd, int new); + int libc_system_init(void) { #ifdef RT_USING_POSIX_STDIO @@ -28,7 +30,15 @@ int libc_system_init(void) dev_console = rt_console_get_device(); if (dev_console) { - libc_stdio_set_console(dev_console->parent.name, O_RDWR); + int fd = libc_stdio_set_console(dev_console->parent.name, O_RDWR); + if (fd < 0) + { + return -1; + } + /* set fd (0, 1, 2) */ + sys_dup2(fd, 0); + sys_dup2(fd, 1); + sys_dup2(fd, 2); } #endif /* RT_USING_POSIX_STDIO */ return 0; @@ -112,7 +122,6 @@ int libc_stdio_get_console(void) #elif defined(RT_USING_POSIX_STDIO) && defined(RT_USING_MUSLLIBC) static FILE* std_console = NULL; -int sys_dup2(int oldfd, int new); int libc_stdio_set_console(const char* device_name, int mode) { @@ -144,10 +153,6 @@ int libc_stdio_set_console(const char* device_name, int mode) { int fd = fileno(std_console); - // /* set fd (0, 1, 2) */ - // sys_dup2(fd, 0); - // sys_dup2(fd, 1); - // sys_dup2(fd, 2); return fd; } diff --git a/components/libc/posix/io/termios/termios.h b/components/libc/posix/io/termios/termios.h index 8553b01918..8f0275d118 100644 --- a/components/libc/posix/io/termios/termios.h +++ b/components/libc/posix/io/termios/termios.h @@ -6,6 +6,7 @@ * Change Logs: * Date Author Notes * 2017/08/30 Bernard The first version + * 2021/12/10 linzhenxing put tty system */ #ifndef __TERMIOS_H__ @@ -35,6 +36,7 @@ struct termios { speed_t __c_ospeed; }; +/* c_cc characters */ #define VINTR 0 #define VQUIT 1 #define VERASE 2 @@ -53,6 +55,7 @@ struct termios { #define VLNEXT 15 #define VEOL2 16 +/* c_iflag bits */ #define IGNBRK 0000001 #define BRKINT 0000002 #define IGNPAR 0000004 @@ -69,37 +72,42 @@ struct termios { #define IMAXBEL 0020000 #define IUTF8 0040000 +/* c_oflag bits */ #define OPOST 0000001 -#define OLCUC 0000002 -#define ONLCR 0000004 +#define ONLCR 0000002 +#define OLCUC 0000004 + #define OCRNL 0000010 #define ONOCR 0000020 #define ONLRET 0000040 -#define OFILL 0000100 -#define OFDEL 0000200 -#define NLDLY 0000400 -#define NL0 0000000 -#define NL1 0000400 -#define CRDLY 0003000 -#define CR0 0000000 -#define CR1 0001000 -#define CR2 0002000 -#define CR3 0003000 -#define TABDLY 0014000 -#define TAB0 0000000 -#define TAB1 0004000 -#define TAB2 0010000 -#define TAB3 0014000 -#define BSDLY 0020000 -#define BS0 0000000 -#define BS1 0020000 -#define FFDLY 0100000 -#define FF0 0000000 -#define FF1 0100000 -#define VTDLY 0040000 -#define VT0 0000000 -#define VT1 0040000 +#define OFILL 00000100 +#define OFDEL 00000200 +#define NLDLY 00001400 +#define NL0 00000000 +#define NL1 00000400 +#define NL2 00001000 +#define NL3 00001400 +#define TABDLY 00006000 +#define TAB0 00000000 +#define TAB1 00002000 +#define TAB2 00004000 +#define TAB3 00006000 +#define CRDLY 00030000 +#define KCR0 00000000 +#define KCR1 00010000 +#define KCR2 00020000 +#define KCR3 00030000 +#define FFDLY 00040000 +#define FF0 00000000 +#define FF1 00040000 +#define BSDLY 00100000 +#define BS0 00000000 +#define BS1 00100000 +#define VTDLY 00200000 +#define VT0 00000000 +#define VT1 00200000 +#define XTABS 01000000 #define B0 0000000 #define B50 0000001 @@ -146,15 +154,23 @@ struct termios { #define HUPCL 0002000 #define CLOCAL 0004000 -#define ISIG 0000001 -#define ICANON 0000002 -#define ECHO 0000010 -#define ECHOE 0000020 -#define ECHOK 0000040 -#define ECHONL 0000100 -#define NOFLSH 0000200 -#define TOSTOP 0000400 -#define IEXTEN 0100000 +/* c_lflag bits */ +#define ISIG 0000001 +#define ICANON 0000002 +#define XCASE 0000004 +#define ECHO 0000010 +#define ECHOE 0000020 +#define ECHOK 0000040 +#define ECHONL 0000100 +#define NOFLSH 0000200 +#define TOSTOP 0000400 +#define ECHOCTL 0001000 +#define ECHOPRT 0002000 +#define ECHOKE 0004000 +#define FLUSHO 0010000 +#define PENDIN 0040000 +#define IEXTEN 0100000 +#define EXTPROC 0200000 #define TCOOFF 0 #define TCOON 1 @@ -185,7 +201,13 @@ struct termios { #define PENDIN 0040000 #define EXTPROC 0200000 -#define XTABS 0014000 +/* intr=^C quit=^| erase=del kill=^U + eof=^D vtime=\0 vmin=\1 sxtc=\0 + start=^Q stop=^S susp=^Z eol=\0 + reprint=^R discard=^U werase=^W lnext=^V + eol2=\0 +*/ +#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0" speed_t cfgetospeed (const struct termios *); speed_t cfgetispeed (const struct termios *); diff --git a/components/lwp/Kconfig b/components/lwp/Kconfig index 5ea3017479..c4178c8274 100644 --- a/components/lwp/Kconfig +++ b/components/lwp/Kconfig @@ -1,8 +1,75 @@ -config RT_USING_LWP - bool "LWP: light-weight process" - select RT_USING_POSIX_FS - select RT_USING_POSIX_SELECT - depends on ARCH_ARM_CORTEX_M || ARCH_ARM_ARM9 || ARCH_ARM_CORTEX_A +menuconfig RT_USING_LWP + bool "light-weight process" + select RT_USING_DFS + select RT_USING_LIBC + select RT_USING_POSIX_CLOCKTIME + select RT_USING_TTY + select RT_USING_NULL + select RT_USING_ZERO + select RT_USING_RANDOM + select RT_USING_RTC + depends on ARCH_ARM_CORTEX_M || ARCH_ARM_ARM9 || ARCH_ARM_CORTEX_A || ARCH_RISCV64 default n help The lwP is a light weight process running in user mode. + +if RT_USING_LWP + config RT_LWP_MAX_NR + int "The max number of light-weight process" + default 30 + + config LWP_TASK_STACK_SIZE + int "The lwp thread kernel stack size" + default 16384 + + config RT_CH_MSG_MAX_NR + int "The maximum number of channel messages" + default 1024 + + config LWP_CONSOLE_INPUT_BUFFER_SIZE + int "The input buffer size of lwp console device" + default 1024 + + config LWP_TID_MAX_NR + int "The maximum number of lwp thread id" + default 64 + + config LWP_ENABLE_ASID + bool "The switch of ASID feature" + depends on ARCH_ARM_CORTEX_A + default y + + if ARCH_MM_MMU + config RT_LWP_SHM_MAX_NR + int "The maximum number of shared memory" + default 64 + endif + + if ARCH_MM_MPU + config RT_LWP_MPU_MAX_NR + int "The maximum number of mpu region" + default 2 + + config RT_LWP_USING_SHM + bool "Enable shared memory" + default y + endif + + config LWP_UNIX98_PTY + bool "The unix98 PTY support" + default n + + if LWP_UNIX98_PTY + config LWP_PTY_INPUT_BFSZ + int "The unix98 PTY input buffer size" + default 1024 + + config LWP_PTY_PTS_SIZE + int "The unix98 PTY device max num" + default 3 + + config LWP_PTY_USING_DEBUG + bool "The unix98 PTY debug output" + default n + endif +endif diff --git a/components/lwp/SConscript b/components/lwp/SConscript index 98a7cf368a..0b44b08eb3 100644 --- a/components/lwp/SConscript +++ b/components/lwp/SConscript @@ -1,19 +1,44 @@ Import('rtconfig') from building import * +import os cwd = GetCurrentDir() src = [] CPPPATH = [cwd] -support_arch = {"arm": ["cortex-m3", "cortex-m4", "cortex-m7", "arm926", "cortex-a"]} -platform_file = {'armcc': 'rvds.S', 'gcc': 'gcc.S', 'iccarm': 'iar.S'} +support_arch = {"arm": ["cortex-m3", "cortex-m4", "cortex-m7", "arm926", "cortex-a"], + "aarch64":["cortex-a"], + "risc-v": ["rv64"], + "x86": ["i386"]} +platform_file = {'armcc': 'rvds.S', 'gcc': 'gcc.S', 'iar': 'iar.S'} -if rtconfig.PLATFORM in platform_file.keys(): # support platforms - if rtconfig.ARCH in support_arch.keys() and rtconfig.CPU in support_arch[rtconfig.ARCH]: - # arch/arm/cortex-m7/lwp_gcc.S - asm_path = 'arch/' + rtconfig.ARCH + '/' + rtconfig.CPU + '/*_' + platform_file[rtconfig.PLATFORM] - src = Glob('*.c') + Glob(asm_path) +platform = rtconfig.PLATFORM +arch = rtconfig.ARCH +cpu = rtconfig.CPU + +# fix the cpu for risc-v +if arch == 'risc-v': + rv64 = ['virt64', 'c906'] + if cpu in rv64: + cpu = 'rv64' + +if GetDepend('LWP_UNIX98_PTY'): + # print("LWP_UNIX98_PTY") + src += Glob('unix98pty/*.c') + CPPPATH += ['unix98pty/'] + +if platform in platform_file.keys(): # support platforms + if arch in support_arch.keys() and cpu in support_arch[arch]: + asm_path = 'arch/' + arch + '/' + cpu + '/*_' + platform_file[platform] + arch_common = 'arch/' + arch + '/' + 'common/*.c' + if not GetDepend('ARCH_MM_MMU'): + excluded_files = ['ioremap.c', 'lwp_futex.c', 'lwp_mm_area.c', 'lwp_pmutex.c', 'lwp_shm.c', 'lwp_user_mm.c'] + src += [f for f in Glob('*.c') if os.path.basename(str(f)) not in excluded_files] + Glob(asm_path) + Glob(arch_common) + else: + src += Glob('*.c') + Glob(asm_path) + Glob(arch_common) + src += Glob('arch/' + arch + '/' + cpu + '/*.c') CPPPATH = [cwd] + CPPPATH += [cwd + '/arch/' + arch + '/' + cpu] group = DefineGroup('lwP', src, depend = ['RT_USING_LWP'], CPPPATH = CPPPATH) diff --git a/components/lwp/arch/aarch64/common/reloc.c b/components/lwp/arch/aarch64/common/reloc.c new file mode 100644 index 0000000000..4057a5718b --- /dev/null +++ b/components/lwp/arch/aarch64/common/reloc.c @@ -0,0 +1,28 @@ +#include <rtthread.h> +#include <stdint.h> +#include <string.h> +#include <elf.h> +#ifdef RT_USING_USERSPACE +#include <mmu.h> +#include <page.h> +#endif + +#define Elf_Word Elf64_Word +#define Elf_Addr Elf64_Addr +#define Elf_Half Elf64_Half +#define Elf_Ehdr Elf64_Ehdr #define Elf_Phdr Elf64_Phdr +#define Elf_Shdr Elf64_Shdr + +typedef struct +{ + Elf_Word st_name; + Elf_Addr st_value; + Elf_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; +} Elf_sym; + +void arch_elf_reloc(void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf_sym *dynsym) +{ +} diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.c b/components/lwp/arch/aarch64/cortex-a/lwp_arch.c new file mode 100644 index 0000000000..c5f4cebdb9 --- /dev/null +++ b/components/lwp/arch/aarch64/cortex-a/lwp_arch.c @@ -0,0 +1,77 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-18 Jesven first version + */ + +#include <rtthread.h> +#include <rthw.h> + +#ifdef RT_USING_USERSPACE + +#include <mmu.h> +#include <page.h> +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#include <lwp_arch.h> + +extern size_t MMUTable[]; + +int arch_user_space_init(struct rt_lwp *lwp) +{ + size_t *mmu_table; + + mmu_table = (size_t*)rt_pages_alloc(0); + if (!mmu_table) + { + return -1; + } + + lwp->end_heap = USER_HEAP_VADDR; + memset(mmu_table, 0, ARCH_PAGE_SIZE); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, ARCH_PAGE_SIZE); + rt_hw_mmu_map_init(&lwp->mmu_info, (void*)USER_VADDR_START, USER_VADDR_TOP - USER_VADDR_START, mmu_table, PV_OFFSET); + + return 0; +} + +void *arch_kernel_mmu_table_get(void) +{ + return (void*)NULL; +} + +void arch_kuser_init(rt_mmu_info *mmu_info, void *vectors) +{ +} + +void arch_user_space_vtable_free(struct rt_lwp *lwp) +{ + if (lwp && lwp->mmu_info.vtable) + { + rt_pages_free(lwp->mmu_info.vtable, 0); + } +} + +int arch_expand_user_stack(void *addr) +{ + int ret = 0; + size_t stack_addr = (size_t)addr; + + stack_addr &= ~ARCH_PAGE_MASK; + if ((stack_addr >= (size_t)USER_STACK_VSTART) && (stack_addr < (size_t)USER_STACK_VEND)) + { + void *map = lwp_map_user(lwp_self(), (void*)stack_addr, ARCH_PAGE_SIZE, 0); + + if (map || lwp_user_accessable(addr, 1)) + { + ret = 1; + } + } + return ret; +} + +#endif diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_arch.h b/components/lwp/arch/aarch64/cortex-a/lwp_arch.h new file mode 100644 index 0000000000..98dcd989b1 --- /dev/null +++ b/components/lwp/arch/aarch64/cortex-a/lwp_arch.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-18 Jesven first version + */ + +#ifndef LWP_ARCH_H__ +#define LWP_ARCH_H__ + +#include <lwp.h> +#include <lwp_arch_comm.h> + +#ifdef RT_USING_USERSPACE + +#define USER_VADDR_TOP 0x0001000000000000UL +#define USER_HEAP_VEND 0x0000ffffB0000000UL +#define USER_HEAP_VADDR 0x0000ffff80000000UL +#define USER_STACK_VSTART 0x0000ffff70000000UL +#define USER_STACK_VEND USER_HEAP_VADDR +#define LDSO_LOAD_VADDR 0x60000000UL +#define USER_VADDR_START 0x00200000UL +#define USER_LOAD_VADDR USER_VADDR_START + +#ifdef __cplusplus +extern "C" { +#endif + +unsigned long rt_hw_ffz(unsigned long x); + +rt_inline void icache_invalid_all(void) +{ + asm volatile ("ic ialluis\n\tisb sy":::"memory"); +} + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /*LWP_ARCH_H__*/ diff --git a/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S b/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S new file mode 100644 index 0000000000..c873cd5357 --- /dev/null +++ b/components/lwp/arch/aarch64/cortex-a/lwp_gcc.S @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-18 Jesven first version + */ + +#include "rtconfig.h" + +#include "asm-fpu.h" + +/********************* + * SPSR BIT * + *********************/ + +#define SPSR_Mode(v) ((v) << 0) +#define SPSR_A64 (0 << 4) +#define SPSR_RESEVRED_5 (0 << 5) +#define SPSR_FIQ_MASKED(v) ((v) << 6) +#define SPSR_IRQ_MASKED(v) ((v) << 7) +#define SPSR_SERROR_MASKED(v) ((v) << 8) +#define SPSR_D_MASKED(v) ((v) << 9) +#define SPSR_RESEVRED_10_19 (0 << 10) +#define SPSR_IL(v) ((v) << 20) +#define SPSR_SS(v) ((v) << 21) +#define SPSR_RESEVRED_22_27 (0 << 22) +#define SPSR_V(v) ((v) << 28) +#define SPSR_C(v) ((v) << 29) +#define SPSR_Z(v) ((v) << 30) +#define SPSR_N(v) ((v) << 31) + +/********************* + * CONTEXT_OFFSET * + *********************/ + +#define CONTEXT_OFFSET_ELR_EL1 0x0 +#define CONTEXT_OFFSET_SPSR_EL1 0x8 +#define CONTEXT_OFFSET_SP_EL0 0x10 +#define CONTEXT_OFFSET_X30 0x18 +#define CONTEXT_OFFSET_FPCR 0x20 +#define CONTEXT_OFFSET_FPSR 0x28 +#define CONTEXT_OFFSET_X28 0x30 +#define CONTEXT_OFFSET_X29 0x38 +#define CONTEXT_OFFSET_X26 0x40 +#define CONTEXT_OFFSET_X27 0x48 +#define CONTEXT_OFFSET_X24 0x50 +#define CONTEXT_OFFSET_X25 0x58 +#define CONTEXT_OFFSET_X22 0x60 +#define CONTEXT_OFFSET_X23 0x68 +#define CONTEXT_OFFSET_X20 0x70 +#define CONTEXT_OFFSET_X21 0x78 +#define CONTEXT_OFFSET_X18 0x80 +#define CONTEXT_OFFSET_X19 0x88 +#define CONTEXT_OFFSET_X16 0x90 +#define CONTEXT_OFFSET_X17 0x98 +#define CONTEXT_OFFSET_X14 0xa0 +#define CONTEXT_OFFSET_X15 0xa8 +#define CONTEXT_OFFSET_X12 0xb0 +#define CONTEXT_OFFSET_X13 0xb8 +#define CONTEXT_OFFSET_X10 0xc0 +#define CONTEXT_OFFSET_X11 0xc8 +#define CONTEXT_OFFSET_X8 0xd0 +#define CONTEXT_OFFSET_X9 0xd8 +#define CONTEXT_OFFSET_X6 0xe0 +#define CONTEXT_OFFSET_X7 0xe8 +#define CONTEXT_OFFSET_X4 0xf0 +#define CONTEXT_OFFSET_X5 0xf8 +#define CONTEXT_OFFSET_X2 0x100 +#define CONTEXT_OFFSET_X3 0x108 +#define CONTEXT_OFFSET_X0 0x110 +#define CONTEXT_OFFSET_X1 0x118 + +#define CONTEXT_OFFSET_Q15 0x120 +#define CONTEXT_OFFSET_Q14 0x130 +#define CONTEXT_OFFSET_Q13 0x140 +#define CONTEXT_OFFSET_Q12 0x150 +#define CONTEXT_OFFSET_Q11 0x160 +#define CONTEXT_OFFSET_Q10 0x170 +#define CONTEXT_OFFSET_Q9 0x180 +#define CONTEXT_OFFSET_Q8 0x190 +#define CONTEXT_OFFSET_Q7 0x1a0 +#define CONTEXT_OFFSET_Q6 0x1b0 +#define CONTEXT_OFFSET_Q5 0x1c0 +#define CONTEXT_OFFSET_Q4 0x1d0 +#define CONTEXT_OFFSET_Q3 0x1e0 +#define CONTEXT_OFFSET_Q2 0x1f0 +#define CONTEXT_OFFSET_Q1 0x200 +#define CONTEXT_OFFSET_Q0 0x210 + +#define CONTEXT_FPU_SIZE 0x100 +#define CONTEXT_SIZE 0x220 + +/**************************************************/ + +.text + +/* + * void arch_start_umode(args, text, ustack, kstack); + */ +.global arch_start_umode +.type arch_start_umode, % function +arch_start_umode: + mov sp, x3 + mov x4, #(SPSR_Mode(0) | SPSR_A64) + mov x3, x2 ;/* user stack top */ + msr daifset, #3 + dsb sy + mrs x30, sp_el0 + msr spsr_el1, x4 + msr elr_el1, x1 + eret + +/* + * void arch_crt_start_umode(args, text, ustack, kstack); + */ +.global arch_crt_start_umode +.type arch_crt_start_umode, % function +arch_crt_start_umode: + sub x4, x2, #0x10 + adr x2, lwp_thread_return + ldr x5, [x2] + str x5, [x4] + ldr x5, [x2, #4] + str x5, [x4, #4] + ldr x5, [x2, #8] + str x5, [x4, #8] + + mov x5, x4 + dc cvau, x5 + add x5, x5, #8 + dc cvau, x5 + dsb sy + ic ialluis + dsb sy + + msr sp_el0, x4 + + mov sp, x3 + mov x4, #(SPSR_Mode(0) | SPSR_A64) + msr daifset, #3 + dsb sy + mrs x30, sp_el0 + msr spsr_el1, x4 + msr elr_el1, x1 + eret + +/* +void arch_set_thread_context(void *exit_addr, void *new_thread_stack, void *user_stack, void **thread_sp); +*/ +.global arch_set_thread_context +arch_set_thread_context: + sub x1, x1, #CONTEXT_SIZE + str x2, [x1, #CONTEXT_OFFSET_SP_EL0] + sub x1, x1, #CONTEXT_SIZE + str xzr, [x1, #CONTEXT_OFFSET_X0] /* new thread return 0 */ + mov x4, #((3 << 6) | 0x4 | 0x1) /* el1h, disable interrupt */ + str x4, [x1, #CONTEXT_OFFSET_SPSR_EL1] + str x0, [x1, #CONTEXT_OFFSET_ELR_EL1] + str x1, [x3] + ret + +.global arch_get_user_sp +arch_get_user_sp: + mrs x0, sp_el0 + ret + +.global arch_fork_exit +.global arch_clone_exit +arch_fork_exit: +arch_clone_exit: + b arch_syscall_exit + +/* +void lwp_exec_user(void *args, void *kernel_stack, void *user_entry) +*/ +.global lwp_exec_user +lwp_exec_user: + mov sp, x1 + mov x4, #(SPSR_Mode(0) | SPSR_A64) + ldr x3, =0x0000ffff80000000 + msr daifset, #3 + msr spsr_el1, x4 + msr elr_el1, x2 + eret + +/* + * void SVC_Handler(regs); + */ +.global SVC_Handler +.type SVC_Handler, % function +SVC_Handler: + /* x0 is initial sp */ + mov sp, x0 + + msr daifclr, #3 /* enable interrupt */ + + bl rt_thread_self + bl lwp_user_setting_save + + ldp x8, x9, [sp, #(CONTEXT_OFFSET_X8)] + and x0, x8, #0xf000 + cmp x0, #0xe000 + beq arch_signal_quit + + cmp x0, #0xf000 + beq ret_from_user + + uxtb x0, w8 + bl lwp_get_sys_api + cmp x0, xzr + mov x30, x0 + beq arch_syscall_exit + ldp x0, x1, [sp, #(CONTEXT_OFFSET_X0)] + ldp x2, x3, [sp, #(CONTEXT_OFFSET_X2)] + ldp x4, x5, [sp, #(CONTEXT_OFFSET_X4)] + ldp x6, x7, [sp, #(CONTEXT_OFFSET_X6)] + blr x30 + /* jump explictly, make this code position independant */ + b arch_syscall_exit + +.global arch_syscall_exit +arch_syscall_exit: + msr daifset, #3 + + ldp x2, x3, [sp], #0x10 /* SPSR and ELR. */ + msr spsr_el1, x3 + msr elr_el1, x2 + + ldp x29, x30, [sp], #0x10 + msr sp_el0, x29 + ldp x28, x29, [sp], #0x10 + msr fpcr, x28 + msr fpsr, x29 + ldp x28, x29, [sp], #0x10 + ldp x26, x27, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + add sp, sp, #0x40 + RESTORE_FPU sp + +.global arch_ret_to_user +arch_ret_to_user: + SAVE_FPU sp + stp x0, x1, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x20, x21, [sp, #-0x10]! + stp x22, x23, [sp, #-0x10]! + stp x24, x25, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + stp x28, x29, [sp, #-0x10]! + + mrs x0, fpcr + mrs x1, fpsr + stp x0, x1, [sp, #-0x10]! + stp x29, x30, [sp, #-0x10]! + + bl lwp_check_debug + bl lwp_check_exit_request + cbz w0, 1f + mov x0, xzr + b sys_exit +1: + ldr x0, =rt_dbg_ops + ldr x0, [x0] + cbz x0, 3f + bl dbg_thread_in_debug + mov x1, #(1 << 21) + mrs x2, spsr_el1 + cbz w0, 2f + orr x2, x2, x1 + msr spsr_el1, x2 + b 3f +2: + bic x2, x2, x1 + msr spsr_el1, x2 +3: + bl lwp_signal_check + cmp x0, xzr + + ldp x29, x30, [sp], #0x10 + ldp x0, x1, [sp], #0x10 + msr fpcr, x0 + msr fpsr, x1 + + ldp x28, x29, [sp], #0x10 + ldp x26, x27, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x0, x1, [sp], #0x10 + RESTORE_FPU sp + + bne user_do_signal + + stp x0, x1, [sp, #-0x10]! + ldr x0, =rt_dbg_ops + ldr x0, [x0] + cmp x0, xzr + ldp x0, x1, [sp], #0x10 + beq 1f + SAVE_FPU sp + stp x0, x1, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x20, x21, [sp, #-0x10]! + stp x22, x23, [sp, #-0x10]! + stp x24, x25, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + stp x28, x29, [sp, #-0x10]! + mrs x0, fpcr + mrs x1, fpsr + stp x0, x1, [sp, #-0x10]! + stp x29, x30, [sp, #-0x10]! + mrs x0, elr_el1 + bl dbg_attach_req + ldp x29, x30, [sp], #0x10 + ldp x0, x1, [sp], #0x10 + msr fpcr, x0 + msr fpsr, x1 + ldp x28, x29, [sp], #0x10 + ldp x26, x27, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x0, x1, [sp], #0x10 + RESTORE_FPU sp +1: + eret + +/* +struct rt_hw_exp_stack +{ + unsigned long pc; 0 + unsigned long cpsr; + unsigned long sp_el0; 0x10 + unsigned long x30; + unsigned long fpcr; 0x20 + unsigned long fpsr; + unsigned long x28; 0x30 + unsigned long x29; + unsigned long x26; 0x40 + unsigned long x27; + unsigned long x24; 0x50 + unsigned long x25; + unsigned long x22; 0x60 + unsigned long x23; + unsigned long x20; 0x70 + unsigned long x21; + unsigned long x18; 0x80 + unsigned long x19; + unsigned long x16; 0x90 + unsigned long x17; + unsigned long x14; 0xa0 + unsigned long x15; + unsigned long x12; 0xb0 + unsigned long x13; + unsigned long x10; 0xc0 + unsigned long x11; + unsigned long x8; 0xd0 + unsigned long x9; + unsigned long x6; 0xe0 + unsigned long x7; + unsigned long x4; 0xf0 + unsigned long x5; + unsigned long x2; 0x100 + unsigned long x3; + unsigned long x0; 0x110 + unsigned long x1; + + unsigned long long fpu[16]; 0x120 + 0x220 = 0x120 + 0x10 * 0x10 +}; +*/ +.global lwp_check_debug +lwp_check_debug: + ldr x0, =rt_dbg_ops + ldr x0, [x0] + cbnz x0, 1f + ret +1: + stp x29, x30, [sp, #-0x10]! + bl dbg_check_suspend + cbz w0, lwp_check_debug_quit + + mrs x2, sp_el0 + sub x2, x2, #0x10 + mov x3, x2 + msr sp_el0, x2 + ldr x0, =lwp_debugreturn + ldr w1, [x0] + str w1, [x2] + ldr w1, [x0, #4] + str w1, [x2, #4] + + dc cvau, x2 + add x2, x2, #4 + dc cvau, x2 + + dsb sy + isb sy + + ic ialluis + isb sy + + mrs x0, elr_el1 + mrs x1, spsr_el1 + stp x0, x1, [sp, #-0x10]! + msr elr_el1, x3 /* lwp_debugreturn */ + mov x1, #(SPSR_Mode(0) | SPSR_A64) + orr x1, x1, #(1 << 21) + msr spsr_el1, x1 + eret +ret_from_user: + /* sp_el0 += 16 for drop ins lwp_debugreturn */ + mrs x0, sp_el0 + add x0, x0, #0x10 + msr sp_el0, x0 + /* now is el1, sp is pos(empty) - sizeof(context) */ + mov x0, sp + add x0, x0, #0x220 + mov sp, x0 + ldp x0, x1, [sp], #0x10 /* x1 is origin spsr_el1 */ + msr elr_el1, x0 /* x0 is origin elr_el1 */ + msr spsr_el1, x1 +lwp_check_debug_quit: + ldp x29, x30, [sp], #0x10 + ret + +arch_signal_quit: + msr daifset, #3 +/* + drop stack data +*/ + add sp, sp, #CONTEXT_SIZE + bl lwp_signal_restore + /* x0 is user_ctx : ori sp, pc, cpsr */ + ldr x1, [x0] + ldr x2, [x0, #8] + ldr x3, [x0, #16] + msr spsr_el1, x3 + msr elr_el1, x2 + add x1, x1, #16 + msr sp_el0, x1 + + msr spsel, #0 + + ldp x29, x30, [sp], #0x10 + ldp x28, x29, [sp], #0x10 + msr fpcr, x28 + msr fpsr, x29 + ldp x28, x29, [sp], #0x10 + ldp x26, x27, [sp], #0x10 + ldp x24, x25, [sp], #0x10 + ldp x22, x23, [sp], #0x10 + ldp x20, x21, [sp], #0x10 + ldp x18, x19, [sp], #0x10 + ldp x16, x17, [sp], #0x10 + ldp x14, x15, [sp], #0x10 + ldp x12, x13, [sp], #0x10 + ldp x10, x11, [sp], #0x10 + ldp x8, x9, [sp], #0x10 + ldp x6, x7, [sp], #0x10 + ldp x4, x5, [sp], #0x10 + ldp x2, x3, [sp], #0x10 + ldp x0, x1, [sp], #0x10 + RESTORE_FPU sp + + msr spsel, #1 + + b arch_ret_to_user + +user_do_signal: + msr spsel, #0 + SAVE_FPU sp + stp x0, x1, [sp, #-0x10]! + stp x2, x3, [sp, #-0x10]! + stp x4, x5, [sp, #-0x10]! + stp x6, x7, [sp, #-0x10]! + stp x8, x9, [sp, #-0x10]! + stp x10, x11, [sp, #-0x10]! + stp x12, x13, [sp, #-0x10]! + stp x14, x15, [sp, #-0x10]! + stp x16, x17, [sp, #-0x10]! + stp x18, x19, [sp, #-0x10]! + stp x20, x21, [sp, #-0x10]! + stp x22, x23, [sp, #-0x10]! + stp x24, x25, [sp, #-0x10]! + stp x26, x27, [sp, #-0x10]! + stp x28, x29, [sp, #-0x10]! + mrs x28, fpcr + mrs x29, fpsr + stp x28, x29, [sp, #-0x10]! + stp x29, x30, [sp, #-0x10]! + + sub sp, sp, #0x10 + adr x0, lwp_sigreturn + ldr w1, [x0] + str w1, [sp] + ldr w1, [x0, #4] + str w1, [sp, #4] + + mov x20, sp /* lwp_sigreturn */ + mov x0, sp + + dc cvau, x0 + dsb sy + ic ialluis + dsb sy + + msr spsel, #1 + + mrs x1, elr_el1 + mrs x2, spsr_el1 + bl lwp_signal_backup + /* x0 is signal */ + mov x19, x0 + bl lwp_sighandler_get + adds x1, x0, xzr + mov x0, x19 + bne 1f + mov x1, x20 +1: + msr elr_el1, x1 + mov x30, x20 + eret + +lwp_debugreturn: + mov x8, 0xf000 + svc #0 + +lwp_sigreturn: + mov x8, #0xe000 + svc #0 + +lwp_thread_return: + mov x0, xzr + mov x8, #0x01 + svc #0 + +.globl arch_get_tidr +arch_get_tidr: + mrs x0, tpidr_el0 + ret + +.global arch_set_thread_area +arch_set_thread_area: +.globl arch_set_tidr +arch_set_tidr: + msr tpidr_el0, x0 + ret diff --git a/components/lwp/arch/arm/arm926/lwp_gcc.S b/components/lwp/arch/arm/arm926/lwp_gcc.S deleted file mode 100644 index 2a8ce492eb..0000000000 --- a/components/lwp/arch/arm/arm926/lwp_gcc.S +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-12-10 Jesven first version - */ - -#define Mode_USR 0x10 -#define Mode_FIQ 0x11 -#define Mode_IRQ 0x12 -#define Mode_SVC 0x13 -#define Mode_MON 0x16 -#define Mode_ABT 0x17 -#define Mode_UDF 0x1B -#define Mode_SYS 0x1F - -#define A_Bit 0x100 -#define I_Bit 0x80 @; when I bit is set, IRQ is disabled -#define F_Bit 0x40 @; when F bit is set, FIQ is disabled -#define T_Bit 0x20 - -.cpu arm9 -.syntax unified -.text - -/* - * void lwp_user_entry(args, text, data); - */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: - mrs r9, cpsr - mov r8, r9 - bic r9, #0x1f - orr r9, #Mode_USR - - orr r8, #I_Bit - msr cpsr_c, r8 - - msr spsr, r9 - - /* set data address. */ - mov r9, r2 - movs pc, r1 - -/* - * void SVC_Handler(void); - */ -.global SVC_Handler -.type SVC_Handler, % function -SVC_Handler: - push {lr} - mrs lr, spsr - push {r4, r5, lr} - - mrs r4, cpsr - bic r4, #I_Bit - msr cpsr_c, r4 - - push {r0 - r3, r12} - and r0, r7, #0xff - bl lwp_get_sys_api - cmp r0, #0 /* r0 = api */ - mov r4, r0 - pop {r0 - r3, r12} - beq svc_exit - ldr lr, = svc_exit - bx r4 - -svc_exit: - mrs r4, cpsr - orr r4, #I_Bit - msr cpsr_c, r4 - - pop {r4, r5, lr} - msr spsr_cxsf, lr - pop {lr} - movs pc, lr diff --git a/components/lwp/arch/arm/common/reloc.c b/components/lwp/arch/arm/common/reloc.c new file mode 100644 index 0000000000..a232df885d --- /dev/null +++ b/components/lwp/arch/arm/common/reloc.c @@ -0,0 +1,120 @@ +#include <rtthread.h> +#include <stdint.h> +#include <string.h> +#include <lwp_elf.h> +#ifdef RT_USING_USERSPACE +#include <mmu.h> +#include <page.h> +#endif + +typedef struct +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_sym; + +#ifdef RT_USING_USERSPACE +void arch_elf_reloc(rt_mmu_info *m_info, void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf32_sym *dynsym) +{ + size_t rel_off; + void* addr; + + if (rel_dyn_size && !dynsym) + { + return; + } + for (rel_off = 0; rel_off < rel_dyn_size; rel_off += 8) + { + uint32_t v1, v2; + + /* + memcpy(&v1, rel_dyn_start + rel_off, 4); + memcpy(&v2, rel_dyn_start + rel_off + 4, 4); + */ + + addr = rt_hw_mmu_v2p(m_info, (void*)((char*)rel_dyn_start + rel_off)); + addr = (void*)((char*)addr - PV_OFFSET); + memcpy(&v1, addr, 4); + addr = rt_hw_mmu_v2p(m_info, (void*)((char*)rel_dyn_start + rel_off + 4)); + addr = (void*)((char*)addr - PV_OFFSET); + memcpy(&v2, addr, 4); + + addr = rt_hw_mmu_v2p(m_info, (void*)((char*)text_start + v1)); + addr = (void*)((char*)addr - PV_OFFSET); + if ((v2 & 0xff) == R_ARM_RELATIVE) + { + // *(uint32_t*)(text_start + v1) += (uint32_t)text_start; + *(uint32_t*)addr += (uint32_t)text_start; + } + else if ((v2 & 0xff) == R_ARM_ABS32) + { + uint32_t t; + t = (v2 >> 8); + if (t) /* 0 is UDF */ + { + // *(uint32_t*)(text_start + v1) = (uint32_t)(text_start + dynsym[t].st_value); + *(uint32_t*)addr = (uint32_t)((char*)text_start + dynsym[t].st_value); + } + } + } + /* modify got */ + if (got_size) + { + uint32_t *got_item = (uint32_t*)got_start; + + for (rel_off = 0; rel_off < got_size; rel_off += 4, got_item++) + { + //*got_item += (uint32_t)text_start; + addr = rt_hw_mmu_v2p(m_info, got_item); + addr = (void*)((char*)addr - PV_OFFSET); + *(uint32_t *)addr += (uint32_t)text_start; + } + } +} +#else + +void arch_elf_reloc(void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf32_sym *dynsym) +{ + size_t rel_off; + + if (rel_dyn_size && !dynsym) + { + return; + } + for (rel_off = 0; rel_off < rel_dyn_size; rel_off += 8) + { + uint32_t v1, v2; + + memcpy(&v1, (void*)((char*)rel_dyn_start + rel_off), 4); + memcpy(&v2, (void*)((char*)rel_dyn_start + rel_off + 4), 4); + + if ((v2 & 0xff) == R_ARM_RELATIVE) + { + *(uint32_t*)((char*)text_start + v1) += (uint32_t)text_start; + } + else if ((v2 & 0xff) == R_ARM_ABS32) + { + uint32_t t; + t = (v2 >> 8); + if (t) /* 0 is UDF */ + { + *(uint32_t*)((char*)text_start + v1) = (uint32_t)((char*)text_start + dynsym[t].st_value); + } + } + } + /* modify got */ + if (got_size) + { + uint32_t *got_item = (uint32_t*)got_start; + + for (rel_off = 0; rel_off < got_size; rel_off += 4, got_item++) + { + *got_item += (uint32_t)text_start; + } + } +} +#endif diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.c b/components/lwp/arch/arm/cortex-a/lwp_arch.c new file mode 100644 index 0000000000..814c1513ed --- /dev/null +++ b/components/lwp/arch/arm/cortex-a/lwp_arch.c @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + */ + +#include <rtthread.h> +#include <rthw.h> + +#ifdef RT_USING_USERSPACE + +#include <mmu.h> +#include <page.h> +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#include <lwp_arch.h> + +extern size_t MMUTable[]; + +int arch_user_space_init(struct rt_lwp *lwp) +{ + size_t *mmu_table; + + mmu_table = (size_t*)rt_pages_alloc(2); + if (!mmu_table) + { + return -1; + } + + lwp->end_heap = USER_HEAP_VADDR; + rt_memcpy(mmu_table + (KERNEL_VADDR_START >> ARCH_SECTION_SHIFT), MMUTable + (KERNEL_VADDR_START >> ARCH_SECTION_SHIFT), ARCH_PAGE_SIZE); + rt_memset(mmu_table, 0, 3 * ARCH_PAGE_SIZE); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, 4 * ARCH_PAGE_SIZE); + rt_hw_mmu_map_init(&lwp->mmu_info, (void*)USER_VADDR_START, USER_VADDR_TOP - USER_VADDR_START, mmu_table, PV_OFFSET); + + return 0; +} + +void *arch_kernel_mmu_table_get(void) +{ + return (void*)((char*)MMUTable + PV_OFFSET); +} + +void arch_kuser_init(rt_mmu_info *mmu_info, void *vectors) +{ + extern char __kuser_helper_start[], __kuser_helper_end[]; + int kuser_sz = __kuser_helper_end - __kuser_helper_start; + + rt_hw_mmu_map_auto(mmu_info, vectors, 0x1000, MMU_MAP_U_RO); + + rt_memcpy((void*)((char*)vectors + 0x1000 - kuser_sz), __kuser_helper_start, kuser_sz); + /* + * vectors + 0xfe0 = __kuser_get_tls + * vectors + 0xfe8 = hardware TLS instruction at 0xffff0fe8 + */ + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void*)((char*)vectors + 0x1000 - kuser_sz), kuser_sz); + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, (void*)((char*)vectors + 0x1000 - kuser_sz), kuser_sz); +} + +void arch_user_space_vtable_free(struct rt_lwp *lwp) +{ + if (lwp && lwp->mmu_info.vtable) + { + rt_pages_free(lwp->mmu_info.vtable, 2); + } +} + +int arch_expand_user_stack(void *addr) +{ + int ret = 0; + size_t stack_addr = (size_t)addr; + + stack_addr &= ~ARCH_PAGE_MASK; + if ((stack_addr >= (size_t)USER_STACK_VSTART) && (stack_addr < (size_t)USER_STACK_VEND)) + { + void *map = lwp_map_user(lwp_self(), (void*)stack_addr, ARCH_PAGE_SIZE, 0); + + if (map || lwp_user_accessable(addr, 1)) + { + ret = 1; + } + } + return ret; +} + +#ifdef LWP_ENABLE_ASID +#define MAX_ASID_BITS 8 +#define MAX_ASID (1 << MAX_ASID_BITS) +static uint64_t global_generation = 1; +static char asid_valid_bitmap[MAX_ASID]; +unsigned int arch_get_asid(struct rt_lwp *lwp) +{ + if (lwp == RT_NULL) + { + // kernel + return 0; + } + + if (lwp->generation == global_generation) + { + return lwp->asid; + } + + if (lwp->asid && !asid_valid_bitmap[lwp->asid]) + { + asid_valid_bitmap[lwp->asid] = 1; + return lwp->asid; + } + + for (unsigned i = 1; i < MAX_ASID; i++) + { + if (asid_valid_bitmap[i] == 0) + { + asid_valid_bitmap[i] = 1; + lwp->generation = global_generation; + lwp->asid = i; + return lwp->asid; + } + } + + global_generation++; + memset(asid_valid_bitmap, 0, MAX_ASID * sizeof(char)); + + asid_valid_bitmap[1] = 1; + lwp->generation = global_generation; + lwp->asid = 1; + + asm volatile ("mcr p15, 0, r0, c8, c7, 0\ndsb\nisb" ::: "memory"); + + return lwp->asid; +} +#endif + +#endif diff --git a/components/lwp/arch/arm/cortex-a/lwp_arch.h b/components/lwp/arch/arm/cortex-a/lwp_arch.h new file mode 100644 index 0000000000..d627632493 --- /dev/null +++ b/components/lwp/arch/arm/cortex-a/lwp_arch.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef LWP_ARCH_H__ +#define LWP_ARCH_H__ + +#include <lwp.h> +#include <lwp_arch_comm.h> + +#ifdef RT_USING_USERSPACE + +#define USER_VADDR_TOP 0xC0000000UL +#define USER_HEAP_VEND 0xB0000000UL +#define USER_HEAP_VADDR 0x80000000UL +#define USER_STACK_VSTART 0x70000000UL +#define USER_STACK_VEND USER_HEAP_VADDR +#define LDSO_LOAD_VADDR 0x60000000UL +#define USER_VADDR_START 0x00100000UL +#define USER_LOAD_VADDR USER_VADDR_START + +#ifdef __cplusplus +extern "C" { +#endif + +rt_inline unsigned long rt_hw_ffz(unsigned long x) +{ + return __builtin_ffsl(~x) - 1; +} + +rt_inline void icache_invalid_all(void) +{ + asm volatile ("mcr p15, 0, r0, c7, c5, 0\ndsb\nisb":::"memory");//iciallu +} + +unsigned int arch_get_asid(struct rt_lwp *lwp); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /*LWP_ARCH_H__*/ diff --git a/components/lwp/arch/arm/cortex-a/lwp_gcc.S b/components/lwp/arch/arm/cortex-a/lwp_gcc.S index 395974a2b7..30839e5970 100644 --- a/components/lwp/arch/arm/cortex-a/lwp_gcc.S +++ b/components/lwp/arch/arm/cortex-a/lwp_gcc.S @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -8,6 +8,8 @@ * 2018-12-10 Jesven first version */ +#include "rtconfig.h" + #define Mode_USR 0x10 #define Mode_FIQ 0x11 #define Mode_IRQ 0x12 @@ -27,22 +29,126 @@ .text /* - * void lwp_user_entry(args, text, data); + * void arch_start_umode(args, text, ustack, kstack); */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: +.global arch_start_umode +.type arch_start_umode, % function +arch_start_umode: mrs r9, cpsr bic r9, #0x1f orr r9, #Mode_USR cpsid i msr spsr, r9 + mov sp, r3 + mov r3, r2 ;/* user stack top */ /* set data address. */ - mov r9, r2 movs pc, r1 /* + * void arch_crt_start_umode(args, text, ustack, kstack); + */ +.global arch_crt_start_umode +.type arch_crt_start_umode, % function +arch_crt_start_umode: + cps #Mode_SYS + sub sp, r2, #16 + ldr r2, =lwp_thread_return + ldr r4, [r2] + str r4, [sp] + ldr r4, [r2, #4] + str r4, [sp, #4] + ldr r4, [r2, #8] + str r4, [sp, #8] + + mov r4, sp + mcr p15, 0, r4, c7, c11, 1 ;//dc cmvau + add r4, #4 + mcr p15, 0, r4, c7, c11, 1 ;//dc cmvau + add r4, #4 + mcr p15, 0, r4, c7, c11, 1 ;//dc cmvau + dsb + isb + mcr p15, 0, r4, c7, c5, 0 ;//iciallu + dsb + isb + + mov lr, sp + cps #Mode_SVC + + mrs r9, cpsr + bic r9, #0x1f + orr r9, #Mode_USR + cpsid i + msr spsr, r9 + mov sp, r3 + + /* set data address. */ + movs pc, r1 + +/* +void arch_set_thread_context(void *exit_addr, void *new_thread_stack, void *user_stack, void **thread_sp); +*/ +.global arch_set_thread_context +arch_set_thread_context: + sub r1, #(10 * 4 + 4 * 4) /* {r4 - r12, lr} , {r4, r5, spsr, u_pc} */ + stmfd r1!, {r0} + mov r12, #0 + stmfd r1!, {r12} + stmfd r1!, {r1 - r12} + stmfd r1!, {r12} /* new thread return value */ + mrs r12, cpsr + orr r12, #(1 << 7) /* disable irq */ + stmfd r1!, {r12} /* spsr */ + mov r12, #0 + stmfd r1!, {r12} /* now user lr is 0 */ + stmfd r1!, {r2} /* user sp */ +#ifdef RT_USING_FPU + stmfd r1!, {r12} /* not use fpu */ +#endif + str r1, [r3] + mov pc, lr + +.global arch_get_user_sp +arch_get_user_sp: + cps #Mode_SYS + mov r0, sp + cps #Mode_SVC + mov pc, lr + +.global sys_fork +.global sys_vfork +.global arch_fork_exit +sys_fork: +sys_vfork: + push {r4 - r12, lr} + bl _sys_fork +arch_fork_exit: + pop {r4 - r12, lr} + b arch_syscall_exit + +.global sys_clone +.global arch_clone_exit +sys_clone: + push {r4 - r12, lr} + bl _sys_clone +arch_clone_exit: + pop {r4 - r12, lr} + b arch_syscall_exit +/* +void lwp_exec_user(void *args, void *kernel_stack, void *user_entry) +*/ +.global lwp_exec_user +lwp_exec_user: + cpsid i + mov sp, r1 + mov lr, r2 + mov r2, #Mode_USR + msr spsr_cxsf, r2 + ldr r3, =0x80000000 + b arch_ret_to_user + +/* * void SVC_Handler(void); */ .global vector_swi @@ -51,20 +157,277 @@ vector_swi: push {lr} mrs lr, spsr push {r4, r5, lr} + cpsie i push {r0 - r3, r12} + + bl rt_thread_self + bl lwp_user_setting_save + + and r0, r7, #0xf000 + cmp r0, #0xe000 + beq arch_signal_quit + + cmp r0, #0xf000 + beq ret_from_user and r0, r7, #0xff bl lwp_get_sys_api cmp r0, #0 /* r0 = api */ mov lr, r0 + pop {r0 - r3, r12} - beq svc_exit + beq arch_syscall_exit blx lr -svc_exit: +.global arch_syscall_exit +arch_syscall_exit: cpsid i pop {r4, r5, lr} msr spsr_cxsf, lr pop {lr} + +.global arch_ret_to_user +arch_ret_to_user: + push {r0-r3, r12, lr} + bl lwp_check_debug + bl lwp_check_exit_request + cmp r0, #0 + beq 1f + mov r0, #0 + b sys_exit +1: + bl lwp_signal_check + cmp r0, #0 + pop {r0-r3, r12, lr} + bne user_do_signal + + push {r0} + ldr r0, =rt_dbg_ops + ldr r0, [r0] + cmp r0, #0 + pop {r0} + beq 2f + push {r0-r3, r12, lr} + mov r0, lr + bl dbg_attach_req + pop {r0-r3, r12, lr} +2: movs pc, lr + +#ifdef RT_USING_LWP +.global lwp_check_debug +lwp_check_debug: + ldr r0, =rt_dbg_ops + ldr r0, [r0] + cmp r0, #0 + bne 1f + bx lr +1: + push {lr} + bl dbg_check_suspend + cmp r0, #0 + beq lwp_check_debug_quit + + cps #Mode_SYS + sub sp, #8 + ldr r0, =lwp_debugreturn + ldr r1, [r0] + str r1, [sp] + ldr r1, [r0, #4] + str r1, [sp, #4] + + mov r1, sp + mcr p15, 0, r1, c7, c11, 1 ;//dc cmvau + add r1, #4 + mcr p15, 0, r1, c7, c11, 1 ;//dc cmvau + dsb + isb + mcr p15, 0, r0, c7, c5, 0 ;//iciallu + dsb + isb + + mov r0, sp /* lwp_debugreturn */ + cps #Mode_SVC + + mrs r1, spsr + push {r1} + mov r1, #Mode_USR + msr spsr_cxsf, r1 + movs pc, r0 +ret_from_user: + cps #Mode_SYS + add sp, #8 + cps #Mode_SVC + /* + pop {r0 - r3, r12} + pop {r4 - r6, lr} + */ + add sp, #(4*9) + pop {r4} + msr spsr_cxsf, r4 +lwp_check_debug_quit: + pop {pc} + +arch_signal_quit: + cpsid i + pop {r0 - r3, r12} + pop {r4, r5, lr} + pop {lr} + bl lwp_signal_restore + /* r0 is user_ctx : ori sp, pc, cpsr*/ + ldr r1, [r0] + ldr r2, [r0, #4] + ldr r3, [r0, #8] + msr spsr_cxsf, r3 + mov lr, r2 + cps #Mode_SYS + mov sp, r1 + pop {r0-r12, lr} + cps #Mode_SVC + b arch_ret_to_user + +user_do_signal: + mov r0, r0 + cps #Mode_SYS + push {r0-r12, lr} + + sub sp, #8 + ldr r0, =lwp_sigreturn + ldr r1, [r0] + str r1, [sp] + ldr r1, [r0, #4] + str r1, [sp, #4] + + mov r1, sp + mcr p15, 0, r1, c7, c11, 1 ;//dc cmvau + add r1, #4 + mcr p15, 0, r1, c7, c11, 1 ;//dc cmvau + dsb + isb + mcr p15, 0, r0, c7, c5, 0 ;//iciallu + dsb + isb + + mov r5, sp ;//if func is 0 + mov lr, sp + + add r0, sp, #8 /* lwp_sigreturn */ + cps #Mode_SVC + mov r1, lr + mrs r2, spsr + bl lwp_signal_backup + /* r0 is signal */ + mov r4, r0 + bl lwp_sighandler_get + mov lr, r0 + cmp lr, #0 + moveq lr, r5 + mov r0, r4 + movs pc, lr + +lwp_debugreturn: + mov r7, #0xf000 + svc #0 + +lwp_sigreturn: + mov r7, #0xe000 + svc #0 + +lwp_thread_return: + mov r0, #0 + mov r7, #0x01 + svc #0 +#endif + +.global check_vfp +check_vfp: +#ifdef RT_USING_FPU + vmrs r0, fpexc + ubfx r0, r0, #30, #1 +#else + mov r0, #0 +#endif + mov pc, lr + +.global get_vfp +get_vfp: +#ifdef RT_USING_FPU + vstmia r0!, {d0-d15} + vstmia r0!, {d16-d31} + vmrs r1, fpscr + str r1, [r0] +#endif + mov pc, lr + +.globl arch_get_tidr +arch_get_tidr: + mrc p15, 0, r0, c13, c0, 3 + bx lr + +.global arch_set_thread_area +arch_set_thread_area: +.globl arch_set_tidr +arch_set_tidr: + mcr p15, 0, r0, c13, c0, 3 + bx lr + +/* kuser suppurt */ + .macro kuser_pad, sym, size + .if (. - \sym) & 3 + .rept 4 - (. - \sym) & 3 + .byte 0 + .endr + .endif + .rept (\size - (. - \sym)) / 4 + .word 0xe7fddef1 + .endr + .endm + +.align 5 +.globl __kuser_helper_start +__kuser_helper_start: +__kuser_cmpxchg64: @ 0xffff0f60 + stmfd sp!, {r4, r5, r6, lr} + ldmia r0, {r4, r5} @ load old val + ldmia r1, {r6, lr} @ load new val +1: ldmia r2, {r0, r1} @ load current val + eors r3, r0, r4 @ compare with oldval (1) + eorseq r3, r1, r5 @ compare with oldval (2) +2: stmiaeq r2, {r6, lr} @ store newval if eq + rsbs r0, r3, #0 @ set return val and C flag + ldmfd sp!, {r4, r5, r6, pc} + + kuser_pad __kuser_cmpxchg64, 64 + +__kuser_memory_barrier: @ 0xffff0fa0 + dmb + mov pc, lr + + kuser_pad __kuser_memory_barrier, 32 + +__kuser_cmpxchg: @ 0xffff0fc0 +1: ldr r3, [r2] @ load current val + subs r3, r3, r0 @ compare with oldval +2: streq r1, [r2] @ store newval if eq + rsbs r0, r3, #0 @ set return val and C flag + mov pc, lr + +kuser_pad __kuser_cmpxchg, 32 + +__kuser_get_tls: @ 0xffff0fe0 + mrc p15, 0, r0, c13, c0, 3 @ 0xffff0fe8 hardware TLS code + mov pc, lr + ldr r0, [pc, #(16 - 8)] @ read TLS, set in kuser_get_tls_init + + kuser_pad __kuser_get_tls, 16 + + .rep 3 + .word 0 @ 0xffff0ff0 software TLS value, then + .endr @ pad up to __kuser_helper_version + +__kuser_helper_version: @ 0xffff0ffc + .word ((__kuser_helper_end - __kuser_helper_start) >> 5) + + .globl __kuser_helper_end +__kuser_helper_end: diff --git a/components/lwp/arch/arm/cortex-a9/lwp_gcc.S b/components/lwp/arch/arm/cortex-a9/lwp_gcc.S deleted file mode 100644 index 93e88f7c97..0000000000 --- a/components/lwp/arch/arm/cortex-a9/lwp_gcc.S +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-12-10 Jesven first version - */ - -#define Mode_USR 0x10 -#define Mode_FIQ 0x11 -#define Mode_IRQ 0x12 -#define Mode_SVC 0x13 -#define Mode_MON 0x16 -#define Mode_ABT 0x17 -#define Mode_UDF 0x1B -#define Mode_SYS 0x1F - -#define A_Bit 0x100 -#define I_Bit 0x80 @; when I bit is set, IRQ is disabled -#define F_Bit 0x40 @; when F bit is set, FIQ is disabled -#define T_Bit 0x20 - -.cpu cortex-a9 -.syntax unified -.text - -/* - * void lwp_user_entry(args, text, data); - */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: - mrs r9, cpsr - bic r9, #0x1f - orr r9, #Mode_USR - cpsid i - msr spsr, r9 - - /* set data address. */ - mov r9, r2 - movs pc, r1 - -/* - * void vector_swi(void); - */ -.global vector_swi -.type vector_swi, % function -vector_swi: - push {lr} - mrs lr, spsr - push {r4, r5, lr} - cpsie i - - push {r0 - r3, r12} - and r0, r7, #0xff - bl lwp_get_sys_api - cmp r0, #0 /* r0 = api */ - mov lr, r0 - pop {r0 - r3, r12} - beq svc_exit - blx lr - -svc_exit: - cpsid i - pop {r4, r5, lr} - msr spsr_cxsf, lr - pop {lr} - movs pc, lr diff --git a/components/lwp/arch/arm/cortex-m3/lwp_gcc.S b/components/lwp/arch/arm/cortex-m3/lwp_gcc.S deleted file mode 100644 index 846b06bb26..0000000000 --- a/components/lwp/arch/arm/cortex-m3/lwp_gcc.S +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-10-30 heyuanjie first version - */ - -.cpu cortex-m3 -.syntax unified -.thumb -.text - -/* - * void* lwp_get_sys_api(rt_uint32_t number); - */ -.global lwp_get_sys_api -.global lwp_get_kernel_sp -.global lwp_set_kernel_sp - - -/* - * void lwp_user_entry(args, text, data); - */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: - PUSH {R0-R3} @; push text&data addr. - - MOV R0, SP @; v1 = SP - BL lwp_set_kernel_sp @; lwp_set_kernel_sp(v1) - - @; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 @; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} @; pop app address to R1. - @; set data address. - MOV R9, R2 - - @; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -/* - * void SVC_Handler(void); - */ -.global SVC_Handler -.type SVC_Handler, % function -SVC_Handler: - PUSH {LR} - - @; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} @; push app SP. - - @; get SVC number. - mov R0, R7 - - @; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} @; push api - - @; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} @; pop api to R2. - POP {R1} @; pop app SP to R1. - - stmfd r0!, {r1} @; save app SP to kernel SP - - @;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - @; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} @; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} @; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] @; update LR - STR R2, [R0, #24] @; update api to PC - MSR PSP, R0 @; update SP, API is executed with kernel SP - - @; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} @; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR -/* -* void svc_exit(void); -*/ -.global svc_exit -.type svc_exit, % function -svc_exit: - @; get user SP. - PUSH {R0} @; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] @; load pc - add r3, #32 @; exception_stack_frame size - MSR PSP, R3 @; restore app stack pointer - @; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - @; return to lwp. - ORR R1, R1, #0x01 @; only Thumb-mode. - BX R1 @; return to user app. diff --git a/components/lwp/arch/arm/cortex-m3/lwp_iar.S b/components/lwp/arch/arm/cortex-m3/lwp_iar.S deleted file mode 100644 index a218569375..0000000000 --- a/components/lwp/arch/arm/cortex-m3/lwp_iar.S +++ /dev/null @@ -1,123 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - SECTION .text:CODE(2) - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ - EXPORT lwp_user_entry -lwp_user_entry: - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -;/* -; * void SVC_Handler(void); -; */ - EXPORT SVC_Handler -SVC_Handler: - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - -;/* -; * void svc_exit(void); -; */ - EXPORT svc_exit -svc_exit: - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - END diff --git a/components/lwp/arch/arm/cortex-m3/lwp_rvds.S b/components/lwp/arch/arm/cortex-m3/lwp_rvds.S deleted file mode 100644 index 246751a847..0000000000 --- a/components/lwp/arch/arm/cortex-m3/lwp_rvds.S +++ /dev/null @@ -1,135 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - AREA |.text|, CODE, READONLY, ALIGN=2 - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ -lwp_user_entry PROC - EXPORT lwp_user_entry - - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - - ; never reach here! - ENDP - -;/* -; * void SVC_Handler(void); -; */ -SVC_Handler PROC - EXPORT SVC_Handler - - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - - ENDP - -;/* -; * void svc_exit(void); -; */ -svc_exit PROC - EXPORT svc_exit - - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - ENDP - - ALIGN - - END diff --git a/components/lwp/arch/arm/cortex-m4/lwp_gcc.S b/components/lwp/arch/arm/cortex-m4/lwp_gcc.S deleted file mode 100644 index e71dbb82c9..0000000000 --- a/components/lwp/arch/arm/cortex-m4/lwp_gcc.S +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-10-30 heyuanjie first version - */ - -.cpu cortex-m4 -.syntax unified -.thumb -.text - -/* - * void* lwp_get_sys_api(rt_uint32_t number); - */ -.global lwp_get_sys_api -.global lwp_get_kernel_sp -.global lwp_set_kernel_sp - - -/* - * void lwp_user_entry(args, text, data); - */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: - PUSH {R0-R3} @; push text&data addr. - - MOV R0, SP @; v1 = SP - BL lwp_set_kernel_sp @; lwp_set_kernel_sp(v1) - - @; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 @; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} @; pop app address to R1. - @; set data address. - MOV R9, R2 - - @; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -/* - * void SVC_Handler(void); - */ -.global SVC_Handler -.type SVC_Handler, % function -SVC_Handler: - PUSH {LR} - - @; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} @; push app SP. - - @; get SVC number. - mov R0, R7 - - @; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} @; push api - - @; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} @; pop api to R2. - POP {R1} @; pop app SP to R1. - - stmfd r0!, {r1} @; save app SP to kernel SP - - @;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - @; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} @; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} @; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] @; update LR - STR R2, [R0, #24] @; update api to PC - MSR PSP, R0 @; update SP, API is executed with kernel SP - - @; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} @; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR -/* -* void svc_exit(void); -*/ -.global svc_exit -.type svc_exit, % function -svc_exit: - @; get user SP. - PUSH {R0} @; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] @; load pc - add r3, #32 @; exception_stack_frame size - MSR PSP, R3 @; restore app stack pointer - @; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - @; return to lwp. - ORR R1, R1, #0x01 @; only Thumb-mode. - BX R1 @; return to user app. diff --git a/components/lwp/arch/arm/cortex-m4/lwp_iar.S b/components/lwp/arch/arm/cortex-m4/lwp_iar.S deleted file mode 100644 index a218569375..0000000000 --- a/components/lwp/arch/arm/cortex-m4/lwp_iar.S +++ /dev/null @@ -1,123 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - SECTION .text:CODE(2) - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ - EXPORT lwp_user_entry -lwp_user_entry: - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -;/* -; * void SVC_Handler(void); -; */ - EXPORT SVC_Handler -SVC_Handler: - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - -;/* -; * void svc_exit(void); -; */ - EXPORT svc_exit -svc_exit: - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - END diff --git a/components/lwp/arch/arm/cortex-m4/lwp_rvds.S b/components/lwp/arch/arm/cortex-m4/lwp_rvds.S deleted file mode 100644 index 246751a847..0000000000 --- a/components/lwp/arch/arm/cortex-m4/lwp_rvds.S +++ /dev/null @@ -1,135 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - AREA |.text|, CODE, READONLY, ALIGN=2 - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ -lwp_user_entry PROC - EXPORT lwp_user_entry - - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - - ; never reach here! - ENDP - -;/* -; * void SVC_Handler(void); -; */ -SVC_Handler PROC - EXPORT SVC_Handler - - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - - ENDP - -;/* -; * void svc_exit(void); -; */ -svc_exit PROC - EXPORT svc_exit - - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - ENDP - - ALIGN - - END diff --git a/components/lwp/arch/arm/cortex-m7/lwp_gcc.S b/components/lwp/arch/arm/cortex-m7/lwp_gcc.S deleted file mode 100644 index 848c592919..0000000000 --- a/components/lwp/arch/arm/cortex-m7/lwp_gcc.S +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-10-30 heyuanjie first version - */ - -.cpu cortex-m7 -.syntax unified -.thumb -.text - -/* - * void* lwp_get_sys_api(rt_uint32_t number); - */ -.global lwp_get_sys_api -.global lwp_get_kernel_sp -.global lwp_set_kernel_sp - - -/* - * void lwp_user_entry(args, text, data); - */ -.global lwp_user_entry -.type lwp_user_entry, % function -lwp_user_entry: - PUSH {R0-R3} @; push text&data addr. - - MOV R0, SP @; v1 = SP - BL lwp_set_kernel_sp @; lwp_set_kernel_sp(v1) - - @; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 @; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} @; pop app address to R1. - @; set data address. - MOV R9, R2 - - @; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -/* - * void SVC_Handler(void); - */ -.global SVC_Handler -.type SVC_Handler, % function -SVC_Handler: - PUSH {LR} - - @; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} @; push app SP. - - @; get SVC number. - mov R0, R7 - - @; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} @; push api - - @; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} @; pop api to R2. - POP {R1} @; pop app SP to R1. - - stmfd r0!, {r1} @; save app SP to kernel SP - - @;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - @; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} @; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} @; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] @; update LR - STR R2, [R0, #24] @; update api to PC - MSR PSP, R0 @; update SP, API is executed with kernel SP - - @; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} @; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR -/* -* void svc_exit(void); -*/ -.global svc_exit -.type svc_exit, % function -svc_exit: - @; get user SP. - PUSH {R0} @; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] @; load pc - add r3, #32 @; exception_stack_frame size - MSR PSP, R3 @; restore app stack pointer - @; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - @; return to lwp. - ORR R1, R1, #0x01 @; only Thumb-mode. - BX R1 @; return to user app. diff --git a/components/lwp/arch/arm/cortex-m7/lwp_iar.S b/components/lwp/arch/arm/cortex-m7/lwp_iar.S deleted file mode 100644 index a218569375..0000000000 --- a/components/lwp/arch/arm/cortex-m7/lwp_iar.S +++ /dev/null @@ -1,123 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - SECTION .text:CODE(2) - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ - EXPORT lwp_user_entry -lwp_user_entry: - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - -;/* -; * void SVC_Handler(void); -; */ - EXPORT SVC_Handler -SVC_Handler: - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - -;/* -; * void svc_exit(void); -; */ - EXPORT svc_exit -svc_exit: - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - END diff --git a/components/lwp/arch/arm/cortex-m7/lwp_rvds.S b/components/lwp/arch/arm/cortex-m7/lwp_rvds.S deleted file mode 100644 index 246751a847..0000000000 --- a/components/lwp/arch/arm/cortex-m7/lwp_rvds.S +++ /dev/null @@ -1,135 +0,0 @@ -;/* -; * Copyright (c) 2006-2018, RT-Thread Development Team -; * -; * SPDX-License-Identifier: Apache-2.0 -; * -; * Change Logs: -; * Date Author Notes -; * 2018-10-30 heyuanjie first version -; */ - - AREA |.text|, CODE, READONLY, ALIGN=2 - THUMB - REQUIRE8 - PRESERVE8 - -;/* -; * void* lwp_get_sys_api(rt_uint32_t number); -; */ - IMPORT lwp_get_sys_api - IMPORT lwp_get_kernel_sp - IMPORT lwp_set_kernel_sp - -;/* -; * void lwp_user_entry(args, text, data); -; */ -lwp_user_entry PROC - EXPORT lwp_user_entry - - PUSH {R0-R3} ; push text&data addr. - - MOV R0, SP ; v1 = SP - BL lwp_set_kernel_sp ; lwp_set_kernel_sp(v1) - - ; set CPU to user-thread mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 ; use PSP, user-thread mode. - MSR CONTROL, R2 - - POP {R0-R3} ; pop app address to R1. - ; set data address. - MOV R9, R2 - - ; run app, only Thumb-mode. - ORR R1, R1, #0x01 - BX R1 - - ; never reach here! - ENDP - -;/* -; * void SVC_Handler(void); -; */ -SVC_Handler PROC - EXPORT SVC_Handler - - PUSH {LR} - - ; get user SP. - TST LR, #0x4 - ITE EQ - MRSEQ R1, MSP - MRSNE R1, PSP - PUSH {R1} ; push app SP. - - ; get SVC number. - mov R0, R7 - - ; get kernel system API - BL lwp_get_sys_api - - PUSH {R0} ; push api - - ; get kernel SP to R0. - BL lwp_get_kernel_sp - - POP {R2} ; pop api to R2. - POP {R1} ; pop app SP to R1. - - stmfd r0!, {r1} ; save app SP to kernel SP - - ;push app parm5~6 to kernel SP - STMFD R0!, {R4 - R5} - ; copy R1(app SP) to R0(kernel SP). - push {r8-r11} - LDMFD R1, {R4 - R11} ; pop exception_stack_frame to r4 - r11 register - STMFD R0!, {R4 - R11} ; push exception_stack_frame to server SP. - pop {r8-r11} - - LDR R3, =svc_exit - STR R3, [R0, #20] ; update LR - STR R2, [R0, #24] ; update api to PC - MSR PSP, R0 ; update SP, API is executed with kernel SP - - ; set to thread-privilege mode. - MRS R3, CONTROL - BIC R3, R3, #0x01 - ORR R3, R3, #0x02 - MSR CONTROL, R3 - - POP {LR} ; 0xFFFFFFED - ORR LR, LR, #0x10 - BX LR - - ENDP - -;/* -; * void svc_exit(void); -; */ -svc_exit PROC - EXPORT svc_exit - - ; get user SP. - PUSH {R0} ; push result to SP. - BL lwp_get_kernel_sp - ldr r3, [r0, #-4] - pop {r0} - - ldr lr, [r3, #20] - ldr r1, [r3, #24] ; load pc - add r3, #32 ; exception_stack_frame size - MSR PSP, R3 ; restore app stack pointer - ; restore to PSP & thread-unprivilege mode. - MRS R2, CONTROL - ORR R2, R2, #0x03 - MSR CONTROL, R2 - - ; return to lwp. - ORR R1, R1, #0x01 ; only Thumb-mode. - BX R1 ; return to user app. - - ENDP - - ALIGN - - END diff --git a/components/lwp/arch/risc-v/rv64/SConscript b/components/lwp/arch/risc-v/rv64/SConscript new file mode 100644 index 0000000000..e98e7650b1 --- /dev/null +++ b/components/lwp/arch/risc-v/rv64/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.S') +CPPPATH = [cwd] + +group = DefineGroup('lwp-riscv', src, depend = ['RT_USING_LWP'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/lwp/arch/risc-v/rv64/lwp_arch.c b/components/lwp/arch/risc-v/rv64/lwp_arch.c new file mode 100644 index 0000000000..a1e0b8f31a --- /dev/null +++ b/components/lwp/arch/risc-v/rv64/lwp_arch.c @@ -0,0 +1,264 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-11-18 Jesven first version + * 2021-02-03 lizhirui port to riscv64 + * 2021-02-06 lizhirui add thread filter + * 2021-02-19 lizhirui port to new version of rt-smart + * 2021-03-02 lizhirui add a auxillary function for interrupt + * 2021-03-04 lizhirui delete thread filter + * 2021-03-04 lizhirui modify for new version of rt-smart + * 2021-11-22 JasonHu add lwp_set_thread_context + * 2021-11-30 JasonHu add clone/fork support + */ + +#include <rthw.h> +#include <stddef.h> + +#ifdef RT_USING_USERSPACE + +#include <mmu.h> +#include <page.h> +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#include <lwp_arch.h> + +#include <stack.h> +#include <cpuport.h> +#include <encoding.h> + +extern rt_ubase_t MMUTable[]; + +int arch_expand_user_stack(void *addr) +{ + int ret = 0; + rt_ubase_t stack_addr = (rt_ubase_t)addr; + + stack_addr &= ~PAGE_OFFSET_MASK; + if ((stack_addr >= (rt_ubase_t)USER_STACK_VSTART) && (stack_addr < (rt_ubase_t)USER_STACK_VEND)) + { + void *map = lwp_map_user(lwp_self(), (void *)stack_addr, PAGE_SIZE, RT_FALSE); + + if (map || lwp_user_accessable(addr, 1)) + { + ret = 1; + } + } + return ret; +} + +void *lwp_copy_return_code_to_user_stack() +{ + void lwp_thread_return(); + void lwp_thread_return_end(); + rt_thread_t tid = rt_thread_self(); + + if (tid->user_stack != RT_NULL) + { + rt_size_t size = (rt_size_t)lwp_thread_return_end - (rt_size_t)lwp_thread_return; + rt_size_t userstack = (rt_size_t)tid->user_stack + tid->user_stack_size - size; + rt_memcpy((void *)userstack, lwp_thread_return, size); + return (void *)userstack; + } + + return RT_NULL; +} + +rt_mmu_info *arch_kernel_get_mmu_info(void) +{ + extern rt_mmu_info *mmu_info; + + return mmu_info; +} + +rt_ubase_t lwp_fix_sp(rt_ubase_t cursp) +{ + void lwp_thread_return(); + void lwp_thread_return_end(); + + if (cursp == 0) + { + return 0; + } + + return cursp - ((rt_size_t)lwp_thread_return_end - (rt_size_t)lwp_thread_return); +} + +rt_thread_t rt_thread_sp_to_thread(void *spmember_addr) +{ + return (rt_thread_t)(((rt_ubase_t)spmember_addr) - (offsetof(struct rt_thread, sp))); +} + +void *get_thread_kernel_stack_top(rt_thread_t thread) +{ + return (void *)(((rt_size_t)thread->stack_addr) + ((rt_size_t)thread->stack_size)); +} + +void *arch_get_user_sp(void) +{ + /* user sp saved in interrupt context */ + rt_thread_t self = rt_thread_self(); + rt_uint8_t *stack_top = (rt_uint8_t *)self->stack_addr + self->stack_size; + struct rt_hw_stack_frame *frame = (struct rt_hw_stack_frame *)(stack_top - sizeof(struct rt_hw_stack_frame)); + + return (void *)frame->user_sp_exc_stack; +} + +int arch_user_space_init(struct rt_lwp *lwp) +{ + rt_ubase_t *mmu_table; + + mmu_table = (rt_ubase_t *)rt_pages_alloc(0); + if (!mmu_table) + { + return -1; + } + + lwp->end_heap = USER_HEAP_VADDR; + + rt_memcpy(mmu_table, MMUTable, PAGE_SIZE); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, 4 * PAGE_SIZE); + rt_hw_mmu_map_init(&lwp->mmu_info, (void *)USER_VADDR_START, USER_VADDR_TOP - USER_VADDR_START, (rt_size_t *)mmu_table, 0); + + return 0; +} + +void *arch_kernel_mmu_table_get(void) +{ + return (void *)((char *)MMUTable); +} + +void arch_user_space_vtable_free(struct rt_lwp *lwp) +{ + if (lwp && lwp->mmu_info.vtable) + { + rt_pages_free(lwp->mmu_info.vtable, 0); + } +} + +long _sys_clone(void *arg[]); +long sys_clone(void *arg[]) +{ + return _sys_clone(arg); +} + +long _sys_fork(void); +long sys_fork(void) +{ + return _sys_fork(); +} + +long _sys_vfork(void); +long sys_vfork(void) +{ + return _sys_fork(); +} + +/** + * set exec context for fork/clone. + */ +int arch_set_thread_context(void (*exit)(void), void *new_thread_stack, + void *user_stack, void **thread_sp) +{ + RT_ASSERT(exit != RT_NULL); + RT_ASSERT(user_stack != RT_NULL); + RT_ASSERT(new_thread_stack != RT_NULL); + RT_ASSERT(thread_sp != RT_NULL); + struct rt_hw_stack_frame *syscall_frame; + struct rt_hw_stack_frame *thread_frame; + + rt_uint8_t *stk; + rt_uint8_t *syscall_stk; + + stk = (rt_uint8_t *)new_thread_stack; + /* reserve syscall context, all the registers are copyed from parent */ + stk -= CTX_REG_NR * REGBYTES; + syscall_stk = stk; + + syscall_frame = (struct rt_hw_stack_frame *)stk; + + /* modify user sp */ + syscall_frame->user_sp_exc_stack = (rt_ubase_t)user_stack; + + /* skip ecall */ + syscall_frame->epc += 4; + + /* child return value is 0 */ + syscall_frame->a0 = 0; + syscall_frame->a1 = 0; + + /* reset thread area */ + rt_thread_t thread = rt_container_of((unsigned long)thread_sp, struct rt_thread, sp); + syscall_frame->tp = (rt_ubase_t)thread->thread_idr; + +#ifdef ARCH_USING_NEW_CTX_SWITCH + extern void *_rt_hw_stack_init(rt_ubase_t *sp, rt_ubase_t ra, rt_ubase_t sstatus); + rt_ubase_t sstatus = read_csr(sstatus) | SSTATUS_SPP; + sstatus &= ~SSTATUS_SIE; + + /* compatible to RESTORE_CONTEXT */ + stk = (void *)_rt_hw_stack_init((rt_ubase_t *)stk, (rt_ubase_t)exit, sstatus); +#else + /* build temp thread context */ + stk -= sizeof(struct rt_hw_stack_frame); + + thread_frame = (struct rt_hw_stack_frame *)stk; + + int i; + for (i = 0; i < sizeof(struct rt_hw_stack_frame) / sizeof(rt_ubase_t); i++) + { + ((rt_ubase_t *)thread_frame)[i] = 0xdeadbeaf; + } + + /* set pc for thread */ + thread_frame->epc = (rt_ubase_t)exit; + + /* set old exception mode as supervisor, because in kernel */ + thread_frame->sstatus = read_csr(sstatus) | SSTATUS_SPP; + thread_frame->sstatus &= ~SSTATUS_SIE; /* must disable interrupt */ + + /* set stack as syscall stack */ + thread_frame->user_sp_exc_stack = (rt_ubase_t)syscall_stk; + +#endif /* ARCH_USING_NEW_CTX_SWITCH */ + /* save new stack top */ + *thread_sp = (void *)stk; + + /** + * The stack for child thread: + * + * +------------------------+ --> kernel stack top + * | syscall stack | + * | | + * | @sp | --> `user_stack` + * | @epc | --> user ecall addr + 4 (skip ecall) + * | @a0&a1 | --> 0 (for child return 0) + * | | + * +------------------------+ --> temp thread stack top + * | temp thread stack | ^ + * | | | + * | @sp | ---------/ + * | @epc | --> `exit` (arch_clone_exit/arch_fork_exit) + * | | + * +------------------------+ --> thread sp + */ +} + +/** + * void lwp_exec_user(void *args, void *kernel_stack, void *user_entry) + */ +void lwp_exec_user(void *args, void *kernel_stack, void *user_entry) +{ + arch_start_umode(args, user_entry, (void*)USER_STACK_VEND, kernel_stack); +} + +void *arch_get_usp_from_uctx(struct rt_user_context *uctx) +{ + return uctx->sp; +} + +#endif /* RT_USING_USERSPACE */ diff --git a/components/lwp/arch/risc-v/rv64/lwp_arch.h b/components/lwp/arch/risc-v/rv64/lwp_arch.h new file mode 100644 index 0000000000..9e12aa5ba7 --- /dev/null +++ b/components/lwp/arch/risc-v/rv64/lwp_arch.h @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef LWP_ARCH_H__ +#define LWP_ARCH_H__ + +#include <lwp.h> +#include <lwp_arch_comm.h> +#include <riscv_mmu.h> + +#ifdef RT_USING_USERSPACE + +#ifdef RT_USING_USERSPACE_32BIT_LIMIT +#define USER_HEAP_VADDR 0xF0000000UL +#define USER_HEAP_VEND 0xFE000000UL +#define USER_STACK_VSTART 0xE0000000UL +#define USER_STACK_VEND USER_HEAP_VADDR +#define USER_VADDR_START 0xC0000000UL +#define USER_VADDR_TOP 0xFF000000UL +#define USER_LOAD_VADDR 0xD0000000UL +#define LDSO_LOAD_VADDR USER_LOAD_VADDR +#else +#define USER_HEAP_VADDR 0x300000000UL +#define USER_HEAP_VEND 0xffffffffffff0000UL +#define USER_STACK_VSTART 0x270000000UL +#define USER_STACK_VEND USER_HEAP_VADDR +#define USER_VADDR_START 0x100000000UL +#define USER_VADDR_TOP 0xfffffffffffff000UL +#define USER_LOAD_VADDR 0x200000000 +#define LDSO_LOAD_VADDR 0x200000000 +#endif + +/* this attribution is cpu specified, and it should be defined in riscv_mmu.h */ +#ifndef MMU_MAP_U_RWCB +#define MMU_MAP_U_RWCB 0 +#endif + +#ifndef MMU_MAP_U_RW +#define MMU_MAP_U_RW 0 +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +rt_mmu_info* arch_kernel_get_mmu_info(void); + +rt_inline unsigned long rt_hw_ffz(unsigned long x) +{ + return __builtin_ffsl(~x) - 1; +} + +rt_inline void icache_invalid_all(void) +{ + //TODO: +} + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /*LWP_ARCH_H__*/ diff --git a/components/lwp/arch/risc-v/rv64/lwp_gcc.S b/components/lwp/arch/risc-v/rv64/lwp_gcc.S new file mode 100644 index 0000000000..edf8166e77 --- /dev/null +++ b/components/lwp/arch/risc-v/rv64/lwp_gcc.S @@ -0,0 +1,337 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-12-10 Jesven first version + * 2021-02-03 lizhirui port to riscv64 + * 2021-02-19 lizhirui port to new version of rt-smart + * 2022-11-08 Wangxiaoyao Cleanup codes; + * Support new context switch + */ + +#include "rtconfig.h" + +#ifndef __ASSEMBLY__ +#define __ASSEMBLY__ +#endif /* __ASSEMBLY__ */ + +#include "cpuport.h" +#include "encoding.h" +#include "stackframe.h" + +.section .text.lwp + +/* + * void arch_start_umode(args, text, ustack, kstack); + */ +.global arch_start_umode +.type arch_start_umode, % function +arch_start_umode: + // load kstack for user process + csrw sscratch, a3 + li t0, SSTATUS_SPP | SSTATUS_SIE // set as user mode, close interrupt + csrc sstatus, t0 + li t0, SSTATUS_SPIE // enable interrupt when return to user mode + csrs sstatus, t0 + + csrw sepc, a1 + mv a3, a2 + sret//enter user mode + +/* + * void arch_crt_start_umode(args, text, ustack, kstack); + */ +.global arch_crt_start_umode +.type arch_crt_start_umode, % function +arch_crt_start_umode: + li t0, SSTATUS_SPP | SSTATUS_SIE // set as user mode, close interrupt + csrc sstatus, t0 + li t0, SSTATUS_SPIE // enable interrupt when return to user mode + csrs sstatus, t0 + + csrw sepc, a1 + mv s0, a0 + mv s1, a1 + mv s2, a2 + mv s3, a3 + mv a0, s2 + call lwp_copy_return_code_to_user_stack + mv a0, s2 + call lwp_fix_sp + mv sp, a0//user_sp + mv ra, a0//return address + mv a0, s0//args + + csrw sscratch, s3 + sret//enter user mode + +/** + * Unify exit point from kernel mode to enter user space + * we handle following things here: + * 1. restoring user mode debug state (not support yet) + * 2. handling thread's exit request + * 3. handling POSIX signal + * 4. restoring user context + * 5. jump to user mode + */ +.global arch_ret_to_user +arch_ret_to_user: + // TODO: we don't support kernel gdb server in risc-v yet + // so we don't check debug state here and handle debugging bussiness + + call lwp_check_exit_request + beqz a0, 1f + mv a0, x0 + call sys_exit + +1: + call lwp_signal_check + beqz a0, ret_to_user_exit + J user_do_signal + +ret_to_user_exit: + RESTORE_ALL + // `RESTORE_ALL` also reset sp to user sp, and setup sscratch + sret + +/** + * Restore user context from exception frame stroraged in ustack + * And handle pending signals; + */ +arch_signal_quit: + call lwp_signal_restore + call arch_get_usp_from_uctx + // return value is user sp + mv sp, a0 + + // restore user sp before enter trap + addi a0, sp, CTX_REG_NR * REGBYTES + csrw sscratch, a0 + + RESTORE_ALL + SAVE_ALL + j arch_ret_to_user + +/** + * Prepare and enter user signal handler + * Move user exception frame and setup signal return + * routine in user stack + */ +user_do_signal: + /** restore and backup kernel sp carefully to avoid leaking */ + addi t0, sp, CTX_REG_NR * REGBYTES + csrw sscratch, t0 + + RESTORE_ALL + SAVE_ALL + + /** + * save lwp_sigreturn in user memory + */ + mv s0, sp + la t0, lwp_sigreturn + la t1, lwp_sigreturn_end + // t1 <- size + sub t1, t1, t0 + // s0 <- dst + sub s0, s0, t1 + mv s2, t1 +lwp_sigreturn_copy_loop: + addi t2, t1, -1 + add t3, t0, t2 + add t4, s0, t2 + lb t5, 0(t3) + sb t5, 0(t4) + mv t1, t2 + bnez t1, lwp_sigreturn_copy_loop + + /** + * 1. clear sscratch & restore kernel sp to + * enter kernel mode routine + * 2. storage exp frame address to restore context, + * by calling to lwp_signal_backup + * 3. storage lwp_sigreturn entry address + * 4. get signal id as param for signal handler + */ + mv s1, sp + csrrw sp, sscratch, x0 + + /** + * synchronize dcache & icache if target is + * a Harvard Architecture machine, otherwise + * do nothing + */ + mv a0, s0 + mv a1, s2 + call rt_hw_sync_cache_local + + /** + * backup user sp (point to saved exception frame, skip sigreturn routine) + * And get signal id + + * a0: user sp + * a1: user_pc (not used, marked as 0 to avoid abuse) + * a2: user_flag (not used, marked as 0 to avoid abuse) + */ + mv a0, s1 + mv a1, zero + mv a2, zero + call lwp_signal_backup + + /** + * backup signal id in s2, + * and get sighandler by signal id + */ + mv s2, a0 + call lwp_sighandler_get + + /** + * set regiter RA to user signal handler + * set sp to user sp & save kernel sp in sscratch + */ + mv ra, s0 + csrw sscratch, sp + mv sp, s0 + + /** + * a0 is signal_handler, + * s1 = s0 == NULL ? lwp_sigreturn : s0; + */ + mv s1, s0 + beqz a0, skip_user_signal_handler + mv s1, a0 + +skip_user_signal_handler: + // enter user mode and enable interrupt when return to user mode + li t0, SSTATUS_SPP + csrc sstatus, t0 + li t0, SSTATUS_SPIE + csrs sstatus, t0 + + // sepc <- signal_handler + csrw sepc, s1 + // a0 <- signal id + mv a0, s2 + sret + +.align 3 +lwp_debugreturn: + li a7, 0xff + ecall + +.align 3 +lwp_sigreturn: + li a7, 0xfe + ecall + +.align 3 +lwp_sigreturn_end: + +.align 3 +.global lwp_thread_return +lwp_thread_return: + li a0, 0 + li a7, 1 + ecall + +.align 3 +.global lwp_thread_return_end +lwp_thread_return_end: + +.globl arch_get_tidr +arch_get_tidr: + mv a0, tp + ret + +.global arch_set_thread_area +arch_set_thread_area: +.globl arch_set_tidr +arch_set_tidr: + mv tp, a0 + ret + +.global arch_clone_exit +.global arch_fork_exit +arch_fork_exit: +arch_clone_exit: + j arch_syscall_exit + +.global syscall_entry +syscall_entry: +#ifndef ARCH_USING_NEW_CTX_SWITCH + //swap to thread kernel stack + csrr t0, sstatus + andi t0, t0, 0x100 + beqz t0, __restore_sp_from_tcb + +__restore_sp_from_sscratch: // from kernel + csrr t0, sscratch + j __move_stack_context + +__restore_sp_from_tcb: // from user + la a0, rt_current_thread + LOAD a0, 0(a0) + jal get_thread_kernel_stack_top + mv t0, a0 + +__move_stack_context: + mv t1, sp//src + mv sp, t0//switch stack + addi sp, sp, -CTX_REG_NR * REGBYTES + //copy context + li s0, CTX_REG_NR//cnt + mv t2, sp//dst + +copy_context_loop: + LOAD t0, 0(t1) + STORE t0, 0(t2) + addi s0, s0, -1 + addi t1, t1, 8 + addi t2, t2, 8 + bnez s0, copy_context_loop +#endif /* ARCH_USING_NEW_CTX_SWITCH */ + + /* fetch SYSCALL ID */ + LOAD a7, 17 * REGBYTES(sp) + addi a7, a7, -0xfe + beqz a7, arch_signal_quit + +#ifdef RT_USING_USERSPACE + /* save setting when syscall enter */ + call rt_thread_self + call lwp_user_setting_save +#endif + + mv a0, sp + OPEN_INTERRUPT + call syscall_handler + j arch_syscall_exit + +.global arch_syscall_exit +arch_syscall_exit: + CLOSE_INTERRUPT + + #if defined(RT_USING_USERSPACE) + LOAD s0, 2 * REGBYTES(sp) + andi s0, s0, 0x100 + bnez s0, dont_ret_to_user + j arch_ret_to_user + #endif +dont_ret_to_user: + +#ifdef RT_USING_USERSPACE + /* restore setting when syscall exit */ + call rt_thread_self + call lwp_user_setting_restore + + /* after restore the reg `tp`, need modify context */ + STORE tp, 4 * REGBYTES(sp) +#endif + + //restore context + RESTORE_ALL + csrw sscratch, zero + sret diff --git a/components/lwp/arch/risc-v/rv64/reloc.c b/components/lwp/arch/risc-v/rv64/reloc.c new file mode 100644 index 0000000000..b35f8bef27 --- /dev/null +++ b/components/lwp/arch/risc-v/rv64/reloc.c @@ -0,0 +1,108 @@ +#include <rtthread.h> +#include <stdint.h> +#include <string.h> +#include <elf.h> +#ifdef RT_USING_USERSPACE +#include <mmu.h> +#include <page.h> +#endif + +typedef struct +{ + Elf64_Word st_name; + Elf64_Addr st_value; + Elf64_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf64_Half st_shndx; +} Elf64_sym; + +#ifdef RT_USING_USERSPACE +void arch_elf_reloc(rt_mmu_info *m_info, void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf64_sym *dynsym) +{ + size_t rel_off; + void* addr; + + if (rel_dyn_size && !dynsym) + { + return; + } + for (rel_off = 0; rel_off < rel_dyn_size; rel_off += 8) + { + uint32_t v1, v2; + + addr = rt_hw_mmu_v2p(m_info, (void *)(((rt_size_t)rel_dyn_start) + rel_off)); + memcpy(&v1, addr, 4); + addr = rt_hw_mmu_v2p(m_info, (void *)(((rt_size_t)rel_dyn_start) + rel_off + 4)); + memcpy(&v2, addr, 4); + + addr = rt_hw_mmu_v2p(m_info, (void *)((rt_size_t)text_start + v1)); + if ((v2 & 0xff) == R_ARM_RELATIVE) + { + *(rt_size_t*)addr += (rt_size_t)text_start; + } + else if ((v2 & 0xff) == R_ARM_ABS32) + { + uint32_t t; + t = (v2 >> 8); + if (t) /* 0 is UDF */ + { + *(rt_size_t*)addr = (((rt_size_t)text_start) + dynsym[t].st_value); + } + } + } + /* modify got */ + if (got_size) + { + uint32_t *got_item = (uint32_t*)got_start; + + for (rel_off = 0; rel_off < got_size; rel_off += 4, got_item++) + { + addr = rt_hw_mmu_v2p(m_info, got_item); + *(rt_size_t *)addr += (rt_size_t)text_start; + } + } +} +#else + +void arch_elf_reloc(void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf64_sym *dynsym) +{ + size_t rel_off; + + if (rel_dyn_size && !dynsym) + { + return; + } + for (rel_off = 0; rel_off < rel_dyn_size; rel_off += 8) + { + uint32_t v1, v2; + + memcpy(&v1, ((rt_uint8_t *)rel_dyn_start) + rel_off, 4); + memcpy(&v2, ((rt_uint8_t *)rel_dyn_start) + rel_off + 4, 4); + + if ((v2 & 0xff) == R_ARM_RELATIVE) + { + *(uint32_t*)(((rt_size_t)text_start) + v1) += (uint32_t)text_start; + } + else if ((v2 & 0xff) == R_ARM_ABS32) + { + uint32_t t; + t = (v2 >> 8); + if (t) /* 0 is UDF */ + { + *(uint32_t*)(((rt_size_t)text_start) + v1) = (uint32_t)(((rt_size_t)text_start) + dynsym[t].st_value); + } + } + } + /* modify got */ + if (got_size) + { + uint32_t *got_item = (uint32_t*)got_start; + + for (rel_off = 0; rel_off < got_size; rel_off += 4, got_item++) + { + *got_item += (uint32_t)text_start; + } + } +} +#endif diff --git a/components/lwp/arch/x86/i386/SConscript b/components/lwp/arch/x86/i386/SConscript new file mode 100644 index 0000000000..c0d3aead77 --- /dev/null +++ b/components/lwp/arch/x86/i386/SConscript @@ -0,0 +1,11 @@ +# RT-Thread building script for component + +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') + Glob('*.S') +CPPPATH = [cwd] + +group = DefineGroup('lwp-x86-i386', src, depend = ['RT_USING_LWP'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/lwp/arch/x86/i386/lwp_arch.c b/components/lwp/arch/x86/i386/lwp_arch.c new file mode 100644 index 0000000000..5ad150121b --- /dev/null +++ b/components/lwp/arch/x86/i386/lwp_arch.c @@ -0,0 +1,371 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-7-14 JasonHu first version + */ + +#include <rthw.h> +#include <stddef.h> +#include <rtconfig.h> +#include <rtdbg.h> + +#ifdef RT_USING_USERSPACE + +#include <stackframe.h> +#include <interrupt.h> +#include <segment.h> + +#include <mmu.h> +#include <page.h> +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#include <lwp_arch.h> + +#ifdef RT_USING_SIGNALS +#include <lwp_signal.h> +#endif /* RT_USING_SIGNALS */ + +extern size_t g_mmu_table[]; + +int arch_expand_user_stack(void *addr) +{ + int ret = 0; + size_t stack_addr = (size_t)addr; + + stack_addr &= ~PAGE_OFFSET_MASK; + if ((stack_addr >= (size_t)USER_STACK_VSTART) && (stack_addr < (size_t)USER_STACK_VEND)) + { + void *map = lwp_map_user(lwp_self(), (void *)stack_addr, PAGE_SIZE, RT_FALSE); + + if (map || lwp_user_accessable(addr, 1)) + { + ret = 1; /* map success */ + } + else /* map failed, send signal SIGSEGV */ + { +#ifdef RT_USING_SIGNALS + dbg_log(DBG_ERROR, "[fault] thread %s mapped addr %p failed!\n", rt_thread_self()->name, addr); + lwp_thread_kill(rt_thread_self(), SIGSEGV); + ret = 1; /* return 1, will return back to intr, then check exit */ +#endif + } + } + else /* not stack, send signal SIGSEGV */ + { +#ifdef RT_USING_SIGNALS + dbg_log(DBG_ERROR, "[fault] thread %s access unmapped addr %p!\n", rt_thread_self()->name, addr); + lwp_thread_kill(rt_thread_self(), SIGSEGV); + ret = 1; /* return 1, will return back to intr, then check exit */ +#endif + } + return ret; +} + +void *get_thread_kernel_stack_top(rt_thread_t thread) +{ + return RT_NULL; +} + +/** + * don't support this in i386, it's ok! + */ +void *arch_get_user_sp() +{ + return RT_NULL; +} + +int arch_user_space_init(struct rt_lwp *lwp) +{ + rt_size_t *mmu_table; + + mmu_table = (rt_size_t *)rt_pages_alloc(0); + if (!mmu_table) + { + return -1; + } + rt_memset(mmu_table, 0, ARCH_PAGE_SIZE); + + lwp->end_heap = USER_HEAP_VADDR; + memcpy(mmu_table, g_mmu_table, ARCH_PAGE_SIZE / 4); + memset((rt_uint8_t *)mmu_table + ARCH_PAGE_SIZE / 4, 0, ARCH_PAGE_SIZE / 4 * 3); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, mmu_table, ARCH_PAGE_SIZE); + if (rt_hw_mmu_map_init(&lwp->mmu_info, (void*)USER_VADDR_START, USER_VADDR_TOP - USER_VADDR_START, mmu_table, PV_OFFSET) < 0) + { + rt_pages_free(mmu_table, 0); + return -1; + } + return 0; +} + +void *arch_kernel_mmu_table_get(void) +{ + return (void *)((char *)g_mmu_table); +} + +void arch_user_space_vtable_free(struct rt_lwp *lwp) +{ + if (lwp && lwp->mmu_info.vtable) + { + rt_pages_free(lwp->mmu_info.vtable, 0); + lwp->mmu_info.vtable = NULL; + } +} + +void arch_set_thread_area(void *p) +{ + rt_hw_seg_tls_set((rt_ubase_t) p); + rt_thread_t cur = rt_thread_self(); + cur->thread_idr = p; /* update thread idr after first set */ +} + +void *arch_get_tidr(void) +{ + rt_thread_t cur = rt_thread_self(); + if (!cur->lwp) /* no lwp, don't get thread idr from tls seg */ + return NULL; + return (void *)rt_hw_seg_tls_get(); /* get thread idr from tls seg */ +} + +void arch_set_tidr(void *p) +{ + rt_thread_t cur = rt_thread_self(); + if (!cur->lwp) /* no lwp, don't set thread idr to tls seg */ + return; + rt_hw_seg_tls_set((rt_ubase_t) p); /* set tls seg addr as thread idr */ +} + +static void lwp_user_stack_init(rt_hw_stack_frame_t *frame) +{ + frame->ds = frame->es = USER_DATA_SEL; + frame->cs = USER_CODE_SEL; + frame->ss = USER_STACK_SEL; + frame->gs = USER_TLS_SEL; + frame->fs = 0; /* unused */ + + frame->edi = frame->esi = \ + frame->ebp = frame->esp_dummy = 0; + frame->eax = frame->ebx = \ + frame->ecx = frame->edx = 0; + + frame->error_code = 0; + frame->vec_no = 0; + + frame->eflags = (EFLAGS_MBS | EFLAGS_IF_1 | EFLAGS_IOPL_3); +} + +extern void lwp_switch_to_user(void *frame); +/** + * user entry, set frame. + * at the end of execute, we need enter user mode, + * in x86, we can set stack, arg, text entry in a stack frame, + * then pop then into register, final use iret to switch kernel mode to user mode. + */ +void arch_start_umode(void *args, const void *text, void *ustack, void *k_stack) +{ + rt_uint8_t *stk = k_stack; + stk -= sizeof(struct rt_hw_stack_frame); + struct rt_hw_stack_frame *frame = (struct rt_hw_stack_frame *)stk; + + lwp_user_stack_init(frame); + frame->esp = (rt_uint32_t)ustack - 32; + frame->ebx = (rt_uint32_t)args; + frame->eip = (rt_uint32_t)text; + lwp_switch_to_user(frame); + /* should never return */ +} + +void lwp_exec_user(void *args, void *kernel_stack, void *user_entry) +{ + arch_start_umode(args, (const void *)user_entry, (void *)USER_STACK_VEND, kernel_stack); +} + +extern void lwp_thread_return(); +extern void lwp_thread_return_end(); + +static void *lwp_copy_return_code_to_user_stack(void *ustack) +{ + size_t size = (size_t)lwp_thread_return_end - (size_t)lwp_thread_return; + void *retcode = (void *)((size_t)ustack - size); + memcpy(retcode, (void *)lwp_thread_return, size); + return retcode; +} + +/** + * when called sys_thread_create, need create a thread, after thread stared, will come here, + * like arch_start_umode, will enter user mode, but we must set thread exit function. it looks like: + * void func(void *arg) + * { + * ... + * } + * when thread func return, we must call exit code to exit thread, or not the program runs away. + * so we need copy exit code to user and call exit code when func return. + */ +void arch_crt_start_umode(void *args, const void *text, void *ustack, void *k_stack) +{ + RT_ASSERT(ustack != NULL); + + rt_uint8_t *stk; + stk = (rt_uint8_t *)((rt_uint8_t *)k_stack + sizeof(rt_ubase_t)); + stk = (rt_uint8_t *)RT_ALIGN_DOWN(((rt_ubase_t)stk), sizeof(rt_ubase_t)); + stk -= sizeof(struct rt_hw_stack_frame); + struct rt_hw_stack_frame *frame = (struct rt_hw_stack_frame *)stk; + + lwp_user_stack_init(frame); + + /* make user thread stack */ + unsigned long *retcode = lwp_copy_return_code_to_user_stack(ustack); /* copy ret code */ + unsigned long *retstack = (unsigned long *)RT_ALIGN_DOWN(((rt_ubase_t)retcode), sizeof(rt_ubase_t)); + + /** + * x86 call stack + * + * retcode here + * + * arg n + * arg n - 1 + * ... + * arg 2 + * arg 1 + * arg 0 + * eip (caller return addr, point to retcode) + * esp + */ + *(--retstack) = (unsigned long) args; /* arg */ + *(--retstack) = (unsigned long) retcode; /* ret eip */ + + frame->esp = (rt_uint32_t)retstack; + frame->eip = (rt_uint32_t)text; + lwp_switch_to_user(frame); + /* should never return */ +} + +rt_thread_t rt_thread_sp_to_thread(void *spmember_addr) +{ + return (rt_thread_t)(((rt_ubase_t)spmember_addr) - (offsetof(struct rt_thread, sp))); +} + +/** + * set exec context for fork/clone. + * user_stack(unused) + */ +void arch_set_thread_context(void *exit_addr, void *new_thread_stack, void *user_stack, void **thread_sp) +{ + /** + * thread kernel stack was set to tss.esp0, when intrrupt/syscall occur, + * the stack frame will store in kernel stack top, so we can get the stack + * frame by kernel stack top. + */ + rt_hw_stack_frame_t *frame = (rt_hw_stack_frame_t *)((rt_ubase_t)new_thread_stack - sizeof(rt_hw_stack_frame_t)); + + frame->eax = 0; /* child return 0 */ + + rt_hw_context_t *context = (rt_hw_context_t *) (((rt_uint32_t *)frame) - HW_CONTEXT_MEMBER_NR); + context->eip = (void *)exit_addr; /* when thread started, jump to intr exit for enter user mode */ + context->ebp = context->ebx = context->esi = context->edi = 0; + + /** + * set sp as the address of first member of rt_hw_context, + * when scheduler call switch, pop stack from context stack. + */ + *thread_sp = (void *)&context->ebp; + + /** + * after set context, the stack like this: + * + * ----------- + * stack frame| eax = 0 + * ----------- + * context(only HW_CONTEXT_MEMBER_NR)| eip = rt_hw_intr_exit + * ----------- + * thread sp | to <- rt_hw_context_switch(from, to) + * ----------- + */ +} + +#ifdef RT_USING_SIGNALS + +#define SIGNAL_RET_CODE_SIZE 16 + +struct rt_signal_frame +{ + char *ret_addr; /* return addr when handler return */ + int signo; /* signal for user handler arg */ + rt_hw_stack_frame_t frame; /* save kernel signal stack */ + char ret_code[SIGNAL_RET_CODE_SIZE]; /* save return code */ +}; +typedef struct rt_signal_frame rt_signal_frame_t; + +extern void lwp_signal_return(); +extern void lwp_signal_return_end(); + +void lwp_try_do_signal(rt_hw_stack_frame_t *frame) +{ + if (!lwp_signal_check()) + return; + + /* 1. backup signal mask */ + int signal = lwp_signal_backup((void *) frame->esp, (void *) frame->eip, (void *) frame->eflags); + + /* 2. get signal handler */ + lwp_sighandler_t handler = lwp_sighandler_get(signal); + if (handler == RT_NULL) /* no handler, ignore */ + { + lwp_signal_restore(); + return; + } + + rt_base_t level = rt_hw_interrupt_disable(); + /* 3. backup frame */ + rt_signal_frame_t *sig_frame = (rt_signal_frame_t *)((frame->esp - sizeof(rt_signal_frame_t)) & -8UL); + memcpy(&sig_frame->frame, frame, sizeof(rt_hw_stack_frame_t)); + sig_frame->signo = signal; + + /** + * 4. copy user return code into user stack + * + * save current frame on user stack. the user stack like: + * + * ---------- + * user code stack + * ----------+ -> esp before enter kernel + * signal frame + * ----------+ -> esp when handle signal handler + * signal handler stack + * ---------- + */ + size_t ret_code_size = (size_t)lwp_signal_return_end - (size_t)lwp_signal_return; + memcpy(sig_frame->ret_code, (void *)lwp_signal_return, ret_code_size); + sig_frame->ret_addr = sig_frame->ret_code; + + /* 5. jmp to user execute handler, update frame register info */ + lwp_user_stack_init(frame); + frame->eip = (rt_uint32_t) handler; + frame->esp = (rt_uint32_t) sig_frame; + + rt_hw_interrupt_enable(level); +} + +void lwp_signal_do_return(rt_hw_stack_frame_t *frame) +{ + /** + * ASSUME: in x86, each stack push and pop element is 4 byte. so STACK_ELEM_SIZE = sizeof(int) => 4. + * when signal handler return, the stack move to the buttom of signal frame. + * but return will pop eip from esp, then {esp += STACK_ELEM_SIZE}, thus {esp = (signal frame) + STACK_ELEM_SIZE}. + * so {(signal frame) = esp - STACK_ELEM_SIZE} + */ + rt_signal_frame_t *sig_frame = (rt_signal_frame_t *)(frame->esp - sizeof(rt_uint32_t)); + memcpy(frame, &sig_frame->frame, sizeof(rt_hw_stack_frame_t)); + + /** + * restore signal info, but don't use rt_user_context, + * we use sig_frame to restore stack frame + */ + lwp_signal_restore(); +} +#endif /* RT_USING_SIGNALS */ + +#endif /* RT_USING_USERSPACE */ diff --git a/components/lwp/arch/x86/i386/lwp_arch.h b/components/lwp/arch/x86/i386/lwp_arch.h new file mode 100644 index 0000000000..f9a56ca3f5 --- /dev/null +++ b/components/lwp/arch/x86/i386/lwp_arch.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-07-18 JasonHu first version + */ + +#ifndef LWP_ARCH_H__ +#define LWP_ARCH_H__ + +#include <lwp.h> +#include <lwp_arch_comm.h> +#include <stackframe.h> + +#ifdef RT_USING_USERSPACE +#define USER_VADDR_TOP 0xFFFFF000UL +#define USER_HEAP_VEND 0xE0000000UL +#define USER_HEAP_VADDR 0x90000000UL +#define USER_STACK_VSTART 0x80000000UL +#define USER_STACK_VEND USER_HEAP_VADDR +#define LDSO_LOAD_VADDR 0x70000000UL +#define USER_VADDR_START 0x40000000UL +#define USER_LOAD_VADDR USER_VADDR_START + +#define SIGNAL_RETURN_SYSCAL_ID 0xe000 + +#ifdef __cplusplus +extern "C" { +#endif + +rt_thread_t rt_thread_sp_to_thread(void *spmember_addr); + +void lwp_signal_do_return(rt_hw_stack_frame_t *frame); + +rt_inline unsigned long rt_hw_ffz(unsigned long x) +{ + return __builtin_ffsl(~x) - 1; +} + +#ifdef __cplusplus +} +#endif + +#endif /* RT_USING_USERSPACE */ + +#endif /*LWP_ARCH_H__*/ diff --git a/components/lwp/arch/x86/i386/lwp_gcc.S b/components/lwp/arch/x86/i386/lwp_gcc.S new file mode 100644 index 0000000000..c1245ebe11 --- /dev/null +++ b/components/lwp/arch/x86/i386/lwp_gcc.S @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-7-14 JasonHu first version + */ + +#include "rtconfig.h" + +.section .text.lwp + +/* + * void lwp_switch_to_user(frame); + */ +.global lwp_switch_to_user +lwp_switch_to_user: + movl 0x4(%esp), %esp + addl $4,%esp // skip intr no + popal + popl %gs + popl %fs + popl %es + popl %ds + addl $4, %esp // skip error_code + iret // enter to user mode + +.extern arch_syscall_exit +.global sys_fork +.global sys_vfork +.global arch_fork_exit +sys_fork: +sys_vfork: + jmp _sys_fork +arch_fork_exit: + jmp arch_syscall_exit + +.global sys_clone +.global arch_clone_exit +sys_clone: + jmp _sys_clone +arch_clone_exit: + jmp arch_syscall_exit + +/** + * rt thread return code + */ +.align 4 +.global lwp_thread_return +lwp_thread_return: + movl $1, %eax // eax = 1, sys_exit + movl $0, %ebx + int $0x80 +.align 4 +.global lwp_thread_return_end +lwp_thread_return_end: + +#ifdef RT_USING_SIGNALS +/** + * signal return code + */ +.align 4 +.global lwp_signal_return +lwp_signal_return: + movl $0xe000, %eax // special syscall id for return code + int $0x80 +.align 4 +.global lwp_signal_return_end +lwp_signal_return_end: + +#endif /* RT_USING_SIGNALS */ diff --git a/components/lwp/arch/x86/i386/reloc.c b/components/lwp/arch/x86/i386/reloc.c new file mode 100644 index 0000000000..9677bd65c6 --- /dev/null +++ b/components/lwp/arch/x86/i386/reloc.c @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-07-28 JasonHu first version + */ + +#include <rtthread.h> +#include <stdint.h> +#include <string.h> +#include <elf.h> +#ifdef RT_USING_USERSPACE +#include <mmu.h> +#include <page.h> +#endif + +typedef struct +{ + Elf32_Word st_name; + Elf32_Addr st_value; + Elf32_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf32_Half st_shndx; +} Elf32_sym; + +#ifdef RT_USING_USERSPACE +void arch_elf_reloc(rt_mmu_info *m_info, void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf32_sym *dynsym) +{ + +} +#else + +void arch_elf_reloc(void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, Elf32_sym *dynsym) +{ + +} +#endif diff --git a/components/lwp/ioremap.c b/components/lwp/ioremap.c new file mode 100644 index 0000000000..7bdd187103 --- /dev/null +++ b/components/lwp/ioremap.c @@ -0,0 +1,120 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-06 Jesven first version + */ +#include <rthw.h> +#include <rtthread.h> + +#include <ioremap.h> + +#ifdef RT_USING_USERSPACE +#include <mmu.h> +#include <lwp_mm_area.h> +#include <lwp_mm.h> + +static struct lwp_avl_struct *k_map_area; +extern rt_mmu_info mmu_info; + +static void _iounmap_range(void *addr, size_t size) +{ + void *va = RT_NULL, *pa = RT_NULL; + int i = 0; + + for (va = addr, i = 0; i < size; va = (void *)((char *)va + ARCH_PAGE_SIZE), i += ARCH_PAGE_SIZE) + { + pa = rt_hw_mmu_v2p(&mmu_info, va); + if (pa) + { + rt_hw_mmu_unmap(&mmu_info, va, ARCH_PAGE_SIZE); + } + } +} + +static void *_ioremap_type(void *paddr, size_t size, int type) +{ + void *v_addr = NULL; + size_t attr; + + switch (type) + { + case MM_AREA_TYPE_PHY: + attr = MMU_MAP_K_DEVICE; + break; + case MM_AREA_TYPE_PHY_CACHED: + attr = MMU_MAP_K_RWCB; + break; + default: + return v_addr; + } + + rt_mm_lock(); + v_addr = rt_hw_mmu_map(&mmu_info, 0, paddr, size, attr); + if (v_addr) + { + int ret = lwp_map_area_insert(&k_map_area, (size_t)v_addr, size, type); + if (ret != 0) + { + _iounmap_range(v_addr, size); + v_addr = NULL; + } + } + rt_mm_unlock(); + return v_addr; +} + +void *rt_ioremap(void *paddr, size_t size) +{ + return _ioremap_type(paddr, size, MM_AREA_TYPE_PHY); +} + +void *rt_ioremap_nocache(void *paddr, size_t size) +{ + return _ioremap_type(paddr, size, MM_AREA_TYPE_PHY); +} + +void *rt_ioremap_cached(void *paddr, size_t size) +{ + return _ioremap_type(paddr, size, MM_AREA_TYPE_PHY_CACHED); +} + +void rt_iounmap(volatile void *vaddr) +{ + struct lwp_avl_struct *ma_avl_node; + + rt_mm_lock(); + ma_avl_node = lwp_map_find(k_map_area, (size_t)vaddr); + if (ma_avl_node) + { + struct rt_mm_area_struct *ma = (struct rt_mm_area_struct *)ma_avl_node->data; + + _iounmap_range((void *)ma->addr, ma->size); + lwp_map_area_remove(&k_map_area, (size_t)vaddr); + } + rt_mm_unlock(); +} + +#else +void *rt_ioremap(void *paddr, size_t size) +{ + return paddr; +} + +void *rt_ioremap_nocache(void *paddr, size_t size) +{ + return paddr; +} + +void *rt_ioremap_cached(void *paddr, size_t size) +{ + return paddr; +} + +void rt_iounmap(volatile void *vaddr) +{ +} +#endif diff --git a/components/lwp/ioremap.h b/components/lwp/ioremap.h new file mode 100644 index 0000000000..a2d64523f1 --- /dev/null +++ b/components/lwp/ioremap.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-05-06 Jesven first version + */ +#ifndef __IOREMAP_H__ +#define __IOREMAP_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +void *rt_ioremap(void *paddr, size_t size); +void *rt_ioremap_nocache(void *paddr, size_t size); +void *rt_ioremap_cached (void *paddr, size_t size); +void rt_iounmap(volatile void *addr); + +#ifdef __cplusplus +} +#endif + +#endif /*__LWP_IOREMAP_H__*/ diff --git a/components/lwp/lwp.c b/components/lwp/lwp.c index 4b91538b68..c59d1c8261 100644 --- a/components/lwp/lwp.c +++ b/components/lwp/lwp.c @@ -7,375 +7,1374 @@ * Date Author Notes * 2006-03-12 Bernard first version * 2018-11-02 heyuanjie fix complie error in iar + * 2021-02-03 lizhirui add 64-bit arch support and riscv64 arch support + * 2021-08-26 linzhenxing add lwp_setcwd\lwp_getcwd */ -#include <rtthread.h> #include <rthw.h> +#include <rtthread.h> + #include <dfs_file.h> #include <unistd.h> +#include <stdio.h> /* rename() */ +#include <fcntl.h> #include <sys/stat.h> -#include <sys/statfs.h> +#include <sys/statfs.h> /* statfs() */ + +#include <lwp_elf.h> #ifndef RT_USING_DFS - #error "lwp need file system(RT_USING_DFS)" +#error "lwp need file system(RT_USING_DFS)" #endif #include "lwp.h" +#include "lwp_arch.h" +#include "lwp_arch_comm.h" +#include "console.h" -#define DBG_TAG "LWP" -#define DBG_LVL DBG_WARNING +#define DBG_TAG "LWP" +#define DBG_LVL DBG_WARNING #include <rtdbg.h> -extern void lwp_user_entry(void *args, const void *text, void *data); +#ifdef RT_USING_USERSPACE +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#endif /* end of RT_USING_USERSPACE */ + + +#ifndef O_DIRECTORY +#define O_DIRECTORY 0x200000 +#endif + +#ifndef O_BINARY +#define O_BINARY 0x10000 +#endif + + +static const char elf_magic[] = {0x7f, 'E', 'L', 'F'}; +#ifdef DFS_USING_WORKDIR +extern char working_directory[]; +#endif +static struct termios stdin_termios, old_stdin_termios; + +int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]); + +struct termios *get_old_termios(void) +{ + return &old_stdin_termios; +} + +void lwp_setcwd(char *buf) +{ + struct rt_lwp *lwp = RT_NULL; + + if(strlen(buf) >= DFS_PATH_MAX) + { + rt_kprintf("buf too long!\n"); + return ; + } + + lwp = (struct rt_lwp *)rt_thread_self()->lwp; + if (lwp) + { + rt_strncpy(lwp->working_directory, buf, DFS_PATH_MAX); + } + else + { + rt_strncpy(working_directory, buf, DFS_PATH_MAX); + } + + return ; +} + +char *lwp_getcwd(void) +{ + char *dir_buf = RT_NULL; + struct rt_lwp *lwp = RT_NULL; + + lwp = (struct rt_lwp *)rt_thread_self()->lwp; + if (lwp) + { + if(lwp->working_directory[0] != '/') + { + dir_buf = &working_directory[0]; + } + else + { + dir_buf = &lwp->working_directory[0]; + } + } + else + dir_buf = &working_directory[0]; + + return dir_buf; +} /** * RT-Thread light-weight process */ void lwp_set_kernel_sp(uint32_t *sp) { - struct rt_lwp *user_data; - user_data = (struct rt_lwp *)rt_thread_self()->lwp; - user_data->kernel_sp = sp; + rt_thread_self()->kernel_sp = (rt_uint32_t *)sp; } uint32_t *lwp_get_kernel_sp(void) { - struct rt_lwp *user_data; - user_data = (struct rt_lwp *)rt_thread_self()->lwp; - - return user_data->kernel_sp; +#ifdef RT_USING_USERSPACE + return (uint32_t *)rt_thread_self()->sp; +#else + uint32_t* kernel_sp; + extern rt_uint32_t rt_interrupt_from_thread; + extern rt_uint32_t rt_thread_switch_interrupt_flag; + if (rt_thread_switch_interrupt_flag) + { + kernel_sp = (uint32_t *)((rt_thread_t)rt_container_of(rt_interrupt_from_thread, struct rt_thread, sp))->kernel_sp; + } + else + { + kernel_sp = (uint32_t *)rt_thread_self()->kernel_sp; + } + return kernel_sp; +#endif } -static int lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv) +#ifdef RT_USING_USERSPACE +struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp) +{ + int size = sizeof(size_t) * 5; /* store argc, argv, envp, aux, NULL */ + int *args; + char *str; + char *str_k; + char **new_argve; + int i; + int len; + size_t *args_k; + struct process_aux *aux; + + for (i = 0; i < argc; i++) + { + size += (rt_strlen(argv[i]) + 1); + } + size += (sizeof(size_t) * argc); + + i = 0; + if (envp) + { + while (envp[i] != 0) + { + size += (rt_strlen(envp[i]) + 1); + size += sizeof(size_t); + i++; + } + } + + /* for aux */ + size += sizeof(struct process_aux); + + if (size > ARCH_PAGE_SIZE) + { + return RT_NULL; + } + + /* args = (int *)lwp_map_user(lwp, 0, size); */ + args = (int *)lwp_map_user(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE), size, 0); + if (args == RT_NULL) + { + return RT_NULL; + } + + args_k = (size_t *)rt_hw_mmu_v2p(&lwp->mmu_info, args); + args_k = (size_t *)((size_t)args_k - PV_OFFSET); + + /* argc, argv[], 0, envp[], 0 , aux[] */ + str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(size_t)); + str_k = (char *)((size_t)args_k + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(size_t)); + + new_argve = (char **)&args_k[1]; + args_k[0] = argc; + + for (i = 0; i < argc; i++) + { + len = rt_strlen(argv[i]) + 1; + new_argve[i] = str; + rt_memcpy(str_k, argv[i], len); + str += len; + str_k += len; + } + new_argve[i] = 0; + i++; + + new_argve[i] = 0; + if (envp) + { + int j; + + for (j = 0; envp[j] != 0; j++) + { + len = rt_strlen(envp[j]) + 1; + new_argve[i] = str; + rt_memcpy(str_k, envp[j], len); + str += len; + str_k += len; + i++; + } + new_argve[i] = 0; + } + i++; + + /* aux */ + aux = (struct process_aux *)(new_argve + i); + aux->item[0].key = AT_EXECFN; + aux->item[0].value = (size_t)(size_t)new_argve[0]; + i += AUX_ARRAY_ITEMS_NR * 2; + new_argve[i] = 0; + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, args_k, size); + + lwp->args = args; + + return aux; +} +#else +static struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp) { - int size = sizeof(int)*3; /* store argc, argv, NULL */ +#ifdef ARCH_MM_MMU + int size = sizeof(int) * 5; /* store argc, argv, envp, aux, NULL */ + struct process_aux *aux; +#else + int size = sizeof(int) * 4; /* store argc, argv, envp, NULL */ +#endif /* ARCH_MM_MMU */ int *args; char *str; - char **new_argv; + char **new_argve; int i; int len; - for (i = 0; i < argc; i ++) + for (i = 0; i < argc; i++) { size += (rt_strlen(argv[i]) + 1); } - size += (sizeof(int) * argc); + size += (sizeof(int) * argc); - args = (int*)rt_malloc(size); + i = 0; + if (envp) + { + while (envp[i] != 0) + { + size += (rt_strlen(envp[i]) + 1); + size += sizeof(int); + i++; + } + } + +#ifdef ARCH_MM_MMU + /* for aux */ + size += sizeof(struct process_aux); + + args = (int *)rt_malloc(size); if (args == RT_NULL) - return -1; + { + return RT_NULL; + } - str = (char*)((int)args + (argc + 3) * sizeof(int)); - new_argv = (char**)&args[2]; + /* argc, argv[], 0, envp[], 0 */ + str = (char *)((size_t)args + (argc + 2 + i + 1 + AUX_ARRAY_ITEMS_NR * 2 + 1) * sizeof(int)); +#else + args = (int *)rt_malloc(size); + if (args == RT_NULL) + { + return RT_NULL; + } + str = (char*)((int)args + (argc + 2 + i + 1) * sizeof(int)); +#endif /* ARCH_MM_MMU */ + + new_argve = (char **)&args[1]; args[0] = argc; - args[1] = (int)new_argv; - for (i = 0; i < argc; i ++) + for (i = 0; i < argc; i++) { len = rt_strlen(argv[i]) + 1; - new_argv[i] = str; + new_argve[i] = str; rt_memcpy(str, argv[i], len); str += len; } - new_argv[i] = 0; + new_argve[i] = 0; + i++; + + new_argve[i] = 0; + if (envp) + { + int j; + for (j = 0; envp[j] != 0; j++) + { + len = rt_strlen(envp[j]) + 1; + new_argve[i] = str; + rt_memcpy(str, envp[j], len); + str += len; + i++; + } + new_argve[i] = 0; + } +#ifdef ARCH_MM_MMU + /* aux */ + aux = (struct process_aux *)(new_argve + i); + aux->item[0].key = AT_EXECFN; + aux->item[0].value = (uint32_t)(size_t)new_argve[0]; + i += AUX_ARRAY_ITEMS_NR * 2; + new_argve[i] = 0; + lwp->args = args; - return 0; + return aux; +#else + lwp->args = args; + lwp->args_length = size; + + return (struct process_aux *)(new_argve + i); +#endif /* ARCH_MM_MMU */ } +#endif + +#ifdef ARCH_MM_MMU +#define check_off(voff, vlen) \ + do \ + { \ + if (voff > vlen) \ + { \ + result = -RT_ERROR; \ + goto _exit; \ + } \ + } while (0) + +#define check_read(vrlen, vrlen_want) \ + do \ + { \ + if (vrlen < vrlen_want) \ + { \ + result = -RT_ERROR; \ + goto _exit; \ + } \ + } while (0) -static int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size) +static size_t load_fread(void *ptr, size_t size, size_t nmemb, int fd) { - int fd; - uint8_t *ptr; - int result = RT_EOK; - int nbytes; - struct lwp_header header; - struct lwp_chunk chunk; + size_t read_block = 0; - /* check file name */ - RT_ASSERT(filename != RT_NULL); - /* check lwp control block */ - RT_ASSERT(lwp != RT_NULL); + while (nmemb) + { + size_t count; - if (load_addr != RT_NULL) + count = read(fd, ptr, size * nmemb) / size; + if (count < nmemb) + { + LOG_E("ERROR: file size error!"); + break; + } + + ptr = (void *)((uint8_t *)ptr + (count * size)); + nmemb -= count; + read_block += count; + } + + return read_block; +} + +typedef struct +{ + Elf_Word st_name; + Elf_Addr st_value; + Elf_Word st_size; + unsigned char st_info; + unsigned char st_other; + Elf_Half st_shndx; +} Elf_sym; + +#ifdef RT_USING_USERSPACE +struct map_range +{ + void *start; + size_t size; +}; + +static void expand_map_range(struct map_range *m, void *start, size_t size) +{ + if (!m->start) { - lwp->lwp_type = LWP_TYPE_FIX_ADDR; - ptr = load_addr; + m->start = start; + m->size = size; } else { - lwp->lwp_type = LWP_TYPE_DYN_ADDR; - ptr = RT_NULL; + void *end = (void *)((char*)start + size); + void *mend = (void *)((char*)m->start + m->size); + + if (m->start > start) + { + m->start = start; + } + if (mend < end) + { + mend = end; + } + m->size = (char *)mend - (char *)m->start; } +} - /* open lwp */ - fd = open(filename, 0, O_RDONLY); - if (fd < 0) +static int map_range_ckeck(struct map_range *m1, struct map_range *m2) +{ + void *m1_start = (void *)((size_t)m1->start & ~ARCH_PAGE_MASK); + void *m1_end = (void *)((((size_t)m1->start + m1->size) + ARCH_PAGE_MASK) & ~ARCH_PAGE_MASK); + void *m2_start = (void *)((size_t)m2->start & ~ARCH_PAGE_MASK); + void *m2_end = (void *)((((size_t)m2->start + m2->size) + ARCH_PAGE_MASK) & ~ARCH_PAGE_MASK); + + if (m1->size) + { + if (m1_start < (void *)USER_LOAD_VADDR) + { + return -1; + } + if (m1_start > (void *)USER_STACK_VSTART) + { + return -1; + } + if (m1_end < (void *)USER_LOAD_VADDR) + { + return -1; + } + if (m1_end > (void *)USER_STACK_VSTART) + { + return -1; + } + } + if (m2->size) + { + if (m2_start < (void *)USER_LOAD_VADDR) + { + return -1; + } + if (m2_start > (void *)USER_STACK_VSTART) + { + return -1; + } + if (m2_end < (void *)USER_LOAD_VADDR) + { + return -1; + } + if (m2_end > (void *)USER_STACK_VSTART) + { + return -1; + } + } + + if ((m1->size != 0) && (m2->size != 0)) { - dbg_log(DBG_ERROR, "open file:%s failed!\n", filename); - result = -RT_ENOSYS; - goto _exit; + if (m1_start < m2_start) + { + if (m1_end > m2_start) + { + return -1; + } + } + else /* m2_start <= m1_start */ + { + if (m2_end > m1_start) + { + return -1; + } + } } + return 0; +} +#endif - /* read lwp header */ - nbytes = read(fd, &header, sizeof(struct lwp_header)); - if (nbytes != sizeof(struct lwp_header)) +static int load_elf(int fd, int len, struct rt_lwp *lwp, uint8_t *load_addr, struct process_aux *aux) +{ + uint32_t i; + uint32_t off = 0; + size_t load_off = 0; + char *p_section_str = 0; + Elf_sym *dynsym = 0; + Elf_Ehdr eheader; + Elf_Phdr pheader; + Elf_Shdr sheader; + int result = RT_EOK; + uint32_t magic; + size_t read_len; + void *got_start = 0; + size_t got_size = 0; + void *rel_dyn_start = 0; + size_t rel_dyn_size = 0; + size_t dynsym_off = 0; + size_t dynsym_size = 0; +#ifdef RT_USING_USERSPACE + struct map_range user_area[2] = {{NULL, 0}, {NULL, 0}}; /* 0 is text, 1 is data */ + void *pa, *va; + void *va_self; + + rt_mmu_info *m_info = &lwp->mmu_info; +#endif + + if (len < sizeof eheader) { - dbg_log(DBG_ERROR, "read lwp header return error size: %d!\n", nbytes); - result = -RT_EIO; - goto _exit; + LOG_E("len < sizeof eheader!"); + return -RT_ERROR; } - /* check file header */ - if (header.magic != LWP_MAGIC) + lseek(fd, 0, SEEK_SET); + read_len = load_fread(&magic, 1, sizeof magic, fd); + check_read(read_len, sizeof magic); + + if (memcmp(elf_magic, &magic, 4) != 0) { - dbg_log(DBG_ERROR, "erro header magic number: 0x%02X\n", header.magic); - result = -RT_EINVAL; - goto _exit; + LOG_E("elf_magic not same, magic:0x%x!", magic); + return -RT_ERROR; + } + + lseek(fd, off, SEEK_SET); + read_len = load_fread(&eheader, 1, sizeof eheader, fd); + check_read(read_len, sizeof eheader); + +#ifndef ARCH_CPU_64BIT + if (eheader.e_ident[4] != 1) + { /* not 32bit */ + LOG_E("elf not 32bit, %d!", eheader.e_ident[4]); + return -RT_ERROR; + } +#else + if (eheader.e_ident[4] != 2) + { /* not 64bit */ + LOG_E("elf not 64bit, %d!", eheader.e_ident[4]); + return -RT_ERROR; + } +#endif + + if (eheader.e_ident[6] != 1) + { /* ver not 1 */ + LOG_E("elf Version not 1,ver:%d!", eheader.e_ident[6]); + return -RT_ERROR; } - /* read text chunk info */ - nbytes = read(fd, &chunk, sizeof(struct lwp_chunk)); - if (nbytes != sizeof(struct lwp_chunk)) + if ((eheader.e_type != ET_DYN) +#ifdef RT_USING_USERSPACE + && (eheader.e_type != ET_EXEC) +#endif + ) { - dbg_log(DBG_ERROR, "read text chunk info failed!\n"); - result = -RT_EIO; - goto _exit; + /* not pie or exec elf */ + LOG_E("elf type not pie or exec, type:%d!", eheader.e_type); + return -RT_ERROR; } - dbg_log(DBG_LOG, "chunk name: %s, total len %d, data %d, need space %d!\n", - "text", /*chunk.name*/ chunk.total_len, chunk.data_len, chunk.data_len_space); +#ifdef RT_USING_USERSPACE + { + off = eheader.e_phoff; + for (i = 0; i < eheader.e_phnum; i++, off += sizeof pheader) + { + check_off(off, len); + lseek(fd, off, SEEK_SET); + read_len = load_fread(&pheader, 1, sizeof pheader, fd); + check_read(read_len, sizeof pheader); - /* load text */ + if (pheader.p_type == PT_DYNAMIC) + { + /* load ld.so */ + return 1; /* 1 means dynamic */ + } + } + } +#endif + + if (eheader.e_entry != 0) { - lwp->text_size = RT_ALIGN(chunk.data_len_space, 4); - if (load_addr) - lwp->text_entry = ptr; - else + if ((eheader.e_entry != USER_LOAD_VADDR) + && (eheader.e_entry != LDSO_LOAD_VADDR)) { -#ifdef RT_USING_CACHE - lwp->text_entry = (rt_uint8_t *)rt_malloc_align(lwp->text_size, RT_CPU_CACHE_LINE_SZ); + /* the entry is invalidate */ + LOG_E("elf entry is invalidate, entry:0x%x!", eheader.e_entry); + return -RT_ERROR; + } + } + + { /* load aux */ + uint8_t *process_header; + size_t process_header_size; + + off = eheader.e_phoff; + process_header_size = eheader.e_phnum * sizeof pheader; +#ifdef RT_USING_USERSPACE + if (process_header_size > ARCH_PAGE_SIZE - sizeof(char[16])) + { + LOG_E("process_header_size too big, size:0x%x!", process_header_size); + return -RT_ERROR; + } + va = (uint8_t *)lwp_map_user(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE * 2), process_header_size, 0); + if (!va) + { + LOG_E("lwp map user failed!"); + return -RT_ERROR; + } + pa = rt_hw_mmu_v2p(m_info, va); + process_header = (uint8_t *)pa - PV_OFFSET; +#else + process_header = (uint8_t *)rt_malloc(process_header_size + sizeof(char[16])); + if (!process_header) + { + LOG_E("process_header malloc failed, size:0x%x!", process_header_size + sizeof(char[16])); + return -RT_ERROR; + } +#endif + check_off(off, len); + lseek(fd, off, SEEK_SET); + read_len = load_fread(process_header, 1, process_header_size, fd); + check_read(read_len, process_header_size); +#ifdef RT_USING_USERSPACE + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, process_header, process_header_size); +#endif + + aux->item[1].key = AT_PAGESZ; +#ifdef RT_USING_USERSPACE + aux->item[1].value = ARCH_PAGE_SIZE; #else - lwp->text_entry = (rt_uint8_t *)rt_malloc(lwp->text_size); + aux->item[1].value = RT_MM_PAGE_SIZE; #endif + aux->item[2].key = AT_RANDOM; + { + uint32_t random_value = rt_tick_get(); + uint8_t *random; +#ifdef RT_USING_USERSPACE + uint8_t *krandom; + + random = (uint8_t *)(USER_VADDR_TOP - ARCH_PAGE_SIZE - sizeof(char[16])); - if (lwp->text_entry == RT_NULL) + krandom = (uint8_t *)rt_hw_mmu_v2p(m_info, random); + krandom = (uint8_t *)krandom - PV_OFFSET; + rt_memcpy(krandom, &random_value, sizeof random_value); +#else + random = (uint8_t *)(process_header + process_header_size); + rt_memcpy(random, &random_value, sizeof random_value); +#endif + aux->item[2].value = (size_t)random; + } + aux->item[3].key = AT_PHDR; +#ifdef RT_USING_USERSPACE + aux->item[3].value = (size_t)va; +#else + aux->item[3].value = (size_t)process_header; +#endif + aux->item[4].key = AT_PHNUM; + aux->item[4].value = eheader.e_phnum; + aux->item[5].key = AT_PHENT; + aux->item[5].value = sizeof pheader; +#ifdef RT_USING_USERSPACE + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, aux, sizeof *aux); +#endif + } + + if (load_addr) + { + load_off = (size_t)load_addr; + } +#ifdef RT_USING_USERSPACE + else + { + /* map user */ + off = eheader.e_shoff; + for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader) + { + check_off(off, len); + lseek(fd, off, SEEK_SET); + read_len = load_fread(&sheader, 1, sizeof sheader, fd); + check_read(read_len, sizeof sheader); + + if ((sheader.sh_flags & SHF_ALLOC) == 0) { - dbg_log(DBG_ERROR, "alloc text memory faild!\n"); - result = -RT_ENOMEM; - goto _exit; + continue; } - else + + switch (sheader.sh_type) { - dbg_log(DBG_LOG, "lwp text malloc : %p, size: %d!\n", lwp->text_entry, lwp->text_size); + case SHT_PROGBITS: + if ((sheader.sh_flags & SHF_WRITE) == 0) + { + expand_map_range(&user_area[0], (void *)sheader.sh_addr, sheader.sh_size); + } + else + { + expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size); + } + break; + case SHT_NOBITS: + expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size); + break; + default: + expand_map_range(&user_area[1], (void *)sheader.sh_addr, sheader.sh_size); + break; } } - dbg_log(DBG_INFO, "load text %d => (0x%08x, 0x%08x)\n", lwp->text_size, (uint32_t)lwp->text_entry, (uint32_t)lwp->text_entry + lwp->text_size); - nbytes = read(fd, lwp->text_entry, chunk.data_len); - if (nbytes != chunk.data_len) + if (user_area[0].size == 0) { - dbg_log(DBG_ERROR, "read text region from file failed!\n"); - result = -RT_EIO; + /* no code */ + result = -RT_ERROR; goto _exit; } -#ifdef RT_USING_CACHE - else + + if (user_area[0].start == NULL) { - rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lwp->text_entry, lwp->text_size); - rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, lwp->text_entry, lwp->text_size); + /* DYN */ + load_off = USER_LOAD_VADDR; + user_area[0].start = (void *)((char*)user_area[0].start + load_off); + user_area[1].start = (void *)((char*)user_area[1].start + load_off); } -#endif - if (ptr != RT_NULL) ptr += nbytes; + if (map_range_ckeck(&user_area[0], &user_area[1]) != 0) + { + result = -RT_ERROR; + goto _exit; + } - /* skip text hole */ - if ((chunk.total_len - sizeof(struct lwp_chunk) - chunk.data_len)) + /* text and data */ + for (i = 0; i < 2; i++) { - dbg_log(DBG_LOG, "skip text hole %d!\n", (chunk.total_len - sizeof(struct lwp_chunk) - chunk.data_len)); - lseek(fd, (chunk.total_len - sizeof(struct lwp_chunk) - chunk.data_len), SEEK_CUR); + if (user_area[i].size != 0) + { + va = lwp_map_user(lwp, user_area[i].start, user_area[i].size, (int)(i == 0)); + if (!va || (va != user_area[i].start)) + { + result = -RT_ERROR; + goto _exit; + } + } } + lwp->text_size = user_area[0].size; } - - /* load data */ - nbytes = read(fd, &chunk, sizeof(struct lwp_chunk)); - if (nbytes != sizeof(struct lwp_chunk)) +#else + else { - dbg_log(DBG_ERROR, "read data chunk info failed!\n"); - result = -RT_EIO; - goto _exit; - } - - dbg_log(DBG_LOG, "chunk name: %s, total len %d, data %d, need space %d!\n", - chunk.name, chunk.total_len, chunk.data_len, chunk.data_len_space); + size_t start = -1UL; + size_t end = 0UL; + size_t total_size; - { - lwp->data_size = RT_ALIGN(chunk.data_len_space, 4); - if (load_addr) - lwp->data = ptr; - else + off = eheader.e_shoff; + for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader) { - lwp->data = rt_malloc(lwp->data_size); - if (lwp->data == RT_NULL) + check_off(off, len); + lseek(fd, off, SEEK_SET); + read_len = load_fread(&sheader, 1, sizeof sheader, fd); + check_read(read_len, sizeof sheader); + + if ((sheader.sh_flags & SHF_ALLOC) == 0) { - dbg_log(DBG_ERROR, "alloc data memory faild!\n"); - result = -RT_ENOMEM; - goto _exit; + continue; } - else + + switch (sheader.sh_type) { - dbg_log(DBG_LOG, "lwp data malloc : %p, size: %d!\n", lwp->data, lwp->data_size); - rt_memset(lwp->data, 0, lwp->data_size); + case SHT_PROGBITS: + case SHT_NOBITS: + if (start > sheader.sh_addr) + { + start = sheader.sh_addr; + } + if (sheader.sh_addr + sheader.sh_size > end) + { + end = sheader.sh_addr + sheader.sh_size; + } + break; + default: + break; } } - dbg_log(DBG_INFO, "load data %d => (0x%08x, 0x%08x)\n", lwp->data_size, (uint32_t)lwp->data, (uint32_t)lwp->data + lwp->data_size); - nbytes = read(fd, lwp->data, chunk.data_len); - if (nbytes != chunk.data_len) + total_size = end - start; + +#ifdef RT_USING_CACHE + load_off = (size_t)rt_malloc_align(total_size, RT_CPU_CACHE_LINE_SZ); +#else + load_off = (size_t)rt_malloc(total_size); +#endif + if (load_off == 0) { - dbg_log(DBG_ERROR, "read data region from file failed!\n"); - result = -RT_ERROR; + LOG_E("alloc text memory faild!"); + result = -RT_ENOMEM; goto _exit; } + else + { + LOG_D("lwp text malloc : %p, size: %d!", (void *)load_off, lwp->text_size); + } + lwp->load_off = load_off; /* for free */ + lwp->text_size = total_size; } +#endif + lwp->text_entry = (void *)(eheader.e_entry + load_off); -_exit: - if (fd >= 0) - close(fd); - - if (result != RT_EOK) + off = eheader.e_phoff; + for (i = 0; i < eheader.e_phnum; i++, off += sizeof pheader) { - if (lwp->lwp_type == LWP_TYPE_DYN_ADDR) + check_off(off, len); + lseek(fd, off, SEEK_SET); + read_len = load_fread(&pheader, 1, sizeof pheader, fd); + check_read(read_len, sizeof pheader); + + if (pheader.p_type == PT_LOAD) { - dbg_log(DBG_ERROR, "lwp dynamic load faild, %d\n", result); - if (lwp->text_entry) + if (pheader.p_filesz > pheader.p_memsz) { - dbg_log(DBG_LOG, "lwp text free: %p\n", lwp->text_entry); -#ifdef RT_USING_CACHE - rt_free_align(lwp->text_entry); + LOG_E("pheader.p_filesz > pheader.p_memsz, p_filesz:0x%x;p_memsz:0x%x!", pheader.p_filesz, pheader.p_memsz); + return -RT_ERROR; + } + + check_off(pheader.p_offset, len); + lseek(fd, pheader.p_offset, SEEK_SET); +#ifdef RT_USING_USERSPACE + { + uint32_t size = pheader.p_filesz; + size_t tmp_len = 0; + + va = (void *)(pheader.p_vaddr + load_addr); + read_len = 0; + while (size) + { + pa = rt_hw_mmu_v2p(m_info, va); + va_self = (void *)((char *)pa - PV_OFFSET); + LOG_D("va_self = %p pa = %p", va_self, pa); + tmp_len = (size < ARCH_PAGE_SIZE) ? size : ARCH_PAGE_SIZE; + tmp_len = load_fread(va_self, 1, tmp_len, fd); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, va_self, tmp_len); + read_len += tmp_len; + size -= tmp_len; + va = (void *)((char *)va + ARCH_PAGE_SIZE); + } + } +#else + read_len = load_fread((void*)(pheader.p_vaddr + load_off), 1, pheader.p_filesz, fd); +#endif + check_read(read_len, pheader.p_filesz); + + if (pheader.p_filesz < pheader.p_memsz) + { +#ifdef RT_USING_USERSPACE + uint32_t size = pheader.p_memsz - pheader.p_filesz; + uint32_t size_s; + uint32_t off; + + off = pheader.p_filesz & ARCH_PAGE_MASK; + va = (void *)((pheader.p_vaddr + pheader.p_filesz + load_off) & ~ARCH_PAGE_MASK); + while (size) + { + size_s = (size < ARCH_PAGE_SIZE - off) ? size : ARCH_PAGE_SIZE - off; + pa = rt_hw_mmu_v2p(m_info, va); + va_self = (void *)((char *)pa - PV_OFFSET); + memset((void *)((char *)va_self + off), 0, size_s); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, (void *)((char *)va_self + off), size_s); + off = 0; + size -= size_s; + va = (void *)((char *)va + ARCH_PAGE_SIZE); + } #else - rt_free(lwp->text_entry); + memset((uint8_t *)pheader.p_vaddr + pheader.p_filesz + load_off, 0, (size_t)(pheader.p_memsz - pheader.p_filesz)); #endif } - if (lwp->data) + } + } + + /* relocate */ + if (eheader.e_type == ET_DYN) + { + /* section info */ + off = eheader.e_shoff; + /* find section string table */ + check_off(off, len); + lseek(fd, off + (sizeof sheader) * eheader.e_shstrndx, SEEK_SET); + read_len = load_fread(&sheader, 1, sizeof sheader, fd); + check_read(read_len, sizeof sheader); + + p_section_str = (char *)rt_malloc(sheader.sh_size); + if (!p_section_str) + { + LOG_E("out of memory!"); + result = -ENOMEM; + goto _exit; + } + + check_off(sheader.sh_offset, len); + lseek(fd, sheader.sh_offset, SEEK_SET); + read_len = load_fread(p_section_str, 1, sheader.sh_size, fd); + check_read(read_len, sheader.sh_size); + + check_off(off, len); + lseek(fd, off, SEEK_SET); + for (i = 0; i < eheader.e_shnum; i++, off += sizeof sheader) + { + read_len = load_fread(&sheader, 1, sizeof sheader, fd); + check_read(read_len, sizeof sheader); + + if (strcmp(p_section_str + sheader.sh_name, ".got") == 0) + { + got_start = (void *)((uint8_t *)sheader.sh_addr + load_off); + got_size = (size_t)sheader.sh_size; + } + else if (strcmp(p_section_str + sheader.sh_name, ".rel.dyn") == 0) + { + rel_dyn_start = (void *)((uint8_t *)sheader.sh_addr + load_off); + rel_dyn_size = (size_t)sheader.sh_size; + } + else if (strcmp(p_section_str + sheader.sh_name, ".dynsym") == 0) { - dbg_log(DBG_LOG, "lwp data free: %p\n", lwp->data); - rt_free(lwp->data); + dynsym_off = (size_t)sheader.sh_offset; + dynsym_size = (size_t)sheader.sh_size; } } + /* reloc */ + if (dynsym_size) + { + dynsym = rt_malloc(dynsym_size); + if (!dynsym) + { + LOG_E("ERROR: Malloc error!"); + result = -ENOMEM; + goto _exit; + } + check_off(dynsym_off, len); + lseek(fd, dynsym_off, SEEK_SET); + read_len = load_fread(dynsym, 1, dynsym_size, fd); + check_read(read_len, dynsym_size); + } +#ifdef RT_USING_USERSPACE + arch_elf_reloc(m_info, (void *)load_off, rel_dyn_start, rel_dyn_size, got_start, got_size, dynsym); +#else + arch_elf_reloc((void *)load_off, rel_dyn_start, rel_dyn_size, got_start, got_size, dynsym); + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lwp->text_entry, lwp->text_size); + rt_hw_cpu_icache_ops(RT_HW_CACHE_INVALIDATE, lwp->text_entry, lwp->text_size); +#endif } + LOG_D("lwp->text_entry = 0x%p", lwp->text_entry); + LOG_D("lwp->text_size = 0x%p", lwp->text_size); +_exit: + if (dynsym) + { + rt_free(dynsym); + } + if (p_section_str) + { + rt_free(p_section_str); + } + if (result != RT_EOK) + { + LOG_E("lwp load faild, %d", result); + } return result; } +#endif /* ARCH_MM_MMU */ -static void lwp_cleanup(struct rt_thread *tid) +RT_WEAK int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux) { + uint8_t *ptr; + int ret = -1; + int len; + int fd = -1; + + /* check file name */ + RT_ASSERT(filename != RT_NULL); + /* check lwp control block */ + RT_ASSERT(lwp != RT_NULL); + + /* copy file name to process name */ + rt_strncpy(lwp->cmd, filename, RT_NAME_MAX); + + if (load_addr != RT_NULL) + { + lwp->lwp_type = LWP_TYPE_FIX_ADDR; + ptr = load_addr; + } + else + { + lwp->lwp_type = LWP_TYPE_DYN_ADDR; + ptr = RT_NULL; + } + + fd = open(filename, O_BINARY | O_RDONLY, 0); + if (fd < 0) + { + LOG_E("ERROR: Can't open elf file %s!", filename); + goto out; + } + len = lseek(fd, 0, SEEK_END); + if (len < 0) + { + LOG_E("ERROR: File %s size error!", filename); + goto out; + } + + lseek(fd, 0, SEEK_SET); + + ret = load_elf(fd, len, lwp, ptr, aux); + if ((ret != RT_EOK) && (ret != 1)) + { + LOG_E("lwp load ret = %d", ret); + } + +out: + if (fd > 0) + { + close(fd); + } + return ret; +} + +void lwp_cleanup(struct rt_thread *tid) +{ + rt_base_t level; struct rt_lwp *lwp; + struct tty_node *tty_head = RT_NULL; + + if (tid == NULL) + { + return; + } - dbg_log(DBG_INFO, "thread: %s, stack_addr: %08X\n", tid->name, tid->stack_addr); + LOG_I("cleanup thread: %s, stack_addr: %08X", tid->name, tid->stack_addr); + level = rt_hw_interrupt_disable(); lwp = (struct rt_lwp *)tid->lwp; - if (lwp->lwp_type == LWP_TYPE_DYN_ADDR) + lwp_tid_put(tid->tid); + rt_list_remove(&tid->sibling); + rt_hw_interrupt_enable(level); + if (lwp->tty != RT_NULL) { - dbg_log(DBG_INFO, "dynamic lwp\n"); - if (lwp->text_entry) - { - dbg_log(DBG_LOG, "lwp text free: %p\n", lwp->text_entry); -#ifdef RT_USING_CACHE - rt_free_align(lwp->text_entry); -#else - rt_free(lwp->text_entry); -#endif - } - if (lwp->data) + tty_head = lwp->tty->head; + } + if (!lwp_ref_dec(lwp)) + { + if (tty_head) { - dbg_log(DBG_LOG, "lwp data free: %p\n", lwp->data); - rt_free(lwp->data); + tty_pop(&tty_head, lwp); } } + + return; +} - dbg_log(DBG_LOG, "lwp free memory pages\n"); - rt_lwp_mem_deinit(lwp); - - /* cleanup fd table */ - rt_free(lwp->fdt.fds); - rt_free(lwp->args); +static void lwp_copy_stdio_fdt(struct rt_lwp *lwp) +{ + struct dfs_fd *d; + struct dfs_fdtable *lwp_fdt; - dbg_log(DBG_LOG, "lwp free: %p\n", lwp); - rt_free(lwp); + lwp_fdt = &lwp->fdt; + /* init 4 fds */ + lwp_fdt->fds = rt_calloc(4, sizeof(void *)); + if (lwp_fdt->fds) + { + lwp_fdt->maxfd = 4; + d = fd_get(0); + fd_associate(lwp_fdt, 0, d); + d = fd_get(1); + fd_associate(lwp_fdt, 1, d); + d = fd_get(2); + fd_associate(lwp_fdt, 2, d); + } - /* TODO: cleanup fd table */ + return; } -static void lwp_thread(void *parameter) +static void _lwp_thread_entry(void *parameter) { rt_thread_t tid; struct rt_lwp *lwp; - lwp = (struct rt_lwp *)parameter; - rt_lwp_mem_init(lwp); tid = rt_thread_self(); - tid->lwp = lwp; + lwp = (struct rt_lwp *)tid->lwp; tid->cleanup = lwp_cleanup; + tid->user_stack = RT_NULL; + + if (lwp->debug) + { + lwp->bak_first_ins = *(uint32_t *)lwp->text_entry; + *(uint32_t *)lwp->text_entry = dbg_get_ins(); + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, lwp->text_entry, sizeof(uint32_t)); + icache_invalid_all(); + } - lwp_user_entry(lwp->args, lwp->text_entry, lwp->data); +#ifdef ARCH_MM_MMU + arch_start_umode(lwp->args, lwp->text_entry, (void *)USER_STACK_VEND, tid->stack_addr + tid->stack_size); +#else + arch_start_umode(lwp->args, lwp->text_entry, lwp->data_entry, (void *)((uint32_t)lwp->data_entry + lwp->data_size)); +#endif /* ARCH_MM_MMU */ } -struct rt_lwp *rt_lwp_self(void) +struct rt_lwp *lwp_self(void) { - return (struct rt_lwp *)rt_thread_self()->lwp; + rt_thread_t tid; + + tid = rt_thread_self(); + if (tid) + { + return (struct rt_lwp *)tid->lwp; + } + + return RT_NULL; } -int exec(char *filename, int argc, char **argv) +pid_t lwp_execve(char *filename, int debug, int argc, char **argv, char **envp) { - struct rt_lwp *lwp; int result; + rt_base_t level; + struct rt_lwp *lwp; + char *thread_name; + char *argv_last = argv[argc - 1]; + int bg = 0; + struct process_aux *aux; + int tid = 0; + int ret; if (filename == RT_NULL) + { return -RT_ERROR; + } + + lwp = lwp_new(); - lwp = (struct rt_lwp *)rt_malloc(sizeof(struct rt_lwp)); if (lwp == RT_NULL) { dbg_log(DBG_ERROR, "lwp struct out of memory!\n"); return -RT_ENOMEM; } - dbg_log(DBG_INFO, "lwp malloc : %p, size: %d!\n", lwp, sizeof(struct rt_lwp)); + LOG_D("lwp malloc : %p, size: %d!", lwp, sizeof(struct rt_lwp)); + + if ((tid = lwp_tid_get()) == 0) + { + lwp_ref_dec(lwp); + return -ENOMEM; + } +#ifdef RT_USING_USERSPACE + if (lwp_user_space_init(lwp) != 0) + { + lwp_tid_put(tid); + lwp_ref_dec(lwp); + return -ENOMEM; + } +#endif + + if (argv_last[0] == '&' && argv_last[1] == '\0') + { + argc--; + bg = 1; + } - rt_memset(lwp, 0, sizeof(*lwp)); - if (lwp_argscopy(lwp, argc, argv) != 0) + if ((aux = lwp_argscopy(lwp, argc, argv, envp)) == RT_NULL) { - rt_free(lwp); + lwp_tid_put(tid); + lwp_ref_dec(lwp); return -ENOMEM; } - result = lwp_load(filename, lwp, RT_NULL, 0); + result = lwp_load(filename, lwp, RT_NULL, 0, aux); +#ifdef ARCH_MM_MMU + if (result == 1) + { + /* dynmaic */ + lwp_unmap_user(lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE)); + result = load_ldso(lwp, filename, argv, envp); + } +#endif /* ARCH_MM_MMU */ if (result == RT_EOK) { - rt_thread_t tid; + rt_thread_t thread = RT_NULL; + rt_uint32_t priority = 25, tick = 200; - tid = rt_thread_create("user", lwp_thread, (void *)lwp, - 1024 * 4, 2, 200); - if (tid != RT_NULL) + lwp_copy_stdio_fdt(lwp); + + /* obtain the base name */ + thread_name = strrchr(filename, '/'); + thread_name = thread_name ? thread_name + 1 : filename; +#ifndef ARCH_MM_MMU + struct lwp_app_head *app_head = lwp->text_entry; + if (app_head->priority) { - dbg_log(DBG_LOG, "lwp kernel => (0x%08x, 0x%08x)\n", (rt_uint32_t)tid->stack_addr, (rt_uint32_t)tid->stack_addr + tid->stack_size); - rt_thread_startup(tid); - return RT_EOK; + priority = app_head->priority; } - else + if (app_head->tick) { -#ifdef RT_USING_CACHE - rt_free_align(lwp->text_entry); -#else - rt_free(lwp->text_entry); -#endif - rt_free(lwp->data); + tick = app_head->tick; + } +#endif /* not defined ARCH_MM_MMU */ + thread = rt_thread_create(thread_name, _lwp_thread_entry, RT_NULL, + LWP_TASK_STACK_SIZE, priority, tick); + if (thread != RT_NULL) + { + struct rt_lwp *self_lwp; + + thread->tid = tid; + lwp_tid_set_thread(tid, thread); + LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_size_t)thread->stack_addr, + (rt_size_t)thread->stack_addr + thread->stack_size); + level = rt_hw_interrupt_disable(); + self_lwp = lwp_self(); + if (self_lwp) + { + //lwp->tgroup_leader = &thread; //add thread group leader for lwp + lwp->__pgrp = tid; + lwp->session = self_lwp->session; + /* lwp add to children link */ + lwp->sibling = self_lwp->first_child; + self_lwp->first_child = lwp; + lwp->parent = self_lwp; + } + else + { + //lwp->tgroup_leader = &thread; //add thread group leader for lwp + lwp->__pgrp = tid; + } + if (!bg) + { + if (lwp->session == -1) + { + struct tty_struct *tty = RT_NULL; + struct rt_lwp *old_lwp; + tty = (struct tty_struct *)console_tty_get(); + old_lwp = tty->foreground; + rt_mutex_take(&tty->lock, RT_WAITING_FOREVER); + ret = tty_push(&tty->head, old_lwp); + rt_mutex_release(&tty->lock); + if (ret < 0) + { + lwp_tid_put(tid); + lwp_ref_dec(lwp); + LOG_E("malloc fail!\n"); + return -ENOMEM; + } + + lwp->tty = tty; + lwp->tty->pgrp = lwp->__pgrp; + lwp->tty->session = lwp->session; + lwp->tty->foreground = lwp; + tcgetattr(1, &stdin_termios); + old_stdin_termios = stdin_termios; + stdin_termios.c_lflag |= ICANON | ECHO | ECHOCTL; + tcsetattr(1, 0, &stdin_termios); + } + else + { + if (self_lwp != RT_NULL) + { + rt_mutex_take(&self_lwp->tty->lock, RT_WAITING_FOREVER); + ret = tty_push(&self_lwp->tty->head, self_lwp); + rt_mutex_release(&self_lwp->tty->lock); + if (ret < 0) + { + lwp_tid_put(tid); + lwp_ref_dec(lwp); + LOG_E("malloc fail!\n"); + return -ENOMEM; + } + + lwp->tty = self_lwp->tty; + lwp->tty->pgrp = lwp->__pgrp; + lwp->tty->session = lwp->session; + lwp->tty->foreground = lwp; + } + else + { + lwp->tty = RT_NULL; + } + + } + } + thread->lwp = lwp; +#ifndef ARCH_MM_MMU + struct lwp_app_head *app_head = (struct lwp_app_head*)lwp->text_entry; + thread->user_stack = app_head->stack_offset ? + (void *)(app_head->stack_offset - + app_head->data_offset + + (uint32_t)lwp->data_entry) : RT_NULL; + thread->user_stack_size = app_head->stack_size; + /* init data area */ + rt_memset(lwp->data_entry, 0, lwp->data_size); + /* init user stack */ + rt_memset(thread->user_stack, '#', thread->user_stack_size); +#endif /* not defined ARCH_MM_MMU */ + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + + if (debug && rt_dbg_ops) + { + lwp->debug = debug; + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void*)0); + } + rt_hw_interrupt_enable(level); + + rt_thread_startup(thread); + return lwp_to_pid(lwp); } } - rt_free(lwp->args); - rt_free(lwp); + lwp_tid_put(tid); + lwp_ref_dec(lwp); return -RT_ERROR; } + +#ifdef RT_USING_MUSL +extern char **__environ; +#else +char __environ = 0; +#endif + +pid_t exec(char *filename, int debug, int argc, char **argv) +{ + return lwp_execve(filename, debug, argc, argv, __environ); +} + +#ifdef ARCH_MM_MMU +void lwp_user_setting_save(rt_thread_t thread) +{ + if (thread) + { + thread->thread_idr = arch_get_tidr(); + } +} + +void lwp_user_setting_restore(rt_thread_t thread) +{ + if (!thread) + { + return; + } +#if !defined(ARCH_RISCV64) + /* tidr will be set in RESTORE_ALL in risc-v */ + arch_set_tidr(thread->thread_idr); +#endif + + if (rt_dbg_ops) + { + struct rt_lwp *l = (struct rt_lwp *)thread->lwp; + + if (l != 0) + { + rt_hw_set_process_id((size_t)l->pid); + } + else + { + rt_hw_set_process_id(0); + } + if (l && l->debug) + { + uint32_t step_type = 0; + + step_type = dbg_step_type(); + + if ((step_type == 2) || (thread->step_exec && (step_type == 1))) + { + dbg_activate_step(); + } + else + { + dbg_deactivate_step(); + } + } + } +} +#endif /* ARCH_MM_MMU */ diff --git a/components/lwp/lwp.h b/components/lwp/lwp.h index 3607f89b17..da8ff0eef2 100644 --- a/components/lwp/lwp.h +++ b/components/lwp/lwp.h @@ -6,11 +6,50 @@ * Change Logs: * Date Author Notes * 2018-06-29 heyuanjie first version + * 2019-10-12 Jesven Add MMU and userspace support + * 2020-10-08 Bernard Architecture and code cleanup + * 2021-08-26 linzhenxing add lwp_setcwd\lwp_getcwd */ +/* + * RT-Thread light-weight process + */ #ifndef __LWP_H__ #define __LWP_H__ +#include <stdint.h> + +#include <rthw.h> +#include <rtthread.h> +#include <dfs.h> + +#include "lwp_pid.h" +#include "lwp_ipc.h" +#include "lwp_signal.h" +#include "lwp_syscall.h" +#include "lwp_avl.h" + +#ifdef RT_USING_USERSPACE +#include "lwp_shm.h" + +#include "mmu.h" +#include "page.h" +#else +#include "lwp_mpu.h" +#endif +#include "lwp_arch.h" + +#ifdef RT_USING_MUSL +#include <locale.h> +#endif +#ifdef RT_USING_TTY +struct tty_struct; +#endif + +#ifdef __cplusplus +extern "C" { +#endif + #define LWP_MAGIC 0x5A #define LWP_TYPE_FIX_ADDR 0x01 @@ -18,54 +57,244 @@ #define LWP_ARG_MAX 8 -#include <stdint.h> -#include <rtthread.h> -#include <dfs.h> -#include <lwp_memheap.h> - struct rt_lwp { +#ifdef RT_USING_USERSPACE + rt_mmu_info mmu_info; + struct lwp_avl_struct *map_area; + size_t end_heap; +#else +#ifdef ARCH_MM_MPU + struct rt_mpu_info mpu_info; +#endif /* ARCH_MM_MPU */ +#endif + +#ifdef RT_USING_SMP + int bind_cpu; +#endif + uint8_t lwp_type; - uint8_t heap_cnt; - uint8_t reserv[2]; + uint8_t reserv[3]; - rt_list_t hlist; /**< headp list */ + struct rt_lwp *parent; + struct rt_lwp *first_child; + struct rt_lwp *sibling; - uint8_t *text_entry; - uint32_t text_size; + rt_list_t wait_list; + int32_t finish; + int lwp_ret; - uint8_t *data; + void *text_entry; + uint32_t text_size; + void *data_entry; uint32_t data_size; - uint32_t *kernel_sp; /**< kernel stack point */ - struct dfs_fdtable fdt; + int ref; void *args; + uint32_t args_length; + pid_t pid; + pid_t __pgrp; /*Accessed via process_group()*/ + pid_t tty_old_pgrp; + pid_t session; + rt_list_t t_grp; + + int leader; /*boolean value for session group_leader*/ + struct dfs_fdtable fdt; + char cmd[RT_NAME_MAX]; + + int sa_flags; + lwp_sigset_t signal; + lwp_sigset_t signal_mask; + int signal_mask_bak; + rt_uint32_t signal_in_process; + lwp_sighandler_t signal_handler[_LWP_NSIG]; + + struct lwp_avl_struct *object_root; + struct rt_mutex object_mutex; + struct rt_user_context user_ctx; + + struct rt_wqueue wait_queue; /*for console */ + struct tty_struct *tty; /* NULL if no tty */ + + struct lwp_avl_struct *address_search_head; /* for addressed object fast rearch */ + char working_directory[DFS_PATH_MAX]; + int debug; + uint32_t bak_first_ins; + +#ifdef LWP_ENABLE_ASID + uint64_t generation; + unsigned int asid; +#endif }; -struct lwp_header +struct rt_lwp *lwp_self(void); + +enum lwp_exit_request_type { - uint8_t magic; - uint8_t compress_encrypt_algo; - uint16_t reserved; + LWP_EXIT_REQUEST_NONE = 0, + LWP_EXIT_REQUEST_TRIGGERED, + LWP_EXIT_REQUEST_IN_PROCESS, +}; +struct termios *get_old_termios(void); +void lwp_setcwd(char *buf); +char *lwp_getcwd(void); +void lwp_request_thread_exit(rt_thread_t thread_to_exit); +int lwp_check_exit_request(void); +void lwp_terminate(struct rt_lwp *lwp); +void lwp_wait_subthread_exit(void); - uint32_t crc32; +int lwp_tid_get(void); +void lwp_tid_put(int tid); +rt_thread_t lwp_tid_get_thread(int tid); +void lwp_tid_set_thread(int tid, rt_thread_t thread); + +size_t lwp_user_strlen(const char *s, int *err); +int lwp_execve(char *filename, int debug, int argc, char **argv, char **envp); + +/*create by lwp_setsid.c*/ +int setsid(void); +#ifdef RT_USING_USERSPACE +void lwp_mmu_switch(struct rt_thread *thread); +#endif +void lwp_user_setting_save(rt_thread_t thread); +void lwp_user_setting_restore(rt_thread_t thread); +int lwp_setaffinity(pid_t pid, int cpu); + +#ifdef RT_USING_USERSPACE +struct __pthread { + /* Part 1 -- these fields may be external or + * * internal (accessed via asm) ABI. Do not change. */ + struct pthread *self; + uintptr_t *dtv; + struct pthread *prev, *next; /* non-ABI */ + uintptr_t sysinfo; + uintptr_t canary, canary2; + + /* Part 2 -- implementation details, non-ABI. */ + int tid; + int errno_val; + volatile int detach_state; + volatile int cancel; + volatile unsigned char canceldisable, cancelasync; + unsigned char tsd_used:1; + unsigned char dlerror_flag:1; + unsigned char *map_base; + size_t map_size; + void *stack; + size_t stack_size; + size_t guard_size; + void *result; + struct __ptcb *cancelbuf; + void **tsd; + struct { + volatile void *volatile head; + long off; + volatile void *volatile pending; + } robust_list; + volatile int timer_id; + locale_t locale; + volatile int killlock[1]; + char *dlerror_buf; + void *stdio_locks; + + /* Part 3 -- the positions of these fields relative to + * * the end of the structure is external and internal ABI. */ + uintptr_t canary_at_end; + uintptr_t *dtv_copy; }; +#endif -struct lwp_chunk +/* for futex op */ +#define FUTEX_WAIT 0 +#define FUTEX_WAKE 1 + +/* for pmutex op */ +#define PMUTEX_INIT 0 +#define PMUTEX_LOCK 1 +#define PMUTEX_UNLOCK 2 +#define PMUTEX_DESTROY 3 + +#ifdef __cplusplus +} +#endif + +#define AUX_ARRAY_ITEMS_NR 6 + +/* aux key */ +#define AT_NULL 0 +#define AT_IGNORE 1 +#define AT_EXECFD 2 +#define AT_PHDR 3 +#define AT_PHENT 4 +#define AT_PHNUM 5 +#define AT_PAGESZ 6 +#define AT_BASE 7 +#define AT_FLAGS 8 +#define AT_ENTRY 9 +#define AT_NOTELF 10 +#define AT_UID 11 +#define AT_EUID 12 +#define AT_GID 13 +#define AT_EGID 14 +#define AT_CLKTCK 17 +#define AT_PLATFORM 15 +#define AT_HWCAP 16 +#define AT_FPUCW 18 +#define AT_DCACHEBSIZE 19 +#define AT_ICACHEBSIZE 20 +#define AT_UCACHEBSIZE 21 +#define AT_IGNOREPPC 22 +#define AT_SECURE 23 +#define AT_BASE_PLATFORM 24 +#define AT_RANDOM 25 +#define AT_HWCAP2 26 +#define AT_EXECFN 31 + +struct process_aux_item +{ + size_t key; + size_t value; +}; + +struct process_aux { - uint32_t total_len; + struct process_aux_item item[AUX_ARRAY_ITEMS_NR]; +}; - char name[4]; - uint32_t data_len; - uint32_t data_len_space; +struct lwp_args_info +{ + char **argv; + char **envp; + int argc; + int envc; + int size; +}; + +struct dbg_ops_t +{ + int (*dbg)(int argc, char **argv); + uint32_t (*arch_get_ins)(void); + void (*arch_activate_step)(void); + void (*arch_deactivate_step)(void); + int (*check_debug_event)(struct rt_hw_exp_stack *regs, unsigned long esr); + rt_channel_t (*gdb_get_server_channel)(void); + int (*gdb_get_step_type)(void); + void (*lwp_check_debug_attach_req)(void *pc); + int (*lwp_check_debug_suspend)(void); }; +extern struct dbg_ops_t *rt_dbg_ops; -extern struct rt_lwp *rt_lwp_self(void); +int dbg_thread_in_debug(void); +void dbg_register(struct dbg_ops_t *dbg_ops); -extern void rt_lwp_mem_init(struct rt_lwp *lwp); -extern void rt_lwp_mem_deinit(struct rt_lwp *lwp); -extern void *rt_lwp_mem_malloc(rt_uint32_t size); -extern void rt_lwp_mem_free(void *addr); -extern void *rt_lwp_mem_realloc(void *rmem, rt_size_t newsize); +uint32_t dbg_get_ins(void); +void dbg_activate_step(void); +void dbg_deactivate_step(void); +int dbg_check_event(struct rt_hw_exp_stack *regs, unsigned long arg); +rt_channel_t gdb_server_channel(void); +int dbg_step_type(void); +void dbg_attach_req(void *pc); +int dbg_check_suspend(void); +void rt_hw_set_process_id(int pid); #endif diff --git a/components/lwp/lwp_arch_comm.h b/components/lwp/lwp_arch_comm.h new file mode 100644 index 0000000000..91b4a9523e --- /dev/null +++ b/components/lwp/lwp_arch_comm.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __LWP_ARCH_COMM__ +#define __LWP_ARCH_COMM__ + +#include <rtthread.h> + +/** + * APIs that must port to all architectures + */ + +/* syscall handlers */ +void arch_clone_exit(void); +void arch_fork_exit(void); +void arch_syscall_exit(); +void arch_ret_to_user(); + +/* ELF relocation */ +#ifdef RT_USING_USERSPACE +void arch_elf_reloc(rt_mmu_info *m_info, void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, void *dynsym); +#else +void arch_elf_reloc(void *text_start, void *rel_dyn_start, size_t rel_dyn_size, void *got_start, size_t got_size, void *dynsym); +#endif + +/* User entry. enter user program code for the first time */ +void arch_crt_start_umode(void *args, const void *text, void *ustack, void *user_stack); +void arch_start_umode(void *args, const void *text, void *ustack, void *k_stack); + +/* lwp create and setup */ +int arch_set_thread_context(void (*exit)(void), void *new_thread_stack, + void *user_stack, void **thread_sp); +void *arch_get_user_sp(void); + +/* user space setup and control */ +int arch_user_space_init(struct rt_lwp *lwp); +void arch_user_space_vtable_free(struct rt_lwp *lwp); +void *arch_kernel_mmu_table_get(void); +void arch_kuser_init(rt_mmu_info *mmu_info, void *vectors); +int arch_expand_user_stack(void *addr); + +/* thread id register */ +void arch_set_thread_area(void *p); +void* arch_get_tidr(void); +void arch_set_tidr(void *p); + +#endif /* __LWP_ARCH_COMM__ */ diff --git a/components/lwp/lwp_avl.c b/components/lwp/lwp_avl.c new file mode 100644 index 0000000000..71eb089264 --- /dev/null +++ b/components/lwp/lwp_avl.c @@ -0,0 +1,227 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ +#include <rtthread.h> +#include <lwp_avl.h> + +static void lwp_avl_rebalance(struct lwp_avl_struct ***nodeplaces_ptr, int count) +{ + for (; count > 0; count--) + { + struct lwp_avl_struct **nodeplace = *--nodeplaces_ptr; + struct lwp_avl_struct *node = *nodeplace; + struct lwp_avl_struct *nodeleft = node->avl_left; + struct lwp_avl_struct *noderight = node->avl_right; + int heightleft = heightof(nodeleft); + int heightright = heightof(noderight); + if (heightright + 1 < heightleft) + { + struct lwp_avl_struct *nodeleftleft = nodeleft->avl_left; + struct lwp_avl_struct *nodeleftright = nodeleft->avl_right; + int heightleftright = heightof(nodeleftright); + if (heightof(nodeleftleft) >= heightleftright) + { + node->avl_left = nodeleftright; + nodeleft->avl_right = node; + nodeleft->avl_height = 1 + (node->avl_height = 1 + heightleftright); + *nodeplace = nodeleft; + } + else + { + nodeleft->avl_right = nodeleftright->avl_left; + node->avl_left = nodeleftright->avl_right; + nodeleftright->avl_left = nodeleft; + nodeleftright->avl_right = node; + nodeleft->avl_height = node->avl_height = heightleftright; + nodeleftright->avl_height = heightleft; + *nodeplace = nodeleftright; + } + } + else if (heightleft + 1 < heightright) + { + struct lwp_avl_struct *noderightright = noderight->avl_right; + struct lwp_avl_struct *noderightleft = noderight->avl_left; + int heightrightleft = heightof(noderightleft); + if (heightof(noderightright) >= heightrightleft) + { + node->avl_right = noderightleft; + noderight->avl_left = node; + noderight->avl_height = 1 + (node->avl_height = 1 + heightrightleft); + *nodeplace = noderight; + } + else + { + noderight->avl_left = noderightleft->avl_right; + node->avl_right = noderightleft->avl_left; + noderightleft->avl_right = noderight; + noderightleft->avl_left = node; + noderight->avl_height = node->avl_height = heightrightleft; + noderightleft->avl_height = heightright; + *nodeplace = noderightleft; + } + } + else + { + int height = (heightleft < heightright ? heightright : heightleft) + 1; + if (height == node->avl_height) + break; + node->avl_height = height; + } + } +} + +void lwp_avl_remove(struct lwp_avl_struct *node_to_delete, struct lwp_avl_struct **ptree) +{ + avl_key_t key = node_to_delete->avl_key; + struct lwp_avl_struct **nodeplace = ptree; + struct lwp_avl_struct **stack[avl_maxheight]; + uint32_t stack_count = 0; + struct lwp_avl_struct ***stack_ptr = &stack[0]; /* = &stack[stackcount] */ + struct lwp_avl_struct **nodeplace_to_delete; + for (;;) + { + struct lwp_avl_struct *node = *nodeplace; + if (node == AVL_EMPTY) + { + return; + } + + *stack_ptr++ = nodeplace; + stack_count++; + if (key == node->avl_key) + break; + if (key < node->avl_key) + nodeplace = &node->avl_left; + else + nodeplace = &node->avl_right; + } + nodeplace_to_delete = nodeplace; + if (node_to_delete->avl_left == AVL_EMPTY) + { + *nodeplace_to_delete = node_to_delete->avl_right; + stack_ptr--; + stack_count--; + } + else + { + struct lwp_avl_struct ***stack_ptr_to_delete = stack_ptr; + struct lwp_avl_struct **nodeplace = &node_to_delete->avl_left; + struct lwp_avl_struct *node; + for (;;) + { + node = *nodeplace; + if (node->avl_right == AVL_EMPTY) + break; + *stack_ptr++ = nodeplace; + stack_count++; + nodeplace = &node->avl_right; + } + *nodeplace = node->avl_left; + node->avl_left = node_to_delete->avl_left; + node->avl_right = node_to_delete->avl_right; + node->avl_height = node_to_delete->avl_height; + *nodeplace_to_delete = node; + *stack_ptr_to_delete = &node->avl_left; + } + lwp_avl_rebalance(stack_ptr, stack_count); +} + +void lwp_avl_insert(struct lwp_avl_struct *new_node, struct lwp_avl_struct **ptree) +{ + avl_key_t key = new_node->avl_key; + struct lwp_avl_struct **nodeplace = ptree; + struct lwp_avl_struct **stack[avl_maxheight]; + int stack_count = 0; + struct lwp_avl_struct ***stack_ptr = &stack[0]; /* = &stack[stackcount] */ + for (;;) + { + struct lwp_avl_struct *node = *nodeplace; + if (node == AVL_EMPTY) + break; + *stack_ptr++ = nodeplace; + stack_count++; + if (key < node->avl_key) + nodeplace = &node->avl_left; + else + nodeplace = &node->avl_right; + } + new_node->avl_left = AVL_EMPTY; + new_node->avl_right = AVL_EMPTY; + new_node->avl_height = 1; + *nodeplace = new_node; + lwp_avl_rebalance(stack_ptr, stack_count); +} + +struct lwp_avl_struct *lwp_avl_find(avl_key_t key, struct lwp_avl_struct *ptree) +{ + for (;;) + { + if (ptree == AVL_EMPTY) + { + return (struct lwp_avl_struct *)0; + } + if (key == ptree->avl_key) + break; + if (key < ptree->avl_key) + ptree = ptree->avl_left; + else + ptree = ptree->avl_right; + } + return ptree; +} + +int lwp_avl_traversal(struct lwp_avl_struct *ptree, int (*fun)(struct lwp_avl_struct *, void *), void *arg) +{ + int ret; + + if (!ptree) + { + return 0; + } + if (ptree->avl_left) + { + ret = lwp_avl_traversal(ptree->avl_left, fun, arg); + if (ret != 0) + { + return ret; + } + } + ret = (*fun)(ptree, arg); + if (ret != 0) + { + return ret; + } + if (ptree->avl_right) + { + ret = lwp_avl_traversal(ptree->avl_right, fun, arg); + if (ret != 0) + { + return ret; + } + } + return ret; +} + +RT_WEAK struct lwp_avl_struct* lwp_map_find_first(struct lwp_avl_struct* ptree) +{ + if (ptree == AVL_EMPTY) + { + return (struct lwp_avl_struct *)0; + } + while (1) + { + if (!ptree->avl_left) + { + break; + } + ptree = ptree->avl_left; + } + return ptree; +} + diff --git a/components/lwp/lwp_avl.h b/components/lwp/lwp_avl.h new file mode 100644 index 0000000000..2d555794d6 --- /dev/null +++ b/components/lwp/lwp_avl.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ +#ifndef LWP_AVL_H__ +#define LWP_AVL_H__ + +#include <rtthread.h> + +#include <string.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#define avl_key_t size_t +#define AVL_EMPTY (struct lwp_avl_struct *)0 +#define avl_maxheight 32 +#define heightof(tree) ((tree) == AVL_EMPTY ? 0 : (tree)->avl_height) + +struct lwp_avl_struct +{ + struct lwp_avl_struct *avl_left; + struct lwp_avl_struct *avl_right; + int avl_height; + avl_key_t avl_key; + void *data; +}; + +void lwp_avl_remove(struct lwp_avl_struct * node_to_delete, struct lwp_avl_struct ** ptree); +void lwp_avl_insert (struct lwp_avl_struct * new_node, struct lwp_avl_struct ** ptree); +struct lwp_avl_struct* lwp_avl_find(avl_key_t key, struct lwp_avl_struct* ptree); +int lwp_avl_traversal(struct lwp_avl_struct* ptree, int (*fun)(struct lwp_avl_struct*, void *), void *arg); +struct lwp_avl_struct* lwp_map_find_first(struct lwp_avl_struct* ptree); + +#ifdef __cplusplus +} +#endif + +#endif /* LWP_AVL_H__ */ diff --git a/components/lwp/lwp_dbg.c b/components/lwp/lwp_dbg.c new file mode 100644 index 0000000000..c630f3291e --- /dev/null +++ b/components/lwp/lwp_dbg.c @@ -0,0 +1,119 @@ +#include <rtthread.h> +#include <rthw.h> +#include <lwp.h> + +int dbg_thread_in_debug(void) +{ + int ret = 0; + struct rt_lwp *lwp = lwp_self(); + + if (lwp && lwp->debug) + { + ret = 1; + } + return ret; +} + +struct dbg_ops_t *rt_dbg_ops = RT_NULL; +RTM_EXPORT(rt_dbg_ops); + +void dbg_register(struct dbg_ops_t *dbg_ops) +{ + rt_dbg_ops = dbg_ops; +} +RTM_EXPORT(dbg_register); + +static int dbg(int argc, char **argv) +{ + int ret = -1; + + if (rt_dbg_ops) + { + ret = rt_dbg_ops->dbg(argc, argv); + } + else + { + rt_kprintf("Error: DBG command is not enabled!\n"); + } + return ret; +} +MSH_CMD_EXPORT(dbg, dbg); + +uint32_t dbg_get_ins(void) +{ + uint32_t ins = 0; + + if (rt_dbg_ops) + { + ins = rt_dbg_ops->arch_get_ins(); + } + return ins; +} + +void dbg_activate_step(void) +{ + if (rt_dbg_ops) + { + rt_dbg_ops->arch_activate_step(); + } +} + +void dbg_deactivate_step(void) +{ + if (rt_dbg_ops) + { + rt_dbg_ops->arch_deactivate_step(); + } +} + +int dbg_check_event(struct rt_hw_exp_stack *regs, unsigned long esr) +{ + int ret = 0; + + if (rt_dbg_ops) + { + ret = rt_dbg_ops->check_debug_event(regs, esr); + } + return ret; +} + +rt_channel_t gdb_server_channel(void) +{ + rt_channel_t ret = RT_NULL; + + if (rt_dbg_ops) + { + ret = rt_dbg_ops->gdb_get_server_channel(); + } + return ret; +} + +int dbg_step_type(void) +{ + int ret = 0; + + if (rt_dbg_ops) + { + ret = rt_dbg_ops->gdb_get_step_type(); + } + return ret; +} + +void dbg_attach_req(void *pc) +{ + if (rt_dbg_ops) + { + rt_dbg_ops->lwp_check_debug_attach_req(pc); + } +} + +int dbg_check_suspend(void) +{ + int ret = 0; + + if (rt_dbg_ops) + { + ret = rt_dbg_ops->lwp_check_debug_suspend(); + } + return ret; +} diff --git a/components/lwp/lwp_elf.h b/components/lwp/lwp_elf.h new file mode 100644 index 0000000000..e4817fe6f9 --- /dev/null +++ b/components/lwp/lwp_elf.h @@ -0,0 +1,3520 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + */ +#ifndef LWP_ELF_H__ +#define LWP_ELF_H__ + +#include <stdint.h> +#include <rtthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +#ifdef ARCH_CPU_64BIT +#define elfhdr elf64_hdr +#define elf_phdr elf64_phdr +#define elf_shdr elf64_shdr +#define elf_note elf64_note +#define elf_addr_t Elf64_Off +#define Elf_Word Elf64_Word +#define Elf_Addr Elf64_Addr +#define Elf_Half Elf64_Half +#define Elf_Ehdr Elf64_Ehdr +#define Elf_Phdr Elf64_Phdr +#define Elf_Shdr Elf64_Shdr +#else +#define elfhdr elf32_hdr +#define elf_phdr elf32_phdr +#define elf_shdr elf32_shdr +#define elf_note elf32_note +#define elf_addr_t Elf32_Off +#define Elf_Word Elf32_Word +#define Elf_Addr Elf32_Addr +#define Elf_Half Elf32_Half +#define Elf_Ehdr Elf32_Ehdr +#define Elf_Phdr Elf32_Phdr +#define Elf_Shdr Elf32_Shdr +#endif + +/* Type for a 16-bit quantity. */ +typedef uint16_t Elf32_Half; +typedef uint16_t Elf64_Half; + +/* Types for signed and unsigned 32-bit quantities. */ +typedef uint32_t Elf32_Word; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf64_Word; +typedef int32_t Elf64_Sword; + +/* Types for signed and unsigned 64-bit quantities. */ +typedef uint64_t Elf32_Xword; +typedef int64_t Elf32_Sxword; +typedef uint64_t Elf64_Xword; +typedef int64_t Elf64_Sxword; + +/* Type of addresses. */ +typedef uint32_t Elf32_Addr; +typedef uint64_t Elf64_Addr; + +/* Type of file offsets. */ +typedef uint32_t Elf32_Off; +typedef uint64_t Elf64_Off; + +/* Type for section indices, which are 16-bit quantities. */ +typedef uint16_t Elf32_Section; +typedef uint16_t Elf64_Section; + +/* Type for version symbol information. */ +typedef Elf32_Half Elf32_Versym; +typedef Elf64_Half Elf64_Versym; + + +/* The ELF file header. This appears at the start of every ELF file. */ + +#define EI_NIDENT (16) + +typedef struct elfhdr +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf32_Half e_type; /* Object file type */ + Elf32_Half e_machine; /* Architecture */ + Elf32_Word e_version; /* Object file version */ + Elf32_Addr e_entry; /* Entry point virtual address */ + Elf32_Off e_phoff; /* Program header table file offset */ + Elf32_Off e_shoff; /* Section header table file offset */ + Elf32_Word e_flags; /* Processor-specific flags */ + Elf32_Half e_ehsize; /* ELF header size in bytes */ + Elf32_Half e_phentsize; /* Program header table entry size */ + Elf32_Half e_phnum; /* Program header table entry count */ + Elf32_Half e_shentsize; /* Section header table entry size */ + Elf32_Half e_shnum; /* Section header table entry count */ + Elf32_Half e_shstrndx; /* Section header string table index */ +} Elf32_Ehdr; + +typedef struct +{ + unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ + Elf64_Half e_type; /* Object file type */ + Elf64_Half e_machine; /* Architecture */ + Elf64_Word e_version; /* Object file version */ + Elf64_Addr e_entry; /* Entry point virtual address */ + Elf64_Off e_phoff; /* Program header table file offset */ + Elf64_Off e_shoff; /* Section header table file offset */ + Elf64_Word e_flags; /* Processor-specific flags */ + Elf64_Half e_ehsize; /* ELF header size in bytes */ + Elf64_Half e_phentsize; /* Program header table entry size */ + Elf64_Half e_phnum; /* Program header table entry count */ + Elf64_Half e_shentsize; /* Section header table entry size */ + Elf64_Half e_shnum; /* Section header table entry count */ + Elf64_Half e_shstrndx; /* Section header string table index */ +} Elf64_Ehdr; + +/* Fields in the e_ident array. The EI_* macros are indices into the + array. The macros under each EI_* macro are the values the byte + may have. */ + +#define EI_MAG0 0 /* File identification byte 0 index */ +#define ELFMAG0 0x7f /* Magic number byte 0 */ + +#define EI_MAG1 1 /* File identification byte 1 index */ +#define ELFMAG1 'E' /* Magic number byte 1 */ + +#define EI_MAG2 2 /* File identification byte 2 index */ +#define ELFMAG2 'L' /* Magic number byte 2 */ + +#define EI_MAG3 3 /* File identification byte 3 index */ +#define ELFMAG3 'F' /* Magic number byte 3 */ + +/* Conglomeration of the identification bytes, for easy testing as a word. */ +#define ELFMAG "\177ELF" +#define SELFMAG 4 + +#define EI_CLASS 4 /* File class byte index */ +#define ELFCLASSNONE 0 /* Invalid class */ +#define ELFCLASS32 1 /* 32-bit objects */ +#define ELFCLASS64 2 /* 64-bit objects */ +#define ELFCLASSNUM 3 + +#define EI_DATA 5 /* Data encoding byte index */ +#define ELFDATANONE 0 /* Invalid data encoding */ +#define ELFDATA2LSB 1 /* 2's complement, little endian */ +#define ELFDATA2MSB 2 /* 2's complement, big endian */ +#define ELFDATANUM 3 + +#define EI_VERSION 6 /* File version byte index */ + /* Value must be EV_CURRENT */ + +#define EI_OSABI 7 /* OS ABI identification */ +#define ELFOSABI_NONE 0 /* UNIX System V ABI */ +#define ELFOSABI_SYSV 0 /* Alias. */ +#define ELFOSABI_HPUX 1 /* HP-UX */ +#define ELFOSABI_NETBSD 2 /* NetBSD. */ +#define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ +#define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ +#define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ +#define ELFOSABI_AIX 7 /* IBM AIX. */ +#define ELFOSABI_IRIX 8 /* SGI Irix. */ +#define ELFOSABI_FREEBSD 9 /* FreeBSD. */ +#define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ +#define ELFOSABI_MODESTO 11 /* Novell Modesto. */ +#define ELFOSABI_OPENBSD 12 /* OpenBSD. */ +#define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ +#define ELFOSABI_ARM 97 /* ARM */ +#define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ + +#define EI_ABIVERSION 8 /* ABI version */ + +#define EI_PAD 9 /* Byte index of padding bytes */ + +/* Legal values for e_type (object file type). */ + +#define ET_NONE 0 /* No file type */ +#define ET_REL 1 /* Relocatable file */ +#define ET_EXEC 2 /* Executable file */ +#define ET_DYN 3 /* Shared object file */ +#define ET_CORE 4 /* Core file */ +#define ET_NUM 5 /* Number of defined types */ +#define ET_LOOS 0xfe00 /* OS-specific range start */ +#define ET_HIOS 0xfeff /* OS-specific range end */ +#define ET_LOPROC 0xff00 /* Processor-specific range start */ +#define ET_HIPROC 0xffff /* Processor-specific range end */ + +/* Legal values for e_machine (architecture). */ + +#define EM_NONE 0 /* No machine */ +#define EM_M32 1 /* AT&T WE 32100 */ +#define EM_SPARC 2 /* SUN SPARC */ +#define EM_386 3 /* Intel 80386 */ +#define EM_68K 4 /* Motorola m68k family */ +#define EM_88K 5 /* Motorola m88k family */ +#define EM_860 7 /* Intel 80860 */ +#define EM_MIPS 8 /* MIPS R3000 big-endian */ +#define EM_S370 9 /* IBM System/370 */ +#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ + +#define EM_PARISC 15 /* HPPA */ +#define EM_VPP500 17 /* Fujitsu VPP500 */ +#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ +#define EM_960 19 /* Intel 80960 */ +#define EM_PPC 20 /* PowerPC */ +#define EM_PPC64 21 /* PowerPC 64-bit */ +#define EM_S390 22 /* IBM S390 */ + +#define EM_V800 36 /* NEC V800 series */ +#define EM_FR20 37 /* Fujitsu FR20 */ +#define EM_RH32 38 /* TRW RH-32 */ +#define EM_RCE 39 /* Motorola RCE */ +#define EM_ARM 40 /* ARM */ +#define EM_FAKE_ALPHA 41 /* Digital Alpha */ +#define EM_SH 42 /* Hitachi SH */ +#define EM_SPARCV9 43 /* SPARC v9 64-bit */ +#define EM_TRICORE 44 /* Siemens Tricore */ +#define EM_ARC 45 /* Argonaut RISC Core */ +#define EM_H8_300 46 /* Hitachi H8/300 */ +#define EM_H8_300H 47 /* Hitachi H8/300H */ +#define EM_H8S 48 /* Hitachi H8S */ +#define EM_H8_500 49 /* Hitachi H8/500 */ +#define EM_IA_64 50 /* Intel Merced */ +#define EM_MIPS_X 51 /* Stanford MIPS-X */ +#define EM_COLDFIRE 52 /* Motorola Coldfire */ +#define EM_68HC12 53 /* Motorola M68HC12 */ +#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ +#define EM_PCP 55 /* Siemens PCP */ +#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ +#define EM_NDR1 57 /* Denso NDR1 microprocessor */ +#define EM_STARCORE 58 /* Motorola Start*Core processor */ +#define EM_ME16 59 /* Toyota ME16 processor */ +#define EM_ST100 60 /* STMicroelectronic ST100 processor */ +#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ +#define EM_X86_64 62 /* AMD x86-64 architecture */ +#define EM_PDSP 63 /* Sony DSP Processor */ + +#define EM_FX66 66 /* Siemens FX66 microcontroller */ +#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ +#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ +#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ +#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ +#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ +#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ +#define EM_SVX 73 /* Silicon Graphics SVx */ +#define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ +#define EM_VAX 75 /* Digital VAX */ +#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ +#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ +#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ +#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ +#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ +#define EM_HUANY 81 /* Harvard University machine-independent object files */ +#define EM_PRISM 82 /* SiTera Prism */ +#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ +#define EM_FR30 84 /* Fujitsu FR30 */ +#define EM_D10V 85 /* Mitsubishi D10V */ +#define EM_D30V 86 /* Mitsubishi D30V */ +#define EM_V850 87 /* NEC v850 */ +#define EM_M32R 88 /* Mitsubishi M32R */ +#define EM_MN10300 89 /* Matsushita MN10300 */ +#define EM_MN10200 90 /* Matsushita MN10200 */ +#define EM_PJ 91 /* picoJava */ +#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ +#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ +#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ +#define EM_ALTERA_NIOS2 113 /* Altera Nios II */ +#define EM_AARCH64 183 /* ARM AARCH64 */ +#define EM_TILEPRO 188 /* Tilera TILEPro */ +#define EM_MICROBLAZE 189 /* Xilinx MicroBlaze */ +#define EM_TILEGX 191 /* Tilera TILE-Gx */ +#define EM_NUM 192 + +/* If it is necessary to assign new unofficial EM_* values, please + pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the + chances of collision with official or non-GNU unofficial values. */ + +#define EM_ALPHA 0x9026 + +/* Legal values for e_version (version). */ + +#define EV_NONE 0 /* Invalid ELF version */ +#define EV_CURRENT 1 /* Current version */ +#define EV_NUM 2 + +/* Section header. */ + +typedef struct +{ + Elf32_Word sh_name; /* Section name (string tbl index) */ + Elf32_Word sh_type; /* Section type */ + Elf32_Word sh_flags; /* Section flags */ + Elf32_Addr sh_addr; /* Section virtual addr at execution */ + Elf32_Off sh_offset; /* Section file offset */ + Elf32_Word sh_size; /* Section size in bytes */ + Elf32_Word sh_link; /* Link to another section */ + Elf32_Word sh_info; /* Additional section information */ + Elf32_Word sh_addralign; /* Section alignment */ + Elf32_Word sh_entsize; /* Entry size if section holds table */ +} Elf32_Shdr; + +typedef struct +{ + Elf64_Word sh_name; /* Section name (string tbl index) */ + Elf64_Word sh_type; /* Section type */ + Elf64_Xword sh_flags; /* Section flags */ + Elf64_Addr sh_addr; /* Section virtual addr at execution */ + Elf64_Off sh_offset; /* Section file offset */ + Elf64_Xword sh_size; /* Section size in bytes */ + Elf64_Word sh_link; /* Link to another section */ + Elf64_Word sh_info; /* Additional section information */ + Elf64_Xword sh_addralign; /* Section alignment */ + Elf64_Xword sh_entsize; /* Entry size if section holds table */ +} Elf64_Shdr; + +/* Special section indices. */ + +#define SHN_UNDEF 0 /* Undefined section */ +#define SHN_LORESERVE 0xff00 /* Start of reserved indices */ +#define SHN_LOPROC 0xff00 /* Start of processor-specific */ +#define SHN_BEFORE 0xff00 /* Order section before all others + (Solaris). */ +#define SHN_AFTER 0xff01 /* Order section after all others + (Solaris). */ +#define SHN_HIPROC 0xff1f /* End of processor-specific */ +#define SHN_LOOS 0xff20 /* Start of OS-specific */ +#define SHN_HIOS 0xff3f /* End of OS-specific */ +#define SHN_ABS 0xfff1 /* Associated symbol is absolute */ +#define SHN_COMMON 0xfff2 /* Associated symbol is common */ +#define SHN_XINDEX 0xffff /* Index is in extra table. */ +#define SHN_HIRESERVE 0xffff /* End of reserved indices */ + +/* Legal values for sh_type (section type). */ + +#define SHT_NULL 0 /* Section header table entry unused */ +#define SHT_PROGBITS 1 /* Program data */ +#define SHT_SYMTAB 2 /* Symbol table */ +#define SHT_STRTAB 3 /* String table */ +#define SHT_RELA 4 /* Relocation entries with addends */ +#define SHT_HASH 5 /* Symbol hash table */ +#define SHT_DYNAMIC 6 /* Dynamic linking information */ +#define SHT_NOTE 7 /* Notes */ +#define SHT_NOBITS 8 /* Program space with no data (bss) */ +#define SHT_REL 9 /* Relocation entries, no addends */ +#define SHT_SHLIB 10 /* Reserved */ +#define SHT_DYNSYM 11 /* Dynamic linker symbol table */ +#define SHT_INIT_ARRAY 14 /* Array of constructors */ +#define SHT_FINI_ARRAY 15 /* Array of destructors */ +#define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ +#define SHT_GROUP 17 /* Section group */ +#define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ +#define SHT_NUM 19 /* Number of defined types. */ +#define SHT_LOOS 0x60000000 /* Start OS-specific. */ +#define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ +#define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ +#define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ +#define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ +#define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ +#define SHT_SUNW_move 0x6ffffffa +#define SHT_SUNW_COMDAT 0x6ffffffb +#define SHT_SUNW_syminfo 0x6ffffffc +#define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ +#define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ +#define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ +#define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ +#define SHT_HIOS 0x6fffffff /* End OS-specific type */ +#define SHT_LOPROC 0x70000000 /* Start of processor-specific */ +#define SHT_HIPROC 0x7fffffff /* End of processor-specific */ +#define SHT_LOUSER 0x80000000 /* Start of application-specific */ +#define SHT_HIUSER 0x8fffffff /* End of application-specific */ + +/* Legal values for sh_flags (section flags). */ + +#define SHF_WRITE (1 << 0) /* Writable */ +#define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ +#define SHF_EXECINSTR (1 << 2) /* Executable */ +#define SHF_MERGE (1 << 4) /* Might be merged */ +#define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ +#define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ +#define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ +#define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling + required */ +#define SHF_GROUP (1 << 9) /* Section is member of a group. */ +#define SHF_TLS (1 << 10) /* Section hold thread-local data. */ +#define SHF_COMPRESSED (1 << 11) /* Section with compressed data. */ +#define SHF_MASKOS 0x0ff00000 /* OS-specific. */ +#define SHF_MASKPROC 0xf0000000 /* Processor-specific */ +#define SHF_ORDERED (1 << 30) /* Special ordering requirement + (Solaris). */ +#define SHF_EXCLUDE (1U << 31) /* Section is excluded unless + referenced or allocated (Solaris).*/ + +/* Section compression header. Used when SHF_COMPRESSED is set. */ + +typedef struct +{ + Elf32_Word ch_type; /* Compression format. */ + Elf32_Word ch_size; /* Uncompressed data size. */ + Elf32_Word ch_addralign; /* Uncompressed data alignment. */ +} Elf32_Chdr; + +typedef struct +{ + Elf64_Word ch_type; /* Compression format. */ + Elf64_Word ch_reserved; + Elf64_Xword ch_size; /* Uncompressed data size. */ + Elf64_Xword ch_addralign; /* Uncompressed data alignment. */ +} Elf64_Chdr; + +/* Legal values for ch_type (compression algorithm). */ +#define ELFCOMPRESS_ZLIB 1 /* ZLIB/DEFLATE algorithm. */ +#define ELFCOMPRESS_LOOS 0x60000000 /* Start of OS-specific. */ +#define ELFCOMPRESS_HIOS 0x6fffffff /* End of OS-specific. */ +#define ELFCOMPRESS_LOPROC 0x70000000 /* Start of processor-specific. */ +#define ELFCOMPRESS_HIPROC 0x7fffffff /* End of processor-specific. */ + +/* Section group handling. */ +#define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ + +/* Symbol table entry. */ + +typedef struct +{ + Elf32_Word st_name; /* Symbol name (string tbl index) */ + Elf32_Addr st_value; /* Symbol value */ + Elf32_Word st_size; /* Symbol size */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf32_Section st_shndx; /* Section index */ +} Elf32_Sym; + +typedef struct +{ + Elf64_Word st_name; /* Symbol name (string tbl index) */ + unsigned char st_info; /* Symbol type and binding */ + unsigned char st_other; /* Symbol visibility */ + Elf64_Section st_shndx; /* Section index */ + Elf64_Addr st_value; /* Symbol value */ + Elf64_Xword st_size; /* Symbol size */ +} Elf64_Sym; + +/* The syminfo section if available contains additional information about + every dynamic symbol. */ + +typedef struct +{ + Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf32_Half si_flags; /* Per symbol flags */ +} Elf32_Syminfo; + +typedef struct +{ + Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ + Elf64_Half si_flags; /* Per symbol flags */ +} Elf64_Syminfo; + +/* Possible values for si_boundto. */ +#define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ +#define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ +#define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ + +/* Possible bitmasks for si_flags. */ +#define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ +#define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ +#define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ +#define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy + loaded */ +/* Syminfo version values. */ +#define SYMINFO_NONE 0 +#define SYMINFO_CURRENT 1 +#define SYMINFO_NUM 2 + + +/* How to extract and insert information held in the st_info field. */ + +#define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) +#define ELF32_ST_TYPE(val) ((val) & 0xf) +#define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) + +/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ +#define ELF64_ST_BIND(val) ELF32_ST_BIND (val) +#define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) +#define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) + +/* Legal values for ST_BIND subfield of st_info (symbol binding). */ + +#define STB_LOCAL 0 /* Local symbol */ +#define STB_GLOBAL 1 /* Global symbol */ +#define STB_WEAK 2 /* Weak symbol */ +#define STB_NUM 3 /* Number of defined types. */ +#define STB_LOOS 10 /* Start of OS-specific */ +#define STB_GNU_UNIQUE 10 /* Unique symbol. */ +#define STB_HIOS 12 /* End of OS-specific */ +#define STB_LOPROC 13 /* Start of processor-specific */ +#define STB_HIPROC 15 /* End of processor-specific */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_NOTYPE 0 /* Symbol type is unspecified */ +#define STT_OBJECT 1 /* Symbol is a data object */ +#define STT_FUNC 2 /* Symbol is a code object */ +#define STT_SECTION 3 /* Symbol associated with a section */ +#define STT_FILE 4 /* Symbol's name is file name */ +#define STT_COMMON 5 /* Symbol is a common data object */ +#define STT_TLS 6 /* Symbol is thread-local data object*/ +#define STT_NUM 7 /* Number of defined types. */ +#define STT_LOOS 10 /* Start of OS-specific */ +#define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ +#define STT_HIOS 12 /* End of OS-specific */ +#define STT_LOPROC 13 /* Start of processor-specific */ +#define STT_HIPROC 15 /* End of processor-specific */ + + +/* Symbol table indices are found in the hash buckets and chain table + of a symbol hash table section. This special index value indicates + the end of a chain, meaning no further symbols are found in that bucket. */ + +#define STN_UNDEF 0 /* End of a chain. */ + + +/* How to extract and insert information held in the st_other field. */ + +#define ELF32_ST_VISIBILITY(o) ((o) & 0x03) + +/* For ELF64 the definitions are the same. */ +#define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) + +/* Symbol visibility specification encoded in the st_other field. */ +#define STV_DEFAULT 0 /* Default symbol visibility rules */ +#define STV_INTERNAL 1 /* Processor specific hidden class */ +#define STV_HIDDEN 2 /* Sym unavailable in other modules */ +#define STV_PROTECTED 3 /* Not preemptible, not exported */ + + +/* Relocation table entry without addend (in section of type SHT_REL). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ +} Elf32_Rel; + +/* I have seen two different definitions of the Elf64_Rel and + Elf64_Rela structures, so we'll leave them out until Novell (or + whoever) gets their act together. */ +/* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ +} Elf64_Rel; + +/* Relocation table entry with addend (in section of type SHT_RELA). */ + +typedef struct +{ + Elf32_Addr r_offset; /* Address */ + Elf32_Word r_info; /* Relocation type and symbol index */ + Elf32_Sword r_addend; /* Addend */ +} Elf32_Rela; + +typedef struct +{ + Elf64_Addr r_offset; /* Address */ + Elf64_Xword r_info; /* Relocation type and symbol index */ + Elf64_Sxword r_addend; /* Addend */ +} Elf64_Rela; + +/* How to extract and insert information held in the r_info field. */ + +#define ELF32_R_SYM(val) ((val) >> 8) +#define ELF32_R_TYPE(val) ((val) & 0xff) +#define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) + +#define ELF64_R_SYM(i) ((i) >> 32) +#define ELF64_R_TYPE(i) ((i) & 0xffffffff) +#define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) + +/* Program segment header. */ + +typedef struct elf_phdr +{ + Elf32_Word p_type; /* Segment type */ + Elf32_Off p_offset; /* Segment file offset */ + Elf32_Addr p_vaddr; /* Segment virtual address */ + Elf32_Addr p_paddr; /* Segment physical address */ + Elf32_Word p_filesz; /* Segment size in file */ + Elf32_Word p_memsz; /* Segment size in memory */ + Elf32_Word p_flags; /* Segment flags */ + Elf32_Word p_align; /* Segment alignment */ +} Elf32_Phdr; + +typedef struct +{ + Elf64_Word p_type; /* Segment type */ + Elf64_Word p_flags; /* Segment flags */ + Elf64_Off p_offset; /* Segment file offset */ + Elf64_Addr p_vaddr; /* Segment virtual address */ + Elf64_Addr p_paddr; /* Segment physical address */ + Elf64_Xword p_filesz; /* Segment size in file */ + Elf64_Xword p_memsz; /* Segment size in memory */ + Elf64_Xword p_align; /* Segment alignment */ +} Elf64_Phdr; + +/* Special value for e_phnum. This indicates that the real number of + program headers is too large to fit into e_phnum. Instead the real + value is in the field sh_info of section 0. */ + +#define PN_XNUM 0xffff + +/* Legal values for p_type (segment type). */ + +#define PT_NULL 0 /* Program header table entry unused */ +#define PT_LOAD 1 /* Loadable program segment */ +#define PT_DYNAMIC 2 /* Dynamic linking information */ +#define PT_INTERP 3 /* Program interpreter */ +#define PT_NOTE 4 /* Auxiliary information */ +#define PT_SHLIB 5 /* Reserved */ +#define PT_PHDR 6 /* Entry for header table itself */ +#define PT_TLS 7 /* Thread-local storage segment */ +#define PT_NUM 8 /* Number of defined types */ +#define PT_LOOS 0x60000000 /* Start of OS-specific */ +#define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ +#define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ +#define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ +#define PT_LOSUNW 0x6ffffffa +#define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ +#define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ +#define PT_HISUNW 0x6fffffff +#define PT_HIOS 0x6fffffff /* End of OS-specific */ +#define PT_LOPROC 0x70000000 /* Start of processor-specific */ +#define PT_HIPROC 0x7fffffff /* End of processor-specific */ + +/* Legal values for p_flags (segment flags). */ + +#define PF_X (1 << 0) /* Segment is executable */ +#define PF_W (1 << 1) /* Segment is writable */ +#define PF_R (1 << 2) /* Segment is readable */ +#define PF_MASKOS 0x0ff00000 /* OS-specific */ +#define PF_MASKPROC 0xf0000000 /* Processor-specific */ + +/* Legal values for note segment descriptor types for core files. */ + +#define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ +#define NT_FPREGSET 2 /* Contains copy of fpregset struct */ +#define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ +#define NT_PRXREG 4 /* Contains copy of prxregset struct */ +#define NT_TASKSTRUCT 4 /* Contains copy of task structure */ +#define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ +#define NT_AUXV 6 /* Contains copy of auxv array */ +#define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ +#define NT_ASRS 8 /* Contains copy of asrset struct */ +#define NT_PSTATUS 10 /* Contains copy of pstatus struct */ +#define NT_PSINFO 13 /* Contains copy of psinfo struct */ +#define NT_PRCRED 14 /* Contains copy of prcred struct */ +#define NT_UTSNAME 15 /* Contains copy of utsname struct */ +#define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ +#define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ +#define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ +#define NT_SIGINFO 0x53494749 /* Contains copy of siginfo_t, + size might increase */ +#define NT_FILE 0x46494c45 /* Contains information about mapped + files */ +#define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ +#define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ +#define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ +#define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ +#define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ +#define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ +#define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ +#define NT_S390_HIGH_GPRS 0x300 /* s390 upper register halves */ +#define NT_S390_TIMER 0x301 /* s390 timer register */ +#define NT_S390_TODCMP 0x302 /* s390 TOD clock comparator register */ +#define NT_S390_TODPREG 0x303 /* s390 TOD programmable register */ +#define NT_S390_CTRS 0x304 /* s390 control registers */ +#define NT_S390_PREFIX 0x305 /* s390 prefix register */ +#define NT_S390_LAST_BREAK 0x306 /* s390 breaking event address */ +#define NT_S390_SYSTEM_CALL 0x307 /* s390 system call restart data */ +#define NT_S390_TDB 0x308 /* s390 transaction diagnostic block */ +#define NT_ARM_VFP 0x400 /* ARM VFP/NEON registers */ +#define NT_ARM_TLS 0x401 /* ARM TLS register */ +#define NT_ARM_HW_BREAK 0x402 /* ARM hardware breakpoint registers */ +#define NT_ARM_HW_WATCH 0x403 /* ARM hardware watchpoint registers */ + +/* Legal values for the note segment descriptor types for object files. */ + +#define NT_VERSION 1 /* Contains a version string. */ + + +/* Dynamic section entry. */ + +typedef struct +{ + Elf32_Sword d_tag; /* Dynamic entry type */ + union + { + Elf32_Word d_val; /* Integer value */ + Elf32_Addr d_ptr; /* Address value */ + } d_un; +} Elf32_Dyn; + +typedef struct +{ + Elf64_Sxword d_tag; /* Dynamic entry type */ + union + { + Elf64_Xword d_val; /* Integer value */ + Elf64_Addr d_ptr; /* Address value */ + } d_un; +} Elf64_Dyn; + +/* Legal values for d_tag (dynamic entry type). */ + +#define DT_NULL 0 /* Marks end of dynamic section */ +#define DT_NEEDED 1 /* Name of needed library */ +#define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ +#define DT_PLTGOT 3 /* Processor defined value */ +#define DT_HASH 4 /* Address of symbol hash table */ +#define DT_STRTAB 5 /* Address of string table */ +#define DT_SYMTAB 6 /* Address of symbol table */ +#define DT_RELA 7 /* Address of Rela relocs */ +#define DT_RELASZ 8 /* Total size of Rela relocs */ +#define DT_RELAENT 9 /* Size of one Rela reloc */ +#define DT_STRSZ 10 /* Size of string table */ +#define DT_SYMENT 11 /* Size of one symbol table entry */ +#define DT_INIT 12 /* Address of init function */ +#define DT_FINI 13 /* Address of termination function */ +#define DT_SONAME 14 /* Name of shared object */ +#define DT_RPATH 15 /* Library search path (deprecated) */ +#define DT_SYMBOLIC 16 /* Start symbol search here */ +#define DT_REL 17 /* Address of Rel relocs */ +#define DT_RELSZ 18 /* Total size of Rel relocs */ +#define DT_RELENT 19 /* Size of one Rel reloc */ +#define DT_PLTREL 20 /* Type of reloc in PLT */ +#define DT_DEBUG 21 /* For debugging; unspecified */ +#define DT_TEXTREL 22 /* Reloc might modify .text */ +#define DT_JMPREL 23 /* Address of PLT relocs */ +#define DT_BIND_NOW 24 /* Process relocations of object */ +#define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ +#define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ +#define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ +#define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ +#define DT_RUNPATH 29 /* Library search path */ +#define DT_FLAGS 30 /* Flags for the object being loaded */ +#define DT_ENCODING 32 /* Start of encoded range */ +#define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ +#define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ +#define DT_NUM 34 /* Number used */ +#define DT_LOOS 0x6000000d /* Start of OS-specific */ +#define DT_HIOS 0x6ffff000 /* End of OS-specific */ +#define DT_LOPROC 0x70000000 /* Start of processor-specific */ +#define DT_HIPROC 0x7fffffff /* End of processor-specific */ +#define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ + +/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the + Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's + approach. */ +#define DT_VALRNGLO 0x6ffffd00 +#define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ +#define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ +#define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ +#define DT_CHECKSUM 0x6ffffdf8 +#define DT_PLTPADSZ 0x6ffffdf9 +#define DT_MOVEENT 0x6ffffdfa +#define DT_MOVESZ 0x6ffffdfb +#define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ +#define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting + the following DT_* entry. */ +#define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ +#define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ +#define DT_VALRNGHI 0x6ffffdff +#define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ +#define DT_VALNUM 12 + +/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the + Dyn.d_un.d_ptr field of the Elf*_Dyn structure. + + If any adjustment is made to the ELF object after it has been + built these entries will need to be adjusted. */ +#define DT_ADDRRNGLO 0x6ffffe00 +#define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ +#define DT_TLSDESC_PLT 0x6ffffef6 +#define DT_TLSDESC_GOT 0x6ffffef7 +#define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ +#define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ +#define DT_CONFIG 0x6ffffefa /* Configuration information. */ +#define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ +#define DT_AUDIT 0x6ffffefc /* Object auditing. */ +#define DT_PLTPAD 0x6ffffefd /* PLT padding. */ +#define DT_MOVETAB 0x6ffffefe /* Move table. */ +#define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ +#define DT_ADDRRNGHI 0x6ffffeff +#define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ +#define DT_ADDRNUM 11 + +/* The versioning entry types. The next are defined as part of the + GNU extension. */ +#define DT_VERSYM 0x6ffffff0 + +#define DT_RELACOUNT 0x6ffffff9 +#define DT_RELCOUNT 0x6ffffffa + +/* These were chosen by Sun. */ +#define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ +#define DT_VERDEF 0x6ffffffc /* Address of version definition + table */ +#define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ +#define DT_VERNEED 0x6ffffffe /* Address of table with needed + versions */ +#define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ +#define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ +#define DT_VERSIONTAGNUM 16 + +/* Sun added these machine-independent extensions in the "processor-specific" + range. Be compatible. */ +#define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ +#define DT_FILTER 0x7fffffff /* Shared object to get values from */ +#define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) +#define DT_EXTRANUM 3 + +/* Values of `d_un.d_val' in the DT_FLAGS entry. */ +#define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ +#define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ +#define DF_TEXTREL 0x00000004 /* Object contains text relocations */ +#define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ +#define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ + +/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 + entry in the dynamic section. */ +#define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ +#define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ +#define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ +#define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ +#define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ +#define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ +#define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ +#define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ +#define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ +#define DF_1_TRANS 0x00000200 +#define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ +#define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ +#define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ +#define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ +#define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ +#define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ +#define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ +#define DF_1_NODIRECT 0x00020000 /* Object has no-direct binding. */ +#define DF_1_IGNMULDEF 0x00040000 +#define DF_1_NOKSYMS 0x00080000 +#define DF_1_NOHDR 0x00100000 +#define DF_1_EDITED 0x00200000 /* Object is modified after built. */ +#define DF_1_NORELOC 0x00400000 +#define DF_1_SYMINTPOSE 0x00800000 /* Object has individual interposers. */ +#define DF_1_GLOBAUDIT 0x01000000 /* Global auditing required. */ +#define DF_1_SINGLETON 0x02000000 /* Singleton symbols are used. */ + +/* Flags for the feature selection in DT_FEATURE_1. */ +#define DTF_1_PARINIT 0x00000001 +#define DTF_1_CONFEXP 0x00000002 + +/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ +#define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ +#define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not + generally available. */ + +/* Version definition sections. */ + +typedef struct +{ + Elf32_Half vd_version; /* Version revision */ + Elf32_Half vd_flags; /* Version information */ + Elf32_Half vd_ndx; /* Version Index */ + Elf32_Half vd_cnt; /* Number of associated aux entries */ + Elf32_Word vd_hash; /* Version name hash value */ + Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf32_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf32_Verdef; + +typedef struct +{ + Elf64_Half vd_version; /* Version revision */ + Elf64_Half vd_flags; /* Version information */ + Elf64_Half vd_ndx; /* Version Index */ + Elf64_Half vd_cnt; /* Number of associated aux entries */ + Elf64_Word vd_hash; /* Version name hash value */ + Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ + Elf64_Word vd_next; /* Offset in bytes to next verdef + entry */ +} Elf64_Verdef; + + +/* Legal values for vd_version (version revision). */ +#define VER_DEF_NONE 0 /* No version */ +#define VER_DEF_CURRENT 1 /* Current version */ +#define VER_DEF_NUM 2 /* Given version number */ + +/* Legal values for vd_flags (version information flags). */ +#define VER_FLG_BASE 0x1 /* Version definition of file itself */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + +/* Versym symbol index values. */ +#define VER_NDX_LOCAL 0 /* Symbol is local. */ +#define VER_NDX_GLOBAL 1 /* Symbol is global. */ +#define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ +#define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ + +/* Auxialiary version information. */ + +typedef struct +{ + Elf32_Word vda_name; /* Version or dependency names */ + Elf32_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf32_Verdaux; + +typedef struct +{ + Elf64_Word vda_name; /* Version or dependency names */ + Elf64_Word vda_next; /* Offset in bytes to next verdaux + entry */ +} Elf64_Verdaux; + + +/* Version dependency section. */ + +typedef struct +{ + Elf32_Half vn_version; /* Version of structure */ + Elf32_Half vn_cnt; /* Number of associated aux entries */ + Elf32_Word vn_file; /* Offset of filename for this + dependency */ + Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf32_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf32_Verneed; + +typedef struct +{ + Elf64_Half vn_version; /* Version of structure */ + Elf64_Half vn_cnt; /* Number of associated aux entries */ + Elf64_Word vn_file; /* Offset of filename for this + dependency */ + Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ + Elf64_Word vn_next; /* Offset in bytes to next verneed + entry */ +} Elf64_Verneed; + + +/* Legal values for vn_version (version revision). */ +#define VER_NEED_NONE 0 /* No version */ +#define VER_NEED_CURRENT 1 /* Current version */ +#define VER_NEED_NUM 2 /* Given version number */ + +/* Auxiliary needed version information. */ + +typedef struct +{ + Elf32_Word vna_hash; /* Hash value of dependency name */ + Elf32_Half vna_flags; /* Dependency specific information */ + Elf32_Half vna_other; /* Unused */ + Elf32_Word vna_name; /* Dependency name string offset */ + Elf32_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf32_Vernaux; + +typedef struct +{ + Elf64_Word vna_hash; /* Hash value of dependency name */ + Elf64_Half vna_flags; /* Dependency specific information */ + Elf64_Half vna_other; /* Unused */ + Elf64_Word vna_name; /* Dependency name string offset */ + Elf64_Word vna_next; /* Offset in bytes to next vernaux + entry */ +} Elf64_Vernaux; + + +/* Legal values for vna_flags. */ +#define VER_FLG_WEAK 0x2 /* Weak version identifier */ + + +/* Auxiliary vector. */ + +/* This vector is normally only used by the program interpreter. The + usual definition in an ABI supplement uses the name auxv_t. The + vector is not usually defined in a standard <elf.h> file, but it + can't hurt. We rename it to avoid conflicts. The sizes of these + types are an arrangement between the exec server and the program + interpreter, so we don't fully specify them here. */ + +typedef struct +{ + uint32_t a_type; /* Entry type */ + union + { + uint32_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf32_auxv_t; + +typedef struct +{ + uint64_t a_type; /* Entry type */ + union + { + uint64_t a_val; /* Integer value */ + /* We use to have pointer elements added here. We cannot do that, + though, since it does not work when using 32-bit definitions + on 64-bit platforms and vice versa. */ + } a_un; +} Elf64_auxv_t; + +//#include <bits/auxv.h> +/* Note section contents. Each entry in the note section begins with + a header of a fixed form. */ + +typedef struct +{ + Elf32_Word n_namesz; /* Length of the note's name. */ + Elf32_Word n_descsz; /* Length of the note's descriptor. */ + Elf32_Word n_type; /* Type of the note. */ +} Elf32_Nhdr; + +typedef struct +{ + Elf64_Word n_namesz; /* Length of the note's name. */ + Elf64_Word n_descsz; /* Length of the note's descriptor. */ + Elf64_Word n_type; /* Type of the note. */ +} Elf64_Nhdr; + +/* Known names of notes. */ + +/* Solaris entries in the note section have this name. */ +#define ELF_NOTE_SOLARIS "SUNW Solaris" + +/* Note entries for GNU systems have this name. */ +#define ELF_NOTE_GNU "GNU" + + +/* Defined types of notes for Solaris. */ + +/* Value of descriptor (one word) is desired pagesize for the binary. */ +#define ELF_NOTE_PAGESIZE_HINT 1 + + +/* Defined note types for GNU systems. */ + +/* ABI information. The descriptor consists of words: + word 0: OS descriptor + word 1: major version of the ABI + word 2: minor version of the ABI + word 3: subminor version of the ABI +*/ +#define NT_GNU_ABI_TAG 1 +#define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ + +/* Known OSes. These values can appear in word 0 of an + NT_GNU_ABI_TAG note section entry. */ +#define ELF_NOTE_OS_LINUX 0 +#define ELF_NOTE_OS_GNU 1 +#define ELF_NOTE_OS_SOLARIS2 2 +#define ELF_NOTE_OS_FREEBSD 3 + +/* Synthetic hwcap information. The descriptor begins with two words: + word 0: number of entries + word 1: bitmask of enabled entries + Then follow variable-length entries, one byte followed by a + '\0'-terminated hwcap name string. The byte gives the bit + number to test if enabled, (1U << bit) & bitmask. */ +#define NT_GNU_HWCAP 2 + +/* Build ID bits as generated by ld --build-id. + The descriptor consists of any nonzero number of bytes. */ +#define NT_GNU_BUILD_ID 3 + +/* Version note generated by GNU gold containing a version string. */ +#define NT_GNU_GOLD_VERSION 4 + + +/* Move records. */ +typedef struct +{ + Elf32_Xword m_value; /* Symbol value. */ + Elf32_Word m_info; /* Size and index. */ + Elf32_Word m_poffset; /* Symbol offset. */ + Elf32_Half m_repeat; /* Repeat count. */ + Elf32_Half m_stride; /* Stride info. */ +} Elf32_Move; + +typedef struct +{ + Elf64_Xword m_value; /* Symbol value. */ + Elf64_Xword m_info; /* Size and index. */ + Elf64_Xword m_poffset; /* Symbol offset. */ + Elf64_Half m_repeat; /* Repeat count. */ + Elf64_Half m_stride; /* Stride info. */ +} Elf64_Move; + +/* Macro to construct move records. */ +#define ELF32_M_SYM(info) ((info) >> 8) +#define ELF32_M_SIZE(info) ((unsigned char) (info)) +#define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) + +#define ELF64_M_SYM(info) ELF32_M_SYM (info) +#define ELF64_M_SIZE(info) ELF32_M_SIZE (info) +#define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) + + +/* Motorola 68k specific definitions. */ + +/* Values for Elf32_Ehdr.e_flags. */ +#define EF_CPU32 0x00810000 + +/* m68k relocs. */ + +#define R_68K_NONE 0 /* No reloc */ +#define R_68K_32 1 /* Direct 32 bit */ +#define R_68K_16 2 /* Direct 16 bit */ +#define R_68K_8 3 /* Direct 8 bit */ +#define R_68K_PC32 4 /* PC relative 32 bit */ +#define R_68K_PC16 5 /* PC relative 16 bit */ +#define R_68K_PC8 6 /* PC relative 8 bit */ +#define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ +#define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ +#define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ +#define R_68K_GOT32O 10 /* 32 bit GOT offset */ +#define R_68K_GOT16O 11 /* 16 bit GOT offset */ +#define R_68K_GOT8O 12 /* 8 bit GOT offset */ +#define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ +#define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ +#define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ +#define R_68K_PLT32O 16 /* 32 bit PLT offset */ +#define R_68K_PLT16O 17 /* 16 bit PLT offset */ +#define R_68K_PLT8O 18 /* 8 bit PLT offset */ +#define R_68K_COPY 19 /* Copy symbol at runtime */ +#define R_68K_GLOB_DAT 20 /* Create GOT entry */ +#define R_68K_JMP_SLOT 21 /* Create PLT entry */ +#define R_68K_RELATIVE 22 /* Adjust by program base */ +#define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ +#define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ +#define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ +#define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ +#define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ +#define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ +#define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ +#define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ +#define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ +#define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ +#define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ +#define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ +#define R_68K_TLS_LE32 37 /* 32 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE16 38 /* 16 bit offset relative to + static TLS block */ +#define R_68K_TLS_LE8 39 /* 8 bit offset relative to + static TLS block */ +#define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ +#define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ +#define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ +/* Keep this the last entry. */ +#define R_68K_NUM 43 + +/* Intel 80386 specific definitions. */ + +/* i386 relocs. */ + +#define R_386_NONE 0 /* No reloc */ +#define R_386_32 1 /* Direct 32 bit */ +#define R_386_PC32 2 /* PC relative 32 bit */ +#define R_386_GOT32 3 /* 32 bit GOT entry */ +#define R_386_PLT32 4 /* 32 bit PLT address */ +#define R_386_COPY 5 /* Copy symbol at runtime */ +#define R_386_GLOB_DAT 6 /* Create GOT entry */ +#define R_386_JMP_SLOT 7 /* Create PLT entry */ +#define R_386_RELATIVE 8 /* Adjust by program base */ +#define R_386_GOTOFF 9 /* 32 bit offset to GOT */ +#define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ +#define R_386_32PLT 11 +#define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ +#define R_386_TLS_IE 15 /* Address of GOT entry for static TLS + block offset */ +#define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block + offset */ +#define R_386_TLS_LE 17 /* Offset relative to static TLS + block */ +#define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of + general dynamic thread local data */ +#define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of + local dynamic thread local data + in LE code */ +#define R_386_16 20 +#define R_386_PC16 21 +#define R_386_8 22 +#define R_386_PC8 23 +#define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic + thread local data */ +#define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ +#define R_386_TLS_GD_CALL 26 /* Relocation for call to + __tls_get_addr() */ +#define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ +#define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic + thread local data in LE code */ +#define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ +#define R_386_TLS_LDM_CALL 30 /* Relocation for call to + __tls_get_addr() in LDM code */ +#define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ +#define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ +#define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS + block offset */ +#define R_386_TLS_LE_32 34 /* Negated offset relative to static + TLS block */ +#define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ +#define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ +#define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ +#define R_386_SIZE32 38 /* 32-bit symbol size */ +#define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ +#define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS + descriptor for + relaxation. */ +#define R_386_TLS_DESC 41 /* TLS descriptor containing + pointer to code and to + argument, returning the TLS + offset for the symbol. */ +#define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ +/* Keep this the last entry. */ +#define R_386_NUM 43 + +/* SUN SPARC specific definitions. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ + +/* Values for Elf64_Ehdr.e_flags. */ + +#define EF_SPARCV9_MM 3 +#define EF_SPARCV9_TSO 0 +#define EF_SPARCV9_PSO 1 +#define EF_SPARCV9_RMO 2 +#define EF_SPARC_LEDATA 0x800000 /* little endian data */ +#define EF_SPARC_EXT_MASK 0xFFFF00 +#define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ +#define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ +#define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ +#define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ + +/* SPARC relocs. */ + +#define R_SPARC_NONE 0 /* No reloc */ +#define R_SPARC_8 1 /* Direct 8 bit */ +#define R_SPARC_16 2 /* Direct 16 bit */ +#define R_SPARC_32 3 /* Direct 32 bit */ +#define R_SPARC_DISP8 4 /* PC relative 8 bit */ +#define R_SPARC_DISP16 5 /* PC relative 16 bit */ +#define R_SPARC_DISP32 6 /* PC relative 32 bit */ +#define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ +#define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ +#define R_SPARC_HI22 9 /* High 22 bit */ +#define R_SPARC_22 10 /* Direct 22 bit */ +#define R_SPARC_13 11 /* Direct 13 bit */ +#define R_SPARC_LO10 12 /* Truncated 10 bit */ +#define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ +#define R_SPARC_GOT13 14 /* 13 bit GOT entry */ +#define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ +#define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ +#define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ +#define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ +#define R_SPARC_COPY 19 /* Copy symbol at runtime */ +#define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ +#define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ +#define R_SPARC_RELATIVE 22 /* Adjust by program base */ +#define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ + +/* Additional Sparc64 relocs. */ + +#define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ +#define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ +#define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ +#define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ +#define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ +#define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ +#define R_SPARC_10 30 /* Direct 10 bit */ +#define R_SPARC_11 31 /* Direct 11 bit */ +#define R_SPARC_64 32 /* Direct 64 bit */ +#define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ +#define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ +#define R_SPARC_HM10 35 /* High middle 10 bits of ... */ +#define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ +#define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ +#define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ +#define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ +#define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ +#define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ +#define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ +#define R_SPARC_7 43 /* Direct 7 bit */ +#define R_SPARC_5 44 /* Direct 5 bit */ +#define R_SPARC_6 45 /* Direct 6 bit */ +#define R_SPARC_DISP64 46 /* PC relative 64 bit */ +#define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ +#define R_SPARC_HIX22 48 /* High 22 bit complemented */ +#define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ +#define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ +#define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ +#define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ +#define R_SPARC_REGISTER 53 /* Global register usage */ +#define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ +#define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ +#define R_SPARC_TLS_GD_HI22 56 +#define R_SPARC_TLS_GD_LO10 57 +#define R_SPARC_TLS_GD_ADD 58 +#define R_SPARC_TLS_GD_CALL 59 +#define R_SPARC_TLS_LDM_HI22 60 +#define R_SPARC_TLS_LDM_LO10 61 +#define R_SPARC_TLS_LDM_ADD 62 +#define R_SPARC_TLS_LDM_CALL 63 +#define R_SPARC_TLS_LDO_HIX22 64 +#define R_SPARC_TLS_LDO_LOX10 65 +#define R_SPARC_TLS_LDO_ADD 66 +#define R_SPARC_TLS_IE_HI22 67 +#define R_SPARC_TLS_IE_LO10 68 +#define R_SPARC_TLS_IE_LD 69 +#define R_SPARC_TLS_IE_LDX 70 +#define R_SPARC_TLS_IE_ADD 71 +#define R_SPARC_TLS_LE_HIX22 72 +#define R_SPARC_TLS_LE_LOX10 73 +#define R_SPARC_TLS_DTPMOD32 74 +#define R_SPARC_TLS_DTPMOD64 75 +#define R_SPARC_TLS_DTPOFF32 76 +#define R_SPARC_TLS_DTPOFF64 77 +#define R_SPARC_TLS_TPOFF32 78 +#define R_SPARC_TLS_TPOFF64 79 +#define R_SPARC_GOTDATA_HIX22 80 +#define R_SPARC_GOTDATA_LOX10 81 +#define R_SPARC_GOTDATA_OP_HIX22 82 +#define R_SPARC_GOTDATA_OP_LOX10 83 +#define R_SPARC_GOTDATA_OP 84 +#define R_SPARC_H34 85 +#define R_SPARC_SIZE32 86 +#define R_SPARC_SIZE64 87 +#define R_SPARC_WDISP10 88 +#define R_SPARC_JMP_IREL 248 +#define R_SPARC_IRELATIVE 249 +#define R_SPARC_GNU_VTINHERIT 250 +#define R_SPARC_GNU_VTENTRY 251 +#define R_SPARC_REV32 252 +/* Keep this the last entry. */ +#define R_SPARC_NUM 253 + +/* For Sparc64, legal values for d_tag of Elf64_Dyn. */ + +#define DT_SPARC_REGISTER 0x70000001 +#define DT_SPARC_NUM 2 + +/* MIPS R3000 specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used. */ +#define EF_MIPS_PIC 2 /* Contains PIC code. */ +#define EF_MIPS_CPIC 4 /* Uses PIC calling sequence. */ +#define EF_MIPS_XGOT 8 +#define EF_MIPS_64BIT_WHIRL 16 +#define EF_MIPS_ABI2 32 +#define EF_MIPS_ABI_ON32 64 +#define EF_MIPS_FP64 512 /* Uses FP64 (12 callee-saved). */ +#define EF_MIPS_NAN2008 1024 /* Uses IEEE 754-2008 NaN encoding. */ +#define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level. */ + +/* Legal values for MIPS architecture level. */ + +#define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ +#define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ +#define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ +#define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ +#define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ +#define EF_MIPS_ARCH_32 0x50000000 /* MIPS32 code. */ +#define EF_MIPS_ARCH_64 0x60000000 /* MIPS64 code. */ +#define EF_MIPS_ARCH_32R2 0x70000000 /* MIPS32r2 code. */ +#define EF_MIPS_ARCH_64R2 0x80000000 /* MIPS64r2 code. */ + +/* The following are unofficial names and should not be used. */ + +#define E_MIPS_ARCH_1 EF_MIPS_ARCH_1 +#define E_MIPS_ARCH_2 EF_MIPS_ARCH_2 +#define E_MIPS_ARCH_3 EF_MIPS_ARCH_3 +#define E_MIPS_ARCH_4 EF_MIPS_ARCH_4 +#define E_MIPS_ARCH_5 EF_MIPS_ARCH_5 +#define E_MIPS_ARCH_32 EF_MIPS_ARCH_32 +#define E_MIPS_ARCH_64 EF_MIPS_ARCH_64 + +/* Special section indices. */ + +#define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols. */ +#define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ +#define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ +#define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols. */ +#define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link. */ +#define SHT_MIPS_MSYM 0x70000001 +#define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols. */ +#define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes. */ +#define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ +#define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging info. */ +#define SHT_MIPS_REGINFO 0x70000006 /* Register usage information. */ +#define SHT_MIPS_PACKAGE 0x70000007 +#define SHT_MIPS_PACKSYM 0x70000008 +#define SHT_MIPS_RELD 0x70000009 +#define SHT_MIPS_IFACE 0x7000000b +#define SHT_MIPS_CONTENT 0x7000000c +#define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ +#define SHT_MIPS_SHDR 0x70000010 +#define SHT_MIPS_FDESC 0x70000011 +#define SHT_MIPS_EXTSYM 0x70000012 +#define SHT_MIPS_DENSE 0x70000013 +#define SHT_MIPS_PDESC 0x70000014 +#define SHT_MIPS_LOCSYM 0x70000015 +#define SHT_MIPS_AUXSYM 0x70000016 +#define SHT_MIPS_OPTSYM 0x70000017 +#define SHT_MIPS_LOCSTR 0x70000018 +#define SHT_MIPS_LINE 0x70000019 +#define SHT_MIPS_RFDESC 0x7000001a +#define SHT_MIPS_DELTASYM 0x7000001b +#define SHT_MIPS_DELTAINST 0x7000001c +#define SHT_MIPS_DELTACLASS 0x7000001d +#define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ +#define SHT_MIPS_DELTADECL 0x7000001f +#define SHT_MIPS_SYMBOL_LIB 0x70000020 +#define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ +#define SHT_MIPS_TRANSLATE 0x70000022 +#define SHT_MIPS_PIXIE 0x70000023 +#define SHT_MIPS_XLATE 0x70000024 +#define SHT_MIPS_XLATE_DEBUG 0x70000025 +#define SHT_MIPS_WHIRL 0x70000026 +#define SHT_MIPS_EH_REGION 0x70000027 +#define SHT_MIPS_XLATE_OLD 0x70000028 +#define SHT_MIPS_PDR_EXCEPTION 0x70000029 + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_MIPS_GPREL 0x10000000 /* Must be in global data area. */ +#define SHF_MIPS_MERGE 0x20000000 +#define SHF_MIPS_ADDR 0x40000000 +#define SHF_MIPS_STRINGS 0x80000000 +#define SHF_MIPS_NOSTRIP 0x08000000 +#define SHF_MIPS_LOCAL 0x04000000 +#define SHF_MIPS_NAMES 0x02000000 +#define SHF_MIPS_NODUPE 0x01000000 + + +/* Symbol tables. */ + +/* MIPS specific values for `st_other'. */ +#define STO_MIPS_DEFAULT 0x0 +#define STO_MIPS_INTERNAL 0x1 +#define STO_MIPS_HIDDEN 0x2 +#define STO_MIPS_PROTECTED 0x3 +#define STO_MIPS_PLT 0x8 +#define STO_MIPS_SC_ALIGN_UNUSED 0xff + +/* MIPS specific values for `st_info'. */ +#define STB_MIPS_SPLIT_COMMON 13 + +/* Entries found in sections of type SHT_MIPS_GPTAB. */ + +typedef union +{ + struct + { + Elf32_Word gt_current_g_value; /* -G value used for compilation. */ + Elf32_Word gt_unused; /* Not used. */ + } gt_header; /* First entry in section. */ + struct + { + Elf32_Word gt_g_value; /* If this value were used for -G. */ + Elf32_Word gt_bytes; /* This many bytes would be used. */ + } gt_entry; /* Subsequent entries in section. */ +} Elf32_gptab; + +/* Entry found in sections of type SHT_MIPS_REGINFO. */ + +typedef struct +{ + Elf32_Word ri_gprmask; /* General registers used. */ + Elf32_Word ri_cprmask[4]; /* Coprocessor registers used. */ + Elf32_Sword ri_gp_value; /* $gp register value. */ +} Elf32_RegInfo; + +/* Entries found in sections of type SHT_MIPS_OPTIONS. */ + +typedef struct +{ + unsigned char kind; /* Determines interpretation of the + variable part of descriptor. */ + unsigned char size; /* Size of descriptor, including header. */ + Elf32_Section section; /* Section header index of section affected, + 0 for global options. */ + Elf32_Word info; /* Kind-specific information. */ +} Elf_Options; + +/* Values for `kind' field in Elf_Options. */ + +#define ODK_NULL 0 /* Undefined. */ +#define ODK_REGINFO 1 /* Register usage information. */ +#define ODK_EXCEPTIONS 2 /* Exception processing options. */ +#define ODK_PAD 3 /* Section padding options. */ +#define ODK_HWPATCH 4 /* Hardware workarounds performed */ +#define ODK_FILL 5 /* record the fill value used by the linker. */ +#define ODK_TAGS 6 /* reserve space for desktop tools to write. */ +#define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ +#define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ + +/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ + +#define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ +#define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ +#define OEX_PAGE0 0x10000 /* page zero must be mapped. */ +#define OEX_SMM 0x20000 /* Force sequential memory mode? */ +#define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ +#define OEX_PRECISEFP OEX_FPDBUG +#define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ + +#define OEX_FPU_INVAL 0x10 +#define OEX_FPU_DIV0 0x08 +#define OEX_FPU_OFLO 0x04 +#define OEX_FPU_UFLO 0x02 +#define OEX_FPU_INEX 0x01 + +/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ + +#define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ +#define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ +#define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ +#define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ + +#define OPAD_PREFIX 0x1 +#define OPAD_POSTFIX 0x2 +#define OPAD_SYMBOL 0x4 + +/* Entry found in `.options' section. */ + +typedef struct +{ + Elf32_Word hwp_flags1; /* Extra flags. */ + Elf32_Word hwp_flags2; /* Extra flags. */ +} Elf_Options_Hw; + +/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ + +#define OHWA0_R4KEOP_CHECKED 0x00000001 +#define OHWA1_R4KEOP_CLEAN 0x00000002 + +/* MIPS relocs. */ + +#define R_MIPS_NONE 0 /* No reloc */ +#define R_MIPS_16 1 /* Direct 16 bit */ +#define R_MIPS_32 2 /* Direct 32 bit */ +#define R_MIPS_REL32 3 /* PC relative 32 bit */ +#define R_MIPS_26 4 /* Direct 26 bit shifted */ +#define R_MIPS_HI16 5 /* High 16 bit */ +#define R_MIPS_LO16 6 /* Low 16 bit */ +#define R_MIPS_GPREL16 7 /* GP relative 16 bit */ +#define R_MIPS_LITERAL 8 /* 16 bit literal entry */ +#define R_MIPS_GOT16 9 /* 16 bit GOT entry */ +#define R_MIPS_PC16 10 /* PC relative 16 bit */ +#define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ +#define R_MIPS_GPREL32 12 /* GP relative 32 bit */ + +#define R_MIPS_SHIFT5 16 +#define R_MIPS_SHIFT6 17 +#define R_MIPS_64 18 +#define R_MIPS_GOT_DISP 19 +#define R_MIPS_GOT_PAGE 20 +#define R_MIPS_GOT_OFST 21 +#define R_MIPS_GOT_HI16 22 +#define R_MIPS_GOT_LO16 23 +#define R_MIPS_SUB 24 +#define R_MIPS_INSERT_A 25 +#define R_MIPS_INSERT_B 26 +#define R_MIPS_DELETE 27 +#define R_MIPS_HIGHER 28 +#define R_MIPS_HIGHEST 29 +#define R_MIPS_CALL_HI16 30 +#define R_MIPS_CALL_LO16 31 +#define R_MIPS_SCN_DISP 32 +#define R_MIPS_REL16 33 +#define R_MIPS_ADD_IMMEDIATE 34 +#define R_MIPS_PJUMP 35 +#define R_MIPS_RELGOT 36 +#define R_MIPS_JALR 37 +#define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ +#define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ +#define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ +#define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ +#define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ +#define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ +#define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ +#define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ +#define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ +#define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ +#define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ +#define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ +#define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ +#define R_MIPS_GLOB_DAT 51 +#define R_MIPS_COPY 126 +#define R_MIPS_JUMP_SLOT 127 +/* Keep this the last entry. */ +#define R_MIPS_NUM 128 + +/* Legal values for p_type field of Elf32_Phdr. */ + +#define PT_MIPS_REGINFO 0x70000000 /* Register usage information. */ +#define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ +#define PT_MIPS_OPTIONS 0x70000002 +#define PT_MIPS_ABIFLAGS 0x70000003 /* FP mode requirement. */ + +/* Special program header types. */ + +#define PF_MIPS_LOCAL 0x10000000 + +/* Legal values for d_tag field of Elf32_Dyn. */ + +#define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ +#define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ +#define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ +#define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ +#define DT_MIPS_FLAGS 0x70000005 /* Flags */ +#define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ +#define DT_MIPS_MSYM 0x70000007 +#define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ +#define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ +#define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ +#define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ +#define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ +#define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ +#define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ +#define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ +#define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ +#define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ +#define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ +#define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in + DT_MIPS_DELTA_CLASS. */ +#define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ +#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in + DT_MIPS_DELTA_INSTANCE. */ +#define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ +#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in + DT_MIPS_DELTA_RELOC. */ +#define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta + relocations refer to. */ +#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in + DT_MIPS_DELTA_SYM. */ +#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the + class declaration. */ +#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in + DT_MIPS_DELTA_CLASSSYM. */ +#define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ +#define DT_MIPS_PIXIE_INIT 0x70000023 +#define DT_MIPS_SYMBOL_LIB 0x70000024 +#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 +#define DT_MIPS_LOCAL_GOTIDX 0x70000026 +#define DT_MIPS_HIDDEN_GOTIDX 0x70000027 +#define DT_MIPS_PROTECTED_GOTIDX 0x70000028 +#define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ +#define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ +#define DT_MIPS_DYNSTR_ALIGN 0x7000002b +#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ +#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve + function stored in GOT. */ +#define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added + by rld on dlopen() calls. */ +#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ +#define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ +#define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ +/* The address of .got.plt in an executable using the new non-PIC ABI. */ +#define DT_MIPS_PLTGOT 0x70000032 +/* The base of the PLT in an executable using the new non-PIC ABI if that + PLT is writable. For a non-writable PLT, this is omitted or has a zero + value. */ +#define DT_MIPS_RWPLT 0x70000034 +/* An alternative description of the classic MIPS RLD_MAP that is usable + in a PIE as it stores a relative offset from the address of the tag + rather than an absolute address. */ +#define DT_MIPS_RLD_MAP_REL 0x70000035 +#define DT_MIPS_NUM 0x36 + +/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ + +#define RHF_NONE 0 /* No flags */ +#define RHF_QUICKSTART (1 << 0) /* Use quickstart */ +#define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ +#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ +#define RHF_NO_MOVE (1 << 3) +#define RHF_SGI_ONLY (1 << 4) +#define RHF_GUARANTEE_INIT (1 << 5) +#define RHF_DELTA_C_PLUS_PLUS (1 << 6) +#define RHF_GUARANTEE_START_INIT (1 << 7) +#define RHF_PIXIE (1 << 8) +#define RHF_DEFAULT_DELAY_LOAD (1 << 9) +#define RHF_REQUICKSTART (1 << 10) +#define RHF_REQUICKSTARTED (1 << 11) +#define RHF_CORD (1 << 12) +#define RHF_NO_UNRES_UNDEF (1 << 13) +#define RHF_RLD_ORDER_SAFE (1 << 14) + +/* Entries found in sections of type SHT_MIPS_LIBLIST. */ + +typedef struct +{ + Elf32_Word l_name; /* Name (string table index) */ + Elf32_Word l_time_stamp; /* Timestamp */ + Elf32_Word l_checksum; /* Checksum */ + Elf32_Word l_version; /* Interface version */ + Elf32_Word l_flags; /* Flags */ +} Elf32_Lib; + +typedef struct +{ + Elf64_Word l_name; /* Name (string table index) */ + Elf64_Word l_time_stamp; /* Timestamp */ + Elf64_Word l_checksum; /* Checksum */ + Elf64_Word l_version; /* Interface version */ + Elf64_Word l_flags; /* Flags */ +} Elf64_Lib; + + +/* Legal values for l_flags. */ + +#define LL_NONE 0 +#define LL_EXACT_MATCH (1 << 0) /* Require exact match */ +#define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ +#define LL_REQUIRE_MINOR (1 << 2) +#define LL_EXPORTS (1 << 3) +#define LL_DELAY_LOAD (1 << 4) +#define LL_DELTA (1 << 5) + +/* Entries found in sections of type SHT_MIPS_CONFLICT. */ + +typedef Elf32_Addr Elf32_Conflict; + +typedef struct +{ + /* Version of flags structure. */ + Elf32_Half version; + /* The level of the ISA: 1-5, 32, 64. */ + unsigned char isa_level; + /* The revision of ISA: 0 for MIPS V and below, 1-n otherwise. */ + unsigned char isa_rev; + /* The size of general purpose registers. */ + unsigned char gpr_size; + /* The size of co-processor 1 registers. */ + unsigned char cpr1_size; + /* The size of co-processor 2 registers. */ + unsigned char cpr2_size; + /* The floating-point ABI. */ + unsigned char fp_abi; + /* Processor-specific extension. */ + Elf32_Word isa_ext; + /* Mask of ASEs used. */ + Elf32_Word ases; + /* Mask of general flags. */ + Elf32_Word flags1; + Elf32_Word flags2; +} Elf_MIPS_ABIFlags_v0; + +/* Values for the register size bytes of an abi flags structure. */ + +#define MIPS_AFL_REG_NONE 0x00 /* No registers. */ +#define MIPS_AFL_REG_32 0x01 /* 32-bit registers. */ +#define MIPS_AFL_REG_64 0x02 /* 64-bit registers. */ +#define MIPS_AFL_REG_128 0x03 /* 128-bit registers. */ + +/* Masks for the ases word of an ABI flags structure. */ + +#define MIPS_AFL_ASE_DSP 0x00000001 /* DSP ASE. */ +#define MIPS_AFL_ASE_DSPR2 0x00000002 /* DSP R2 ASE. */ +#define MIPS_AFL_ASE_EVA 0x00000004 /* Enhanced VA Scheme. */ +#define MIPS_AFL_ASE_MCU 0x00000008 /* MCU (MicroController) ASE. */ +#define MIPS_AFL_ASE_MDMX 0x00000010 /* MDMX ASE. */ +#define MIPS_AFL_ASE_MIPS3D 0x00000020 /* MIPS-3D ASE. */ +#define MIPS_AFL_ASE_MT 0x00000040 /* MT ASE. */ +#define MIPS_AFL_ASE_SMARTMIPS 0x00000080 /* SmartMIPS ASE. */ +#define MIPS_AFL_ASE_VIRT 0x00000100 /* VZ ASE. */ +#define MIPS_AFL_ASE_MSA 0x00000200 /* MSA ASE. */ +#define MIPS_AFL_ASE_MIPS16 0x00000400 /* MIPS16 ASE. */ +#define MIPS_AFL_ASE_MICROMIPS 0x00000800 /* MICROMIPS ASE. */ +#define MIPS_AFL_ASE_XPA 0x00001000 /* XPA ASE. */ +#define MIPS_AFL_ASE_MASK 0x00001fff /* All ASEs. */ + +/* Values for the isa_ext word of an ABI flags structure. */ + +#define MIPS_AFL_EXT_XLR 1 /* RMI Xlr instruction. */ +#define MIPS_AFL_EXT_OCTEON2 2 /* Cavium Networks Octeon2. */ +#define MIPS_AFL_EXT_OCTEONP 3 /* Cavium Networks OcteonP. */ +#define MIPS_AFL_EXT_LOONGSON_3A 4 /* Loongson 3A. */ +#define MIPS_AFL_EXT_OCTEON 5 /* Cavium Networks Octeon. */ +#define MIPS_AFL_EXT_5900 6 /* MIPS R5900 instruction. */ +#define MIPS_AFL_EXT_4650 7 /* MIPS R4650 instruction. */ +#define MIPS_AFL_EXT_4010 8 /* LSI R4010 instruction. */ +#define MIPS_AFL_EXT_4100 9 /* NEC VR4100 instruction. */ +#define MIPS_AFL_EXT_3900 10 /* Toshiba R3900 instruction. */ +#define MIPS_AFL_EXT_10000 11 /* MIPS R10000 instruction. */ +#define MIPS_AFL_EXT_SB1 12 /* Broadcom SB-1 instruction. */ +#define MIPS_AFL_EXT_4111 13 /* NEC VR4111/VR4181 instruction. */ +#define MIPS_AFL_EXT_4120 14 /* NEC VR4120 instruction. */ +#define MIPS_AFL_EXT_5400 15 /* NEC VR5400 instruction. */ +#define MIPS_AFL_EXT_5500 16 /* NEC VR5500 instruction. */ +#define MIPS_AFL_EXT_LOONGSON_2E 17 /* ST Microelectronics Loongson 2E. */ +#define MIPS_AFL_EXT_LOONGSON_2F 18 /* ST Microelectronics Loongson 2F. */ + +/* Masks for the flags1 word of an ABI flags structure. */ +#define MIPS_AFL_FLAGS1_ODDSPREG 1 /* Uses odd single-precision registers. */ + +/* Object attribute values. */ +enum +{ + /* Not tagged or not using any ABIs affected by the differences. */ + Val_GNU_MIPS_ABI_FP_ANY = 0, + /* Using hard-float -mdouble-float. */ + Val_GNU_MIPS_ABI_FP_DOUBLE = 1, + /* Using hard-float -msingle-float. */ + Val_GNU_MIPS_ABI_FP_SINGLE = 2, + /* Using soft-float. */ + Val_GNU_MIPS_ABI_FP_SOFT = 3, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_OLD_64 = 4, + /* Using -mfpxx. */ + Val_GNU_MIPS_ABI_FP_XX = 5, + /* Using -mips32r2 -mfp64. */ + Val_GNU_MIPS_ABI_FP_64 = 6, + /* Using -mips32r2 -mfp64 -mno-odd-spreg. */ + Val_GNU_MIPS_ABI_FP_64A = 7, + /* Maximum allocated FP ABI value. */ + Val_GNU_MIPS_ABI_FP_MAX = 7 +}; + +/* HPPA specific definitions. */ + +/* Legal values for e_flags field of Elf32_Ehdr. */ + +#define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ +#define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ +#define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ +#define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ +#define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch + prediction. */ +#define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ +#define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ + +/* Defined values for `e_flags & EF_PARISC_ARCH' are: */ + +#define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ +#define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ +#define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ + +/* Additional section indeces. */ + +#define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared + symbols in ANSI C. */ +#define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ + +/* Legal values for sh_type field of Elf32_Shdr. */ + +#define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ +#define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ +#define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ + +/* Legal values for sh_flags field of Elf32_Shdr. */ + +#define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ +#define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ +#define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ + +/* Legal values for ST_TYPE subfield of st_info (symbol type). */ + +#define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ + +#define STT_HP_OPAQUE (STT_LOOS + 0x1) +#define STT_HP_STUB (STT_LOOS + 0x2) + +/* HPPA relocs. */ + +#define R_PARISC_NONE 0 /* No reloc. */ +#define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ +#define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ +#define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ +#define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ +#define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ +#define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ +#define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ +#define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ +#define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ +#define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ +#define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ +#define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ +#define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ +#define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ +#define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ +#define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ +#define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ +#define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ +#define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ +#define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ +#define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ +#define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ +#define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ +#define R_PARISC_FPTR64 64 /* 64 bits function address. */ +#define R_PARISC_PLABEL32 65 /* 32 bits function address. */ +#define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ +#define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ +#define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ +#define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ +#define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ +#define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ +#define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ +#define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ +#define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ +#define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ +#define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ +#define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ +#define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ +#define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ +#define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ +#define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ +#define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ +#define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ +#define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ +#define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ +#define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ +#define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ +#define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ +#define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ +#define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ +#define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ +#define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ +#define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ +#define R_PARISC_LORESERVE 128 +#define R_PARISC_COPY 128 /* Copy relocation. */ +#define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ +#define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ +#define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ +#define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ +#define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ +#define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ +#define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ +#define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ +#define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ +#define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ +#define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ +#define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ +#define R_PARISC_GNU_VTENTRY 232 +#define R_PARISC_GNU_VTINHERIT 233 +#define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ +#define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ +#define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ +#define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ +#define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ +#define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ +#define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ +#define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ +#define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ +#define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ +#define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ +#define R_PARISC_TLS_LE21L R_PARISC_TPREL21L +#define R_PARISC_TLS_LE14R R_PARISC_TPREL14R +#define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L +#define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R +#define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 +#define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 +#define R_PARISC_HIRESERVE 255 + +/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ + +#define PT_HP_TLS (PT_LOOS + 0x0) +#define PT_HP_CORE_NONE (PT_LOOS + 0x1) +#define PT_HP_CORE_VERSION (PT_LOOS + 0x2) +#define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) +#define PT_HP_CORE_COMM (PT_LOOS + 0x4) +#define PT_HP_CORE_PROC (PT_LOOS + 0x5) +#define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) +#define PT_HP_CORE_STACK (PT_LOOS + 0x7) +#define PT_HP_CORE_SHM (PT_LOOS + 0x8) +#define PT_HP_CORE_MMF (PT_LOOS + 0x9) +#define PT_HP_PARALLEL (PT_LOOS + 0x10) +#define PT_HP_FASTBIND (PT_LOOS + 0x11) +#define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) +#define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) +#define PT_HP_STACK (PT_LOOS + 0x14) + +#define PT_PARISC_ARCHEXT 0x70000000 +#define PT_PARISC_UNWIND 0x70000001 + +/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ + +#define PF_PARISC_SBP 0x08000000 + +#define PF_HP_PAGE_SIZE 0x00100000 +#define PF_HP_FAR_SHARED 0x00200000 +#define PF_HP_NEAR_SHARED 0x00400000 +#define PF_HP_CODE 0x01000000 +#define PF_HP_MODIFY 0x02000000 +#define PF_HP_LAZYSWAP 0x04000000 +#define PF_HP_SBP 0x08000000 + + +/* Alpha specific definitions. */ + +/* Legal values for e_flags field of Elf64_Ehdr. */ + +#define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ +#define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ + +/* Legal values for sh_type field of Elf64_Shdr. */ + +/* These two are primerily concerned with ECOFF debugging info. */ +#define SHT_ALPHA_DEBUG 0x70000001 +#define SHT_ALPHA_REGINFO 0x70000002 + +/* Legal values for sh_flags field of Elf64_Shdr. */ + +#define SHF_ALPHA_GPREL 0x10000000 + +/* Legal values for st_other field of Elf64_Sym. */ +#define STO_ALPHA_NOPV 0x80 /* No PV required. */ +#define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ + +/* Alpha relocs. */ + +#define R_ALPHA_NONE 0 /* No reloc */ +#define R_ALPHA_REFLONG 1 /* Direct 32 bit */ +#define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ +#define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ +#define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ +#define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ +#define R_ALPHA_GPDISP 6 /* Add displacement to GP */ +#define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ +#define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ +#define R_ALPHA_SREL16 9 /* PC relative 16 bit */ +#define R_ALPHA_SREL32 10 /* PC relative 32 bit */ +#define R_ALPHA_SREL64 11 /* PC relative 64 bit */ +#define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ +#define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ +#define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ +#define R_ALPHA_COPY 24 /* Copy symbol at runtime */ +#define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ +#define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ +#define R_ALPHA_RELATIVE 27 /* Adjust by program base */ +#define R_ALPHA_TLS_GD_HI 28 +#define R_ALPHA_TLSGD 29 +#define R_ALPHA_TLS_LDM 30 +#define R_ALPHA_DTPMOD64 31 +#define R_ALPHA_GOTDTPREL 32 +#define R_ALPHA_DTPREL64 33 +#define R_ALPHA_DTPRELHI 34 +#define R_ALPHA_DTPRELLO 35 +#define R_ALPHA_DTPREL16 36 +#define R_ALPHA_GOTTPREL 37 +#define R_ALPHA_TPREL64 38 +#define R_ALPHA_TPRELHI 39 +#define R_ALPHA_TPRELLO 40 +#define R_ALPHA_TPREL16 41 +/* Keep this the last entry. */ +#define R_ALPHA_NUM 46 + +/* Magic values of the LITUSE relocation addend. */ +#define LITUSE_ALPHA_ADDR 0 +#define LITUSE_ALPHA_BASE 1 +#define LITUSE_ALPHA_BYTOFF 2 +#define LITUSE_ALPHA_JSR 3 +#define LITUSE_ALPHA_TLS_GD 4 +#define LITUSE_ALPHA_TLS_LDM 5 + +/* Legal values for d_tag of Elf64_Dyn. */ +#define DT_ALPHA_PLTRO (DT_LOPROC + 0) +#define DT_ALPHA_NUM 1 + +/* PowerPC specific declarations */ + +/* Values for Elf32/64_Ehdr.e_flags. */ +#define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ + +/* Cygnus local bits below */ +#define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ +#define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib + flag */ + +/* PowerPC relocations defined by the ABIs */ +#define R_PPC_NONE 0 +#define R_PPC_ADDR32 1 /* 32bit absolute address */ +#define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ +#define R_PPC_ADDR16 3 /* 16bit absolute address */ +#define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ +#define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ +#define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ +#define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ +#define R_PPC_ADDR14_BRTAKEN 8 +#define R_PPC_ADDR14_BRNTAKEN 9 +#define R_PPC_REL24 10 /* PC relative 26 bit */ +#define R_PPC_REL14 11 /* PC relative 16 bit */ +#define R_PPC_REL14_BRTAKEN 12 +#define R_PPC_REL14_BRNTAKEN 13 +#define R_PPC_GOT16 14 +#define R_PPC_GOT16_LO 15 +#define R_PPC_GOT16_HI 16 +#define R_PPC_GOT16_HA 17 +#define R_PPC_PLTREL24 18 +#define R_PPC_COPY 19 +#define R_PPC_GLOB_DAT 20 +#define R_PPC_JMP_SLOT 21 +#define R_PPC_RELATIVE 22 +#define R_PPC_LOCAL24PC 23 +#define R_PPC_UADDR32 24 +#define R_PPC_UADDR16 25 +#define R_PPC_REL32 26 +#define R_PPC_PLT32 27 +#define R_PPC_PLTREL32 28 +#define R_PPC_PLT16_LO 29 +#define R_PPC_PLT16_HI 30 +#define R_PPC_PLT16_HA 31 +#define R_PPC_SDAREL16 32 +#define R_PPC_SECTOFF 33 +#define R_PPC_SECTOFF_LO 34 +#define R_PPC_SECTOFF_HI 35 +#define R_PPC_SECTOFF_HA 36 + +/* PowerPC relocations defined for the TLS access ABI. */ +#define R_PPC_TLS 67 /* none (sym+add)@tls */ +#define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ +#define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ +#define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ +#define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ +#define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ +#define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ +#define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ +#define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ +#define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ +#define R_PPC_TLSGD 95 /* none (sym+add)@tlsgd */ +#define R_PPC_TLSLD 96 /* none (sym+add)@tlsld */ + +/* The remaining relocs are from the Embedded ELF ABI, and are not + in the SVR4 ELF ABI. */ +#define R_PPC_EMB_NADDR32 101 +#define R_PPC_EMB_NADDR16 102 +#define R_PPC_EMB_NADDR16_LO 103 +#define R_PPC_EMB_NADDR16_HI 104 +#define R_PPC_EMB_NADDR16_HA 105 +#define R_PPC_EMB_SDAI16 106 +#define R_PPC_EMB_SDA2I16 107 +#define R_PPC_EMB_SDA2REL 108 +#define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ +#define R_PPC_EMB_MRKREF 110 +#define R_PPC_EMB_RELSEC16 111 +#define R_PPC_EMB_RELST_LO 112 +#define R_PPC_EMB_RELST_HI 113 +#define R_PPC_EMB_RELST_HA 114 +#define R_PPC_EMB_BIT_FLD 115 +#define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ + +/* Diab tool relocations. */ +#define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ +#define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ +#define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ +#define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ +#define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ +#define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ + +/* GNU extension to support local ifunc. */ +#define R_PPC_IRELATIVE 248 + +/* GNU relocs used in PIC code sequences. */ +#define R_PPC_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* This is a phony reloc to handle any old fashioned TOC16 references + that may still be in object files. */ +#define R_PPC_TOC16 255 + +/* PowerPC specific values for the Dyn d_tag field. */ +#define DT_PPC_GOT (DT_LOPROC + 0) +#define DT_PPC_OPT (DT_LOPROC + 1) +#define DT_PPC_NUM 2 + +/* PowerPC specific values for the DT_PPC_OPT Dyn entry. */ +#define PPC_OPT_TLS 1 + +/* PowerPC64 relocations defined by the ABIs */ +#define R_PPC64_NONE R_PPC_NONE +#define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ +#define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ +#define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ +#define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ +#define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ +#define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ +#define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ +#define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN +#define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN +#define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ +#define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ +#define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN +#define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN +#define R_PPC64_GOT16 R_PPC_GOT16 +#define R_PPC64_GOT16_LO R_PPC_GOT16_LO +#define R_PPC64_GOT16_HI R_PPC_GOT16_HI +#define R_PPC64_GOT16_HA R_PPC_GOT16_HA + +#define R_PPC64_COPY R_PPC_COPY +#define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT +#define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT +#define R_PPC64_RELATIVE R_PPC_RELATIVE + +#define R_PPC64_UADDR32 R_PPC_UADDR32 +#define R_PPC64_UADDR16 R_PPC_UADDR16 +#define R_PPC64_REL32 R_PPC_REL32 +#define R_PPC64_PLT32 R_PPC_PLT32 +#define R_PPC64_PLTREL32 R_PPC_PLTREL32 +#define R_PPC64_PLT16_LO R_PPC_PLT16_LO +#define R_PPC64_PLT16_HI R_PPC_PLT16_HI +#define R_PPC64_PLT16_HA R_PPC_PLT16_HA + +#define R_PPC64_SECTOFF R_PPC_SECTOFF +#define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO +#define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI +#define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA +#define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ +#define R_PPC64_ADDR64 38 /* doubleword64 S + A */ +#define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ +#define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ +#define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ +#define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ +#define R_PPC64_UADDR64 43 /* doubleword64 S + A */ +#define R_PPC64_REL64 44 /* doubleword64 S + A - P */ +#define R_PPC64_PLT64 45 /* doubleword64 L + A */ +#define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ +#define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ +#define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ +#define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ +#define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ +#define R_PPC64_TOC 51 /* doubleword64 .TOC */ +#define R_PPC64_PLTGOT16 52 /* half16* M + A */ +#define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ +#define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ +#define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ + +#define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ +#define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ +#define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ +#define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ +#define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ +#define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ +#define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ +#define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ +#define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ +#define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ +#define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ + +/* PowerPC64 relocations defined for the TLS access ABI. */ +#define R_PPC64_TLS 67 /* none (sym+add)@tls */ +#define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ +#define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ +#define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ +#define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ +#define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ +#define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ +#define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ +#define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ +#define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ +#define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ +#define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ +#define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ +#define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ +#define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ +#define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ +#define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ +#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ +#define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ +#define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ +#define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ +#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ +#define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ +#define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ +#define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ +#define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ +#define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ +#define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ +#define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ +#define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ +#define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ +#define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ +#define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ +#define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ +#define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ +#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ +#define R_PPC64_TLSGD 107 /* none (sym+add)@tlsgd */ +#define R_PPC64_TLSLD 108 /* none (sym+add)@tlsld */ +#define R_PPC64_TOCSAVE 109 /* none */ + +/* Added when HA and HI relocs were changed to report overflows. */ +#define R_PPC64_ADDR16_HIGH 110 +#define R_PPC64_ADDR16_HIGHA 111 +#define R_PPC64_TPREL16_HIGH 112 +#define R_PPC64_TPREL16_HIGHA 113 +#define R_PPC64_DTPREL16_HIGH 114 +#define R_PPC64_DTPREL16_HIGHA 115 + +/* GNU extension to support local ifunc. */ +#define R_PPC64_JMP_IREL 247 +#define R_PPC64_IRELATIVE 248 +#define R_PPC64_REL16 249 /* half16 (sym+add-.) */ +#define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ +#define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ +#define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ + +/* e_flags bits specifying ABI. + 1 for original function descriptor using ABI, + 2 for revised ABI without function descriptors, + 0 for unspecified or not using any features affected by the differences. */ +#define EF_PPC64_ABI 3 + +/* PowerPC64 specific values for the Dyn d_tag field. */ +#define DT_PPC64_GLINK (DT_LOPROC + 0) +#define DT_PPC64_OPD (DT_LOPROC + 1) +#define DT_PPC64_OPDSZ (DT_LOPROC + 2) +#define DT_PPC64_OPT (DT_LOPROC + 3) +#define DT_PPC64_NUM 4 + +/* PowerPC64 specific values for the DT_PPC64_OPT Dyn entry. */ +#define PPC64_OPT_TLS 1 +#define PPC64_OPT_MULTI_TOC 2 + +/* PowerPC64 specific values for the Elf64_Sym st_other field. */ +#define STO_PPC64_LOCAL_BIT 5 +#define STO_PPC64_LOCAL_MASK (7 << STO_PPC64_LOCAL_BIT) +#define PPC64_LOCAL_ENTRY_OFFSET(other) \ + (((1 << (((other) & STO_PPC64_LOCAL_MASK) >> STO_PPC64_LOCAL_BIT)) >> 2) << 2) + + +/* ARM specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_ARM_RELEXEC 0x01 +#define EF_ARM_HASENTRY 0x02 +#define EF_ARM_INTERWORK 0x04 +#define EF_ARM_APCS_26 0x08 +#define EF_ARM_APCS_FLOAT 0x10 +#define EF_ARM_PIC 0x20 +#define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ +#define EF_ARM_NEW_ABI 0x80 +#define EF_ARM_OLD_ABI 0x100 +#define EF_ARM_SOFT_FLOAT 0x200 +#define EF_ARM_VFP_FLOAT 0x400 +#define EF_ARM_MAVERICK_FLOAT 0x800 + +#define EF_ARM_ABI_FLOAT_SOFT 0x200 /* NB conflicts with EF_ARM_SOFT_FLOAT */ +#define EF_ARM_ABI_FLOAT_HARD 0x400 /* NB conflicts with EF_ARM_VFP_FLOAT */ + + +/* Other constants defined in the ARM ELF spec. version B-01. */ +/* NB. These conflict with values defined above. */ +#define EF_ARM_SYMSARESORTED 0x04 +#define EF_ARM_DYNSYMSUSESEGIDX 0x08 +#define EF_ARM_MAPSYMSFIRST 0x10 +#define EF_ARM_EABIMASK 0XFF000000 + +/* Constants defined in AAELF. */ +#define EF_ARM_BE8 0x00800000 +#define EF_ARM_LE8 0x00400000 + +#define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) +#define EF_ARM_EABI_UNKNOWN 0x00000000 +#define EF_ARM_EABI_VER1 0x01000000 +#define EF_ARM_EABI_VER2 0x02000000 +#define EF_ARM_EABI_VER3 0x03000000 +#define EF_ARM_EABI_VER4 0x04000000 +#define EF_ARM_EABI_VER5 0x05000000 + +/* Additional symbol types for Thumb. */ +#define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ +#define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ + +/* ARM-specific values for sh_flags */ +#define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ +#define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined + in the input to a link step. */ + +/* ARM-specific program header flags */ +#define PF_ARM_SB 0x10000000 /* Segment contains the location + addressed by the static base. */ +#define PF_ARM_PI 0x20000000 /* Position-independent segment. */ +#define PF_ARM_ABS 0x40000000 /* Absolute segment. */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ +#define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ +#define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ + + +/* AArch64 relocs. */ + +#define R_AARCH64_NONE 0 /* No relocation. */ + +/* ILP32 AArch64 relocs. */ +#define R_AARCH64_P32_ABS32 1 /* Direct 32 bit. */ +#define R_AARCH64_P32_COPY 180 /* Copy symbol at runtime. */ +#define R_AARCH64_P32_GLOB_DAT 181 /* Create GOT entry. */ +#define R_AARCH64_P32_JUMP_SLOT 182 /* Create PLT entry. */ +#define R_AARCH64_P32_RELATIVE 183 /* Adjust by program base. */ +#define R_AARCH64_P32_TLS_DTPMOD 184 /* Module number, 32 bit. */ +#define R_AARCH64_P32_TLS_DTPREL 185 /* Module-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLS_TPREL 186 /* TP-relative offset, 32 bit. */ +#define R_AARCH64_P32_TLSDESC 187 /* TLS Descriptor. */ +#define R_AARCH64_P32_IRELATIVE 188 /* STT_GNU_IFUNC relocation. */ + +/* LP64 AArch64 relocs. */ +#define R_AARCH64_ABS64 257 /* Direct 64 bit. */ +#define R_AARCH64_ABS32 258 /* Direct 32 bit. */ +#define R_AARCH64_ABS16 259 /* Direct 16-bit. */ +#define R_AARCH64_PREL64 260 /* PC-relative 64-bit. */ +#define R_AARCH64_PREL32 261 /* PC-relative 32-bit. */ +#define R_AARCH64_PREL16 262 /* PC-relative 16-bit. */ +#define R_AARCH64_MOVW_UABS_G0 263 /* Dir. MOVZ imm. from bits 15:0. */ +#define R_AARCH64_MOVW_UABS_G0_NC 264 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G1 265 /* Dir. MOVZ imm. from bits 31:16. */ +#define R_AARCH64_MOVW_UABS_G1_NC 266 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G2 267 /* Dir. MOVZ imm. from bits 47:32. */ +#define R_AARCH64_MOVW_UABS_G2_NC 268 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_UABS_G3 269 /* Dir. MOV{K,Z} imm. from 63:48. */ +#define R_AARCH64_MOVW_SABS_G0 270 /* Dir. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_SABS_G1 271 /* Dir. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_SABS_G2 272 /* Dir. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_LD_PREL_LO19 273 /* PC-rel. LD imm. from bits 20:2. */ +#define R_AARCH64_ADR_PREL_LO21 274 /* PC-rel. ADR imm. from bits 20:0. */ +#define R_AARCH64_ADR_PREL_PG_HI21 275 /* Page-rel. ADRP imm. from 32:12. */ +#define R_AARCH64_ADR_PREL_PG_HI21_NC 276 /* Likewise; no overflow check. */ +#define R_AARCH64_ADD_ABS_LO12_NC 277 /* Dir. ADD imm. from bits 11:0. */ +#define R_AARCH64_LDST8_ABS_LO12_NC 278 /* Likewise for LD/ST; no check. */ +#define R_AARCH64_TSTBR14 279 /* PC-rel. TBZ/TBNZ imm. from 15:2. */ +#define R_AARCH64_CONDBR19 280 /* PC-rel. cond. br. imm. from 20:2. */ +#define R_AARCH64_JUMP26 282 /* PC-rel. B imm. from bits 27:2. */ +#define R_AARCH64_CALL26 283 /* Likewise for CALL. */ +#define R_AARCH64_LDST16_ABS_LO12_NC 284 /* Dir. ADD imm. from bits 11:1. */ +#define R_AARCH64_LDST32_ABS_LO12_NC 285 /* Likewise for bits 11:2. */ +#define R_AARCH64_LDST64_ABS_LO12_NC 286 /* Likewise for bits 11:3. */ +#define R_AARCH64_MOVW_PREL_G0 287 /* PC-rel. MOV{N,Z} imm. from 15:0. */ +#define R_AARCH64_MOVW_PREL_G0_NC 288 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G1 289 /* PC-rel. MOV{N,Z} imm. from 31:16. */ +#define R_AARCH64_MOVW_PREL_G1_NC 290 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G2 291 /* PC-rel. MOV{N,Z} imm. from 47:32. */ +#define R_AARCH64_MOVW_PREL_G2_NC 292 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_PREL_G3 293 /* PC-rel. MOV{N,Z} imm. from 63:48. */ +#define R_AARCH64_LDST128_ABS_LO12_NC 299 /* Dir. ADD imm. from bits 11:4. */ +#define R_AARCH64_MOVW_GOTOFF_G0 300 /* GOT-rel. off. MOV{N,Z} imm. 15:0. */ +#define R_AARCH64_MOVW_GOTOFF_G0_NC 301 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G1 302 /* GOT-rel. o. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_MOVW_GOTOFF_G1_NC 303 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G2 304 /* GOT-rel. o. MOV{N,Z} imm. 47:32. */ +#define R_AARCH64_MOVW_GOTOFF_G2_NC 305 /* Likewise for MOVK; no check. */ +#define R_AARCH64_MOVW_GOTOFF_G3 306 /* GOT-rel. o. MOV{N,Z} imm. 63:48. */ +#define R_AARCH64_GOTREL64 307 /* GOT-relative 64-bit. */ +#define R_AARCH64_GOTREL32 308 /* GOT-relative 32-bit. */ +#define R_AARCH64_GOT_LD_PREL19 309 /* PC-rel. GOT off. load imm. 20:2. */ +#define R_AARCH64_LD64_GOTOFF_LO15 310 /* GOT-rel. off. LD/ST imm. 14:3. */ +#define R_AARCH64_ADR_GOT_PAGE 311 /* P-page-rel. GOT off. ADRP 32:12. */ +#define R_AARCH64_LD64_GOT_LO12_NC 312 /* Dir. GOT off. LD/ST imm. 11:3. */ +#define R_AARCH64_LD64_GOTPAGE_LO15 313 /* GOT-page-rel. GOT off. LD/ST 14:3 */ +#define R_AARCH64_TLSGD_ADR_PREL21 512 /* PC-relative ADR imm. 20:0. */ +#define R_AARCH64_TLSGD_ADR_PAGE21 513 /* page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSGD_ADD_LO12_NC 514 /* direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSGD_MOVW_G1 515 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSGD_MOVW_G0_NC 516 /* GOT-rel. MOVK imm. 15:0. */ +#define R_AARCH64_TLSLD_ADR_PREL21 517 /* Like 512; local dynamic model. */ +#define R_AARCH64_TLSLD_ADR_PAGE21 518 /* Like 513; local dynamic model. */ +#define R_AARCH64_TLSLD_ADD_LO12_NC 519 /* Like 514; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G1 520 /* Like 515; local dynamic model. */ +#define R_AARCH64_TLSLD_MOVW_G0_NC 521 /* Like 516; local dynamic model. */ +#define R_AARCH64_TLSLD_LD_PREL19 522 /* TLS PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G2 523 /* TLS DTP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1 524 /* TLS DTP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G1_NC 525 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0 526 /* TLS DTP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLD_MOVW_DTPREL_G0_NC 527 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_HI12 528 /* DTP-rel. ADD imm. from 23:12. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12 529 /* DTP-rel. ADD imm. from 11:0. */ +#define R_AARCH64_TLSLD_ADD_DTPREL_LO12_NC 530 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12 531 /* DTP-rel. LD/ST imm. 11:0. */ +#define R_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC 532 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12 533 /* DTP-rel. LD/ST imm. 11:1. */ +#define R_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC 534 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12 535 /* DTP-rel. LD/ST imm. 11:2. */ +#define R_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC 536 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12 537 /* DTP-rel. LD/ST imm. 11:3. */ +#define R_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC 538 /* Likewise; no check. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G1 539 /* GOT-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC 540 /* GOT-rel. MOVK 15:0. */ +#define R_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21 541 /* Page-rel. ADRP 32:12. */ +#define R_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC 542 /* Direct LD off. 11:3. */ +#define R_AARCH64_TLSIE_LD_GOTTPREL_PREL19 543 /* PC-rel. load imm. 20:2. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G2 544 /* TLS TP-rel. MOV{N,Z} 47:32. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1 545 /* TLS TP-rel. MOV{N,Z} 31:16. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G1_NC 546 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0 547 /* TLS TP-rel. MOV{N,Z} 15:0. */ +#define R_AARCH64_TLSLE_MOVW_TPREL_G0_NC 548 /* Likewise; MOVK; no check. */ +#define R_AARCH64_TLSLE_ADD_TPREL_HI12 549 /* TP-rel. ADD imm. 23:12. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12 550 /* TP-rel. ADD imm. 11:0. */ +#define R_AARCH64_TLSLE_ADD_TPREL_LO12_NC 551 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12 552 /* TP-rel. LD/ST off. 11:0. */ +#define R_AARCH64_TLSLE_LDST8_TPREL_LO12_NC 553 /* Likewise; no ovfl. check. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12 554 /* TP-rel. LD/ST off. 11:1. */ +#define R_AARCH64_TLSLE_LDST16_TPREL_LO12_NC 555 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12 556 /* TP-rel. LD/ST off. 11:2. */ +#define R_AARCH64_TLSLE_LDST32_TPREL_LO12_NC 557 /* Likewise; no check. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12 558 /* TP-rel. LD/ST off. 11:3. */ +#define R_AARCH64_TLSLE_LDST64_TPREL_LO12_NC 559 /* Likewise; no check. */ +#define R_AARCH64_TLSDESC_LD_PREL19 560 /* PC-rel. load immediate 20:2. */ +#define R_AARCH64_TLSDESC_ADR_PREL21 561 /* PC-rel. ADR immediate 20:0. */ +#define R_AARCH64_TLSDESC_ADR_PAGE21 562 /* Page-rel. ADRP imm. 32:12. */ +#define R_AARCH64_TLSDESC_LD64_LO12 563 /* Direct LD off. from 11:3. */ +#define R_AARCH64_TLSDESC_ADD_LO12 564 /* Direct ADD imm. from 11:0. */ +#define R_AARCH64_TLSDESC_OFF_G1 565 /* GOT-rel. MOV{N,Z} imm. 31:16. */ +#define R_AARCH64_TLSDESC_OFF_G0_NC 566 /* GOT-rel. MOVK imm. 15:0; no ck. */ +#define R_AARCH64_TLSDESC_LDR 567 /* Relax LDR. */ +#define R_AARCH64_TLSDESC_ADD 568 /* Relax ADD. */ +#define R_AARCH64_TLSDESC_CALL 569 /* Relax BLR. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12 570 /* TP-rel. LD/ST off. 11:4. */ +#define R_AARCH64_TLSLE_LDST128_TPREL_LO12_NC 571 /* Likewise; no check. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12 572 /* DTP-rel. LD/ST imm. 11:4. */ +#define R_AARCH64_TLSLD_LDST128_DTPREL_LO12_NC 573 /* Likewise; no check. */ +#define R_AARCH64_COPY 1024 /* Copy symbol at runtime. */ +#define R_AARCH64_GLOB_DAT 1025 /* Create GOT entry. */ +#define R_AARCH64_JUMP_SLOT 1026 /* Create PLT entry. */ +#define R_AARCH64_RELATIVE 1027 /* Adjust by program base. */ +#define R_AARCH64_TLS_DTPMOD 1028 /* Module number, 64 bit. */ +#define R_AARCH64_TLS_DTPREL 1029 /* Module-relative offset, 64 bit. */ +#define R_AARCH64_TLS_TPREL 1030 /* TP-relative offset, 64 bit. */ +#define R_AARCH64_TLSDESC 1031 /* TLS Descriptor. */ +#define R_AARCH64_IRELATIVE 1032 /* STT_GNU_IFUNC relocation. */ + +/* ARM relocs. */ + +#define R_ARM_NONE 0 /* No reloc */ +#define R_ARM_PC24 1 /* Deprecated PC relative 26 + bit branch. */ +#define R_ARM_ABS32 2 /* Direct 32 bit */ +#define R_ARM_REL32 3 /* PC relative 32 bit */ +#define R_ARM_PC13 4 +#define R_ARM_ABS16 5 /* Direct 16 bit */ +#define R_ARM_ABS12 6 /* Direct 12 bit */ +#define R_ARM_THM_ABS5 7 /* Direct & 0x7C (LDR, STR). */ +#define R_ARM_ABS8 8 /* Direct 8 bit */ +#define R_ARM_SBREL32 9 +#define R_ARM_THM_PC22 10 /* PC relative 24 bit (Thumb32 BL). */ +#define R_ARM_THM_PC8 11 /* PC relative & 0x3FC + (Thumb16 LDR, ADD, ADR). */ +#define R_ARM_AMP_VCALL9 12 +#define R_ARM_SWI24 13 /* Obsolete static relocation. */ +#define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ +#define R_ARM_THM_SWI8 14 /* Reserved. */ +#define R_ARM_XPC25 15 /* Reserved. */ +#define R_ARM_THM_XPC22 16 /* Reserved. */ +#define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ +#define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ +#define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ +#define R_ARM_COPY 20 /* Copy symbol at runtime */ +#define R_ARM_GLOB_DAT 21 /* Create GOT entry */ +#define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ +#define R_ARM_RELATIVE 23 /* Adjust by program base */ +#define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ +#define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ +#define R_ARM_GOT32 26 /* 32 bit GOT entry */ +#define R_ARM_PLT32 27 /* Deprecated, 32 bit PLT address. */ +#define R_ARM_CALL 28 /* PC relative 24 bit (BL, BLX). */ +#define R_ARM_JUMP24 29 /* PC relative 24 bit + (B, BL<cond>). */ +#define R_ARM_THM_JUMP24 30 /* PC relative 24 bit (Thumb32 B.W). */ +#define R_ARM_BASE_ABS 31 /* Adjust by program base. */ +#define R_ARM_ALU_PCREL_7_0 32 /* Obsolete. */ +#define R_ARM_ALU_PCREL_15_8 33 /* Obsolete. */ +#define R_ARM_ALU_PCREL_23_15 34 /* Obsolete. */ +#define R_ARM_LDR_SBREL_11_0 35 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_19_12 36 /* Deprecated, prog. base relative. */ +#define R_ARM_ALU_SBREL_27_20 37 /* Deprecated, prog. base relative. */ +#define R_ARM_TARGET1 38 +#define R_ARM_SBREL31 39 /* Program base relative. */ +#define R_ARM_V4BX 40 +#define R_ARM_TARGET2 41 +#define R_ARM_PREL31 42 /* 32 bit PC relative. */ +#define R_ARM_MOVW_ABS_NC 43 /* Direct 16-bit (MOVW). */ +#define R_ARM_MOVT_ABS 44 /* Direct high 16-bit (MOVT). */ +#define R_ARM_MOVW_PREL_NC 45 /* PC relative 16-bit (MOVW). */ +#define R_ARM_MOVT_PREL 46 /* PC relative (MOVT). */ +#define R_ARM_THM_MOVW_ABS_NC 47 /* Direct 16 bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_ABS 48 /* Direct high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_PREL_NC 49 /* PC relative 16 bit + (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_PREL 50 /* PC relative high 16 bit + (Thumb32 MOVT). */ +#define R_ARM_THM_JUMP19 51 /* PC relative 20 bit + (Thumb32 B<cond>.W). */ +#define R_ARM_THM_JUMP6 52 /* PC relative X & 0x7E + (Thumb16 CBZ, CBNZ). */ +#define R_ARM_THM_ALU_PREL_11_0 53 /* PC relative 12 bit + (Thumb32 ADR.W). */ +#define R_ARM_THM_PC12 54 /* PC relative 12 bit + (Thumb32 LDR{D,SB,H,SH}). */ +#define R_ARM_ABS32_NOI 55 /* Direct 32-bit. */ +#define R_ARM_REL32_NOI 56 /* PC relative 32-bit. */ +#define R_ARM_ALU_PC_G0_NC 57 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G0 58 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1_NC 59 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G1 60 /* PC relative (ADD, SUB). */ +#define R_ARM_ALU_PC_G2 61 /* PC relative (ADD, SUB). */ +#define R_ARM_LDR_PC_G1 62 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDR_PC_G2 63 /* PC relative (LDR,STR,LDRB,STRB). */ +#define R_ARM_LDRS_PC_G0 64 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G1 65 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDRS_PC_G2 66 /* PC relative (STR{D,H}, + LDR{D,SB,H,SH}). */ +#define R_ARM_LDC_PC_G0 67 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G1 68 /* PC relative (LDC, STC). */ +#define R_ARM_LDC_PC_G2 69 /* PC relative (LDC, STC). */ +#define R_ARM_ALU_SB_G0_NC 70 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G0 71 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1_NC 72 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G1 73 /* Program base relative (ADD,SUB). */ +#define R_ARM_ALU_SB_G2 74 /* Program base relative (ADD,SUB). */ +#define R_ARM_LDR_SB_G0 75 /* Program base relative (LDR, + STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G1 76 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDR_SB_G2 77 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G0 78 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G1 79 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDRS_SB_G2 80 /* Program base relative + (LDR, STR, LDRB, STRB). */ +#define R_ARM_LDC_SB_G0 81 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G1 82 /* Program base relative (LDC,STC). */ +#define R_ARM_LDC_SB_G2 83 /* Program base relative (LDC,STC). */ +#define R_ARM_MOVW_BREL_NC 84 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_MOVT_BREL 85 /* Program base relative high + 16 bit (MOVT). */ +#define R_ARM_MOVW_BREL 86 /* Program base relative 16 + bit (MOVW). */ +#define R_ARM_THM_MOVW_BREL_NC 87 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_THM_MOVT_BREL 88 /* Program base relative high + 16 bit (Thumb32 MOVT). */ +#define R_ARM_THM_MOVW_BREL 89 /* Program base relative 16 + bit (Thumb32 MOVW). */ +#define R_ARM_TLS_GOTDESC 90 +#define R_ARM_TLS_CALL 91 +#define R_ARM_TLS_DESCSEQ 92 /* TLS relaxation. */ +#define R_ARM_THM_TLS_CALL 93 +#define R_ARM_PLT32_ABS 94 +#define R_ARM_GOT_ABS 95 /* GOT entry. */ +#define R_ARM_GOT_PREL 96 /* PC relative GOT entry. */ +#define R_ARM_GOT_BREL12 97 /* GOT entry relative to GOT + origin (LDR). */ +#define R_ARM_GOTOFF12 98 /* 12 bit, GOT entry relative + to GOT origin (LDR, STR). */ +#define R_ARM_GOTRELAX 99 +#define R_ARM_GNU_VTENTRY 100 +#define R_ARM_GNU_VTINHERIT 101 +#define R_ARM_THM_PC11 102 /* PC relative & 0xFFE (Thumb16 B). */ +#define R_ARM_THM_PC9 103 /* PC relative & 0x1FE + (Thumb16 B/B<cond>). */ +#define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic + thread local data */ +#define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic + thread local data */ +#define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS + block */ +#define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of + static TLS block offset */ +#define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static + TLS block */ +#define R_ARM_TLS_LDO12 109 /* 12 bit relative to TLS + block (LDR, STR). */ +#define R_ARM_TLS_LE12 110 /* 12 bit relative to static + TLS block (LDR, STR). */ +#define R_ARM_TLS_IE12GP 111 /* 12 bit GOT entry relative + to GOT origin (LDR). */ +#define R_ARM_ME_TOO 128 /* Obsolete. */ +#define R_ARM_THM_TLS_DESCSEQ 129 +#define R_ARM_THM_TLS_DESCSEQ16 129 +#define R_ARM_THM_TLS_DESCSEQ32 130 +#define R_ARM_THM_GOT_BREL12 131 /* GOT entry relative to GOT + origin, 12 bit (Thumb32 LDR). */ +#define R_ARM_IRELATIVE 160 +#define R_ARM_RXPC25 249 +#define R_ARM_RSBREL32 250 +#define R_ARM_THM_RPC22 251 +#define R_ARM_RREL32 252 +#define R_ARM_RABS22 253 +#define R_ARM_RPC24 254 +#define R_ARM_RBASE 255 +/* Keep this the last entry. */ +#define R_ARM_NUM 256 + +/* IA-64 specific declarations. */ + +/* Processor specific flags for the Ehdr e_flags field. */ +#define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ +#define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ +#define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ + +/* Processor specific values for the Phdr p_type field. */ +#define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ +#define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ +#define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) +#define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) +#define PT_IA_64_HP_STACK (PT_LOOS + 0x14) + +/* Processor specific flags for the Phdr p_flags field. */ +#define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Shdr sh_type field. */ +#define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ +#define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ + +/* Processor specific flags for the Shdr sh_flags field. */ +#define SHF_IA_64_SHORT 0x10000000 /* section near gp */ +#define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ + +/* Processor specific values for the Dyn d_tag field. */ +#define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) +#define DT_IA_64_NUM 1 + +/* IA-64 relocations. */ +#define R_IA64_NONE 0x00 /* none */ +#define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ +#define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ +#define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ +#define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ +#define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ +#define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ +#define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ +#define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ +#define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ +#define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ +#define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ +#define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ +#define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ +#define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ +#define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ +#define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ +#define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ +#define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ +#define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ +#define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ +#define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ +#define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ +#define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ +#define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ +#define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ +#define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ +#define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ +#define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ +#define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ +#define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ +#define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ +#define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ +#define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ +#define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ +#define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ +#define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ +#define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ +#define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ +#define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ +#define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ +#define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ +#define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ +#define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ +#define R_IA64_REL32MSB 0x6c /* data 4 + REL */ +#define R_IA64_REL32LSB 0x6d /* data 4 + REL */ +#define R_IA64_REL64MSB 0x6e /* data 8 + REL */ +#define R_IA64_REL64LSB 0x6f /* data 8 + REL */ +#define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ +#define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ +#define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ +#define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ +#define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ +#define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ +#define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ +#define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ +#define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ +#define R_IA64_COPY 0x84 /* copy relocation */ +#define R_IA64_SUB 0x85 /* Addend and symbol difference */ +#define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ +#define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ +#define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ +#define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ +#define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ +#define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ +#define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ +#define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ +#define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ +#define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ +#define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ +#define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ +#define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ +#define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ +#define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ +#define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ +#define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ + +/* SH specific declarations */ + +/* Processor specific flags for the ELF header e_flags field. */ +#define EF_SH_MACH_MASK 0x1f +#define EF_SH_UNKNOWN 0x0 +#define EF_SH1 0x1 +#define EF_SH2 0x2 +#define EF_SH3 0x3 +#define EF_SH_DSP 0x4 +#define EF_SH3_DSP 0x5 +#define EF_SH4AL_DSP 0x6 +#define EF_SH3E 0x8 +#define EF_SH4 0x9 +#define EF_SH2E 0xb +#define EF_SH4A 0xc +#define EF_SH2A 0xd +#define EF_SH4_NOFPU 0x10 +#define EF_SH4A_NOFPU 0x11 +#define EF_SH4_NOMMU_NOFPU 0x12 +#define EF_SH2A_NOFPU 0x13 +#define EF_SH3_NOMMU 0x14 +#define EF_SH2A_SH4_NOFPU 0x15 +#define EF_SH2A_SH3_NOFPU 0x16 +#define EF_SH2A_SH4 0x17 +#define EF_SH2A_SH3E 0x18 + +/* SH relocs. */ +#define R_SH_NONE 0 +#define R_SH_DIR32 1 +#define R_SH_REL32 2 +#define R_SH_DIR8WPN 3 +#define R_SH_IND12W 4 +#define R_SH_DIR8WPL 5 +#define R_SH_DIR8WPZ 6 +#define R_SH_DIR8BP 7 +#define R_SH_DIR8W 8 +#define R_SH_DIR8L 9 +#define R_SH_SWITCH16 25 +#define R_SH_SWITCH32 26 +#define R_SH_USES 27 +#define R_SH_COUNT 28 +#define R_SH_ALIGN 29 +#define R_SH_CODE 30 +#define R_SH_DATA 31 +#define R_SH_LABEL 32 +#define R_SH_SWITCH8 33 +#define R_SH_GNU_VTINHERIT 34 +#define R_SH_GNU_VTENTRY 35 +#define R_SH_TLS_GD_32 144 +#define R_SH_TLS_LD_32 145 +#define R_SH_TLS_LDO_32 146 +#define R_SH_TLS_IE_32 147 +#define R_SH_TLS_LE_32 148 +#define R_SH_TLS_DTPMOD32 149 +#define R_SH_TLS_DTPOFF32 150 +#define R_SH_TLS_TPOFF32 151 +#define R_SH_GOT32 160 +#define R_SH_PLT32 161 +#define R_SH_COPY 162 +#define R_SH_GLOB_DAT 163 +#define R_SH_JMP_SLOT 164 +#define R_SH_RELATIVE 165 +#define R_SH_GOTOFF 166 +#define R_SH_GOTPC 167 +/* Keep this the last entry. */ +#define R_SH_NUM 256 + +/* S/390 specific definitions. */ + +/* Valid values for the e_flags field. */ + +#define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ + +/* Additional s390 relocs */ + +#define R_390_NONE 0 /* No reloc. */ +#define R_390_8 1 /* Direct 8 bit. */ +#define R_390_12 2 /* Direct 12 bit. */ +#define R_390_16 3 /* Direct 16 bit. */ +#define R_390_32 4 /* Direct 32 bit. */ +#define R_390_PC32 5 /* PC relative 32 bit. */ +#define R_390_GOT12 6 /* 12 bit GOT offset. */ +#define R_390_GOT32 7 /* 32 bit GOT offset. */ +#define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ +#define R_390_COPY 9 /* Copy symbol at runtime. */ +#define R_390_GLOB_DAT 10 /* Create GOT entry. */ +#define R_390_JMP_SLOT 11 /* Create PLT entry. */ +#define R_390_RELATIVE 12 /* Adjust by program base. */ +#define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ +#define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ +#define R_390_GOT16 15 /* 16 bit GOT offset. */ +#define R_390_PC16 16 /* PC relative 16 bit. */ +#define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ +#define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ +#define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ +#define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ +#define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ +#define R_390_64 22 /* Direct 64 bit. */ +#define R_390_PC64 23 /* PC relative 64 bit. */ +#define R_390_GOT64 24 /* 64 bit GOT offset. */ +#define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ +#define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ +#define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ +#define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ +#define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ +#define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ +#define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ +#define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ +#define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ +#define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ +#define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ +#define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ +#define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ +#define R_390_TLS_GDCALL 38 /* Tag for function call in general + dynamic TLS code. */ +#define R_390_TLS_LDCALL 39 /* Tag for function call in local + dynamic TLS code. */ +#define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic + thread local data. */ +#define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic + thread local data. */ +#define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS + block offset. */ +#define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic + thread local data in LE code. */ +#define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for + negated static TLS block offset. */ +#define R_390_TLS_LE32 50 /* 32 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LE64 51 /* 64 bit negated offset relative to + static TLS block. */ +#define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS + block. */ +#define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS + block. */ +#define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ +#define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ +#define R_390_TLS_TPOFF 56 /* Negated offset in static TLS + block. */ +#define R_390_20 57 /* Direct 20 bit. */ +#define R_390_GOT20 58 /* 20 bit GOT offset. */ +#define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ +#define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS + block offset. */ +#define R_390_IRELATIVE 61 /* STT_GNU_IFUNC relocation. */ +/* Keep this the last entry. */ +#define R_390_NUM 62 + + +/* CRIS relocations. */ +#define R_CRIS_NONE 0 +#define R_CRIS_8 1 +#define R_CRIS_16 2 +#define R_CRIS_32 3 +#define R_CRIS_8_PCREL 4 +#define R_CRIS_16_PCREL 5 +#define R_CRIS_32_PCREL 6 +#define R_CRIS_GNU_VTINHERIT 7 +#define R_CRIS_GNU_VTENTRY 8 +#define R_CRIS_COPY 9 +#define R_CRIS_GLOB_DAT 10 +#define R_CRIS_JUMP_SLOT 11 +#define R_CRIS_RELATIVE 12 +#define R_CRIS_16_GOT 13 +#define R_CRIS_32_GOT 14 +#define R_CRIS_16_GOTPLT 15 +#define R_CRIS_32_GOTPLT 16 +#define R_CRIS_32_GOTREL 17 +#define R_CRIS_32_PLT_GOTREL 18 +#define R_CRIS_32_PLT_PCREL 19 + +#define R_CRIS_NUM 20 + + +/* AMD x86-64 relocations. */ +#define R_X86_64_NONE 0 /* No reloc */ +#define R_X86_64_64 1 /* Direct 64 bit */ +#define R_X86_64_PC32 2 /* PC relative 32 bit signed */ +#define R_X86_64_GOT32 3 /* 32 bit GOT entry */ +#define R_X86_64_PLT32 4 /* 32 bit PLT address */ +#define R_X86_64_COPY 5 /* Copy symbol at runtime */ +#define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ +#define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ +#define R_X86_64_RELATIVE 8 /* Adjust by program base */ +#define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative + offset to GOT */ +#define R_X86_64_32 10 /* Direct 32 bit zero extended */ +#define R_X86_64_32S 11 /* Direct 32 bit sign extended */ +#define R_X86_64_16 12 /* Direct 16 bit zero extended */ +#define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ +#define R_X86_64_8 14 /* Direct 8 bit sign extended */ +#define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ +#define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ +#define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ +#define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ +#define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset + to two GOT entries for GD symbol */ +#define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset + to two GOT entries for LD symbol */ +#define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ +#define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset + to GOT entry for IE symbol */ +#define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ +#define R_X86_64_PC64 24 /* PC relative 64 bit */ +#define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ +#define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative + offset to GOT */ +#define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ +#define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset + to GOT entry */ +#define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ +#define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ +#define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset + to PLT entry */ +#define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ +#define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ +#define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ +#define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS + descriptor. */ +#define R_X86_64_TLSDESC 36 /* TLS descriptor. */ +#define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ +#define R_X86_64_RELATIVE64 38 /* 64-bit adjust by program base */ + +#define R_X86_64_NUM 39 + + +/* AM33 relocations. */ +#define R_MN10300_NONE 0 /* No reloc. */ +#define R_MN10300_32 1 /* Direct 32 bit. */ +#define R_MN10300_16 2 /* Direct 16 bit. */ +#define R_MN10300_8 3 /* Direct 8 bit. */ +#define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ +#define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ +#define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ +#define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ +#define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ +#define R_MN10300_24 9 /* Direct 24 bit. */ +#define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ +#define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ +#define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ +#define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ +#define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ +#define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ +#define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ +#define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ +#define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ +#define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ +#define R_MN10300_COPY 20 /* Copy symbol at runtime. */ +#define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ +#define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ +#define R_MN10300_RELATIVE 23 /* Adjust by program base. */ +#define R_MN10300_TLS_GD 24 /* 32-bit offset for global dynamic. */ +#define R_MN10300_TLS_LD 25 /* 32-bit offset for local dynamic. */ +#define R_MN10300_TLS_LDO 26 /* Module-relative offset. */ +#define R_MN10300_TLS_GOTIE 27 /* GOT offset for static TLS block + offset. */ +#define R_MN10300_TLS_IE 28 /* GOT address for static TLS block + offset. */ +#define R_MN10300_TLS_LE 29 /* Offset relative to static TLS + block. */ +#define R_MN10300_TLS_DTPMOD 30 /* ID of module containing symbol. */ +#define R_MN10300_TLS_DTPOFF 31 /* Offset in module TLS block. */ +#define R_MN10300_TLS_TPOFF 32 /* Offset in static TLS block. */ +#define R_MN10300_SYM_DIFF 33 /* Adjustment for next reloc as needed + by linker relaxation. */ +#define R_MN10300_ALIGN 34 /* Alignment requirement for linker + relaxation. */ +#define R_MN10300_NUM 35 + + +/* M32R relocs. */ +#define R_M32R_NONE 0 /* No reloc. */ +#define R_M32R_16 1 /* Direct 16 bit. */ +#define R_M32R_32 2 /* Direct 32 bit. */ +#define R_M32R_24 3 /* Direct 24 bit. */ +#define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ +#define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ +#define R_M32R_LO16 9 /* Low 16 bit. */ +#define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ +#define R_M32R_GNU_VTINHERIT 11 +#define R_M32R_GNU_VTENTRY 12 +/* M32R relocs use SHT_RELA. */ +#define R_M32R_16_RELA 33 /* Direct 16 bit. */ +#define R_M32R_32_RELA 34 /* Direct 32 bit. */ +#define R_M32R_24_RELA 35 /* Direct 24 bit. */ +#define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ +#define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ +#define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ +#define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ +#define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ +#define R_M32R_LO16_RELA 41 /* Low 16 bit */ +#define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ +#define R_M32R_RELA_GNU_VTINHERIT 43 +#define R_M32R_RELA_GNU_VTENTRY 44 +#define R_M32R_REL32 45 /* PC relative 32 bit. */ + +#define R_M32R_GOT24 48 /* 24 bit GOT entry */ +#define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ +#define R_M32R_COPY 50 /* Copy symbol at runtime */ +#define R_M32R_GLOB_DAT 51 /* Create GOT entry */ +#define R_M32R_JMP_SLOT 52 /* Create PLT entry */ +#define R_M32R_RELATIVE 53 /* Adjust by program base */ +#define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ +#define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ +#define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned + low */ +#define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed + low */ +#define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ +#define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to + GOT with unsigned low */ +#define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to + GOT with signed low */ +#define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to + GOT */ +#define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT + with unsigned low */ +#define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT + with signed low */ +#define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ +#define R_M32R_NUM 256 /* Keep this the last entry. */ + +/* MicroBlaze relocations */ +#define R_MICROBLAZE_NONE 0 /* No reloc. */ +#define R_MICROBLAZE_32 1 /* Direct 32 bit. */ +#define R_MICROBLAZE_32_PCREL 2 /* PC relative 32 bit. */ +#define R_MICROBLAZE_64_PCREL 3 /* PC relative 64 bit. */ +#define R_MICROBLAZE_32_PCREL_LO 4 /* Low 16 bits of PCREL32. */ +#define R_MICROBLAZE_64 5 /* Direct 64 bit. */ +#define R_MICROBLAZE_32_LO 6 /* Low 16 bit. */ +#define R_MICROBLAZE_SRO32 7 /* Read-only small data area. */ +#define R_MICROBLAZE_SRW32 8 /* Read-write small data area. */ +#define R_MICROBLAZE_64_NONE 9 /* No reloc. */ +#define R_MICROBLAZE_32_SYM_OP_SYM 10 /* Symbol Op Symbol relocation. */ +#define R_MICROBLAZE_GNU_VTINHERIT 11 /* GNU C++ vtable hierarchy. */ +#define R_MICROBLAZE_GNU_VTENTRY 12 /* GNU C++ vtable member usage. */ +#define R_MICROBLAZE_GOTPC_64 13 /* PC-relative GOT offset. */ +#define R_MICROBLAZE_GOT_64 14 /* GOT entry offset. */ +#define R_MICROBLAZE_PLT_64 15 /* PLT offset (PC-relative). */ +#define R_MICROBLAZE_REL 16 /* Adjust by program base. */ +#define R_MICROBLAZE_JUMP_SLOT 17 /* Create PLT entry. */ +#define R_MICROBLAZE_GLOB_DAT 18 /* Create GOT entry. */ +#define R_MICROBLAZE_GOTOFF_64 19 /* 64 bit offset to GOT. */ +#define R_MICROBLAZE_GOTOFF_32 20 /* 32 bit offset to GOT. */ +#define R_MICROBLAZE_COPY 21 /* Runtime copy. */ +#define R_MICROBLAZE_TLS 22 /* TLS Reloc. */ +#define R_MICROBLAZE_TLSGD 23 /* TLS General Dynamic. */ +#define R_MICROBLAZE_TLSLD 24 /* TLS Local Dynamic. */ +#define R_MICROBLAZE_TLSDTPMOD32 25 /* TLS Module ID. */ +#define R_MICROBLAZE_TLSDTPREL32 26 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSDTPREL64 27 /* TLS Offset Within TLS Block. */ +#define R_MICROBLAZE_TLSGOTTPREL32 28 /* TLS Offset From Thread Pointer. */ +#define R_MICROBLAZE_TLSTPREL32 29 /* TLS Offset From Thread Pointer. */ + +/* Legal values for d_tag (dynamic entry type). */ +#define DT_NIOS2_GP 0x70000002 /* Address of _gp. */ + +/* Nios II relocations. */ +#define R_NIOS2_NONE 0 /* No reloc. */ +#define R_NIOS2_S16 1 /* Direct signed 16 bit. */ +#define R_NIOS2_U16 2 /* Direct unsigned 16 bit. */ +#define R_NIOS2_PCREL16 3 /* PC relative 16 bit. */ +#define R_NIOS2_CALL26 4 /* Direct call. */ +#define R_NIOS2_IMM5 5 /* 5 bit constant expression. */ +#define R_NIOS2_CACHE_OPX 6 /* 5 bit expression, shift 22. */ +#define R_NIOS2_IMM6 7 /* 6 bit constant expression. */ +#define R_NIOS2_IMM8 8 /* 8 bit constant expression. */ +#define R_NIOS2_HI16 9 /* High 16 bit. */ +#define R_NIOS2_LO16 10 /* Low 16 bit. */ +#define R_NIOS2_HIADJ16 11 /* High 16 bit, adjusted. */ +#define R_NIOS2_BFD_RELOC_32 12 /* 32 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_16 13 /* 16 bit symbol value + addend. */ +#define R_NIOS2_BFD_RELOC_8 14 /* 8 bit symbol value + addend. */ +#define R_NIOS2_GPREL 15 /* 16 bit GP pointer offset. */ +#define R_NIOS2_GNU_VTINHERIT 16 /* GNU C++ vtable hierarchy. */ +#define R_NIOS2_GNU_VTENTRY 17 /* GNU C++ vtable member usage. */ +#define R_NIOS2_UJMP 18 /* Unconditional branch. */ +#define R_NIOS2_CJMP 19 /* Conditional branch. */ +#define R_NIOS2_CALLR 20 /* Indirect call through register. */ +#define R_NIOS2_ALIGN 21 /* Alignment requirement for + linker relaxation. */ +#define R_NIOS2_GOT16 22 /* 16 bit GOT entry. */ +#define R_NIOS2_CALL16 23 /* 16 bit GOT entry for function. */ +#define R_NIOS2_GOTOFF_LO 24 /* %lo of offset to GOT pointer. */ +#define R_NIOS2_GOTOFF_HA 25 /* %hiadj of offset to GOT pointer. */ +#define R_NIOS2_PCREL_LO 26 /* %lo of PC relative offset. */ +#define R_NIOS2_PCREL_HA 27 /* %hiadj of PC relative offset. */ +#define R_NIOS2_TLS_GD16 28 /* 16 bit GOT offset for TLS GD. */ +#define R_NIOS2_TLS_LDM16 29 /* 16 bit GOT offset for TLS LDM. */ +#define R_NIOS2_TLS_LDO16 30 /* 16 bit module relative offset. */ +#define R_NIOS2_TLS_IE16 31 /* 16 bit GOT offset for TLS IE. */ +#define R_NIOS2_TLS_LE16 32 /* 16 bit LE TP-relative offset. */ +#define R_NIOS2_TLS_DTPMOD 33 /* Module number. */ +#define R_NIOS2_TLS_DTPREL 34 /* Module-relative offset. */ +#define R_NIOS2_TLS_TPREL 35 /* TP-relative offset. */ +#define R_NIOS2_COPY 36 /* Copy symbol at runtime. */ +#define R_NIOS2_GLOB_DAT 37 /* Create GOT entry. */ +#define R_NIOS2_JUMP_SLOT 38 /* Create PLT entry. */ +#define R_NIOS2_RELATIVE 39 /* Adjust by program base. */ +#define R_NIOS2_GOTOFF 40 /* 16 bit offset to GOT pointer. */ +#define R_NIOS2_CALL26_NOAT 41 /* Direct call in .noat section. */ +#define R_NIOS2_GOT_LO 42 /* %lo() of GOT entry. */ +#define R_NIOS2_GOT_HA 43 /* %hiadj() of GOT entry. */ +#define R_NIOS2_CALL_LO 44 /* %lo() of function GOT entry. */ +#define R_NIOS2_CALL_HA 45 /* %hiadj() of function GOT entry. */ + +/* TILEPro relocations. */ +#define R_TILEPRO_NONE 0 /* No reloc */ +#define R_TILEPRO_32 1 /* Direct 32 bit */ +#define R_TILEPRO_16 2 /* Direct 16 bit */ +#define R_TILEPRO_8 3 /* Direct 8 bit */ +#define R_TILEPRO_32_PCREL 4 /* PC relative 32 bit */ +#define R_TILEPRO_16_PCREL 5 /* PC relative 16 bit */ +#define R_TILEPRO_8_PCREL 6 /* PC relative 8 bit */ +#define R_TILEPRO_LO16 7 /* Low 16 bit */ +#define R_TILEPRO_HI16 8 /* High 16 bit */ +#define R_TILEPRO_HA16 9 /* High 16 bit, adjusted */ +#define R_TILEPRO_COPY 10 /* Copy relocation */ +#define R_TILEPRO_GLOB_DAT 11 /* Create GOT entry */ +#define R_TILEPRO_JMP_SLOT 12 /* Create PLT entry */ +#define R_TILEPRO_RELATIVE 13 /* Adjust by program base */ +#define R_TILEPRO_BROFF_X1 14 /* X1 pipe branch offset */ +#define R_TILEPRO_JOFFLONG_X1 15 /* X1 pipe jump offset */ +#define R_TILEPRO_JOFFLONG_X1_PLT 16 /* X1 pipe jump offset to PLT */ +#define R_TILEPRO_IMM8_X0 17 /* X0 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y0 18 /* Y0 pipe 8-bit */ +#define R_TILEPRO_IMM8_X1 19 /* X1 pipe 8-bit */ +#define R_TILEPRO_IMM8_Y1 20 /* Y1 pipe 8-bit */ +#define R_TILEPRO_MT_IMM15_X1 21 /* X1 pipe mtspr */ +#define R_TILEPRO_MF_IMM15_X1 22 /* X1 pipe mfspr */ +#define R_TILEPRO_IMM16_X0 23 /* X0 pipe 16-bit */ +#define R_TILEPRO_IMM16_X1 24 /* X1 pipe 16-bit */ +#define R_TILEPRO_IMM16_X0_LO 25 /* X0 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X1_LO 26 /* X1 pipe low 16-bit */ +#define R_TILEPRO_IMM16_X0_HI 27 /* X0 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X1_HI 28 /* X1 pipe high 16-bit */ +#define R_TILEPRO_IMM16_X0_HA 29 /* X0 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X1_HA 30 /* X1 pipe high 16-bit, adjusted */ +#define R_TILEPRO_IMM16_X0_PCREL 31 /* X0 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X1_PCREL 32 /* X1 pipe PC relative 16 bit */ +#define R_TILEPRO_IMM16_X0_LO_PCREL 33 /* X0 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X1_LO_PCREL 34 /* X1 pipe PC relative low 16 bit */ +#define R_TILEPRO_IMM16_X0_HI_PCREL 35 /* X0 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X1_HI_PCREL 36 /* X1 pipe PC relative high 16 bit */ +#define R_TILEPRO_IMM16_X0_HA_PCREL 37 /* X0 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X1_HA_PCREL 38 /* X1 pipe PC relative ha() 16 bit */ +#define R_TILEPRO_IMM16_X0_GOT 39 /* X0 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT 40 /* X1 pipe 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_LO 41 /* X0 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_LO 42 /* X1 pipe low 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HI 43 /* X0 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HI 44 /* X1 pipe high 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X0_GOT_HA 45 /* X0 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_IMM16_X1_GOT_HA 46 /* X1 pipe ha() 16-bit GOT offset */ +#define R_TILEPRO_MMSTART_X0 47 /* X0 pipe mm "start" */ +#define R_TILEPRO_MMEND_X0 48 /* X0 pipe mm "end" */ +#define R_TILEPRO_MMSTART_X1 49 /* X1 pipe mm "start" */ +#define R_TILEPRO_MMEND_X1 50 /* X1 pipe mm "end" */ +#define R_TILEPRO_SHAMT_X0 51 /* X0 pipe shift amount */ +#define R_TILEPRO_SHAMT_X1 52 /* X1 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y0 53 /* Y0 pipe shift amount */ +#define R_TILEPRO_SHAMT_Y1 54 /* Y1 pipe shift amount */ +#define R_TILEPRO_DEST_IMM8_X1 55 /* X1 pipe destination 8-bit */ +/* Relocs 56-59 are currently not defined. */ +#define R_TILEPRO_TLS_GD_CALL 60 /* "jal" for TLS GD */ +#define R_TILEPRO_IMM8_X0_TLS_GD_ADD 61 /* X0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_X1_TLS_GD_ADD 62 /* X1 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y0_TLS_GD_ADD 63 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEPRO_IMM8_Y1_TLS_GD_ADD 64 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEPRO_TLS_IE_LOAD 65 /* "lw_tls" for TLS IE */ +#define R_TILEPRO_IMM16_X0_TLS_GD 66 /* X0 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD 67 /* X1 pipe 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_LO 68 /* X0 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_LO 69 /* X1 pipe low 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HI 70 /* X0 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HI 71 /* X1 pipe high 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_GD_HA 72 /* X0 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X1_TLS_GD_HA 73 /* X1 pipe ha() 16-bit TLS GD offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE 74 /* X0 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE 75 /* X1 pipe 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_LO 76 /* X0 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_LO 77 /* X1 pipe low 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HI 78 /* X0 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HI 79 /* X1 pipe high 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X0_TLS_IE_HA 80 /* X0 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_IMM16_X1_TLS_IE_HA 81 /* X1 pipe ha() 16-bit TLS IE offset */ +#define R_TILEPRO_TLS_DTPMOD32 82 /* ID of module containing symbol */ +#define R_TILEPRO_TLS_DTPOFF32 83 /* Offset in TLS block */ +#define R_TILEPRO_TLS_TPOFF32 84 /* Offset in static TLS block */ +#define R_TILEPRO_IMM16_X0_TLS_LE 85 /* X0 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE 86 /* X1 pipe 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_LO 87 /* X0 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_LO 88 /* X1 pipe low 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HI 89 /* X0 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HI 90 /* X1 pipe high 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X0_TLS_LE_HA 91 /* X0 pipe ha() 16-bit TLS LE offset */ +#define R_TILEPRO_IMM16_X1_TLS_LE_HA 92 /* X1 pipe ha() 16-bit TLS LE offset */ + +#define R_TILEPRO_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEPRO_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEPRO_NUM 130 + + +/* TILE-Gx relocations. */ +#define R_TILEGX_NONE 0 /* No reloc */ +#define R_TILEGX_64 1 /* Direct 64 bit */ +#define R_TILEGX_32 2 /* Direct 32 bit */ +#define R_TILEGX_16 3 /* Direct 16 bit */ +#define R_TILEGX_8 4 /* Direct 8 bit */ +#define R_TILEGX_64_PCREL 5 /* PC relative 64 bit */ +#define R_TILEGX_32_PCREL 6 /* PC relative 32 bit */ +#define R_TILEGX_16_PCREL 7 /* PC relative 16 bit */ +#define R_TILEGX_8_PCREL 8 /* PC relative 8 bit */ +#define R_TILEGX_HW0 9 /* hword 0 16-bit */ +#define R_TILEGX_HW1 10 /* hword 1 16-bit */ +#define R_TILEGX_HW2 11 /* hword 2 16-bit */ +#define R_TILEGX_HW3 12 /* hword 3 16-bit */ +#define R_TILEGX_HW0_LAST 13 /* last hword 0 16-bit */ +#define R_TILEGX_HW1_LAST 14 /* last hword 1 16-bit */ +#define R_TILEGX_HW2_LAST 15 /* last hword 2 16-bit */ +#define R_TILEGX_COPY 16 /* Copy relocation */ +#define R_TILEGX_GLOB_DAT 17 /* Create GOT entry */ +#define R_TILEGX_JMP_SLOT 18 /* Create PLT entry */ +#define R_TILEGX_RELATIVE 19 /* Adjust by program base */ +#define R_TILEGX_BROFF_X1 20 /* X1 pipe branch offset */ +#define R_TILEGX_JUMPOFF_X1 21 /* X1 pipe jump offset */ +#define R_TILEGX_JUMPOFF_X1_PLT 22 /* X1 pipe jump offset to PLT */ +#define R_TILEGX_IMM8_X0 23 /* X0 pipe 8-bit */ +#define R_TILEGX_IMM8_Y0 24 /* Y0 pipe 8-bit */ +#define R_TILEGX_IMM8_X1 25 /* X1 pipe 8-bit */ +#define R_TILEGX_IMM8_Y1 26 /* Y1 pipe 8-bit */ +#define R_TILEGX_DEST_IMM8_X1 27 /* X1 pipe destination 8-bit */ +#define R_TILEGX_MT_IMM14_X1 28 /* X1 pipe mtspr */ +#define R_TILEGX_MF_IMM14_X1 29 /* X1 pipe mfspr */ +#define R_TILEGX_MMSTART_X0 30 /* X0 pipe mm "start" */ +#define R_TILEGX_MMEND_X0 31 /* X0 pipe mm "end" */ +#define R_TILEGX_SHAMT_X0 32 /* X0 pipe shift amount */ +#define R_TILEGX_SHAMT_X1 33 /* X1 pipe shift amount */ +#define R_TILEGX_SHAMT_Y0 34 /* Y0 pipe shift amount */ +#define R_TILEGX_SHAMT_Y1 35 /* Y1 pipe shift amount */ +#define R_TILEGX_IMM16_X0_HW0 36 /* X0 pipe hword 0 */ +#define R_TILEGX_IMM16_X1_HW0 37 /* X1 pipe hword 0 */ +#define R_TILEGX_IMM16_X0_HW1 38 /* X0 pipe hword 1 */ +#define R_TILEGX_IMM16_X1_HW1 39 /* X1 pipe hword 1 */ +#define R_TILEGX_IMM16_X0_HW2 40 /* X0 pipe hword 2 */ +#define R_TILEGX_IMM16_X1_HW2 41 /* X1 pipe hword 2 */ +#define R_TILEGX_IMM16_X0_HW3 42 /* X0 pipe hword 3 */ +#define R_TILEGX_IMM16_X1_HW3 43 /* X1 pipe hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST 44 /* X0 pipe last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST 45 /* X1 pipe last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST 46 /* X0 pipe last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST 47 /* X1 pipe last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST 48 /* X0 pipe last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST 49 /* X1 pipe last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_PCREL 50 /* X0 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PCREL 51 /* X1 pipe PC relative hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PCREL 52 /* X0 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PCREL 53 /* X1 pipe PC relative hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PCREL 54 /* X0 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PCREL 55 /* X1 pipe PC relative hword 2 */ +#define R_TILEGX_IMM16_X0_HW3_PCREL 56 /* X0 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PCREL 57 /* X1 pipe PC relative hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PCREL 58 /* X0 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PCREL 59 /* X1 pipe PC-rel last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PCREL 60 /* X0 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PCREL 61 /* X1 pipe PC-rel last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PCREL 62 /* X0 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PCREL 63 /* X1 pipe PC-rel last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_GOT 64 /* X0 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_GOT 65 /* X1 pipe hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW0_PLT_PCREL 66 /* X0 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_PLT_PCREL 67 /* X1 pipe PC-rel PLT hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_PLT_PCREL 68 /* X0 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_PLT_PCREL 69 /* X1 pipe PC-rel PLT hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_PLT_PCREL 70 /* X0 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_PLT_PCREL 71 /* X1 pipe PC-rel PLT hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_GOT 72 /* X0 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X1_HW0_LAST_GOT 73 /* X1 pipe last hword 0 GOT offset */ +#define R_TILEGX_IMM16_X0_HW1_LAST_GOT 74 /* X0 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X1_HW1_LAST_GOT 75 /* X1 pipe last hword 1 GOT offset */ +#define R_TILEGX_IMM16_X0_HW3_PLT_PCREL 76 /* X0 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X1_HW3_PLT_PCREL 77 /* X1 pipe PC-rel PLT hword 3 */ +#define R_TILEGX_IMM16_X0_HW0_TLS_GD 78 /* X0 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_GD 79 /* X1 pipe hword 0 TLS GD offset */ +#define R_TILEGX_IMM16_X0_HW0_TLS_LE 80 /* X0 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_LE 81 /* X1 pipe hword 0 TLS LE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_LE 82 /* X0 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_LE 83 /* X1 pipe last hword 0 LE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_LE 84 /* X0 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_LE 85 /* X1 pipe last hword 1 LE off */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_GD 86 /* X0 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_GD 87 /* X1 pipe last hword 0 GD off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_GD 88 /* X0 pipe last hword 1 GD off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_GD 89 /* X1 pipe last hword 1 GD off */ +/* Relocs 90-91 are currently not defined. */ +#define R_TILEGX_IMM16_X0_HW0_TLS_IE 92 /* X0 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X1_HW0_TLS_IE 93 /* X1 pipe hword 0 TLS IE offset */ +#define R_TILEGX_IMM16_X0_HW0_LAST_PLT_PCREL 94 /* X0 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X1_HW0_LAST_PLT_PCREL 95 /* X1 pipe PC-rel PLT last hword 0 */ +#define R_TILEGX_IMM16_X0_HW1_LAST_PLT_PCREL 96 /* X0 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X1_HW1_LAST_PLT_PCREL 97 /* X1 pipe PC-rel PLT last hword 1 */ +#define R_TILEGX_IMM16_X0_HW2_LAST_PLT_PCREL 98 /* X0 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X1_HW2_LAST_PLT_PCREL 99 /* X1 pipe PC-rel PLT last hword 2 */ +#define R_TILEGX_IMM16_X0_HW0_LAST_TLS_IE 100 /* X0 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X1_HW0_LAST_TLS_IE 101 /* X1 pipe last hword 0 IE off */ +#define R_TILEGX_IMM16_X0_HW1_LAST_TLS_IE 102 /* X0 pipe last hword 1 IE off */ +#define R_TILEGX_IMM16_X1_HW1_LAST_TLS_IE 103 /* X1 pipe last hword 1 IE off */ +/* Relocs 104-105 are currently not defined. */ +#define R_TILEGX_TLS_DTPMOD64 106 /* 64-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF64 107 /* 64-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF64 108 /* 64-bit offset in static TLS block */ +#define R_TILEGX_TLS_DTPMOD32 109 /* 32-bit ID of symbol's module */ +#define R_TILEGX_TLS_DTPOFF32 110 /* 32-bit offset in TLS block */ +#define R_TILEGX_TLS_TPOFF32 111 /* 32-bit offset in static TLS block */ +#define R_TILEGX_TLS_GD_CALL 112 /* "jal" for TLS GD */ +#define R_TILEGX_IMM8_X0_TLS_GD_ADD 113 /* X0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_X1_TLS_GD_ADD 114 /* X1 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y0_TLS_GD_ADD 115 /* Y0 pipe "addi" for TLS GD */ +#define R_TILEGX_IMM8_Y1_TLS_GD_ADD 116 /* Y1 pipe "addi" for TLS GD */ +#define R_TILEGX_TLS_IE_LOAD 117 /* "ld_tls" for TLS IE */ +#define R_TILEGX_IMM8_X0_TLS_ADD 118 /* X0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_X1_TLS_ADD 119 /* X1 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y0_TLS_ADD 120 /* Y0 pipe "addi" for TLS GD/IE */ +#define R_TILEGX_IMM8_Y1_TLS_ADD 121 /* Y1 pipe "addi" for TLS GD/IE */ + +#define R_TILEGX_GNU_VTINHERIT 128 /* GNU C++ vtable hierarchy */ +#define R_TILEGX_GNU_VTENTRY 129 /* GNU C++ vtable member usage */ + +#define R_TILEGX_NUM 130 + +#ifdef __cplusplus +} +#endif + +#endif /*__LWP_ELF_H__*/ diff --git a/components/lwp/lwp_futex.c b/components/lwp/lwp_futex.c new file mode 100644 index 0000000000..ed03f43717 --- /dev/null +++ b/components/lwp/lwp_futex.c @@ -0,0 +1,252 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021/01/02 bernard the first version + */ + +#include <rtthread.h> +#include <lwp.h> +#ifdef RT_USING_USERSPACE +#include <lwp_user_mm.h> +#endif +#include "sys/time.h" + +struct rt_futex +{ + int *uaddr; + rt_list_t waiting_thread; + struct lwp_avl_struct node; + struct rt_object *custom_obj; +}; + +static struct rt_mutex _futex_lock; + +static int futex_system_init(void) +{ + rt_mutex_init(&_futex_lock, "futexList", RT_IPC_FLAG_FIFO); + return 0; +} +INIT_PREV_EXPORT(futex_system_init); + +rt_err_t futex_destory(void *data) +{ + rt_err_t ret = -1; + rt_base_t level; + struct rt_futex *futex = (struct rt_futex *)data; + + if (futex) + { + level = rt_hw_interrupt_disable(); + /* remove futex from futext avl */ + lwp_avl_remove(&futex->node, (struct lwp_avl_struct **)futex->node.data); + rt_hw_interrupt_enable(level); + + /* release object */ + rt_free(futex); + ret = 0; + } + return ret; +} + +struct rt_futex *futex_create(int *uaddr, struct rt_lwp *lwp) +{ + struct rt_futex *futex = RT_NULL; + struct rt_object *obj = RT_NULL; + + if (!lwp) + { + return RT_NULL; + } + futex = (struct rt_futex *)rt_malloc(sizeof(struct rt_futex)); + if (!futex) + { + return RT_NULL; + } + obj = rt_custom_object_create("futex", (void *)futex, futex_destory); + if (!obj) + { + rt_free(futex); + return RT_NULL; + } + + futex->uaddr = uaddr; + futex->node.avl_key = (avl_key_t)uaddr; + futex->node.data = &lwp->address_search_head; + futex->custom_obj = obj; + rt_list_init(&(futex->waiting_thread)); + + /* insert into futex head */ + lwp_avl_insert(&futex->node, &lwp->address_search_head); + return futex; +} + +static struct rt_futex *futex_get(void *uaddr, struct rt_lwp *lwp) +{ + struct rt_futex *futex = RT_NULL; + struct lwp_avl_struct *node = RT_NULL; + + node = lwp_avl_find((avl_key_t)uaddr, lwp->address_search_head); + if (!node) + { + return RT_NULL; + } + futex = rt_container_of(node, struct rt_futex, node); + return futex; +} + +int futex_wait(struct rt_futex *futex, int value, const struct timespec *timeout) +{ + rt_base_t level = 0; + rt_err_t ret = -RT_EINTR; + + if (*(futex->uaddr) == value) + { + rt_thread_t thread = rt_thread_self(); + + level = rt_hw_interrupt_disable(); + ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE); + + if (ret < 0) + { + rt_mutex_release(&_futex_lock); + rt_hw_interrupt_enable(level); + rt_set_errno(EINTR); + return ret; + } + + /* add into waiting thread list */ + rt_list_insert_before(&(futex->waiting_thread), &(thread->tlist)); + + /* with timeout */ + if (timeout) + { + rt_int32_t time = rt_timespec_to_tick(timeout); + + /* start the timer of thread */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } + rt_mutex_release(&_futex_lock); + rt_hw_interrupt_enable(level); + + /* do schedule */ + rt_schedule(); + + ret = thread->error; + /* check errno */ + } + else + { + rt_mutex_release(&_futex_lock); + rt_set_errno(EAGAIN); + } + + return ret; +} + +void futex_wake(struct rt_futex *futex, int number) +{ + rt_base_t level = rt_hw_interrupt_disable(); + while (!rt_list_isempty(&(futex->waiting_thread)) && number) + { + rt_thread_t thread; + + thread = rt_list_entry(futex->waiting_thread.next, struct rt_thread, tlist); + /* remove from waiting list */ + rt_list_remove(&(thread->tlist)); + + thread->error = RT_EOK; + /* resume the suspended thread */ + rt_thread_resume(thread); + + number--; + } + rt_mutex_release(&_futex_lock); + rt_hw_interrupt_enable(level); + + /* do schedule */ + rt_schedule(); +} + +int sys_futex(int *uaddr, int op, int val, const struct timespec *timeout, + int *uaddr2, int val3) +{ + struct rt_lwp *lwp = RT_NULL; + struct rt_futex *futex = RT_NULL; + int ret = 0; + rt_err_t lock_ret = 0; + + if (!lwp_user_accessable(uaddr, sizeof(int))) + { + rt_set_errno(EINVAL); + return -RT_EINVAL; + } + + /** + * if (op & (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI)) was TRUE + * `timeout` should be ignored by implementation, according to POSIX futex(2) manual. + * since only FUTEX_WAKE is implemented in rt-smart, only FUTEX_WAKE was omitted currently + */ + if (timeout && !(op & (FUTEX_WAKE))) + { + if (!lwp_user_accessable((void *)timeout, sizeof(struct timespec))) + { + rt_set_errno(EINVAL); + return -RT_EINVAL; + } + } + lock_ret = rt_mutex_take_interruptible(&_futex_lock, RT_WAITING_FOREVER); + if (lock_ret != RT_EOK) + { + rt_set_errno(EAGAIN); + return -RT_EINTR; + } + + lwp = lwp_self(); + futex = futex_get(uaddr, lwp); + if (futex == RT_NULL) + { + /* create a futex according to this uaddr */ + futex = futex_create(uaddr, lwp); + if (futex == RT_NULL) + { + rt_mutex_release(&_futex_lock); + rt_set_errno(ENOMEM); + return -RT_ENOMEM; + } + if (lwp_user_object_add(lwp, futex->custom_obj) != 0) + { + rt_custom_object_destroy(futex->custom_obj); + rt_mutex_release(&_futex_lock); + rt_set_errno(ENOMEM); + return -RT_ENOMEM; + } + } + + switch (op) + { + case FUTEX_WAIT: + ret = futex_wait(futex, val, timeout); + /* _futex_lock is released by futex_wait */ + break; + + case FUTEX_WAKE: + futex_wake(futex, val); + /* _futex_lock is released by futex_wake */ + break; + + default: + rt_mutex_release(&_futex_lock); + rt_set_errno(ENOSYS); + ret = -ENOSYS; + break; + } + + return ret; +} diff --git a/components/lwp/lwp_ipc.c b/components/lwp/lwp_ipc.c new file mode 100644 index 0000000000..dc53310e82 --- /dev/null +++ b/components/lwp/lwp_ipc.c @@ -0,0 +1,1153 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ +#include <rtthread.h> +#include <rthw.h> +#include <lwp.h> + +#include "lwp_ipc.h" +#include "lwp_ipc_internal.h" + +#include <dfs_file.h> +#include <poll.h> + +/** + * the IPC channel states + */ +enum +{ + RT_IPC_STAT_IDLE, /* no suspended threads */ + RT_IPC_STAT_WAIT, /* suspended receivers exist */ + RT_IPC_STAT_ACTIVE, /* suspended senders exist */ +}; + +/** + * IPC message structure. + * + * They are allocated and released in the similar way like 'rt_chfd'. + */ +struct rt_ipc_msg +{ + struct rt_channel_msg msg; /**< the payload of msg */ + rt_list_t mlist; /**< the msg list */ + rt_uint8_t need_reply; /**< whether msg wait reply*/ +}; +typedef struct rt_ipc_msg *rt_ipc_msg_t; + +static rt_ipc_msg_t _ipc_msg_free_list = (rt_ipc_msg_t)RT_NULL; /* released chain */ +static int rt_ipc_msg_used = 0; /* first unallocated entry */ +static struct rt_ipc_msg ipc_msg_pool[RT_CH_MSG_MAX_NR]; /* initial message array */ + +/** + * Allocate an IPC message from the statically-allocated array. + */ +static rt_ipc_msg_t _ipc_msg_alloc(void) +{ + rt_ipc_msg_t p = (rt_ipc_msg_t)RT_NULL; + + if (_ipc_msg_free_list) /* use the released chain first */ + { + p = _ipc_msg_free_list; + _ipc_msg_free_list = (rt_ipc_msg_t)p->msg.sender; /* emtry payload as a pointer */ + } + else if (rt_ipc_msg_used < RT_CH_MSG_MAX_NR) + { + p = &ipc_msg_pool[rt_ipc_msg_used]; + rt_ipc_msg_used++; + } + return p; +} + +/** + * Put a released IPC message back to the released chain. + */ +static void _ipc_msg_free(rt_ipc_msg_t p_msg) +{ + p_msg->msg.sender = (void*)_ipc_msg_free_list; + _ipc_msg_free_list = p_msg; +} + +/** + * Initialized the IPC message. + */ +static void rt_ipc_msg_init(rt_ipc_msg_t msg, struct rt_channel_msg *data, rt_uint8_t need_reply) +{ + RT_ASSERT(msg != RT_NULL); + + msg->need_reply = need_reply; + msg->msg = *data; + msg->msg.sender = (void*)rt_thread_self(); + rt_list_init(&msg->mlist); +} + +/** + * Initialized the list of the waiting receivers on the IPC channel. + */ +rt_inline rt_err_t rt_channel_object_init(struct rt_ipc_object *ipc) +{ + rt_list_init(&(ipc->suspend_thread)); /* receiver list */ + + return RT_EOK; +} + +/** + * Wakeup the first suspened thread in the list. + */ +rt_inline rt_err_t rt_channel_list_resume(rt_list_t *list) +{ + struct rt_thread *thread; + + /* get the first thread entry waiting for sending */ + thread = rt_list_entry(list->next, struct rt_thread, tlist); + + rt_thread_resume(thread); + + return RT_EOK; +} + +/** + * Wakeup all the suspended threads in the list. + */ +rt_inline rt_err_t rt_channel_list_resume_all(rt_list_t *list) +{ + struct rt_thread *thread; + register rt_ubase_t temp; + + /* wakeup all suspended threads for sending */ + while (!rt_list_isempty(list)) + { + temp = rt_hw_interrupt_disable(); + thread = rt_list_entry(list->next, struct rt_thread, tlist); + thread->error = -RT_ERROR; + rt_thread_resume(thread); + rt_hw_interrupt_enable(temp); + } + + return RT_EOK; +} + +/** + * Suspend the thread and chain it into the end of the list. + */ +rt_inline rt_err_t rt_channel_list_suspend(rt_list_t *list, struct rt_thread *thread) +{ + /* suspend thread */ + rt_err_t ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE); + + if (ret == RT_EOK) + { + rt_list_insert_before(list, &(thread->tlist)); /* list end */ + } + + return ret; +} + + +static void _rt_channel_check_wq_wakup(rt_channel_t ch) +{ + rt_base_t level; + + level = rt_hw_interrupt_disable(); + if (rt_list_isempty(&ch->wait_msg)) + { + rt_hw_interrupt_enable(level); + return; + } + + rt_wqueue_wakeup(&ch->reader_queue, 0); + rt_hw_interrupt_enable(level); +} + +/** + * Create a new or open an existing IPC channel. + */ +rt_channel_t rt_raw_channel_open(const char *name, int flags) +{ + register rt_ubase_t temp = 0; + rt_channel_t ch = RT_NULL; + + struct rt_object *object; + struct rt_list_node *node; + struct rt_object_information *information; + + temp = rt_hw_interrupt_disable(); + information = rt_object_get_information(RT_Object_Class_Channel); + RT_ASSERT(information != RT_NULL); + + /* retrieve the existing IPC channels */ + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) + { + object = rt_list_entry(node, struct rt_object, list); + if (rt_strncmp(object->name, name, RT_NAME_MAX) == 0) + { + if ((flags & O_CREAT) && (flags & O_EXCL)) + { + goto quit; + } + /* find the IPC channel with the specific name */ + ch = (rt_channel_t)object; + ch->ref++; /* increase the reference count */ + break; + } + } + if (!ch) /* create a new IPC channel */ + { + if (flags & O_CREAT) + { + RT_DEBUG_NOT_IN_INTERRUPT; + + /* allocate a real IPC channel structure */ + ch = (rt_channel_t)rt_object_allocate(RT_Object_Class_Channel, name); + } + + if (!ch) + { + goto quit; + } + + rt_channel_object_init(&ch->parent); /* suspended receivers */ + rt_list_init(&ch->wait_msg); /* unhandled messages */ + rt_list_init(&ch->wait_thread); /* suspended senders */ + rt_wqueue_init(&ch->reader_queue); /* reader poll queue */ + ch->reply = RT_NULL; + ch->stat = RT_IPC_STAT_IDLE; /* no suspended threads */ + ch->ref = 1; + } +quit: + rt_hw_interrupt_enable(temp); + return ch; +} + +/** + * Close an existiong IPC channel, release the resources. + */ +rt_err_t rt_raw_channel_close(rt_channel_t ch) +{ + register rt_ubase_t temp; + + RT_DEBUG_NOT_IN_INTERRUPT; + + if (ch == RT_NULL) + { + return -RT_EIO; + } + + temp = rt_hw_interrupt_disable(); + if (rt_object_get_type(&ch->parent.parent) != RT_Object_Class_Channel) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + if (rt_object_is_systemobject(&ch->parent.parent) != RT_FALSE) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + + if (ch->ref == 0) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + ch->ref--; + if (ch->ref == 0) + { + /* wakeup all the suspended receivers and senders */ + rt_channel_list_resume_all(&ch->parent.suspend_thread); + rt_channel_list_resume_all(&ch->wait_thread); + + /* all ipc msg will lost */ + rt_list_init(&ch->wait_msg); + + rt_object_delete(&ch->parent.parent); /* release the IPC channel structure */ + } + rt_hw_interrupt_enable(temp); + return RT_EOK; +} + +static rt_err_t wakeup_sender_wait_recv(void *object, struct rt_thread *thread) +{ + rt_channel_t ch; + + ch = (rt_channel_t)object; + if (ch->stat == RT_IPC_STAT_ACTIVE && ch->reply == thread) + { + ch->stat = RT_IPC_STAT_IDLE; + ch->reply = RT_NULL; + } + else + { + rt_ipc_msg_t msg; + rt_list_t *l; + + l = ch->wait_msg.next; + while (l != &ch->wait_msg) + { + msg = rt_list_entry(l, struct rt_ipc_msg, mlist); + if (msg->need_reply && msg->msg.sender == thread) + { + rt_list_remove(&msg->mlist); /* remove the msg from the channel */ + _ipc_msg_free(msg); + break; + } + l = l->next; + } + } + thread->error = -RT_EINTR; + return rt_thread_resume(thread); /* wake up the sender */ +} + +static rt_err_t wakeup_sender_wait_reply(void *object, struct rt_thread *thread) +{ + rt_channel_t ch; + + ch = (rt_channel_t)object; + RT_ASSERT(ch->stat == RT_IPC_STAT_ACTIVE && ch->reply == thread); + ch->stat = RT_IPC_STAT_IDLE; + ch->reply = RT_NULL; + thread->error = -RT_EINTR; + return rt_thread_resume(thread); /* wake up the sender */ +} + +static void sender_timeout(void *parameter) +{ + struct rt_thread *thread = (struct rt_thread*)parameter; + rt_channel_t ch; + + ch = (rt_channel_t)(thread->wakeup.user_data); + if (ch->stat == RT_IPC_STAT_ACTIVE && ch->reply == thread) + { + ch->stat = RT_IPC_STAT_IDLE; + ch->reply = RT_NULL; + } + else + { + rt_ipc_msg_t msg; + rt_list_t *l; + + l = ch->wait_msg.next; + while (l != &ch->wait_msg) + { + msg = rt_list_entry(l, struct rt_ipc_msg, mlist); + if (msg->need_reply && msg->msg.sender == thread) + { + rt_list_remove(&msg->mlist); /* remove the msg from the channel */ + _ipc_msg_free(msg); + break; + } + l = l->next; + } + } + thread->error = -RT_ETIMEOUT; + thread->wakeup.func = RT_NULL; + + rt_list_remove(&(thread->tlist)); + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + /* do schedule */ + rt_schedule(); +} + +/** + * Send data through an IPC channel, wait for the reply or not. + */ +static rt_err_t _rt_raw_channel_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, int need_reply, rt_channel_msg_t data_ret, rt_int32_t time) +{ + rt_ipc_msg_t msg; + struct rt_thread *thread_recv, *thread_send = 0; + register rt_base_t temp; + rt_err_t ret; + void (*old_timeout_func)(void *) = 0; + + if (need_reply) + { + RT_DEBUG_NOT_IN_INTERRUPT; + } + + if (ch == RT_NULL) + { + return -RT_EIO; + } + + temp = rt_hw_interrupt_disable(); + + if (rt_object_get_type(&ch->parent.parent) != RT_Object_Class_Channel) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + if (need_reply && time == 0) + { + rt_hw_interrupt_enable(temp); + return -RT_ETIMEOUT; + } + + /* allocate an IPC message */ + msg = _ipc_msg_alloc(); + if (!msg) + { + rt_hw_interrupt_enable(temp); + return -RT_ENOMEM; + } + + rt_ipc_msg_init(msg, data, need_reply); + + if (need_reply) + { + thread_send = rt_thread_self(); + thread_send->error = RT_EOK; + } + + switch (ch->stat) + { + case RT_IPC_STAT_IDLE: + case RT_IPC_STAT_ACTIVE: + if (need_reply) + { + ret = rt_channel_list_suspend(&ch->wait_thread, thread_send); + if (ret != RT_EOK) + { + _ipc_msg_free(msg); + rt_hw_interrupt_enable(temp); + return ret; + } + rt_thread_wakeup_set(thread_send, wakeup_sender_wait_recv, (void*)ch); + if (time > 0) + { + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_GET_FUNC, + &old_timeout_func); + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_SET_FUNC, + sender_timeout); + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread_send->thread_timer)); + } + } + /* + * If there is no thread waiting for messages, chain the message + * into the list. + */ + rt_list_insert_before(&ch->wait_msg, &msg->mlist); + break; + case RT_IPC_STAT_WAIT: + /* + * If there are suspended receivers on the IPC channel, transfer the + * pointer of the message to the first receiver directly and wake it + * up. + */ + RT_ASSERT(ch->parent.suspend_thread.next != &ch->parent.suspend_thread); + + if (need_reply) + { + ret = rt_channel_list_suspend(&ch->wait_thread, thread_send); + if (ret != RT_EOK) + { + _ipc_msg_free(msg); + rt_hw_interrupt_enable(temp); + return ret; + } + ch->reply = thread_send; /* record the current waiting sender */ + ch->stat = RT_IPC_STAT_ACTIVE; + rt_thread_wakeup_set(thread_send, wakeup_sender_wait_reply, (void*)ch); + if (time > 0) + { + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_GET_FUNC, + &old_timeout_func); + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_SET_FUNC, + sender_timeout); + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread_send->thread_timer)); + } + } + else + { + ch->stat = RT_IPC_STAT_IDLE; + } + thread_recv = rt_list_entry(ch->parent.suspend_thread.next, struct rt_thread, tlist); + thread_recv->msg_ret = msg; /* to the first suspended receiver */ + thread_recv->error = RT_EOK; + rt_channel_list_resume(&ch->parent.suspend_thread); + break; + default: + break; + } + + if ( ch->stat == RT_IPC_STAT_IDLE) + { + _rt_channel_check_wq_wakup(ch); + } + rt_hw_interrupt_enable(temp); + + /* reschedule in order to let the potential receivers run */ + rt_schedule(); + + if (need_reply) + { + temp = rt_hw_interrupt_disable(); + if (old_timeout_func) + { + rt_timer_control(&(thread_send->thread_timer), + RT_TIMER_CTRL_SET_FUNC, + old_timeout_func); + } + ret = thread_send->error; + rt_hw_interrupt_enable(temp); + + if (ret != RT_EOK) + { + return ret; + } + + /* If the sender gets the chance to run, the requested reply must be valid. */ + RT_ASSERT(data_ret != RT_NULL); + *data_ret = ((rt_ipc_msg_t)(thread_send->msg_ret))->msg; /* extract data */ + temp = rt_hw_interrupt_disable(); + _ipc_msg_free(thread_send->msg_ret); /* put back the message to kernel */ + rt_hw_interrupt_enable(temp); + thread_send->msg_ret = RT_NULL; + } + + return RT_EOK; +} + +/** + * Send data through an IPC channel with no reply. + */ +rt_err_t rt_raw_channel_send(rt_channel_t ch, rt_channel_msg_t data) +{ + return _rt_raw_channel_send_recv_timeout(ch, data, 0, 0, RT_WAITING_FOREVER); +} + +/** + * Send data through an IPC channel and wait for the relpy. + */ +rt_err_t rt_raw_channel_send_recv(rt_channel_t ch, rt_channel_msg_t data, rt_channel_msg_t data_ret) +{ + return _rt_raw_channel_send_recv_timeout(ch, data, 1, data_ret, RT_WAITING_FOREVER); +} + +/** + * Send data through an IPC channel and wait for the relpy. + */ +rt_err_t rt_raw_channel_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) +{ + return _rt_raw_channel_send_recv_timeout(ch, data, 1, data_ret, time); +} + +/** + * Reply to the waiting sender and wake it up. + */ +rt_err_t rt_raw_channel_reply(rt_channel_t ch, rt_channel_msg_t data) +{ + rt_ipc_msg_t msg; + struct rt_thread *thread; + register rt_base_t temp; + + if (ch == RT_NULL) + { + return -RT_EIO; + } + + temp = rt_hw_interrupt_disable(); + + if (rt_object_get_type(&ch->parent.parent) != RT_Object_Class_Channel) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + + if (ch->stat != RT_IPC_STAT_ACTIVE) + { + rt_hw_interrupt_enable(temp); + return -RT_ERROR; + } + + if (ch->reply == RT_NULL) + { + rt_hw_interrupt_enable(temp); + return -RT_ERROR; + } + + /* allocate an IPC message */ + msg = _ipc_msg_alloc(); + if (!msg) + { + rt_hw_interrupt_enable(temp); + return -RT_ENOMEM; + } + + rt_ipc_msg_init(msg, data, 0); + + thread = ch->reply; + thread->msg_ret = msg; /* transfer the reply to the sender */ + rt_thread_resume(thread); /* wake up the sender */ + ch->stat = RT_IPC_STAT_IDLE; + ch->reply = RT_NULL; + + _rt_channel_check_wq_wakup(ch); + rt_hw_interrupt_enable(temp); + rt_schedule(); + + return RT_EOK; +} + +static rt_err_t wakeup_receiver(void *object, struct rt_thread *thread) +{ + rt_channel_t ch; + rt_err_t ret; + + ch = (rt_channel_t)object; + ch->stat = RT_IPC_STAT_IDLE; + thread->error = -RT_EINTR; + ret = rt_channel_list_resume(&ch->parent.suspend_thread); + _rt_channel_check_wq_wakup(ch); + return ret; +} + +static void receiver_timeout(void *parameter) +{ + struct rt_thread *thread = (struct rt_thread*)parameter; + rt_channel_t ch; + + ch = (rt_channel_t)(thread->wakeup.user_data); + + ch->stat = RT_IPC_STAT_IDLE; + thread->error = -RT_ETIMEOUT; + thread->wakeup.func = RT_NULL; + + rt_list_remove(&(thread->tlist)); + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); + + _rt_channel_check_wq_wakup(ch); + /* do schedule */ + rt_schedule(); +} + +/** + * Fetch a message from the specified IPC channel. + */ +static rt_err_t _rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_int32_t time) +{ + struct rt_thread *thread; + rt_ipc_msg_t msg_ret; + register rt_base_t temp; + rt_err_t ret; + void (*old_timeout_func)(void *) = 0; + + RT_DEBUG_NOT_IN_INTERRUPT; + + if (ch == RT_NULL) + { + return -RT_EIO; + } + + temp = rt_hw_interrupt_disable(); + + if (rt_object_get_type(&ch->parent.parent) != RT_Object_Class_Channel) + { + rt_hw_interrupt_enable(temp); + return -RT_EIO; + } + if (ch->stat != RT_IPC_STAT_IDLE) + { + rt_hw_interrupt_enable(temp); + return -RT_ERROR; + } + + if (ch->wait_msg.next != &ch->wait_msg) /* there exist unhandled messages */ + { + msg_ret = rt_list_entry(ch->wait_msg.next, struct rt_ipc_msg, mlist); + rt_list_remove(ch->wait_msg.next); /* remove the message from the channel */ + if (msg_ret->need_reply) + { + RT_ASSERT(ch->wait_thread.next != &ch->wait_thread); + + thread = rt_list_entry(ch->wait_thread.next, struct rt_thread, tlist); + rt_list_remove(ch->wait_thread.next); + ch->reply = thread; /* record the waiting sender */ + ch->stat = RT_IPC_STAT_ACTIVE; /* no valid suspened receivers */ + } + *data = msg_ret->msg; /* extract the transferred data */ + _ipc_msg_free(msg_ret); /* put back the message to kernel */ + } + else + { + if (time == 0) + { + rt_hw_interrupt_enable(temp); + return -RT_ETIMEOUT; + } + /* no valid message, we must wait */ + thread = rt_thread_self(); + + ret = rt_channel_list_suspend(&ch->parent.suspend_thread, thread); + if (ret != RT_EOK) + { + rt_hw_interrupt_enable(temp); + return ret; + } + rt_thread_wakeup_set(thread, wakeup_receiver, (void*)ch); + ch->stat = RT_IPC_STAT_WAIT;/* no valid suspended senders */ + thread->error = RT_EOK; + if (time > 0) + { + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_GET_FUNC, + &old_timeout_func); + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_FUNC, + receiver_timeout); + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &time); + rt_timer_start(&(thread->thread_timer)); + } + rt_hw_interrupt_enable(temp); + + rt_schedule(); /* let the senders run */ + + temp = rt_hw_interrupt_disable(); + if (old_timeout_func) + { + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_FUNC, + old_timeout_func); + } + ret = thread->error; + if ( ret != RT_EOK) + { + rt_hw_interrupt_enable(temp); + return ret; + } + /* If waked up, the received message has been store into the thread. */ + *data = ((rt_ipc_msg_t)(thread->msg_ret))->msg; /* extract data */ + _ipc_msg_free(thread->msg_ret); /* put back the message to kernel */ + thread->msg_ret = RT_NULL; + } + + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} + +rt_err_t rt_raw_channel_recv(rt_channel_t ch, rt_channel_msg_t data) +{ + return _rt_raw_channel_recv_timeout(ch, data, RT_WAITING_FOREVER); +} + +rt_err_t rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_int32_t time) +{ + return _rt_raw_channel_recv_timeout(ch, data, time); +} +/** + * Peek a message from the specified IPC channel. + */ +rt_err_t rt_raw_channel_peek(rt_channel_t ch, rt_channel_msg_t data) +{ + return _rt_raw_channel_recv_timeout(ch, data, 0); +} + +/* for API */ + +static int lwp_fd_new(int fdt_type) +{ + struct dfs_fdtable *fdt; + + if (fdt_type) + { + fdt = dfs_fdtable_get_global(); + } + else + { + fdt = dfs_fdtable_get(); + } + return fdt_fd_new(fdt); +} + +static struct dfs_fd *lwp_fd_get(int fdt_type, int fd) +{ + struct dfs_fdtable *fdt; + + if (fdt_type) + { + fdt = dfs_fdtable_get_global(); + } + else + { + fdt = dfs_fdtable_get(); + } + return fdt_fd_get(fdt, fd); +} + +static void lwp_fd_release(int fdt_type, int fd) +{ + struct dfs_fdtable *fdt; + + if (fdt_type) + { + fdt = dfs_fdtable_get_global(); + } + else + { + fdt = dfs_fdtable_get(); + } + fdt_fd_release(fdt, fd); +} + +static int _chfd_alloc(int fdt_type) +{ + /* create a BSD socket */ + int fd; + + /* allocate a fd */ + fd = lwp_fd_new(fdt_type); + + if (fd < 0) + { + return -1; + } + return fd; +} + +static void _chfd_free(int fd, int fdt_type) +{ + struct dfs_fd *d; + + d = lwp_fd_get(fdt_type, fd); + if (d == RT_NULL) + { + return; + } + lwp_fd_release(fdt_type, fd); +} + +/* for fops */ +static int channel_fops_poll(struct dfs_fd *file, struct rt_pollreq *req) +{ + int mask = POLLOUT; + rt_channel_t ch; + + ch = (rt_channel_t)file->vnode->data; + rt_poll_add(&(ch->reader_queue), req); + if (ch->stat != RT_IPC_STAT_IDLE) + { + return mask; + } + if (!rt_list_isempty(&ch->wait_msg)) + { + mask |= POLLIN; + } + return mask; +} + +static int channel_fops_close(struct dfs_fd *file) +{ + rt_channel_t ch; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + ch = (rt_channel_t)file->vnode->data; + if (file->vnode->ref_count == 1) + { + ch->ref--; + if (ch->ref == 0) + { + /* wakeup all the suspended receivers and senders */ + rt_channel_list_resume_all(&ch->parent.suspend_thread); + rt_channel_list_resume_all(&ch->wait_thread); + + /* all ipc msg will lost */ + rt_list_init(&ch->wait_msg); + + rt_object_delete(&ch->parent.parent); /* release the IPC channel structure */ + } + } + rt_hw_interrupt_enable(level); + return 0; +} + +static const struct dfs_file_ops channel_fops = +{ + NULL, /* open */ + channel_fops_close, + NULL, + NULL, + NULL, + NULL, + NULL, /* lseek */ + NULL, /* getdents */ + channel_fops_poll, +}; + +int lwp_channel_open(int fdt_type, const char *name, int flags) +{ + int fd; + rt_channel_t ch = RT_NULL; + struct dfs_fd *d; + + fd = _chfd_alloc(fdt_type); /* allocate an IPC channel descriptor */ + if (fd == -1) + { + goto quit; + } + d = lwp_fd_get(fdt_type, fd); + d->vnode = (struct dfs_fnode *)rt_malloc(sizeof(struct dfs_fnode)); + if (!d->vnode) + { + _chfd_free(fd, fdt_type); + fd = -1; + goto quit; + } + + ch = rt_raw_channel_open(name, flags); + if (ch) + { + rt_memset(d->vnode, 0, sizeof(struct dfs_fnode)); + rt_list_init(&d->vnode->list); + d->vnode->type = FT_USER; + d->vnode->path = NULL; + d->vnode->fullpath = NULL; + + d->vnode->fops = &channel_fops; + + d->flags = O_RDWR; /* set flags as read and write */ + d->vnode->size = 0; + d->pos = 0; + d->vnode->ref_count = 1; + + /* set socket to the data of dfs_fd */ + d->vnode->data = (void *)ch; + } + else + { + rt_free(d->vnode); + _chfd_free(fd, fdt_type); + fd = -1; + } +quit: + return fd; +} + +static rt_channel_t fd_2_channel(int fdt_type, int fd) +{ + struct dfs_fd *d; + + d = lwp_fd_get(fdt_type, fd); + if (d) + { + rt_channel_t ch; + + ch = (rt_channel_t)d->vnode->data; + if (ch) + { + return ch; + } + } + return RT_NULL; +} + +rt_err_t lwp_channel_close(int fdt_type, int fd) +{ + rt_channel_t ch; + struct dfs_fd *d; + struct dfs_fnode *vnode; + + d = lwp_fd_get(fdt_type, fd); + if (!d) + { + return -RT_EIO; + } + + vnode = d->vnode; + if (!vnode) + { + return -RT_EIO; + } + + ch = fd_2_channel(fdt_type, fd); + if (!ch) + { + return -RT_EIO; + } + _chfd_free(fd, fdt_type); + if (vnode->ref_count == 1) + { + rt_free(vnode); + return rt_raw_channel_close(ch); + } + + return 0; +} + +rt_err_t lwp_channel_send(int fdt_type, int fd, rt_channel_msg_t data) +{ + rt_channel_t ch; + ch = fd_2_channel(fdt_type, fd); + if (ch) + { + return rt_raw_channel_send(ch, data); + } + return -RT_EIO; +} + +rt_err_t lwp_channel_send_recv_timeout(int fdt_type, int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) +{ + rt_channel_t ch; + ch = fd_2_channel(fdt_type, fd); + if (ch) + { + return rt_raw_channel_send_recv_timeout(ch, data, data_ret, time); + } + return -RT_EIO; +} + +rt_err_t lwp_channel_reply(int fdt_type, int fd, rt_channel_msg_t data) +{ + rt_channel_t ch; + ch = fd_2_channel(fdt_type, fd); + if (ch) + { + return rt_raw_channel_reply(ch, data); + } + return -RT_EIO; +} + +rt_err_t lwp_channel_recv_timeout(int fdt_type, int fd, rt_channel_msg_t data, rt_int32_t time) +{ + rt_channel_t ch; + ch = fd_2_channel(fdt_type, fd); + if (ch) + { + return rt_raw_channel_recv_timeout(ch, data, time); + } + return -RT_EIO; +} + +int rt_channel_open(const char *name, int flags) +{ + return lwp_channel_open(FDT_TYPE_KERNEL, name, flags); +} + +rt_err_t rt_channel_close(int fd) +{ + return lwp_channel_close(FDT_TYPE_KERNEL, fd); +} + +rt_err_t rt_channel_send(int fd, rt_channel_msg_t data) +{ + return lwp_channel_send(FDT_TYPE_KERNEL, fd, data); +} + +rt_err_t rt_channel_send_recv_timeout(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) +{ + return lwp_channel_send_recv_timeout(FDT_TYPE_KERNEL, fd, data, data_ret, time); +} + +rt_err_t rt_channel_send_recv(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret) +{ + return lwp_channel_send_recv_timeout(FDT_TYPE_KERNEL, fd, data, data_ret, RT_WAITING_FOREVER); +} + +rt_err_t rt_channel_reply(int fd, rt_channel_msg_t data) +{ + return lwp_channel_reply(FDT_TYPE_KERNEL, fd, data); +} + +rt_err_t rt_channel_recv_timeout(int fd, rt_channel_msg_t data, rt_int32_t time) +{ + return lwp_channel_recv_timeout(FDT_TYPE_KERNEL, fd, data, time); +} + +rt_err_t rt_channel_recv(int fd, rt_channel_msg_t data) +{ + return lwp_channel_recv_timeout(FDT_TYPE_KERNEL, fd, data, RT_WAITING_FOREVER); +} + +rt_err_t rt_channel_peek(int fd, rt_channel_msg_t data) +{ + return lwp_channel_recv_timeout(FDT_TYPE_KERNEL, fd, data, 0); +} + +#ifdef RT_USING_FINSH +static int list_channel(void) +{ + rt_base_t level; + rt_channel_t *channels; + rt_ubase_t index, count; + struct rt_object *object; + struct rt_list_node *node; + struct rt_object_information *information; + + const char* stat_strs[] = {"idle", "wait", "active"}; + + information = rt_object_get_information(RT_Object_Class_Channel); + RT_ASSERT(information != RT_NULL); + + count = 0; + level = rt_hw_interrupt_disable(); + /* get the count of IPC channels */ + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) + { + count ++; + } + rt_hw_interrupt_enable(level); + + if (count == 0) return 0; + + channels = (rt_channel_t *) rt_calloc(count, sizeof(rt_channel_t)); + if (channels == RT_NULL) return 0; /* out of memory */ + + index = 0; + level = rt_hw_interrupt_disable(); + /* retrieve pointer of IPC channels */ + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) + { + object = rt_list_entry(node, struct rt_object, list); + + channels[index] = (rt_channel_t)object; + index ++; + } + rt_hw_interrupt_enable(level); + + rt_kprintf(" channel state\n"); + rt_kprintf("-------- -------\n"); + for (index = 0; index < count; index ++) + { + if (channels[index] != RT_NULL) + { + rt_kprintf("%-*.s", RT_NAME_MAX, channels[index]->parent.parent.name); + if (channels[index]->stat < 3) + rt_kprintf(" %s\n", stat_strs[channels[index]->stat]); + } + } + + rt_free(channels); + + return 0; +} +MSH_CMD_EXPORT(list_channel, list IPC channel information); +#endif + diff --git a/components/lwp/lwp_ipc.h b/components/lwp/lwp_ipc.h new file mode 100644 index 0000000000..adfe906914 --- /dev/null +++ b/components/lwp/lwp_ipc.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ + +#ifndef LWP_IPC_H__ +#define LWP_IPC_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + RT_CHANNEL_RAW, + RT_CHANNEL_BUFFER +}; + +struct rt_channel_msg +{ + void *sender; + int type; + union + { + struct chbuf + { + void *buf; + size_t length; + } b; + void* d; + } u; +}; +typedef struct rt_channel_msg *rt_channel_msg_t; + +int rt_channel_open(const char *name, int flags); +rt_err_t rt_channel_close(int fd); +rt_err_t rt_channel_send(int fd, rt_channel_msg_t data); +rt_err_t rt_channel_send_recv(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret); +rt_err_t rt_channel_send_recv_timeout(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time); +rt_err_t rt_channel_reply(int fd, rt_channel_msg_t data); +rt_err_t rt_channel_recv(int fd, rt_channel_msg_t data); +rt_err_t rt_channel_recv_timeout(int fd, rt_channel_msg_t data, rt_int32_t time); +rt_err_t rt_channel_peek(int fd, rt_channel_msg_t data); + +rt_channel_t rt_raw_channel_open(const char *name, int flags); +rt_err_t rt_raw_channel_close(rt_channel_t ch); +rt_err_t rt_raw_channel_send(rt_channel_t ch, rt_channel_msg_t data); +rt_err_t rt_raw_channel_send_recv(rt_channel_t ch, rt_channel_msg_t data, rt_channel_msg_t data_ret); +rt_err_t rt_raw_channel_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time); +rt_err_t rt_raw_channel_reply(rt_channel_t ch, rt_channel_msg_t data); +rt_err_t rt_raw_channel_recv(rt_channel_t ch, rt_channel_msg_t data); +rt_err_t rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_int32_t time); +rt_err_t rt_raw_channel_peek(rt_channel_t ch, rt_channel_msg_t data); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/lwp/lwp_ipc_internal.h b/components/lwp/lwp_ipc_internal.h new file mode 100644 index 0000000000..1efa7a1c02 --- /dev/null +++ b/components/lwp/lwp_ipc_internal.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-16 Jesven first version + */ +#ifndef LWP_IPC_INTERNAL_H__ +#define LWP_IPC_INTERNAL_H__ + +#include <rthw.h> +#include <rtthread.h> +#include <lwp.h> + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + FDT_TYPE_LWP, + FDT_TYPE_KERNEL +}; + +int lwp_channel_open(int fdt_type, const char *name, int flags); +rt_err_t lwp_channel_close(int fdt_type, int fd); +rt_err_t lwp_channel_send(int fdt_type, int fd, rt_channel_msg_t data); +rt_err_t lwp_channel_send_recv_timeout(int fdt_type, int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time); +rt_err_t lwp_channel_reply(int fdt_type, int fd, rt_channel_msg_t data); +rt_err_t lwp_channel_recv_timeout(int fdt_type, int fd, rt_channel_msg_t data, rt_int32_t time); + +#ifdef __cplusplus +} +#endif + +#endif /* LWP_IPC_INTERNAL_H__*/ diff --git a/components/lwp/lwp_mem.c b/components/lwp/lwp_mem.c deleted file mode 100644 index fe9de9eb67..0000000000 --- a/components/lwp/lwp_mem.c +++ /dev/null @@ -1,220 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-06-10 Bernard first version - */ - -#include <rtthread.h> -#include <lwp.h> - -#define DBG_TAG "LWPMEM" -#define DBG_LVL DBG_WARNING -#include <rtdbg.h> - -// todo: remove repleat code -#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_lwp_memheap_item), RT_ALIGN_SIZE) -#define MEMITEM_SIZE(item) ((rt_ubase_t)item->next - (rt_ubase_t)item - RT_MEMHEAP_SIZE) - -#ifndef LWP_MEM_PAGE_SIZE - #define LWP_MEM_PAGE_SIZE (4 * 1024) -#endif - -#ifndef LWP_MEM_MAX_PAGE_COUNT - #define LWP_MEM_MAX_PAGE_COUNT (256 * 4) -#endif - -static void *rt_lwp_malloc_page(struct rt_lwp *lwp, rt_size_t npages) -{ - void *chunk; - char name[6]; - struct rt_lwp_memheap *lwp_heap; - rt_size_t page_cnt; - - RT_ASSERT(lwp != RT_NULL); - - page_cnt = lwp->heap_cnt + npages; - if (page_cnt > LWP_MEM_MAX_PAGE_COUNT) - { - dbg_log(DBG_ERROR, "alloc new page failed, lwp memory size out of limited: %d\n", page_cnt); - return RT_NULL; - } - - lwp_heap = rt_malloc(sizeof(struct rt_lwp_memheap)); - if (lwp_heap == RT_NULL) - { - dbg_log(DBG_ERROR, "alloc new page head failed, out of memory : %d\n", page_cnt); - return RT_NULL; - } - - chunk = rt_malloc(npages * LWP_MEM_PAGE_SIZE); - if (chunk == RT_NULL) - { - dbg_log(DBG_ERROR, "alloc new page buffer failed, out of memory : %d\n", page_cnt); - rt_free(lwp_heap); - return RT_NULL; - } - - dbg_log(DBG_LOG, "lwp alloc page: %d\n", npages); - - rt_sprintf(name, "lwp%02x", lwp->heap_cnt); - rt_lwp_memheap_init(lwp_heap, name, chunk, npages * LWP_MEM_PAGE_SIZE); - - rt_list_insert_before(&lwp->hlist, &lwp_heap->mlist); - - lwp->heap_cnt += npages; - - return chunk; -} - -static void rt_lwp_free_page(struct rt_lwp *lwp, struct rt_lwp_memheap *lwp_heap) -{ - rt_size_t npages; - - RT_ASSERT(lwp != RT_NULL); - RT_ASSERT(lwp_heap != RT_NULL); - RT_ASSERT(lwp_heap->start_addr != RT_NULL); - - npages = lwp_heap->pool_size / LWP_MEM_PAGE_SIZE; - lwp->heap_cnt -= npages; - - dbg_log(DBG_LOG, "lwp free page: %d\n", npages); - - rt_list_remove(&lwp_heap->mlist); - - rt_free(lwp_heap->start_addr); - rt_free(lwp_heap); -} - -void rt_lwp_mem_init(struct rt_lwp *lwp) -{ - RT_ASSERT(lwp != RT_NULL); - rt_list_init(&lwp->hlist); -} - -void rt_lwp_mem_deinit(struct rt_lwp *lwp) -{ - struct rt_list_node *node; - - RT_ASSERT(lwp != RT_NULL); - - node = lwp->hlist.next; - - while (node != &(lwp->hlist)) - { - struct rt_lwp_memheap *lwp_heap; - - lwp_heap = rt_list_entry(node, struct rt_lwp_memheap, mlist); - RT_ASSERT(lwp_heap != RT_NULL); - - /* update note before free page*/ - node = node->next; - - rt_lwp_free_page(lwp, lwp_heap); - } -} - -void *rt_lwp_mem_malloc(rt_uint32_t size) -{ - struct rt_lwp *lwp; - struct rt_list_node *node; - void *addr = RT_NULL; - rt_uint32_t npages; - - if (size == 0) - return RT_NULL; - - lwp = rt_lwp_self(); - RT_ASSERT(lwp != RT_NULL); - - for (node = lwp->hlist.next; node != &(lwp->hlist); node = node->next) - { - struct rt_lwp_memheap *lwp_heap; - lwp_heap = rt_list_entry(node, struct rt_lwp_memheap, mlist); - - addr = rt_lwp_memheap_alloc(lwp_heap, size); - if (addr != RT_NULL) - { - dbg_log(DBG_LOG, "lwp alloc 0x%x/%d\n", addr, size); - return addr; - } - } - - npages = (size + rt_lwp_memheap_unavailable_size_get() + LWP_MEM_PAGE_SIZE) / LWP_MEM_PAGE_SIZE; - if (RT_NULL != rt_lwp_malloc_page(lwp, npages)) - return rt_lwp_mem_malloc(size); - else - return RT_NULL; -} - -void rt_lwp_mem_free(void *addr) -{ - struct rt_lwp_memheap_item *header_ptr; - struct rt_lwp_memheap *lwp_heap; - - if (addr == RT_NULL) - return ; - - /* get memory item */ - header_ptr = (struct rt_lwp_memheap_item *)((rt_uint8_t *)addr - RT_MEMHEAP_SIZE); - RT_ASSERT(header_ptr); - - lwp_heap = header_ptr->pool_ptr; - RT_ASSERT(lwp_heap); - - dbg_log(DBG_LOG, "lwp free 0x%x\n", addr); - rt_lwp_memheap_free((void *)addr); - - if (rt_lwp_memheap_is_empty(lwp_heap)) - { - rt_lwp_free_page(rt_lwp_self(), lwp_heap); - } -} - -void *rt_lwp_mem_realloc(void *rmem, rt_size_t newsize) -{ - void *new_ptr; - struct rt_lwp_memheap_item *header_ptr; - - if (rmem == RT_NULL) - return rt_lwp_mem_malloc(newsize); - - if (newsize == 0) - { - rt_lwp_mem_free(rmem); - return RT_NULL; - } - - /* get old memory item */ - header_ptr = (struct rt_lwp_memheap_item *) - ((rt_uint8_t *)rmem - RT_MEMHEAP_SIZE); - - new_ptr = rt_lwp_memheap_realloc(header_ptr->pool_ptr, rmem, newsize); - if (new_ptr == RT_NULL) - { - /* allocate memory block from other memheap */ - new_ptr = rt_lwp_mem_malloc(newsize); - if (new_ptr != RT_NULL && rmem != RT_NULL) - { - rt_size_t oldsize; - - /* get the size of old memory block */ - oldsize = MEMITEM_SIZE(header_ptr); - if (newsize > oldsize) - rt_memcpy(new_ptr, rmem, oldsize); - else - rt_memcpy(new_ptr, rmem, newsize); - - dbg_log(DBG_LOG, "lwp realloc with memcpy 0x%x -> 0x%x/%d\n", rmem, new_ptr, newsize); - rt_lwp_mem_free(rmem); - - } - } - - dbg_log(DBG_LOG, "lwp realloc in same address 0x%x/%d\n", rmem, newsize); - - return new_ptr; -} diff --git a/components/lwp/lwp_mem.h b/components/lwp/lwp_mem.h deleted file mode 100644 index 7ef1c2293c..0000000000 --- a/components/lwp/lwp_mem.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-06-10 Bernard first version - */ - -#ifndef __LWP_MEM_H__ -#define __LWP_MEM_H__ - -extern void rt_lwp_mem_init(struct rt_lwp *lwp); -extern void rt_lwp_mem_deinit(struct rt_lwp *lwp); - -extern void *rt_lwp_mem_malloc(rt_uint32_t size); -extern void rt_lwp_mem_free(void *addr); -extern void *rt_lwp_mem_realloc(void *rmem, rt_size_t newsize); - -#endif diff --git a/components/lwp/lwp_memheap.c b/components/lwp/lwp_memheap.c deleted file mode 100644 index 9695aab282..0000000000 --- a/components/lwp/lwp_memheap.c +++ /dev/null @@ -1,576 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2012-04-10 Bernard first implementation - * 2012-10-16 Bernard add the mutex lock for heap object. - * 2012-12-29 Bernard memheap can be used as system heap. - * change mutex lock to semaphore lock. - * 2013-04-10 Bernard add rt_lwp_memheap_realloc function. - * 2013-05-24 Bernard fix the rt_lwp_memheap_realloc issue. - * 2013-07-11 Grissiom fix the memory block splitting issue. - * 2013-07-15 Grissiom optimize rt_lwp_memheap_realloc - */ - -#include <rthw.h> -#include <rtthread.h> -#include <lwp.h> - -/* dynamic pool magic and mask */ -#define RT_MEMHEAP_MAGIC 0x1ea01ea0 -#define RT_MEMHEAP_MASK 0xfffffffe -#define RT_MEMHEAP_USED 0x01 -#define RT_MEMHEAP_FREED 0x00 - -#define RT_MEMHEAP_IS_USED(i) ((i)->magic & RT_MEMHEAP_USED) -#define RT_MEMHEAP_MINIALLOC 12 - -#define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_lwp_memheap_item), RT_ALIGN_SIZE) -#define MEMITEM_SIZE(item) ((rt_ubase_t)item->next - (rt_ubase_t)item - RT_MEMHEAP_SIZE) - -/* - * The initialized memory pool will be: - * +-----------------------------------+--------------------------+ - * | whole freed memory block | Used Memory Block Tailer | - * +-----------------------------------+--------------------------+ - * - * block_list --> whole freed memory block - * - * The length of Used Memory Block Tailer is 0, - * which is prevents block merging across list - */ -rt_err_t rt_lwp_memheap_init(struct rt_lwp_memheap *memheap, - const char *name, - void *start_addr, - rt_uint32_t size) -{ - struct rt_lwp_memheap_item *item; - - RT_ASSERT(memheap != RT_NULL); - - /* initialize pool object */ - memheap->start_addr = start_addr; - memheap->pool_size = RT_ALIGN_DOWN(size, RT_ALIGN_SIZE); - memheap->available_size = memheap->pool_size - (2 * RT_MEMHEAP_SIZE); - memheap->max_used_size = memheap->pool_size - memheap->available_size; - - /* initialize the free list header */ - item = &(memheap->free_header); - item->magic = RT_MEMHEAP_MAGIC; - item->pool_ptr = memheap; - item->next = RT_NULL; - item->prev = RT_NULL; - item->next_free = item; - item->prev_free = item; - - /* set the free list to free list header */ - memheap->free_list = item; - - /* initialize the first big memory block */ - item = (struct rt_lwp_memheap_item *)start_addr; - item->magic = RT_MEMHEAP_MAGIC; - item->pool_ptr = memheap; - item->next = RT_NULL; - item->prev = RT_NULL; - item->next_free = item; - item->prev_free = item; - - item->next = (struct rt_lwp_memheap_item *) - ((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE); - item->prev = item->next; - - /* block list header */ - memheap->block_list = item; - - /* place the big memory block to free list */ - item->next_free = memheap->free_list->next_free; - item->prev_free = memheap->free_list; - memheap->free_list->next_free->prev_free = item; - memheap->free_list->next_free = item; - - /* move to the end of memory pool to build a small tailer block, - * which prevents block merging - */ - item = item->next; - /* it's a used memory block */ - item->magic = RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED; - item->pool_ptr = memheap; - item->next = (struct rt_lwp_memheap_item *)start_addr; - item->prev = (struct rt_lwp_memheap_item *)start_addr; - /* not in free list */ - item->next_free = item->prev_free = RT_NULL; - - /* initialize semaphore lock */ - rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_FIFO); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x\n", - start_addr, size, &(memheap->free_header))); - - return RT_EOK; -} - -void *rt_lwp_memheap_alloc(struct rt_lwp_memheap *heap, rt_uint32_t size) -{ - rt_err_t result; - rt_uint32_t free_size; - struct rt_lwp_memheap_item *header_ptr; - - RT_ASSERT(heap != RT_NULL); - - /* align allocated size */ - size = RT_ALIGN(size, RT_ALIGN_SIZE); - if (size < RT_MEMHEAP_MINIALLOC) - size = RT_MEMHEAP_MINIALLOC; - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate %d on heap:%8.*s", - size, RT_NAME_MAX, heap->parent.name)); - - if (size < heap->available_size) - { - /* search on free list */ - free_size = 0; - - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) - { - rt_set_errno(result); - - return RT_NULL; - } - - /* get the first free memory block */ - header_ptr = heap->free_list->next_free; - while (header_ptr != heap->free_list && free_size < size) - { - /* get current freed memory block size */ - free_size = MEMITEM_SIZE(header_ptr); - if (free_size < size) - { - /* move to next free memory block */ - header_ptr = header_ptr->next_free; - } - } - - /* determine if the memory is available. */ - if (free_size >= size) - { - /* a block that satisfies the request has been found. */ - - /* determine if the block needs to be split. */ - if (free_size >= (size + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC)) - { - struct rt_lwp_memheap_item *new_ptr; - - /* split the block. */ - new_ptr = (struct rt_lwp_memheap_item *) - (((rt_uint8_t *)header_ptr) + size + RT_MEMHEAP_SIZE); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n", - header_ptr, - header_ptr->next, - header_ptr->prev, - new_ptr)); - - /* mark the new block as a memory block and freed. */ - new_ptr->magic = RT_MEMHEAP_MAGIC; - - /* put the pool pointer into the new block. */ - new_ptr->pool_ptr = heap; - - /* break down the block list */ - new_ptr->prev = header_ptr; - new_ptr->next = header_ptr->next; - header_ptr->next->prev = new_ptr; - header_ptr->next = new_ptr; - - /* remove header ptr from free list */ - header_ptr->next_free->prev_free = header_ptr->prev_free; - header_ptr->prev_free->next_free = header_ptr->next_free; - header_ptr->next_free = RT_NULL; - header_ptr->prev_free = RT_NULL; - - /* insert new_ptr to free list */ - new_ptr->next_free = heap->free_list->next_free; - new_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = new_ptr; - heap->free_list->next_free = new_ptr; - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x\n", - new_ptr->next_free, - new_ptr->prev_free)); - - /* decrement the available byte count. */ - heap->available_size = heap->available_size - - size - - RT_MEMHEAP_SIZE; - if (heap->pool_size - heap->available_size > heap->max_used_size) - heap->max_used_size = heap->pool_size - heap->available_size; - } - else - { - /* decrement the entire free size from the available bytes count. */ - heap->available_size = heap->available_size - free_size; - if (heap->pool_size - heap->available_size > heap->max_used_size) - heap->max_used_size = heap->pool_size - heap->available_size; - - /* remove header_ptr from free list */ - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("one block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x\n", - header_ptr, - header_ptr->next_free, - header_ptr->prev_free)); - - header_ptr->next_free->prev_free = header_ptr->prev_free; - header_ptr->prev_free->next_free = header_ptr->next_free; - header_ptr->next_free = RT_NULL; - header_ptr->prev_free = RT_NULL; - } - - /* Mark the allocated block as not available. */ - header_ptr->magic |= RT_MEMHEAP_USED; - - /* release lock */ - rt_sem_release(&(heap->lock)); - - /* Return a memory address to the caller. */ - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("alloc mem: memory[0x%08x], heap[0x%08x], size: %d\n", - (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE), - header_ptr, - size)); - - return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE); - } - - /* release lock */ - rt_sem_release(&(heap->lock)); - } - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n")); - - /* Return the completion status. */ - return RT_NULL; -} - -void *rt_lwp_memheap_realloc(struct rt_lwp_memheap *heap, void *ptr, rt_size_t newsize) -{ - rt_err_t result; - rt_size_t oldsize; - struct rt_lwp_memheap_item *header_ptr; - struct rt_lwp_memheap_item *new_ptr; - - if (newsize == 0) - { - rt_lwp_memheap_free(ptr); - - return RT_NULL; - } - /* align allocated size */ - newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE); - if (newsize < RT_MEMHEAP_MINIALLOC) - newsize = RT_MEMHEAP_MINIALLOC; - - if (ptr == RT_NULL) - { - return rt_lwp_memheap_alloc(heap, newsize); - } - - /* get memory block header and get the size of memory block */ - header_ptr = (struct rt_lwp_memheap_item *) - ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE); - oldsize = MEMITEM_SIZE(header_ptr); - /* re-allocate memory */ - if (newsize > oldsize) - { - void *new_ptr; - struct rt_lwp_memheap_item *next_ptr; - - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) - { - rt_set_errno(result); - return RT_NULL; - } - - next_ptr = header_ptr->next; - - /* header_ptr should not be the tail */ - RT_ASSERT(next_ptr > header_ptr); - - /* check whether the following free space is enough to expand */ - if (!RT_MEMHEAP_IS_USED(next_ptr)) - { - rt_int32_t nextsize; - - nextsize = MEMITEM_SIZE(next_ptr); - RT_ASSERT(next_ptr > 0); - - /* Here is the ASCII art of the situation that we can make use of - * the next free node without alloc/memcpy, |*| is the control - * block: - * - * oldsize free node - * |*|-----------|*|----------------------|*| - * newsize >= minialloc - * |*|----------------|*|-----------------|*| - */ - if (nextsize + oldsize > newsize + RT_MEMHEAP_MINIALLOC) - { - /* decrement the entire free size from the available bytes count. */ - heap->available_size = heap->available_size - (newsize - oldsize); - if (heap->pool_size - heap->available_size > heap->max_used_size) - heap->max_used_size = heap->pool_size - heap->available_size; - - /* remove next_ptr from free list */ - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("remove block: block[0x%08x], next_free 0x%08x, prev_free 0x%08x", - next_ptr, - next_ptr->next_free, - next_ptr->prev_free)); - - next_ptr->next_free->prev_free = next_ptr->prev_free; - next_ptr->prev_free->next_free = next_ptr->next_free; - next_ptr->next->prev = next_ptr->prev; - next_ptr->prev->next = next_ptr->next; - - /* build a new one on the right place */ - next_ptr = (struct rt_lwp_memheap_item *)((char *)ptr + newsize); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("new free block: block[0x%08x] nextm[0x%08x] prevm[0x%08x]", - next_ptr, - next_ptr->next, - next_ptr->prev)); - - /* mark the new block as a memory block and freed. */ - next_ptr->magic = RT_MEMHEAP_MAGIC; - - /* put the pool pointer into the new block. */ - next_ptr->pool_ptr = heap; - - next_ptr->prev = header_ptr; - next_ptr->next = header_ptr->next; - header_ptr->next->prev = next_ptr; - header_ptr->next = next_ptr; - - /* insert next_ptr to free list */ - next_ptr->next_free = heap->free_list->next_free; - next_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = next_ptr; - heap->free_list->next_free = next_ptr; - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x", - next_ptr->next_free, - next_ptr->prev_free)); - - /* release lock */ - rt_sem_release(&(heap->lock)); - - return ptr; - } - } - - /* release lock */ - rt_sem_release(&(heap->lock)); - - /* re-allocate a memory block */ - new_ptr = (void *)rt_lwp_memheap_alloc(heap, newsize); - if (new_ptr != RT_NULL) - { - rt_memcpy(new_ptr, ptr, oldsize < newsize ? oldsize : newsize); - rt_lwp_memheap_free(ptr); - } - - return new_ptr; - } - - /* don't split when there is less than one node space left */ - if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize) - return ptr; - - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) - { - rt_set_errno(result); - - return RT_NULL; - } - - /* split the block. */ - new_ptr = (struct rt_lwp_memheap_item *) - (((rt_uint8_t *)header_ptr) + newsize + RT_MEMHEAP_SIZE); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("split: block[0x%08x] nextm[0x%08x] prevm[0x%08x] to new[0x%08x]\n", - header_ptr, - header_ptr->next, - header_ptr->prev, - new_ptr)); - - /* mark the new block as a memory block and freed. */ - new_ptr->magic = RT_MEMHEAP_MAGIC; - /* put the pool pointer into the new block. */ - new_ptr->pool_ptr = heap; - - /* break down the block list */ - new_ptr->prev = header_ptr; - new_ptr->next = header_ptr->next; - header_ptr->next->prev = new_ptr; - header_ptr->next = new_ptr; - - /* determine if the block can be merged with the next neighbor. */ - if (!RT_MEMHEAP_IS_USED(new_ptr->next)) - { - struct rt_lwp_memheap_item *free_ptr; - - /* merge block with next neighbor. */ - free_ptr = new_ptr->next; - heap->available_size = heap->available_size - MEMITEM_SIZE(free_ptr); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n", - header_ptr, header_ptr->next_free, header_ptr->prev_free)); - - free_ptr->next->prev = new_ptr; - new_ptr->next = free_ptr->next; - - /* remove free ptr from free list */ - free_ptr->next_free->prev_free = free_ptr->prev_free; - free_ptr->prev_free->next_free = free_ptr->next_free; - } - - /* insert the split block to free list */ - new_ptr->next_free = heap->free_list->next_free; - new_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = new_ptr; - heap->free_list->next_free = new_ptr; - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new free ptr: next_free 0x%08x, prev_free 0x%08x\n", - new_ptr->next_free, - new_ptr->prev_free)); - - /* increment the available byte count. */ - heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr); - - /* release lock */ - rt_sem_release(&(heap->lock)); - - /* return the old memory block */ - return ptr; -} - -void rt_lwp_memheap_free(void *ptr) -{ - rt_err_t result; - struct rt_lwp_memheap *heap; - struct rt_lwp_memheap_item *header_ptr, *new_ptr; - rt_uint32_t insert_header; - - /* NULL check */ - if (ptr == RT_NULL) return; - - /* set initial status as OK */ - insert_header = 1; - new_ptr = RT_NULL; - header_ptr = (struct rt_lwp_memheap_item *) - ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE); - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("free memory: memory[0x%08x], block[0x%08x]\n", - ptr, header_ptr)); - - /* check magic */ - RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); - RT_ASSERT(header_ptr->magic & RT_MEMHEAP_USED); - /* check whether this block of memory has been over-written. */ - RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); - - /* get pool ptr */ - heap = header_ptr->pool_ptr; - - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) - { - rt_set_errno(result); - - return ; - } - - /* Mark the memory as available. */ - header_ptr->magic &= ~RT_MEMHEAP_USED; - /* Adjust the available number of bytes. */ - heap->available_size = heap->available_size + MEMITEM_SIZE(header_ptr); - - /* Determine if the block can be merged with the previous neighbor. */ - if (!RT_MEMHEAP_IS_USED(header_ptr->prev)) - { - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("merge: left node 0x%08x\n", - header_ptr->prev)); - - /* adjust the available number of bytes. */ - heap->available_size = heap->available_size + RT_MEMHEAP_SIZE; - - /* yes, merge block with previous neighbor. */ - (header_ptr->prev)->next = header_ptr->next; - (header_ptr->next)->prev = header_ptr->prev; - - /* move header pointer to previous. */ - header_ptr = header_ptr->prev; - /* don't insert header to free list */ - insert_header = 0; - } - - /* determine if the block can be merged with the next neighbor. */ - if (!RT_MEMHEAP_IS_USED(header_ptr->next)) - { - /* adjust the available number of bytes. */ - heap->available_size = heap->available_size + RT_MEMHEAP_SIZE; - - /* merge block with next neighbor. */ - new_ptr = header_ptr->next; - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("merge: right node 0x%08x, next_free 0x%08x, prev_free 0x%08x\n", - new_ptr, new_ptr->next_free, new_ptr->prev_free)); - - new_ptr->next->prev = header_ptr; - header_ptr->next = new_ptr->next; - - /* remove new ptr from free list */ - new_ptr->next_free->prev_free = new_ptr->prev_free; - new_ptr->prev_free->next_free = new_ptr->next_free; - } - - if (insert_header) - { - /* no left merge, insert to free list */ - header_ptr->next_free = heap->free_list->next_free; - header_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = header_ptr; - heap->free_list->next_free = header_ptr; - - RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, - ("insert to free list: next_free 0x%08x, prev_free 0x%08x\n", - header_ptr->next_free, header_ptr->prev_free)); - } - - /* release lock */ - rt_sem_release(&(heap->lock)); -} - -rt_bool_t rt_lwp_memheap_is_empty(struct rt_lwp_memheap *memheap) -{ - RT_ASSERT(memheap != RT_NULL); - - return (memheap->available_size + 2 * sizeof(struct rt_lwp_memheap_item)) == memheap->pool_size; -} - -rt_bool_t rt_lwp_memheap_unavailable_size_get(void) -{ - return 2 * RT_MEMHEAP_SIZE + 3; -} diff --git a/components/lwp/lwp_memheap.h b/components/lwp/lwp_memheap.h deleted file mode 100644 index 845feb8594..0000000000 --- a/components/lwp/lwp_memheap.h +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-06-10 Bernard first version - */ - -#ifndef __LWP_MEMHEAP_H__ -#define __LWP_MEMHEAP_H__ - -#include <stdint.h> -#include <rtthread.h> - -/** - * memory item on the heap - */ -struct rt_lwp_memheap_item -{ - rt_uint32_t magic; /**< magic number for memheap */ - struct rt_lwp_memheap *pool_ptr; /**< point of pool */ - - struct rt_lwp_memheap_item *next; /**< next memheap item */ - struct rt_lwp_memheap_item *prev; /**< prev memheap item */ - - struct rt_lwp_memheap_item *next_free; /**< next free memheap item */ - struct rt_lwp_memheap_item *prev_free; /**< prev free memheap item */ -}; - -/** - * Base structure of memory heap object - */ -struct rt_lwp_memheap -{ - struct rt_object parent; /**< inherit from rt_object */ - - void *start_addr; /**< pool start address and size */ - - rt_uint32_t pool_size; /**< pool size */ - rt_uint32_t available_size; /**< available size */ - rt_uint32_t max_used_size; /**< maximum allocated size */ - - struct rt_lwp_memheap_item *block_list; /**< used block list */ - struct rt_lwp_memheap_item *free_list; /**< free block list */ - struct rt_lwp_memheap_item free_header; /**< free block list header */ - - struct rt_semaphore lock; /**< semaphore lock */ - - rt_list_t mlist; -}; - -extern rt_err_t rt_lwp_memheap_init(struct rt_lwp_memheap *memheap, const char *name, void *start_addr, rt_uint32_t size); -extern void *rt_lwp_memheap_alloc(struct rt_lwp_memheap *heap, rt_uint32_t size); -extern void rt_lwp_memheap_free(void *ptr); -extern void *rt_lwp_memheap_realloc(struct rt_lwp_memheap *heap, void *ptr, rt_size_t newsize); -extern rt_bool_t rt_lwp_memheap_is_empty(struct rt_lwp_memheap *memheap); -extern rt_bool_t rt_lwp_memheap_unavailable_size_get(void); - -#endif diff --git a/components/lwp/lwp_mm.c b/components/lwp/lwp_mm.c new file mode 100644 index 0000000000..f8d051133c --- /dev/null +++ b/components/lwp/lwp_mm.c @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#include <rthw.h> +#include <rtthread.h> +#include "lwp_mm.h" + +static rt_mutex_t mm_lock; + +void rt_mm_lock(void) +{ + if (rt_thread_self()) + { + if (!mm_lock) + { + mm_lock = rt_mutex_create("mm_lock", RT_IPC_FLAG_FIFO); + } + if (mm_lock) + { + rt_mutex_take(mm_lock, RT_WAITING_FOREVER); + } + } +} + +void rt_mm_unlock(void) +{ + if (rt_thread_self()) + { + if (mm_lock) + { + rt_mutex_release(mm_lock); + } + } +} diff --git a/components/lwp/lwp_mm.h b/components/lwp/lwp_mm.h new file mode 100644 index 0000000000..57f9b78faf --- /dev/null +++ b/components/lwp/lwp_mm.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ + +#ifndef __LWP_MM_H__ +#define __LWP_MM_H__ + +void rt_mm_lock(void); +void rt_mm_unlock(void); + +#endif /*__LWP_MM_H__*/ diff --git a/components/lwp/lwp_mm_area.c b/components/lwp/lwp_mm_area.c new file mode 100644 index 0000000000..08ecf0684a --- /dev/null +++ b/components/lwp/lwp_mm_area.c @@ -0,0 +1,122 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + */ +#include <rtthread.h> + +#ifdef RT_USING_USERSPACE +#include <lwp_mm_area.h> + +int lwp_map_area_insert(struct lwp_avl_struct **avl_tree, size_t addr, size_t size, int ma_type) +{ + struct lwp_avl_struct *node = RT_NULL; + struct rt_mm_area_struct *ma = RT_NULL; + + if (!size) + { + return -1; + } + ma = (struct rt_mm_area_struct *)rt_malloc(sizeof(struct rt_mm_area_struct)); + if (!ma) + { + return -1; + } + ma->addr = addr; + ma->size = size; + ma->type = ma_type; + + node = (struct lwp_avl_struct *)rt_malloc(sizeof(struct lwp_avl_struct)); + if (!node) + { + rt_free(ma); + return -1; + } + memset(node, 0, sizeof(struct lwp_avl_struct)); + + node->avl_key = ma->addr; + node->data = (void *)ma; + lwp_avl_insert(node, avl_tree); + return 0; +} + +void lwp_map_area_remove(struct lwp_avl_struct **avl_tree, size_t addr) +{ + struct lwp_avl_struct *node = RT_NULL; + + node = lwp_avl_find(addr, *avl_tree); + if (!node) + { + return; + } + lwp_avl_remove(node, avl_tree); + rt_free(node->data); + rt_free(node); +} + +struct lwp_avl_struct* lwp_map_find(struct lwp_avl_struct* ptree, size_t addr) +{ + struct lwp_avl_struct *node = ptree; + + while (1) + { + if (!node) + { + return node; + } + if ((size_t)node->avl_key <= addr) + { + struct rt_mm_area_struct *ma = (struct rt_mm_area_struct *)node->data; + if ((ma->addr <= addr) && (addr < ma->addr + ma->size)) + { + /* find area */ + break; + } + node = node->avl_right; + } + else + { + node = node->avl_left; + } + } + return node; +} + +struct lwp_avl_struct* lwp_map_find_first(struct lwp_avl_struct* ptree) +{ + if (ptree == AVL_EMPTY) + { + return (struct lwp_avl_struct *)0; + } + while (1) + { + if (!ptree->avl_left) + { + break; + } + ptree = ptree->avl_left; + } + return ptree; +} + +int top_mem_fun(struct lwp_avl_struct* ptree, void *arg) +{ + size_t *vs = (size_t *)arg; + struct rt_mm_area_struct *ma; + + ma = (struct rt_mm_area_struct *)ptree->data; + *vs += ma->size; + return 0; +} + +size_t lwp_vmem_count(struct lwp_avl_struct *ptree) +{ + size_t vsize = 0; + lwp_avl_traversal(ptree, top_mem_fun, &vsize); + return vsize; +} +#endif diff --git a/components/lwp/lwp_mm_area.h b/components/lwp/lwp_mm_area.h new file mode 100644 index 0000000000..555a03ce48 --- /dev/null +++ b/components/lwp/lwp_mm_area.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + */ +#ifndef __LWP_MM_AREA_H__ +#define __LWP_MM_AREA_H__ + +#include <string.h> +#include <stdint.h> + +#include <lwp_avl.h> + +#ifdef RT_USING_USERSPACE + +#ifdef __cplusplus +extern "C" { +#endif + +enum +{ + MM_AREA_TYPE_PHY = 0, /* mm_area physical address is IO register or reserved memory no cached*/ + MM_AREA_TYPE_PHY_CACHED, /* mm_area physical address is IO register or reserved memory with cached */ + MM_AREA_TYPE_SHM, /* mm_area physical address is shared memory */ + MM_AREA_TYPE_DATA, /* mm_area physical address is alloced from page manager for data */ + MM_AREA_TYPE_TEXT, /* mm_area physical address is alloced from page manager for text */ + MM_AREA_TYPE_UNKNOW, +}; + +struct rt_mm_area_struct +{ + size_t addr; + size_t size; + int type; +}; + +int lwp_map_area_insert(struct lwp_avl_struct **avl_tree, size_t addr, size_t size, int ma_type); +void lwp_map_area_remove(struct lwp_avl_struct **avl_tree, size_t addr); +struct lwp_avl_struct* lwp_map_find(struct lwp_avl_struct* ptree, size_t addr); +struct lwp_avl_struct* lwp_map_find_first(struct lwp_avl_struct* ptree); +size_t lwp_vmem_count(struct lwp_avl_struct *ptree); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /*__LWP_MM_AREA_H__*/ diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c new file mode 100644 index 0000000000..086a271e91 --- /dev/null +++ b/components/lwp/lwp_pid.c @@ -0,0 +1,1117 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-16 zhangjun first version + * 2021-02-20 lizhirui fix warning + */ + +#include <rthw.h> +#include <rtthread.h> + +#include <dfs_file.h> +#include <unistd.h> +#include <stdio.h> /* rename() */ +#include <sys/stat.h> +#include <sys/statfs.h> /* statfs() */ + +#include "lwp.h" +#include "lwp_pid.h" +#include "tty.h" + +#ifdef RT_USING_USERSPACE +#include "lwp_user_mm.h" +#endif + +#define DBG_TAG "LWP_PID" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define PID_MAX 10000 + +#define PID_CT_ASSERT(name, x) \ + struct assert_##name {char ary[2 * (x) - 1];} + +PID_CT_ASSERT(pid_min_nr, RT_LWP_MAX_NR > 1); +PID_CT_ASSERT(pid_max_nr, RT_LWP_MAX_NR < PID_MAX); + +static struct lwp_avl_struct lwp_pid_ary[RT_LWP_MAX_NR]; +static struct lwp_avl_struct *lwp_pid_free_head = RT_NULL; +static int lwp_pid_ary_alloced = 0; +static struct lwp_avl_struct *lwp_pid_root = RT_NULL; +static pid_t current_pid = 0; + +struct lwp_avl_struct *lwp_get_pid_ary(void) +{ + return lwp_pid_ary; +} + +static pid_t lwp_pid_get(void) +{ + rt_base_t level; + struct lwp_avl_struct *p; + pid_t pid = 0; + + level = rt_hw_interrupt_disable(); + p = lwp_pid_free_head; + if (p) + { + lwp_pid_free_head = (struct lwp_avl_struct *)p->avl_right; + } + else if (lwp_pid_ary_alloced < RT_LWP_MAX_NR) + { + p = lwp_pid_ary + lwp_pid_ary_alloced; + lwp_pid_ary_alloced++; + } + if (p) + { + int found_noused = 0; + + RT_ASSERT(p->data == RT_NULL); + for (pid = current_pid + 1; pid < PID_MAX; pid++) + { + if (!lwp_avl_find(pid, lwp_pid_root)) + { + found_noused = 1; + break; + } + } + if (!found_noused) + { + for (pid = 1; pid <= current_pid; pid++) + { + if (!lwp_avl_find(pid, lwp_pid_root)) + { + found_noused = 1; + break; + } + } + } + p->avl_key = pid; + lwp_avl_insert(p, &lwp_pid_root); + current_pid = pid; + } + rt_hw_interrupt_enable(level); + return pid; +} + +static void lwp_pid_put(pid_t pid) +{ + rt_base_t level; + struct lwp_avl_struct *p; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(pid, lwp_pid_root); + if (p) + { + p->data = RT_NULL; + lwp_avl_remove(p, &lwp_pid_root); + p->avl_right = lwp_pid_free_head; + lwp_pid_free_head = p; + } + rt_hw_interrupt_enable(level); +} + +static void lwp_pid_set_lwp(pid_t pid, struct rt_lwp *lwp) +{ + rt_base_t level; + struct lwp_avl_struct *p; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(pid, lwp_pid_root); + if (p) + { + p->data = lwp; + } + rt_hw_interrupt_enable(level); +} + +static void __exit_files(struct rt_lwp *lwp) +{ + int fd = lwp->fdt.maxfd - 1; + + while (fd >= 0) + { + struct dfs_fd *d; + + d = lwp->fdt.fds[fd]; + if (d) + { + dfs_file_close(d); + fdt_fd_release(&lwp->fdt, fd); + } + fd--; + } +} + +void lwp_user_object_lock_init(struct rt_lwp *lwp) +{ + rt_mutex_init(&lwp->object_mutex, "lwp_obj", RT_IPC_FLAG_PRIO); +} + +void lwp_user_object_lock_destroy(struct rt_lwp *lwp) +{ + rt_mutex_detach(&lwp->object_mutex); +} + +void lwp_user_object_lock(struct rt_lwp *lwp) +{ + if (lwp) + { + rt_mutex_take(&lwp->object_mutex, RT_WAITING_FOREVER); + } + else + { + RT_ASSERT(0); + } +} + +void lwp_user_object_unlock(struct rt_lwp *lwp) +{ + if (lwp) + { + rt_mutex_release(&lwp->object_mutex); + } + else + { + RT_ASSERT(0); + } +} + +int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object) +{ + int ret = -1; + + if (lwp && object) + { + lwp_user_object_lock(lwp); + if (!lwp_avl_find((avl_key_t)object, lwp->object_root)) + { + struct lwp_avl_struct *node; + + node = (struct lwp_avl_struct *)rt_malloc(sizeof(struct lwp_avl_struct)); + if (node) + { + rt_base_t level; + + level = rt_hw_interrupt_disable(); + object->lwp_ref_count++; + rt_hw_interrupt_enable(level); + node->avl_key = (avl_key_t)object; + lwp_avl_insert(node, &lwp->object_root); + ret = 0; + } + } + lwp_user_object_unlock(lwp); + } + return ret; +} + +static rt_err_t _object_node_delete(struct rt_lwp *lwp, struct lwp_avl_struct *node) +{ + rt_err_t ret = -1; + rt_object_t object; + + if (!lwp || !node) + { + return ret; + } + object = (rt_object_t)node->avl_key; + object->lwp_ref_count--; + if (object->lwp_ref_count == 0) + { + /* remove from kernel object list */ + switch (object->type) + { + case RT_Object_Class_Semaphore: + ret = rt_sem_delete((rt_sem_t)object); + break; + case RT_Object_Class_Mutex: + ret = rt_mutex_delete((rt_mutex_t)object); + break; + case RT_Object_Class_Event: + ret = rt_event_delete((rt_event_t)object); + break; + case RT_Object_Class_MailBox: + ret = rt_mb_delete((rt_mailbox_t)object); + break; + case RT_Object_Class_MessageQueue: + ret = rt_mq_delete((rt_mq_t)object); + break; + case RT_Object_Class_Timer: + ret = rt_timer_delete((rt_timer_t)object); + break; + case RT_Object_Class_Custom: + ret = rt_custom_object_destroy(object); + break; + default: + LOG_E("input object type(%d) error", object->type); + break; + } + } + else + { + ret = 0; + } + lwp_avl_remove(node, &lwp->object_root); + rt_free(node); + return ret; +} + +rt_err_t lwp_user_object_delete(struct rt_lwp *lwp, rt_object_t object) +{ + rt_err_t ret = -1; + + if (lwp && object) + { + struct lwp_avl_struct *node; + + lwp_user_object_lock(lwp); + node = lwp_avl_find((avl_key_t)object, lwp->object_root); + ret = _object_node_delete(lwp, node); + lwp_user_object_unlock(lwp); + } + return ret; +} + +void lwp_user_object_clear(struct rt_lwp *lwp) +{ + struct lwp_avl_struct *node; + + lwp_user_object_lock(lwp); + while ((node = lwp_map_find_first(lwp->object_root)) != RT_NULL) + { + _object_node_delete(lwp, node); + } + lwp_user_object_unlock(lwp); +} + +static int _object_dup(struct lwp_avl_struct *node, void *arg) +{ + rt_object_t object; + struct rt_lwp *dst_lwp = (struct rt_lwp *)arg; + + object = (rt_object_t)node->avl_key; + lwp_user_object_add(dst_lwp, object); + return 0; +} + +void lwp_user_object_dup(struct rt_lwp *dst_lwp, struct rt_lwp *src_lwp) +{ + lwp_user_object_lock(src_lwp); + lwp_avl_traversal(src_lwp->object_root, _object_dup, dst_lwp); + lwp_user_object_unlock(src_lwp); +} + +struct rt_lwp* lwp_new(void) +{ + pid_t pid; + rt_base_t level; + struct rt_lwp* lwp = RT_NULL; + + lwp = (struct rt_lwp *)rt_malloc(sizeof(struct rt_lwp)); + if (lwp == RT_NULL) + { + return lwp; + } + rt_memset(lwp, 0, sizeof(*lwp)); + //lwp->tgroup_leader = RT_NULL; + rt_list_init(&lwp->wait_list); + lwp->leader = 0; + lwp->session = -1; + lwp->tty = RT_NULL; + rt_list_init(&lwp->t_grp); + lwp_user_object_lock_init(lwp); + lwp->address_search_head = RT_NULL; + rt_wqueue_init(&lwp->wait_queue); + lwp->ref = 1; + + level = rt_hw_interrupt_disable(); + pid = lwp_pid_get(); + if (pid == 0) + { + lwp_user_object_lock_destroy(lwp); + rt_free(lwp); + lwp = RT_NULL; + LOG_E("pid slot fulled!\n"); + goto out; + } + lwp->pid = pid; + lwp_pid_set_lwp(pid, lwp); + +#ifdef LWP_ENABLE_ASID + lwp->generation = 0; + lwp->asid = 0; +#endif + +out: + rt_hw_interrupt_enable(level); + return lwp; +} + +void lwp_free(struct rt_lwp* lwp) +{ + rt_base_t level; + + if (lwp == RT_NULL) + { + return; + } + + LOG_D("lwp free: %p\n", lwp); + + level = rt_hw_interrupt_disable(); + lwp->finish = 1; + rt_hw_interrupt_enable(level); + + if (lwp->args != RT_NULL) + { +#ifndef ARCH_MM_MMU + lwp->args_length = RT_NULL; +#ifndef ARCH_MM_MPU + rt_free(lwp->args); +#endif /* not defined ARCH_MM_MPU */ +#endif /* ARCH_MM_MMU */ + lwp->args = RT_NULL; + } + + if (lwp->fdt.fds != RT_NULL) + { + /* auto clean fds */ + __exit_files(lwp); + rt_free(lwp->fdt.fds); + lwp->fdt.fds = RT_NULL; + } + + lwp_user_object_clear(lwp); + lwp_user_object_lock_destroy(lwp); + + /* free data section */ + if (lwp->data_entry != RT_NULL) + { +#ifdef ARCH_MM_MMU + rt_free_align(lwp->data_entry); +#else +#ifdef ARCH_MM_MPU + rt_lwp_umap_user(lwp, lwp->text_entry, 0); + rt_lwp_free_user(lwp, lwp->data_entry, lwp->data_size); +#else + rt_free_align(lwp->data_entry); +#endif /* ARCH_MM_MPU */ +#endif /* ARCH_MM_MMU */ + lwp->data_entry = RT_NULL; + } + + /* free text section */ + if (lwp->lwp_type == LWP_TYPE_DYN_ADDR) + { + if (lwp->text_entry) + { + LOG_D("lwp text free: %p", lwp->text_entry); +#ifndef ARCH_MM_MMU + rt_free((void*)lwp->text_entry); +#endif /* not defined ARCH_MM_MMU */ + lwp->text_entry = RT_NULL; + } + } + +#ifdef RT_USING_USERSPACE + lwp_unmap_user_space(lwp); +#endif + + level = rt_hw_interrupt_disable(); + /* for children */ + while (lwp->first_child) + { + struct rt_lwp *child; + + child = lwp->first_child; + lwp->first_child = child->sibling; + if (child->finish) + { + lwp_pid_put(lwp_to_pid(child)); + rt_hw_interrupt_enable(level); + rt_free(child); + level = rt_hw_interrupt_disable(); + } + else + { + child->sibling = RT_NULL; + child->parent = RT_NULL; + } + } + + rt_hw_interrupt_enable(level); + /* for parent */ + { + struct termios *old_stdin_termios = get_old_termios(); + struct rt_lwp *old_lwp = NULL; + + if (lwp->session == -1) + { + tcsetattr(1, 0, old_stdin_termios); + } + level = rt_hw_interrupt_disable(); + if (lwp->tty != RT_NULL) + { + rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER); + old_lwp = tty_pop(&lwp->tty->head, RT_NULL); + rt_mutex_release(&lwp->tty->lock); + if (lwp->tty->foreground == lwp) + { + lwp->tty->foreground = old_lwp; + lwp->tty = RT_NULL; + } + } + + if (lwp->parent) + { + struct rt_thread *thread; + if (!rt_list_isempty(&lwp->wait_list)) + { + thread = rt_list_entry(lwp->wait_list.next, struct rt_thread, tlist); + thread->error = RT_EOK; + thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret; + rt_thread_resume(thread); + rt_hw_interrupt_enable(level); + return; + } + else + { + struct rt_lwp **it = &lwp->parent->first_child; + + while (*it != lwp) + { + it = &(*it)->sibling; + } + *it = lwp->sibling; + } + } + lwp_pid_put(lwp_to_pid(lwp)); + rt_hw_interrupt_enable(level); + rt_free(lwp); + } +} + +int lwp_ref_inc(struct rt_lwp *lwp) +{ + rt_base_t level; + + level = rt_hw_interrupt_disable(); + lwp->ref++; + rt_hw_interrupt_enable(level); + + return 0; +} + +int lwp_ref_dec(struct rt_lwp *lwp) +{ + rt_base_t level; + int ref = -1; + + level = rt_hw_interrupt_disable(); + if (lwp->ref) + { + lwp->ref--; + ref = lwp->ref; + } + rt_hw_interrupt_enable(level); + if (!ref) + { + struct rt_channel_msg msg; + + if (lwp->debug) + { + memset(&msg, 0, sizeof msg); + rt_raw_channel_send(gdb_server_channel(), &msg); + } + +#ifndef ARCH_MM_MMU +#ifdef RT_LWP_USING_SHM + lwp_shm_lwp_free(lwp); +#endif /* RT_LWP_USING_SHM */ +#endif /* not defined ARCH_MM_MMU */ + lwp_free(lwp); + + return 0; + } + + return -1; +} + +struct rt_lwp* lwp_from_pid(pid_t pid) +{ + rt_base_t level; + struct lwp_avl_struct *p; + struct rt_lwp *lwp = RT_NULL; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(pid, lwp_pid_root); + if (p) + { + lwp = (struct rt_lwp *)p->data; + } + rt_hw_interrupt_enable(level); + return lwp; +} + +pid_t lwp_to_pid(struct rt_lwp* lwp) +{ + if (!lwp) + { + return 0; + } + return lwp->pid; +} + +char* lwp_pid2name(int32_t pid) +{ + struct rt_lwp *lwp; + char* process_name = RT_NULL; + + lwp = lwp_from_pid(pid); + if (lwp) + { + process_name = strrchr(lwp->cmd, '/'); + process_name = process_name? process_name + 1: lwp->cmd; + } + return process_name; +} + +pid_t lwp_name2pid(const char *name) +{ + int idx; + pid_t pid = 0; + rt_thread_t main_thread; + char* process_name = RT_NULL; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + for (idx = 0; idx < RT_LWP_MAX_NR; idx++) + { + /* 0 is reserved */ + struct rt_lwp *lwp = (struct rt_lwp *)lwp_pid_ary[idx].data; + + if (lwp) + { + process_name = strrchr(lwp->cmd, '/'); + process_name = process_name? process_name + 1: lwp->cmd; + if (!rt_strncmp(name, process_name, RT_NAME_MAX)) + { + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (!(main_thread->stat & RT_THREAD_CLOSE)) + { + pid = lwp->pid; + } + } + } + } + rt_hw_interrupt_enable(level); + return pid; +} + +int lwp_getpid(void) +{ + return ((struct rt_lwp *)rt_thread_self()->lwp)->pid; +} + +pid_t waitpid(pid_t pid, int *status, int options) +{ + pid_t ret = -1; + rt_base_t level; + struct rt_thread *thread; + struct rt_lwp *lwp; + struct rt_lwp *lwp_self; + + level = rt_hw_interrupt_disable(); + lwp = lwp_from_pid(pid); + if (!lwp) + { + goto quit; + } + + lwp_self = (struct rt_lwp *)rt_thread_self()->lwp; + if (!lwp_self) + { + goto quit; + } + if (lwp->parent != lwp_self) + { + goto quit; + } + + if (lwp->finish) + { + ret = pid; + } + else + { + if (!rt_list_isempty(&lwp->wait_list)) + { + goto quit; + } + thread = rt_thread_self(); + rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); + rt_list_insert_before(&lwp->wait_list, &(thread->tlist)); + rt_schedule(); + if (thread->error == RT_EOK) + { + ret = pid; + } + } + + if (ret != -1) + { + struct rt_lwp **lwp_node; + + *status = lwp->lwp_ret; + lwp_node = &lwp_self->first_child; + while (*lwp_node != lwp) + { + RT_ASSERT(*lwp_node != RT_NULL); + lwp_node = &(*lwp_node)->sibling; + } + (*lwp_node) = lwp->sibling; + + lwp_pid_put(pid); + rt_free(lwp); + } + +quit: + rt_hw_interrupt_enable(level); + return ret; +} + +#ifdef RT_USING_FINSH +/* copy from components/finsh/cmd.c */ +static void object_split(int len) +{ + while (len--) + { + rt_kprintf("-"); + } +} + +static void print_thread_info(struct rt_thread* thread, int maxlen) +{ + rt_uint8_t *ptr; + rt_uint8_t stat; + +#ifdef RT_USING_SMP + if (thread->oncpu != RT_CPU_DETACHED) + rt_kprintf("%-*.*s %3d %3d ", maxlen, RT_NAME_MAX, thread->name, thread->oncpu, thread->current_priority); + else + rt_kprintf("%-*.*s N/A %3d ", maxlen, RT_NAME_MAX, thread->name, thread->current_priority); +#else + rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->name, thread->current_priority); +#endif /*RT_USING_SMP*/ + + stat = (thread->stat & RT_THREAD_STAT_MASK); + if (stat == RT_THREAD_READY) rt_kprintf(" ready "); + else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) rt_kprintf(" suspend"); + else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); + else if (stat == RT_THREAD_CLOSE) rt_kprintf(" close "); + else if (stat == RT_THREAD_RUNNING) rt_kprintf(" running"); + +#if defined(ARCH_CPU_STACK_GROWS_UPWARD) + ptr = (rt_uint8_t *)thread->stack_addr + thread->stack_size; + while (*ptr == '#')ptr--; + + rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", + ((rt_uint32_t)thread->sp - (rt_uint32_t)thread->stack_addr), + thread->stack_size, + ((rt_uint32_t)ptr - (rt_uint32_t)thread->stack_addr) * 100 / thread->stack_size, + thread->remaining_tick, + thread->error); +#else + ptr = (rt_uint8_t *)thread->stack_addr; + while (*ptr == '#')ptr++; + + rt_kprintf(" 0x%08x 0x%08x %02d%% 0x%08x %03d\n", + (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)thread->sp), + thread->stack_size, + (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)ptr) * 100 + / thread->stack_size, + thread->remaining_tick, + thread->error); +#endif +} + +long list_process(void) +{ + int index; + int maxlen; + rt_ubase_t level; + struct rt_thread *thread; + struct rt_list_node *node, *list; + const char *item_title = "thread"; + + int count = 0; + struct rt_thread **threads; + + maxlen = RT_NAME_MAX; +#ifdef RT_USING_SMP + rt_kprintf("%-*.s %-*.s %-*.s cpu pri status sp stack size max used left tick error\n", 4, "PID", maxlen, "CMD", maxlen, item_title); + object_split(4);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" "); + rt_kprintf( "--- --- ------- ---------- ---------- ------ ---------- ---\n"); +#else + rt_kprintf("%-*.s %-*.s %-*.s pri status sp stack size max used left tick error\n", 4, "PID", maxlen, "CMD", maxlen, item_title); + object_split(4);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" ");object_split(maxlen);rt_kprintf(" "); + rt_kprintf( "--- ------- ---------- ---------- ------ ---------- ---\n"); +#endif /*RT_USING_SMP*/ + + count = rt_object_get_length(RT_Object_Class_Thread); + if (count > 0) + { + /* get thread pointers */ + threads = (struct rt_thread **)rt_calloc(count, sizeof(struct rt_thread *)); + if (threads) + { + index = rt_object_get_pointers(RT_Object_Class_Thread, (rt_object_t *)threads, count); + + if (index > 0) + { + for (index = 0; index <count; index++) + { + struct rt_thread th; + + thread = threads[index]; + level = rt_hw_interrupt_disable(); + if ((thread->type & ~RT_Object_Class_Static) != RT_Object_Class_Thread) + { + rt_hw_interrupt_enable(level); + continue; + } + + rt_memcpy(&th, thread, sizeof(struct rt_thread)); + rt_hw_interrupt_enable(level); + + if (th.lwp == RT_NULL) + { + rt_kprintf(" %-*.*s ", maxlen, RT_NAME_MAX, "kernel"); + print_thread_info(&th, maxlen); + } + } + } + rt_free(threads); + } + } + + for (index = 0; index < RT_LWP_MAX_NR; index++) + { + struct rt_lwp *lwp = (struct rt_lwp *)lwp_pid_ary[index].data; + + if (lwp) + { + list = &lwp->t_grp; + for (node = list->next; node != list; node = node->next) + { + thread = rt_list_entry(node, struct rt_thread, sibling); + rt_kprintf("%4d %-*.*s ", lwp_to_pid(lwp), maxlen, RT_NAME_MAX, lwp->cmd); + print_thread_info(thread, maxlen); + } + } + } + return 0; +} +MSH_CMD_EXPORT(list_process, list process); + +static void cmd_kill(int argc, char** argv) +{ + int pid; + int sig = 0; + + if (argc < 2) + { + rt_kprintf("kill pid or kill pid -s signal\n"); + return; + } + + pid = atoi(argv[1]); + if (argc >= 4) + { + if (argv[2][0] == '-' && argv[2][1] == 's') + { + sig = atoi(argv[3]); + } + } + lwp_kill(pid, sig); +} +MSH_CMD_EXPORT_ALIAS(cmd_kill, kill, send a signal to a process); + +static void cmd_killall(int argc, char** argv) +{ + int pid; + if (argc < 2) + { + rt_kprintf("killall processes_name\n"); + return; + } + + while((pid = lwp_name2pid(argv[1])) > 0) + { + lwp_kill(pid, SIGKILL); + rt_thread_mdelay(100); + } +} +MSH_CMD_EXPORT_ALIAS(cmd_killall, killall, kill processes by name); + +#endif + +int lwp_check_exit_request(void) +{ + rt_thread_t thread = rt_thread_self(); + if (!thread->lwp) + { + return 0; + } + + if (thread->exit_request == LWP_EXIT_REQUEST_TRIGGERED) + { + thread->exit_request = LWP_EXIT_REQUEST_IN_PROCESS; + return 1; + } + return 0; +} + +static int found_thread(struct rt_lwp* lwp, rt_thread_t thread) +{ + int found = 0; + rt_base_t level; + rt_list_t *list; + + level = rt_hw_interrupt_disable(); + list = lwp->t_grp.next; + while (list != &lwp->t_grp) + { + rt_thread_t iter_thread; + + iter_thread = rt_list_entry(list, struct rt_thread, sibling); + if (thread == iter_thread) + { + found = 1; + break; + } + list = list->next; + } + rt_hw_interrupt_enable(level); + return found; +} + +void lwp_request_thread_exit(rt_thread_t thread_to_exit) +{ + rt_thread_t main_thread; + rt_base_t level; + rt_list_t *list; + struct rt_lwp *lwp; + + lwp = lwp_self(); + + if ((!thread_to_exit) || (!lwp)) + { + return; + } + + level = rt_hw_interrupt_disable(); + + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (thread_to_exit == main_thread) + { + goto finish; + } + if ((struct rt_lwp *)thread_to_exit->lwp != lwp) + { + goto finish; + } + + for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) + { + rt_thread_t thread; + + thread = rt_list_entry(list, struct rt_thread, sibling); + if (thread != thread_to_exit) + { + continue; + } + if (thread->exit_request == LWP_EXIT_REQUEST_NONE) + { + thread->exit_request = LWP_EXIT_REQUEST_TRIGGERED; + } + if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + { + thread->error = -RT_EINTR; + rt_hw_dsb(); + rt_thread_wakeup(thread); + } + break; + } + + while (found_thread(lwp, thread_to_exit)) + { + rt_thread_mdelay(10); + } + +finish: + rt_hw_interrupt_enable(level); + return; +} + +void lwp_terminate(struct rt_lwp *lwp) +{ + rt_base_t level; + rt_list_t *list; + + if (!lwp) + { + /* kernel thread not support */ + return; + } + + level = rt_hw_interrupt_disable(); + for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) + { + rt_thread_t thread; + + thread = rt_list_entry(list, struct rt_thread, sibling); + if (thread->exit_request == LWP_EXIT_REQUEST_NONE) + { + thread->exit_request = LWP_EXIT_REQUEST_TRIGGERED; + } + if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + { + thread->error = RT_EINTR; + rt_hw_dsb(); + rt_thread_wakeup(thread); + } + } + rt_hw_interrupt_enable(level); +} + +void lwp_wait_subthread_exit(void) +{ + rt_base_t level; + struct rt_lwp *lwp; + rt_thread_t thread; + rt_thread_t main_thread; + + lwp = lwp_self(); + if (!lwp) + { + return; + } + + thread = rt_thread_self(); + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (thread != main_thread) + { + return; + } + + while (1) + { + int subthread_is_terminated; + + level = rt_hw_interrupt_disable(); + subthread_is_terminated = (int)(thread->sibling.prev == &lwp->t_grp); + if (!subthread_is_terminated) + { + rt_thread_t sub_thread; + rt_list_t *list; + int all_subthread_in_init = 1; + + /* check all subthread is in init state */ + for (list = thread->sibling.prev; list != &lwp->t_grp; list = list->prev) + { + + sub_thread = rt_list_entry(list, struct rt_thread, sibling); + if ((sub_thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + { + all_subthread_in_init = 0; + break; + } + } + if (all_subthread_in_init) + { + /* delete all subthread */ + while ((list = thread->sibling.prev) != &lwp->t_grp) + { + sub_thread = rt_list_entry(list, struct rt_thread, sibling); + rt_list_remove(&sub_thread->sibling); + rt_thread_delete(sub_thread); + } + subthread_is_terminated = 1; + } + } + rt_hw_interrupt_enable(level); + + if (subthread_is_terminated) + { + break; + } + rt_thread_mdelay(10); + } +} + +static int _lwp_setaffinity(pid_t pid, int cpu) +{ + struct rt_lwp *lwp; + int ret = -1; + + lwp = lwp_from_pid(pid); + if (lwp) + { +#ifdef RT_USING_SMP + rt_list_t *list; + + lwp->bind_cpu = cpu; + for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) + { + rt_thread_t thread; + + thread = rt_list_entry(list, struct rt_thread, sibling); + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)(rt_size_t)cpu); + } +#endif + ret = 0; + } + return ret; +} + +int lwp_setaffinity(pid_t pid, int cpu) +{ + rt_base_t level; + int ret; + +#ifdef RT_USING_SMP + if (cpu < 0 || cpu > RT_CPUS_NR) + { + cpu = RT_CPUS_NR; + } +#endif + level = rt_hw_interrupt_disable(); + ret = _lwp_setaffinity(pid, cpu); + rt_hw_interrupt_enable(level); + return ret; +} + +#ifdef RT_USING_SMP +static void cmd_cpu_bind(int argc, char** argv) +{ + int pid; + int cpu; + + if (argc < 3) + { + rt_kprintf("Useage: cpu_bind pid cpu\n"); + return; + } + + pid = atoi(argv[1]); + cpu = atoi(argv[2]); + lwp_setaffinity((pid_t)pid, cpu); +} +MSH_CMD_EXPORT_ALIAS(cmd_cpu_bind, cpu_bind, set a process bind to a cpu); +#endif diff --git a/components/lwp/lwp_pid.h b/components/lwp/lwp_pid.h new file mode 100644 index 0000000000..901c6f80b8 --- /dev/null +++ b/components/lwp/lwp_pid.h @@ -0,0 +1,52 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-02-23 Jesven first version. + */ + +#ifndef LWP_PID_H__ +#define LWP_PID_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +struct rt_lwp; + +struct lwp_avl_struct *lwp_get_pid_ary(void); + +struct rt_lwp* lwp_new(void); +void lwp_free(struct rt_lwp* lwp); + +int lwp_ref_inc(struct rt_lwp *lwp); +int lwp_ref_dec(struct rt_lwp *lwp); + +struct rt_lwp* lwp_from_pid(pid_t pid); +pid_t lwp_to_pid(struct rt_lwp* lwp); + +pid_t lwp_name2pid(const char* name); +char* lwp_pid2name(int32_t pid); + +int lwp_getpid(void); + +pid_t waitpid(pid_t pid, int *status, int options); +long list_process(void); + +void lwp_user_object_lock_init(struct rt_lwp *lwp); +void lwp_user_object_lock_destroy(struct rt_lwp *lwp); +void lwp_user_object_lock(struct rt_lwp *lwp); +void lwp_user_object_unlock(struct rt_lwp *lwp); +int lwp_user_object_add(struct rt_lwp *lwp, rt_object_t object); +rt_err_t lwp_user_object_delete(struct rt_lwp *lwp, rt_object_t object); +void lwp_user_object_clear(struct rt_lwp *lwp); +void lwp_user_object_dup(struct rt_lwp *dst_lwp, struct rt_lwp *src_lwp); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/lwp/lwp_pmutex.c b/components/lwp/lwp_pmutex.c new file mode 100644 index 0000000000..bac99f2283 --- /dev/null +++ b/components/lwp/lwp_pmutex.c @@ -0,0 +1,413 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021/01/02 bernard the first version + */ + +#include <rtthread.h> +#include <lwp.h> +#ifdef RT_USING_USERSPACE +#include <lwp_user_mm.h> +#endif +#include <sys/time.h> + +#define PMUTEX_NORMAL 0 /* Unable to recursion */ +#define PMUTEX_RECURSIVE 1 /* Can be recursion */ +#define PMUTEX_ERRORCHECK 2 /* This type of mutex provides error checking */ + +struct rt_pmutex +{ + union + { + rt_mutex_t kmutex; + rt_sem_t ksem; /* use sem to emulate the mutex without recursive */ + } lock; + + struct lwp_avl_struct node; + struct rt_object *custom_obj; + rt_uint8_t type; /* pmutex type */ +}; + +static struct rt_mutex _pmutex_lock; + +static int pmutex_system_init(void) +{ + rt_mutex_init(&_pmutex_lock, "pmtxLock", RT_IPC_FLAG_FIFO); + return 0; +} +INIT_PREV_EXPORT(pmutex_system_init); + +static rt_err_t pmutex_destory(void *data) +{ + rt_err_t ret = -1; + rt_base_t level = 0; + struct rt_pmutex *pmutex = (struct rt_pmutex *)data; + + if (pmutex) + { + level = rt_hw_interrupt_disable(); + /* remove pmutex from pmutext avl */ + lwp_avl_remove(&pmutex->node, (struct lwp_avl_struct **)pmutex->node.data); + rt_hw_interrupt_enable(level); + + if (pmutex->type == PMUTEX_NORMAL) + { + rt_sem_delete(pmutex->lock.ksem); + } + else + { + rt_mutex_delete(pmutex->lock.kmutex); + } + + /* release object */ + rt_free(pmutex); + ret = 0; + } + return ret; +} + +static struct rt_pmutex* pmutex_create(void *umutex, struct rt_lwp *lwp) +{ + struct rt_pmutex *pmutex = RT_NULL; + struct rt_object *obj = RT_NULL; + rt_ubase_t type; + + if (!lwp) + { + return RT_NULL; + } + + long *p = (long *)umutex; + /* umutex[0] bit[0-1] saved mutex type */ + type = *p & 3; + if (type != PMUTEX_NORMAL && type != PMUTEX_RECURSIVE && type != PMUTEX_ERRORCHECK) + { + return RT_NULL; + } + + pmutex = (struct rt_pmutex *)rt_malloc(sizeof(struct rt_pmutex)); + if (!pmutex) + { + return RT_NULL; + } + + if (type == PMUTEX_NORMAL) + { + pmutex->lock.ksem = rt_sem_create("pmutex", 1, RT_IPC_FLAG_PRIO); + if (!pmutex->lock.ksem) + { + rt_free(pmutex); + return RT_NULL; + } + } + else + { + pmutex->lock.kmutex = rt_mutex_create("pmutex", RT_IPC_FLAG_PRIO); + if (!pmutex->lock.kmutex) + { + rt_free(pmutex); + return RT_NULL; + } + } + + obj = rt_custom_object_create("pmutex", (void *)pmutex, pmutex_destory); + if (!obj) + { + if (pmutex->type == PMUTEX_NORMAL) + { + rt_sem_delete(pmutex->lock.ksem); + } + else + { + rt_mutex_delete(pmutex->lock.kmutex); + } + rt_free(pmutex); + return RT_NULL; + } + pmutex->node.avl_key = (avl_key_t)umutex; + pmutex->node.data = &lwp->address_search_head; + pmutex->custom_obj = obj; + pmutex->type = type; + + /* insert into pmutex head */ + lwp_avl_insert(&pmutex->node, &lwp->address_search_head); + return pmutex; +} + +static struct rt_pmutex* pmutex_get(void *umutex, struct rt_lwp *lwp) +{ + struct rt_pmutex *pmutex = RT_NULL; + struct lwp_avl_struct *node = RT_NULL; + + node = lwp_avl_find((avl_key_t)umutex, lwp->address_search_head); + if (!node) + { + return RT_NULL; + } + pmutex = rt_container_of(node, struct rt_pmutex, node); + return pmutex; +} + +static int _pthread_mutex_init(void *umutex) +{ + struct rt_lwp *lwp = RT_NULL; + struct rt_pmutex *pmutex = RT_NULL; + rt_err_t lock_ret = 0; + + /* umutex union is 6 x (void *) */ + if (!lwp_user_accessable(umutex, sizeof(void *) * 6)) + { + rt_set_errno(EINVAL); + return -EINVAL; + } + + lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER); + if (lock_ret != RT_EOK) + { + rt_set_errno(EAGAIN); + return -EINTR; + } + + lwp = lwp_self(); + pmutex = pmutex_get(umutex, lwp); + if (pmutex == RT_NULL) + { + /* create a pmutex according to this umutex */ + pmutex = pmutex_create(umutex, lwp); + if (pmutex == RT_NULL) + { + rt_mutex_release(&_pmutex_lock); + rt_set_errno(ENOMEM); + return -ENOMEM; + } + if (lwp_user_object_add(lwp, pmutex->custom_obj) != 0) + { + rt_custom_object_destroy(pmutex->custom_obj); + rt_set_errno(ENOMEM); + return -ENOMEM; + } + } + else + { + rt_base_t level = rt_hw_interrupt_disable(); + + if (pmutex->type == PMUTEX_NORMAL) + { + pmutex->lock.ksem->value = 1; + } + else + { + pmutex->lock.kmutex->owner = RT_NULL; + pmutex->lock.kmutex->priority = 0xFF; + pmutex->lock.kmutex->hold = 0; + pmutex->lock.kmutex->ceiling_priority = 0xFF; + } + rt_hw_interrupt_enable(level); + } + + rt_mutex_release(&_pmutex_lock); + + return 0; +} + +static int _pthread_mutex_lock_timeout(void *umutex, struct timespec *timeout) +{ + struct rt_lwp *lwp = RT_NULL; + struct rt_pmutex *pmutex = RT_NULL; + rt_err_t lock_ret = 0; + rt_int32_t time = RT_WAITING_FOREVER; + register rt_base_t temp; + + if (timeout) + { + if (!lwp_user_accessable((void *)timeout, sizeof(struct timespec))) + { + rt_set_errno(EINVAL); + return -EINVAL; + } + time = rt_timespec_to_tick(timeout); + } + + lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER); + if (lock_ret != RT_EOK) + { + rt_set_errno(EAGAIN); + return -EINTR; + } + + lwp = lwp_self(); + pmutex = pmutex_get(umutex, lwp); + if (pmutex == RT_NULL) + { + rt_mutex_release(&_pmutex_lock); + rt_set_errno(EINVAL); + return -ENOMEM; /* umutex not recored in kernel */ + } + + rt_mutex_release(&_pmutex_lock); + + switch (pmutex->type) + { + case PMUTEX_NORMAL: + lock_ret = rt_sem_take_interruptible(pmutex->lock.ksem, time); + break; + case PMUTEX_RECURSIVE: + lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time); + break; + case PMUTEX_ERRORCHECK: + temp = rt_hw_interrupt_disable(); + if (pmutex->lock.kmutex->owner == rt_thread_self()) + { + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + return -EDEADLK; + } + lock_ret = rt_mutex_take_interruptible(pmutex->lock.kmutex, time); + rt_hw_interrupt_enable(temp); + break; + default: /* unknown type */ + return -EINVAL; + } + + if (lock_ret != RT_EOK) + { + if (lock_ret == -RT_ETIMEOUT) + { + if (time == 0) /* timeout is 0, means try lock failed */ + { + rt_set_errno(EBUSY); + return -EBUSY; + } + else + { + rt_set_errno(ETIMEDOUT); + return -ETIMEDOUT; + } + } + else + { + rt_set_errno(EAGAIN); + return -EAGAIN; + } + } + return 0; +} + +static int _pthread_mutex_unlock(void *umutex) +{ + struct rt_lwp *lwp = RT_NULL; + struct rt_pmutex *pmutex = RT_NULL; + rt_err_t lock_ret = 0; + + lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER); + if (lock_ret != RT_EOK) + { + rt_set_errno(EAGAIN); + return -EINTR; + } + + lwp = lwp_self(); + pmutex = pmutex_get(umutex, lwp); + if (pmutex == RT_NULL) + { + rt_mutex_release(&_pmutex_lock); + rt_set_errno(EPERM); + return -EPERM;//unlock static mutex of unlock state + } + + rt_mutex_release(&_pmutex_lock); + + switch (pmutex->type) + { + case PMUTEX_NORMAL: + if(pmutex->lock.ksem->value >=1) + { + rt_set_errno(EPERM); + return -EPERM;//unlock dynamic mutex of unlock state + } + else + { + lock_ret = rt_sem_release(pmutex->lock.ksem); + } + break; + case PMUTEX_RECURSIVE: + case PMUTEX_ERRORCHECK: + lock_ret = rt_mutex_release(pmutex->lock.kmutex); + break; + default: /* unknown type */ + return -EINVAL; + } + + if (lock_ret != RT_EOK) + { + rt_set_errno(EPERM); + return -EAGAIN; + } + return 0; +} + +static int _pthread_mutex_destroy(void *umutex) +{ + struct rt_lwp *lwp = RT_NULL; + struct rt_pmutex *pmutex = RT_NULL; + rt_err_t lock_ret = 0; + + lock_ret = rt_mutex_take_interruptible(&_pmutex_lock, RT_WAITING_FOREVER); + if (lock_ret != RT_EOK) + { + rt_set_errno(EAGAIN); + return -EINTR; + } + + lwp = lwp_self(); + pmutex = pmutex_get(umutex, lwp); + if (pmutex == RT_NULL) + { + rt_mutex_release(&_pmutex_lock); + rt_set_errno(EINVAL); + return -EINVAL; + } + + lwp_user_object_delete(lwp, pmutex->custom_obj); + rt_mutex_release(&_pmutex_lock); + + return 0; +} + +int sys_pmutex(void *umutex, int op, void *arg) +{ + int ret = -EINVAL; + + switch (op) + { + case PMUTEX_INIT: + ret = _pthread_mutex_init(umutex); + break; + case PMUTEX_LOCK: + ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg); + if (ret == -ENOMEM) + { + /* lock not init, try init it and lock again. */ + ret = _pthread_mutex_init(umutex); + if (ret == 0) + { + ret = _pthread_mutex_lock_timeout(umutex, (struct timespec*)arg); + } + } + break; + case PMUTEX_UNLOCK: + ret = _pthread_mutex_unlock(umutex); + break; + case PMUTEX_DESTROY: + ret = _pthread_mutex_destroy(umutex); + break; + default: + rt_set_errno(EINVAL); + break; + } + return ret; +} diff --git a/components/lwp/lwp_setsid.c b/components/lwp/lwp_setsid.c new file mode 100644 index 0000000000..d72363bde5 --- /dev/null +++ b/components/lwp/lwp_setsid.c @@ -0,0 +1,27 @@ +#include <rthw.h> +#include <rtthread.h> + +#include "lwp.h" +//#include "lwp_tid.h" +#include "lwp_pid.h" + +int setsid(void) +{ + int err = -EPERM; + struct rt_thread *current_thread = rt_thread_self(); + struct rt_lwp *current_lwp = (struct rt_lwp *)rt_thread_self()->lwp; + + if (current_lwp->session == current_thread->tid) + { + return err; + } + + current_lwp->session = current_thread->tid; + current_lwp->__pgrp = current_thread->tid; + current_lwp->leader = 1; + current_lwp->tty = RT_NULL; + current_lwp->tty_old_pgrp = 0; + + err = current_lwp->session; + return err; +} diff --git a/components/lwp/lwp_shm.c b/components/lwp/lwp_shm.c new file mode 100644 index 0000000000..a28e3161a9 --- /dev/null +++ b/components/lwp/lwp_shm.c @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ +#include <rthw.h> +#include <rtthread.h> + +#ifdef RT_USING_USERSPACE +#include <lwp.h> +#include <lwp_shm.h> +#include <lwp_mm.h> + +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> + +/* the kernel structure to represent a share-memory */ +struct lwp_shm_struct +{ + size_t addr; /* point to the next item in the free list when not used */ + size_t size; + int ref; + size_t key; +}; + +static struct lwp_avl_struct *shm_tree_key; +static struct lwp_avl_struct *shm_tree_pa; + +static int shm_free_list = -1; /* the single-direct list of freed items */ +static int shm_id_used = 0; /* the latest allocated item in the array */ +static struct lwp_shm_struct _shm_ary[RT_LWP_SHM_MAX_NR]; + +/* + * Try to allocate an structure 'lwp_shm_struct' from the freed list or the + * static array. + */ +static int _shm_id_alloc(void) +{ + int id = -1; + + if (shm_free_list != -1) /* first try the freed list */ + { + id = shm_free_list; + shm_free_list = (int)_shm_ary[shm_free_list].addr; /* single-direction */ + } + else if (shm_id_used < RT_LWP_SHM_MAX_NR) /* then try the array */ + { + id = shm_id_used; + shm_id_used++; + } + return id; +} + +/* Release the item in the static array to the freed list. */ +static void shm_id_free(int id) +{ + /* link the freed itme to the single-direction list */ + _shm_ary[id].addr = (size_t)shm_free_list; + shm_free_list = id; +} + +/* Locate the shared memory through 'key' or create a new one. */ +static int _lwp_shmget(size_t key, size_t size, int create) +{ + int id = -1; + struct lwp_avl_struct *node_key = 0; + struct lwp_avl_struct *node_pa = 0; + void *page_addr = 0, *page_addr_p = RT_NULL; + uint32_t bit = 0; + + /* try to locate the item with the key in the binary tree */ + node_key = lwp_avl_find(key, shm_tree_key); + if (node_key) + { + return (struct lwp_shm_struct *)node_key->data - _shm_ary; /* the index */ + } + + /* If there doesn't exist such an item and we're allowed to create one ... */ + if (create) + { + struct lwp_shm_struct* p; + + if (!size) + { + goto err; + } + + id = _shm_id_alloc(); + if (id == -1) + { + goto err; + } + + /* allocate pages up to 2's exponent to cover the required size */ + bit = rt_page_bits(size); + page_addr = rt_pages_alloc(bit); /* virtual address */ + if (!page_addr) + { + goto err; + } + page_addr_p = (void *)((char *)page_addr + PV_OFFSET); /* physical address */ + + /* initialize the shared memory structure */ + p = _shm_ary + id; + p->addr = (size_t)page_addr_p; + p->size = (1UL << (bit + ARCH_PAGE_SHIFT)); + p->ref = 0; + p->key = key; + + /* then insert it into the balancing binary tree */ + node_key = (struct lwp_avl_struct *)rt_malloc(sizeof(struct lwp_avl_struct) * 2); + if (!node_key) + { + goto err; + } + node_key->avl_key = p->key; + node_key->data = (void *)p; + lwp_avl_insert(node_key, &shm_tree_key); + node_pa = node_key + 1; + node_pa->avl_key = p->addr; + node_pa->data = (void *)p; + lwp_avl_insert(node_pa, &shm_tree_pa); + } + return id; + +err: + if (id != -1) + { + shm_id_free(id); + } + if (page_addr) + { + rt_pages_free(page_addr, bit); + } + if (node_key) + { + rt_free(node_key); + } + return -1; +} + +/* A wrapping function, get the shared memory with interrupts disabled. */ +int lwp_shmget(size_t key, size_t size, int create) +{ + int ret = 0; + + rt_mm_lock(); + ret = _lwp_shmget(key, size, create); + rt_mm_unlock(); + return ret; +} + +/* Locate the binary tree node_key corresponding to the shared-memory id. */ +static struct lwp_avl_struct *shm_id_to_node(int id) +{ + struct lwp_avl_struct *node_key = 0; + struct lwp_shm_struct *p = RT_NULL; + + /* check id */ + if (id < 0 || id >= RT_LWP_SHM_MAX_NR) + { + return RT_NULL; + } + + p = _shm_ary + id; /* the address of the shared-memory structure */ + node_key = lwp_avl_find(p->key, shm_tree_key); + if (!node_key) + { + return RT_NULL; + } + if (node_key->data != (void *)p) + { + return RT_NULL; + } + return node_key; +} + +/* Free the shared pages, the shared-memory structure and its binary tree node_key. */ +static int _lwp_shmrm(int id) +{ + struct lwp_avl_struct *node_key = RT_NULL; + struct lwp_avl_struct *node_pa = RT_NULL; + struct lwp_shm_struct* p = RT_NULL; + uint32_t bit = 0; + + node_key = shm_id_to_node(id); + if (!node_key) + { + return -1; + } + p = (struct lwp_shm_struct *)node_key->data; + if (p->ref) + { + return 0; + } + bit = rt_page_bits(p->size); + rt_pages_free((void *)((char *)p->addr - PV_OFFSET), bit); + lwp_avl_remove(node_key, &shm_tree_key); + node_pa = node_key + 1; + lwp_avl_remove(node_pa, &shm_tree_pa); + rt_free(node_key); + shm_id_free(id); + return 0; +} + +/* A wrapping function, free the shared memory with interrupt disabled. */ +int lwp_shmrm(int id) +{ + int ret = 0; + + rt_mm_lock(); + ret = _lwp_shmrm(id); + rt_mm_unlock(); + return ret; +} + +/* Map the shared memory specified by 'id' to the specified virtual address. */ +static void *_lwp_shmat(int id, void *shm_vaddr) +{ + struct rt_lwp *lwp = RT_NULL; + struct lwp_avl_struct *node_key = RT_NULL; + struct lwp_shm_struct *p = RT_NULL; + void *va = RT_NULL; + + /* The id is used to locate the node_key in the binary tree, and then get the + * shared-memory structure linked to the node_key. We don't use the id to refer + * to the shared-memory structure directly, because the binary tree is used + * to verify the structure is really in use. + */ + node_key = shm_id_to_node(id); + if (!node_key) + { + return RT_NULL; + } + p = (struct lwp_shm_struct *)node_key->data; /* p = _shm_ary[id]; */ + + /* map the shared memory into the address space of the current thread */ + lwp = lwp_self(); + if (!lwp) + { + return RT_NULL; + } + va = lwp_map_user_type(lwp, shm_vaddr, (void *)p->addr, p->size, 1, MM_AREA_TYPE_SHM); + if (va) + { + p->ref++; + } + return va; +} + +/* A wrapping function: attach the shared memory to the specified address. */ +void *lwp_shmat(int id, void *shm_vaddr) +{ + void *ret = RT_NULL; + + if (((size_t)shm_vaddr & ARCH_PAGE_MASK) != 0) + { + return RT_NULL; + } + rt_mm_lock(); + ret = _lwp_shmat(id, shm_vaddr); + rt_mm_unlock(); + return ret; +} + +static struct lwp_shm_struct *_lwp_shm_struct_get(struct rt_lwp *lwp, void *shm_vaddr) +{ + void *pa = RT_NULL; + struct lwp_avl_struct *node_pa = RT_NULL; + + if (!lwp) + { + return RT_NULL; + } + pa = rt_hw_mmu_v2p(&lwp->mmu_info, shm_vaddr); /* physical memory */ + + node_pa = lwp_avl_find((size_t)pa, shm_tree_pa); + if (!node_pa) + { + return RT_NULL; + } + return (struct lwp_shm_struct *)node_pa->data; +} + +static int _lwp_shm_ref_inc(struct rt_lwp *lwp, void *shm_vaddr) +{ + struct lwp_shm_struct* p = _lwp_shm_struct_get(lwp, shm_vaddr); + + if (p) + { + p->ref++; + return p->ref; + } + return -1; +} + +int lwp_shm_ref_inc(struct rt_lwp *lwp, void *shm_vaddr) +{ + int ret = 0; + + rt_mm_lock(); + ret = _lwp_shm_ref_inc(lwp, shm_vaddr); + rt_mm_unlock(); + + return ret; +} + +static int _lwp_shm_ref_dec(struct rt_lwp *lwp, void *shm_vaddr) +{ + struct lwp_shm_struct* p = _lwp_shm_struct_get(lwp, shm_vaddr); + + if (p && (p->ref > 0)) + { + p->ref--; + return p->ref; + } + return -1; +} + +int lwp_shm_ref_dec(struct rt_lwp *lwp, void *shm_vaddr) +{ + int ret = 0; + + rt_mm_lock(); + ret = _lwp_shm_ref_dec(lwp, shm_vaddr); + rt_mm_unlock(); + + return ret; +} + +/* Unmap the shared memory from the address space of the current thread. */ +int _lwp_shmdt(void *shm_vaddr) +{ + struct rt_lwp *lwp = RT_NULL; + int ret = 0; + + lwp = lwp_self(); + if (!lwp) + { + return -1; + } + ret = _lwp_shm_ref_dec(lwp, shm_vaddr); + if (ret >= 0) + { + lwp_unmap_user_phy(lwp, shm_vaddr); + return 0; + } + return -1; +} +/* A wrapping function: detach the mapped shared memory. */ +int lwp_shmdt(void *shm_vaddr) +{ + int ret = 0; + + rt_mm_lock(); + ret = _lwp_shmdt(shm_vaddr); + rt_mm_unlock(); + + return ret; +} + +/* Get the virtual address of a shared memory in kernel. */ +void *_lwp_shminfo(int id) +{ + struct lwp_avl_struct *node_key = RT_NULL; + struct lwp_shm_struct *p = RT_NULL; + + /* the share memory is in use only if it exsits in the binary tree */ + node_key = shm_id_to_node(id); + if (!node_key) + { + return RT_NULL; + } + p = (struct lwp_shm_struct *)node_key->data; /* p = _shm_ary[id]; */ + + return (void *)((char *)p->addr - PV_OFFSET); /* get the virtual address */ +} + +/* A wrapping function: get the virtual address of a shared memory. */ +void *lwp_shminfo(int id) +{ + void *vaddr = RT_NULL; + + rt_mm_lock(); + vaddr = _lwp_shminfo(id); + rt_mm_unlock(); + return vaddr; +} + +#ifdef RT_USING_FINSH +static int _shm_info(struct lwp_avl_struct* node_key, void *data) +{ + int id = 0; + struct lwp_shm_struct* p = (struct lwp_shm_struct *)node_key->data; + + id = p - _shm_ary; + rt_kprintf("0x%08x 0x%08x 0x%08x %8d\n", p->key, p->addr, p->size, id); + return 0; +} + +void list_shm(void) +{ + rt_kprintf(" key paddr size id\n"); + rt_kprintf("---------- ---------- ---------- --------\n"); + rt_mm_lock(); + lwp_avl_traversal(shm_tree_key, _shm_info, NULL); + rt_mm_unlock(); +} +MSH_CMD_EXPORT(list_shm, show share memory info); +#endif + +#endif diff --git a/components/lwp/lwp_shm.h b/components/lwp/lwp_shm.h new file mode 100644 index 0000000000..db6b165614 --- /dev/null +++ b/components/lwp/lwp_shm.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-12 Jesven first version + */ +#ifndef __LWP_SHM_H__ +#define __LWP_SHM_H__ + +#include <lwp_avl.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int lwp_shmget(size_t key, size_t size, int create); +int lwp_shmrm(int id); +void* lwp_shmat(int id, void* shm_vaddr); +int lwp_shmdt(void* shm_vaddr); +void *lwp_shminfo(int id); +int lwp_shm_ref_inc(struct rt_lwp *lwp, void *shm_vaddr); +int lwp_shm_ref_dec(struct rt_lwp *lwp, void *shm_vaddr); + +#ifdef __cplusplus +} +#endif + +#endif /*__LWP_SHM_H__*/ diff --git a/components/lwp/lwp_signal.c b/components/lwp/lwp_signal.c new file mode 100644 index 0000000000..ccebec7d80 --- /dev/null +++ b/components/lwp/lwp_signal.c @@ -0,0 +1,606 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-11-12 Jesven first version + */ + +#include <rthw.h> +#include <rtthread.h> + +#include "lwp.h" +#include "lwp_arch.h" +#include "sys/signal.h" + +rt_inline void lwp_sigaddset(lwp_sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + + if (_LWP_NSIG_WORDS == 1) + { + set->sig[0] |= 1UL << sig; + } + else + { + set->sig[sig / _LWP_NSIG_BPW] |= 1UL << (sig % _LWP_NSIG_BPW); + } +} + +rt_inline void lwp_sigdelset(lwp_sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + + if (_LWP_NSIG_WORDS == 1) + { + set->sig[0] &= ~(1UL << sig); + } + else + { + set->sig[sig / _LWP_NSIG_BPW] &= ~(1UL << (sig % _LWP_NSIG_BPW)); + } +} + +rt_inline int lwp_sigisemptyset(lwp_sigset_t *set) +{ + switch (_LWP_NSIG_WORDS) + { + case 4: + return (set->sig[3] | set->sig[2] | + set->sig[1] | set->sig[0]) == 0; + case 2: + return (set->sig[1] | set->sig[0]) == 0; + case 1: + return set->sig[0] == 0; + default: + return 1; + } +} + +rt_inline int lwp_sigismember(lwp_sigset_t *set, int _sig) +{ + unsigned long sig = _sig - 1; + + if (_LWP_NSIG_WORDS == 1) + { + return 1 & (set->sig[0] >> sig); + } + else + { + return 1 & (set->sig[sig / _LWP_NSIG_BPW] >> (sig % _LWP_NSIG_BPW)); + } +} + +rt_inline int next_signal(lwp_sigset_t *pending, lwp_sigset_t *mask) +{ + unsigned long i, *s, *m, x; + int sig = 0; + + s = pending->sig; + m = mask->sig; + + x = *s & ~*m; + if (x) + { + sig = rt_hw_ffz(~x) + 1; + return sig; + } + + switch (_LWP_NSIG_WORDS) + { + default: + for (i = 1; i < _LWP_NSIG_WORDS; ++i) + { + x = *++s &~ *++m; + if (!x) + continue; + sig = rt_hw_ffz(~x) + i*_LWP_NSIG_BPW + 1; + break; + } + break; + + case 2: + x = s[1] &~ m[1]; + if (!x) + break; + sig = rt_hw_ffz(~x) + _LWP_NSIG_BPW + 1; + break; + + case 1: + /* Nothing to do */ + break; + } + + return sig; +} + +int lwp_suspend_sigcheck(rt_thread_t thread, int suspend_flag) +{ + struct rt_lwp *lwp = (struct rt_lwp*)thread->lwp; + int ret = 0; + + switch (suspend_flag) + { + case RT_INTERRUPTIBLE: + if (!lwp_sigisemptyset(&thread->signal)) + { + break; + } + if (thread->lwp && !lwp_sigisemptyset(&lwp->signal)) + { + break; + } + ret = 1; + break; + case RT_KILLABLE: + if (lwp_sigismember(&thread->signal, SIGKILL)) + { + break; + } + if (thread->lwp && lwp_sigismember(&lwp->signal, SIGKILL)) + { + break; + } + ret = 1; + break; + case RT_UNINTERRUPTIBLE: + ret = 1; + break; + default: + RT_ASSERT(0); + break; + } + return ret; +} + +int lwp_signal_check(void) +{ + rt_base_t level; + struct rt_thread *thread; + struct rt_lwp *lwp; + uint32_t have_signal = 0; + + level = rt_hw_interrupt_disable(); + + thread = rt_thread_self(); + + if (thread->signal_in_process) + { + goto out; + } + + lwp = (struct rt_lwp*)thread->lwp; + + if (lwp->signal_in_process) + { + goto out; + } + + have_signal = !lwp_sigisemptyset(&thread->signal); + if (have_signal) + { + thread->signal_in_process = 1; + goto out; + } + have_signal = !lwp_sigisemptyset(&lwp->signal); + if (have_signal) + { + lwp->signal_in_process = 1; + } +out: + rt_hw_interrupt_enable(level); + return have_signal; +} + +int lwp_signal_backup(void *user_sp, void *user_pc, void* user_flag) +{ + rt_base_t level; + struct rt_thread *thread; + struct rt_lwp *lwp; + int signal; + + level = rt_hw_interrupt_disable(); + thread = rt_thread_self(); + if (thread->signal_in_process) + { + thread->user_ctx.sp = user_sp; + thread->user_ctx.pc = user_pc; + thread->user_ctx.flag = user_flag; + + signal = next_signal(&thread->signal, &thread->signal_mask); + RT_ASSERT(signal != 0); + lwp_sigaddset(&thread->signal_mask, signal); + thread->signal_mask_bak = signal; + lwp_sigdelset(&thread->signal, signal); + } + else + { + lwp = (struct rt_lwp*)thread->lwp; + lwp->user_ctx.sp = user_sp; + lwp->user_ctx.pc = user_pc; + lwp->user_ctx.flag = user_flag; + + signal = next_signal(&lwp->signal, &lwp->signal_mask); + RT_ASSERT(signal != 0); + lwp_sigaddset(&lwp->signal_mask, signal); + lwp->signal_mask_bak = signal; + lwp_sigdelset(&lwp->signal, signal); + } + rt_hw_interrupt_enable(level); + return signal; +} + +struct rt_user_context *lwp_signal_restore(void) +{ + rt_base_t level; + struct rt_thread *thread; + struct rt_lwp *lwp; + struct rt_user_context *ctx; + + level = rt_hw_interrupt_disable(); + thread = rt_thread_self(); + if (thread->signal_in_process) + { + ctx = &thread->user_ctx; + thread->signal_in_process = 0; + + lwp_sigdelset(&thread->signal_mask, thread->signal_mask_bak); + thread->signal_mask_bak = 0; + } + else + { + lwp = (struct rt_lwp*)thread->lwp; + ctx = &lwp->user_ctx; + RT_ASSERT(lwp->signal_in_process != 0); + lwp->signal_in_process = 0; + + lwp_sigdelset(&lwp->signal_mask, lwp->signal_mask_bak); + lwp->signal_mask_bak = 0; + } + rt_hw_interrupt_enable(level); + return ctx; +} + +rt_inline int _lwp_check_ignore(int sig) +{ + if (sig == SIGCHLD || sig == SIGCONT) + { + return 1; + } + return 0; +} + +void sys_exit(int value); +lwp_sighandler_t lwp_sighandler_get(int sig) +{ + lwp_sighandler_t func = RT_NULL; + struct rt_lwp *lwp; + rt_thread_t thread; + rt_base_t level; + + if (sig == 0 || sig > _LWP_NSIG) + { + return func; + } + level = rt_hw_interrupt_disable(); + thread = rt_thread_self(); +#ifndef ARCH_MM_MMU + if (thread->signal_in_process) + { + func = thread->signal_handler[sig - 1]; + goto out; + } +#endif + lwp = (struct rt_lwp*)thread->lwp; + + func = lwp->signal_handler[sig - 1]; + if (!func) + { + if (_lwp_check_ignore(sig)) + { + goto out; + } + if (lwp->signal_in_process) + { + lwp_terminate(lwp); + } + sys_exit(0); + } +out: + rt_hw_interrupt_enable(level); + + if (func == (lwp_sighandler_t)SIG_IGN) + { + func = RT_NULL; + } + return func; +} + +void lwp_sighandler_set(int sig, lwp_sighandler_t func) +{ + rt_base_t level; + + if (sig == 0 || sig > _LWP_NSIG) + return; + if (sig == SIGKILL || sig == SIGSTOP) + return; + level = rt_hw_interrupt_disable(); + ((struct rt_lwp*)rt_thread_self()->lwp)->signal_handler[sig - 1] = func; + rt_hw_interrupt_enable(level); +} + +#ifndef ARCH_MM_MMU +void lwp_thread_sighandler_set(int sig, lwp_sighandler_t func) +{ + rt_base_t level; + + if (sig == 0 || sig > _LWP_NSIG) + return; + level = rt_hw_interrupt_disable(); + rt_thread_self()->signal_handler[sig - 1] = func; + rt_hw_interrupt_enable(level); +} +#endif + +int lwp_sigaction(int sig, const struct lwp_sigaction *act, + struct lwp_sigaction *oact, size_t sigsetsize) +{ + rt_base_t level; + struct rt_lwp *lwp; + int ret = -RT_EINVAL; + lwp_sigset_t newset; + + level = rt_hw_interrupt_disable(); + lwp = (struct rt_lwp*)rt_thread_self()->lwp; + if (!lwp) + { + goto out; + } + if (sigsetsize != sizeof(lwp_sigset_t)) + { + goto out; + } + if (!act && !oact) + { + goto out; + } + if (oact) + { + oact->sa_flags = lwp->sa_flags; + oact->sa_mask = lwp->signal_mask; + oact->sa_restorer = RT_NULL; + oact->__sa_handler._sa_handler = lwp->signal_handler[sig - 1]; + } + if (act) + { + lwp->sa_flags = act->sa_flags; + newset = act->sa_mask; + lwp_sigdelset(&newset, SIGKILL); + lwp_sigdelset(&newset, SIGSTOP); + lwp->signal_mask = newset; + lwp_sighandler_set(sig, act->__sa_handler._sa_handler); + } + ret = 0; +out: + rt_hw_interrupt_enable(level); + return ret; +} + +rt_inline void sigorsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1) +{ + switch (_LWP_NSIG_WORDS) + { + case 4: + dset->sig[3] = set0->sig[3] | set1->sig[3]; + dset->sig[2] = set0->sig[2] | set1->sig[2]; + case 2: + dset->sig[1] = set0->sig[1] | set1->sig[1]; + case 1: + dset->sig[0] = set0->sig[0] | set1->sig[0]; + default: + return; + } +} + +rt_inline void sigandsets(lwp_sigset_t *dset, const lwp_sigset_t *set0, const lwp_sigset_t *set1) +{ + switch (_LWP_NSIG_WORDS) + { + case 4: + dset->sig[3] = set0->sig[3] & set1->sig[3]; + dset->sig[2] = set0->sig[2] & set1->sig[2]; + case 2: + dset->sig[1] = set0->sig[1] & set1->sig[1]; + case 1: + dset->sig[0] = set0->sig[0] & set1->sig[0]; + default: + return; + } +} + +int lwp_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset) +{ + int ret = -1; + rt_base_t level; + struct rt_lwp *lwp; + struct rt_thread *thread; + lwp_sigset_t newset; + + level = rt_hw_interrupt_disable(); + + thread = rt_thread_self(); + lwp = (struct rt_lwp*)thread->lwp; + if (!lwp) + { + goto out; + } + if (oset) + { + rt_memcpy(oset, &lwp->signal_mask, sizeof(lwp_sigset_t)); + } + + if (sigset) + { + switch (how) + { + case SIG_BLOCK: + sigorsets(&newset, &lwp->signal_mask, sigset); + break; + case SIG_UNBLOCK: + sigandsets(&newset, &lwp->signal_mask, sigset); + break; + case SIG_SETMASK: + newset = *sigset; + break; + default: + ret = RT_EINVAL; + goto out; + } + + lwp_sigdelset(&newset, SIGKILL); + lwp_sigdelset(&newset, SIGSTOP); + + lwp->signal_mask = newset; + } + ret = 0; +out: + rt_hw_interrupt_enable(level); + return ret; +} + +int lwp_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset) +{ + rt_base_t level; + struct rt_thread *thread; + lwp_sigset_t newset; + + level = rt_hw_interrupt_disable(); + thread = rt_thread_self(); + + if (oset) + { + rt_memcpy(oset, &thread->signal_mask, sizeof(lwp_sigset_t)); + } + + if (sigset) + { + switch (how) + { + case SIG_BLOCK: + sigorsets(&newset, &thread->signal_mask, sigset); + break; + case SIG_UNBLOCK: + sigandsets(&newset, &thread->signal_mask, sigset); + break; + case SIG_SETMASK: + newset = *sigset; + break; + default: + goto out; + } + + lwp_sigdelset(&newset, SIGKILL); + lwp_sigdelset(&newset, SIGSTOP); + + thread->signal_mask = newset; + } +out: + rt_hw_interrupt_enable(level); + return 0; +} + +static void _do_signal_wakeup(rt_thread_t thread, int sig) +{ + if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + { + int need_schedule = 1; + + if ((thread->stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK) + { + rt_thread_wakeup(thread); + } + else if ((sig == SIGKILL) && ((thread->stat & RT_SIGNAL_KILL_WAKEUP_MASK) != RT_SIGNAL_KILL_WAKEUP_MASK)) + { + rt_thread_wakeup(thread); + } + else + { + need_schedule = 0; + } + + /* do schedule */ + if (need_schedule) + { + rt_schedule(); + } + } +} + +int lwp_kill(pid_t pid, int sig) +{ + rt_base_t level; + struct rt_lwp *lwp; + int ret = -1; + rt_thread_t thread; + + if (sig < 0 || sig >= _LWP_NSIG) + { + rt_set_errno(EINVAL); + return ret; + } + level = rt_hw_interrupt_disable(); + lwp = lwp_from_pid(pid); + if (!lwp || lwp->finish) + { + rt_set_errno(ESRCH); + goto out; + } + if (sig) + { + /* check main thread */ + thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (!lwp_sigismember(&lwp->signal_mask, sig)) /* if signal masked */ + { + lwp_sigaddset(&lwp->signal, sig); + _do_signal_wakeup(thread, sig); + } + } + ret = 0; +out: + rt_hw_interrupt_enable(level); + return ret; +} + +int lwp_thread_kill(rt_thread_t thread, int sig) +{ + rt_base_t level; + int ret = -RT_EINVAL; + + if (!thread) + { + rt_set_errno(ESRCH); + return ret; + } + if (sig < 0 || sig >= _LWP_NSIG) + { + rt_set_errno(EINVAL); + return ret; + } + level = rt_hw_interrupt_disable(); + if (!thread->lwp) + { + rt_set_errno(EPERM); + goto out; + } + if (!lwp_sigismember(&thread->signal_mask, sig)) /* if signal masked */ + { + lwp_sigaddset(&thread->signal, sig); + _do_signal_wakeup(thread, sig); + } + ret = 0; +out: + rt_hw_interrupt_enable(level); + return ret; +} diff --git a/components/lwp/lwp_signal.h b/components/lwp/lwp_signal.h new file mode 100644 index 0000000000..6d393f0098 --- /dev/null +++ b/components/lwp/lwp_signal.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2020-02-23 Jesven first version. + */ + +#ifndef LWP_SIGNAL_H__ +#define LWP_SIGNAL_H__ + +#include <rtthread.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int lwp_signal_check(void); +int lwp_signal_backup(void *user_sp, void *user_pc, void* user_flag); +struct rt_user_context *lwp_signal_restore(void); +lwp_sighandler_t lwp_sighandler_get(int sig); +void lwp_sighandler_set(int sig, lwp_sighandler_t func); +#ifndef ARCH_MM_MMU +void lwp_thread_sighandler_set(int sig, lwp_sighandler_t func); +#endif +int lwp_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset); +int lwp_sigaction(int sig, const struct lwp_sigaction *act, struct lwp_sigaction * oact, size_t sigsetsize); +int lwp_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset); + +int lwp_kill(pid_t pid, int sig); +int lwp_thread_kill(rt_thread_t thread, int sig); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c index f2aec9303f..c241e1ef83 100644 --- a/components/lwp/lwp_syscall.c +++ b/components/lwp/lwp_syscall.c @@ -6,112 +6,997 @@ * Change Logs: * Date Author Notes * 2018-06-10 Bernard first version + * 2021-02-03 lizhirui add limit condition for network syscall and add 64-bit arch support + * 2021-02-06 lizhirui fix some bugs + * 2021-02-12 lizhirui add 64-bit support for sys_brk + * 2021-02-20 lizhirui fix some warnings */ - +#define _GNU_SOURCE /* RT-Thread System call */ +#include <rthw.h> +#include <board.h> + #include <lwp.h> -#include <lwp_mem.h> -#include <lwp_syscall.h> +#ifdef RT_USING_USERSPACE +#include <lwp_user_mm.h> +#include <lwp_arch.h> +#endif + +#include <fcntl.h> +#ifdef RT_USING_DFS #include <poll.h> #include <sys/select.h> +#include <dfs_file.h> +#include <unistd.h> +#include <stdio.h> /* rename() */ +#include <sys/stat.h> +#include <sys/statfs.h> /* statfs() */ +#endif + +#include "syscall_data.h" #if (defined(RT_USING_SAL) && defined(SAL_USING_POSIX)) #include <sys/socket.h> -#define SYSCALL_NET(f) ((void*)(f)) + +#define SYSCALL_NET(f) f #else -#define SYSCALL_NET(f) ((void*)sys_notimpl) +#define SYSCALL_NET(f) SYSCALL_SIGN(sys_notimpl) #endif -#define DBG_TAG "LWP_CALL" -#define DBG_LVL DBG_WARNING +#if defined(RT_USING_DFS) && defined(RT_USING_USERSPACE) +#define SYSCALL_USPACE(f) f +#else +#define SYSCALL_USPACE(f) SYSCALL_SIGN(sys_notimpl) +#endif + +#define DBG_TAG "SYSCALL" +#define DBG_LVL DBG_INFO #include <rtdbg.h> -static void __exit_files(rt_thread_t tid) +#ifdef RT_USING_SAL +#include <netdev_ipaddr.h> +#include <netdev.h> + +#include <sal_netdb.h> +#include <sal_socket.h> +#include <sys/socket.h> +#endif /* RT_USING_SAL */ + +#include <tty.h> +#include "lwp_ipc_internal.h" +#include <sched.h> +#ifndef GRND_NONBLOCK +#define GRND_NONBLOCK 0x0001 +#endif /* GRND_NONBLOCK */ + +#ifndef GRND_RANDOM +#define GRND_RANDOM 0x0002 +#endif /*GRND_RANDOM */ + +#define SET_ERRNO(no) rt_set_errno(-(no)) +#define GET_ERRNO() ((rt_get_errno() > 0) ? (-rt_get_errno()) : rt_get_errno()) +struct musl_sockaddr { - struct rt_lwp *lwp; + uint16_t sa_family; + char sa_data[14]; +}; - lwp = (struct rt_lwp *)tid->lwp; - while (lwp->fdt.maxfd > 0) +int sys_dup(int oldfd); +int sys_dup2(int oldfd, int new); +void lwp_cleanup(struct rt_thread *tid); + +#ifdef ARCH_MM_MMU +#define ALLOC_KERNEL_STACK_SIZE 5120 + +int sys_futex(int *uaddr, int op, int val, void *timeout, void *uaddr2, int val3); +int sys_pmutex(void *umutex, int op, void *arg); +int sys_cacheflush(void *addr, int len, int cache); +static void *kmem_get(size_t size) +{ + return rt_malloc(size); +} + +static void kmem_put(void *kptr) +{ + rt_free(kptr); +} +#else +#define ALLOC_KERNEL_STACK_SIZE 1536 +#define ALLOC_KERNEL_STACK_SIZE_MIN 1024 +#define ALLOC_KERNEL_STACK_SIZE_MAX 4096 + +extern void set_user_context(void *stack); +#endif /* ARCH_MM_MMU */ + +/* The same socket option is defined differently in the user interfaces and the + * implementation. The options should be converted in the kernel. */ + +/* socket levels */ +#define INTF_SOL_SOCKET 1 +#define IMPL_SOL_SOCKET 0xFFF + +#define INTF_IPPROTO_IP 0 +#define IMPL_IPPROTO_IP 0 + +#define INTF_IPPROTO_TCP 6 +#define IMPL_IPPROTO_TCP 6 + +#define INTF_IPPROTO_IPV6 41 +#define IMPL_IPPROTO_IPV6 41 + +/* SOL_SOCKET option names */ +#define INTF_SO_BROADCAST 6 +#define INTF_SO_KEEPALIVE 9 +#define INTF_SO_REUSEADDR 2 +#define INTF_SO_TYPE 3 +#define INTF_SO_ERROR 4 +#define INTF_SO_SNDTIMEO 21 +#define INTF_SO_RCVTIMEO 20 +#define INTF_SO_RCVBUF 8 +#define INTF_SO_LINGER 13 +#define INTF_SO_NO_CHECK 11 +#define INTF_SO_ACCEPTCONN 30 +#define INTF_SO_DONTROUTE 5 +#define INTF_SO_OOBINLINE 10 +#define INTF_SO_REUSEPORT 15 +#define INTF_SO_SNDBUF 7 +#define INTF_SO_SNDLOWAT 19 +#define INTF_SO_RCVLOWAT 18 + +#define IMPL_SO_BROADCAST 0x0020 +#define IMPL_SO_KEEPALIVE 0x0008 +#define IMPL_SO_REUSEADDR 0x0004 +#define IMPL_SO_TYPE 0x1008 +#define IMPL_SO_ERROR 0x1007 +#define IMPL_SO_SNDTIMEO 0x1005 +#define IMPL_SO_RCVTIMEO 0x1006 +#define IMPL_SO_RCVBUF 0x1002 +#define IMPL_SO_LINGER 0x0080 +#define IMPL_SO_NO_CHECK 0x100a +#define IMPL_SO_ACCEPTCONN 0x0002 +#define IMPL_SO_DONTROUTE 0x0010 +#define IMPL_SO_OOBINLINE 0x0100 +#define IMPL_SO_REUSEPORT 0x0200 +#define IMPL_SO_SNDBUF 0x1001 +#define IMPL_SO_SNDLOWAT 0x1003 +#define IMPL_SO_RCVLOWAT 0x1004 + +/* IPPROTO_IP option names */ +#define INTF_IP_TTL 2 +#define INTF_IP_TOS 1 +#define INTF_IP_MULTICAST_TTL 33 +#define INTF_IP_MULTICAST_IF 32 +#define INTF_IP_MULTICAST_LOOP 34 +#define INTF_IP_ADD_MEMBERSHIP 35 +#define INTF_IP_DROP_MEMBERSHIP 36 + +#define IMPL_IP_TTL 2 +#define IMPL_IP_TOS 1 +#define IMPL_IP_MULTICAST_TTL 5 +#define IMPL_IP_MULTICAST_IF 6 +#define IMPL_IP_MULTICAST_LOOP 7 +#define IMPL_IP_ADD_MEMBERSHIP 3 +#define IMPL_IP_DROP_MEMBERSHIP 4 + +/* IPPROTO_TCP option names */ +#define INTF_TCP_NODELAY 1 +#define INTF_TCP_KEEPALIVE 9 +#define INTF_TCP_KEEPIDLE 4 +#define INTF_TCP_KEEPINTVL 5 +#define INTF_TCP_KEEPCNT 6 + +#define IMPL_TCP_NODELAY 0x01 +#define IMPL_TCP_KEEPALIVE 0x02 +#define IMPL_TCP_KEEPIDLE 0x03 +#define IMPL_TCP_KEEPINTVL 0x04 +#define IMPL_TCP_KEEPCNT 0x05 + +/* IPPROTO_IPV6 option names */ +#define INTF_IPV6_V6ONLY 26 +#define IMPL_IPV6_V6ONLY 27 + +#ifdef RT_USING_SAL +static void convert_sockopt(int *level, int *optname) +{ + if (*level == INTF_SOL_SOCKET) { - lwp->fdt.maxfd --; - close(lwp->fdt.maxfd); + *level = IMPL_SOL_SOCKET; + + switch (*optname) + { + case INTF_SO_REUSEADDR: + *optname = IMPL_SO_REUSEADDR; + break; + case INTF_SO_KEEPALIVE: + *optname = IMPL_SO_KEEPALIVE; + break; + case INTF_SO_BROADCAST: + *optname = IMPL_SO_BROADCAST; + break; + case INTF_SO_ACCEPTCONN: + *optname = IMPL_SO_ACCEPTCONN; + break; + case INTF_SO_DONTROUTE: + *optname = IMPL_SO_DONTROUTE; + break; + case INTF_SO_LINGER: + *optname = IMPL_SO_LINGER; + break; + case INTF_SO_OOBINLINE: + *optname = IMPL_SO_OOBINLINE; + break; + case INTF_SO_REUSEPORT: + *optname = IMPL_SO_REUSEPORT; + break; + case INTF_SO_SNDBUF: + *optname = IMPL_SO_SNDBUF; + break; + case INTF_SO_RCVBUF: + *optname = IMPL_SO_RCVBUF; + break; + case INTF_SO_SNDLOWAT: + *optname = IMPL_SO_SNDLOWAT; + break; + case INTF_SO_RCVLOWAT: + *optname = IMPL_SO_RCVLOWAT; + break; + case INTF_SO_SNDTIMEO: + *optname = IMPL_SO_SNDTIMEO; + break; + case INTF_SO_RCVTIMEO: + *optname = IMPL_SO_RCVTIMEO; + break; + case INTF_SO_ERROR: + *optname = IMPL_SO_ERROR; + break; + case INTF_SO_TYPE: + *optname = IMPL_SO_TYPE; + break; + case INTF_SO_NO_CHECK: + *optname = IMPL_SO_NO_CHECK; + break; + + /* + * SO_DONTLINGER (*level = ((int)(~SO_LINGER))), + * SO_USELOOPBACK (*level = 0x0040) and + * SO_CONTIMEO (*level = 0x1009) are not supported for now. + */ + default: + *optname = 0; + break; + } + return; } + + if (*level == INTF_IPPROTO_IP) + { + *level = IMPL_IPPROTO_IP; + + switch (*optname) + { + case INTF_IP_TTL: + *optname = IMPL_IP_TTL; + break; + case INTF_IP_TOS: + *optname = IMPL_IP_TOS; + break; + case INTF_IP_MULTICAST_TTL: + *optname = IMPL_IP_MULTICAST_TTL; + break; + case INTF_IP_MULTICAST_IF: + *optname = IMPL_IP_MULTICAST_IF; + break; + case INTF_IP_MULTICAST_LOOP: + *optname = IMPL_IP_MULTICAST_LOOP; + break; + case INTF_IP_ADD_MEMBERSHIP: + *optname = IMPL_IP_ADD_MEMBERSHIP; + break; + case INTF_IP_DROP_MEMBERSHIP: + *optname = IMPL_IP_DROP_MEMBERSHIP; + break; + default: + break; + } + } + + if (*level == INTF_IPPROTO_TCP) + { + *level = IMPL_IPPROTO_TCP; + + switch (*optname) + { + case INTF_TCP_NODELAY: + *optname = IMPL_TCP_NODELAY; + break; + case INTF_TCP_KEEPALIVE: + *optname = IMPL_TCP_KEEPALIVE; + break; + case INTF_TCP_KEEPIDLE: + *optname = IMPL_TCP_KEEPIDLE; + break; + case INTF_TCP_KEEPINTVL: + *optname = IMPL_TCP_KEEPINTVL; + break; + case INTF_TCP_KEEPCNT: + *optname = IMPL_TCP_KEEPCNT; + break; + default: + break; + } + return; + } + + if (*level == INTF_IPPROTO_IPV6) + { + *level = IMPL_IPPROTO_IPV6; + + switch (*optname) + { + case INTF_IPV6_V6ONLY: + *optname = IMPL_IPV6_V6ONLY; + break; + default: + break; + } + return; + } + +} +#endif /* RT_USING_SAL */ + +#if defined(RT_USING_LWIP) || defined(SAL_USING_UNET) + static void sockaddr_tolwip(const struct musl_sockaddr *std, struct sockaddr *lwip) + { + if (std && lwip) + { + lwip->sa_len = sizeof(*lwip); + lwip->sa_family = (sa_family_t) std->sa_family; + memcpy(lwip->sa_data, std->sa_data, sizeof(lwip->sa_data)); + } + } + + static void sockaddr_tomusl(const struct sockaddr *lwip, struct musl_sockaddr *std) + { + if (std && lwip) + { + std->sa_family = (uint16_t) lwip->sa_family; + memcpy(std->sa_data, lwip->sa_data, sizeof(std->sa_data)); + } + } +#endif + +static void _crt_thread_entry(void *parameter) +{ + rt_thread_t tid; + rt_size_t user_stack; + + tid = rt_thread_self(); + + user_stack = (rt_size_t)tid->user_stack + tid->user_stack_size; + user_stack &= ~7; //align 8 + +#ifdef ARCH_MM_MMU + arch_crt_start_umode(parameter, tid->user_entry, (void *)user_stack, tid->stack_addr + tid->stack_size); +#else + set_user_context((void*)user_stack); + arch_start_umode(parameter, tid->user_entry, ((struct rt_lwp *)tid->lwp)->data_entry, (void*)user_stack); +#endif /* ARCH_MM_MMU */ } /* thread/process */ void sys_exit(int value) { - rt_thread_t tid; + rt_base_t level; + rt_thread_t tid, main_thread; + struct rt_lwp *lwp; + + LOG_D("thread/process exit."); - /* TODO: handle the return_value */ - dbg_log(DBG_LOG, "enter sys_exit\n"); tid = rt_thread_self(); - __exit_files(tid); - rt_thread_delete(tid); + lwp = (struct rt_lwp *)tid->lwp; + + level = rt_hw_interrupt_disable(); +#ifdef ARCH_MM_MMU + if (tid->clear_child_tid) + { + int t = 0; + int *clear_child_tid = tid->clear_child_tid; + tid->clear_child_tid = RT_NULL; + lwp_put_to_user(clear_child_tid, &t, sizeof t); + sys_futex(clear_child_tid, FUTEX_WAKE, 1, RT_NULL, RT_NULL, 0); + } + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (main_thread == tid) + { + lwp_terminate(lwp); + lwp_wait_subthread_exit(); + lwp->lwp_ret = value; + } +#else + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + if (main_thread == tid) + { + rt_thread_t sub_thread; + rt_list_t *list; + + lwp_terminate(lwp); + + /* delete all subthread */ + while ((list = tid->sibling.prev) != &lwp->t_grp) + { + sub_thread = rt_list_entry(list, struct rt_thread, sibling); + rt_list_remove(&sub_thread->sibling); + rt_thread_delete(sub_thread); + } + lwp->lwp_ret = value; + } +#endif /* ARCH_MM_MMU */ + + rt_thread_delete(tid); rt_schedule(); + rt_hw_interrupt_enable(level); return; } +/* exit group */ +void sys_exit_group(int status) +{ + return; +} + /* syscall: "read" ret: "ssize_t" args: "int" "void *" "size_t" */ ssize_t sys_read(int fd, void *buf, size_t nbyte) { - return read(fd, buf, nbyte); +#ifdef RT_USING_USERSPACE + void *kmem = RT_NULL; + ssize_t ret = -1; + + if (!nbyte) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + + kmem = kmem_get(nbyte); + if (!kmem) + { + return -ENOMEM; + } + + ret = read(fd, kmem, nbyte); + if (ret > 0) + { + lwp_put_to_user(buf, kmem, ret); + } + + kmem_put(kmem); + return (ret < 0 ? GET_ERRNO() : ret); +#else + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + ssize_t ret = read(fd, buf, nbyte); + return (ret < 0 ? GET_ERRNO() : ret); +#endif } /* syscall: "write" ret: "ssize_t" args: "int" "const void *" "size_t" */ ssize_t sys_write(int fd, const void *buf, size_t nbyte) { - return write(fd, buf, nbyte); +#ifdef RT_USING_USERSPACE + void *kmem = RT_NULL; + ssize_t ret = -1; + + if (!nbyte) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + + kmem = kmem_get(nbyte); + if (!kmem) + { + return -ENOMEM; + } + + lwp_get_from_user(kmem, (void *)buf, nbyte); + ret = write(fd, kmem, nbyte); + + kmem_put(kmem); + return (ret < 0 ? GET_ERRNO() : ret); +#else + if (!lwp_user_accessable((void *)buf, nbyte)) + { + return -EFAULT; + } + ssize_t ret = write(fd, buf, nbyte); + return (ret < 0 ? GET_ERRNO() : ret); +#endif } /* syscall: "lseek" ret: "off_t" args: "int" "off_t" "int" */ off_t sys_lseek(int fd, off_t offset, int whence) { - return lseek(fd, offset, whence); + off_t ret = lseek(fd, offset, whence); + return (ret < 0 ? GET_ERRNO() : ret); } /* syscall: "open" ret: "int" args: "const char *" "int" "..." */ -int sys_open(const char *name, int mode, ...) +int sys_open(const char *name, int flag, ...) { - return open(name, mode, 0); +#ifdef RT_USING_USERSPACE + int ret = -1; + rt_size_t len = 0; + char *kname = RT_NULL; + + if (!lwp_user_accessable((void *)name, 1)) + { + return -EFAULT; + } + + len = rt_strlen(name); + if (!len) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + lwp_get_from_user(kname, (void *)name, len + 1); + ret = open(kname, flag, 0); + + kmem_put(kname); + return (ret < 0 ? GET_ERRNO() : ret); +#else + if (!lwp_user_accessable((void *)name, 1)) + { + return -EFAULT; + } + int ret = open(name, flag, 0); + return (ret < 0 ? GET_ERRNO() : ret); +#endif } /* syscall: "close" ret: "int" args: "int" */ int sys_close(int fd) { - return close(fd); + int ret = close(fd); + return (ret < 0 ? GET_ERRNO() : ret); } /* syscall: "ioctl" ret: "int" args: "int" "u_long" "..." */ int sys_ioctl(int fd, unsigned long cmd, void* data) { - return ioctl(fd, cmd, data); + int ret = ioctl(fd, cmd, data); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_fstat(int file, struct stat *buf) +{ +#ifdef RT_USING_USERSPACE + int ret = -1; + struct stat statbuff; + + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + else + { + ret = fstat(file, &statbuff); + + if (ret == 0) + { + lwp_put_to_user(buf, &statbuff, sizeof statbuff); + } + else + { + ret = GET_ERRNO(); + } + return (ret < 0 ? GET_ERRNO() : ret); + } +#else + if (!lwp_user_accessable((void *)buf, sizeof(struct stat))) + { + return -EFAULT; + } + int ret = fstat(file, buf); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +/* DFS and lwip definitions */ +#define IMPL_POLLIN (0x01) + +#define IMPL_POLLOUT (0x02) + +#define IMPL_POLLERR (0x04) +#define IMPL_POLLHUP (0x08) +#define IMPL_POLLNVAL (0x10) + +/* musl definitions */ +#define INTF_POLLIN 0x001 +#define INTF_POLLPRI 0x002 +#define INTF_POLLOUT 0x004 +#define INTF_POLLERR 0x008 +#define INTF_POLLHUP 0x010 +#define INTF_POLLNVAL 0x020 +#define INTF_POLLRDNORM 0x040 +#define INTF_POLLRDBAND 0x080 +#define INTF_POLLWRNORM 0x100 +#define INTF_POLLWRBAND 0x200 +#define INTF_POLLMSG 0x400 +#define INTF_POLLRDHUP 0x2000 + +#define INTF_POLLIN_MASK (INTF_POLLIN | INTF_POLLRDNORM | INTF_POLLRDBAND | INTF_POLLPRI) +#define INTF_POLLOUT_MASK (INTF_POLLOUT | INTF_POLLWRNORM | INTF_POLLWRBAND) + +static void musl2dfs_events(short *events) +{ + short origin_e = *events; + short result_e = 0; + + if (origin_e & INTF_POLLIN_MASK) + { + result_e |= IMPL_POLLIN; + } + + if (origin_e & INTF_POLLOUT_MASK) + { + result_e |= IMPL_POLLOUT; + } + + if (origin_e & INTF_POLLERR) + { + result_e |= IMPL_POLLERR; + } + + if (origin_e & INTF_POLLHUP) + { + result_e |= IMPL_POLLHUP; + } + + if (origin_e & INTF_POLLNVAL) + { + result_e |= IMPL_POLLNVAL; + } + + *events = result_e; +} + +static void dfs2musl_events(short *events) +{ + short origin_e = *events; + short result_e = 0; + + if (origin_e & IMPL_POLLIN) + { + result_e |= INTF_POLLIN_MASK; + } + + if (origin_e & IMPL_POLLOUT) + { + result_e |= INTF_POLLOUT_MASK; + } + + if (origin_e & IMPL_POLLERR) + { + result_e |= INTF_POLLERR; + } + + if (origin_e & IMPL_POLLHUP) + { + result_e |= INTF_POLLHUP; + } + + if (origin_e & IMPL_POLLNVAL) + { + result_e |= INTF_POLLNVAL; + } + + *events = result_e; +} + +int sys_poll(struct pollfd *fds, nfds_t nfds, int timeout) +{ + int ret = -1; + int i = 0; +#ifdef RT_USING_USERSPACE + struct pollfd *kfds = RT_NULL; + + if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) + { + return -EFAULT; + } + + kfds = (struct pollfd *)kmem_get(nfds * sizeof *kfds); + if (!kfds) + { + return -ENOMEM; + } + + lwp_get_from_user(kfds, fds, nfds * sizeof *kfds); + + for (i = 0; i < nfds; i++) + { + musl2dfs_events(&kfds[i].events); + } + ret = poll(kfds, nfds, timeout); + if (ret > 0) + { + for (i = 0; i < nfds; i++) + { + dfs2musl_events(&kfds->revents); + } + lwp_put_to_user(fds, kfds, nfds * sizeof *kfds); + } + + kmem_put(kfds); + return ret; +#else +#ifdef RT_USING_MUSL + for (i = 0; i < nfds; i++) + { + musl2dfs_events(&fds->events); + } +#endif /* RT_USING_MUSL */ + if (!lwp_user_accessable((void *)fds, nfds * sizeof *fds)) + { + return -EFAULT; + } + ret = poll(fds, nfds, timeout); +#ifdef RT_USING_MUSL + if (ret > 0) + { + for (i = 0; i < nfds; i++) + { + dfs2musl_events(&fds->revents); + } + } +#endif /* RT_USING_MUSL */ + return ret; +#endif /* RT_USING_USERSPACE */ +} + +int sys_select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout) +{ +#ifdef RT_USING_USERSPACE + int ret = -1; + fd_set *kreadfds = RT_NULL, *kwritefds = RT_NULL, *kexceptfds = RT_NULL; + + if (readfds) + { + if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kreadfds = (fd_set *)kmem_get(sizeof *kreadfds); + if (!kreadfds) + { + SET_ERRNO(ENOMEM); + goto quit; + } + lwp_get_from_user(kreadfds, readfds, sizeof *kreadfds); + } + if (writefds) + { + if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kwritefds = (fd_set *)kmem_get(sizeof *kwritefds); + if (!kwritefds) + { + SET_ERRNO(ENOMEM); + goto quit; + } + lwp_get_from_user(kwritefds, writefds, sizeof *kwritefds); + } + if (exceptfds) + { + if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) + { + SET_ERRNO(EFAULT); + goto quit; + } + kexceptfds = (fd_set *)kmem_get(sizeof *kexceptfds); + if (!kexceptfds) + { + SET_ERRNO(EINVAL); + goto quit; + } + lwp_get_from_user(kexceptfds, exceptfds, sizeof *kexceptfds); + } + + ret = select(nfds, kreadfds, kwritefds, kexceptfds, timeout); + if (kreadfds) + { + lwp_put_to_user(readfds, kreadfds, sizeof *kreadfds); + } + if (kwritefds) + { + lwp_put_to_user(writefds, kwritefds, sizeof *kwritefds); + } + if (kexceptfds) + { + lwp_put_to_user(exceptfds, kexceptfds, sizeof *kexceptfds); + } +quit: + if (kreadfds) + { + kmem_put(kreadfds); + } + if (kwritefds) + { + kmem_put(kwritefds); + } + if (kexceptfds) + { + kmem_put(kexceptfds); + } + return (ret < 0 ? GET_ERRNO() : ret); +#else + int ret; + + if (!lwp_user_accessable((void *)readfds, sizeof *readfds)) + { + return -EFAULT; + } + if (!lwp_user_accessable((void *)writefds, sizeof *writefds)) + { + return -EFAULT; + } + if (!lwp_user_accessable((void *)exceptfds, sizeof *exceptfds)) + { + return -EFAULT; + } + ret = select(nfds, readfds, writefds, exceptfds, timeout); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +int sys_unlink(const char *pathname) +{ +#ifdef RT_USING_USERSPACE + int ret = -1; + rt_size_t len = 0; + char *kname = RT_NULL; + int a_err = 0; + + lwp_user_strlen(pathname, &a_err); + if (a_err) + { + return -EFAULT; + } + + len = rt_strlen(pathname); + if (!len) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!kname) + { + return -ENOMEM; + } + + lwp_get_from_user(kname, (void *)pathname, len + 1); + ret = unlink(kname); + + kmem_put(kname); + return (ret < 0 ? GET_ERRNO() : ret); +#else + int ret = 0; + ret = unlink(pathname); + return (ret < 0 ? GET_ERRNO() : ret); +#endif } /* syscall: "nanosleep" ret: "int" args: "const struct timespec *" "struct timespec *" */ int sys_nanosleep(const struct timespec *rqtp, struct timespec *rmtp) { - rt_tick_t tick; - + int ret = 0; dbg_log(DBG_LOG, "sys_nanosleep\n"); + if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) + return -EFAULT; - tick = rqtp->tv_sec * RT_TICK_PER_SECOND + (rqtp->tv_nsec * RT_TICK_PER_SECOND)/ 1000000000; - rt_thread_delay(tick); - +#ifdef RT_USING_USERSPACE + struct timespec rqtp_k; + struct timespec rmtp_k; + + lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); + ret = nanosleep(&rqtp_k, &rmtp_k); + if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); + return -EINTR; + } +#else if (rmtp) { - tick = rt_tick_get() - tick; - /* get the passed time */ - rmtp->tv_sec = tick/RT_TICK_PER_SECOND; - rmtp->tv_nsec = (tick%RT_TICK_PER_SECOND) * (1000000000/RT_TICK_PER_SECOND); + if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + return -EFAULT; + ret = nanosleep(rqtp, rmtp); + } +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +/* syscall: "gettimeofday" ret: "int" args: "struct timeval *" "struct timezone *" */ +int sys_gettimeofday(struct timeval *tp, struct timezone *tzp) +{ +#ifdef RT_USING_USERSPACE + struct timeval t_k; + + if (tp) + { + if (!lwp_user_accessable((void *)tp, sizeof *tp)) + { + return -EFAULT; + } + + t_k.tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; + t_k.tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); + + lwp_put_to_user(tp, (void *)&t_k, sizeof t_k); + } +#else + if (tp) + { + if (!lwp_user_accessable((void *)tp, sizeof *tp)) + { + return -EFAULT; + } + tp->tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; + tp->tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); } +#endif + + return 0; +} +int sys_settimeofday(const struct timeval *tv, const struct timezone *tzp) +{ return 0; } +int sys_exec(char *filename, int argc, char **argv, char **envp) +{ + return lwp_execve(filename, 0, argc, argv, envp); +} + +int sys_kill(int pid, int sig) +{ + int ret = 0; + ret = lwp_kill(pid, sig); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_getpid(void) +{ + return lwp_getpid(); +} + /* syscall: "getpriority" ret: "int" args: "int" "id_t" */ int sys_getpriority(int which, id_t who) { @@ -120,7 +1005,7 @@ int sys_getpriority(int which, id_t who) rt_thread_t tid; tid = rt_thread_self(); - if (who == (id_t)tid || who == 0xff) + if (who == (id_t)(rt_size_t)tid || who == 0xff) { return tid->current_priority; } @@ -137,7 +1022,7 @@ int sys_setpriority(int which, id_t who, int prio) rt_thread_t tid; tid = rt_thread_self(); - if ((who == (id_t)tid || who == 0xff) && (prio >= 0 && prio < RT_THREAD_PRIORITY_MAX)) + if ((who == (id_t)(rt_size_t)tid || who == 0xff) && (prio >= 0 && prio < RT_THREAD_PRIORITY_MAX)) { rt_thread_control(tid, RT_THREAD_CTRL_CHANGE_PRIORITY, &prio); return 0; @@ -147,122 +1032,3374 @@ int sys_setpriority(int which, id_t who, int prio) return -1; } -/* syscall: "gettimeofday" ret: "int" args: "struct timeval *" "struct timezone *" */ -int sys_gettimeofday(struct timeval *tp, struct timezone *tzp) +rt_sem_t sys_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) { - if (tp) + rt_sem_t sem = rt_sem_create(name, value, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)sem) != 0) { - tp->tv_sec = rt_tick_get() / RT_TICK_PER_SECOND; - tp->tv_usec = (rt_tick_get() % RT_TICK_PER_SECOND) * (1000000 / RT_TICK_PER_SECOND); + rt_sem_delete(sem); + sem = NULL; } + return sem; +} - return 0; +rt_err_t sys_sem_delete(rt_sem_t sem) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)sem); } -/* syscall: "settimeofday" ret: "int" args: "const struct timeval *" "const struct timezone *" */ -int sys_settimeofday(const struct timeval *tv, const struct timezone *tzp) +rt_err_t sys_sem_take(rt_sem_t sem, rt_int32_t time) { - return 0; + return rt_sem_take_interruptible(sem, time); } -/* syscall: "msgget" ret: "int" args: "key_t" "int" */ -int sys_msgget(key_t key, int msgflg) +rt_err_t sys_sem_release(rt_sem_t sem) { - return -1; + return rt_sem_release(sem); } -/* syscall: "msgsnd" ret: "int" args: "int" "const void *" "size_t" "int" */ -int sys_msgsend(int msqid, const void *msgp, size_t msgsz, int msgflg) +rt_mutex_t sys_mutex_create(const char *name, rt_uint8_t flag) { - return -1; + rt_mutex_t mutex = rt_mutex_create(name, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)mutex) != 0) + { + rt_mutex_delete(mutex); + mutex = NULL; + } + return mutex; } -/* syscall: "msgrcv" ret: "int" args: "int" "void *" "size_t" "long" "int" */ -int sys_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg) +rt_err_t sys_mutex_delete(rt_mutex_t mutex) { - return -1; + return lwp_user_object_delete(lwp_self(), (rt_object_t)mutex); +} + +rt_err_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time) +{ + return rt_mutex_take_interruptible(mutex, time); +} + +rt_err_t sys_mutex_release(rt_mutex_t mutex) +{ + return rt_mutex_release(mutex); +} + +#ifdef RT_USING_USERSPACE +/* memory allocation */ +rt_base_t sys_brk(void *addr) +{ + return lwp_brk(addr); +} + +void *sys_mmap2(void *addr, size_t length, int prot, + int flags, int fd, off_t pgoffset) +{ + return lwp_mmap2(addr, length, prot, flags, fd, pgoffset); +} + +int sys_munmap(void *addr, size_t length) +{ + return lwp_munmap(addr); +} + +void *sys_mremap(void *old_address, size_t old_size, + size_t new_size, int flags, void *new_address) +{ + return (void *)-1; +} + +int sys_madvise(void *addr, size_t len, int behav) +{ + return -ENOSYS; +} +#endif + +rt_event_t sys_event_create(const char *name, rt_uint8_t flag) +{ + rt_event_t event = rt_event_create(name, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)event) != 0) + { + rt_event_delete(event); + event = NULL; + } + return event; +} + +rt_err_t sys_event_delete(rt_event_t event) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)event); +} + +rt_err_t sys_event_send(rt_event_t event, rt_uint32_t set) +{ + return rt_event_send(event, set); +} + +rt_err_t sys_event_recv(rt_event_t event, + rt_uint32_t set, + rt_uint8_t opt, + rt_int32_t timeout, + rt_uint32_t *recved) +{ + if ((recved != NULL) && !lwp_user_accessable((void *)recved, sizeof(rt_uint32_t *))) + { + return -EFAULT; + } + return rt_event_recv(event, set, opt, timeout, recved); +} + +rt_mailbox_t sys_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) +{ + rt_mailbox_t mb = rt_mb_create(name, size, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)mb) != 0) + { + rt_mb_delete(mb); + mb = NULL; + } + return mb; +} + +rt_err_t sys_mb_delete(rt_mailbox_t mb) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)mb); +} + +rt_err_t sys_mb_send(rt_mailbox_t mb, rt_ubase_t value) +{ + return rt_mb_send(mb, value); +} + +rt_err_t sys_mb_send_wait(rt_mailbox_t mb, + rt_ubase_t value, + rt_int32_t timeout) +{ + return rt_mb_send_wait(mb, value, timeout); +} + +rt_err_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) +{ + if (!lwp_user_accessable((void *)value, sizeof(rt_ubase_t *))) + { + return -EFAULT; + } + return rt_mb_recv(mb, (rt_ubase_t *)value, timeout); +} + +rt_mq_t sys_mq_create(const char *name, + rt_size_t msg_size, + rt_size_t max_msgs, + rt_uint8_t flag) +{ + rt_mq_t mq = rt_mq_create(name, msg_size, max_msgs, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)mq) != 0) + { + rt_mq_delete(mq); + mq = NULL; + } + return mq; +} + +rt_err_t sys_mq_delete(rt_mq_t mq) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)mq); +} + +rt_err_t sys_mq_send(rt_mq_t mq, void *buffer, rt_size_t size) +{ + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + return rt_mq_send(mq, buffer, size); +} + +rt_err_t sys_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size) +{ + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + return rt_mq_urgent(mq, buffer, size); +} + +rt_err_t sys_mq_recv(rt_mq_t mq, + void *buffer, + rt_size_t size, + rt_int32_t timeout) +{ + if (!lwp_user_accessable((void *)buffer, size)) + { + return -EFAULT; + } + return rt_mq_recv(mq, buffer, size, timeout); +} + +static void timer_timeout_callback(void *parameter) +{ + rt_sem_t sem = (rt_sem_t)parameter; + rt_sem_release(sem); +} + +rt_timer_t sys_rt_timer_create(const char *name, + void *data, + rt_tick_t time, + rt_uint8_t flag) +{ + rt_timer_t timer = rt_timer_create(name, timer_timeout_callback, (void *)data, time, flag); + if (lwp_user_object_add(lwp_self(), (rt_object_t)timer) != 0) + { + rt_timer_delete(timer); + timer = NULL; + } + return timer; +} + +rt_err_t sys_rt_timer_delete(rt_timer_t timer) +{ + return lwp_user_object_delete(lwp_self(), (rt_object_t)timer); +} + +rt_err_t sys_rt_timer_start(rt_timer_t timer) +{ + return rt_timer_start(timer); +} + +rt_err_t sys_rt_timer_stop(rt_timer_t timer) +{ + return rt_timer_stop(timer); +} + +rt_err_t sys_rt_timer_control(rt_timer_t timer, int cmd, void *arg) +{ + return rt_timer_control(timer, cmd, arg); +} + +rt_err_t sys_timer_create(clockid_t clockid, struct sigevent *restrict sevp, timer_t *restrict timerid) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + struct sigevent sevp_k; + timer_t timerid_k; + struct sigevent evp_default; + if (sevp == NULL) + { + sevp_k.sigev_notify = SIGEV_SIGNAL; + sevp_k.sigev_signo = SIGALRM; + sevp = &sevp_k; + } + else + lwp_get_from_user(&sevp_k, (void *)sevp, sizeof sevp_k); + lwp_get_from_user(&timerid_k, (void *)timerid, sizeof timerid_k); + ret = timer_create(clockid, &sevp_k, &timerid_k); + if (ret != -RT_ERROR){ + lwp_put_to_user(sevp, (void *)&sevp_k, sizeof sevp_k); + lwp_put_to_user(timerid, (void *)&timerid_k, sizeof timerid_k); + } +#else + ret = timer_create(clockid, sevp, timerid); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_err_t sys_timer_delete(timer_t timerid) +{ + int ret = timer_delete(timerid); + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_err_t sys_timer_settime(timer_t timerid, int flags, + const struct itimerspec *restrict new_value, + struct itimerspec *restrict old_value) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + struct itimerspec new_value_k; + struct itimerspec old_value_k; + + lwp_get_from_user(&new_value_k, (void *)new_value, sizeof new_value_k); + lwp_get_from_user(&old_value_k, (void *)timerid, sizeof old_value_k); + ret = timer_settime(timerid, flags, &new_value_k, &old_value_k); + lwp_put_to_user(old_value, (void *)&old_value_k, sizeof old_value_k); + +#else + ret = timer_settime(timerid, flags, new_value, old_value); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_err_t sys_timer_gettime(timer_t timerid, struct itimerspec *curr_value) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + + struct itimerspec curr_value_k; + lwp_get_from_user(&curr_value_k, (void *)curr_value, sizeof curr_value_k); + ret = timer_gettime(timerid, &curr_value_k); + lwp_put_to_user(curr_value, (void *)&curr_value_k, sizeof curr_value_k); +#else + ret = timer_gettime(timerid, curr_value); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_err_t sys_timer_getoverrun(timer_t timerid) +{ + int ret = 0; + ret = timer_getoverrun(timerid); + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_thread_t sys_thread_create(void *arg[]) +{ + rt_base_t level = 0; + void *user_stack = 0; + struct rt_lwp *lwp = 0; + rt_thread_t thread = RT_NULL; + int tid = 0; + + lwp = rt_thread_self()->lwp; + lwp_ref_inc(lwp); +#ifdef RT_USING_USERSPACE + user_stack = lwp_map_user(lwp, 0, (size_t)arg[3], 0); + if (!user_stack) + { + goto fail; + } + if ((tid = lwp_tid_get()) == 0) + { + goto fail; + } + thread = rt_thread_create((const char *)arg[0], + _crt_thread_entry, + (void *)arg[2], + ALLOC_KERNEL_STACK_SIZE, + (rt_uint8_t)(size_t)arg[4], + (rt_uint32_t)(rt_size_t)arg[5]); + if (!thread) + { + goto fail; + } + +#ifdef RT_USING_SMP + thread->bind_cpu = lwp->bind_cpu; +#endif + thread->cleanup = lwp_cleanup; + thread->user_entry = (void (*)(void *))arg[1]; + thread->user_stack = (void *)user_stack; + thread->user_stack_size = (rt_size_t)arg[3]; +#else + rt_uint32_t kstack_size = (rt_uint32_t)arg[7]; + if (kstack_size < ALLOC_KERNEL_STACK_SIZE_MIN) + { + /* When kstack size is 0, the default size of the kernel stack is used */ + kstack_size = kstack_size ? ALLOC_KERNEL_STACK_SIZE_MIN : ALLOC_KERNEL_STACK_SIZE; + } + else if (kstack_size > ALLOC_KERNEL_STACK_SIZE_MAX) + { + kstack_size = ALLOC_KERNEL_STACK_SIZE_MAX; + } + + user_stack = (void *)arg[3]; + if ((!user_stack) || ((rt_uint32_t)arg[6] == RT_NULL)) + { + goto fail; + } + + if ((tid = lwp_tid_get()) == 0) + { + goto fail; + } + + thread = rt_thread_create((const char *)arg[0], _crt_thread_entry, (void *)arg[2], kstack_size, (rt_uint8_t)(size_t)arg[5], (rt_uint32_t)arg[6]); + if (!thread) + { + goto fail; + } + thread->cleanup = lwp_cleanup; + thread->user_entry = (void (*)(void *))arg[1]; + thread->user_stack = (void *)user_stack; + thread->user_stack_size = (uint32_t)arg[4]; + rt_memset(thread->user_stack, '#', thread->user_stack_size); +#endif /* RT_USING_USERSPACE */ + + thread->lwp = (void*)lwp; + thread->tid = tid; + lwp_tid_set_thread(tid, thread); + + if (lwp->debug) + { + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void*)0); + } + + level = rt_hw_interrupt_disable(); + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + rt_hw_interrupt_enable(level); + + return thread; + +fail: + lwp_tid_put(tid); + if (lwp) + { + lwp_ref_dec(lwp); + } + return RT_NULL; +} +#ifdef ARCH_MM_MMU +#define CLONE_VM 0x00000100 +#define CLONE_FS 0x00000200 +#define CLONE_FILES 0x00000400 +#define CLONE_SIGHAND 0x00000800 +#define CLONE_PTRACE 0x00002000 +#define CLONE_VFORK 0x00004000 +#define CLONE_PARENT 0x00008000 +#define CLONE_THREAD 0x00010000 +#define CLONE_NEWNS 0x00020000 +#define CLONE_SYSVSEM 0x00040000 +#define CLONE_SETTLS 0x00080000 +#define CLONE_PARENT_SETTID 0x00100000 +#define CLONE_CHILD_CLEARTID 0x00200000 +#define CLONE_DETACHED 0x00400000 +#define CLONE_UNTRACED 0x00800000 +#define CLONE_CHILD_SETTID 0x01000000 +#define CLONE_NEWCGROUP 0x02000000 +#define CLONE_NEWUTS 0x04000000 +#define CLONE_NEWIPC 0x08000000 +#define CLONE_NEWUSER 0x10000000 +#define CLONE_NEWPID 0x20000000 +#define CLONE_NEWNET 0x40000000 +#define CLONE_IO 0x80000000 + +/* arg[] -> flags + * stack + * new_tid + * tls + * set_clear_tid_address + * quit_func + * start_args + * */ +#define SYS_CLONE_ARGS_NR 7 + +long _sys_clone(void *arg[]) +{ + rt_base_t level = 0; + struct rt_lwp *lwp = 0; + rt_thread_t thread = RT_NULL; + rt_thread_t self = RT_NULL; + int tid = 0; + + unsigned long flags = 0; + void *user_stack = RT_NULL; + int *new_tid = RT_NULL; + void *tls = RT_NULL; + /* + musl call flags (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND + | CLONE_THREAD | CLONE_SYSVSEM | CLONE_SETTLS + | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_DETACHED); + */ + + /* check args */ + if (!lwp_user_accessable(arg, sizeof(void *[SYS_CLONE_ARGS_NR]))) + { + return -EFAULT; + } + + flags = (unsigned long)(size_t)arg[0]; + if ((flags & (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) + != (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_THREAD | CLONE_SYSVSEM)) + { + return -EINVAL; + } + + user_stack = arg[1]; + new_tid = (int *)arg[2]; + tls = (void *)arg[3]; + + if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) + { + if (!lwp_user_accessable(new_tid, sizeof(int))) + { + return -EFAULT; + } + } + + self = rt_thread_self(); + lwp = self->lwp; + lwp_ref_inc(lwp); + if (!user_stack) + { + SET_ERRNO(EINVAL); + goto fail; + } + if ((tid = lwp_tid_get()) == 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + thread = rt_thread_create(self->name, + RT_NULL, + RT_NULL, + self->stack_size, + self->init_priority, + self->init_tick); + if (!thread) + { + goto fail; + } + +#ifdef RT_USING_SMP + thread->bind_cpu = lwp->bind_cpu; +#endif + thread->cleanup = lwp_cleanup; + thread->user_entry = RT_NULL; + thread->user_stack = RT_NULL; + thread->user_stack_size = 0; + thread->lwp = (void *)lwp; + thread->tid = tid; + + if ((flags & CLONE_SETTLS) == CLONE_SETTLS) + { + thread->thread_idr = tls; + } + if ((flags & CLONE_PARENT_SETTID) == CLONE_PARENT_SETTID) + { + *new_tid = (int)(tid); + } + if ((flags & CLONE_CHILD_CLEARTID) == CLONE_CHILD_CLEARTID) + { + thread->clear_child_tid = (int *)arg[4]; + } + + if (lwp->debug) + { + rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void*)0); + } + + level = rt_hw_interrupt_disable(); + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + rt_hw_interrupt_enable(level); + + /* copy origin stack */ + rt_memcpy(thread->stack_addr, self->stack_addr, thread->stack_size); + lwp_tid_set_thread(tid, thread); + arch_set_thread_context(arch_clone_exit, + (void *)((char *)thread->stack_addr + thread->stack_size), + user_stack, &thread->sp); + /* new thread never reach there */ + rt_thread_startup(thread); + return (long)tid; +fail: + lwp_tid_put(tid); + if (lwp) + { + lwp_ref_dec(lwp); + } + return GET_ERRNO(); +} + +RT_WEAK long sys_clone(void *arg[]) +{ + return _sys_clone(arg); +} + +int lwp_dup_user(struct lwp_avl_struct* ptree, void *arg); + +static int _copy_process(struct rt_lwp *dest_lwp, struct rt_lwp *src_lwp) +{ + return lwp_avl_traversal(src_lwp->map_area, lwp_dup_user, dest_lwp); +} + +static void lwp_struct_copy(struct rt_lwp *dst, struct rt_lwp *src) +{ +#ifdef RT_USING_USERSPACE + dst->end_heap = src->end_heap; +#endif + dst->lwp_type = src->lwp_type; + dst->text_entry = src->text_entry; + dst->text_size = src->text_size; + dst->data_entry = src->data_entry; + dst->data_size = src->data_size; + dst->args = src->args; + dst->leader = 0; + dst->session = src->session; + dst->tty_old_pgrp = 0; + dst->__pgrp = src->__pgrp; + dst->tty = src->tty; + rt_memcpy(dst->cmd, src->cmd, RT_NAME_MAX); + + dst->sa_flags = src->sa_flags; + dst->signal_mask = src->signal_mask; + rt_memcpy(dst->signal_handler, src->signal_handler, sizeof dst->signal_handler); +} + +static int lwp_copy_files(struct rt_lwp *dst, struct rt_lwp *src) +{ + struct dfs_fdtable *dst_fdt; + struct dfs_fdtable *src_fdt; + + src_fdt = &src->fdt; + dst_fdt = &dst->fdt; + /* init fds */ + dst_fdt->fds = rt_calloc(src_fdt->maxfd, sizeof(void *)); + if (dst_fdt->fds) + { + struct dfs_fd *d_s; + int i; + + dst_fdt->maxfd = src_fdt->maxfd; + + dfs_fd_lock(); + /* dup files */ + for (i = 0; i < src_fdt->maxfd; i++) + { + d_s = fdt_fd_get(src_fdt, i); + if (d_s) + { + dst_fdt->fds[i] = d_s; + d_s->ref_count++; + } + } + dfs_fd_unlock(); + return 0; + } + return -RT_ERROR; +} + +int _sys_fork(void) +{ + rt_base_t level; + int tid = 0; + struct rt_lwp *lwp = RT_NULL; + struct rt_lwp *self_lwp = RT_NULL; + rt_thread_t thread = RT_NULL; + rt_thread_t self_thread = RT_NULL; + void *user_stack = RT_NULL; + + /* new lwp */ + lwp = lwp_new(); + if (!lwp) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* new tid */ + if ((tid = lwp_tid_get()) == 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* user space init */ + if (lwp_user_space_init(lwp) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + self_lwp = lwp_self(); + + /* copy process */ + if (_copy_process(lwp, self_lwp) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* copy lwp struct data */ + lwp_struct_copy(lwp, self_lwp); + + /* copy files */ + if (lwp_copy_files(lwp, self_lwp) != 0) + { + SET_ERRNO(ENOMEM); + goto fail; + } + + /* create thread */ + self_thread = rt_thread_self(); + + thread = rt_thread_create(self_thread->name, + RT_NULL, + RT_NULL, + self_thread->stack_size, + self_thread->init_priority, + self_thread->init_tick); + if (!thread) + { + goto fail; + } + + thread->cleanup = self_thread->cleanup; + thread->user_entry = self_thread->user_entry; + thread->user_stack = self_thread->user_stack; + thread->user_stack_size = self_thread->user_stack_size; + thread->signal_mask = self_thread->signal_mask; + thread->thread_idr = self_thread->thread_idr; + thread->lwp = (void *)lwp; + thread->tid = tid; + + level = rt_hw_interrupt_disable(); + + /* add thread to lwp process */ + rt_list_insert_after(&lwp->t_grp, &thread->sibling); + + /* lwp add to children link */ + lwp->sibling = self_lwp->first_child; + self_lwp->first_child = lwp; + lwp->parent = self_lwp; + + rt_hw_interrupt_enable(level); + + /* copy origin stack */ + rt_memcpy(thread->stack_addr, self_thread->stack_addr, self_thread->stack_size); + lwp_tid_set_thread(tid, thread); + + /* duplicate user objects */ + lwp_user_object_dup(lwp, self_lwp); + + level = rt_hw_interrupt_disable(); + user_stack = arch_get_user_sp(); + rt_hw_interrupt_enable(level); + + arch_set_thread_context(arch_fork_exit, + (void *)((char *)thread->stack_addr + thread->stack_size), + user_stack, &thread->sp); + /* new thread never reach there */ + level = rt_hw_interrupt_disable(); + if (lwp->tty != RT_NULL) + { + int ret; + struct rt_lwp *old_lwp; + + old_lwp = lwp->tty->foreground; + rt_mutex_take(&lwp->tty->lock, RT_WAITING_FOREVER); + ret = tty_push(&lwp->tty->head, old_lwp); + rt_mutex_release(&lwp->tty->lock); + if (ret < 0) + { + LOG_E("malloc fail!\n"); + goto fail; + } + + lwp->tty->foreground = lwp; + } + rt_hw_interrupt_enable(level); + rt_thread_startup(thread); + return lwp_to_pid(lwp); +fail: + if (tid != 0) + { + lwp_tid_put(tid); + } + if (lwp) + { + lwp_ref_dec(lwp); + } + return GET_ERRNO(); +} + +size_t lwp_user_strlen(const char *s, int *err) +{ + size_t len = 0; + + while (1) + { + if (!lwp_user_accessable((void *)(s + len), sizeof(char))) + { + if (err) + { + *err = 1; + } + return 0; + } + if (s[len] == '\0') + { + if (err) + { + *err = 0; + } + return len; + } + len++; + } +} + +/* arm needs to wrap fork/clone call to preserved lr & caller saved regs */ + +RT_WEAK int sys_fork(void) +{ + return _sys_fork(); +} + +RT_WEAK int sys_vfork(void) +{ + return sys_fork(); +} + +struct process_aux *lwp_argscopy(struct rt_lwp *lwp, int argc, char **argv, char **envp); +int lwp_load(const char *filename, struct rt_lwp *lwp, uint8_t *load_addr, size_t addr_size, struct process_aux *aux); +void lwp_user_obj_free(struct rt_lwp *lwp); + +#define _swap_lwp_data(lwp_used, lwp_new, type, member) \ + do {\ + type tmp;\ + tmp = lwp_used->member;\ + lwp_used->member = lwp_new->member;\ + lwp_new->member = tmp;\ + } while (0) + +static char *_insert_args(int new_argc, char *new_argv[], struct lwp_args_info *args) +{ + void *page = NULL; + int err = 0; + char **nargv; + char **nenvp; + char *p; + int i, len; + int nsize; + + if (new_argc == 0) + { + goto quit; + } + page = rt_pages_alloc(0); /* 1 page */ + if (!page) + { + goto quit; + } + + nsize = new_argc * sizeof(char *); + for (i = 0; i < new_argc; i++) + { + nsize += rt_strlen(new_argv[i]) + 1; + } + if (nsize + args->size > ARCH_PAGE_SIZE) + { + err = 1; + goto quit; + } + nargv = (char **)page; + nenvp = nargv + args->argc + new_argc + 1; + p = (char *)(nenvp + args->envc + 1); + /* insert argv */ + for (i = 0; i < new_argc; i++) + { + nargv[i] = p; + len = rt_strlen(new_argv[i]) + 1; + rt_memcpy(p, new_argv[i], len); + p += len; + } + /* copy argv */ + nargv += new_argc; + for (i = 0; i < args->argc; i++) + { + nargv[i] = p; + len = rt_strlen(args->argv[i]) + 1; + rt_memcpy(p, args->argv[i], len); + p += len; + } + nargv[i] = NULL; + /* copy envp */ + for (i = 0; i < args->envc; i++) + { + nenvp[i] = p; + len = rt_strlen(args->envp[i]) + 1; + rt_memcpy(p, args->envp[i], len); + p += len; + } + nenvp[i] = NULL; + + /* update args */ + args->argv = (char **)page; + args->argc = args->argc + new_argc; + args->envp = args->argv + args->argc + 1; + /* args->envc no change */ + args->size = args->size + nsize; + +quit: + if (err && page) + { + rt_pages_free(page, 0); + page = NULL; + } + return page; +} + +#define INTERP_BUF_SIZE 128 +static char *_load_script(const char *filename, struct lwp_args_info *args) +{ + void *page = NULL; + char *new_page; + int fd = -RT_ERROR; + int len; + char interp[INTERP_BUF_SIZE]; + char *cp; + char *i_name; + char *i_arg; + + fd = open(filename, O_BINARY | O_RDONLY, 0); + if (fd < 0) + { + goto quit; + } + len = read(fd, interp, INTERP_BUF_SIZE); + if (len < 2) + { + goto quit; + } + + if ((interp[0] != '#') || (interp[1] != '!')) + { + goto quit; + } + + if (len == INTERP_BUF_SIZE) + { + len--; + } + interp[len] = '\0'; + + if ((cp = strchr(interp, '\n')) == NULL) + { + cp = interp + INTERP_BUF_SIZE - 1; + } + *cp = '\0'; + while (cp > interp) + { + cp--; + if ((*cp == ' ') || (*cp == '\t')) + { + *cp = '\0'; + } + else + { + break; + } + } + for (cp = interp + 2; (*cp == ' ') || (*cp == '\t'); cp++) + { + /* nothing */ + } + if (*cp == '\0') + { + goto quit; /* No interpreter name found */ + } + i_name = cp; + i_arg = NULL; + for (; *cp && (*cp != ' ') && (*cp != '\t'); cp++) + { + /* nothing */ + } + while ((*cp == ' ') || (*cp == '\t')) + { + *cp++ = '\0'; + } + if (*cp) + { + i_arg = cp; + } + + if (i_arg) + { + new_page = _insert_args(1, &i_arg, args); + rt_pages_free(page, 0); + page = new_page; + if (!page) + { + goto quit; + } + } + new_page = _insert_args(1, &i_name, args); + rt_pages_free(page, 0); + page = new_page; + +quit: + if (fd >= 0) + { + close(fd); + } + return page; +} + +int load_ldso(struct rt_lwp *lwp, char *exec_name, char *const argv[], char *const envp[]) +{ + int ret = -1; + int i; + void *page; + void *new_page; + int argc = 0; + int envc = 0; + int size; + char **kargv; + char **kenvp; + size_t len; + char *p; + char *i_arg; + struct lwp_args_info args_info; + struct process_aux *aux; + + size = sizeof(char *); + if (argv) + { + while (1) + { + if (!argv[argc]) + { + break; + } + len = rt_strlen((const char *)argv[argc]); + size += sizeof(char *) + len + 1; + argc++; + } + } + if (envp) + { + while (1) + { + if (!envp[envc]) + { + break; + } + len = rt_strlen((const char *)envp[envc]); + size += sizeof(char *) + len + 1; + envc++; + } + } + + page = rt_pages_alloc(0); /* 1 page */ + if (!page) + { + SET_ERRNO(ENOMEM); + goto quit; + } + kargv = (char **)page; + kenvp = kargv + argc + 1; + p = (char *)(kenvp + envc + 1); + /* copy argv */ + if (argv) + { + for (i = 0; i < argc; i++) + { + kargv[i] = p; + len = rt_strlen(argv[i]) + 1; + rt_memcpy(p, argv[i], len); + p += len; + } + kargv[i] = NULL; + } + /* copy envp */ + if (envp) + { + for (i = 0; i < envc; i++) + { + kenvp[i] = p; + len = rt_strlen(envp[i]) + 1; + rt_memcpy(p, envp[i], len); + p += len; + } + kenvp[i] = NULL; + } + + args_info.argc = argc; + args_info.argv = kargv; + args_info.envc = envc; + args_info.envp = kenvp; + args_info.size = size; + + new_page = _insert_args(1, &exec_name, &args_info); + rt_pages_free(page, 0); + page = new_page; + if (!page) + { + SET_ERRNO(ENOMEM); + goto quit; + } + + i_arg = "-e"; + new_page = _insert_args(1, &i_arg, &args_info); + rt_pages_free(page, 0); + page = new_page; + if (!page) + { + SET_ERRNO(ENOMEM); + goto quit; + } + + i_arg = "ld.so"; + new_page = _insert_args(1, &i_arg, &args_info); + rt_pages_free(page, 0); + page = new_page; + if (!page) + { + SET_ERRNO(ENOMEM); + goto quit; + } + + if ((aux = lwp_argscopy(lwp, args_info.argc, args_info.argv, args_info.envp)) == NULL) + { + SET_ERRNO(ENOMEM); + goto quit; + } + + ret = lwp_load("/lib/ld.so", lwp, RT_NULL, 0, aux); + + rt_strncpy(lwp->cmd, exec_name, RT_NAME_MAX); +quit: + if (page) + { + rt_pages_free(page, 0); + } + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_execve(const char *path, char *const argv[], char *const envp[]) +{ + int ret = -1; + int argc = 0; + int envc = 0; + void *page = NULL; + void *new_page; + int size = 0; + size_t len; + int access_err; + char **kargv; + char **kenvp; + char *p; + struct rt_lwp *new_lwp = NULL; + struct rt_lwp *lwp; + rt_base_t level; + int uni_thread; + rt_thread_t thread; + struct process_aux *aux; + int i; + struct lwp_args_info args_info; + + lwp = lwp_self(); + thread = rt_thread_self(); + uni_thread = 1; + level = rt_hw_interrupt_disable(); + if (lwp->t_grp.prev != &thread->sibling) + { + uni_thread = 0; + } + if (lwp->t_grp.next != &thread->sibling) + { + uni_thread = 0; + } + rt_hw_interrupt_enable(level); + if (!uni_thread) + { + SET_ERRNO(EINVAL); + goto quit; + } + + len = lwp_user_strlen(path, &access_err); + if (access_err) + { + SET_ERRNO(EFAULT); + goto quit; + } + + size += sizeof(char *); + if (argv) + { + while (1) + { + if (!lwp_user_accessable((void *)(argv + argc), sizeof(char *))) + { + SET_ERRNO(EFAULT); + goto quit; + } + if (!argv[argc]) + { + break; + } + len = lwp_user_strlen((const char *)argv[argc], &access_err); + if (access_err) + { + SET_ERRNO(EFAULT); + goto quit; + } + size += sizeof(char *) + len + 1; + argc++; + } + } + size += sizeof(char *); + if (envp) + { + while (1) + { + if (!lwp_user_accessable((void *)(envp + envc), sizeof(char *))) + { + SET_ERRNO(EFAULT); + goto quit; + } + if (!envp[envc]) + { + break; + } + len = lwp_user_strlen((const char *)envp[envc], &access_err); + if (access_err) + { + SET_ERRNO(EFAULT); + goto quit; + } + size += sizeof(char *) + len + 1; + envc++; + } + } + if (size > ARCH_PAGE_SIZE) + { + SET_ERRNO(EINVAL); + goto quit; + } + page = rt_pages_alloc(0); /* 1 page */ + if (!page) + { + SET_ERRNO(ENOMEM); + goto quit; + } + + kargv = (char **)page; + kenvp = kargv + argc + 1; + p = (char *)(kenvp + envc + 1); + /* copy argv */ + if (argv) + { + for (i = 0; i < argc; i++) + { + kargv[i] = p; + len = rt_strlen(argv[i]) + 1; + rt_memcpy(p, argv[i], len); + p += len; + } + kargv[i] = NULL; + } + /* copy envp */ + if (envp) + { + for (i = 0; i < envc; i++) + { + kenvp[i] = p; + len = rt_strlen(envp[i]) + 1; + rt_memcpy(p, envp[i], len); + p += len; + } + kenvp[i] = NULL; + } + + /* alloc new lwp to operation */ + new_lwp = (struct rt_lwp *)rt_malloc(sizeof(struct rt_lwp)); + if (!new_lwp) + { + SET_ERRNO(ENOMEM); + goto quit; + } + rt_memset(new_lwp, 0, sizeof(struct rt_lwp)); + new_lwp->ref = 1; + lwp_user_object_lock_init(new_lwp); + ret = arch_user_space_init(new_lwp); + if (ret != 0) + { + SET_ERRNO(ENOMEM); + goto quit; + } + /* file is a script ? */ + args_info.argc = argc; + args_info.argv = kargv; + args_info.envc = envc; + args_info.envp = kenvp; + args_info.size = size; + while (1) + { + new_page = _load_script(path, &args_info); + if (!new_page) + { + break; + } + rt_pages_free(page, 0); + page = new_page; + path = args_info.argv[0]; + } + + /* now load elf */ + if ((aux = lwp_argscopy(new_lwp, args_info.argc, args_info.argv, args_info.envp)) == NULL) + { + SET_ERRNO(ENOMEM); + goto quit; + } + ret = lwp_load(path, new_lwp, RT_NULL, 0, aux); + if (ret == 1) + { + /* dynamic */ + lwp_unmap_user(new_lwp, (void *)(USER_VADDR_TOP - ARCH_PAGE_SIZE)); + ret = load_ldso(new_lwp, (char *)path, args_info.argv, args_info.envp); + } + if (ret == RT_EOK) + { + int off = 0; + int last_backslash = 0; + char *run_name = args_info.argv[0]; + + /* clear all user objects */ + lwp_user_object_clear(lwp); + + /* find last \ or / */ + while (1) + { + char c = run_name[off++]; + + if (c == '\0') + { + break; + } + if (c == '\\' || c == '/') + { + last_backslash = off; + } + } + + /* load ok, now set thread name and swap the data of lwp and new_lwp */ + level = rt_hw_interrupt_disable(); + + rt_strncpy(thread->name, run_name + last_backslash, RT_NAME_MAX); + + rt_pages_free(page, 0); + +#ifdef RT_USING_USERSPACE + _swap_lwp_data(lwp, new_lwp, rt_mmu_info, mmu_info); + _swap_lwp_data(lwp, new_lwp, struct lwp_avl_struct *, map_area); + _swap_lwp_data(lwp, new_lwp, size_t, end_heap); +#endif + _swap_lwp_data(lwp, new_lwp, uint8_t, lwp_type); + _swap_lwp_data(lwp, new_lwp, void *, text_entry); + _swap_lwp_data(lwp, new_lwp, uint32_t, text_size); + _swap_lwp_data(lwp, new_lwp, void *, data_entry); + _swap_lwp_data(lwp, new_lwp, uint32_t, data_size); + + _swap_lwp_data(lwp, new_lwp, void *, args); + + rt_memset(&thread->signal_mask, 0, sizeof(thread->signal_mask)); + rt_memset(&thread->signal_mask_bak, 0, sizeof(thread->signal_mask_bak)); + lwp->sa_flags = 0; + rt_memset(&lwp->signal_mask, 0, sizeof(lwp->signal_mask)); + rt_memset(&lwp->signal_mask_bak, 0, sizeof(lwp->signal_mask_bak)); + rt_memset(lwp->signal_handler, 0, sizeof(lwp->signal_handler)); + + /* to do: clsoe files with flag CLOEXEC */ + + lwp_mmu_switch(thread); + + rt_hw_interrupt_enable(level); + + lwp_ref_dec(new_lwp); + arch_start_umode(lwp->args, + lwp->text_entry, + (void*)USER_STACK_VEND, + thread->stack_addr + thread->stack_size); + /* never reach here */ + } + return -EINVAL; +quit: + if (page) + { + rt_pages_free(page, 0); + } + if (new_lwp) + { + lwp_ref_dec(new_lwp); + } + return (ret < 0 ? GET_ERRNO() : ret); +} +#endif /* ARCH_MM_MMU */ + +rt_err_t sys_thread_delete(rt_thread_t thread) +{ +#ifdef ARCH_MM_MMU + return rt_thread_delete(thread); +#else + rt_err_t ret = 0; + + if(thread->type != RT_Object_Class_Thread) + { + ret = -EINVAL; + goto __exit; + } + + ret = rt_thread_delete(thread); + + if (rt_thread_self() == thread) + { + rt_schedule(); + } + +__exit: + return ret; +#endif +} + +rt_err_t sys_thread_startup(rt_thread_t thread) +{ + return rt_thread_startup(thread); +} + +rt_thread_t sys_thread_self(void) +{ + return rt_thread_self(); +} + +/* sys channel */ + +int sys_channel_open(const char *name, int flags) +{ + return lwp_channel_open(FDT_TYPE_LWP, name, flags); +} + +rt_err_t sys_channel_close(int fd) +{ + return lwp_channel_close(FDT_TYPE_LWP, fd); +} + +rt_err_t sys_channel_send(int fd, rt_channel_msg_t data) +{ + return lwp_channel_send(FDT_TYPE_LWP, fd, data); +} + +rt_err_t sys_channel_send_recv_timeout(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret, rt_int32_t time) +{ + return lwp_channel_send_recv_timeout(FDT_TYPE_LWP, fd, data, data_ret, time); +} + +rt_err_t sys_channel_reply(int fd, rt_channel_msg_t data) +{ + return lwp_channel_reply(FDT_TYPE_LWP, fd, data); +} + +rt_err_t sys_channel_recv_timeout(int fd, rt_channel_msg_t data, rt_int32_t time) +{ + return lwp_channel_recv_timeout(FDT_TYPE_LWP, fd, data, time); +} + +static struct rt_semaphore critical_lock; + +static int critical_init(void) +{ + rt_sem_init(&critical_lock, "ct_lock", 1, RT_IPC_FLAG_FIFO); + return 0; +} +INIT_DEVICE_EXPORT(critical_init); + +void sys_enter_critical(void) +{ + rt_sem_take(&critical_lock, RT_WAITING_FOREVER); +} + +void sys_exit_critical(void) +{ + rt_sem_release(&critical_lock); } /* syscall: "sys_log" ret: "int" args: "const char*" "size" */ +static int __sys_log_enable = 0; +static int sys_log_enable(int argc, char** argv) +{ + if (argc == 1) + { + rt_kprintf("sys_log = %d\n", __sys_log_enable); + return 0; + } + else + { + __sys_log_enable = atoi(argv[1]); + } + + return 0; +} +MSH_CMD_EXPORT_ALIAS(sys_log_enable, sys_log, sys_log 1(enable)/0(disable)); + int sys_log(const char* log, int size) { rt_device_t console = rt_console_get_device(); - if (console) rt_device_write(console, -1, log, size); + if (console && __sys_log_enable) + { + rt_device_write(console, -1, log, size); + } return 0; } -void *sys_malloc(size_t size) +int sys_stat(const char *file, struct stat *buf) +{ + int ret = 0; + ret = stat(file, buf); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_notimpl(void) { - return rt_lwp_mem_malloc(size); + return -ENOSYS; } -void sys_free(void *addr) +uint32_t sys_hw_interrupt_disable(void) { - rt_lwp_mem_free(addr); + return rt_hw_interrupt_disable(); } -void *sys_realloc(void *rmem, size_t newsize) +void sys_hw_interrupt_enable(uint32_t level) { - return rt_lwp_mem_realloc(rmem, newsize); + rt_hw_interrupt_enable(level); } -int sys_fstat(int file, struct stat *buf) +#ifdef RT_USING_USERSPACE +int sys_shmget(size_t key, size_t size, int create) { - return fstat(file, buf); + return lwp_shmget(key, size, create); } -int sys_notimpl(void) +int sys_shmrm(int id) +{ + return lwp_shmrm(id); +} + +void* sys_shmat(int id, void* shm_vaddr) +{ + return lwp_shmat(id, shm_vaddr); +} + +int sys_shmdt(void* shm_vaddr) +{ + return lwp_shmdt(shm_vaddr); +} +#elif defined RT_LWP_USING_SHM +void *sys_shm_alloc(int size) +{ + if (size < 0) + { + return RT_NULL; + } + return lwp_shm_alloc((rt_size_t)size); +} + +void *sys_shm_retain(void *mem) +{ + if (!lwp_user_accessable(mem, sizeof (void *))) + { + return RT_NULL; + } + return lwp_shm_retain(mem); +} + +int sys_shm_free(void *mem) +{ + if (!lwp_user_accessable(mem, sizeof (void *))) + { + return -EFAULT; + } + lwp_shm_free(mem); + return 0; +} +#endif + +/* device interfaces */ +rt_err_t sys_device_init(rt_device_t dev) +{ + return rt_device_init(dev); +} + +rt_err_t sys_device_register(rt_device_t dev, const char *name, rt_uint16_t flags) +{ + return rt_device_register(dev, name, flags); +} + +rt_err_t sys_device_control(rt_device_t dev, int cmd, void *arg) +{ + return rt_device_control(dev, cmd, arg); +} + +rt_device_t sys_device_find(const char* name) +{ + return rt_device_find(name); +} + +rt_err_t sys_device_open(rt_device_t dev, rt_uint16_t oflag) +{ + return rt_device_open(dev, oflag); +} + +rt_err_t sys_device_close(rt_device_t dev) +{ + return rt_device_close(dev); +} + +rt_size_t sys_device_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size) +{ + return rt_device_read(dev, pos, buffer, size); +} + +rt_size_t sys_device_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size) +{ + return rt_device_write(dev, pos, buffer, size); +} + +#ifdef RT_USING_SAL +/* network interfaces */ +int sys_accept(int socket, struct musl_sockaddr *addr, socklen_t *addrlen) +{ + int ret = -1; + struct sockaddr ksa; + struct musl_sockaddr kmusladdr; + socklen_t uaddrlen; + socklen_t kaddrlen; + + if (addr) + { + if (!lwp_user_accessable(addrlen, sizeof (socklen_t))) + { + return -EFAULT; + } + lwp_get_from_user(&uaddrlen, addrlen, sizeof (socklen_t)); + if (!uaddrlen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(addr, uaddrlen)) + { + return -EFAULT; + } + } + + kaddrlen = sizeof(struct sockaddr); + ret = accept(socket, &ksa, &kaddrlen); + if (ret >= 0) + { + if (addr) + { + sockaddr_tomusl(&ksa, &kmusladdr); + if (uaddrlen > sizeof(struct musl_sockaddr)) + { + uaddrlen = sizeof(struct musl_sockaddr); + } + lwp_put_to_user(addr, &kmusladdr, uaddrlen); + lwp_put_to_user(addrlen, &uaddrlen, sizeof (socklen_t)); + } + } + return ret; +} + +int sys_bind(int socket, const struct musl_sockaddr *name, socklen_t namelen) +{ + struct sockaddr sa; + struct musl_sockaddr kname; + + if (!lwp_user_accessable((void *)name, namelen)) + { + return -EFAULT; + } + +#ifdef SAL_USING_AF_UNIX + if (name->sa_family == AF_UNIX) + { + namelen = sizeof(struct sockaddr); + } +#endif /* SAL_USING_AF_UNIX */ + + lwp_get_from_user(&kname, (void *)name, namelen); + + sockaddr_tolwip(&kname, &sa); + + return bind(socket, &sa, namelen); +} + +int sys_shutdown(int socket, int how) +{ + return shutdown(socket, how); +} + +int sys_getpeername (int socket, struct musl_sockaddr *name, socklen_t *namelen) +{ + int ret = -1; + struct sockaddr sa; + struct musl_sockaddr kname; + socklen_t unamelen; + socklen_t knamelen; + + if (!lwp_user_accessable(namelen, sizeof (socklen_t *))) + { + return -EFAULT; + } + lwp_get_from_user(&unamelen, namelen, sizeof (socklen_t *)); + if (!unamelen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(name, unamelen)) + { + return -EFAULT; + } + + knamelen = sizeof(struct sockaddr); + ret = getpeername(socket, &sa, &knamelen); + + if (ret == 0) + { + sockaddr_tomusl(&sa, &kname); + if (unamelen > sizeof(struct musl_sockaddr)) + { + unamelen = sizeof(struct musl_sockaddr); + } + lwp_put_to_user(name, &kname, unamelen); + lwp_put_to_user(namelen, &unamelen, sizeof (socklen_t *)); + } + else + { + ret = GET_ERRNO(); + } + + return ret; +} + +int sys_getsockname (int socket, struct musl_sockaddr *name, socklen_t *namelen) +{ + int ret = -1; + struct sockaddr sa; + struct musl_sockaddr kname; + socklen_t unamelen; + socklen_t knamelen; + + if (!lwp_user_accessable(namelen, sizeof (socklen_t *))) + { + return -EFAULT; + } + lwp_get_from_user(&unamelen, namelen, sizeof (socklen_t *)); + if (!unamelen) + { + return -EINVAL; + } + + if (!lwp_user_accessable(name, unamelen)) + { + return -EFAULT; + } + + knamelen = sizeof(struct sockaddr); + ret = getsockname(socket, &sa, &knamelen); + if (ret == 0) + { + sockaddr_tomusl(&sa, &kname); + if (unamelen > sizeof(struct musl_sockaddr)) + { + unamelen = sizeof(struct musl_sockaddr); + } + lwp_put_to_user(name, &kname, unamelen); + lwp_put_to_user(namelen, &unamelen, sizeof(socklen_t *)); + } + else + { + ret = GET_ERRNO(); + } + return ret; +} + +int sys_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen) +{ + int ret; + convert_sockopt(&level, &optname); + ret = getsockopt(socket, level, optname, optval, optlen); + + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) +{ + int ret; + convert_sockopt(&level, &optname); + ret = setsockopt(socket, level, optname, optval, optlen); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_connect(int socket, const struct musl_sockaddr *name, socklen_t namelen) +{ + int ret; + struct sockaddr sa; + struct musl_sockaddr kname; + + if (!lwp_user_accessable((void *)name, namelen)) + { + return -EFAULT; + } + +#ifdef SAL_USING_AF_UNIX + if (name->sa_family == AF_UNIX) + { + namelen = sizeof(struct sockaddr); + } +#endif /* SAL_USING_AF_UNIX */ + + lwp_get_from_user(&kname, (void *)name, namelen); + + sockaddr_tolwip(&kname, &sa); + + ret = connect(socket, &sa, namelen); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_listen(int socket, int backlog) +{ + return listen(socket, backlog); +} + +#define MUSLC_MSG_OOB 0x0001 +#define MUSLC_MSG_PEEK 0x0002 +#define MUSLC_MSG_DONTWAIT 0x0040 +#define MUSLC_MSG_WAITALL 0x0100 +#define MUSLC_MSG_MORE 0x8000 + +static int netflags_muslc_2_lwip(int flags) +{ + int flgs = 0; + + if (flags & MUSLC_MSG_PEEK) + { + flgs |= MSG_PEEK; + } + if (flags & MUSLC_MSG_WAITALL) + { + flgs |= MSG_WAITALL; + } + if (flags & MUSLC_MSG_OOB) + { + flgs |= MSG_OOB; + } + if (flags & MUSLC_MSG_DONTWAIT) + { + flgs |= MSG_DONTWAIT; + } + if (flags & MUSLC_MSG_MORE) + { + flgs |= MSG_MORE; + } + return flgs; +} + +int sys_recvfrom(int socket, void *mem, size_t len, int flags, + struct musl_sockaddr *from, socklen_t *fromlen) +{ + int flgs = 0; +#ifdef RT_USING_USERSPACE + int ret = -1; + void *kmem = RT_NULL; +#endif + + flgs = netflags_muslc_2_lwip(flags); +#ifdef RT_USING_USERSPACE + if (!len) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)mem, len)) + { + return -EFAULT; + } + + kmem = kmem_get(len); + if (!kmem) + { + return -ENOMEM; + } + + if (flags == 0x2) + { + flags = 0x1; + } + + if (from) + { + struct sockaddr sa; + + ret = recvfrom(socket, kmem, len, flgs, &sa, fromlen); + sockaddr_tomusl(&sa, from); + } + else + { + ret = recvfrom(socket, kmem, len, flgs, NULL, NULL); + } + + if (ret > 0) + { + lwp_put_to_user(mem, kmem, len); + } + + kmem_put(kmem); + return (ret < 0 ? GET_ERRNO() : ret); +#else + int ret = -1; + if (from) + { + struct sockaddr sa = {0}; + + ret = recvfrom(socket, mem, len, flgs, &sa, fromlen); + sockaddr_tomusl(&sa, from); + } + else + { + ret = recvfrom(socket, mem, len, flags, NULL, NULL); + } + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +int sys_recv(int socket, void *mem, size_t len, int flags) +{ + int flgs = 0; + int ret; + + flgs = netflags_muslc_2_lwip(flags); + ret = recvfrom(socket, mem, len, flgs, NULL, NULL); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_sendto(int socket, const void *dataptr, size_t size, int flags, + const struct musl_sockaddr *to, socklen_t tolen) +{ + int flgs = 0; +#ifdef RT_USING_USERSPACE + int ret = -1; + void *kmem = RT_NULL; +#endif + + flgs = netflags_muslc_2_lwip(flags); +#ifdef RT_USING_USERSPACE + if (!size) + { + return -EINVAL; + } + + if (!lwp_user_accessable((void *)dataptr, size)) + { + return -EFAULT; + } + + kmem = kmem_get(size); + if (!kmem) + { + return -ENOMEM; + } + + lwp_get_from_user(kmem, (void *)dataptr, size); + + if (to) + { + struct sockaddr sa; + sockaddr_tolwip(to, &sa); + + ret = sendto(socket, kmem, size, flgs, &sa, tolen); + } + else + { + ret = sendto(socket, kmem, size, flgs, NULL, tolen); + } + + kmem_put(kmem); + return (ret < 0 ? GET_ERRNO() : ret); +#else + int ret; + if (to) + { + struct sockaddr sa; + sockaddr_tolwip(to, &sa); + + ret = sendto(socket, dataptr, size, flgs, &sa, tolen); + } + else + { + ret = sendto(socket, dataptr, size, flgs, NULL, tolen); + } + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +int sys_send(int socket, const void *dataptr, size_t size, int flags) +{ + int ret; + int flgs = 0; + + flgs = netflags_muslc_2_lwip(flags); + ret = sendto(socket, dataptr, size, flgs, NULL, 0); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_socket(int domain, int type, int protocol) +{ + int fd = -1; + int nonblock = 0; + /* not support SOCK_CLOEXEC type */ + if (type & SOCK_CLOEXEC) + { + type &= ~SOCK_CLOEXEC; + } + if (type & SOCK_NONBLOCK) + { + nonblock = 1; + type &= ~SOCK_NONBLOCK; + } + + fd = socket(domain, type, protocol); + if (fd < 0) + { + goto out; + } + if (nonblock) + { + fcntl(fd, F_SETFL, O_NONBLOCK); + } + +out: + return (fd < 0 ? GET_ERRNO() : fd); +} + +int sys_closesocket(int socket) +{ + return closesocket(socket); +} + +#endif + +rt_thread_t sys_thread_find(char *name) +{ + return rt_thread_find(name); +} + +rt_tick_t sys_tick_get(void) +{ + return rt_tick_get(); +} + +rt_err_t sys_thread_mdelay(rt_int32_t ms) +{ + return rt_thread_mdelay(ms); +} + +struct k_sigaction { + void (*handler)(int); + unsigned long flags; + void (*restorer)(void); + unsigned mask[2]; +}; + +int sys_sigaction(int sig, const struct k_sigaction *act, + struct k_sigaction *oact, size_t sigsetsize) +{ + int ret = -RT_EINVAL; + struct lwp_sigaction kact, *pkact = RT_NULL; + struct lwp_sigaction koact, *pkoact = RT_NULL; + + if (!sigsetsize) + { + SET_ERRNO(EINVAL); + goto out; + } + if (sigsetsize > sizeof(lwp_sigset_t)) + { + sigsetsize = sizeof(lwp_sigset_t); + } + if (!act && !oact) + { + SET_ERRNO(EINVAL); + goto out; + } + if (oact) + { + if (!lwp_user_accessable((void *)oact, sizeof(*oact))) + { + SET_ERRNO(EFAULT); + goto out; + } + pkoact = &koact; + } + if (act) + { + if (!lwp_user_accessable((void *)act, sizeof(*act))) + { + SET_ERRNO(EFAULT); + goto out; + } + kact.sa_flags = act->flags; + kact.__sa_handler._sa_handler = act->handler; + memcpy(&kact.sa_mask, &act->mask, sigsetsize); + kact.sa_restorer = act->restorer; + pkact = &kact; + } + + ret = lwp_sigaction(sig, pkact, pkoact, sigsetsize); +#ifdef ARCH_MM_MMU + if (ret == 0 && oact) + { + lwp_put_to_user(&oact->handler, &pkoact->__sa_handler._sa_handler, sizeof(void (*)(int))); + lwp_put_to_user(&oact->mask, &pkoact->sa_mask, sigsetsize); + lwp_put_to_user(&oact->flags, &pkoact->sa_flags, sizeof(int)); + lwp_put_to_user(&oact->restorer, &pkoact->sa_restorer, sizeof(void (*)(void))); + } +#endif /* ARCH_MM_MMU */ +out: + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_sigprocmask(int how, const sigset_t *sigset, sigset_t *oset, size_t size) +{ + int ret = -1; + lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; +#ifdef ARCH_MM_MMU + lwp_sigset_t newset, oldset; +#endif /* ARCH_MM_MMU*/ + + if (!size) + { + return -EINVAL; + } + if (!oset && !sigset) + { + return -EINVAL; + } + if (size > sizeof(lwp_sigset_t)) + { + size = sizeof(lwp_sigset_t); + } + if (oset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = &oldset; +#else + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = (lwp_sigset_t *)oset; +#endif + } + if (sigset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + lwp_get_from_user(&newset, (void *)sigset, size); + pnewset = &newset; +#else + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + pnewset = (lwp_sigset_t *)sigset; +#endif /* ARCH_MM_MMU */ + } + ret = lwp_sigprocmask(how, pnewset, poldset); +#ifdef ARCH_MM_MMU + if (ret < 0) + { + return ret; + } + if (oset) + { + lwp_put_to_user(oset, poldset, size); + } +#endif /* ARCH_MM_MMU */ + return (ret < 0 ? -EFAULT: ret); +} + +int sys_tkill(int tid, int sig) +{ +#ifdef ARCH_MM_MMU + rt_base_t level; + rt_thread_t thread; + int ret; + + level = rt_hw_interrupt_disable(); + thread = lwp_tid_get_thread(tid); + ret = lwp_thread_kill(thread, sig); + rt_hw_interrupt_enable(level); + return ret; +#else + return lwp_thread_kill((rt_thread_t)tid, sig); +#endif +} + +int sys_thread_sigprocmask(int how, const lwp_sigset_t *sigset, lwp_sigset_t *oset, size_t size) +{ + int ret = -1; + lwp_sigset_t *pnewset = RT_NULL, *poldset = RT_NULL; +#ifdef ARCH_MM_MMU + lwp_sigset_t newset, oldset; +#endif /* ARCH_MM_MMU */ + + if (!size) + { + return -EINVAL; + } + if (!oset && !sigset) + { + return -EINVAL; + } + if (size != sizeof(lwp_sigset_t)) + { + return -EINVAL; + } + if (oset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = &oldset; +#else + if (!lwp_user_accessable((void *)oset, size)) + { + return -EFAULT; + } + poldset = oset; +#endif + } + if (sigset) + { +#ifdef ARCH_MM_MMU + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + lwp_get_from_user(&newset, (void *)sigset, sizeof(lwp_sigset_t)); + pnewset = &newset; +#else + if (!lwp_user_accessable((void *)sigset, size)) + { + return -EFAULT; + } + pnewset = (lwp_sigset_t *)sigset; +#endif + } + ret = lwp_thread_sigprocmask(how, pnewset, poldset); + if (ret < 0) + { + return ret; + } +#ifdef ARCH_MM_MMU + if (oset) + { + lwp_put_to_user(oset, poldset, sizeof(lwp_sigset_t)); + } +#endif + return (ret < 0 ? -EFAULT: ret); +} + +#ifndef ARCH_MM_MMU +int sys_lwp_sighandler_set(int sig, lwp_sighandler_t func) +{ + if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) + { + return -EFAULT; + } + + lwp_sighandler_set(sig, func); + return 0; +} + +int sys_thread_sighandler_set(int sig, lwp_sighandler_t func) +{ + if (!lwp_user_accessable((void *)func, sizeof(lwp_sighandler_t))) + { + return -EFAULT; + } + + lwp_thread_sighandler_set(sig, func); + return 0; +} +#endif /* not defined ARCH_MM_MMU */ + +int32_t sys_waitpid(int32_t pid, int *status, int options) +{ + int ret = -1; +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)status, sizeof(int))) + { + return -EFAULT; + } + else + { + ret = waitpid(pid, status, options); + } +#else + if (!lwp_user_accessable((void *)status, sizeof(int))) + { + return -EFAULT; + } + ret = waitpid(pid, status, options); +#endif + return ret; +} + +#if defined(RT_USING_SAL) && defined(SAL_USING_POSIX) +struct musl_addrinfo +{ + int ai_flags; + int ai_family; + int ai_socktype; + int ai_protocol; + socklen_t ai_addrlen; + + struct musl_sockaddr *ai_addr; + char *ai_canonname; + + struct musl_addrinfo *ai_next; +}; + +int sys_getaddrinfo(const char *nodename, + const char *servname, + const struct musl_addrinfo *hints, + struct musl_addrinfo *res) +{ + int ret = -1; + struct addrinfo *k_res = NULL; + char *k_nodename = NULL; + char *k_servname = NULL; + struct addrinfo *k_hints = NULL; +#ifdef RT_USING_USERSPACE + int err; +#endif + +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)res, sizeof(*res))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + if (nodename) + { +#ifdef RT_USING_USERSPACE + lwp_user_strlen(nodename, &err); + if (err) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + k_nodename = rt_strdup(nodename); + if (!k_nodename) + { + SET_ERRNO(ENOMEM); + goto exit; + } + } + if (servname) + { +#ifdef RT_USING_USERSPACE + lwp_user_strlen(servname, &err); + if (err) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + k_servname = rt_strdup(servname); + if (!k_servname) + { + SET_ERRNO(ENOMEM); + goto exit; + } + } + + if (hints) + { +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)hints, sizeof(*hints))) + { + SET_ERRNO(EFAULT); + goto exit; + } +#endif + k_hints = (struct addrinfo *) rt_malloc(sizeof *hints); + if (!k_hints) + { + SET_ERRNO(ENOMEM); + goto exit; + } + + rt_memset(k_hints, 0x0, sizeof(struct addrinfo)); + k_hints->ai_flags = hints->ai_flags; + k_hints->ai_family = hints->ai_family; + k_hints->ai_socktype = hints->ai_socktype; + k_hints->ai_protocol = hints->ai_protocol; + k_hints->ai_addrlen = hints->ai_addrlen; + } + + ret = sal_getaddrinfo(k_nodename, k_servname, k_hints, &k_res); + if (ret == 0) + { + /* set sockaddr */ + sockaddr_tomusl(k_res->ai_addr, res->ai_addr); + res->ai_addrlen = k_res->ai_addrlen; + + /* set up addrinfo */ + res->ai_family = k_res->ai_family; + res->ai_flags = k_res->ai_flags; + res->ai_next = NULL; + + if (hints != NULL) + { + /* copy socktype & protocol from hints if specified */ + res->ai_socktype = hints->ai_socktype; + res->ai_protocol = hints->ai_protocol; + } + + sal_freeaddrinfo(k_res); + k_res = NULL; + } + +exit: + if (k_nodename) + { + rt_free(k_nodename); + } + if (k_servname) + { + rt_free(k_servname); + } + if (k_hints) + { + rt_free(k_hints); + } + + return (ret < 0 ? GET_ERRNO() : ret); +} + +#define HOSTENT_BUFSZ 512 +int sys_gethostbyname2_r(const char *name, int af, struct hostent *ret, + char *buf, size_t buflen, + struct hostent **result, int *err) +{ + int ret_val = -1; + int sal_ret = -1 , sal_err = -1; + struct hostent sal_he; + struct hostent *sal_result = NULL; + char *sal_buf = NULL; + char *k_name = NULL; + int a_err = 0; + +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)err, sizeof(*err))) + { + SET_ERRNO(EFAULT); + goto __exit; + } + + if (!lwp_user_accessable((void *)result, sizeof(*result)) + || !lwp_user_accessable((void *)ret, sizeof(*ret)) + || !lwp_user_accessable((void *)buf, buflen)) + { + /* not all arguments given */ + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } + + lwp_user_strlen(name, &a_err); + if (a_err) + { + *err = EFAULT; + SET_ERRNO(EFAULT); + goto __exit; + } +#endif + + *result = ret; + sal_buf = (char *)malloc(HOSTENT_BUFSZ); + if (sal_buf == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + k_name = rt_strdup(name); + if (k_name == NULL) + { + SET_ERRNO(ENOMEM); + goto __exit; + } + + /* get host by name in SAL */ + sal_ret = sal_gethostbyname_r(k_name, &sal_he, sal_buf, HOSTENT_BUFSZ, &sal_result, &sal_err); + if (sal_ret == 0) + { + int index = 0, cnt = 0; + char *ptr = buf; + + /* get counter */ + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + index++; + } + cnt = index + 1; + + /* update user space hostent */ + ret->h_addrtype = sal_he.h_addrtype; + ret->h_length = sal_he.h_length; + + rt_strncpy(ptr, k_name, buflen - (ptr - buf)); + ret->h_name = ptr; + ptr += rt_strlen(k_name); + + ret->h_addr_list = (char**)ptr; + ptr += cnt * sizeof(char *); + + index = 0; + while (sal_he.h_addr_list[index] != NULL) + { + ret->h_addr_list[index] = ptr; + rt_memcpy(ptr, sal_he.h_addr_list[index], sal_he.h_length); + + ptr += sal_he.h_length; + index++; + } + ret->h_addr_list[index] = NULL; + } + + ret_val = 0; + +__exit: + /* release buffer */ + if (sal_buf) + { + free(sal_buf); + } + if (k_name) + { + free(k_name); + } + + return (ret_val < 0 ? GET_ERRNO() : ret_val); +} +#endif + +char *sys_getcwd(char *buf, size_t size) +{ + if (!lwp_user_accessable((void *)buf, size)) + { + return RT_NULL; + } + getcwd(buf, size); + + return (char *)strlen(buf); +} + +int sys_chdir(const char *path) +{ +#ifdef RT_USING_USERSPACE + int err = 0; + + lwp_user_strlen(path, &err); + if (err) + { + return -EFAULT; + } + err = chdir(path); + return (err < 0 ? GET_ERRNO() : err); +#else + int ret = chdir(path); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +int sys_mkdir(const char *path, mode_t mode) +{ +#ifdef RT_USING_USERSPACE + int err = 0; + + lwp_user_strlen(path, &err); + if (err) + { + return -EFAULT; + } + err = mkdir(path, mode); + return (err < 0 ? GET_ERRNO() : err); +#else + int ret = mkdir(path, mode); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +int sys_rmdir(const char *path) +{ +#ifdef RT_USING_USERSPACE + int err = 0; + + lwp_user_strlen(path, &err); + if (err) + { + return -EFAULT; + } + err = unlink(path); + return (err < 0 ? GET_ERRNO() : err); +#else + int ret = unlink(path); + return (ret < 0 ? GET_ERRNO() : ret); +#endif +} + +#ifdef RT_USING_MUSL +typedef uint64_t ino_t; +#endif +struct libc_dirent { + ino_t d_ino; + off_t d_off; + unsigned short d_reclen; + unsigned char d_type; + char d_name[256]; +}; +int sys_getdents(int fd, struct libc_dirent *dirp, size_t nbytes) +{ + int ret = -1; + struct dfs_fd *dfs_fd; + size_t cnt = (nbytes / sizeof(struct libc_dirent)); + size_t rtt_nbytes = 0; + struct dirent *rtt_dirp; + +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)dirp, sizeof(struct libc_dirent))) + { + return -EFAULT; + } +#endif + + if (cnt == 0) + { + return -EINVAL; + } + rtt_nbytes = cnt * sizeof(struct dirent); + rtt_dirp = (struct dirent *)rt_malloc(rtt_nbytes); + if (!rtt_dirp) + { + return -ENOMEM; + } + dfs_fd = fd_get(fd); + ret = dfs_file_getdents(dfs_fd, rtt_dirp, rtt_nbytes); + if (ret > 0) + { + size_t i = 0; + cnt = ret / sizeof(struct dirent); + for (i = 0; i < cnt; i++) + { + dirp[i].d_ino = 0; + dirp[i].d_off = i*sizeof(struct libc_dirent); + dirp[i].d_type = rtt_dirp[i].d_type; + dirp[i].d_reclen = sizeof(struct libc_dirent); + strcpy(dirp[i].d_name, rtt_dirp[i].d_name); + } + ret = cnt * sizeof(struct libc_dirent); + } + rt_free(rtt_dirp); + return (ret < 0 ? GET_ERRNO() : ret); +} + +rt_err_t sys_get_errno(void) +{ + return rt_get_errno(); +} +#ifdef ARCH_MM_MMU +int sys_set_thread_area(void *p) +{ + rt_thread_t thread; + + thread = rt_thread_self(); + thread->thread_idr = p; + arch_set_thread_area(p); + + return 0; +} + +int sys_set_tid_address(int *tidptr) +{ + rt_thread_t thread; + +#ifdef RT_USING_USERSPACE + if (!lwp_user_accessable((void *)tidptr, sizeof(int))) + { + return -EFAULT; + } +#endif + thread = rt_thread_self(); + thread->clear_child_tid = tidptr; + return thread->tid; +} +#endif /* ARCH_MM_MMU */ + +int sys_gettid(void) +{ + return rt_thread_self()->tid; +} + +int sys_access(const char *filename, int mode) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + rt_size_t len = 0; + char *kname = RT_NULL; + int a_err = 0; + + lwp_user_strlen(filename, &a_err); + if (a_err) + { + return -EFAULT; + } + + len = rt_strlen(filename); + if (!len) + { + return -EINVAL; + } + + kname = (char *)kmem_get(len + 1); + if (!ret && !kname) + { + return -ENOMEM; + } + + if (!ret) + { + lwp_get_from_user(kname, (void *)filename, len + 1); + ret = access(kname, mode); + kmem_put(kname); + } +#else + ret = access(filename, mode); +#endif + + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_pipe(int fd[2]) +{ + int ret; + if (!lwp_user_accessable((void *)fd, sizeof(int[2]))) + { + return -EFAULT; + } + ret = pipe(fd); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_clock_settime(clockid_t clk, const struct timespec *ts) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + size_t size = sizeof(struct timespec); + struct timespec *kts = NULL; + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + kts = kmem_get(size); + if (!kts) + { + return -ENOMEM; + } + + lwp_get_from_user(kts, (void *)ts, size); + ret = clock_settime(clk, kts); + kmem_put(kts); +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_settime(clk, ts); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_clock_gettime(clockid_t clk, struct timespec *ts) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + size_t size = sizeof(struct timespec); + struct timespec *kts = NULL; + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + kts = kmem_get(size); + if (!kts) + { + return -ENOMEM; + } + + ret = clock_gettime(clk, kts); + if (ret != -1) + lwp_put_to_user(ts, kts, size); + + kmem_put(kts); +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_gettime(clk, ts); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_clock_nanosleep(clockid_t clk, int flags, const struct timespec *rqtp, struct timespec *rmtp) +{ + int ret = 0; + dbg_log(DBG_LOG, "sys_nanosleep\n"); + if (!lwp_user_accessable((void *)rqtp, sizeof *rqtp)) + return -EFAULT; + +#ifdef RT_USING_USERSPACE + struct timespec rqtp_k; + struct timespec rmtp_k; + + lwp_get_from_user(&rqtp_k, (void *)rqtp, sizeof rqtp_k); + ret = clock_nanosleep(clk, flags, &rqtp_k, &rmtp_k); + if ((ret != -1 || rt_get_errno() == EINTR) && rmtp && lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + { + lwp_put_to_user(rmtp, (void *)&rmtp_k, sizeof rmtp_k); + return -EINTR; + } +#else + if (rmtp) + { + if (!lwp_user_accessable((void *)rmtp, sizeof *rmtp)) + return -EFAULT; + ret = clock_nanosleep(clk, flags, rqtp, rmtp); + } +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_clock_getres(clockid_t clk, struct timespec *ts) +{ + int ret = 0; +#ifdef RT_USING_USERSPACE + struct timespec kts; + size_t size = sizeof(struct timespec); + + if (!lwp_user_accessable((void *)ts, size)) + { + return -EFAULT; + } + + ret = clock_getres(clk, &kts); + + if (ret != -1) + lwp_put_to_user(ts, &kts, size); +#else + if (!lwp_user_accessable((void *)ts, sizeof(struct timespec))) + { + return -EFAULT; + } + ret = clock_getres(clk, ts); +#endif + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_rename(const char *oldpath, const char *newpath) +{ + int ret = -1; +#ifdef RT_USING_USERSPACE + int err; + + lwp_user_strlen(oldpath, &err); + if (err) + { + return -EFAULT; + } + + lwp_user_strlen(newpath, &err); + if (err) + { + return -EFAULT; + } +#endif + ret = rename(oldpath, newpath); + return (ret < 0 ? GET_ERRNO() : ret); +} + +typedef unsigned long long rlim_t; + +struct rlimit { + rlim_t rlim_cur; + rlim_t rlim_max; +}; + +#define RLIMIT_CPU 0 +#define RLIMIT_FSIZE 1 +#define RLIMIT_DATA 2 +#define RLIMIT_STACK 3 +#define RLIMIT_CORE 4 +#define RLIMIT_RSS 5 +#define RLIMIT_NPROC 6 +#define RLIMIT_NOFILE 7 +#define RLIMIT_MEMLOCK 8 +#define RLIMIT_AS 9 + +int sys_prlimit64(pid_t pid, + unsigned int resource, + const struct rlimit *new_rlim, + struct rlimit *old_rlim) { return -ENOSYS; } +int sys_getrlimit(unsigned int resource, unsigned long rlim[2]) +{ + int ret = -1; + + if (!lwp_user_accessable((void *)rlim, sizeof(unsigned long [2]))) + { + return -EFAULT; + } + switch (resource) + { + case RLIMIT_NOFILE: + { + struct dfs_fdtable *fdt = dfs_fdtable_get(); + + dfs_fd_lock(); + rlim[0] = fdt->maxfd; + dfs_fd_unlock(); + rlim[1] = DFS_FD_MAX; + ret = 0; + } + break; + default: + return -EINVAL; + break; + } + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_setrlimit(unsigned int resource, struct rlimit *rlim) +{ + return -ENOSYS; +} + +int sys_setsid(void) +{ + int ret = 0; + ret = setsid(); + return (ret < 0 ? GET_ERRNO() : ret); +} + +int sys_getrandom(void *buf, size_t buflen, unsigned int flags) +{ + int ret = -1; + int count = 0; + void *kmem = RT_NULL; + rt_device_t rd_dev = RT_NULL; + + if (flags & GRND_RANDOM) + rd_dev = rt_device_find("random"); + else + rd_dev = rt_device_find("urandom"); + + if (rd_dev == RT_NULL) + { + return -EFAULT; + } + + if (rt_device_open(rd_dev, RT_DEVICE_OFLAG_RDONLY) != RT_EOK) + { + return -EFAULT; + } + + if (!lwp_user_accessable(buf, buflen)) + { + rt_device_close(rd_dev); + return -EFAULT; + } + +#ifdef RT_USING_USERSPACE + kmem = kmem_get(buflen); + if (!kmem) + { + rt_device_close(rd_dev); + return -ENOMEM; + } + + while (count < buflen) + { + ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); + if (ret <= 0) + break; + count += ret; + } + rt_device_close(rd_dev); + + ret = count; + if (count > 0) + { + ret = lwp_put_to_user(buf, kmem, count); + } + kmem_put(kmem); +#else + while (count < buflen) + { + ret = rt_device_read(rd_dev, count, (char *)kmem + count, buflen - count); + if (ret <= 0) + break; + count += ret; + } + rt_device_close(rd_dev); + + ret = count; +#endif + return ret; +} +int sys_setaffinity(pid_t pid, size_t size, void *set) +{ + if (!lwp_user_accessable(set, sizeof(cpu_set_t))) + { + return -EFAULT; + } + for (int i = 0;i < size * 8; i++) + { + if (CPU_ISSET(i, (cpu_set_t *)set)) + { + return lwp_setaffinity(pid, i); + } + } + return -1; +} + +int sys_sched_setparam(pid_t pid, void *param) +{ + struct sched_param *sched_param = (struct sched_param *)param; + struct rt_lwp *lwp = NULL; + rt_thread_t main_thread; + int ret = -1; + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + if (pid > 0) + { + lwp = lwp_from_pid(pid); + } + else if (pid == 0) + { + lwp = lwp_self(); + } + if (lwp) + { + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + return rt_thread_control(main_thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority); + } + return ret; +} + +int sys_sched_getparam(pid_t pid, void *param) +{ + struct sched_param *sched_param = (struct sched_param *)param; + struct rt_lwp *lwp = NULL; + rt_thread_t main_thread; + int ret = -1; + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + if (pid > 0) + { + lwp = lwp_from_pid(pid); + } + else if (pid == 0) + { + lwp = lwp_self(); + } + if (lwp) + { + main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); + sched_param->sched_priority = main_thread->current_priority; + ret = 0; + } + return ret; +} + +int sys_sched_get_priority_max(int policy) +{ + if(policy < 0) + { + rt_set_errno(EINVAL); + return -rt_get_errno(); + } + return RT_THREAD_PRIORITY_MAX; +} + +int sys_sched_get_priority_min(int policy) +{ + if(policy < 0) + { + rt_set_errno(EINVAL); + return -rt_get_errno(); + } + return 0; +} + +int sys_sched_setscheduler(int tid, int policy, void *param) +{ + struct sched_param *sched_param = (struct sched_param *)param; + rt_thread_t thread = lwp_tid_get_thread(tid); + + if (!lwp_user_accessable(param, sizeof(struct sched_param))) + { + return -EFAULT; + } + return rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, (void *)&sched_param->sched_priority); + return 0; +} + +int sys_sched_getscheduler(int tid, int *policy, void *param) +{ + struct sched_param *sched_param = (struct sched_param *)param; + rt_thread_t thread = lwp_tid_get_thread(tid); + if (!lwp_user_accessable(sched_param, sizeof(struct sched_param))) + { + return -EFAULT; + } + sched_param->sched_priority = thread->current_priority; + *policy = 0; + return 0; +} + +int sys_fsync(int fd) +{ + int res = fsync(fd); + if (res < 0) + res = rt_get_errno(); + return res; +} + const static void* func_table[] = { - (void *)sys_exit, // 0x01 - (void *)sys_read, // 0x02 - (void *)sys_write, // 0x03 - (void *)sys_lseek, // 0x04 - (void *)sys_open, // 0x05 - (void *)sys_close, // 0x06 - (void *)sys_ioctl, // 0x07 + SYSCALL_SIGN(sys_exit), /* 01 */ + SYSCALL_SIGN(sys_read), + SYSCALL_SIGN(sys_write), + SYSCALL_SIGN(sys_lseek), + SYSCALL_SIGN(sys_open), /* 05 */ + SYSCALL_SIGN(sys_close), + SYSCALL_SIGN(sys_ioctl), + SYSCALL_SIGN(sys_fstat), + SYSCALL_SIGN(sys_poll), + SYSCALL_SIGN(sys_nanosleep), /* 10 */ + SYSCALL_SIGN(sys_gettimeofday), + SYSCALL_SIGN(sys_settimeofday), + SYSCALL_SIGN(sys_exec), + SYSCALL_SIGN(sys_kill), + SYSCALL_SIGN(sys_getpid), /* 15 */ + SYSCALL_SIGN(sys_getpriority), + SYSCALL_SIGN(sys_setpriority), + SYSCALL_SIGN(sys_sem_create), + SYSCALL_SIGN(sys_sem_delete), + SYSCALL_SIGN(sys_sem_take), /* 20 */ + SYSCALL_SIGN(sys_sem_release), + SYSCALL_SIGN(sys_mutex_create), + SYSCALL_SIGN(sys_mutex_delete), + SYSCALL_SIGN(sys_mutex_take), + SYSCALL_SIGN(sys_mutex_release), /* 25 */ + SYSCALL_SIGN(sys_event_create), + SYSCALL_SIGN(sys_event_delete), + SYSCALL_SIGN(sys_event_send), + SYSCALL_SIGN(sys_event_recv), + SYSCALL_SIGN(sys_mb_create), /* 30 */ + SYSCALL_SIGN(sys_mb_delete), + SYSCALL_SIGN(sys_mb_send), + SYSCALL_SIGN(sys_mb_send_wait), + SYSCALL_SIGN(sys_mb_recv), + SYSCALL_SIGN(sys_mq_create), /* 35 */ + SYSCALL_SIGN(sys_mq_delete), + SYSCALL_SIGN(sys_mq_send), + SYSCALL_SIGN(sys_mq_urgent), + SYSCALL_SIGN(sys_mq_recv), + SYSCALL_SIGN(sys_thread_create), /* 40 */ + SYSCALL_SIGN(sys_thread_delete), + SYSCALL_SIGN(sys_thread_startup), + SYSCALL_SIGN(sys_thread_self), + SYSCALL_SIGN(sys_channel_open), + SYSCALL_SIGN(sys_channel_close), /* 45 */ + SYSCALL_SIGN(sys_channel_send), + SYSCALL_SIGN(sys_channel_send_recv_timeout), + SYSCALL_SIGN(sys_channel_reply), + SYSCALL_SIGN(sys_channel_recv_timeout), + SYSCALL_SIGN(sys_enter_critical), /* 50 */ + SYSCALL_SIGN(sys_exit_critical), - (void *)sys_nanosleep, // 0x08 + SYSCALL_USPACE(SYSCALL_SIGN(sys_brk)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_mmap2)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_munmap)), +#ifdef ARCH_MM_MMU + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmget)), /* 55 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmrm)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmat)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_shmdt)), +#else +#ifdef RT_LWP_USING_SHM + SYSCALL_SIGN(sys_shm_alloc), /* 55 */ + SYSCALL_SIGN(sys_shm_free), + SYSCALL_SIGN(sys_shm_retain), + SYSCALL_SIGN(sys_notimpl), +#else + SYSCALL_SIGN(sys_notimpl), /* 55 */ + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#endif /* RT_LWP_USING_SHM */ +#endif /* ARCH_MM_MMU */ + SYSCALL_SIGN(sys_device_init), + SYSCALL_SIGN(sys_device_register), /* 60 */ + SYSCALL_SIGN(sys_device_control), + SYSCALL_SIGN(sys_device_find), + SYSCALL_SIGN(sys_device_open), + SYSCALL_SIGN(sys_device_close), + SYSCALL_SIGN(sys_device_read), /* 65 */ + SYSCALL_SIGN(sys_device_write), + + SYSCALL_SIGN(sys_stat), + SYSCALL_SIGN(sys_thread_find), - (void *)sys_getpriority, // 0x09 - (void *)sys_setpriority, // 0x0a + SYSCALL_NET(SYSCALL_SIGN(sys_accept)), + SYSCALL_NET(SYSCALL_SIGN(sys_bind)), /* 70 */ + SYSCALL_NET(SYSCALL_SIGN(sys_shutdown)), + SYSCALL_NET(SYSCALL_SIGN(sys_getpeername)), + SYSCALL_NET(SYSCALL_SIGN(sys_getsockname)), + SYSCALL_NET(SYSCALL_SIGN(sys_getsockopt)), + SYSCALL_NET(SYSCALL_SIGN(sys_setsockopt)), /* 75 */ + SYSCALL_NET(SYSCALL_SIGN(sys_connect)), + SYSCALL_NET(SYSCALL_SIGN(sys_listen)), + SYSCALL_NET(SYSCALL_SIGN(sys_recv)), + SYSCALL_NET(SYSCALL_SIGN(sys_recvfrom)), + SYSCALL_NET(SYSCALL_SIGN(sys_send)), /* 80 */ + SYSCALL_NET(SYSCALL_SIGN(sys_sendto)), + SYSCALL_NET(SYSCALL_SIGN(sys_socket)), - (void *)sys_gettimeofday, // 0x0b - (void *)sys_settimeofday, // 0x0c + SYSCALL_NET(SYSCALL_SIGN(sys_closesocket)), + SYSCALL_NET(SYSCALL_SIGN(sys_getaddrinfo)), + SYSCALL_NET(SYSCALL_SIGN(sys_gethostbyname2_r)), /* 85 */ - (void *)sys_malloc, // 0x0d - (void *)sys_free, // 0x0e - (void *)sys_realloc, //0x0f - (void *)sys_fstat, // 0x10 - (void *)poll, // 0x11 + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, /* 90 */ + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, + SYSCALL_SIGN(sys_notimpl), //network, - SYSCALL_NET(accept), // 0x12 - SYSCALL_NET(bind), // 0x13 - SYSCALL_NET(shutdown), // 0x14 - SYSCALL_NET(getpeername),// 0x15 - SYSCALL_NET(getsockname),// 0x16 - SYSCALL_NET(getsockopt), // 0x17 - SYSCALL_NET(setsockopt), // 0x18 - SYSCALL_NET(connect), // 0x19 - SYSCALL_NET(listen), // 0x1a - SYSCALL_NET(recv), // 0x1b - SYSCALL_NET(recvfrom), // 0x1c - SYSCALL_NET(send), // 0x1d - SYSCALL_NET(sendto), // 0x1e - SYSCALL_NET(socket), // 0x1f +#ifdef RT_USING_DFS + SYSCALL_SIGN(sys_select), +#else + SYSCALL_SIGN(sys_notimpl), +#endif - (void *)select, // 0x20 + SYSCALL_SIGN(sys_notimpl), //SYSCALL_SIGN(sys_hw_interrupt_disable), /* 95 */ + SYSCALL_SIGN(sys_notimpl), //SYSCALL_SIGN(sys_hw_interrupt_enable), + + SYSCALL_SIGN(sys_tick_get), + SYSCALL_SIGN(sys_exit_group), + + SYSCALL_SIGN(sys_notimpl), //rt_delayed_work_init, + SYSCALL_SIGN(sys_notimpl), //rt_work_submit, /* 100 */ + SYSCALL_SIGN(sys_notimpl), //rt_wqueue_wakeup, + SYSCALL_SIGN(sys_thread_mdelay), + SYSCALL_SIGN(sys_sigaction), + SYSCALL_SIGN(sys_sigprocmask), + SYSCALL_SIGN(sys_tkill), /* 105 */ + SYSCALL_SIGN(sys_thread_sigprocmask), +#ifdef ARCH_MM_MMU + SYSCALL_SIGN(sys_cacheflush), + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#else + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_lwp_sighandler_set), + SYSCALL_SIGN(sys_thread_sighandler_set), +#endif + SYSCALL_SIGN(sys_waitpid), /* 110 */ + + SYSCALL_SIGN(sys_rt_timer_create), + SYSCALL_SIGN(sys_rt_timer_delete), + SYSCALL_SIGN(sys_rt_timer_start), + SYSCALL_SIGN(sys_rt_timer_stop), + SYSCALL_SIGN(sys_rt_timer_control), /* 115 */ + SYSCALL_SIGN(sys_getcwd), + SYSCALL_SIGN(sys_chdir), + SYSCALL_SIGN(sys_unlink), + SYSCALL_SIGN(sys_mkdir), + SYSCALL_SIGN(sys_rmdir), /* 120 */ + SYSCALL_SIGN(sys_getdents), + SYSCALL_SIGN(sys_get_errno), +#ifdef ARCH_MM_MMU + SYSCALL_SIGN(sys_set_thread_area), + SYSCALL_SIGN(sys_set_tid_address), +#else + SYSCALL_SIGN(sys_notimpl), + SYSCALL_SIGN(sys_notimpl), +#endif + SYSCALL_SIGN(sys_access), /* 125 */ + SYSCALL_SIGN(sys_pipe), + SYSCALL_SIGN(sys_clock_settime), + SYSCALL_SIGN(sys_clock_gettime), + SYSCALL_SIGN(sys_clock_getres), + SYSCALL_USPACE(SYSCALL_SIGN(sys_clone)), /* 130 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_futex)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_pmutex)), + SYSCALL_SIGN(sys_dup), + SYSCALL_SIGN(sys_dup2), + SYSCALL_SIGN(sys_rename), /* 135 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_fork)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_execve)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_vfork)), + SYSCALL_SIGN(sys_gettid), + SYSCALL_SIGN(sys_prlimit64), /* 140 */ + SYSCALL_SIGN(sys_getrlimit), + SYSCALL_SIGN(sys_setrlimit), + SYSCALL_SIGN(sys_setsid), + SYSCALL_SIGN(sys_getrandom), + SYSCALL_SIGN(sys_notimpl), // SYSCALL_SIGN(sys_readlink) /* 145 */ + SYSCALL_USPACE(SYSCALL_SIGN(sys_mremap)), + SYSCALL_USPACE(SYSCALL_SIGN(sys_madvise)), + SYSCALL_SIGN(sys_sched_setparam), + SYSCALL_SIGN(sys_sched_getparam), + SYSCALL_SIGN(sys_sched_get_priority_max), + SYSCALL_SIGN(sys_sched_get_priority_min), + SYSCALL_SIGN(sys_sched_setscheduler), + SYSCALL_SIGN(sys_sched_getscheduler), + SYSCALL_SIGN(sys_setaffinity), + SYSCALL_SIGN(sys_fsync), + SYSCALL_SIGN(sys_clock_nanosleep), + SYSCALL_SIGN(sys_timer_create), + SYSCALL_SIGN(sys_timer_delete), + SYSCALL_SIGN(sys_timer_settime), + SYSCALL_SIGN(sys_timer_gettime), + SYSCALL_SIGN(sys_timer_getoverrun), }; const void *lwp_get_sys_api(rt_uint32_t number) { - const void *func = (const void*)sys_notimpl; + const void *func = (const void *)sys_notimpl; if (number == 0xff) { @@ -271,11 +4408,32 @@ const void *lwp_get_sys_api(rt_uint32_t number) else { number -= 1; - if (number < sizeof(func_table)/sizeof(func_table[0])) + if (number < sizeof(func_table) / sizeof(func_table[0]) / 2) { - func = func_table[number]; + func = func_table[number << 1]; } } return func; } + +const char *lwp_get_syscall_name(rt_uint32_t number) +{ + const char *name = "sys_notimpl"; + + if (number == 0xff) + { + name = "sys_log"; + } + else + { + number -= 1; + if (number < sizeof(func_table) / sizeof(func_table[0]) / 2) + { + name = (char*)func_table[(number << 1) + 1]; + } + } + + // skip sys_ + return name + 4; +} diff --git a/components/lwp/lwp_syscall.h b/components/lwp/lwp_syscall.h index 749406c1ca..3b4e0f7ac1 100644 --- a/components/lwp/lwp_syscall.h +++ b/components/lwp/lwp_syscall.h @@ -1,11 +1,11 @@ /* - * Copyright (c) 2006-2021, RT-Thread Development Team + * Copyright (c) 2006-2020, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * * Change Logs: * Date Author Notes - * 2018-12-10 Jesven fix complie error in iar and keil + * 2019-11-12 Jesven the first version */ #ifndef __LWP_SYSCALL_H__ @@ -15,11 +15,16 @@ #include <rtthread.h> #include <dfs_file.h> #include <unistd.h> +#include <stdio.h> /* rename() */ #include <sys/stat.h> -#include <sys/statfs.h> +#include <sys/statfs.h> /* statfs() */ +#include <poll.h> #include <sys/time.h> #include <sys/types.h> -#include <sys/ioctl.h> + +#ifdef __cplusplus +extern "C" { +#endif typedef long suseconds_t; /* microseconds (signed) */ typedef uint32_t id_t; /* may contain pid, uid or gid */ @@ -27,26 +32,15 @@ typedef uint32_t id_t; /* may contain pid, uid or gid */ /* * Process priority specifications to get/setpriority. */ -#define PRIO_MIN (-20) -#define PRIO_MAX 20 +#define PRIO_MIN (-20) +#define PRIO_MAX 20 -#define PRIO_PROCESS 0 /* only support lwp process */ -#define PRIO_PGRP 1 -#define PRIO_USER 2 +#define PRIO_PROCESS 0 /* only support lwp process */ +#define PRIO_PGRP 1 +#define PRIO_USER 2 -#ifndef TIMEVAL_TO_TIMESPEC -#define TIMEVAL_TO_TIMESPEC(tv, ts) { \ - (ts)->tv_sec = (tv)->tv_sec; \ - (ts)->tv_nsec = (tv)->tv_usec * 1000; \ -} -#endif - -#ifndef TIMESPEC_TO_TIMEVAL -#define TIMESPEC_TO_TIMEVAL(tv, ts) { \ - (tv)->tv_sec = (ts)->tv_sec; \ - (tv)->tv_usec = (ts)->tv_nsec / 1000; \ -} -#endif +const char *lwp_get_syscall_name(rt_uint32_t number); +const void *lwp_get_sys_api(rt_uint32_t number); void sys_exit(int value); ssize_t sys_read(int fd, void *buf, size_t nbyte); @@ -54,15 +48,56 @@ ssize_t sys_write(int fd, const void *buf, size_t nbyte); off_t sys_lseek(int fd, off_t offset, int whence); int sys_open(const char *name, int mode, ...); int sys_close(int fd); +int sys_ioctl(int fd, unsigned long cmd, void* data); +int sys_fstat(int file, struct stat *buf); +int sys_poll(struct pollfd *fds, nfds_t nfds, int timeout); int sys_nanosleep(const struct timespec *rqtp, struct timespec *rmtp); -int sys_getpriority(int which, id_t who); -int sys_setpriority(int which, id_t who, int prio); int sys_gettimeofday(struct timeval *tp, struct timezone *tzp); int sys_settimeofday(const struct timeval *tv, const struct timezone *tzp); -int sys_msgget(key_t key, int msgflg); -int sys_msgsend(int msqid, const void *msgp, size_t msgsz, int msgflg); -int sys_msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); +int sys_exec(char *filename, int argc, char **argv, char **envp); +int sys_kill(int pid, int sig); +int sys_getpid(void); +int sys_getpriority(int which, id_t who); +int sys_setpriority(int which, id_t who, int prio); +rt_sem_t sys_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag); +rt_err_t sys_sem_delete(rt_sem_t sem); +rt_err_t sys_sem_take(rt_sem_t sem, rt_int32_t time); +rt_err_t sys_sem_release(rt_sem_t sem); +rt_mutex_t sys_mutex_create(const char *name, rt_uint8_t flag); +rt_err_t sys_mutex_delete(rt_mutex_t mutex); +rt_err_t sys_mutex_take(rt_mutex_t mutex, rt_int32_t time); +rt_err_t sys_mutex_release(rt_mutex_t mutex); +rt_event_t sys_event_create(const char *name, rt_uint8_t flag); +rt_err_t sys_event_delete(rt_event_t event); +rt_err_t sys_event_send(rt_event_t event, rt_uint32_t set); +rt_err_t sys_event_recv(rt_event_t event, rt_uint32_t set, rt_uint8_t opt, rt_int32_t timeout, rt_uint32_t *recved); +rt_mailbox_t sys_mb_create(const char *name, rt_size_t size, rt_uint8_t flag); +rt_err_t sys_mb_delete(rt_mailbox_t mb); +rt_err_t sys_mb_send(rt_mailbox_t mb, rt_ubase_t value); +rt_err_t sys_mb_send_wait(rt_mailbox_t mb, rt_ubase_t value, rt_int32_t timeout); +rt_err_t sys_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout); +rt_mq_t sys_mq_create(const char *name, rt_size_t msg_size, rt_size_t max_msgs, rt_uint8_t flag); +rt_err_t sys_mq_delete(rt_mq_t mq); +rt_err_t sys_mq_send(rt_mq_t mq, void *buffer, rt_size_t size); +rt_err_t sys_mq_urgent(rt_mq_t mq, void *buffer, rt_size_t size); +rt_err_t sys_mq_recv(rt_mq_t mq, void *buffer, rt_size_t size, rt_int32_t timeout); +rt_thread_t sys_thread_create(void *arg[]); +rt_err_t sys_thread_delete(rt_thread_t thread); +rt_err_t sys_thread_startup(rt_thread_t thread); +rt_thread_t sys_thread_self(void); +int sys_channel_open(const char *name, int flags); +rt_err_t sys_channel_close(int fd); +rt_err_t sys_channel_send(int fd, rt_channel_msg_t data); +rt_err_t sys_channel_send_recv(int fd, rt_channel_msg_t data, rt_channel_msg_t data_ret); +rt_err_t sys_channel_reply(int fd, rt_channel_msg_t data); +rt_err_t sys_channel_recv(int fd, rt_channel_msg_t data); +void sys_enter_critical(void); +void sys_exit_critical(void); int sys_log(const char* log, int size); +#ifdef __cplusplus +} +#endif + #endif diff --git a/components/lwp/lwp_tid.c b/components/lwp/lwp_tid.c new file mode 100644 index 0000000000..0a77c5308c --- /dev/null +++ b/components/lwp/lwp_tid.c @@ -0,0 +1,132 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-01-15 shaojinchun first version + */ + +#include <rthw.h> +#include <rtthread.h> + +#include "lwp.h" + +#ifdef RT_USING_USERSPACE +#include "lwp_user_mm.h" +#endif + +#define DBG_TAG "LWP_TID" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#define TID_MAX 10000 + +#define TID_CT_ASSERT(name, x) \ + struct assert_##name {char ary[2 * (x) - 1];} + +TID_CT_ASSERT(tid_min_nr, LWP_TID_MAX_NR > 1); +TID_CT_ASSERT(tid_max_nr, LWP_TID_MAX_NR < TID_MAX); + +static struct lwp_avl_struct lwp_tid_ary[LWP_TID_MAX_NR]; +static struct lwp_avl_struct *lwp_tid_free_head = RT_NULL; +static int lwp_tid_ary_alloced = 0; +static struct lwp_avl_struct *lwp_tid_root = RT_NULL; +static int current_tid = 0; + +int lwp_tid_get(void) +{ + rt_base_t level; + struct lwp_avl_struct *p; + int tid = 0; + + level = rt_hw_interrupt_disable(); + p = lwp_tid_free_head; + if (p) + { + lwp_tid_free_head = (struct lwp_avl_struct *)p->avl_right; + } + else if (lwp_tid_ary_alloced < LWP_TID_MAX_NR) + { + p = lwp_tid_ary + lwp_tid_ary_alloced; + lwp_tid_ary_alloced++; + } + if (p) + { + int found_noused = 0; + + RT_ASSERT(p->data == RT_NULL); + for (tid = current_tid + 1; tid < TID_MAX; tid++) + { + if (!lwp_avl_find(tid, lwp_tid_root)) + { + found_noused = 1; + break; + } + } + if (!found_noused) + { + for (tid = 1; tid <= current_tid; tid++) + { + if (!lwp_avl_find(tid, lwp_tid_root)) + { + found_noused = 1; + break; + } + } + } + p->avl_key = tid; + lwp_avl_insert(p, &lwp_tid_root); + current_tid = tid; + } + rt_hw_interrupt_enable(level); + return tid; +} + +void lwp_tid_put(int tid) +{ + rt_base_t level; + struct lwp_avl_struct *p; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(tid, lwp_tid_root); + if (p) + { + p->data = RT_NULL; + lwp_avl_remove(p, &lwp_tid_root); + p->avl_right = lwp_tid_free_head; + lwp_tid_free_head = p; + } + rt_hw_interrupt_enable(level); +} + +rt_thread_t lwp_tid_get_thread(int tid) +{ + rt_base_t level; + struct lwp_avl_struct *p; + rt_thread_t thread = RT_NULL; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(tid, lwp_tid_root); + if (p) + { + thread = (rt_thread_t)p->data; + } + rt_hw_interrupt_enable(level); + return thread; +} + +void lwp_tid_set_thread(int tid, rt_thread_t thread) +{ + rt_base_t level; + struct lwp_avl_struct *p; + + level = rt_hw_interrupt_disable(); + p = lwp_avl_find(tid, lwp_tid_root); + if (p) + { + p->data = thread; + } + rt_hw_interrupt_enable(level); +} diff --git a/components/lwp/lwp_user_mm.c b/components/lwp/lwp_user_mm.c new file mode 100644 index 0000000000..44e2a3a96a --- /dev/null +++ b/components/lwp/lwp_user_mm.c @@ -0,0 +1,623 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + * 2021-02-06 lizhirui fixed fixed vtable size problem + * 2021-02-12 lizhirui add 64-bit support for lwp_brk + * 2021-02-19 lizhirui add riscv64 support for lwp_user_accessable and lwp_get_from_user + * 2021-06-07 lizhirui modify user space bound check + */ + +#include <rtthread.h> +#include <rthw.h> + +#ifdef RT_USING_USERSPACE + +#include <mmu.h> +#include <page.h> +#include <lwp_mm_area.h> +#include <lwp_user_mm.h> +#include <lwp_arch.h> +#include <lwp_mm.h> + +int lwp_user_space_init(struct rt_lwp *lwp) +{ + return arch_user_space_init(lwp); +} + +#ifdef LWP_ENABLE_ASID +void rt_hw_mmu_switch(void *mtable, unsigned int pid, unsigned int asid); +#else +void rt_hw_mmu_switch(void *mtable); +#endif +void *rt_hw_mmu_tbl_get(void); +void lwp_mmu_switch(struct rt_thread *thread) +{ + struct rt_lwp *l = RT_NULL; + void *pre_mmu_table = RT_NULL, *new_mmu_table = RT_NULL; + + if (thread->lwp) + { + l = (struct rt_lwp *)thread->lwp; + new_mmu_table = (void *)((char *)l->mmu_info.vtable + l->mmu_info.pv_off); + } + else + { + new_mmu_table = arch_kernel_mmu_table_get(); + } + + pre_mmu_table = rt_hw_mmu_tbl_get(); + if (pre_mmu_table != new_mmu_table) + { +#ifdef LWP_ENABLE_ASID + rt_hw_mmu_switch(new_mmu_table, l ? l->pid : 0, arch_get_asid(l)); +#else + rt_hw_mmu_switch(new_mmu_table); +#endif + } +} + +static void unmap_range(struct rt_lwp *lwp, void *addr, size_t size, int pa_need_free) +{ + void *va = RT_NULL, *pa = RT_NULL; + int i = 0; + + for (va = addr, i = 0; i < size; va = (void *)((char *)va + ARCH_PAGE_SIZE), i += ARCH_PAGE_SIZE) + { + pa = rt_hw_mmu_v2p(&lwp->mmu_info, va); + if (pa) + { + rt_hw_mmu_unmap(&lwp->mmu_info, va, ARCH_PAGE_SIZE); + if (pa_need_free) + { + rt_pages_free((void *)((char *)pa - PV_OFFSET), 0); + } + } + } +} + +void lwp_unmap_user_space(struct rt_lwp *lwp) +{ + struct lwp_avl_struct *node = RT_NULL; + + while ((node = lwp_map_find_first(lwp->map_area)) != 0) + { + struct rt_mm_area_struct *ma = (struct rt_mm_area_struct *)node->data; + int pa_need_free = 0; + + RT_ASSERT(ma->type < MM_AREA_TYPE_UNKNOW); + + switch (ma->type) + { + case MM_AREA_TYPE_DATA: + case MM_AREA_TYPE_TEXT: + pa_need_free = 1; + break; + case MM_AREA_TYPE_SHM: + lwp_shm_ref_dec(lwp, (void *)ma->addr); + break; + } + unmap_range(lwp, (void *)ma->addr, ma->size, pa_need_free); + lwp_map_area_remove(&lwp->map_area, ma->addr); + } + + arch_user_space_vtable_free(lwp); +} + +static void *_lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size, int text) +{ + void *va = RT_NULL; + int ret = 0; + rt_mmu_info *m_info = &lwp->mmu_info; + int area_type; + + va = rt_hw_mmu_map_auto(m_info, map_va, map_size, MMU_MAP_U_RWCB); + if (!va) + { + return 0; + } + + area_type = text ? MM_AREA_TYPE_TEXT : MM_AREA_TYPE_DATA; + ret = lwp_map_area_insert(&lwp->map_area, (size_t)va, map_size, area_type); + if (ret != 0) + { + unmap_range(lwp, va, map_size, 1); + return 0; + } + return va; +} + +int lwp_unmap_user(struct rt_lwp *lwp, void *va) +{ + struct lwp_avl_struct *ma_avl_node = RT_NULL; + struct rt_mm_area_struct *ma = RT_NULL; + int pa_need_free = 0; + + rt_mm_lock(); + va = (void *)((size_t)va & ~ARCH_PAGE_MASK); + ma_avl_node = lwp_map_find(lwp->map_area, (size_t)va); + if (!ma_avl_node) + { + rt_mm_unlock(); + return -1; + } + ma = (struct rt_mm_area_struct *)ma_avl_node->data; + + RT_ASSERT(ma->type < MM_AREA_TYPE_UNKNOW); + if ((ma->type == MM_AREA_TYPE_DATA) || (ma->type == MM_AREA_TYPE_TEXT)) + { + pa_need_free = 1; + } + unmap_range(lwp, (void *)ma->addr, ma->size, pa_need_free); + lwp_map_area_remove(&lwp->map_area, (size_t)va); + rt_mm_unlock(); + return 0; +} + +int lwp_dup_user(struct lwp_avl_struct *ptree, void *arg) +{ + struct rt_lwp *self_lwp = lwp_self(); + struct rt_lwp *new_lwp = (struct rt_lwp *)arg; + struct rt_mm_area_struct *ma = (struct rt_mm_area_struct *)ptree->data; + void *pa = RT_NULL; + void *va = RT_NULL; + + switch (ma->type) + { + case MM_AREA_TYPE_PHY: + pa = rt_hw_mmu_v2p(&self_lwp->mmu_info, (void *)ma->addr); + va = lwp_map_user_type(new_lwp, (void *)ma->addr, pa, ma->size, 0, MM_AREA_TYPE_PHY); + break; + case MM_AREA_TYPE_PHY_CACHED: + pa = rt_hw_mmu_v2p(&self_lwp->mmu_info, (void *)ma->addr); + va = lwp_map_user_type(new_lwp, (void *)ma->addr, pa, ma->size, 0, MM_AREA_TYPE_PHY_CACHED); + break; + case MM_AREA_TYPE_SHM: + va = (void *)ma->addr; + if (lwp_shm_ref_inc(self_lwp, va) > 0) + { + pa = rt_hw_mmu_v2p(&self_lwp->mmu_info, va); + va = lwp_map_user_type(new_lwp, va, pa, ma->size, 1, MM_AREA_TYPE_SHM); + } + break; + case MM_AREA_TYPE_DATA: + va = lwp_map_user(new_lwp, (void *)ma->addr, ma->size, 0); + if (va == (void *)ma->addr) + { + lwp_data_put(&new_lwp->mmu_info, va, va, ma->size); + } + break; + case MM_AREA_TYPE_TEXT: + { + char *addr = (char *)ma->addr; + size_t size = ma->size; + + while (size) + { + pa = rt_hw_mmu_v2p(&self_lwp->mmu_info, (void *)addr); + rt_page_ref_inc((char *)pa - self_lwp->mmu_info.pv_off, 0); + va = lwp_map_user_type(new_lwp, addr, pa, ARCH_PAGE_SIZE, 1, MM_AREA_TYPE_TEXT); + if (va != addr) + { + return -1; + } + addr += ARCH_PAGE_SIZE; + size -= ARCH_PAGE_SIZE; + } + va = (void *)ma->addr; + } + break; + default: + RT_ASSERT(0); + break; + } + if (va != (void *)ma->addr) + { + return -1; + } + return 0; +} + +int lwp_unmap_user_phy(struct rt_lwp *lwp, void *va) +{ + return lwp_unmap_user(lwp, va); +} + +int lwp_unmap_user_type(struct rt_lwp *lwp, void *va) +{ + return lwp_unmap_user(lwp, va); +} + +void *lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size, int text) +{ + void *ret = RT_NULL; + size_t offset = 0; + + if (!map_size) + { + return 0; + } + offset = (size_t)map_va & ARCH_PAGE_MASK; + map_size += (offset + ARCH_PAGE_SIZE - 1); + map_size &= ~ARCH_PAGE_MASK; + map_va = (void *)((size_t)map_va & ~ARCH_PAGE_MASK); + + rt_mm_lock(); + ret = _lwp_map_user(lwp, map_va, map_size, text); + rt_mm_unlock(); + if (ret) + { + ret = (void *)((char *)ret + offset); + } + return ret; +} + +static void *_lwp_map_user_type(struct rt_lwp *lwp, void *map_va, void *map_pa, size_t map_size, int cached, int type) +{ + void *va = RT_NULL; + rt_mmu_info *m_info = &lwp->mmu_info; + size_t attr = 0; + int ret = 0; + + if (cached) + { + attr = MMU_MAP_U_RWCB; + if (type == MM_AREA_TYPE_PHY) + { + type = MM_AREA_TYPE_PHY_CACHED; + } + } + else + { + attr = MMU_MAP_U_RW; + } + + va = rt_hw_mmu_map(m_info, map_va, map_pa, map_size, attr); + if (va) + { + ret = lwp_map_area_insert(&lwp->map_area, (size_t)va, map_size, type); + if (ret != 0) + { + unmap_range(lwp, va, map_size, 0); + return 0; + } + } + return va; +} + +void *lwp_map_user_type(struct rt_lwp *lwp, void *map_va, void *map_pa, size_t map_size, int cached, int type) +{ + void *ret = RT_NULL; + size_t offset = 0; + + if (!map_size) + { + return 0; + } + if (map_va) + { + if (((size_t)map_va & ARCH_PAGE_MASK) != ((size_t)map_pa & ARCH_PAGE_MASK)) + { + return 0; + } + } + offset = (size_t)map_pa & ARCH_PAGE_MASK; + map_size += (offset + ARCH_PAGE_SIZE - 1); + map_size &= ~ARCH_PAGE_MASK; + map_pa = (void *)((size_t)map_pa & ~ARCH_PAGE_MASK); + + rt_mm_lock(); + ret = _lwp_map_user_type(lwp, map_va, map_pa, map_size, cached, type); + rt_mm_unlock(); + if (ret) + { + ret = (void *)((char *)ret + offset); + } + return ret; +} + +void *lwp_map_user_phy(struct rt_lwp *lwp, void *map_va, void *map_pa, size_t map_size, int cached) +{ + return lwp_map_user_type(lwp, map_va, map_pa, map_size, cached, MM_AREA_TYPE_PHY); +} + +rt_base_t lwp_brk(void *addr) +{ + rt_base_t ret = -1; + struct rt_lwp *lwp = RT_NULL; + + rt_mm_lock(); + lwp = rt_thread_self()->lwp; + + if ((size_t)addr <= lwp->end_heap) + { + ret = (rt_base_t)lwp->end_heap; + } + else + { + size_t size = 0; + void *va = RT_NULL; + + if ((size_t)addr <= USER_HEAP_VEND) + { + size = (((size_t)addr - lwp->end_heap) + ARCH_PAGE_SIZE - 1) & ~ARCH_PAGE_MASK; + va = lwp_map_user(lwp, (void *)lwp->end_heap, size, 0); + } + if (va) + { + lwp->end_heap += size; + ret = lwp->end_heap; + } + } + rt_mm_unlock(); + return ret; +} + +#define MAP_ANONYMOUS 0x20 + +void* lwp_mmap2(void *addr, size_t length, int prot, + int flags, int fd, off_t pgoffset) +{ + void *ret = (void *)-1; + + if (fd == -1) + { + rt_mm_lock(); + ret = lwp_map_user(lwp_self(), addr, length, 0); + rt_mm_unlock(); + + if (ret) + { + if ((flags & MAP_ANONYMOUS) != 0) + { + rt_memset(ret, 0, length); + } + } + else + { + ret = (void *)-1; + } + } + else + { + struct dfs_fd *d; + + d = fd_get(fd); + if (d && d->vnode->type == FT_DEVICE) + { + struct dfs_mmap2_args mmap2; + + mmap2.addr = addr; + mmap2.length = length; + mmap2.prot = prot; + mmap2.flags = flags; + mmap2.pgoffset = pgoffset; + mmap2.ret = (void*) -1; + + if (dfs_file_mmap2(d, &mmap2) == 0) + { + ret = mmap2.ret; + } + } + } + + return ret; +} + +int lwp_munmap(void *addr) +{ + int ret = 0; + + rt_mm_lock(); + ret = lwp_unmap_user(lwp_self(), addr); + rt_mm_unlock(); + + return ret; +} + +size_t lwp_get_from_user(void *dst, void *src, size_t size) +{ + struct rt_lwp *lwp = RT_NULL; + rt_mmu_info *m_info = RT_NULL; + + /* check src */ + + if (src < (void *)USER_VADDR_START) + { + return 0; + } + if (src >= (void *)USER_VADDR_TOP) + { + return 0; + } + if ((void *)((char *)src + size) > (void *)USER_VADDR_TOP) + { + return 0; + } + + lwp = lwp_self(); + if (!lwp) + { + return 0; + } + m_info = &lwp->mmu_info; + + return lwp_data_get(m_info, dst, src, size); +} + +size_t lwp_put_to_user(void *dst, void *src, size_t size) +{ + struct rt_lwp *lwp = RT_NULL; + rt_mmu_info *m_info = RT_NULL; + + /* check dst */ + if (dst < (void *)USER_VADDR_START) + { + return 0; + } + if (dst >= (void *)USER_VADDR_TOP) + { + return 0; + } + if ((void *)((char *)dst + size) > (void *)USER_VADDR_TOP) + { + return 0; + } + + lwp = lwp_self(); + if (!lwp) + { + return 0; + } + m_info = &lwp->mmu_info; + return lwp_data_put(m_info, dst, src, size); +} + +int lwp_user_accessable(void *addr, size_t size) +{ + void *addr_start = RT_NULL, *addr_end = RT_NULL, *next_page = RT_NULL; + void *tmp_addr = RT_NULL; + struct rt_lwp *lwp = lwp_self(); + rt_mmu_info *mmu_info = RT_NULL; + + if (!lwp) + { + return 0; + } + if (!size || !addr) + { + return 0; + } + addr_start = addr; + addr_end = (void *)((char *)addr + size); + +#ifdef ARCH_RISCV64 + if(addr_start < (void *)USER_VADDR_START) + { + return 0; + } +#else + if (addr_start >= (void *)USER_VADDR_TOP) + { + return 0; + } + if (addr_end > (void *)USER_VADDR_TOP) + { + return 0; + } +#endif + + mmu_info = &lwp->mmu_info; + next_page = (void *)(((size_t)addr_start + ARCH_PAGE_SIZE) & ~(ARCH_PAGE_SIZE - 1)); + do + { + size_t len = (char *)next_page - (char *)addr_start; + + if (size < len) + { + len = size; + } + tmp_addr = rt_hw_mmu_v2p(mmu_info, addr_start); + if (!tmp_addr) + { + return 0; + } + addr_start = (void *)((char *)addr_start + len); + size -= len; + next_page = (void *)((char *)next_page + ARCH_PAGE_SIZE); + } while (addr_start < addr_end); + return 1; +} + +/* src is in mmu_info space, dst is in current thread space */ +size_t lwp_data_get(rt_mmu_info *mmu_info, void *dst, void *src, size_t size) +{ + size_t copy_len = 0; + void *addr_start = RT_NULL, *addr_end = RT_NULL, *next_page = RT_NULL; + void *tmp_dst = RT_NULL, *tmp_src = RT_NULL; + + if (!size || !dst) + { + return 0; + } + tmp_dst = dst; + addr_start = src; + addr_end = (void *)((char *)src + size); + next_page = (void *)(((size_t)addr_start + ARCH_PAGE_SIZE) & ~(ARCH_PAGE_SIZE - 1)); + do + { + size_t len = (char *)next_page - (char *)addr_start; + + if (size < len) + { + len = size; + } + tmp_src = rt_hw_mmu_v2p(mmu_info, addr_start); + if (!tmp_src) + { + break; + } + tmp_src = (void *)((char *)tmp_src - PV_OFFSET); + rt_memcpy(tmp_dst, tmp_src, len); + tmp_dst = (void *)((char *)tmp_dst + len); + addr_start = (void *)((char *)addr_start + len); + size -= len; + next_page = (void *)((char *)next_page + ARCH_PAGE_SIZE); + copy_len += len; + } while (addr_start < addr_end); + return copy_len; +} + +/* dst is in mmu_info space, src is in current thread space */ +size_t lwp_data_put(rt_mmu_info *mmu_info, void *dst, void *src, size_t size) +{ + size_t copy_len = 0; + void *addr_start = RT_NULL, *addr_end = RT_NULL, *next_page = RT_NULL; + void *tmp_dst = RT_NULL, *tmp_src = RT_NULL; + + if (!size || !dst) + { + return 0; + } + tmp_src = src; + addr_start = dst; + addr_end = (void *)((char *)dst + size); + next_page = (void *)(((size_t)addr_start + ARCH_PAGE_SIZE) & ~(ARCH_PAGE_SIZE - 1)); + do + { + size_t len = (char *)next_page - (char *)addr_start; + + if (size < len) + { + len = size; + } + tmp_dst = rt_hw_mmu_v2p(mmu_info, addr_start); + if (!tmp_dst) + { + break; + } + tmp_dst = (void *)((char *)tmp_dst - PV_OFFSET); + rt_memcpy(tmp_dst, tmp_src, len); + tmp_src = (void *)((char *)tmp_src + len); + addr_start = (void *)((char *)addr_start + len); + size -= len; + next_page = (void *)((char *)next_page + ARCH_PAGE_SIZE); + copy_len += len; + } while (addr_start < addr_end); + return copy_len; +} + +void lwp_data_cache_flush(rt_mmu_info *mmu_info, void *vaddr, size_t size) +{ + void *paddr = RT_NULL; + + paddr = rt_hw_mmu_v2p(mmu_info, vaddr); + paddr = (void *)((char *)paddr - PV_OFFSET); + + rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, paddr, size); +} +#endif diff --git a/components/lwp/lwp_user_mm.h b/components/lwp/lwp_user_mm.h new file mode 100644 index 0000000000..07cb27e722 --- /dev/null +++ b/components/lwp/lwp_user_mm.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2006-2020, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-10-28 Jesven first version + * 2021-02-12 lizhirui add 64-bit support for lwp_brk + */ +#ifndef __LWP_USER_MM_H__ +#define __LWP_USER_MM_H__ + +#include <rthw.h> +#include <rtthread.h> + +#ifdef RT_USING_USERSPACE +#include <lwp.h> +#include <lwp_mm_area.h> + +#ifdef __cplusplus +extern "C" { +#endif + +int lwp_user_space_init(struct rt_lwp *lwp); +void lwp_unmap_user_space(struct rt_lwp *lwp); + +int lwp_unmap_user(struct rt_lwp *lwp, void *va); +void *lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size, int text); + +void *lwp_map_user_phy(struct rt_lwp *lwp, void *map_va, void *map_pa, size_t map_size, int cached); +int lwp_unmap_user_phy(struct rt_lwp *lwp, void *va); + +void *lwp_map_user_type(struct rt_lwp *lwp, void *map_va, void *map_pa, size_t map_size, int cached, int type); +int lwp_unmap_user_type(struct rt_lwp *lwp, void *va); + +rt_base_t lwp_brk(void *addr); +void* lwp_mmap2(void *addr, size_t length, int prot, int flags, int fd, off_t pgoffset); +int lwp_munmap(void *addr); + +size_t lwp_get_from_user(void *dst, void *src, size_t size); +size_t lwp_put_to_user(void *dst, void *src, size_t size); +int lwp_user_accessable(void *addr, size_t size); + +size_t lwp_data_get(rt_mmu_info *mmu_info, void *dst, void *src, size_t size); +size_t lwp_data_put(rt_mmu_info *mmu_info, void *dst, void *src, size_t size); +void lwp_data_cache_flush(rt_mmu_info *mmu_info, void *vaddr, size_t size); + +#ifdef __cplusplus +} +#endif + +#endif + +#endif /*__LWP_USER_MM_H__*/ diff --git a/components/lwp/page.c b/components/lwp/page.c new file mode 100644 index 0000000000..5cf63be5e0 --- /dev/null +++ b/components/lwp/page.c @@ -0,0 +1,424 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-11-01 Jesven The first version + */ +#include <stdint.h> + +#include <rthw.h> +#include <rtthread.h> + +#ifdef RT_USING_USERSPACE + +#include <page.h> +#include <mmu.h> + +#define ARCH_PAGE_LIST_SIZE (ARCH_ADDRESS_WIDTH_BITS - ARCH_PAGE_SHIFT) + +#define DBG_TAG "PAGE" +#define DBG_LVL DBG_WARNING +#include <rtdbg.h> + +struct page +{ + struct page *next; /* same level next */ + struct page *pre; /* same level pre */ + uint32_t size_bits; /* if is ARCH_ADDRESS_WIDTH_BITS, means not free */ + int ref_cnt; /* page group ref count */ +}; + +static struct page* page_start; +static void* page_addr; +static size_t page_nr; + +static struct page *page_list[ARCH_PAGE_LIST_SIZE]; + +RT_WEAK int rt_hw_clz(size_t n) +{ + int bits = sizeof(size_t) * 8; + + n |= (n >> 1); + n |= (n >> 2); + n |= (n >> 4); + n |= (n >> 8); + n |= (n >> 16); + +#ifdef ARCH_CPU_64BIT + n |= (n >> 32); + + n = (n & 0x5555555555555555UL) + ((n >> 1) & 0x5555555555555555UL); + n = (n & 0x3333333333333333UL) + ((n >> 2) & 0x3333333333333333UL); + n = (n & 0x0707070707070707UL) + ((n >> 4) & 0x0707070707070707UL); + n = (n & 0x000f000f000f000fUL) + ((n >> 8) & 0x000f000f000f000fUL); + n = (n & 0x0000001f0000001fUL) + ((n >> 16) & 0x0000001f0000001fUL); + n = (n & 0x000000000000003fUL) + ((n >> 32) & 0x000000000000003fUL); +#else + n = (n & 0x55555555UL) + ((n >> 1) & 0x55555555UL); + n = (n & 0x33333333UL) + ((n >> 2) & 0x33333333UL); + n = (n & 0x07070707UL) + ((n >> 4) & 0x07070707UL); + n = (n & 0x000f000fUL) + ((n >> 8) & 0x000f000fUL); + n = (n & 0x0000001fUL) + ((n >> 16) & 0x0000001fUL); +#endif + return bits - n; +} + +RT_WEAK int rt_hw_ctz(size_t n) +{ + int ret = sizeof(size_t) * 8; + + if (n) + { + ret -= (rt_hw_clz(n ^ (n - 1)) + 1); + } + return ret; +} + +size_t rt_page_bits(size_t size) +{ + int bit = sizeof(size_t) * 8 - rt_hw_clz(size) - 1; + + if ((size ^ (1UL << bit)) != 0) + { + bit++; + } + bit -= ARCH_PAGE_SHIFT; + if (bit < 0) + { + bit = 0; + } + return bit; +} + +static struct page * addr_to_page(void *addr) +{ + size_t off; + + if (addr < page_addr) + { + return 0; + } + off = (size_t)((char*)addr - (char*)page_addr); + off >>= ARCH_PAGE_SHIFT; + if (off >= page_nr) + { + return 0; + } + return &page_start[off]; +} + +static void* page_to_addr(struct page* p) +{ + if (!p) + { + return 0; + } + return (void*)((char*)page_addr + ((p - page_start) << ARCH_PAGE_SHIFT)); +} + +static inline struct page *buddy_get(struct page *p, uint32_t size_bits) +{ + size_t addr; + + addr = (size_t)page_to_addr(p); + addr ^= (1UL << (size_bits + ARCH_PAGE_SHIFT)); + return addr_to_page((void*)addr); +} + +static void page_remove(struct page *p, uint32_t size_bits) +{ + if (p->pre) + { + p->pre->next = p->next; + } + else + { + page_list[size_bits] = p->next; + } + + if (p->next) + { + p->next->pre = p->pre; + } + + p->size_bits = ARCH_ADDRESS_WIDTH_BITS; +} + +static void page_insert(struct page *p, uint32_t size_bits) +{ + p->next = page_list[size_bits]; + if (p->next) + { + p->next->pre = p; + } + p->pre = 0; + page_list[size_bits] = p; + p->size_bits = size_bits; +} + +static void _pages_ref_inc(struct page *p, uint32_t size_bits) +{ + struct page *page_head; + int idx; + + /* find page group head */ + idx = p - page_start; + if (idx < 0 || idx >= page_nr) + { + return; + } + idx = idx & ~((1UL << size_bits) - 1); + + page_head = page_start + idx; + page_head->ref_cnt++; +} + +static int _pages_ref_get(struct page *p, uint32_t size_bits) +{ + struct page *page_head; + int idx; + + /* find page group head */ + idx = p - page_start; + if (idx < 0 || idx >= page_nr) + { + return 0; + } + idx = idx & ~((1UL << size_bits) - 1); + + page_head = page_start + idx; + return page_head->ref_cnt; +} + +static int _pages_free(struct page *p, uint32_t size_bits) +{ + uint32_t level = size_bits; + struct page *buddy; + + RT_ASSERT(p->ref_cnt > 0); + RT_ASSERT(p->size_bits == ARCH_ADDRESS_WIDTH_BITS); + + p->ref_cnt--; + if (p->ref_cnt != 0) + { + return 0; + } + + while (level < ARCH_PAGE_LIST_SIZE) + { + buddy = buddy_get(p, level); + if (buddy && buddy->size_bits == level) + { + page_remove(buddy, level); + p = (p < buddy) ? p : buddy; + level++; + } + else + { + break; + } + } + page_insert(p, level); + return 1; +} + +static struct page *_pages_alloc(uint32_t size_bits) +{ + struct page *p; + + if (page_list[size_bits]) + { + p = page_list[size_bits]; + page_remove(p, size_bits); + } + else + { + uint32_t level; + + for (level = size_bits + 1; level < ARCH_PAGE_LIST_SIZE; level++) + { + if (page_list[level]) + { + break; + } + } + if (level == ARCH_PAGE_LIST_SIZE) + { + return 0; + } + + p = page_list[level]; + page_remove(p, level); + while (level > size_bits) + { + page_insert(p, level - 1); + p = buddy_get(p, level - 1); + level--; + } + } + p->size_bits = ARCH_ADDRESS_WIDTH_BITS; + p->ref_cnt = 1; + return p; +} + +int rt_page_ref_get(void *addr, uint32_t size_bits) +{ + struct page *p; + rt_base_t level; + int ref; + + p = addr_to_page(addr); + level = rt_hw_interrupt_disable(); + ref = _pages_ref_get(p, size_bits); + rt_hw_interrupt_enable(level); + return ref; +} + +void rt_page_ref_inc(void *addr, uint32_t size_bits) +{ + struct page *p; + rt_base_t level; + + p = addr_to_page(addr); + level = rt_hw_interrupt_disable(); + _pages_ref_inc(p, size_bits); + rt_hw_interrupt_enable(level); +} + +void *rt_pages_alloc(uint32_t size_bits) +{ + struct page *p; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + p = _pages_alloc(size_bits); + rt_hw_interrupt_enable(level); + return page_to_addr(p); +} + +int rt_pages_free(void *addr, uint32_t size_bits) +{ + struct page *p; + int real_free = 0; + + p = addr_to_page(addr); + if (p) + { + rt_base_t level; + level = rt_hw_interrupt_disable(); + real_free = _pages_free(p, size_bits); + rt_hw_interrupt_enable(level); + } + return real_free; +} + +void list_page(void) +{ + int i; + size_t total = 0; + + rt_base_t level; + level = rt_hw_interrupt_disable(); + + for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++) + { + struct page *p = page_list[i]; + + rt_kprintf("level %d ", i); + + while (p) + { + total += (1UL << i); + rt_kprintf("[0x%08p]", page_to_addr(p)); + p = p->next; + } + rt_kprintf("\n"); + } + rt_hw_interrupt_enable(level); + rt_kprintf("free pages is %08x\n", total); + rt_kprintf("-------------------------------\n"); +} +MSH_CMD_EXPORT(list_page, show page info); + +void rt_page_get_info(size_t *total_nr, size_t *free_nr) +{ + int i; + size_t total_free = 0; + rt_base_t level; + + level = rt_hw_interrupt_disable(); + for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++) + { + struct page *p = page_list[i]; + + while (p) + { + total_free += (1UL << i); + p = p->next; + } + } + rt_hw_interrupt_enable(level); + *total_nr = page_nr; + *free_nr = total_free; +} + +void rt_page_init(rt_region_t reg) +{ + int i; + + LOG_D("split 0x%08x 0x%08x\n", reg.start, reg.end); + + reg.start += ARCH_PAGE_MASK; + reg.start &= ~ARCH_PAGE_MASK; + + reg.end &= ~ARCH_PAGE_MASK; + + { + int nr = ARCH_PAGE_SIZE / sizeof(struct page); + int total = (reg.end - reg.start) >> ARCH_PAGE_SHIFT; + int mnr = (total + nr) / (nr + 1); + + LOG_D("nr = 0x%08x\n", nr); + LOG_D("total = 0x%08x\n", total); + LOG_D("mnr = 0x%08x\n", mnr); + + RT_ASSERT(mnr < total); + + page_start = (struct page*)reg.start; + reg.start += (mnr << ARCH_PAGE_SHIFT); + page_addr = (void*)reg.start; + page_nr = (reg.end - reg.start) >> ARCH_PAGE_SHIFT; + } + + LOG_D("align 0x%08x 0x%08x\n", reg.start, reg.end); + + /* init free list */ + for (i = 0; i < ARCH_PAGE_LIST_SIZE; i++) + { + page_list[i] = 0; + } + + /* add pages to free list */ + while (reg.start != reg.end) + { + struct page *p; + int align_bits; + int size_bits; + + size_bits = ARCH_ADDRESS_WIDTH_BITS - 1 - rt_hw_clz(reg.end - reg.start); + align_bits = rt_hw_ctz(reg.start); + if (align_bits < size_bits) + { + size_bits = align_bits; + } + p = addr_to_page((void*)reg.start); + p->size_bits = ARCH_ADDRESS_WIDTH_BITS; + p->ref_cnt = 1; + _pages_free(p, size_bits - ARCH_PAGE_SHIFT); + reg.start += (1UL << size_bits); + } +} +#endif diff --git a/components/lwp/page.h b/components/lwp/page.h new file mode 100644 index 0000000000..8b65299d91 --- /dev/null +++ b/components/lwp/page.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2006-2019, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-11-01 Jesven The first version + */ + +#ifndef __PAGE_H__ +#define __PAGE_H__ + +#ifdef RT_USING_USERSPACE + +typedef struct tag_region +{ + size_t start; + size_t end; +} rt_region_t; + +void rt_page_init(rt_region_t reg); + +void *rt_pages_alloc(uint32_t size_bits); + +void rt_page_ref_inc(void *addr, uint32_t size_bits); + +int rt_page_ref_get(void *addr, uint32_t size_bits); + +int rt_pages_free(void *addr, uint32_t size_bits); + +void rt_pageinfo_dump(void); + +size_t rt_page_bits(size_t size); + +void rt_page_get_info(size_t *total_nr, size_t *free_nr); + +#endif + +#endif /*__PAGE_H__*/ diff --git a/components/lwp/syscall_data.h b/components/lwp/syscall_data.h new file mode 100644 index 0000000000..7c700e0dd0 --- /dev/null +++ b/components/lwp/syscall_data.h @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2006-2022, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2022-11-10 RT-Thread The first version + */ +#ifndef __SYSCALL_DATA_H__ +#define __SYSCALL_DATA_H__ + +#include <rtthread.h> + +/** + * @brief signature for syscall, used to locate syscall metadata. + * + * We don't allocate an exclusive section in ELF like Linux do + * to avoid initializing necessary data by iterating that section, + * which increases system booting time. We signature a pointer + * just below each syscall entry in syscall table to make it + * easy to locate every syscall's metadata by using syscall id. + * + * TODO Currently this adds a dummy pointer to syscall name. + * After adding metadata of every syscalls in front of their definition, + * this should be replaced by a pointer to that structure + */ +#define SYSCALL_SIGN(func) \ + (void *)func, \ + RT_STRINGIFY(func) + +#endif /* __SYSCALL_DATA_H__ */
\ No newline at end of file diff --git a/components/net/netdev/src/netdev.c b/components/net/netdev/src/netdev.c index aa06f51fdf..09f6c37d9a 100644 --- a/components/net/netdev/src/netdev.c +++ b/components/net/netdev/src/netdev.c @@ -741,6 +741,14 @@ static void netdev_auto_change_default(struct netdev *netdev) { struct netdev *new_netdev = RT_NULL; + if (netdev->flags & NETDEV_FLAG_LINK_UP) + { + if (!(netdev_default->flags & NETDEV_FLAG_LINK_UP)) + { + netdev_set_default(netdev); + } + return; + } if (rt_memcmp(netdev, netdev_default, sizeof(struct netdev)) == 0) { new_netdev = netdev_get_first_by_flags(NETDEV_FLAG_LINK_UP); @@ -996,6 +1004,35 @@ static void netdev_list_if(void) } } +#ifdef RT_LWIP_DHCP +int netdev_dhcp_open(char* netdev_name) +{ + struct netdev *netdev = RT_NULL; + netdev = netdev_get_by_name(netdev_name); + if (netdev == RT_NULL) + { + rt_kprintf("bad network interface device name(%s).\n", netdev_name); + return -1; + } + netdev_dhcp_enabled(netdev,RT_TRUE); + return 0; +} + +int netdev_dhcp_close(char* netdev_name) +{ + struct netdev *netdev = RT_NULL; + + netdev = netdev_get_by_name(netdev_name); + if (netdev == RT_NULL) + { + rt_kprintf("bad network interface device name(%s).\n", netdev_name); + return -1; + } + netdev_dhcp_enabled(netdev,RT_FALSE); + return 0; +} +#endif + static void netdev_set_if(char* netdev_name, char* ip_addr, char* gw_addr, char* nm_addr) { struct netdev *netdev = RT_NULL; @@ -1008,6 +1045,10 @@ static void netdev_set_if(char* netdev_name, char* ip_addr, char* gw_addr, char* return; } +#ifdef RT_LWIP_DHCP + netdev_dhcp_close(netdev_name); +#endif + /* set IP address */ if ((ip_addr != RT_NULL) && inet_aton(ip_addr, &addr)) { @@ -1033,6 +1074,15 @@ int netdev_ifconfig(int argc, char **argv) { netdev_list_if(); } +#ifdef RT_LWIP_DHCP + else if(argc == 3) + { + if (!strcmp(argv[2], "dhcp")) + { + netdev_dhcp_open(argv[1]); + } + } +#endif else if (argc == 5) { rt_kprintf("config : %s\n", argv[1]); @@ -1044,6 +1094,9 @@ int netdev_ifconfig(int argc, char **argv) else { rt_kprintf("bad parameter! e.g: ifconfig e0 192.168.1.30 192.168.1.1 255.255.255.0\n"); +#ifdef RT_LWIP_DHCP + rt_kprintf("bad parameter! e.g: ifconfig e0 dhcp\n"); +#endif } return 0; diff --git a/components/net/sal/dfs_net/dfs_net.c b/components/net/sal/dfs_net/dfs_net.c index 67b652c320..2aded9fcd8 100644 --- a/components/net/sal/dfs_net/dfs_net.c +++ b/components/net/sal/dfs_net/dfs_net.c @@ -26,39 +26,64 @@ int dfs_net_getsocket(int fd) _dfs_fd = fd_get(fd); if (_dfs_fd == NULL) return -1; - if (_dfs_fd->type != FT_SOCKET) socket = -1; - else socket = (int)_dfs_fd->data; + if (_dfs_fd->vnode->type != FT_SOCKET) socket = -1; + else socket = (int)(size_t)_dfs_fd->vnode->data; - fd_put(_dfs_fd); /* put this dfs fd */ return socket; } static int dfs_net_ioctl(struct dfs_fd* file, int cmd, void* args) { - int socket = (int) file->data; + int ret; + int socket = (int)(size_t)file->vnode->data; - return sal_ioctlsocket(socket, cmd, args); + ret = sal_ioctlsocket(socket, cmd, args); + if (ret < 0) + { + ret = rt_get_errno(); + return (ret > 0) ? (-ret) : ret; + } + return ret; } static int dfs_net_read(struct dfs_fd* file, void *buf, size_t count) { - int socket = (int) file->data; + int ret; + int socket = (int)(size_t)file->vnode->data; - return sal_recvfrom(socket, buf, count, 0, NULL, NULL); + ret = sal_recvfrom(socket, buf, count, 0, NULL, NULL); + if (ret < 0) + { + ret = rt_get_errno(); + return (ret > 0) ? (-ret) : ret; + } + return ret; } static int dfs_net_write(struct dfs_fd *file, const void *buf, size_t count) { - int socket = (int) file->data; - - return sal_sendto(socket, buf, count, 0, NULL, 0); + int ret; + int socket = (int)(size_t)file->vnode->data; + + ret = sal_sendto(socket, buf, count, 0, NULL, 0); + if (ret < 0) + { + ret = rt_get_errno(); + return (ret > 0) ? (-ret) : ret; + } + return ret; } - static int dfs_net_close(struct dfs_fd* file) { - int socket = (int) file->data; + int socket; + int ret = 0; - return sal_closesocket(socket); + if (file->vnode->ref_count == 1) + { + socket = (int)(size_t)file->vnode->data; + ret = sal_closesocket(socket); + } + return ret; } static int dfs_net_poll(struct dfs_fd *file, struct rt_pollreq *req) diff --git a/components/net/sal/impl/af_inet_lwip.c b/components/net/sal/impl/af_inet_lwip.c index e99da3117a..401b9001e4 100644 --- a/components/net/sal/impl/af_inet_lwip.c +++ b/components/net/sal/impl/af_inet_lwip.c @@ -165,7 +165,7 @@ static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len if (event) { - rt_wqueue_wakeup(&sock->wait_head, (void*) event); + rt_wqueue_wakeup(&sock->wait_head, (void*)(size_t)event); } } #endif /* SAL_USING_POSIX */ @@ -229,7 +229,7 @@ int inet_ioctlsocket(int socket, long cmd, void *arg) { case F_GETFL: case F_SETFL: - return lwip_fcntl(socket, cmd, (int) arg); + return lwip_fcntl(socket, cmd, (int)(size_t)arg); default: return lwip_ioctl(socket, cmd, arg); @@ -243,13 +243,13 @@ static int inet_poll(struct dfs_fd *file, struct rt_pollreq *req) struct lwip_sock *sock; struct sal_socket *sal_sock; - sal_sock = sal_get_socket((int) file->data); + sal_sock = sal_get_socket((int)(size_t)file->vnode->data); if(!sal_sock) { return -1; } - sock = lwip_tryget_socket((int)sal_sock->user_data); + sock = lwip_tryget_socket((int)(size_t)sal_sock->user_data); if (sock != NULL) { rt_base_t level; diff --git a/components/net/sal/include/sal_socket.h b/components/net/sal/include/sal_socket.h index c479d21c62..b77487516f 100644 --- a/components/net/sal/include/sal_socket.h +++ b/components/net/sal/include/sal_socket.h @@ -36,8 +36,12 @@ typedef uint16_t in_port_t; #define SOCK_STREAM 1 #define SOCK_DGRAM 2 #define SOCK_RAW 3 +#define SOCK_PACKET 10 -#define SOCK_MAX (SOCK_RAW + 1) +#define SOCK_NONBLOCK 04000 +#define SOCK_CLOEXEC 02000000 + +#define SOCK_MAX (SOCK_CLOEXEC + 1) /* Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c) */ #define SO_REUSEADDR 0x0004 /* Allow local address reuse */ @@ -68,11 +72,13 @@ typedef uint16_t in_port_t; #define SOL_SOCKET 0xfff /* options for socket level */ #define AF_UNSPEC 0 +#define AF_UNIX 1 #define AF_INET 2 #define AF_INET6 10 #define AF_CAN 29 /* Controller Area Network */ #define AF_AT 45 /* AT socket */ #define AF_WIZ 46 /* WIZnet socket */ +#define PF_UNIX AF_UNIX #define PF_INET AF_INET #define PF_INET6 AF_INET6 #define PF_UNSPEC AF_UNSPEC @@ -158,6 +164,15 @@ struct sockaddr char sa_data[14]; }; +/* Structure describing the address of an AF_LOCAL (aka AF_UNIX) socket. */ +struct sockaddr_un +{ + uint8_t sa_len; + sa_family_t sa_family; + char sun_path[108]; /* Path name. */ +}; + + #if NETDEV_IPV4 /* members are in network byte order */ struct sockaddr_in @@ -194,6 +209,40 @@ struct sockaddr_storage #endif /* NETDEV_IPV6 */ }; +#define IFNAMSIZ 16 +struct sal_ifmap +{ + unsigned long int mem_start; + unsigned long int mem_end; + unsigned short int base_addr; + unsigned char irq; + unsigned char dma; + unsigned char port; +}; + +struct sal_ifreq +{ + union + { + char ifrn_name[IFNAMSIZ]; + } ifr_ifrn; + union + { + struct sockaddr ifru_addr; + struct sockaddr ifru_dstaddr; + struct sockaddr ifru_broadaddr; + struct sockaddr ifru_netmask; + struct sockaddr ifru_hwaddr; + short int ifru_flags; + int ifru_ivalue; + int ifru_mtu; + struct sal_ifmap ifru_map; + char ifru_slave[IFNAMSIZ]; + char ifru_newname[IFNAMSIZ]; + char *ifru_data; + } ifr_ifru; +}; + int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen); int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen); int sal_shutdown(int socket, int how); diff --git a/components/net/sal/socket/net_sockets.c b/components/net/sal/socket/net_sockets.c index 1b6db50e6d..e10ffef9b0 100644 --- a/components/net/sal/socket/net_sockets.c +++ b/components/net/sal/socket/net_sockets.c @@ -41,20 +41,28 @@ int accept(int s, struct sockaddr *addr, socklen_t *addrlen) if(d) { /* this is a socket fd */ - d->type = FT_SOCKET; - d->path = NULL; - - d->fops = dfs_net_get_fops(); + d->vnode = (struct dfs_fnode *)rt_malloc(sizeof(struct dfs_fnode)); + if (!d->vnode) + { + /* release fd */ + fd_release(fd); + rt_set_errno(-ENOMEM); + return -1; + } + rt_memset(d->vnode, 0, sizeof(struct dfs_fnode)); + rt_list_init(&d->vnode->list); + d->vnode->type = FT_SOCKET; + d->vnode->path = NULL; + d->vnode->fullpath = NULL; + d->vnode->ref_count = 1; + d->vnode->fops = dfs_net_get_fops(); d->flags = O_RDWR; /* set flags as read and write */ - d->size = 0; + d->vnode->size = 0; d->pos = 0; /* set socket to the data of dfs_fd */ - d->data = (void *) new_socket; - - /* release the ref-count of fd */ - fd_put(d); + d->vnode->data = (void *)(size_t)new_socket; return fd; } @@ -72,6 +80,17 @@ int bind(int s, const struct sockaddr *name, socklen_t namelen) { int socket = dfs_net_getsocket(s); +#ifdef SAL_USING_AF_UNIX + struct sockaddr_in server_addr = {0}; + if (name->sa_family == AF_UNIX) + { + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(514); + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + return sal_bind(socket, (struct sockaddr *)&server_addr, namelen); + } +#endif /* SAL_USING_AF_UNIX */ + return sal_bind(socket, name, namelen); } RTM_EXPORT(bind); @@ -106,8 +125,6 @@ int shutdown(int s, int how) error = -1; } - fd_put(d); - return error; } RTM_EXPORT(shutdown); @@ -148,6 +165,17 @@ int connect(int s, const struct sockaddr *name, socklen_t namelen) { int socket = dfs_net_getsocket(s); +#ifdef SAL_USING_AF_UNIX + struct sockaddr_in server_addr = {0}; + if (name->sa_family == AF_UNIX) + { + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(514); + server_addr.sin_addr.s_addr = inet_addr("127.0.0.1"); + return sal_connect(socket, (struct sockaddr *)&server_addr, namelen); + } +#endif /* SAL_USING_AF_UNIX */ + return sal_connect(socket, name, namelen); } RTM_EXPORT(connect); @@ -210,38 +238,51 @@ int socket(int domain, int type, int protocol) return -1; } d = fd_get(fd); + d->vnode = (struct dfs_fnode *)rt_malloc(sizeof(struct dfs_fnode)); + if (!d->vnode) + { + /* release fd */ + fd_release(fd); + rt_set_errno(-ENOMEM); + return -1; + } + +#ifdef SAL_USING_AF_UNIX + if (domain == AF_UNIX) + { + domain = AF_INET; + } +#endif /* SAL_USING_AF_UNIX */ /* create socket and then put it to the dfs_fd */ socket = sal_socket(domain, type, protocol); if (socket >= 0) { + rt_memset(d->vnode, 0, sizeof(struct dfs_fnode)); + rt_list_init(&d->vnode->list); /* this is a socket fd */ - d->type = FT_SOCKET; - d->path = NULL; - - d->fops = dfs_net_get_fops(); + d->vnode->type = FT_SOCKET; + d->vnode->path = NULL; + d->vnode->fullpath = NULL; + d->vnode->ref_count = 1; + d->vnode->fops = dfs_net_get_fops(); d->flags = O_RDWR; /* set flags as read and write */ - d->size = 0; + d->vnode->size = 0; d->pos = 0; /* set socket to the data of dfs_fd */ - d->data = (void *) socket; + d->vnode->data = (void *)(size_t)socket; } else { + rt_free(d->vnode); /* release fd */ - fd_put(d); - fd_put(d); - + fd_release(fd); rt_set_errno(-ENOMEM); - return -1; } - /* release the ref-count of fd */ - fd_put(d); - return fd; } RTM_EXPORT(socket); @@ -266,6 +307,12 @@ int closesocket(int s) return -1; } + if (!d->vnode) + { + rt_set_errno(-EBADF); + return -1; + } + if (sal_closesocket(socket) == 0) { error = 0; @@ -276,9 +323,9 @@ int closesocket(int s) error = -1; } + rt_free(d->vnode); /* socket has been closed, delete it from file system fd */ - fd_put(d); - fd_put(d); + fd_release(s); return error; } diff --git a/components/net/sal/src/sal_socket.c b/components/net/sal/src/sal_socket.c index e3dafd0cc8..d1f9f645e6 100644 --- a/components/net/sal/src/sal_socket.c +++ b/components/net/sal/src/sal_socket.c @@ -12,6 +12,7 @@ #include <rtthread.h> #include <rthw.h> #include <sys/time.h> +#include <sys/ioctl.h> #include <sal_socket.h> #include <sal_netdb.h> @@ -594,7 +595,7 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen) /* check the network interface socket operations */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, accept); - new_socket = pf->skt_ops->accept((int) sock->user_data, addr, addrlen); + new_socket = pf->skt_ops->accept((int)(size_t)sock->user_data, addr, addrlen); if (new_socket != -1) { int retval; @@ -624,7 +625,7 @@ int sal_accept(int socket, struct sockaddr *addr, socklen_t *addrlen) /* new socket create by accept should have the same netdev with server*/ new_sock->netdev = sock->netdev; /* socket structure user_data used to store the acquired new socket */ - new_sock->user_data = (void *) new_socket; + new_sock->user_data = (void *)(size_t)new_socket; return new_sal_socket; } @@ -690,13 +691,13 @@ int sal_bind(int socket, const struct sockaddr *name, socklen_t namelen) return -1; } sock->netdev = new_netdev; - sock->user_data = (void *) new_socket; + sock->user_data = (void *)(size_t)new_socket; } } /* check and get protocol families by the network interface device */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, bind); - return pf->skt_ops->bind((int) sock->user_data, name, namelen); + return pf->skt_ops->bind((int)(size_t)sock->user_data, name, namelen); } int sal_shutdown(int socket, int how) @@ -712,7 +713,7 @@ int sal_shutdown(int socket, int how) /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, shutdown); - if (pf->skt_ops->shutdown((int) sock->user_data, how) == 0) + if (pf->skt_ops->shutdown((int)(size_t)sock->user_data, how) == 0) { #ifdef SAL_USING_TLS if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, closesocket)) @@ -745,7 +746,7 @@ int sal_getpeername(int socket, struct sockaddr *name, socklen_t *namelen) /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getpeername); - return pf->skt_ops->getpeername((int) sock->user_data, name, namelen); + return pf->skt_ops->getpeername((int)(size_t)sock->user_data, name, namelen); } int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen) @@ -759,7 +760,7 @@ int sal_getsockname(int socket, struct sockaddr *name, socklen_t *namelen) /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockname); - return pf->skt_ops->getsockname((int) sock->user_data, name, namelen); + return pf->skt_ops->getsockname((int)(size_t)sock->user_data, name, namelen); } int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t *optlen) @@ -773,7 +774,7 @@ int sal_getsockopt(int socket, int level, int optname, void *optval, socklen_t * /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, getsockopt); - return pf->skt_ops->getsockopt((int) sock->user_data, level, optname, optval, optlen); + return pf->skt_ops->getsockopt((int)(size_t)sock->user_data, level, optname, optval, optlen); } int sal_setsockopt(int socket, int level, int optname, const void *optval, socklen_t optlen) @@ -819,7 +820,7 @@ int sal_setsockopt(int socket, int level, int optname, const void *optval, sockl return pf->skt_ops->setsockopt((int) sock->user_data, level, optname, optval, optlen); } #else - return pf->skt_ops->setsockopt((int) sock->user_data, level, optname, optval, optlen); + return pf->skt_ops->setsockopt((int)(size_t)sock->user_data, level, optname, optval, optlen); #endif /* SAL_USING_TLS */ } @@ -837,7 +838,7 @@ int sal_connect(int socket, const struct sockaddr *name, socklen_t namelen) /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, connect); - ret = pf->skt_ops->connect((int) sock->user_data, name, namelen); + ret = pf->skt_ops->connect((int)(size_t)sock->user_data, name, namelen); #ifdef SAL_USING_TLS if (ret >= 0 && SAL_SOCKOPS_PROTO_TLS_VALID(sock, connect)) { @@ -864,7 +865,7 @@ int sal_listen(int socket, int backlog) /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, listen); - return pf->skt_ops->listen((int) sock->user_data, backlog); + return pf->skt_ops->listen((int)(size_t)sock->user_data, backlog); } int sal_recvfrom(int socket, void *mem, size_t len, int flags, @@ -894,10 +895,10 @@ int sal_recvfrom(int socket, void *mem, size_t len, int flags, } else { - return pf->skt_ops->recvfrom((int) sock->user_data, mem, len, flags, from, fromlen); + return pf->skt_ops->recvfrom((int)(size_t)sock->user_data, mem, len, flags, from, fromlen); } #else - return pf->skt_ops->recvfrom((int) sock->user_data, mem, len, flags, from, fromlen); + return pf->skt_ops->recvfrom((int)(size_t)sock->user_data, mem, len, flags, from, fromlen); #endif } @@ -931,7 +932,7 @@ int sal_sendto(int socket, const void *dataptr, size_t size, int flags, return pf->skt_ops->sendto((int) sock->user_data, dataptr, size, flags, to, tolen); } #else - return pf->skt_ops->sendto((int) sock->user_data, dataptr, size, flags, to, tolen); + return pf->skt_ops->sendto((int)(size_t)sock->user_data, dataptr, size, flags, to, tolen); #endif } @@ -983,7 +984,7 @@ int sal_socket(int domain, int type, int protocol) } } #endif - sock->user_data = (void *) proto_socket; + sock->user_data = (void *)(size_t)proto_socket; return sock->socket; } socket_delete(socket); @@ -1003,7 +1004,7 @@ int sal_closesocket(int socket) /* valid the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, closesocket); - if (pf->skt_ops->closesocket((int) sock->user_data) == 0) + if (pf->skt_ops->closesocket((int)(size_t)sock->user_data) == 0) { #ifdef SAL_USING_TLS if (SAL_SOCKOPS_PROTO_TLS_VALID(sock, closesocket)) @@ -1031,14 +1032,69 @@ int sal_ioctlsocket(int socket, long cmd, void *arg) { struct sal_socket *sock; struct sal_proto_family *pf; - + struct sockaddr_in *addr_in = RT_NULL; + struct sockaddr *addr = RT_NULL; + ip_addr_t input_ipaddr; /* get the socket object by socket descriptor */ SAL_SOCKET_OBJ_GET(sock, socket); /* check the network interface socket opreation */ SAL_NETDEV_SOCKETOPS_VALID(sock->netdev, pf, ioctlsocket); - return pf->skt_ops->ioctlsocket((int) sock->user_data, cmd, arg); + struct sal_ifreq *ifr = (struct sal_ifreq *)arg; + + if((sock->domain == AF_INET)&&(sock->netdev)&&(ifr != RT_NULL)) + { + switch (cmd) + { + case SIOCGIFADDR: + addr_in = (struct sockaddr_in *)&(ifr->ifr_ifru.ifru_addr); +#if NETDEV_IPV4 && NETDEV_IPV6 + addr_in->sin_addr.s_addr = sock->netdev->ip_addr.u_addr.ip4.addr; +#elif NETDEV_IPV4 + addr_in->sin_addr.s_addr = sock->netdev->ip_addr.addr; +#elif NETDEV_IPV6 +#error "not only support IPV6" +#endif /* NETDEV_IPV4 && NETDEV_IPV6*/ + return 0; + + case SIOCSIFADDR: + addr = (struct sockaddr *)&(ifr->ifr_ifru.ifru_addr); + sal_sockaddr_to_ipaddr(addr,&input_ipaddr); + netdev_set_ipaddr(sock->netdev,&input_ipaddr); + return 0; + + case SIOCGIFNETMASK: + addr_in = (struct sockaddr_in *)&(ifr->ifr_ifru.ifru_netmask); +#if NETDEV_IPV4 && NETDEV_IPV6 + addr_in->sin_addr.s_addr = sock->netdev->netmask.u_addr.ip4.addr; +#elif NETDEV_IPV4 + addr_in->sin_addr.s_addr = sock->netdev->netmask.addr; +#elif NETDEV_IPV6 +#error "not only support IPV6" +#endif /* NETDEV_IPV4 && NETDEV_IPV6*/ + return 0; + + case SIOCSIFNETMASK: + addr = (struct sockaddr *)&(ifr->ifr_ifru.ifru_netmask); + sal_sockaddr_to_ipaddr(addr,&input_ipaddr); + netdev_set_netmask(sock->netdev,&input_ipaddr); + return 0; + + case SIOCGIFHWADDR: + addr = (struct sockaddr *)&(ifr->ifr_ifru.ifru_hwaddr); + rt_memcpy(addr->sa_data,sock->netdev->hwaddr,sock->netdev->hwaddr_len); + return 0; + + case SIOCGIFMTU: + ifr->ifr_ifru.ifru_mtu = sock->netdev->mtu; + return 0; + + default: + break; + } + } + return pf->skt_ops->ioctlsocket((int)(size_t)sock->user_data, cmd, arg); } #ifdef SAL_USING_POSIX @@ -1046,7 +1102,7 @@ int sal_poll(struct dfs_fd *file, struct rt_pollreq *req) { struct sal_socket *sock; struct sal_proto_family *pf; - int socket = (int) file->data; + int socket = (int)(size_t)file->vnode->data; /* get the socket object by socket descriptor */ SAL_SOCKET_OBJ_GET(sock, socket); diff --git a/components/utilities/resource/SConscript b/components/utilities/resource/SConscript new file mode 100644 index 0000000000..3f33b790c5 --- /dev/null +++ b/components/utilities/resource/SConscript @@ -0,0 +1,8 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] +group = DefineGroup('resource', src, depend = ['RT_USING_POSIX_PIPE'], CPPPATH = CPPPATH) + +Return('group') diff --git a/components/utilities/resource/resource_id.c b/components/utilities/resource/resource_id.c new file mode 100644 index 0000000000..43f64e7b03 --- /dev/null +++ b/components/utilities/resource/resource_id.c @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-25 RT-Thread First version + */ +#include <rthw.h> +#include <rtthread.h> +#include <resource_id.h> + +void resource_id_init(resource_id_t *mgr, int size, void **res) +{ + if (mgr) + { + mgr->size = size; + mgr->_res = res; + mgr->noused = 0; + mgr->_free = RT_NULL; + } +} + +int resource_id_get(resource_id_t *mgr) +{ + rt_base_t level; + void **cur; + + level = rt_hw_interrupt_disable(); + if (mgr->_free) + { + cur = mgr->_free; + mgr->_free = (void **)*mgr->_free; + rt_hw_interrupt_enable(level); + return cur - mgr->_res; + } + else if (mgr->noused < mgr->size) + { + cur = &mgr->_res[mgr->noused++]; + rt_hw_interrupt_enable(level); + return cur - mgr->_res; + } + rt_hw_interrupt_enable(level); + return -1; +} + +void resource_id_put(resource_id_t *mgr, int no) +{ + rt_base_t level; + void **cur; + + if (no >= 0 && no < mgr->size) + { + level = rt_hw_interrupt_disable(); + cur = &mgr->_res[no]; + *cur = (void *)mgr->_free; + mgr->_free = cur; + rt_hw_interrupt_enable(level); + } +} + diff --git a/components/utilities/resource/resource_id.h b/components/utilities/resource/resource_id.h new file mode 100644 index 0000000000..9706a3cbaf --- /dev/null +++ b/components/utilities/resource/resource_id.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-08-25 RT-Thread First version + */ + +#ifndef RESOURCE_ID_H__ +#define RESOURCE_ID_H__ + +#include <rthw.h> +#include <rtthread.h> + +#define RESOURCE_ID_INIT(size, pool) {size, pool, 0, RT_NULL} + +typedef struct +{ + int size; + void **_res; + int noused; + void **_free; +} resource_id_t; + +void resource_id_init(resource_id_t *mgr, int size, void **res); +int resource_id_get(resource_id_t *mgr); +void resource_id_put(resource_id_t *mgr, int no); + +#endif /*RESOURCE_ID_H__*/ diff --git a/components/utilities/rt-link/src/rtlink_dev.c b/components/utilities/rt-link/src/rtlink_dev.c index 702aed32da..f15294fc4d 100644 --- a/components/utilities/rt-link/src/rtlink_dev.c +++ b/components/utilities/rt-link/src/rtlink_dev.c @@ -49,7 +49,7 @@ int rtlink_fops_open(struct dfs_fd *fd) break; } - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; if (fd->flags & O_NONBLOCK) { rt_device_control(device, RT_LINK_TX_NONBLOCKING | RT_LINK_RX_NONBLOCKING, RT_NULL); @@ -61,7 +61,7 @@ int rtlink_fops_open(struct dfs_fd *fd) int rtlink_fops_close(struct dfs_fd *fd) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; rt_device_set_rx_indicate(device, RT_NULL); return rt_device_close(device); @@ -70,7 +70,7 @@ int rtlink_fops_close(struct dfs_fd *fd) int rtlink_fops_ioctl(struct dfs_fd *fd, int cmd, void *args) { rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; if (cmd == O_NONBLOCK) { @@ -86,7 +86,7 @@ int rtlink_fops_read(struct dfs_fd *fd, void *buf, size_t count) { int size = 0; rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; size = rt_device_read(device, -1, buf, count); if (size <= 0) @@ -100,7 +100,7 @@ int rtlink_fops_write(struct dfs_fd *fd, const void *buf, size_t count) { int size = 0; rt_device_t device; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; size = rt_device_write(device, -1, buf, count); if (size <= 0) @@ -117,7 +117,7 @@ int rtlink_fops_poll(struct dfs_fd *fd, struct rt_pollreq *req) rt_device_t device; struct rt_link_device *rtlink_dev; - device = (rt_device_t)fd->data; + device = (rt_device_t)fd->vnode->data; RT_ASSERT(device != RT_NULL); rtlink_dev = (struct rt_link_device *)device; |
