1 /***************************************************************************
2  * Copyright (c) 2024 Microsoft Corporation
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the MIT License which is available at
6  * https://opensource.org/licenses/MIT.
7  *
8  * SPDX-License-Identifier: MIT
9  **************************************************************************/
10 
11 
12 /**************************************************************************/
13 /**************************************************************************/
14 /**                                                                       */
15 /** FileX Component                                                       */
16 /**                                                                       */
17 /**   Utility                                                             */
18 /**                                                                       */
19 /**************************************************************************/
20 /**************************************************************************/
21 
22 #define FX_SOURCE_CODE
23 
24 
25 /* Include necessary system files.  */
26 
27 #include "fx_api.h"
28 #include "fx_system.h"
29 #include "fx_utility.h"
30 #ifdef FX_ENABLE_FAULT_TOLERANT
31 #include "fx_fault_tolerant.h"
32 #endif /* FX_ENABLE_FAULT_TOLERANT */
33 
34 
35 /**************************************************************************/
36 /*                                                                        */
37 /*  FUNCTION                                               RELEASE        */
38 /*                                                                        */
39 /*    _fx_utility_logical_sector_write                    PORTABLE C      */
40 /*                                                           6.1.6        */
41 /*  AUTHOR                                                                */
42 /*                                                                        */
43 /*    William E. Lamie, Microsoft Corporation                             */
44 /*                                                                        */
45 /*  DESCRIPTION                                                           */
46 /*                                                                        */
47 /*    This function handles logical sector write requests for all FileX   */
48 /*    components.  If the logical sector is currently in the media's      */
49 /*    buffer supplied by the caller, the function simply marks the buffer */
50 /*    as written to.  Otherwise, physical I/O is requested through the    */
51 /*    corresponding I/O driver.                                           */
52 /*                                                                        */
53 /*    Note: Conversion of the logical sector is done inside the driver.   */
54 /*          This results in a performance boost for FLASH or RAM media    */
55 /*          devices.                                                      */
56 /*                                                                        */
57 /*  INPUT                                                                 */
58 /*                                                                        */
59 /*    media_ptr                             Media control block pointer   */
60 /*    logical_sector                        Logical sector number         */
61 /*    buffer_ptr                            Pointer of sector buffer      */
62 /*    sectors                               Number of sectors to write    */
63 /*    sector_type                           Type of sector(s) to write    */
64 /*                                                                        */
65 /*  OUTPUT                                                                */
66 /*                                                                        */
67 /*    return status                                                       */
68 /*                                                                        */
69 /*  CALLS                                                                 */
70 /*                                                                        */
71 /*    _fx_utility_logical_sector_flush      Flush and invalidate sectors  */
72 /*                                          that overlap with non-cache   */
73 /*                                          sector I/O.                   */
74 /*    I/O Driver                                                          */
75 /*                                                                        */
76 /*  CALLED BY                                                             */
77 /*                                                                        */
78 /*    FileX System Functions                                              */
79 /*                                                                        */
80 /*  RELEASE HISTORY                                                       */
81 /*                                                                        */
82 /*    DATE              NAME                      DESCRIPTION             */
83 /*                                                                        */
84 /*  05-19-2020     William E. Lamie         Initial Version 6.0           */
85 /*  09-30-2020     William E. Lamie         Modified comment(s), and      */
86 /*                                            added conditional to        */
87 /*                                            disable cache,              */
88 /*                                            resulting in version 6.1    */
89 /*  04-02-2021     Bhupendra Naphade        Modified comment(s),          */
90 /*                                            updated check for logical   */
91 /*                                            sector value,               */
92 /*                                            resulting in version 6.1.6  */
93 /*                                                                        */
94 /**************************************************************************/
_fx_utility_logical_sector_write(FX_MEDIA * media_ptr,ULONG64 logical_sector,VOID * buffer_ptr,ULONG sectors,UCHAR sector_type)95 UINT  _fx_utility_logical_sector_write(FX_MEDIA *media_ptr, ULONG64 logical_sector,
96                                        VOID *buffer_ptr, ULONG sectors, UCHAR sector_type)
97 {
98 
99 #ifndef FX_DISABLE_CACHE
100 FX_CACHED_SECTOR *cache_entry;
101 UINT              cache_size;
102 UINT              index;
103 UINT              i;
104 UCHAR             cache_found = FX_FALSE;
105 #endif /* FX_DISABLE_CACHE */
106 
107 #ifndef FX_MEDIA_STATISTICS_DISABLE
108 
109     /* Determine if the request is for FAT sector.  */
110     if (sector_type == FX_FAT_SECTOR)
111     {
112 
113         /* Increment the number of FAT sector writes.  */
114         media_ptr -> fx_media_fat_sector_writes++;
115     }
116 
117     /* Increment the number of logical sectors written.  */
118     media_ptr -> fx_media_logical_sector_writes++;
119 #endif
120 
121     /* Extended port-specific processing macro, which is by default defined to white space.  */
122     FX_UTILITY_LOGICAL_SECTOR_WRITE_EXTENSION
123 
124 #ifndef FX_DISABLE_CACHE
125     /* Determine if the request is from the internal media buffer area.  */
126     if ((((UCHAR *)buffer_ptr) >= media_ptr -> fx_media_memory_buffer) &&
127         (((UCHAR *)buffer_ptr) <= media_ptr -> fx_media_sector_cache_end))
128     {
129 
130         /* Internal cache buffer is requested.  */
131 
132         /* Determine if the logical sector cache access should use the hash function.  */
133         if (media_ptr -> fx_media_sector_cache_hashed)
134         {
135 
136             /* Calculate the area of the cache for this logical sector.  */
137             index =  (ULONG)(logical_sector & media_ptr -> fx_media_sector_cache_hash_mask) * FX_SECTOR_CACHE_DEPTH;
138 
139             /* Build a pointer to the cache entry.  */
140             cache_entry =  &(media_ptr -> fx_media_sector_cache[index]);
141 
142             for (i = 0; i < FX_SECTOR_CACHE_DEPTH; i++, cache_entry++)
143             {
144 
145 
146                 /* Determine if the logical sector is in the cache - assuming the depth of the
147                    sector cache is 4 entries.  */
148                 if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
149                 {
150                     cache_found = FX_TRUE;
151                     break;
152                 }
153             }
154         }
155         else
156         {
157 
158             /* Search for an entry in the cache that matches this request.  */
159             cache_size =            media_ptr -> fx_media_sector_cache_size;
160             cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
161 
162             /* Look at the cache entries until a match is found or the end of
163                the cache is reached.  */
164             while (cache_size--)
165             {
166 
167                 /* Determine if the requested sector has been found.  */
168                 if ((cache_entry -> fx_cached_sector_valid) && (cache_entry -> fx_cached_sector == logical_sector))
169                 {
170                     cache_found = FX_TRUE;
171                     break;
172                 }
173 
174                 /* Otherwise, we have not found the cached entry yet.  */
175 
176                 /* If there are more entries, move to the next one.  */
177                 if (cache_entry -> fx_cached_sector_next_used)
178                 {
179 
180                     /* Move to the next cache entry.  */
181                     cache_entry =  cache_entry -> fx_cached_sector_next_used;
182                 }
183             }
184         }
185 
186 #ifdef FX_ENABLE_FAULT_TOLERANT
187         if (media_ptr -> fx_media_fault_tolerant_enabled &&
188             (media_ptr -> fx_media_fault_tolerant_state & FX_FAULT_TOLERANT_STATE_STARTED) &&
189             (sector_type == FX_DATA_SECTOR) &&
190             !(cache_found && (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr)))
191         {
192 
193             /* Special use case for file write when fault tolerant is enabled. */
194             /* Data are read from one sector but write to another sector. */
195             /* Need to invalidate both of original and new caches. */
196             if (cache_found)
197             {
198 
199                 /* Invalidate the new cache. */
200                 cache_entry -> fx_cached_sector_valid = FX_FALSE;
201                 cache_found = FX_FALSE;
202             }
203 
204             /* Search for original cache.  */
205             cache_size =            media_ptr -> fx_media_sector_cache_size;
206             cache_entry =           media_ptr -> fx_media_sector_cache_list_ptr;
207 
208             /* Look at the cache entries until a match is found or the end of
209                the cache is reached.  */
210             while (cache_size--)
211             {
212 
213                 /* Determine if the original sector has been found.  */
214                 if ((cache_entry -> fx_cached_sector_valid) &&
215                     (cache_entry -> fx_cached_sector_memory_buffer == buffer_ptr))
216                 {
217 
218                     /* Invalidate the original cache. */
219                     cache_entry -> fx_cached_sector_valid = FX_FALSE;
220                     break;
221                 }
222 
223                 /* Otherwise, we have not found the cached entry yet.  */
224 
225                 /* If there are more entries, move to the next one.  */
226                 if (cache_entry -> fx_cached_sector_next_used)
227                 {
228 
229                     /* Move to the next cache entry.  */
230                     cache_entry =  cache_entry -> fx_cached_sector_next_used;
231                 }
232             }
233         }
234 #endif /* FX_ENABLE_FAULT_TOLERANT */
235 
236         if (cache_found)
237         {
238 
239             /* Yes, we found a match.  */
240 
241 #ifdef FX_FAULT_TOLERANT
242 
243             /* Check for a system sector. Data sector fault tolerance is selected with
244                the FX_FAULT_TOLERANT_DATA option.  */
245             if (sector_type != FX_DATA_SECTOR)
246             {
247 
248                 /* With the fault tolerant option enabled, system sectors are written immediately to
249                    the media.  */
250 
251 #ifndef FX_MEDIA_STATISTICS_DISABLE
252 
253                 /* Increment the number of driver write sector(s) requests.  */
254                 media_ptr -> fx_media_driver_write_requests++;
255 #endif
256 
257                 /* Build write request to the driver.  */
258                 media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
259                 media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
260                 media_ptr -> fx_media_driver_buffer =           cache_entry -> fx_cached_sector_memory_buffer;
261 #ifdef FX_DRIVER_USE_64BIT_LBA
262                 media_ptr -> fx_media_driver_logical_sector =   logical_sector;
263 #else
264                 media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
265 #endif
266                 media_ptr -> fx_media_driver_sectors =          1;
267                 media_ptr -> fx_media_driver_sector_type =      sector_type;
268 
269                 /* Yes, a system sector write is present so set the flag.  The driver
270                    can use this flag to make extra safeguards in writing the sector
271                    out, yielding more fault tolerance.  */
272                 media_ptr -> fx_media_driver_system_write =  FX_TRUE;
273 
274                 /* If trace is enabled, insert this event into the trace buffer.  */
275                 FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, 1, cache_entry -> fx_cached_sector_memory_buffer, FX_TRACE_INTERNAL_EVENTS, 0, 0)
276 
277                 /* Invoke the driver to write the sector(s).  */
278                 (media_ptr -> fx_media_driver_entry) (media_ptr);
279 
280                 /* Clear the system write flag.  */
281                 media_ptr -> fx_media_driver_system_write =  FX_FALSE;
282 
283                 /* Return success.  */
284                 return(media_ptr -> fx_media_driver_status);
285             }
286 #endif
287 
288             /* Determine if this is the first write of this logical sector.  */
289             if (cache_entry -> fx_cached_sector_buffer_dirty == FX_FALSE)
290             {
291 
292                 /* Yes, increment the number of outstanding dirty sectors.  */
293                 media_ptr -> fx_media_sector_cache_dirty_count++;
294 
295                 /* Simply mark this entry as dirty.  */
296                 cache_entry -> fx_cached_sector_buffer_dirty =  FX_TRUE;
297             }
298 
299             /* Don't bother updating the cache linked list since writes are
300                preceded by reads anyway.  */
301 
302             /* Success, return to caller immediately!  */
303             return(FX_SUCCESS);
304         }
305 
306 
307         /* Okay, so if we are here the request must be for the additional FAT writes, since this is the
308            only time a write request is made without a preceding read request.  */
309 
310         /* Is the logical sector valid?  */
311         if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF)))
312         {
313             return(FX_SECTOR_INVALID);
314         }
315 
316         /* Compare logical sector against total sectors to make sure it is valid.  */
317         if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors)
318         {
319             return(FX_SECTOR_INVALID);
320         }
321 
322         /* Just write the buffer to the media.  */
323 
324 #ifndef FX_MEDIA_STATISTICS_DISABLE
325 
326         /* Increment the number of driver write sector(s) requests.  */
327         media_ptr -> fx_media_driver_write_requests++;
328 #endif
329 
330         /* Build write request to the driver.  */
331         media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
332         media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
333         media_ptr -> fx_media_driver_buffer =           buffer_ptr;
334 #ifdef FX_DRIVER_USE_64BIT_LBA
335         media_ptr -> fx_media_driver_logical_sector =   logical_sector;
336 #else
337         media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
338 #endif
339         media_ptr -> fx_media_driver_sectors =          sectors;
340         media_ptr -> fx_media_driver_sector_type =      sector_type;
341 
342         /* Determine if the system write flag needs to be set.  */
343         if (sector_type != FX_DATA_SECTOR)
344         {
345 
346             /* Yes, a system sector write is present so set the flag.  The driver
347                can use this flag to make extra safeguards in writing the sector
348                out, yielding more fault tolerance.  */
349             media_ptr -> fx_media_driver_system_write =  FX_TRUE;
350         }
351 
352         /* If trace is enabled, insert this event into the trace buffer.  */
353         FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
354 
355         /* Invoke the driver to write the sector(s).  */
356         (media_ptr -> fx_media_driver_entry) (media_ptr);
357 
358         /* Clear the system write flag.  */
359         media_ptr -> fx_media_driver_system_write =  FX_FALSE;
360 
361         /* Check for successful completion.  */
362         if (media_ptr -> fx_media_driver_status)
363         {
364 
365             /* Error writing a internal sector out.  Return the
366                error status.  */
367             return(media_ptr -> fx_media_driver_status);
368         }
369 
370         /* At this point, we have a successful write.  */
371         return(FX_SUCCESS);
372     }
373     else
374 #endif /* FX_DISABLE_CACHE */
375     {
376 
377         /* Otherwise, the write request is being made directly from an application
378            buffer. Determine if the logical sector is valid.  */
379 
380         /* Is the logical sector valid? */
381         if ((logical_sector == 0) || (logical_sector == ((ULONG)0xFFFFFFFF)))
382         {
383             return(FX_SECTOR_INVALID);
384         }
385 
386         /* Compare logical sector against total sectors to make sure it is valid.  */
387         if ((logical_sector + sectors - 1) >= media_ptr -> fx_media_total_sectors)
388         {
389             return(FX_SECTOR_INVALID);
390         }
391 
392         /* Flush and invalidate for any entries in the cache that are in this direct I/O read request range.  */
393         _fx_utility_logical_sector_flush(media_ptr, logical_sector, (ULONG64) sectors, FX_TRUE);
394 
395 #ifdef FX_DISABLE_CACHE
396         if ((logical_sector <= media_ptr -> fx_media_memory_buffer_sector) && (logical_sector + sectors >= media_ptr -> fx_media_memory_buffer_sector))
397         {
398             media_ptr -> fx_media_memory_buffer_sector = (ULONG64)-1;
399         }
400 #endif /* FX_DISABLE_CACHE */
401 
402 #ifndef FX_MEDIA_STATISTICS_DISABLE
403 
404         /* Increment the number of driver write sector(s) requests.  */
405         media_ptr -> fx_media_driver_write_requests++;
406 #endif
407 
408         /* Build request to the driver.  */
409         media_ptr -> fx_media_driver_request =          FX_DRIVER_WRITE;
410         media_ptr -> fx_media_driver_status =           FX_IO_ERROR;
411         media_ptr -> fx_media_driver_buffer =           buffer_ptr;
412 #ifdef FX_DRIVER_USE_64BIT_LBA
413         media_ptr -> fx_media_driver_logical_sector =   logical_sector;
414 #else
415         media_ptr -> fx_media_driver_logical_sector =   (ULONG)logical_sector;
416 #endif
417         media_ptr -> fx_media_driver_sectors =          sectors;
418         media_ptr -> fx_media_driver_sector_type =      sector_type;
419 
420         /* Determine if the system write flag needs to be set.  */
421         if (sector_type != FX_DATA_SECTOR)
422         {
423 
424             /* Yes, a system sector write is present so set the flag.  The driver
425                can use this flag to make extra safeguards in writing the sector
426                out, yielding more fault tolerance.  */
427             media_ptr -> fx_media_driver_system_write =  FX_TRUE;
428         }
429 
430         /* If trace is enabled, insert this event into the trace buffer.  */
431         FX_TRACE_IN_LINE_INSERT(FX_TRACE_INTERNAL_IO_DRIVER_WRITE, media_ptr, logical_sector, sectors, buffer_ptr, FX_TRACE_INTERNAL_EVENTS, 0, 0)
432 
433         /* Invoke the driver to write the sector(s).  */
434         (media_ptr -> fx_media_driver_entry) (media_ptr);
435 
436         /* Clear the system write flag.  */
437         media_ptr -> fx_media_driver_system_write =  FX_FALSE;
438 
439         /* Return driver status.  */
440         return(media_ptr -> fx_media_driver_status);
441     }
442 }
443 
444