Skip to content

Commit d4b1534

Browse files
authored
Merge pull request #14983 from LDong-Arm/sfdp_sector_map
SFDP: Add unit tests for Sector Map Parameter Table parsing
2 parents e14b961 + a16c2bf commit d4b1534

File tree

2 files changed

+200
-30
lines changed

2 files changed

+200
-30
lines changed

storage/blockdevice/source/SFDP.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -239,12 +239,14 @@ int sfdp_parse_sector_map_table(Callback<int(bd_addr_t, void *, bd_size_t)> sfdp
239239
// Default set to all type bits 1-4 are common
240240
int min_common_erase_type_bits = SFDP_ERASE_BITMASK_ALL;
241241

242-
// If there's no region map, we have a single region sized the entire device size
243-
sfdp_info.smptbl.region_size[0] = sfdp_info.bptbl.device_size_bytes;
244-
sfdp_info.smptbl.region_high_boundary[0] = sfdp_info.bptbl.device_size_bytes - 1;
245-
246242
if (!sfdp_info.smptbl.addr || !sfdp_info.smptbl.size) {
247243
tr_debug("No Sector Map Table");
244+
245+
// If there's no sector map, we have a single region sized the entire device size
246+
sfdp_info.smptbl.region_cnt = 1;
247+
sfdp_info.smptbl.region_size[0] = sfdp_info.bptbl.device_size_bytes;
248+
sfdp_info.smptbl.region_high_boundary[0] = sfdp_info.bptbl.device_size_bytes - 1;
249+
248250
return MBED_SUCCESS;
249251
}
250252

storage/blockdevice/tests/UNITTESTS/SFDP/test_sfdp.cpp

+194-26
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
/* Copyright (c) 2020 ARM Limited
1+
/* Copyright (c) 2020-2021 ARM Limited
22
* SPDX-License-Identifier: Apache-2.0
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -18,38 +18,97 @@
1818
#include "gmock/gmock.h"
1919
#include "blockdevice/internal/SFDP.h"
2020

21+
using ::testing::_;
22+
using ::testing::MockFunction;
23+
using ::testing::Return;
24+
25+
/**
26+
* The Sector Map Parameter Table of Cypress S25FS512S:
27+
* https://www.cypress.com/file/216376/download Table 71.
28+
*/
29+
static const mbed::bd_addr_t sector_map_start_addr = 0xD81000;
30+
31+
/**
32+
* Based on Cypress S25FS512S, modified to have one descriptor,
33+
* three regions for test purpose.
34+
*/
35+
static const uint8_t sector_map_single_descriptor[] {
36+
0xFF, 0x01, 0x02, 0xFF, // header, highest region = 0x02
37+
0xF1, 0x7F, 0x00, 0x00, // region 0
38+
0xF4, 0x7F, 0x03, 0x00, // region 1
39+
0xF4, 0xFF, 0xFB, 0x03 // region 2
40+
};
41+
42+
/**
43+
* Based on Cypress S25FS512S, modified to have one descriptor,
44+
* twelve regions for test purpose.
45+
*/
46+
static const uint8_t sector_map_single_descriptor_twelve_regions[] {
47+
0xFF, 0x01, 0x0B, 0xFF, // header, highest region = 0x0B
48+
0xF1, 0x7F, 0x00, 0x00, // region 0
49+
0xF4, 0x7F, 0x03, 0x00, // region 1
50+
0xF4, 0xFF, 0xFB, 0x03, // region 2
51+
0xF1, 0x7F, 0x00, 0x00, // region 3
52+
0xF4, 0x7F, 0x03, 0x00, // region 4
53+
0xF4, 0xFF, 0xFB, 0x03, // region 5
54+
0xF1, 0x7F, 0x00, 0x00, // region 6
55+
0xF4, 0x7F, 0x03, 0x00, // region 7
56+
0xF4, 0xFF, 0xFB, 0x03, // region 8
57+
0xF1, 0x7F, 0x00, 0x00, // region 9
58+
0xF4, 0x7F, 0x03, 0x00, // region 10
59+
0xF4, 0xFF, 0xFB, 0x03, // region 11
60+
};
61+
2162
class TestSFDP : public testing::Test {
63+
64+
public:
65+
mbed::Callback<int(mbed::bd_addr_t, void*, bd_size_t)> sfdp_reader_callback;
66+
2267
protected:
23-
struct mbed::sfdp_smptbl_info smptbl;
68+
TestSFDP() : sfdp_reader_callback(this, &TestSFDP::sfdp_reader) {};
69+
70+
int sfdp_reader(mbed::bd_addr_t addr, void *buff, bd_size_t buff_size)
71+
{
72+
int mock_return = sfdp_reader_mock.Call(addr, buff, buff_size);
73+
if (mock_return != 0) {
74+
return mock_return;
75+
}
2476

25-
/**
26-
* Construct Mbed OS SFDP info.
27-
* Normally this is parsed from the flash-chips's
28-
* raw SFDP table bytes, but for unit test we construct
29-
* SFDP info manually
30-
*/
31-
virtual void SetUp()
77+
memcpy(buff, sector_descriptors, sector_descriptors_size);
78+
return 0;
79+
};
80+
81+
void set_sector_map_param_table(mbed::sfdp_smptbl_info &smptbl, const uint8_t *table, const size_t table_size)
3282
{
33-
// The mock flash supports 4KB, 32KB and 64KB erase types
34-
smptbl.erase_type_size_arr[0] = 4 * 1024;
35-
smptbl.erase_type_size_arr[1] = 32 * 1024;
36-
smptbl.erase_type_size_arr[2] = 64 * 1024;
37-
38-
// The mock flash has three regions, with address ranges:
39-
// * 0 to 64KB - 1B
40-
// * 64KB to 256KB - 1B
41-
// * 256KB to 1024KB - 1B
42-
smptbl.region_high_boundary[0] = 64 * 1024 - 1;
43-
smptbl.region_high_boundary[1] = 256 * 1024 - 1;
44-
smptbl.region_high_boundary[2] = 1024 * 1024 - 1;
45-
46-
// Bitfields indicating which regions support which erase types
47-
smptbl.region_erase_types_bitfld[0] = 0b0001; // 4KB only
48-
smptbl.region_erase_types_bitfld[1] = 0b0111; // 64KB, 32KB, 4KB
49-
smptbl.region_erase_types_bitfld[2] = 0b0110; // 64KB, 32KB
83+
smptbl.size = table_size;
84+
smptbl.addr = sector_map_start_addr;
85+
86+
sector_descriptors = table;
87+
sector_descriptors_size = table_size;
5088
}
89+
90+
MockFunction<int(mbed::bd_addr_t, void*, bd_size_t)> sfdp_reader_mock;
91+
const uint8_t *sector_descriptors;
92+
bd_size_t sector_descriptors_size;
5193
};
5294

95+
/**
96+
* Utilities for conversions to bytes.
97+
*/
98+
namespace{
99+
auto operator "" _B(unsigned long long int size) {
100+
return size;
101+
}
102+
103+
auto operator "" _KB(unsigned long long int size) {
104+
return size * 1024;
105+
}
106+
107+
auto operator "" _MB(unsigned long long int size) {
108+
return size * 1024 * 1024;
109+
}
110+
}
111+
53112
/**
54113
* Test if sfdp_iterate_next_largest_erase_type() returns the most
55114
* optimal erase type, whose erase size is as large as possible
@@ -63,6 +122,25 @@ TEST_F(TestSFDP, TestEraseTypeAlgorithm)
63122
int region = 1;
64123
int type;
65124

125+
// The mock flash supports 4KB, 32KB and 64KB erase types
126+
struct mbed::sfdp_smptbl_info smptbl;
127+
smptbl.erase_type_size_arr[0] = 4 * 1024;
128+
smptbl.erase_type_size_arr[1] = 32 * 1024;
129+
smptbl.erase_type_size_arr[2] = 64 * 1024;
130+
131+
// The mock flash has three regions, with address ranges:
132+
// * 0 to 64KB - 1B
133+
// * 64KB to 256KB - 1B
134+
// * 256KB to 1024KB - 1B
135+
smptbl.region_high_boundary[0] = 64 * 1024 - 1;
136+
smptbl.region_high_boundary[1] = 256 * 1024 - 1;
137+
smptbl.region_high_boundary[2] = 1024 * 1024 - 1;
138+
139+
// Bitfields indicating which regions support which erase types
140+
smptbl.region_erase_types_bitfld[0] = 0b0001; // 4KB only
141+
smptbl.region_erase_types_bitfld[1] = 0b0111; // 64KB, 32KB, 4KB
142+
smptbl.region_erase_types_bitfld[2] = 0b0110; // 64KB, 32KB
143+
66144
// Expected outcome:
67145
// * The starting position 92KB is 4KB-aligned
68146
// * The next position 96KB (92KB + 4KB) is 32KB-aligned
@@ -99,3 +177,93 @@ TEST_F(TestSFDP, TestEraseTypeAlgorithm)
99177
smptbl);
100178
EXPECT_EQ(type, -1); // Invalid erase
101179
}
180+
181+
/**
182+
* Test that sfdp_parse_sector_map_table() treats a whole flash
183+
* as one region if no sector map is available.
184+
*/
185+
TEST_F(TestSFDP, TestNoSectorMap)
186+
{
187+
const bd_size_t device_size = 512_KB;
188+
189+
mbed::sfdp_hdr_info header_info;
190+
header_info.smptbl.size = 0; // No Sector Map Table
191+
header_info.bptbl.device_size_bytes = device_size;
192+
193+
// No need to read anything
194+
EXPECT_CALL(sfdp_reader_mock, Call(_, _, _)).Times(0);
195+
196+
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
197+
198+
EXPECT_EQ(1, header_info.smptbl.region_cnt);
199+
EXPECT_EQ(device_size, header_info.smptbl.region_size[0]);
200+
EXPECT_EQ(device_size - 1, header_info.smptbl.region_high_boundary[0]);
201+
}
202+
203+
/**
204+
* When a Sector Map Parameter Table has a single descriptor (i.e. non-configurable flash layout).
205+
*/
206+
TEST_F(TestSFDP, TestSingleSectorConfig)
207+
{
208+
mbed::sfdp_hdr_info header_info;
209+
set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));
210+
211+
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor)))
212+
.Times(1)
213+
.WillOnce(Return(0));
214+
215+
EXPECT_EQ(0, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
216+
217+
// Three regions
218+
EXPECT_EQ(3, header_info.smptbl.region_cnt);
219+
220+
// Region 0: erase type 1 (32KB erase)
221+
EXPECT_EQ(32_KB, header_info.smptbl.region_size[0]);
222+
EXPECT_EQ(32_KB - 1_B, header_info.smptbl.region_high_boundary[0]);
223+
EXPECT_EQ(1 << (1 - 1), header_info.smptbl.region_erase_types_bitfld[0]);
224+
225+
// Region 1: erase type 3 (256KB erase which includes 32KB from Region 0)
226+
EXPECT_EQ(224_KB, header_info.smptbl.region_size[1]);
227+
EXPECT_EQ(256_KB - 1_B, header_info.smptbl.region_high_boundary[1]);
228+
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[1]);
229+
230+
// Region 2: erase type 3 (256KB erase)
231+
EXPECT_EQ(64_MB - 32_KB - 224_KB, header_info.smptbl.region_size[2]);
232+
EXPECT_EQ(64_MB - 1_B, header_info.smptbl.region_high_boundary[2]);
233+
EXPECT_EQ(1 << (3 - 1), header_info.smptbl.region_erase_types_bitfld[2]);
234+
}
235+
236+
/**
237+
* When an SFDP reader fails to read data requested by sfdp_parse_sector_map_table().
238+
*/
239+
TEST_F(TestSFDP, TestSFDPReadFailure)
240+
{
241+
mbed::sfdp_hdr_info header_info;
242+
set_sector_map_param_table(header_info.smptbl, sector_map_single_descriptor, sizeof(sector_map_single_descriptor));
243+
244+
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor)))
245+
.Times(1)
246+
.WillOnce(Return(-1)); // Emulate read failure
247+
248+
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
249+
}
250+
251+
/**
252+
* When a flash layout has more regions than Mbed OS supports (10).
253+
* Note: This is unlikely to happens in practice.
254+
*/
255+
TEST_F(TestSFDP, TestMoreRegionsThanSupported)
256+
{
257+
mbed::sfdp_hdr_info header_info;
258+
set_sector_map_param_table(
259+
header_info.smptbl,
260+
sector_map_single_descriptor_twelve_regions,
261+
sizeof(sector_map_single_descriptor_twelve_regions)
262+
);
263+
264+
EXPECT_CALL(sfdp_reader_mock, Call(sector_map_start_addr, _, sizeof(sector_map_single_descriptor_twelve_regions)))
265+
.Times(1)
266+
.WillOnce(Return(0));
267+
268+
EXPECT_EQ(-1, sfdp_parse_sector_map_table(sfdp_reader_callback, header_info));
269+
}

0 commit comments

Comments
 (0)