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 			&sector_start, &sector_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 			&sector_start, &sector_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 			&sector_start, &sector_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, &sector_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, &sector_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