diff --git a/headers/private/kernel/safemode.h b/headers/private/kernel/safemode.h index 308edfaa10..bcf5751367 100644 --- a/headers/private/kernel/safemode.h +++ b/headers/private/kernel/safemode.h @@ -11,6 +11,9 @@ #include +struct kernel_args; + + #ifdef __cplusplus extern "C" { #endif @@ -19,6 +22,13 @@ extern "C" { status_t get_safemode_option(const char* parameter, char* buffer, size_t* _bufferSize); bool get_safemode_boolean(const char* parameter, bool defaultValue); + +// before driver_settings_init() has been called (limited parsing support) +status_t get_safemode_option_early(kernel_args* args, const char* parameter, + char* buffer, size_t* _bufferSize); +bool get_safemode_boolean_early(kernel_args* args, const char* parameter, + bool defaultValue); + status_t _user_get_safemode_option(const char* parameter, char* buffer, size_t* _bufferSize); diff --git a/src/system/kernel/debug/safemode_settings.cpp b/src/system/kernel/debug/safemode_settings.cpp index 2cb9950783..61334aee7b 100644 --- a/src/system/kernel/debug/safemode_settings.cpp +++ b/src/system/kernel/debug/safemode_settings.cpp @@ -4,59 +4,194 @@ */ +#include + +#include +#include + +#include + #include -#include +#include #include #include -#include + +#ifndef _BOOT_MODE -extern "C" status_t -get_safemode_option(const char* parameter, char* buffer, size_t* _bufferSize) +static status_t +get_option_from_kernel_args(kernel_args* args, const char* settingsName, + const char* parameter, size_t parameterLength, char* buffer, + size_t* _bufferSize) { - status_t status = B_ENTRY_NOT_FOUND; - - void* handle = load_driver_settings(B_SAFEMODE_DRIVER_SETTINGS); - if (handle != NULL) { - status = B_NAME_NOT_FOUND; - - const char* value = get_driver_parameter(handle, parameter, NULL, NULL); - if (value != NULL) { - *_bufferSize = strlcpy(buffer, value, *_bufferSize); - status = B_OK; + // find the settings in the kernel args + const char* settings = NULL; + for (driver_settings_file* file = args->driver_settings; + file != NULL; file = file->next) { + if (strcmp(settingsName, file->name) == 0) { + settings = file->buffer; + break; } - - unload_driver_settings(handle); } - if (status != B_OK) { - // Try kernel settings file as a fall back - handle = load_driver_settings("kernel"); - if (handle != NULL) { - const char* value = get_driver_parameter(handle, parameter, NULL, - NULL); - if (value != NULL) { - *_bufferSize = strlcpy(buffer, value, *_bufferSize); - status = B_OK; + if (settings == NULL) + return B_ENTRY_NOT_FOUND; + + // Unfortunately we can't just use parse_driver_settings_string(), since + // we might not have a working heap yet. So we do very limited parsing + // ourselves. + const char* settingsEnd = settings + strlen(settings); + int32 parameterLevel = 0; + + while (*settings != '\0') { + // find end of line + const char* lineEnd = strchr(settings, '\n'); + const char* nextLine; + if (lineEnd != NULL) + nextLine = lineEnd + 1; + else + nextLine = lineEnd = settingsEnd; + + // ignore any trailing comments + lineEnd = std::find(settings, lineEnd, '#'); + + const char* nameStart = NULL; + const char* nameEnd = NULL; + const char* valueStart = NULL; + const char* valueEnd = NULL; + const char** elementEnd = NULL; + bool sawSeparator = true; + + for (; settings < lineEnd; settings++) { + switch (*settings) { + case '{': + parameterLevel++; + sawSeparator = true; + break; + + case '}': + parameterLevel--; + sawSeparator = true; + break; + + case ';': + // TODO: That's not correct. There should be another loop. + sawSeparator = true; + break; + + default: + if (parameterLevel != 0) + break; + + if (isspace(*settings)) { + sawSeparator = true; + break; + } + + if (!sawSeparator) + break; + + sawSeparator = false; + + if (nameStart == NULL) { + nameStart = settings; + elementEnd = &nameEnd; + } else if (valueStart == NULL) { + valueStart = settings; + elementEnd = &valueEnd; + } + break; } - unload_driver_settings(handle); + if (sawSeparator && elementEnd != NULL) { + *elementEnd = settings; + elementEnd = NULL; + } } + + if (elementEnd != NULL) + *elementEnd = settings; + + if (nameStart != NULL && size_t(nameEnd - nameStart) == parameterLength + && strncmp(parameter, nameStart, parameterLength) == 0) { + if (valueStart == NULL) + return B_NAME_NOT_FOUND; + + size_t length = valueEnd - valueStart; + if (*_bufferSize > 0) { + size_t toCopy = std::min(length, *_bufferSize - 1); + memcpy(buffer, valueStart, toCopy); + buffer[toCopy] = '\0'; + } + + *_bufferSize = length; + return B_OK; + } + + settings = nextLine; + } + + return B_NAME_NOT_FOUND; +} + + +#endif // !_BOOT_MODE + + +static status_t +get_option(kernel_args* args, const char* settingsName, const char* parameter, + size_t parameterLength, char* buffer, size_t* _bufferSize) +{ +#ifndef _BOOT_MODE + if (args != NULL) { + return get_option_from_kernel_args(args, settingsName, parameter, + parameterLength, buffer, _bufferSize); + } +#endif + + void* handle = load_driver_settings(settingsName); + if (handle == NULL) + return B_ENTRY_NOT_FOUND; + + status_t status = B_NAME_NOT_FOUND; + + const char* value = get_driver_parameter(handle, parameter, NULL, NULL); + if (value != NULL) { + *_bufferSize = strlcpy(buffer, value, *_bufferSize); + status = B_OK; + } + + unload_driver_settings(handle); + return status; +} + + +static status_t +get_option(kernel_args* args, const char* parameter, char* buffer, + size_t* _bufferSize) +{ + size_t parameterLength = strlen(parameter); + status_t status = get_option(args, B_SAFEMODE_DRIVER_SETTINGS, parameter, + parameterLength, buffer, _bufferSize); + if (status != B_OK) { + // Try kernel settings file as a fall back + status = get_option(args, "kernel", parameter, parameterLength, buffer, + _bufferSize); } return status; } -extern "C" bool -get_safemode_boolean(const char* parameter, bool defaultValue) +static bool +get_boolean(kernel_args* args, const char* parameter, bool defaultValue) { char value[16]; size_t length = sizeof(value); - if (get_safemode_option(parameter, value, &length) != B_OK) + if (get_option(args, parameter, value, &length) != B_OK) return defaultValue; return !strcasecmp(value, "on") || !strcasecmp(value, "true") @@ -65,6 +200,45 @@ get_safemode_boolean(const char* parameter, bool defaultValue) } +// #pragma mark - + + +status_t +get_safemode_option(const char* parameter, char* buffer, size_t* _bufferSize) +{ + return get_option(NULL, parameter, buffer, _bufferSize); +} + + +bool +get_safemode_boolean(const char* parameter, bool defaultValue) +{ + return get_boolean(NULL, parameter, defaultValue); +} + + +#ifndef _BOOT_MODE + + +status_t +get_safemode_option_early(kernel_args* args, const char* parameter, + char* buffer, size_t* _bufferSize) +{ + return get_option(args, parameter, buffer, _bufferSize); +} + + +bool +get_safemode_boolean_early(kernel_args* args, const char* parameter, + bool defaultValue) +{ + return get_boolean(args, parameter, defaultValue); +} + + +#endif // _BOOT_MODE + + // #pragma mark - syscalls