// SPDX-License-Identifier: GPL-2.0
/*
* Common methods for use with hp-bioscfg driver
*
* Copyright (c) 2022 HP Development Company, L.P.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/wmi.h>
#include "bioscfg.h"
#include "../../firmware_attributes_class.h"
#include <linux/nls.h>
#include <linux/errno.h>
MODULE_AUTHOR("Jorge Lopez <jorge.lopez2@hp.com>");
MODULE_DESCRIPTION("HP BIOS Configuration Driver");
MODULE_LICENSE("GPL");
struct bioscfg_priv bioscfg_drv = {
.mutex = __MUTEX_INITIALIZER(bioscfg_drv.mutex),
};
static struct class *fw_attr_class;
ssize_t display_name_language_code_show(struct kobject *kobj,
struct kobj_attribute *attr,
char *buf)
{
return sysfs_emit(buf, "%s\n", LANG_CODE_STR);
}
struct kobj_attribute common_display_langcode =
__ATTR_RO(display_name_language_code);
int hp_get_integer_from_buffer(u8 **buffer, u32 *buffer_size, u32 *integer)
{
int *ptr = PTR_ALIGN((int *)*buffer, sizeof(int));
/* Ensure there is enough space remaining to read the integer */
if (*buffer_size < sizeof(int))
return -EINVAL;
*integer = *(ptr++);
*buffer = (u8 *)ptr;
*buffer_size -= sizeof(int);
return 0;
}
int hp_get_string_from_buffer(u8 **buffer, u32 *buffer_size, char *dst, u32 dst_size)
{
u16 *src = (u16 *)*buffer;
u16 src_size;
u16 size;
int i;
int conv_dst_size;
if (*buffer_size < sizeof(u16))
return -EINVAL;
src_size = *(src++);
/* size value in u16 chars */
size = src_size / sizeof(u16);
/* Ensure there is enough space remaining to read and convert
* the string
*/
if (*buffer_size < src_size)
return -EINVAL;
for (i = 0; i < size; i++)
if (src[i] == '\\' ||
src[i] == '\r' ||
src[i] == '\n' ||
src[i] == '\t')
size++;
/*
* Conversion is limited to destination string max number of
* bytes.
*/
conv_dst_size = size;
if (size > dst_size)
conv_dst_size = dst_size - 1;
/*
* convert from UTF-16 unicode to ASCII
*/
utf16s_to_utf8s(src, src_size, UTF16_HOST_ENDIAN, dst, conv_dst_size);
dst[conv_dst_size] = 0;
for (i = 0; i < conv_dst_size; i++) {
if (*src == '\\' ||
*src == '\r' ||
*src == '\n' ||
*src == '\t') {
dst[i++] = '\\';
if (i == conv_dst_size)
break;
}
if (*src == '\r')
dst[i] = 'r';
else if (*src == '\n')
dst[i] = 'n';
else if (*src == '\t')
dst[i] = 't';
else if (*src == '"')
dst[i] = '\'';
else
dst[i] = *src;
src++;
}
*buffer = (u8 *)src;
*buffer_size -= size * sizeof(u16);
return size;
}
int hp_get_common_data_from_buffer(u8 **buffer_ptr, u32 *buffer_size,
struct common_data *common_data)
{
int ret = 0;
int reqs;
// PATH:
ret = hp_get_string_from_buffer(buffer_ptr, buffer_size, common_data->path,
sizeof(common_data->path));
if (ret < 0)
goto common_exit;
// IS_READONLY:
ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
&common_data->is_readonly);
if (ret < 0)
goto common_exit;
//DISPLAY_IN_UI:
ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
&common_data->display_in_ui);
if (ret < 0)
goto common_exit;
// REQUIRES_PHYSICAL_PRESENCE:
ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
&common_data->requires_physical_presence);
if (ret < 0)
goto common_exit;
// SEQUENCE:
ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
&common_data->sequence);
if (ret < 0)
goto common_exit;
// PREREQUISITES_SIZE:
ret = hp_get_integer_from_buffer(buffer_ptr, buffer_size,
&common_data->prerequisites_size);
if (ret < 0)
goto common_exit;
if (common_data->prerequisites_size > MAX_PREREQUISITES_SIZE) {
/* Report a message and limit prerequisite size to maximum value */
pr_warn("Prerequisites size value exceeded the maximum number of elements supported or data may be malformed<