blob: 773d20280c51c84eba9dd2441d5e272d6397a640 [file] [log] [blame]
// 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()
*/
}