// SPDX-License-Identifier: GPL-2.0-or-later
/*
* acpi_utils.c - ACPI Utility Functions ($Revision: 10 $)
*
* Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
* Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
*/
#define pr_fmt(fmt) "ACPI: utils: " fmt
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/hardirq.h>
#include <linux/acpi.h>
#include <linux/dynamic_debug.h>
#include "internal.h"
#include "sleep.h"
/* --------------------------------------------------------------------------
Object Evaluation Helpers
-------------------------------------------------------------------------- */
static void acpi_util_eval_error(acpi_handle h, acpi_string p, acpi_status s)
{
acpi_handle_debug(h, "Evaluate [%s]: %s\n", p, acpi_format_exception(s));
}
acpi_status
acpi_extract_package(union acpi_object *package,
struct acpi_buffer *format, struct acpi_buffer *buffer)
{
u32 size_required = 0;
u32 tail_offset = 0;
char *format_string = NULL;
u32 format_count = 0;
u32 i = 0;
u8 *head = NULL;
u8 *tail = NULL;
if (!package || (package->type != ACPI_TYPE_PACKAGE)
|| (package->package.count < 1)) {
pr_debug("Invalid package argument\n");
return AE_BAD_PARAMETER;
}
if (!format || !format->pointer || (format->length < 1)) {
pr_debug("Invalid format argument\n");
return AE_BAD_PARAMETER;
}
if (!buffer) {
pr_debug("Invalid buffer argument\n");
return AE_BAD_PARAMETER;
}
format_count = (format->length / sizeof(char)) - 1;
if (format_count > package->package.count) {
pr_debug("Format specifies more objects [%d] than present [%d]\n",
format_count, package->package.count);
return AE_BAD_DATA;
}
format_string = format->pointer;
/*
* Calculate size_required.
*/
for (i = 0; i < format_count; i++) {
union acpi_object *element = &(package->package.elements[i]);
switch (element->type) {
case ACPI_TYPE_INTEGER:
switch (format_string[i]) {
case 'N':
size_required += sizeof(u64);
tail_offset += sizeof(u64);
break;
case 'S':
size_required +=
sizeof(char *) + sizeof(u64) +
sizeof(char);
tail_offset += sizeof(char *);
break;
default:
pr_debug("Invalid package element [%d]: got number, expected [%c]\n",
i, format_string[i]);
return AE_BAD_DATA;
}
break;
case ACPI_TYPE_STRING:
case ACPI_TYPE_BUFFER:
switch (format_string[i]) {
case 'S':
size_required +=
sizeof(char *) +
(element->string.length * sizeof(char)) +
sizeof(char);
tail_offset += sizeof(char *);
break;
case 'B':
size_required +=
sizeof(u8 *) + element->buffer.length;
tail_offset += sizeof(u8 *);
break;
default:
pr_debug("Invalid package element [%d] got string/buffer, expected [%c]\n",
i, format_string[i]);
return AE_BAD_DATA;
}
break;
case ACPI_TYPE_LOCAL_REFERENCE:
switch (format_string[i]) {
case 'R':
size_required += sizeof(void *);
tail_offset += sizeof(void *);
break;
default:
pr_debug("Invalid package element [%d] got reference, expected [%c]\n",
i, format_string[i]);
return AE_BAD_DATA;
}
break;
case ACPI_TYPE_PACKAGE:
default:
pr_debug("Unsupported element at index=%d\n", i);
/* TBD: handle nested packages... */
return AE_SUPPORT;
}
}
/*
* Validate output buffer.
*/
if (buffer->length == ACPI_ALLOCATE_BUFFER) {
buffer->pointer = ACPI_ALLOCATE_ZEROED(size_required);
if (!buffer->pointer)
return AE_NO_MEMORY;
buffer->length = size_required;
} else {
if (buffer->length < size_required) {
buffer->length = size_required;
return AE_BUFFER_OVERFLOW;
} else if (buffer->length != size_required ||
!buffer->pointer) {
return AE_BAD_PARAMETER;
}
}
head = buffer->pointer;
tail = buffer->pointer + tail_offset;
/*
* Extract package data.
*/
for (i = 0; i < format_count; i++) {
u8 **pointer = NULL;
union acpi_object *element = &(package->package.elements[i]);
switch (element->type)