1 /*
2 * Copyright (c) 2023 Antmicro <www.antmicro.com>
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/logging/log.h>
8 #include <zephyr/device.h>
9 #include <zephyr/storage/disk_access.h>
10
11 #include "ext2.h"
12 #include "ext2_struct.h"
13
14 LOG_MODULE_DECLARE(ext2);
15
16 static struct disk_data {
17 const char *name;
18 uint32_t sector_size;
19 uint32_t sector_count;
20 } disk_data;
21
disk_access_device_size(struct ext2_data * fs)22 static int64_t disk_access_device_size(struct ext2_data *fs)
23 {
24 struct disk_data *disk = fs->backend;
25
26 return disk->sector_count * disk->sector_size;
27 }
28
disk_access_write_size(struct ext2_data * fs)29 static int64_t disk_access_write_size(struct ext2_data *fs)
30 {
31 struct disk_data *disk = fs->backend;
32
33 return disk->sector_size;
34 }
35
disk_read(const char * disk,uint8_t * buf,uint32_t start,uint32_t num)36 static int disk_read(const char *disk, uint8_t *buf, uint32_t start, uint32_t num)
37 {
38 int rc, loop = 0;
39
40 do {
41 rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
42 if (rc == 0) {
43 rc = disk_access_read(disk, buf, start, num);
44 LOG_DBG("disk read: (start:%d, num:%d) (ret: %d)", start, num, rc);
45 }
46 } while ((rc == -EBUSY) && (loop++ < 16));
47 return rc;
48 }
49
disk_write(const char * disk,const uint8_t * buf,uint32_t start,uint32_t num)50 static int disk_write(const char *disk, const uint8_t *buf, uint32_t start, uint32_t num)
51 {
52 int rc, loop = 0;
53
54 do {
55 rc = disk_access_ioctl(disk, DISK_IOCTL_CTRL_SYNC, NULL);
56 if (rc == 0) {
57 rc = disk_access_write(disk, buf, start, num);
58 LOG_DBG("disk write: (start:%d, num:%d) (ret: %d)", start, num, rc);
59 }
60 } while ((rc == -EBUSY) && (loop++ < 16));
61 return rc;
62 }
63
disk_prepare_range(struct disk_data * disk,uint32_t addr,uint32_t size,uint32_t * s_start,uint32_t * s_count)64 static int disk_prepare_range(struct disk_data *disk, uint32_t addr, uint32_t size,
65 uint32_t *s_start, uint32_t *s_count)
66 {
67 *s_start = CONFIG_EXT2_DISK_STARTING_SECTOR + addr / disk->sector_size;
68 *s_count = size / disk->sector_size;
69
70 LOG_DBG("addr:0x%x size:0x%x -> sector_start:%d sector_count:%d",
71 addr, size, *s_start, *s_count);
72
73 /* Check for overflow. */
74 if (*s_count > UINT32_MAX - *s_start) {
75 LOG_ERR("Requested range (%d:+%d) can't be accessed due to overflow.",
76 *s_start, *s_count);
77 return -ENOSPC;
78 }
79
80 /* Cannot read or write outside the disk. */
81 if (*s_start + *s_count > disk->sector_count) {
82 LOG_ERR("Requested sectors: %d-%d are outside of disk (num_sectors: %d)",
83 *s_start, *s_start + *s_count, disk->sector_count);
84 return -ENOSPC;
85 }
86 return 0;
87 }
88
disk_access_read_block(struct ext2_data * fs,void * buf,uint32_t block)89 static int disk_access_read_block(struct ext2_data *fs, void *buf, uint32_t block)
90 {
91 int rc;
92 struct disk_data *disk = fs->backend;
93 uint32_t sector_start, sector_count;
94
95 rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
96 §or_start, §or_count);
97 if (rc < 0) {
98 return rc;
99 }
100 return disk_read(disk->name, buf, sector_start, sector_count);
101 }
102
disk_access_write_block(struct ext2_data * fs,const void * buf,uint32_t block)103 static int disk_access_write_block(struct ext2_data *fs, const void *buf, uint32_t block)
104 {
105 int rc;
106 struct disk_data *disk = fs->backend;
107 uint32_t sector_start, sector_count;
108
109 rc = disk_prepare_range(disk, block * fs->block_size, fs->block_size,
110 §or_start, §or_count);
111 if (rc < 0) {
112 return rc;
113 }
114 return disk_write(disk->name, buf, sector_start, sector_count);
115 }
116
disk_access_read_superblock(struct ext2_data * fs,struct ext2_disk_superblock * sb)117 static int disk_access_read_superblock(struct ext2_data *fs, struct ext2_disk_superblock *sb)
118 {
119 int rc;
120 struct disk_data *disk = fs->backend;
121 uint32_t sector_start, sector_count;
122
123 rc = disk_prepare_range(disk, EXT2_SUPERBLOCK_OFFSET, sizeof(struct ext2_disk_superblock),
124 §or_start, §or_count);
125 if (rc < 0) {
126 return rc;
127 }
128 return disk_read(disk->name, (uint8_t *)sb, sector_start, sector_count);
129 }
130
disk_access_sync(struct ext2_data * fs)131 static int disk_access_sync(struct ext2_data *fs)
132 {
133 struct disk_data *disk = fs->backend;
134
135 LOG_DBG("Sync disk %s", disk->name);
136 return disk_access_ioctl(disk->name, DISK_IOCTL_CTRL_SYNC, NULL);
137 }
138
139 static const struct ext2_backend_ops disk_access_ops = {
140 .get_device_size = disk_access_device_size,
141 .get_write_size = disk_access_write_size,
142 .read_block = disk_access_read_block,
143 .write_block = disk_access_write_block,
144 .read_superblock = disk_access_read_superblock,
145 .sync = disk_access_sync,
146 };
147
ext2_init_disk_access_backend(struct ext2_data * fs,const void * storage_dev,int flags)148 int ext2_init_disk_access_backend(struct ext2_data *fs, const void *storage_dev, int flags)
149 {
150 int rc;
151 uint32_t sector_size, sector_count;
152 const char *name = (const char *)storage_dev;
153
154 rc = disk_access_init(name);
155 if (rc < 0) {
156 LOG_ERR("FAIL: unable to find disk %s: %d\n", name, rc);
157 return rc;
158 }
159
160 rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_COUNT, §or_count);
161 if (rc < 0) {
162 LOG_ERR("Disk access (sector count) error: %d", rc);
163 return rc;
164 }
165
166 rc = disk_access_ioctl(name, DISK_IOCTL_GET_SECTOR_SIZE, §or_size);
167 if (rc < 0) {
168 LOG_ERR("Disk access (sector size) error: %d", rc);
169 return rc;
170 }
171
172 disk_data = (struct disk_data) {
173 .name = storage_dev,
174 .sector_size = sector_size,
175 .sector_count = sector_count,
176 };
177
178 fs->backend = &disk_data;
179 fs->backend_ops = &disk_access_ops;
180 return 0;
181 }
182