kprintf: fix vararg bug
Turns out you can't pass a va_list to subroutines as per the C standard, even though it worked perfectly fine on ARM. Well then, the entire kprintf thing needs to be refactored anyway at some point in the future, so that more formatting options are supported.
This commit is contained in:
		
							parent
							
								
									25e00354ec
								
							
						
					
					
						commit
						8fb2f7987c
					
				
					 3 changed files with 88 additions and 65 deletions
				
			
		|  | @ -3,7 +3,7 @@ | |||
| #pragma once | ||||
| 
 | ||||
| /**
 | ||||
|  * @file Header for the `printf()` family of functions. | ||||
|  * @brief Header for the `kprintf()` family of functions. | ||||
|  */ | ||||
| 
 | ||||
| #include <stdarg.h> | ||||
|  | @ -11,15 +11,61 @@ | |||
| #include <gay/types.h> | ||||
| #include <gay/toolchain.h> | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Print to the kernel log. | ||||
|  * | ||||
|  * @param fmt A printf style format string | ||||
|  * @returns The amount of bytes written, | ||||
|  *	or a negative number from `errno.h` on failure | ||||
|  */ | ||||
| __attribute__(( format(printf, 1, 2) )) | ||||
| int kprintf(const char *__restrict fmt, ...); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Print to the kernel log. | ||||
|  * | ||||
|  * @param fmt A printf style format string | ||||
|  * @param args The variable arguments pointer | ||||
|  * @returns The amount of bytes written, | ||||
|  *	or a negative number from `errno.h` on failure | ||||
|  */ | ||||
| int kvprintf(const char *__restrict fmt, va_list args); | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Renderer functions that `kprintf()` and friends call for writing. | ||||
|  * Depending on the current kernel log output target (which in turn depends | ||||
|  * primarily on the current boot stage), this will be implemented by different | ||||
|  * subsystems.  To change the renderer, call `kprintf_set_renderer()`. | ||||
|  */ | ||||
| struct kprintf_renderer { | ||||
| 	ssize_t (*write)(struct kprintf_renderer *renderer, const void *buf, size_t size); | ||||
| 	/**
 | ||||
| 	 * @brief Write to the kernel log. | ||||
| 	 * | ||||
| 	 * @param renderer A reference to the original structure | ||||
| 	 * @param buf Data to write | ||||
| 	 * @param len Amount of data in bytes | ||||
| 	 * @returns The amount of bytes actually written, | ||||
| 	 *	or a negative code from `errno.h` on failure | ||||
| 	 */ | ||||
| 	ssize_t (*write)(struct kprintf_renderer *renderer, const void *buf, size_t len); | ||||
| 	/**
 | ||||
| 	 * @brief Flush the kernel log buffer. | ||||
| 	 * On implementations that don't have a buffer, this can be a no-op. | ||||
| 	 * If that is the case, this call should always return 0. | ||||
| 	 * | ||||
| 	 * @param renderer A reference to the original structure | ||||
| 	 * @returns The amount of bytes flushed out (0 if none), | ||||
| 	 *	or a negative code from `errno.h` on failure | ||||
| 	 */ | ||||
| 	ssize_t (*flush)(struct kprintf_renderer *renderer); | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Set the current renderer for the `fprintf()` family of functions. | ||||
|  * | ||||
|  * @param renderer Implementation of the write and flush functions | ||||
|  * @returns 0 on success, or a negative code from `errno.h` on failure | ||||
|  */ | ||||
| int kprintf_set_renderer(struct kprintf_renderer *renderer); | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -20,11 +20,11 @@ typedef __UINT64_TYPE__		u64; | |||
| 
 | ||||
| typedef __SIZE_TYPE__		size_t; | ||||
| typedef __PTRDIFF_TYPE__	ptrdiff_t; | ||||
| typedef __PTRDIFF_TYPE__	intptr_t; | ||||
| typedef __INTPTR_TYPE__		intptr_t; | ||||
| 
 | ||||
| #define unsigned signed /* l00k @ d33z m4d h4xx0r sk1llz!!1 */ | ||||
| typedef __SIZE_TYPE__		ssize_t; | ||||
| typedef __PTRDIFF_TYPE__	uintptr_t; | ||||
| typedef __UINTPTR_TYPE__	uintptr_t; | ||||
| #undef unsigned | ||||
| 
 | ||||
| /*
 | ||||
|  |  | |||
|  | @ -38,10 +38,8 @@ int kprintf_set_renderer(struct kprintf_renderer *new) | |||
| static int fmt_handle_ptr(uintptr_t ptr) | ||||
| { | ||||
| 	static const char table[] = { | ||||
| 		'0', '1', '2', '3', | ||||
| 		'4', '5', '6', '7', | ||||
| 		'8', '9', 'a', 'b', | ||||
| 		'c', 'd', 'e', 'f', | ||||
| 		'0', '1', '2', '3', '4', '5', '6', '7', | ||||
| 		'8', '9', 'a', 'b', 'c', 'd', 'e', 'f', | ||||
| 	}; | ||||
| 
 | ||||
| 	int ret; | ||||
|  | @ -53,9 +51,9 @@ static int fmt_handle_ptr(uintptr_t ptr) | |||
| 	str[1] = 'x'; | ||||
| 
 | ||||
| 	do { | ||||
| 		*pos-- = table[ptr % ARRAY_SIZE(table)]; | ||||
| 		*pos-- = table[ptr & 0xf]; | ||||
| 		ptr >>= 4; | ||||
| 	} while (pos != &str[2]); | ||||
| 	} while (pos != &str[1]); | ||||
| 
 | ||||
| 	ret = renderer->write(renderer, str, 2 * sizeof(uintptr_t) + 2); | ||||
| 	return ret; | ||||
|  | @ -97,19 +95,10 @@ static inline int fmt_handle_int(int i) | |||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| /**
 | ||||
|  * @brief Parse a formatting escape sequence, fetch the parameter from the `va_arg` | ||||
|  * stack, and print the resulting string to the standard serial console. | ||||
|  * | ||||
|  * @param pos A reference to the pointer to the fmt sequence beginner (`%`). | ||||
|  *	This is updated to point to the first character *after* the entire | ||||
|  *	escape sequence. | ||||
|  * @param args A pointer to the varargs list.  Will be manipulated. | ||||
|  * @returns The amount of bytes written, or a negative POSIX error code. | ||||
|  */ | ||||
| static inline int fmt_handle(const char **pos, va_list args) | ||||
| int kvprintf(const char *fmt, va_list args) | ||||
| { | ||||
| 	int ret = 0; | ||||
| 	ssize_t ret = 0; | ||||
| 	const char *tmp = fmt; | ||||
| 	union { | ||||
| 		char c; | ||||
| 		int d; | ||||
|  | @ -118,48 +107,6 @@ static inline int fmt_handle(const char **pos, va_list args) | |||
| 		unsigned int u; | ||||
| 	} val; | ||||
| 
 | ||||
| 	switch (**pos) { | ||||
| 	case '%': /* literal percent sign */ | ||||
| 		ret = renderer->write(renderer, *pos, sizeof(**pos)); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'c': /* char */ | ||||
| 		val.c = va_arg(args, int); /* POSIX says chars are cast to an int */ | ||||
| 		ret = renderer->write(renderer, &val.c, sizeof(val.c)); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'd': /* int */ | ||||
| 		val.d = va_arg(args, typeof(val.d)); | ||||
| 		ret = fmt_handle_int(val.d); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'p': /* ptr */ | ||||
| 		val.p = va_arg(args, typeof(val.p)); | ||||
| 		ret = fmt_handle_ptr(val.p); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 's': /* string */ | ||||
| 		val.s = va_arg(args, typeof(val.s)); | ||||
| 		ret = (int)strlen(val.s); | ||||
| 		ret = renderer->write(renderer, val.s, (size_t)ret); | ||||
| 		break; | ||||
| 
 | ||||
| 	case 'u': /* unsigned int */ | ||||
| 		val.u = va_arg(args, typeof(val.u)); | ||||
| 		ret = fmt_handle_uint(val.u); | ||||
| 		break; | ||||
| 	} | ||||
| 
 | ||||
| 	(*pos)++; | ||||
| 
 | ||||
| 	return ret; | ||||
| } | ||||
| 
 | ||||
| int kvprintf(const char *fmt, va_list args) | ||||
| { | ||||
| 	ssize_t ret = 0; | ||||
| 	const char *tmp = fmt; | ||||
| 
 | ||||
| 	while (*tmp != '\0') { | ||||
| 		if (*tmp++ == '%') { | ||||
| 			/* flush out everything we have so far (minus one char for %) */ | ||||
|  | @ -174,7 +121,37 @@ int kvprintf(const char *fmt, va_list args) | |||
| 			} | ||||
| 			ret += write_ret; | ||||
| 
 | ||||
| 			ssize_t fmt_ret = fmt_handle(&tmp, args); | ||||
| 			ssize_t fmt_ret = 0; | ||||
| 			switch (*tmp) { | ||||
| 			case '%': /* literal percent sign */ | ||||
| 				fmt_ret = renderer->write(renderer, tmp, sizeof(*tmp)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'c': /* char */ | ||||
| 				val.c = va_arg(args, int); /* POSIX wants an int */ | ||||
| 				fmt_ret = renderer->write(renderer, &val.c, sizeof(val.c)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'd': /* int */ | ||||
| 			case 'i': | ||||
| 				fmt_ret = fmt_handle_int(va_arg(args, int)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'p': /* ptr */ | ||||
| 				fmt_ret = fmt_handle_ptr(va_arg(args, uintptr_t)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 's': /* string */ | ||||
| 				val.s = va_arg(args, char *); | ||||
| 				fmt_ret = renderer->write(renderer, val.s, strlen(val.s)); | ||||
| 				break; | ||||
| 
 | ||||
| 			case 'u': /* unsigned int */ | ||||
| 				fmt_ret = fmt_handle_uint(va_arg(args, unsigned int)); | ||||
| 				break; | ||||
| 			} | ||||
| 			tmp++; | ||||
| 
 | ||||
| 			/*
 | ||||
| 			 * act as if the current position were the beginning in | ||||
| 			 * order to make the first step of this if block easier | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue