diff options
Diffstat (limited to 'tools/testing/selftests/powerpc/utils.c')
| -rw-r--r-- | tools/testing/selftests/powerpc/utils.c | 412 | 
1 files changed, 353 insertions, 59 deletions
diff --git a/tools/testing/selftests/powerpc/utils.c b/tools/testing/selftests/powerpc/utils.c index 1f36ee1a909a..7c8cfedb012a 100644 --- a/tools/testing/selftests/powerpc/utils.c +++ b/tools/testing/selftests/powerpc/utils.c @@ -8,6 +8,8 @@  #include <elf.h>  #include <errno.h>  #include <fcntl.h> +#include <inttypes.h> +#include <limits.h>  #include <link.h>  #include <sched.h>  #include <stdio.h> @@ -26,34 +28,360 @@  static char auxv[4096]; -int read_auxv(char *buf, ssize_t buf_size) +int read_file(const char *path, char *buf, size_t count, size_t *len)  { -	ssize_t num; -	int rc, fd; +	ssize_t rc; +	int fd; +	int err; +	char eof; -	fd = open("/proc/self/auxv", O_RDONLY); -	if (fd == -1) { -		perror("open"); +	fd = open(path, O_RDONLY); +	if (fd < 0)  		return -errno; + +	rc = read(fd, buf, count); +	if (rc < 0) { +		err = -errno; +		goto out;  	} -	num = read(fd, buf, buf_size); -	if (num < 0) { -		perror("read"); -		rc = -EIO; +	if (len) +		*len = rc; + +	/* Overflow if there are still more bytes after filling the buffer */ +	if (rc == count) { +		rc = read(fd, &eof, 1); +		if (rc != 0) { +			err = -EOVERFLOW; +			goto out; +		} +	} + +	err = 0; + +out: +	close(fd); +	errno = -err; +	return err; +} + +int read_file_alloc(const char *path, char **buf, size_t *len) +{ +	size_t read_offset = 0; +	size_t buffer_len = 0; +	char *buffer = NULL; +	int err; +	int fd; + +	fd = open(path, O_RDONLY); +	if (fd < 0) +		return -errno; + +	/* +	 * We don't use stat & preallocate st_size because some non-files +	 * report 0 file size. Instead just dynamically grow the buffer +	 * as needed. +	 */ +	while (1) { +		ssize_t rc; + +		if (read_offset >= buffer_len / 2) { +			char *next_buffer; + +			buffer_len = buffer_len ? buffer_len * 2 : 4096; +			next_buffer = realloc(buffer, buffer_len); +			if (!next_buffer) { +				err = -errno; +				goto out; +			} +			buffer = next_buffer; +		} + +		rc = read(fd, buffer + read_offset, buffer_len - read_offset); +		if (rc < 0) { +			err = -errno; +			goto out; +		} + +		if (rc == 0) +			break; + +		read_offset += rc; +	} + +	*buf = buffer; +	if (len) +		*len = read_offset; + +	err = 0; + +out: +	close(fd); +	if (err) +		free(buffer); +	errno = -err; +	return err; +} + +int write_file(const char *path, const char *buf, size_t count) +{ +	int fd; +	int err; +	ssize_t rc; + +	fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644); +	if (fd < 0) +		return -errno; + +	rc = write(fd, buf, count); +	if (rc < 0) { +		err = -errno;  		goto out;  	} -	if (num > buf_size) { -		printf("overflowed auxv buffer\n"); -		rc = -EOVERFLOW; +	if (rc != count) { +		err = -EOVERFLOW;  		goto out;  	} -	rc = 0; +	err = 0; +  out:  	close(fd); -	return rc; +	errno = -err; +	return err; +} + +int read_auxv(char *buf, ssize_t buf_size) +{ +	int err; + +	err = read_file("/proc/self/auxv", buf, buf_size, NULL); +	if (err) { +		perror("Error reading /proc/self/auxv"); +		return err; +	} + +	return 0; +} + +int read_debugfs_file(const char *subpath, char *buf, size_t count) +{ +	char path[PATH_MAX] = "/sys/kernel/debug/"; + +	strncat(path, subpath, sizeof(path) - strlen(path) - 1); + +	return read_file(path, buf, count, NULL); +} + +int write_debugfs_file(const char *subpath, const char *buf, size_t count) +{ +	char path[PATH_MAX] = "/sys/kernel/debug/"; + +	strncat(path, subpath, sizeof(path) - strlen(path) - 1); + +	return write_file(path, buf, count); +} + +static int validate_int_parse(const char *buffer, size_t count, char *end) +{ +	int err = 0; + +	/* Require at least one digit */ +	if (end == buffer) { +		err = -EINVAL; +		goto out; +	} + +	/* Require all remaining characters be whitespace-ish */ +	for (; end < buffer + count; end++) { +		if (*end == '\0') +			break; + +		if (*end != ' ' && *end != '\n') { +			err = -EINVAL; +			goto out; +		} +	} + +out: +	errno = -err; +	return err; +} + +static int parse_bounded_int(const char *buffer, size_t count, intmax_t *result, +			     int base, intmax_t min, intmax_t max) +{ +	int err; +	char *end; + +	errno = 0; +	*result = strtoimax(buffer, &end, base); + +	if (errno) +		return -errno; + +	err = validate_int_parse(buffer, count, end); +	if (err) +		goto out; + +	if (*result < min || *result > max) +		err = -EOVERFLOW; + +out: +	errno = -err; +	return err; +} + +static int parse_bounded_uint(const char *buffer, size_t count, uintmax_t *result, +			      int base, uintmax_t max) +{ +	int err = 0; +	char *end; + +	errno = 0; +	*result = strtoumax(buffer, &end, base); + +	if (errno) +		return -errno; + +	err = validate_int_parse(buffer, count, end); +	if (err) +		goto out; + +	if (*result > max) +		err = -EOVERFLOW; + +out: +	errno = -err; +	return err; +} + +int parse_intmax(const char *buffer, size_t count, intmax_t *result, int base) +{ +	return parse_bounded_int(buffer, count, result, base, INTMAX_MIN, INTMAX_MAX); +} + +int parse_uintmax(const char *buffer, size_t count, uintmax_t *result, int base) +{ +	return parse_bounded_uint(buffer, count, result, base, UINTMAX_MAX); +} + +int parse_int(const char *buffer, size_t count, int *result, int base) +{ +	intmax_t parsed; +	int err = parse_bounded_int(buffer, count, &parsed, base, INT_MIN, INT_MAX); + +	*result = parsed; +	return err; +} + +int parse_uint(const char *buffer, size_t count, unsigned int *result, int base) +{ +	uintmax_t parsed; +	int err = parse_bounded_uint(buffer, count, &parsed, base, UINT_MAX); + +	*result = parsed; +	return err; +} + +int parse_long(const char *buffer, size_t count, long *result, int base) +{ +	intmax_t parsed; +	int err = parse_bounded_int(buffer, count, &parsed, base, LONG_MIN, LONG_MAX); + +	*result = parsed; +	return err; +} + +int parse_ulong(const char *buffer, size_t count, unsigned long *result, int base) +{ +	uintmax_t parsed; +	int err = parse_bounded_uint(buffer, count, &parsed, base, ULONG_MAX); + +	*result = parsed; +	return err; +} + +int read_long(const char *path, long *result, int base) +{ +	int err; +	char buffer[32] = {0}; + +	err = read_file(path, buffer, sizeof(buffer) - 1, NULL); +	if (err) +		return err; + +	return parse_long(buffer, sizeof(buffer), result, base); +} + +int read_ulong(const char *path, unsigned long *result, int base) +{ +	int err; +	char buffer[32] = {0}; + +	err = read_file(path, buffer, sizeof(buffer) - 1, NULL); +	if (err) +		return err; + +	return parse_ulong(buffer, sizeof(buffer), result, base); +} + +int write_long(const char *path, long result, int base) +{ +	int err; +	int len; +	char buffer[32]; + +	/* Decimal only for now: no format specifier for signed hex values */ +	if (base != 10) { +		err = -EINVAL; +		goto out; +	} + +	len = snprintf(buffer, sizeof(buffer), "%ld", result); +	if (len < 0 || len >= sizeof(buffer)) { +		err = -EOVERFLOW; +		goto out; +	} + +	err = write_file(path, buffer, len); + +out: +	errno = -err; +	return err; +} + +int write_ulong(const char *path, unsigned long result, int base) +{ +	int err; +	int len; +	char buffer[32]; +	char *fmt; + +	switch (base) { +	case 10: +		fmt = "%lu"; +		break; +	case 16: +		fmt = "%lx"; +		break; +	default: +		err = -EINVAL; +		goto out; +	} + +	len = snprintf(buffer, sizeof(buffer), fmt, result); +	if (len < 0 || len >= sizeof(buffer)) { +		err = -errno; +		goto out; +	} + +	err = write_file(path, buffer, len); + +out: +	errno = -err; +	return err;  }  void *find_auxv_entry(int type, char *auxv) @@ -142,65 +470,31 @@ bool is_ppc64le(void)  int read_sysfs_file(char *fpath, char *result, size_t result_size)  {  	char path[PATH_MAX] = "/sys/"; -	int rc = -1, fd;  	strncat(path, fpath, PATH_MAX - strlen(path) - 1); -	if ((fd = open(path, O_RDONLY)) < 0) -		return rc; - -	rc = read(fd, result, result_size); - -	close(fd); - -	if (rc < 0) -		return rc; - -	return 0; +	return read_file(path, result, result_size, NULL);  } -int read_debugfs_file(char *debugfs_file, int *result) +int read_debugfs_int(const char *debugfs_file, int *result)  { -	int rc = -1, fd; -	char path[PATH_MAX]; -	char value[16]; - -	strcpy(path, "/sys/kernel/debug/"); -	strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); +	int err; +	char value[16] = {0}; -	if ((fd = open(path, O_RDONLY)) < 0) -		return rc; +	err = read_debugfs_file(debugfs_file, value, sizeof(value) - 1); +	if (err) +		return err; -	if ((rc = read(fd, value, sizeof(value))) < 0) -		return rc; - -	value[15] = 0; -	*result = atoi(value); -	close(fd); - -	return 0; +	return parse_int(value, sizeof(value), result, 10);  } -int write_debugfs_file(char *debugfs_file, int result) +int write_debugfs_int(const char *debugfs_file, int result)  { -	int rc = -1, fd; -	char path[PATH_MAX];  	char value[16]; -	strcpy(path, "/sys/kernel/debug/"); -	strncat(path, debugfs_file, PATH_MAX - strlen(path) - 1); - -	if ((fd = open(path, O_WRONLY)) < 0) -		return rc; -  	snprintf(value, 16, "%d", result); -	if ((rc = write(fd, value, strlen(value))) < 0) -		return rc; - -	close(fd); - -	return 0; +	return write_debugfs_file(debugfs_file, value, strlen(value));  }  static long perf_event_open(struct perf_event_attr *hw_event, pid_t pid,  |