1 // SPDX-License-Identifier: GPL-2.0+
2 /*
3  * (C) Copyright 2001
4  * Hans-Joerg Frieden, Hyperion Entertainment
5  * Hans-JoergF@hyperion-entertainment.com
6  */
7 #include <command.h>
8 #include <env.h>
9 #include "part_amiga.h"
10 #include <part.h>
11 #include <vsprintf.h>
12 
13 #undef AMIGA_DEBUG
14 
15 #ifdef AMIGA_DEBUG
16 #define PRINTF(fmt, args...) printf(fmt ,##args)
17 #else
18 #define PRINTF(fmt, args...)
19 #endif
20 
21 struct block_header
22 {
23     u32 id;
24     u32 summed_longs;
25     s32 chk_sum;
26 };
27 
28 static unsigned char block_buffer[DEFAULT_SECTOR_SIZE];
29 static struct rigid_disk_block rdb = {0};
30 static struct bootcode_block bootcode = {0};
31 
32 /*
33  * Copy a bcpl to a c string
34  */
bcpl_strcpy(char * to,char * from)35 static void bcpl_strcpy(char *to, char *from)
36 {
37     int len = *from++;
38 
39     while (len)
40     {
41 	*to++ = *from++;
42 	len--;
43     }
44     *to = 0;
45 }
46 
47 /*
48  * Print a BCPL String. BCPL strings start with a byte with the length
49  * of the string, and don't contain a terminating nul character
50  */
bstr_print(char * string)51 static void bstr_print(char *string)
52 {
53     int len = *string++;
54     char buffer[256];
55     int i;
56 
57     i = 0;
58     while (len)
59     {
60 	buffer[i++] = *string++;
61 	len--;
62     }
63 
64     buffer[i] = 0;
65     printf("%-10s", buffer);
66 }
67 
68 /*
69  * Sum a block. The checksum of a block must end up at zero
70  * to be valid. The chk_sum field is selected so that adding
71  * it yields zero.
72  */
sum_block(struct block_header * header)73 int sum_block(struct block_header *header)
74 {
75     s32 *block = (s32 *)header;
76     u32 i;
77     s32 sum = 0;
78 
79     for (i = 0; i < header->summed_longs; i++)
80 	sum += *block++;
81 
82     return (sum != 0);
83 }
84 
85 /*
86  * Print an AmigaOS disk type. Disk types are a four-byte identifier
87  * describing the file system. They are usually written as a three-letter
88  * word followed by a backslash and a version number. For example,
89  * DOS\0 would be the original file system. SFS\0 would be SmartFileSystem.
90  * DOS\1 is FFS.
91  */
print_disk_type(u32 disk_type)92 static void print_disk_type(u32 disk_type)
93 {
94     char buffer[6];
95     buffer[0] = (disk_type & 0xFF000000)>>24;
96     buffer[1] = (disk_type & 0x00FF0000)>>16;
97     buffer[2] = (disk_type & 0x0000FF00)>>8;
98     buffer[3] = '\\';
99     buffer[4] = (disk_type & 0x000000FF) + '0';
100     buffer[5] = 0;
101     printf("%s", buffer);
102 }
103 
104 /*
105  * Print the info contained within the given partition block
106  */
print_part_info(struct partition_block * p)107 static void print_part_info(struct partition_block *p)
108 {
109     struct amiga_part_geometry *g;
110 
111     g = (struct amiga_part_geometry *)&(p->environment);
112 
113     bstr_print(p->drive_name);
114     printf("%6d\t%6d\t",
115 	   g->low_cyl * g->block_per_track * g->surfaces ,
116 	   (g->high_cyl - g->low_cyl + 1) * g->block_per_track * g->surfaces - 1);
117     print_disk_type(g->dos_type);
118     printf("\t%5d\n", g->boot_priority);
119 }
120 
121 /*
122  * Search for the Rigid Disk Block. The rigid disk block is required
123  * to be within the first 16 blocks of a drive, needs to have
124  * the ID AMIGA_ID_RDISK ('RDSK') and needs to have a valid
125  * sum-to-zero checksum
126  */
get_rdisk(struct blk_desc * desc)127 struct rigid_disk_block *get_rdisk(struct blk_desc *desc)
128 {
129     int i;
130     int limit;
131     char *s;
132 
133     s = env_get("amiga_scanlimit");
134     if (s)
135 	limit = dectoul(s, NULL);
136     else
137 	limit = AMIGA_BLOCK_LIMIT;
138 
139     for (i=0; i<limit; i++)
140     {
141 	ulong res = blk_dread(desc, i, 1, (ulong *)block_buffer);
142 	if (res == 1)
143 	{
144 	    struct rigid_disk_block *trdb = (struct rigid_disk_block *)block_buffer;
145 	    if (trdb->id == AMIGA_ID_RDISK)
146 	    {
147 		PRINTF("Rigid disk block suspect at %d, checking checksum\n",i);
148 		if (sum_block((struct block_header *)block_buffer) == 0)
149 		{
150 		    PRINTF("FOUND\n");
151 		    memcpy(&rdb, trdb, sizeof(struct rigid_disk_block));
152 		    return (struct rigid_disk_block *)&rdb;
153 		}
154 	    }
155 	}
156     }
157     PRINTF("Done scanning, no RDB found\n");
158     return NULL;
159 }
160 
161 /*
162  * Search for boot code
163  * Again, the first boot block must be located somewhere in the first 16 blocks, or rooted in the
164  * Ridgid disk block
165  */
166 
get_bootcode(struct blk_desc * desc)167 struct bootcode_block *get_bootcode(struct blk_desc *desc)
168 {
169     int i;
170     int limit;
171     char *s;
172 
173     s = env_get("amiga_scanlimit");
174     if (s)
175 	limit = dectoul(s, NULL);
176     else
177 	limit = AMIGA_BLOCK_LIMIT;
178 
179     PRINTF("Scanning for BOOT from 0 to %d\n", limit);
180 
181     for (i = 0; i < limit; i++)
182     {
183 	ulong res = blk_dread(desc, i, 1, (ulong *)block_buffer);
184 	if (res == 1)
185 	{
186 	    struct bootcode_block *boot = (struct bootcode_block *)block_buffer;
187 	    if (boot->id == AMIGA_ID_BOOT)
188 	    {
189 		PRINTF("BOOT block at %d, checking checksum\n", i);
190 		if (sum_block((struct block_header *)block_buffer) == 0)
191 		{
192 		    PRINTF("Found valid bootcode block\n");
193 		    memcpy(&bootcode, boot, sizeof(struct bootcode_block));
194 		    return &bootcode;
195 		}
196 	    }
197 	}
198     }
199 
200     PRINTF("No boot code found on disk\n");
201     return 0;
202 }
203 
204 /*
205  * Test if the given partition has an Amiga partition table/Rigid
206  * Disk block
207  */
part_test_amiga(struct blk_desc * desc)208 static int part_test_amiga(struct blk_desc *desc)
209 {
210     struct rigid_disk_block *rdb;
211     struct bootcode_block *bootcode;
212 
213     PRINTF("part_test_amiga: Testing for an Amiga RDB partition\n");
214 
215 	rdb = get_rdisk(desc);
216     if (rdb)
217     {
218 	bootcode = get_bootcode(desc);
219 	if (bootcode)
220 	    PRINTF("part_test_amiga: bootable Amiga disk\n");
221 	else
222 	    PRINTF("part_test_amiga: non-bootable Amiga disk\n");
223 
224 	return 0;
225     }
226     else
227     {
228 	PRINTF("part_test_amiga: no RDB found\n");
229 	return -1;
230     }
231 
232 }
233 
234 /*
235  * Find partition number partnum on the given drive.
236  */
find_partition(struct blk_desc * desc,int partnum)237 static struct partition_block *find_partition(struct blk_desc *desc,
238 					      int partnum)
239 {
240     struct rigid_disk_block *rdb;
241     struct partition_block *p;
242     u32 block;
243 
244     PRINTF("Trying to find partition block %d\n", partnum);
245 	rdb = get_rdisk(desc);
246     if (!rdb)
247     {
248 	PRINTF("find_partition: no rdb found\n");
249 	return NULL;
250     }
251 
252     PRINTF("find_partition: Scanning partition list\n");
253 
254     block = rdb->partition_list;
255     PRINTF("find_partition: partition list at 0x%x\n", block);
256 
257     while (block != 0xFFFFFFFF)
258     {
259 	ulong res = blk_dread(desc, block, 1, (ulong *)block_buffer);
260 	if (res == 1)
261 	{
262 	    p = (struct partition_block *)block_buffer;
263 	    if (p->id == AMIGA_ID_PART)
264 	    {
265 		PRINTF("PART block suspect at 0x%x, checking checksum\n",block);
266 		if (sum_block((struct block_header *)p) == 0)
267 		{
268 		    if (partnum == 0) break;
269 		    else
270 		    {
271 			partnum--;
272 			block = p->next;
273 		    }
274 		}
275 	    } else block = 0xFFFFFFFF;
276 	} else block = 0xFFFFFFFF;
277     }
278 
279     if (block == 0xFFFFFFFF)
280     {
281 	PRINTF("PART block not found\n");
282 	return NULL;
283     }
284 
285     return (struct partition_block *)block_buffer;
286 }
287 
288 /*
289  * Get info about a partition
290  */
part_get_info_amiga(struct blk_desc * desc,int part,struct disk_partition * info)291 static int part_get_info_amiga(struct blk_desc *desc, int part,
292 			       struct disk_partition *info)
293 {
294 	struct partition_block *p = find_partition(desc, part - 1);
295     struct amiga_part_geometry *g;
296     u32 disk_type;
297 
298     if (!p) return -1;
299 
300     g = (struct amiga_part_geometry *)&(p->environment);
301     info->start = g->low_cyl  * g->block_per_track * g->surfaces;
302     info->size  = (g->high_cyl - g->low_cyl + 1) * g->block_per_track * g->surfaces - 1;
303     info->blksz = rdb.block_bytes;
304     bcpl_strcpy((char *)info->name, p->drive_name);
305 
306     disk_type = g->dos_type;
307 
308     info->type[0] = (disk_type & 0xFF000000)>>24;
309     info->type[1] = (disk_type & 0x00FF0000)>>16;
310     info->type[2] = (disk_type & 0x0000FF00)>>8;
311     info->type[3] = '\\';
312     info->type[4] = (disk_type & 0x000000FF) + '0';
313     info->type[5] = 0;
314 
315     return 0;
316 }
317 
part_print_amiga(struct blk_desc * desc)318 static void part_print_amiga(struct blk_desc *desc)
319 {
320     struct rigid_disk_block *rdb;
321     struct bootcode_block *boot;
322     struct partition_block *p;
323     u32 block;
324     int i = 1;
325 
326 	rdb = get_rdisk(desc);
327     if (!rdb)
328     {
329 	PRINTF("part_print_amiga: no rdb found\n");
330 	return;
331     }
332 
333     PRINTF("part_print_amiga: Scanning partition list\n");
334 
335     block = rdb->partition_list;
336     PRINTF("part_print_amiga: partition list at 0x%x\n", block);
337 
338     printf("Summary:  DiskBlockSize: %d\n"
339 	   "          Cylinders    : %d\n"
340 	   "          Sectors/Track: %d\n"
341 	   "          Heads        : %d\n\n",
342 	   rdb->block_bytes, rdb->cylinders, rdb->sectors,
343 	   rdb->heads);
344 
345     printf("                 First   Num. \n"
346 	   "Nr.  Part. Name  Block   Block  Type        Boot Priority\n");
347 
348     while (block != 0xFFFFFFFF)
349     {
350 	ulong res;
351 
352 	PRINTF("Trying to load block #0x%X\n", block);
353 
354 	res = blk_dread(desc, block, 1, (ulong *)block_buffer);
355 	if (res == 1)
356 	{
357 	    p = (struct partition_block *)block_buffer;
358 	    if (p->id == AMIGA_ID_PART)
359 	    {
360 		PRINTF("PART block suspect at 0x%x, checking checksum\n",block);
361 		if (sum_block((struct block_header *)p) == 0)
362 		{
363 		    printf("%-4d ", i); i++;
364 		    print_part_info(p);
365 		    block = p->next;
366 		}
367 	    } else block = 0xFFFFFFFF;
368 	} else block = 0xFFFFFFFF;
369     }
370 
371 	boot = get_bootcode(desc);
372     if (boot)
373     {
374 	printf("Disk is bootable\n");
375     }
376 }
377 
378 U_BOOT_PART_TYPE(amiga) = {
379 	.name		= "AMIGA",
380 	.part_type	= PART_TYPE_AMIGA,
381 	.max_entries	= AMIGA_ENTRY_NUMBERS,
382 	.get_info	= part_get_info_amiga,
383 	.print		= part_print_amiga,
384 	.test		= part_test_amiga,
385 };
386