203 lines
4.2 KiB
C
203 lines
4.2 KiB
C
// SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
|
|
/*
|
|
* NVRAM support
|
|
*
|
|
* Copyright 2013-2018 IBM Corp.
|
|
*/
|
|
|
|
#include <skiboot.h>
|
|
#include <fsp.h>
|
|
#include <opal.h>
|
|
#include <lock.h>
|
|
#include <device.h>
|
|
#include <platform.h>
|
|
#include <nvram.h>
|
|
#include <timebase.h>
|
|
|
|
static void *nvram_image;
|
|
static uint32_t nvram_size;
|
|
|
|
static bool nvram_ready; /* has the nvram been loaded? */
|
|
static bool nvram_valid; /* is the nvram format ok? */
|
|
|
|
static int64_t opal_read_nvram(uint64_t buffer, uint64_t size, uint64_t offset)
|
|
{
|
|
if (!nvram_ready)
|
|
return OPAL_HARDWARE;
|
|
|
|
if (!opal_addr_valid((void *)buffer))
|
|
return OPAL_PARAMETER;
|
|
|
|
if (offset >= nvram_size || (offset + size) > nvram_size)
|
|
return OPAL_PARAMETER;
|
|
|
|
memcpy((void *)buffer, nvram_image + offset, size);
|
|
return OPAL_SUCCESS;
|
|
}
|
|
opal_call(OPAL_READ_NVRAM, opal_read_nvram, 3);
|
|
|
|
static int64_t opal_write_nvram(uint64_t buffer, uint64_t size, uint64_t offset)
|
|
{
|
|
if (!nvram_ready)
|
|
return OPAL_HARDWARE;
|
|
|
|
if (!opal_addr_valid((void *)buffer))
|
|
return OPAL_PARAMETER;
|
|
|
|
if (offset >= nvram_size || (offset + size) > nvram_size)
|
|
return OPAL_PARAMETER;
|
|
memcpy(nvram_image + offset, (void *)buffer, size);
|
|
if (platform.nvram_write)
|
|
platform.nvram_write(offset, nvram_image + offset, size);
|
|
|
|
/* The host OS has written to the NVRAM so we can't be sure that it's
|
|
* well formatted.
|
|
*/
|
|
nvram_valid = false;
|
|
|
|
return OPAL_SUCCESS;
|
|
}
|
|
opal_call(OPAL_WRITE_NVRAM, opal_write_nvram, 3);
|
|
|
|
bool nvram_validate(void)
|
|
{
|
|
if (!nvram_valid) {
|
|
if (!nvram_check(nvram_image, nvram_size))
|
|
nvram_valid = true;
|
|
}
|
|
|
|
return nvram_valid;
|
|
}
|
|
|
|
static void nvram_reformat(void)
|
|
{
|
|
if (nvram_format(nvram_image, nvram_size)) {
|
|
prerror("NVRAM: Failed to format NVRAM!\n");
|
|
nvram_valid = false;
|
|
return;
|
|
}
|
|
|
|
/* Write the whole thing back */
|
|
if (platform.nvram_write)
|
|
platform.nvram_write(0, nvram_image, nvram_size);
|
|
|
|
nvram_validate();
|
|
}
|
|
|
|
void nvram_reinit(void)
|
|
{
|
|
/* It's possible we failed to load nvram at boot. */
|
|
if (!nvram_ready)
|
|
nvram_init();
|
|
else if (!nvram_validate())
|
|
nvram_reformat();
|
|
}
|
|
|
|
void nvram_read_complete(bool success)
|
|
{
|
|
struct dt_node *np;
|
|
|
|
/* Read not successful, error out and free the buffer */
|
|
if (!success) {
|
|
free(nvram_image);
|
|
nvram_size = 0;
|
|
return;
|
|
}
|
|
|
|
if (!nvram_validate())
|
|
nvram_reformat();
|
|
|
|
/* Add nvram node */
|
|
np = dt_new(opal_node, "nvram");
|
|
dt_add_property_cells(np, "#bytes", nvram_size);
|
|
dt_add_property_string(np, "compatible", "ibm,opal-nvram");
|
|
|
|
/* Mark ready */
|
|
nvram_ready = true;
|
|
}
|
|
|
|
bool nvram_wait_for_load(void)
|
|
{
|
|
uint64_t started;
|
|
|
|
/* Short cut */
|
|
if (nvram_ready)
|
|
return true;
|
|
|
|
/* Tell the caller it will never happen */
|
|
if (!platform.nvram_info)
|
|
return false;
|
|
|
|
/*
|
|
* One of two things has happened here.
|
|
* 1. nvram_wait_for_load() was called before nvram_init()
|
|
* 2. The read of NVRAM failed.
|
|
* Either way, this is quite a bad event.
|
|
*/
|
|
if (!nvram_image && !nvram_size) {
|
|
prlog(PR_CRIT, "NVRAM: Possible wait before nvram_init()!\n");
|
|
return false;
|
|
}
|
|
|
|
started = mftb();
|
|
|
|
while (!nvram_ready) {
|
|
opal_run_pollers();
|
|
/* If the read fails, tell the caller */
|
|
if (!nvram_image && !nvram_size)
|
|
return false;
|
|
}
|
|
|
|
prlog(PR_DEBUG, "NVRAM: Waited %lums for nvram to load\n",
|
|
tb_to_msecs(mftb() - started));
|
|
|
|
return true;
|
|
}
|
|
|
|
bool nvram_has_loaded(void)
|
|
{
|
|
return nvram_ready;
|
|
}
|
|
|
|
void nvram_init(void)
|
|
{
|
|
int rc;
|
|
|
|
if (!platform.nvram_info)
|
|
return;
|
|
rc = platform.nvram_info(&nvram_size);
|
|
if (rc) {
|
|
prerror("NVRAM: Error %d retrieving nvram info\n", rc);
|
|
return;
|
|
}
|
|
prlog(PR_INFO, "NVRAM: Size is %d KB\n", nvram_size >> 10);
|
|
if (nvram_size > 0x100000) {
|
|
prlog(PR_WARNING, "NVRAM: Cropping to 1MB !\n");
|
|
nvram_size = 0x100000;
|
|
}
|
|
|
|
/*
|
|
* We allocate the nvram image with 4k alignment to make the
|
|
* FSP backend job's easier
|
|
*/
|
|
nvram_image = memalign(0x1000, nvram_size);
|
|
if (!nvram_image) {
|
|
prerror("NVRAM: Failed to allocate nvram image\n");
|
|
nvram_size = 0;
|
|
return;
|
|
}
|
|
|
|
/* Read it in */
|
|
rc = platform.nvram_start_read(nvram_image, 0, nvram_size);
|
|
if (rc) {
|
|
prerror("NVRAM: Failed to read NVRAM from FSP !\n");
|
|
nvram_size = 0;
|
|
free(nvram_image);
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* We'll get called back later (or recursively from
|
|
* nvram_start_read) in nvram_read_complete()
|
|
*/
|
|
}
|