162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright 2012 Intel Corporation
462306a36Sopenharmony_ci * Author: Josh Triplett <josh@joshtriplett.org>
562306a36Sopenharmony_ci *
662306a36Sopenharmony_ci * Based on the bgrt driver:
762306a36Sopenharmony_ci * Copyright 2012 Red Hat, Inc <mjg@redhat.com>
862306a36Sopenharmony_ci * Author: Matthew Garrett
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/init.h>
1562306a36Sopenharmony_ci#include <linux/acpi.h>
1662306a36Sopenharmony_ci#include <linux/efi.h>
1762306a36Sopenharmony_ci#include <linux/efi-bgrt.h>
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_cistruct acpi_table_bgrt bgrt_tab;
2062306a36Sopenharmony_cisize_t bgrt_image_size;
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_cistruct bmp_header {
2362306a36Sopenharmony_ci	u16 id;
2462306a36Sopenharmony_ci	u32 size;
2562306a36Sopenharmony_ci} __packed;
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_civoid __init efi_bgrt_init(struct acpi_table_header *table)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	void *image;
3062306a36Sopenharmony_ci	struct bmp_header bmp_header;
3162306a36Sopenharmony_ci	struct acpi_table_bgrt *bgrt = &bgrt_tab;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_ci	if (acpi_disabled)
3462306a36Sopenharmony_ci		return;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	if (!efi_enabled(EFI_MEMMAP))
3762306a36Sopenharmony_ci		return;
3862306a36Sopenharmony_ci
3962306a36Sopenharmony_ci	if (table->length < sizeof(bgrt_tab)) {
4062306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: invalid length %u (expected %zu)\n",
4162306a36Sopenharmony_ci		       table->length, sizeof(bgrt_tab));
4262306a36Sopenharmony_ci		return;
4362306a36Sopenharmony_ci	}
4462306a36Sopenharmony_ci	*bgrt = *(struct acpi_table_bgrt *)table;
4562306a36Sopenharmony_ci	/*
4662306a36Sopenharmony_ci	 * Only version 1 is defined but some older laptops (seen on Lenovo
4762306a36Sopenharmony_ci	 * Ivy Bridge models) have a correct version 1 BGRT table with the
4862306a36Sopenharmony_ci	 * version set to 0, so we accept version 0 and 1.
4962306a36Sopenharmony_ci	 */
5062306a36Sopenharmony_ci	if (bgrt->version > 1) {
5162306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: invalid version %u (expected 1)\n",
5262306a36Sopenharmony_ci		       bgrt->version);
5362306a36Sopenharmony_ci		goto out;
5462306a36Sopenharmony_ci	}
5562306a36Sopenharmony_ci	if (bgrt->image_type != 0) {
5662306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: invalid image type %u (expected 0)\n",
5762306a36Sopenharmony_ci		       bgrt->image_type);
5862306a36Sopenharmony_ci		goto out;
5962306a36Sopenharmony_ci	}
6062306a36Sopenharmony_ci	if (!bgrt->image_address) {
6162306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: null image address\n");
6262306a36Sopenharmony_ci		goto out;
6362306a36Sopenharmony_ci	}
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	if (efi_mem_type(bgrt->image_address) != EFI_BOOT_SERVICES_DATA) {
6662306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: invalid image address\n");
6762306a36Sopenharmony_ci		goto out;
6862306a36Sopenharmony_ci	}
6962306a36Sopenharmony_ci	image = early_memremap(bgrt->image_address, sizeof(bmp_header));
7062306a36Sopenharmony_ci	if (!image) {
7162306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: failed to map image header memory\n");
7262306a36Sopenharmony_ci		goto out;
7362306a36Sopenharmony_ci	}
7462306a36Sopenharmony_ci
7562306a36Sopenharmony_ci	memcpy(&bmp_header, image, sizeof(bmp_header));
7662306a36Sopenharmony_ci	early_memunmap(image, sizeof(bmp_header));
7762306a36Sopenharmony_ci	if (bmp_header.id != 0x4d42) {
7862306a36Sopenharmony_ci		pr_notice("Ignoring BGRT: Incorrect BMP magic number 0x%x (expected 0x4d42)\n",
7962306a36Sopenharmony_ci			bmp_header.id);
8062306a36Sopenharmony_ci		goto out;
8162306a36Sopenharmony_ci	}
8262306a36Sopenharmony_ci	bgrt_image_size = bmp_header.size;
8362306a36Sopenharmony_ci	efi_mem_reserve(bgrt->image_address, bgrt_image_size);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return;
8662306a36Sopenharmony_ciout:
8762306a36Sopenharmony_ci	memset(bgrt, 0, sizeof(bgrt_tab));
8862306a36Sopenharmony_ci}
89