Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
MBRBlockDevice.cpp
00001 /* mbed Microcontroller Library 00002 * Copyright (c) 2017 ARM Limited 00003 * 00004 * Licensed under the Apache License, Version 2.0 (the "License"); 00005 * you may not use this file except in compliance with the License. 00006 * You may obtain a copy of the License at 00007 * 00008 * http://www.apache.org/licenses/LICENSE-2.0 00009 * 00010 * Unless required by applicable law or agreed to in writing, software 00011 * distributed under the License is distributed on an "AS IS" BASIS, 00012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 00013 * See the License for the specific language governing permissions and 00014 * limitations under the License. 00015 */ 00016 00017 #include "MBRBlockDevice.h" 00018 #include "platform/mbed_critical.h" 00019 #include "platform/mbed_toolchain.h" 00020 #include "platform/mbed_assert.h" 00021 #include <algorithm> 00022 #include <string.h> 00023 00024 // On disk structures, all entries are little endian 00025 MBED_PACKED(struct) mbr_entry { 00026 uint8_t status; 00027 uint8_t chs_start[3]; 00028 uint8_t type; 00029 uint8_t chs_stop[3]; 00030 uint32_t lba_offset; 00031 uint32_t lba_size; 00032 }; 00033 00034 MBED_PACKED(struct) mbr_table { 00035 struct mbr_entry entries[4]; 00036 uint8_t signature[2]; 00037 }; 00038 00039 // Little-endian conversion, should compile to noop 00040 // if system is little-endian 00041 static inline uint32_t tole32(uint32_t a) 00042 { 00043 union { 00044 uint32_t u32; 00045 uint8_t u8[4]; 00046 } w; 00047 00048 w.u8[0] = a >> 0; 00049 w.u8[1] = a >> 8; 00050 w.u8[2] = a >> 16; 00051 w.u8[3] = a >> 24; 00052 00053 return w.u32; 00054 } 00055 00056 static inline uint32_t fromle32(uint32_t a) 00057 { 00058 return tole32(a); 00059 } 00060 00061 static void tochs(uint32_t lba, uint8_t chs[3]) 00062 { 00063 uint32_t sector = std::min<uint32_t>(lba, 0xfffffd)+1; 00064 chs[0] = (sector >> 6) & 0xff; 00065 chs[1] = ((sector >> 0) & 0x3f) | ((sector >> 16) & 0xc0); 00066 chs[2] = (sector >> 14) & 0xff; 00067 } 00068 00069 00070 // Partition after address are turned into absolute 00071 // addresses, assumes bd is initialized 00072 static int partition_absolute( 00073 BlockDevice *bd, int part, uint8_t type, 00074 bd_size_t offset, bd_size_t size) 00075 { 00076 // Allocate smallest buffer necessary to write MBR 00077 uint32_t buffer_size = std::max<uint32_t>(bd->get_program_size(), sizeof(struct mbr_table)); 00078 00079 // Prevent alignment issues 00080 if(buffer_size % bd->get_program_size() != 0) { 00081 buffer_size += bd->get_program_size() - (buffer_size % bd->get_program_size()); 00082 } 00083 00084 uint8_t *buffer = new uint8_t[buffer_size]; 00085 00086 // Check for existing MBR 00087 int err = bd->read(buffer, 512-buffer_size, buffer_size); 00088 if (err) { 00089 delete[] buffer; 00090 return err; 00091 } 00092 00093 struct mbr_table *table = reinterpret_cast<struct mbr_table*>( 00094 &buffer[buffer_size - sizeof(struct mbr_table)]); 00095 if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) { 00096 // Setup default values for MBR 00097 table->signature[0] = 0x55; 00098 table->signature[1] = 0xaa; 00099 memset(table->entries, 0, sizeof(table->entries)); 00100 } 00101 00102 // For Windows-formatted SD card, it is not partitioned (no MBR), but its PBR has the 00103 // same boot signature (0xaa55) as MBR. We would easily mis-recognize this SD card has valid 00104 // partitions if we only check partition type. We add check by only accepting 0x00 (inactive) 00105 // /0x80 (active) for valid partition status. 00106 for (int i = 1; i <= 4; i++) { 00107 if (table->entries[i-1].status != 0x00 && 00108 table->entries[i-1].status != 0x80) { 00109 memset(table->entries, 0, sizeof(table->entries)); 00110 break; 00111 } 00112 } 00113 00114 // Setup new partition 00115 MBED_ASSERT(part >= 1 && part <= 4); 00116 table->entries[part-1].status = 0x00; // inactive (not bootable) 00117 table->entries[part-1].type = type; 00118 00119 // lba dimensions 00120 MBED_ASSERT(bd->is_valid_erase(offset, size)); 00121 uint32_t sector = std::max<uint32_t>(bd->get_erase_size(), 512); 00122 uint32_t lba_offset = offset / sector; 00123 uint32_t lba_size = size / sector; 00124 table->entries[part-1].lba_offset = tole32(lba_offset); 00125 table->entries[part-1].lba_size = tole32(lba_size); 00126 00127 // chs dimensions 00128 tochs(lba_offset, table->entries[part-1].chs_start); 00129 tochs(lba_offset+lba_size-1, table->entries[part-1].chs_stop); 00130 00131 // Check that we don't overlap other entries 00132 for (int i = 1; i <= 4; i++) { 00133 if (i != part && table->entries[i-1].type != 0x00) { 00134 uint32_t neighbor_lba_offset = fromle32(table->entries[i-1].lba_offset); 00135 uint32_t neighbor_lba_size = fromle32(table->entries[i-1].lba_size); 00136 MBED_ASSERT( 00137 (lba_offset >= neighbor_lba_offset + neighbor_lba_size) || 00138 (lba_offset + lba_size <= neighbor_lba_offset)); 00139 (void)neighbor_lba_offset; 00140 (void)neighbor_lba_size; 00141 } 00142 } 00143 00144 // Write out MBR 00145 err = bd->erase(0, bd->get_erase_size()); 00146 if (err) { 00147 delete[] buffer; 00148 return err; 00149 } 00150 00151 err = bd->program(buffer, 512-buffer_size, buffer_size); 00152 delete[] buffer; 00153 return err; 00154 } 00155 00156 int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t start) 00157 { 00158 int err = bd->init(); 00159 if (err) { 00160 return err; 00161 } 00162 00163 // Calculate dimensions 00164 bd_size_t offset = ((int64_t)start < 0) ? -start : start; 00165 bd_size_t size = bd->size(); 00166 00167 if (offset < 512) { 00168 offset += std::max<uint32_t>(bd->get_erase_size(), 512); 00169 } 00170 00171 size -= offset; 00172 00173 err = partition_absolute(bd, part, type, offset, size); 00174 if (err) { 00175 return err; 00176 } 00177 00178 err = bd->deinit(); 00179 if (err) { 00180 return err; 00181 } 00182 00183 return 0; 00184 } 00185 00186 int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, 00187 bd_addr_t start, bd_addr_t stop) 00188 { 00189 int err = bd->init(); 00190 if (err) { 00191 return err; 00192 } 00193 00194 // Calculate dimensions 00195 bd_size_t offset = ((int64_t)start < 0) ? -start : start; 00196 bd_size_t size = ((int64_t)stop < 0) ? -stop : stop; 00197 00198 if (offset < 512) { 00199 offset += std::max<uint32_t>(bd->get_erase_size(), 512); 00200 } 00201 00202 size -= offset; 00203 00204 err = partition_absolute(bd, part, type, offset, size); 00205 if (err) { 00206 return err; 00207 } 00208 00209 err = bd->deinit(); 00210 if (err) { 00211 return err; 00212 } 00213 00214 return 0; 00215 } 00216 00217 MBRBlockDevice::MBRBlockDevice(BlockDevice *bd, int part) 00218 : _bd(bd), _offset(0), _size(0), _type(0), _part(part), _init_ref_count(0), _is_initialized(false) 00219 { 00220 MBED_ASSERT(_part >= 1 && _part <= 4); 00221 } 00222 00223 int MBRBlockDevice::init() 00224 { 00225 uint32_t buffer_size; 00226 uint8_t *buffer = 0; 00227 struct mbr_table *table; 00228 bd_size_t sector; 00229 int err; 00230 00231 uint32_t val = core_util_atomic_incr_u32(&_init_ref_count, 1); 00232 00233 if (val != 1) { 00234 return BD_ERROR_OK; 00235 } 00236 00237 err = _bd->init(); 00238 if (err) { 00239 goto fail; 00240 } 00241 00242 // Allocate smallest buffer necessary to write MBR 00243 buffer_size = std::max<uint32_t>(_bd->get_read_size(), sizeof(struct mbr_table)); 00244 buffer = new uint8_t[buffer_size]; 00245 00246 err = _bd->read(buffer, 512-buffer_size, buffer_size); 00247 if (err) { 00248 goto fail; 00249 } 00250 00251 // Check for valid table 00252 table = reinterpret_cast<struct mbr_table*>(&buffer[buffer_size - sizeof(struct mbr_table)]); 00253 if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) { 00254 err = BD_ERROR_INVALID_MBR; 00255 goto fail; 00256 } 00257 00258 // Check for valid partition status 00259 // Same reason as in partition_absolute regarding Windows-formatted SD card 00260 if (table->entries[_part-1].status != 0x00 && 00261 table->entries[_part-1].status != 0x80) { 00262 err = BD_ERROR_INVALID_PARTITION; 00263 goto fail; 00264 } 00265 00266 // Check for valid entry 00267 // 0x00 = no entry 00268 // 0x05, 0x0f = extended partitions, currently not supported 00269 if ((table->entries[_part-1].type == 0x00 || 00270 table->entries[_part-1].type == 0x05 || 00271 table->entries[_part-1].type == 0x0f)) { 00272 err = BD_ERROR_INVALID_PARTITION; 00273 goto fail; 00274 } 00275 00276 // Get partition attributes 00277 sector = std::max<uint32_t>(_bd->get_erase_size(), 512); 00278 _type = table->entries[_part-1].type; 00279 _offset = fromle32(table->entries[_part-1].lba_offset) * sector; 00280 _size = fromle32(table->entries[_part-1].lba_size) * sector; 00281 00282 // Check that block addresses are valid 00283 if (!_bd->is_valid_erase(_offset, _size)) { 00284 err = BD_ERROR_INVALID_PARTITION; 00285 goto fail; 00286 } 00287 00288 _is_initialized = true; 00289 delete[] buffer; 00290 return BD_ERROR_OK; 00291 00292 fail: 00293 delete[] buffer; 00294 _is_initialized = false; 00295 _init_ref_count = 0; 00296 return err; 00297 } 00298 00299 int MBRBlockDevice::deinit() 00300 { 00301 if (!_is_initialized) { 00302 return BD_ERROR_OK; 00303 } 00304 00305 uint32_t val = core_util_atomic_decr_u32(&_init_ref_count, 1); 00306 00307 if (val) { 00308 return BD_ERROR_OK; 00309 } 00310 00311 _is_initialized = false; 00312 return _bd->deinit(); 00313 } 00314 00315 int MBRBlockDevice::sync() 00316 { 00317 if (!_is_initialized) { 00318 return BD_ERROR_DEVICE_ERROR; 00319 } 00320 00321 return _bd->sync(); 00322 } 00323 00324 int MBRBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size) 00325 { 00326 MBED_ASSERT(is_valid_read(addr, size)); 00327 if (!_is_initialized) { 00328 return BD_ERROR_DEVICE_ERROR; 00329 } 00330 00331 return _bd->read(b, addr + _offset, size); 00332 } 00333 00334 int MBRBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size) 00335 { 00336 MBED_ASSERT(is_valid_program(addr, size)); 00337 if (!_is_initialized) { 00338 return BD_ERROR_DEVICE_ERROR; 00339 } 00340 00341 return _bd->program(b, addr + _offset, size); 00342 } 00343 00344 int MBRBlockDevice::erase(bd_addr_t addr, bd_size_t size) 00345 { 00346 MBED_ASSERT(is_valid_erase(addr, size)); 00347 if (!_is_initialized) { 00348 return BD_ERROR_DEVICE_ERROR; 00349 } 00350 00351 return _bd->erase(addr + _offset, size); 00352 } 00353 00354 bd_size_t MBRBlockDevice::get_read_size() const 00355 { 00356 if (!_is_initialized) { 00357 return 0; 00358 } 00359 00360 return _bd->get_read_size(); 00361 } 00362 00363 bd_size_t MBRBlockDevice::get_program_size() const 00364 { 00365 if (!_is_initialized) { 00366 return 0; 00367 } 00368 00369 return _bd->get_program_size(); 00370 } 00371 00372 bd_size_t MBRBlockDevice::get_erase_size() const 00373 { 00374 if (!_is_initialized) { 00375 return 0; 00376 } 00377 00378 return _bd->get_erase_size(); 00379 } 00380 00381 bd_size_t MBRBlockDevice::get_erase_size(bd_addr_t addr) const 00382 { 00383 if (!_is_initialized) { 00384 return 0; 00385 } 00386 00387 return _bd->get_erase_size(_offset + addr); 00388 } 00389 00390 int MBRBlockDevice::get_erase_value() const 00391 { 00392 if (!_is_initialized) { 00393 return 0; 00394 } 00395 00396 return _bd->get_erase_value(); 00397 } 00398 00399 bd_size_t MBRBlockDevice::size() const 00400 { 00401 return _size; 00402 } 00403 00404 bd_size_t MBRBlockDevice::get_partition_start() const 00405 { 00406 return _offset; 00407 } 00408 00409 bd_size_t MBRBlockDevice::get_partition_stop() const 00410 { 00411 return _offset+_size; 00412 } 00413 00414 uint8_t MBRBlockDevice::get_partition_type() const 00415 { 00416 return _type; 00417 } 00418 00419 int MBRBlockDevice::get_partition_number() const 00420 { 00421 return _part; 00422 }
Generated on Tue Jul 12 2022 20:52:50 by
1.7.2