Kenji Arai / mbed-os_TYBLE16

Dependents:   TYBLE16_simple_data_logger TYBLE16_MP3_Air

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers MBRBlockDevice.cpp Source File

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_atomic.h"
00019 #include "platform/mbed_toolchain.h"
00020 #include "platform/mbed_assert.h"
00021 #include <algorithm>
00022 #include <string.h>
00023 
00024 namespace mbed {
00025 
00026 // On disk structures, all entries are little endian
00027 MBED_PACKED(struct) mbr_entry {
00028     uint8_t status;
00029     uint8_t chs_start[3];
00030     uint8_t type;
00031     uint8_t chs_stop[3];
00032     uint32_t lba_offset;
00033     uint32_t lba_size;
00034 };
00035 
00036 MBED_PACKED(struct) mbr_table {
00037     struct mbr_entry entries[4];
00038     uint8_t signature[2];
00039 };
00040 
00041 // Little-endian conversion, should compile to noop
00042 // if system is little-endian
00043 static inline uint32_t tole32(uint32_t a)
00044 {
00045     union {
00046         uint32_t u32;
00047         uint8_t u8[4];
00048     } w;
00049 
00050     w.u8[0] = a >>  0;
00051     w.u8[1] = a >>  8;
00052     w.u8[2] = a >> 16;
00053     w.u8[3] = a >> 24;
00054 
00055     return w.u32;
00056 }
00057 
00058 static inline uint32_t fromle32(uint32_t a)
00059 {
00060     return tole32(a);
00061 }
00062 
00063 static void tochs(uint32_t lba, uint8_t chs[3])
00064 {
00065     uint32_t sector = std::min<uint32_t>(lba, 0xfffffd) + 1;
00066     chs[0] = (sector >> 6) & 0xff;
00067     chs[1] = ((sector >> 0) & 0x3f) | ((sector >> 16) & 0xc0);
00068     chs[2] = (sector >> 14) & 0xff;
00069 }
00070 
00071 
00072 // Partition after address are turned into absolute
00073 // addresses, assumes bd is initialized
00074 static int partition_absolute(
00075     BlockDevice *bd, int part, uint8_t type,
00076     bd_size_t offset, bd_size_t size)
00077 {
00078     // Allocate smallest buffer necessary to write MBR
00079     uint32_t buffer_size = std::max<uint32_t>(bd->get_program_size(), sizeof(struct mbr_table));
00080 
00081     // Prevent alignment issues
00082     if (buffer_size % bd->get_program_size() != 0) {
00083         buffer_size += bd->get_program_size() - (buffer_size % bd->get_program_size());
00084     }
00085 
00086     uint8_t *buffer = new uint8_t[buffer_size];
00087 
00088     // Check for existing MBR
00089     int err = bd->read(buffer, 512 - buffer_size, buffer_size);
00090     if (err) {
00091         delete[] buffer;
00092         return err;
00093     }
00094 
00095     uint32_t table_start_offset = buffer_size - sizeof(struct mbr_table);
00096     struct mbr_table *table = reinterpret_cast<struct mbr_table *>(
00097                                       &buffer[table_start_offset]);
00098     if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) {
00099         // Setup default values for MBR
00100         table->signature[0] = 0x55;
00101         table->signature[1] = 0xaa;
00102         memset(table->entries, 0, sizeof(table->entries));
00103     }
00104 
00105     // For Windows-formatted SD card, it is not partitioned (no MBR), but its PBR has the
00106     // same boot signature (0xaa55) as MBR. We would easily mis-recognize this SD card has valid
00107     // partitions if we only check partition type. We add check by only accepting 0x00 (inactive)
00108     // /0x80 (active) for valid partition status.
00109     for (int i = 1; i <= 4; i++) {
00110         if (table->entries[i - 1].status != 0x00 &&
00111                 table->entries[i - 1].status != 0x80) {
00112             memset(table->entries, 0, sizeof(table->entries));
00113             break;
00114         }
00115     }
00116 
00117     // Setup new partition
00118     MBED_ASSERT(part >= 1 && part <= 4);
00119     table->entries[part - 1].status = 0x00; // inactive (not bootable)
00120     table->entries[part - 1].type = type;
00121 
00122     // lba dimensions
00123     MBED_ASSERT(bd->is_valid_erase(offset, size));
00124     uint32_t sector = std::max<uint32_t>(bd->get_erase_size(), 512);
00125     uint32_t lba_offset = offset / sector;
00126     uint32_t lba_size = size / sector;
00127     table->entries[part - 1].lba_offset = tole32(lba_offset);
00128     table->entries[part - 1].lba_size = tole32(lba_size);
00129 
00130     // chs dimensions
00131     tochs(lba_offset,            table->entries[part - 1].chs_start);
00132     tochs(lba_offset + lba_size - 1, table->entries[part - 1].chs_stop);
00133 
00134     // Check that we don't overlap other entries
00135     for (int i = 1; i <= 4; i++) {
00136         if (i != part && table->entries[i - 1].type != 0x00) {
00137             uint32_t neighbor_lba_offset = fromle32(table->entries[i - 1].lba_offset);
00138             uint32_t neighbor_lba_size = fromle32(table->entries[i - 1].lba_size);
00139             MBED_ASSERT(
00140                 (lba_offset >= neighbor_lba_offset + neighbor_lba_size) ||
00141                 (lba_offset + lba_size <= neighbor_lba_offset));
00142             (void)neighbor_lba_offset;
00143             (void)neighbor_lba_size;
00144         }
00145     }
00146 
00147     // As the erase operation may do nothing, erase remainder of the buffer, to eradicate
00148     // any remaining programmed data (such as previously programmed file systems).
00149     if (table_start_offset > 0) {
00150         memset(buffer, 0xFF, table_start_offset);
00151     }
00152     if (table_start_offset + sizeof(struct mbr_table) < buffer_size) {
00153         memset(buffer + table_start_offset + sizeof(struct mbr_table), 0xFF,
00154                buffer_size - (table_start_offset + sizeof(struct mbr_table)));
00155     }
00156 
00157     // Write out MBR
00158     err = bd->erase(0, bd->get_erase_size());
00159     if (err) {
00160         delete[] buffer;
00161         return err;
00162     }
00163 
00164     err = bd->program(buffer, 512 - buffer_size, buffer_size);
00165     delete[] buffer;
00166     return err;
00167 }
00168 
00169 int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type, bd_addr_t start)
00170 {
00171     int err = bd->init();
00172     if (err) {
00173         return err;
00174     }
00175 
00176     // Calculate dimensions
00177     bd_size_t offset = ((int64_t)start < 0) ? -start : start;
00178     bd_size_t size = bd->size();
00179 
00180     if (offset < 512) {
00181         offset += std::max<uint32_t>(bd->get_erase_size(), 512);
00182     }
00183 
00184     size -= offset;
00185 
00186     err = partition_absolute(bd, part, type, offset, size);
00187     if (err) {
00188         return err;
00189     }
00190 
00191     err = bd->deinit();
00192     if (err) {
00193         return err;
00194     }
00195 
00196     return 0;
00197 }
00198 
00199 int MBRBlockDevice::partition(BlockDevice *bd, int part, uint8_t type,
00200                               bd_addr_t start, bd_addr_t stop)
00201 {
00202     int err = bd->init();
00203     if (err) {
00204         return err;
00205     }
00206 
00207     // Calculate dimensions
00208     bd_size_t offset = ((int64_t)start < 0) ? -start : start;
00209     bd_size_t size = ((int64_t)stop < 0) ? -stop : stop;
00210 
00211     if (offset < 512) {
00212         offset += std::max<uint32_t>(bd->get_erase_size(), 512);
00213     }
00214 
00215     size -= offset;
00216 
00217     err = partition_absolute(bd, part, type, offset, size);
00218     if (err) {
00219         return err;
00220     }
00221 
00222     err = bd->deinit();
00223     if (err) {
00224         return err;
00225     }
00226 
00227     return 0;
00228 }
00229 
00230 MBRBlockDevice::MBRBlockDevice(BlockDevice *bd, int part)
00231     : _bd(bd), _offset(0), _size(0), _type(0), _part(part), _init_ref_count(0), _is_initialized(false)
00232 {
00233     MBED_ASSERT(_part >= 1 && _part <= 4);
00234 }
00235 
00236 int MBRBlockDevice::init()
00237 {
00238     uint32_t buffer_size;
00239     uint8_t *buffer = 0;
00240     struct mbr_table *table;
00241     bd_size_t sector;
00242     int err;
00243 
00244     uint32_t val = core_util_atomic_incr_u32 (&_init_ref_count, 1);
00245 
00246     if (val != 1) {
00247         return BD_ERROR_OK;
00248     }
00249 
00250     err = _bd->init();
00251     if (err) {
00252         goto fail;
00253     }
00254 
00255     // Allocate smallest buffer necessary to write MBR
00256     buffer_size = std::max<uint32_t>(_bd->get_read_size(), sizeof(struct mbr_table));
00257     buffer = new uint8_t[buffer_size];
00258 
00259     err = _bd->read(buffer, 512 - buffer_size, buffer_size);
00260     if (err) {
00261         goto fail;
00262     }
00263 
00264     // Check for valid table
00265     table = reinterpret_cast<struct mbr_table *>(&buffer[buffer_size - sizeof(struct mbr_table)]);
00266     if (table->signature[0] != 0x55 || table->signature[1] != 0xaa) {
00267         err = BD_ERROR_INVALID_MBR;
00268         goto fail;
00269     }
00270 
00271     // Check for valid partition status
00272     // Same reason as in partition_absolute regarding Windows-formatted SD card
00273     if (table->entries[_part - 1].status != 0x00 &&
00274             table->entries[_part - 1].status != 0x80) {
00275         err = BD_ERROR_INVALID_PARTITION;
00276         goto fail;
00277     }
00278 
00279     // Check for valid entry
00280     // 0x00 = no entry
00281     // 0x05, 0x0f = extended partitions, currently not supported
00282     if ((table->entries[_part - 1].type == 0x00 ||
00283             table->entries[_part - 1].type == 0x05 ||
00284             table->entries[_part - 1].type == 0x0f)) {
00285         err = BD_ERROR_INVALID_PARTITION;
00286         goto fail;
00287     }
00288 
00289     // Get partition attributes
00290     sector = std::max<uint32_t>(_bd->get_erase_size(), 512);
00291     _type = table->entries[_part - 1].type;
00292     _offset = fromle32(table->entries[_part - 1].lba_offset) * sector;
00293     _size   = fromle32(table->entries[_part - 1].lba_size)   * sector;
00294 
00295     // Check that block addresses are valid
00296     if (!_bd->is_valid_erase(_offset, _size)) {
00297         err = BD_ERROR_INVALID_PARTITION;
00298         goto fail;
00299     }
00300 
00301     _is_initialized = true;
00302     delete[] buffer;
00303     return BD_ERROR_OK;
00304 
00305 fail:
00306     delete[] buffer;
00307     _is_initialized = false;
00308     _init_ref_count = 0;
00309     return err;
00310 }
00311 
00312 int MBRBlockDevice::deinit()
00313 {
00314     if (!_is_initialized) {
00315         return BD_ERROR_OK;
00316     }
00317 
00318     uint32_t val = core_util_atomic_decr_u32 (&_init_ref_count, 1);
00319 
00320     if (val) {
00321         return BD_ERROR_OK;
00322     }
00323 
00324     _is_initialized = false;
00325     return _bd->deinit();
00326 }
00327 
00328 int MBRBlockDevice::sync()
00329 {
00330     if (!_is_initialized) {
00331         return BD_ERROR_DEVICE_ERROR;
00332     }
00333 
00334     return _bd->sync();
00335 }
00336 
00337 int MBRBlockDevice::read(void *b, bd_addr_t addr, bd_size_t size)
00338 {
00339     MBED_ASSERT(is_valid_read(addr, size));
00340     if (!_is_initialized) {
00341         return BD_ERROR_DEVICE_ERROR;
00342     }
00343 
00344     return _bd->read(b, addr + _offset, size);
00345 }
00346 
00347 int MBRBlockDevice::program(const void *b, bd_addr_t addr, bd_size_t size)
00348 {
00349     MBED_ASSERT(is_valid_program(addr, size));
00350     if (!_is_initialized) {
00351         return BD_ERROR_DEVICE_ERROR;
00352     }
00353 
00354     return _bd->program(b, addr + _offset, size);
00355 }
00356 
00357 int MBRBlockDevice::erase(bd_addr_t addr, bd_size_t size)
00358 {
00359     MBED_ASSERT(is_valid_erase(addr, size));
00360     if (!_is_initialized) {
00361         return BD_ERROR_DEVICE_ERROR;
00362     }
00363 
00364     return _bd->erase(addr + _offset, size);
00365 }
00366 
00367 bd_size_t MBRBlockDevice::get_read_size() const
00368 {
00369     if (!_is_initialized) {
00370         return 0;
00371     }
00372 
00373     return _bd->get_read_size();
00374 }
00375 
00376 bd_size_t MBRBlockDevice::get_program_size() const
00377 {
00378     if (!_is_initialized) {
00379         return 0;
00380     }
00381 
00382     return _bd->get_program_size();
00383 }
00384 
00385 bd_size_t MBRBlockDevice::get_erase_size() const
00386 {
00387     if (!_is_initialized) {
00388         return 0;
00389     }
00390 
00391     return _bd->get_erase_size();
00392 }
00393 
00394 bd_size_t MBRBlockDevice::get_erase_size(bd_addr_t addr) const
00395 {
00396     if (!_is_initialized) {
00397         return 0;
00398     }
00399 
00400     return _bd->get_erase_size(_offset + addr);
00401 }
00402 
00403 int MBRBlockDevice::get_erase_value() const
00404 {
00405     if (!_is_initialized) {
00406         return 0;
00407     }
00408 
00409     return _bd->get_erase_value();
00410 }
00411 
00412 bd_size_t MBRBlockDevice::size() const
00413 {
00414     return _size;
00415 }
00416 
00417 bd_size_t MBRBlockDevice::get_partition_start() const
00418 {
00419     return _offset;
00420 }
00421 
00422 bd_size_t MBRBlockDevice::get_partition_stop() const
00423 {
00424     return _offset + _size;
00425 }
00426 
00427 uint8_t MBRBlockDevice::get_partition_type() const
00428 {
00429     return _type;
00430 }
00431 
00432 int MBRBlockDevice::get_partition_number() const
00433 {
00434     return _part;
00435 }
00436 
00437 const char *MBRBlockDevice::get_type() const
00438 {
00439     if (_bd != NULL) {
00440         return _bd->get_type();
00441     }
00442 
00443     return NULL;
00444 }
00445 
00446 } // namespace mbed
00447