mirror of https://github.com/ARMmbed/mbed-os.git
SFDP: Add support for multiple configurations and sector maps
A Sector Map Parameter Table contains a sequence of the following descriptors: * (Optional) configuration detection command descriptors, one for each command to run to determine the current configuration. This exists only if the flash layout is configurable. * Sector map descriptors, one for each possible configuration. On a flash device with a non-configurable layout, there is only one such descriptor. Previously we only supported the non-configurable case with a single descriptor. This commit adds support for multiple configurations.pull/14989/head
parent
f88bf828ab
commit
6bb23815c5
|
|
@ -245,6 +245,110 @@ int sfdp_parse_headers(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, ui
|
|||
return 0;
|
||||
}
|
||||
|
||||
static constexpr size_t min_descriptor_size = 8; // two DWORDs
|
||||
|
||||
static inline bool is_last_descriptor(const uint8_t *descriptor)
|
||||
{
|
||||
// Last descriptor of the current type (detection command/sector map)
|
||||
MBED_ASSERT(nullptr != descriptor);
|
||||
return descriptor[0] & 0x01;
|
||||
}
|
||||
|
||||
static inline bool is_sector_map_descriptor(const uint8_t *descriptor)
|
||||
{
|
||||
// true - sector map descriptor
|
||||
// false - configuration detection command descriptor
|
||||
MBED_ASSERT(nullptr != descriptor);
|
||||
return descriptor[0] & 0x02;
|
||||
}
|
||||
|
||||
static int sfdp_detect_sector_map_configuration(
|
||||
Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader,
|
||||
sfdp_hdr_info &sfdp_info,
|
||||
uint8_t *&descriptor,
|
||||
const uint8_t *table_end,
|
||||
uint8_t &config)
|
||||
{
|
||||
config = 0;
|
||||
|
||||
// If the table starts with a sector map descriptor instead of a configuration
|
||||
// detection command descriptor, this device has only one configuration (i.e. is
|
||||
// not configurable) with ID equal to 0.
|
||||
if (is_sector_map_descriptor(descriptor)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Loop through all configuration detection descriptors and run detection commands
|
||||
while (!is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
|
||||
uint8_t instruction = descriptor[1];
|
||||
uint8_t dummy_cycles = descriptor[2] & 0x0F;
|
||||
auto addr_size = static_cast<sfdp_cmd_addr_size_t>(descriptor[2] >> 6);
|
||||
uint8_t mask = descriptor[3];
|
||||
uint32_t cmd_addr;
|
||||
memcpy(&cmd_addr, &descriptor[4], sizeof(cmd_addr)); // last 32 bits of the descriptor
|
||||
|
||||
uint8_t rx;
|
||||
int status = sfdp_reader(cmd_addr, addr_size, instruction, dummy_cycles, &rx, sizeof(rx));
|
||||
if (status < 0) {
|
||||
tr_error("Sector Map: Configuration detection command failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Shift existing bits to the left, so we can add the newly detected bit
|
||||
config <<= 1;
|
||||
|
||||
// The mask may apply to any bit of rx, so we can't directly combine
|
||||
// (rx & mask) with config. Instead, treat (rx & mask) as a boolean.
|
||||
if (rx & mask) {
|
||||
config |= 0x01;
|
||||
}
|
||||
|
||||
if (is_last_descriptor(descriptor)) {
|
||||
// We've processed the last configuration detection command descriptor
|
||||
descriptor += min_descriptor_size; // Increment the descriptor for the caller
|
||||
return 0;
|
||||
}
|
||||
descriptor += min_descriptor_size; // next descriptor
|
||||
}
|
||||
|
||||
tr_error("Sector Map: Incomplete configuration detection command descriptors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int sfdp_locate_sector_map_by_config(
|
||||
const uint8_t config,
|
||||
sfdp_hdr_info &sfdp_info,
|
||||
uint8_t *&descriptor,
|
||||
const uint8_t *table_end)
|
||||
{
|
||||
// The size of a sector map descriptor depends on the number of regions. Before
|
||||
// the number of regions is calculated, use the minimum possible size in the a loop condition.
|
||||
while (is_sector_map_descriptor(descriptor) && (descriptor + min_descriptor_size <= table_end)) {
|
||||
size_t regions = descriptor[2] + 1; // Region ID starts at 0
|
||||
size_t current_descriptor_size = (1 /*header*/ + regions) * 4 /*DWORD size*/;
|
||||
if (descriptor + current_descriptor_size > table_end) {
|
||||
tr_error("Sector Map: Incomplete sector map descriptor at the end of the table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (descriptor[1] == config) {
|
||||
// matching sector map found
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (is_last_descriptor(descriptor)) {
|
||||
// We've processed the last sector map descriptor
|
||||
tr_error("Sector Map: Failed to find a sector map that matches the current configuration");
|
||||
return -1;
|
||||
}
|
||||
|
||||
descriptor += current_descriptor_size; // next descriptor
|
||||
}
|
||||
|
||||
tr_error("Sector Map: Incomplete sector map descriptors");
|
||||
return -1;
|
||||
}
|
||||
|
||||
int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, uint8_t, uint8_t, void *, bd_size_t)> sfdp_reader, sfdp_hdr_info &sfdp_info)
|
||||
{
|
||||
uint32_t tmp_region_size = 0;
|
||||
|
|
@ -268,7 +372,7 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
|
|||
* - sector map configuration detection commands
|
||||
* - configurations
|
||||
* - regions in each configuration
|
||||
* is variable -> the size of this table is variable
|
||||
* are variable -> the size of this table is variable
|
||||
*/
|
||||
auto smptbl_buff = std::unique_ptr<uint8_t[]>(new (std::nothrow) uint8_t[sfdp_info.smptbl.size]);
|
||||
if (!smptbl_buff) {
|
||||
|
|
@ -291,13 +395,26 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Currently we support only Single Map Descriptor
|
||||
if (!((smptbl_buff[0] & 0x3) == 0x03) && (smptbl_buff[1] == 0x0)) {
|
||||
tr_error("Sector Map: Supporting Only Single Map Descriptor (not map commands)");
|
||||
return -1;
|
||||
uint8_t *table = smptbl_buff.get();
|
||||
uint8_t *descriptor = table;
|
||||
|
||||
// Detect which configuration is in use
|
||||
uint8_t active_config_id = 0x00;
|
||||
status = sfdp_detect_sector_map_configuration(sfdp_reader, sfdp_info, descriptor, table + sfdp_info.smptbl.size, active_config_id);
|
||||
if (status != 0) {
|
||||
tr_error("Failed to detect sector map configuration");
|
||||
return status;
|
||||
}
|
||||
|
||||
sfdp_info.smptbl.region_cnt = smptbl_buff[2] + 1;
|
||||
// Locate the sector map for the configuration
|
||||
status = sfdp_locate_sector_map_by_config(active_config_id, sfdp_info, descriptor, table + sfdp_info.smptbl.size);
|
||||
if (status != 0) {
|
||||
tr_error("Failed to locate a matching sector map");
|
||||
return status;
|
||||
}
|
||||
|
||||
// Find the number of regions from the sector map
|
||||
sfdp_info.smptbl.region_cnt = descriptor[2] + 1;
|
||||
if (sfdp_info.smptbl.region_cnt > SFDP_SECTOR_MAP_MAX_REGIONS) {
|
||||
tr_error("Sector Map: Supporting up to %d regions, current setup to %d regions - fail",
|
||||
SFDP_SECTOR_MAP_MAX_REGIONS,
|
||||
|
|
@ -305,13 +422,13 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
|
|||
return -1;
|
||||
}
|
||||
|
||||
// Loop through Regions and set for each one: size, supported erase types, high boundary offset
|
||||
// Calculate minimum Common Erase Type for all Regions
|
||||
// Loop through the regions and set for each one: size, supported erase types, high boundary offset
|
||||
// Calculate the minimum common erase type for all regions
|
||||
for (auto idx = 0; idx < sfdp_info.smptbl.region_cnt; idx++) {
|
||||
tmp_region_size = ((*((uint32_t *)&smptbl_buff[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
|
||||
tmp_region_size = ((*((uint32_t *)&descriptor[(idx + 1) * 4])) >> 8) & 0x00FFFFFF; // bits 9-32
|
||||
sfdp_info.smptbl.region_size[idx] = (tmp_region_size + 1) * 256; // Region size is 0 based multiple of 256 bytes;
|
||||
|
||||
sfdp_info.smptbl.region_erase_types_bitfld[idx] = smptbl_buff[(idx + 1) * 4] & 0x0F; // bits 1-4
|
||||
sfdp_info.smptbl.region_erase_types_bitfld[idx] = descriptor[(idx + 1) * 4] & 0x0F; // bits 1-4
|
||||
|
||||
min_common_erase_type_bits &= sfdp_info.smptbl.region_erase_types_bitfld[idx];
|
||||
|
||||
|
|
@ -335,7 +452,6 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, sfdp_cmd_addr_size_t, ui
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
size_t sfdp_detect_page_size(uint8_t *basic_param_table_ptr, size_t basic_param_table_size)
|
||||
{
|
||||
constexpr int SFDP_BASIC_PARAM_TABLE_PAGE_SIZE = 40;
|
||||
|
|
|
|||
|
|
@ -19,14 +19,50 @@
|
|||
#include "blockdevice/internal/SFDP.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Expectation;
|
||||
using ::testing::MockFunction;
|
||||
using ::testing::Return;
|
||||
|
||||
// The following data is used by multiple test cases.
|
||||
|
||||
/**
|
||||
* The Sector Map Parameter Table of Cypress S25FS512S:
|
||||
* https://www.cypress.com/file/216376/download Table 71.
|
||||
*/
|
||||
static const mbed::bd_addr_t sector_map_start_addr = 0xD81000;
|
||||
static const mbed::bd_addr_t register_CR1NV = 0x000002;
|
||||
static const mbed::bd_addr_t register_CR3NV = 0x000004;
|
||||
static const uint8_t sector_map_multiple_descriptors[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
0xFD, 0x65, 0xFF, 0x02,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
0xFE, 0x03, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
/**
|
||||
* Based on Cypress S25FS512S, modified to have one descriptor,
|
||||
|
|
@ -54,7 +90,19 @@ protected:
|
|||
return mock_return;
|
||||
}
|
||||
|
||||
memcpy(buff, sector_descriptors, sector_descriptors_size);
|
||||
// The following register values give Configuration ID = 0x03.
|
||||
uint8_t *out = static_cast<uint8_t*>(buff);
|
||||
switch (addr) {
|
||||
case sector_map_start_addr:
|
||||
memcpy(buff, sector_descriptors, sector_descriptors_size);
|
||||
break;
|
||||
case register_CR1NV:
|
||||
out[0] = 0x04;
|
||||
break;
|
||||
case register_CR3NV:
|
||||
out[0] = 0x02;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
|
||||
|
|
@ -291,3 +339,291 @@ TEST_F(TestSFDP, TestMoreRegionsThanSupported)
|
|||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, sfdp_parse_sector_map_table() runs all commands
|
||||
* to find the active configuration and selects the matching sector map.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestMultipleSectorConfigs)
|
||||
{
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
sector_map_multiple_descriptors,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// Fourth call: detect bit-2 of configuration
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_3).WillOnce(Return(0));
|
||||
|
||||
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
|
||||
// Expecting sector map for Configuration ID = 0x03:
|
||||
// Three regions
|
||||
EXPECT_EQ(3, header_info.smptbl.region_cnt);
|
||||
|
||||
// Region 0: erase type 3 (256KB erase)
|
||||
// Range: first 64 MB minus 256 KB
|
||||
EXPECT_EQ(64_MB - 256_KB, header_info.smptbl.region_size[0]);
|
||||
EXPECT_EQ(64_MB - 256_KB - 1_B, header_info.smptbl.region_high_boundary[0]);
|
||||
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[0]);
|
||||
|
||||
// Region 1: erase type 3 (256KB erase, which also covers 32KB from Region 2)
|
||||
// Range: between Region 0 and Region 2
|
||||
EXPECT_EQ(256_KB - 32_KB, header_info.smptbl.region_size[1]);
|
||||
EXPECT_EQ(64_MB - 32_KB - 1_B, header_info.smptbl.region_high_boundary[1]);
|
||||
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[1]);
|
||||
|
||||
// Region 2: erase type 1 (4KB erase)
|
||||
// Range: last 32 KB
|
||||
EXPECT_EQ(32_KB, header_info.smptbl.region_size[2]);
|
||||
EXPECT_EQ(64_MB - 1_B, header_info.smptbl.region_high_boundary[2]);
|
||||
EXPECT_EQ(1 << (1 - 1), header_info.smptbl.region_erase_types_bitfld[2]);
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but one of the detection commands returns
|
||||
* an error (e.g. due to a bus fault).
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigDetectCmdFailure)
|
||||
{
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
sector_map_multiple_descriptors,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(sector_map_multiple_descriptors)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration (failed)
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(-1)); // Emulate command failure
|
||||
|
||||
// No further calls after failure
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(_, _, _, _, _, _)
|
||||
).Times(0).After(call_3);
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but no detection command is declared as the
|
||||
* last command.
|
||||
* Note: This means either reading went wrong, or the SFDP data is inconsistent
|
||||
* possibly due to hardware manufactured with wrong data. When the latter happens in
|
||||
* practice, the solution is to let the block device apply a device-specific quirk
|
||||
* and supply "corrected" SFDP data in its callback.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigIncompleteDetectCommands)
|
||||
{
|
||||
const uint8_t table_incomplete_detect_commands[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
// Removed to trigger a parsing error
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
0xFE, 0x03, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
table_incomplete_detect_commands,
|
||||
sizeof(table_incomplete_detect_commands)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(table_incomplete_detect_commands)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// No further calls - incomplete detect command
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(_, _, _, _, _, _)
|
||||
).Times(0).After(call_3);
|
||||
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
||||
/**
|
||||
* When a Sector Map Parameter Table has multiple configuration detection
|
||||
* commands and sector maps, but no sector map matches the active
|
||||
* configuration.
|
||||
* Note: This means either detection went wrong, or the SFDP data is inconsistent
|
||||
* possibly due to hardware manufactured with wrong data. When the latter happens in
|
||||
* practice, the solution is to let the block device apply a device-specific quirk
|
||||
* and supply "corrected" SFDP data in its callback.
|
||||
*/
|
||||
TEST_F(TestSFDP, TestConfigNoMatchingSectorMap)
|
||||
{
|
||||
const uint8_t table_no_matching_sector_map[] = {
|
||||
// Detect 1
|
||||
0xFC, 0x65, 0xFF, 0x08,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 2
|
||||
0xFC, 0x65, 0xFF, 0x04,
|
||||
0x02, 0x00, 0x00, 0x00,
|
||||
|
||||
// Detect 3
|
||||
0xFD, 0x65, 0xFF, 0x02,
|
||||
0x04, 0x00, 0x00, 0x00,
|
||||
|
||||
// Config 1
|
||||
0xFE, 0x01, 0x02, 0xFF, // header
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 2
|
||||
|
||||
// No Config 2
|
||||
|
||||
// Config 3
|
||||
// The active configuration (for test purpose) is 0x03 which should match header[1],
|
||||
// but we change the latter to 0x02 to trigger a parsing error.
|
||||
0xFE, 0x02, 0x02, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFB, 0x03, // region 0
|
||||
0xF4, 0x7F, 0x03, 0x00, // region 1
|
||||
0xF1, 0x7F, 0x00, 0x00, // region 2
|
||||
|
||||
// Config 4
|
||||
0xFF, 0x05, 0x00, 0xFF, // header
|
||||
0xF4, 0xFF, 0xFF, 0x03 // region 0
|
||||
};
|
||||
|
||||
mbed::sfdp_hdr_info header_info;
|
||||
set_sector_map_param_table(
|
||||
header_info.smptbl,
|
||||
table_no_matching_sector_map,
|
||||
sizeof(table_no_matching_sector_map)
|
||||
);
|
||||
|
||||
// First call: get all detection command and sector map descriptors
|
||||
Expectation call_1 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(
|
||||
sector_map_start_addr,
|
||||
mbed::SFDP_READ_CMD_ADDR_TYPE,
|
||||
mbed::SFDP_READ_CMD_INST,
|
||||
mbed::SFDP_READ_CMD_DUMMY_CYCLES,
|
||||
_,
|
||||
sizeof(table_no_matching_sector_map)
|
||||
)
|
||||
).Times(1).WillOnce(Return(0));
|
||||
|
||||
// Second call: detect bit-0 of configuration
|
||||
Expectation call_2 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_1).WillOnce(Return(0));
|
||||
|
||||
// Third call: detect bit-1 of configuration
|
||||
Expectation call_3 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR1NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_2).WillOnce(Return(0));
|
||||
|
||||
// Fourth call: detect bit-2 of configuration
|
||||
Expectation call_4 = EXPECT_CALL(
|
||||
sfdp_reader_mock,
|
||||
Call(register_CR3NV, mbed::SFDP_CMD_ADDR_SIZE_VARIABLE, 0x65, mbed::SFDP_CMD_DUMMY_CYCLES_VARIABLE, _, 1)
|
||||
).Times(1).After(call_3).WillOnce(Return(0));
|
||||
|
||||
// Failed to find a sector map for the active configuration.
|
||||
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue