1 /*
2  *  SPDX-License-Identifier: GPL-2.0-or-later
3  *
4  *  Copyright (C) 2002-2021  The DOSBox Team
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License along
17  *  with this program; if not, write to the Free Software Foundation, Inc.,
18  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19  */
20 
21 #include "program_boot.h"
22 
23 #include <stdio.h>
24 
25 #include "bios_disk.h"
26 #include "callback.h"
27 #include "control.h"
28 #include "cross.h"
29 #include "dma.h"
30 #include "drives.h"
31 #include "mapper.h"
32 #include "regs.h"
33 #include "string_utils.h"
34 
getFSFile_mounted(char const * filename,Bit32u * ksize,Bit32u * bsize,Bit8u * error)35 FILE *BOOT::getFSFile_mounted(char const *filename, Bit32u *ksize, Bit32u *bsize, Bit8u *error)
36 {
37 	// if return NULL then put in error the errormessage code if an error
38 	// was requested
39 	bool tryload = (*error) ? true : false;
40 	*error = 0;
41 	Bit8u drive;
42 	FILE *tmpfile;
43 	char fullname[DOS_PATHLENGTH];
44 
45 	localDrive *ldp = 0;
46 	if (!DOS_MakeName(const_cast<char *>(filename), fullname, &drive))
47 		return NULL;
48 
49 	try {
50 		ldp = dynamic_cast<localDrive *>(Drives[drive]);
51 		if (!ldp)
52 			return NULL;
53 
54 		tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
55 		if (tmpfile == NULL) {
56 			if (!tryload)
57 				*error = 1;
58 			return NULL;
59 		}
60 
61 		// get file size
62 		fseek(tmpfile, 0L, SEEK_END);
63 		*ksize = (ftell(tmpfile) / 1024);
64 		*bsize = ftell(tmpfile);
65 		fclose(tmpfile);
66 
67 		tmpfile = ldp->GetSystemFilePtr(fullname, "rb+");
68 		if (tmpfile == NULL) {
69 			//				if (!tryload) *error=2;
70 			//				return NULL;
71 			WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
72 			tmpfile = ldp->GetSystemFilePtr(fullname, "rb");
73 			if (tmpfile == NULL) {
74 				if (!tryload)
75 					*error = 1;
76 				return NULL;
77 			}
78 		}
79 
80 		return tmpfile;
81 	} catch (...) {
82 		return NULL;
83 	}
84 }
85 
getFSFile(char const * filename,Bit32u * ksize,Bit32u * bsize,bool tryload)86 FILE *BOOT::getFSFile(char const *filename, Bit32u *ksize, Bit32u *bsize, bool tryload)
87 {
88 	Bit8u error = tryload ? 1 : 0;
89 	FILE *tmpfile = getFSFile_mounted(filename, ksize, bsize, &error);
90 	if (tmpfile)
91 		return tmpfile;
92 	// File not found on mounted filesystem. Try regular filesystem
93 	std::string filename_s(filename);
94 	Cross::ResolveHomedir(filename_s);
95 	tmpfile = fopen_wrap(filename_s.c_str(), "rb+");
96 	if (!tmpfile) {
97 		if ((tmpfile = fopen_wrap(filename_s.c_str(), "rb"))) {
98 			// File exists; So can't be opened in correct mode =>
99 			// error 2
100 			//				fclose(tmpfile);
101 			//				if (tryload) error = 2;
102 			WriteOut(MSG_Get("PROGRAM_BOOT_WRITE_PROTECTED"));
103 			fseek(tmpfile, 0L, SEEK_END);
104 			*ksize = (ftell(tmpfile) / 1024);
105 			*bsize = ftell(tmpfile);
106 			return tmpfile;
107 		}
108 		// Give the delayed errormessages from the mounted variant (or
109 		// from above)
110 		if (error == 1)
111 			WriteOut(MSG_Get("PROGRAM_BOOT_NOT_EXIST"));
112 		if (error == 2)
113 			WriteOut(MSG_Get("PROGRAM_BOOT_NOT_OPEN"));
114 		return NULL;
115 	}
116 	fseek(tmpfile, 0L, SEEK_END);
117 	*ksize = (ftell(tmpfile) / 1024);
118 	*bsize = ftell(tmpfile);
119 	return tmpfile;
120 }
121 
printError(void)122 void BOOT::printError(void)
123 {
124 	WriteOut(MSG_Get("PROGRAM_BOOT_PRINT_ERROR"), PRIMARY_MOD_NAME);
125 }
126 
disable_umb_ems_xms(void)127 void BOOT::disable_umb_ems_xms(void)
128 {
129 	Section *dos_sec = control->GetSection("dos");
130 	dos_sec->ExecuteDestroy(false);
131 	char test[20];
132 	safe_strcpy(test, "umb=false");
133 	dos_sec->HandleInputline(test);
134 	safe_strcpy(test, "xms=false");
135 	dos_sec->HandleInputline(test);
136 	safe_strcpy(test, "ems=false");
137 	dos_sec->HandleInputline(test);
138 	dos_sec->ExecuteInit(false);
139 }
140 
Run(void)141 void BOOT::Run(void)
142 {
143 	// Hack To allow long commandlines
144 	ChangeToLongCmd();
145 	/* In secure mode don't allow people to boot stuff.
146 	 * They might try to corrupt the data on it */
147 	if (control->SecureMode()) {
148 		WriteOut(MSG_Get("PROGRAM_CONFIG_SECURE_DISALLOW"));
149 		return;
150 	}
151 
152 	FILE *usefile_1 = NULL;
153 	FILE *usefile_2 = NULL;
154 	Bitu i = 0;
155 	Bit32u floppysize = 0;
156 	Bit32u rombytesize_1 = 0;
157 	Bit32u rombytesize_2 = 0;
158 	char drive = 'A';
159 	std::string cart_cmd = "";
160 
161 	if (!cmd->GetCount()) {
162 		printError();
163 		return;
164 	}
165 
166 	if (cmd->FindExist("/?", false) || cmd->FindExist("-?", false) ||
167 	    cmd->FindExist("-h", false) || cmd->FindExist("--help", false)) {
168 		WriteOut(MSG_Get("SHELL_CMD_BOOT_HELP_LONG"));
169 		return;
170 	}
171 	if (cmd->GetCount() == 1) {
172 		if (cmd->FindCommand(1, temp_line)) {
173 			if (temp_line.length() == 2 && toupper(temp_line[0]) >= 'A' &&
174 			    toupper(temp_line[0]) <= 'Z' && temp_line[1] == ':') {
175 				drive = toupper(temp_line[0]);
176 				if ((drive != 'A') && (drive != 'C') &&
177 				    (drive != 'D')) {
178 					printError();
179 					return;
180 				}
181 				i++;
182 			}
183 		}
184 	}
185 	while (i < cmd->GetCount()) {
186 		if (cmd->FindCommand(i + 1, temp_line)) {
187 			if ((temp_line == "-l") || (temp_line == "-L")) {
188 				/* Specifying drive... next argument then is the
189 				 * drive */
190 				i++;
191 				if (cmd->FindCommand(i + 1, temp_line)) {
192 					drive = toupper(temp_line[0]);
193 					if ((drive != 'A') && (drive != 'C') &&
194 					    (drive != 'D')) {
195 						printError();
196 						return;
197 					}
198 
199 				} else {
200 					printError();
201 					return;
202 				}
203 				i++;
204 				continue;
205 			}
206 
207 			if ((temp_line == "-e") || (temp_line == "-E")) {
208 				/* Command mode for PCJr cartridges */
209 				i++;
210 				if (cmd->FindCommand(i + 1, temp_line)) {
211 					for (size_t ct = 0;
212 					     ct < temp_line.size(); ct++)
213 						temp_line[ct] = toupper(
214 						        temp_line[ct]);
215 					cart_cmd = temp_line;
216 				} else {
217 					printError();
218 					return;
219 				}
220 				i++;
221 				continue;
222 			}
223 
224 			if (i >= MAX_SWAPPABLE_DISKS) {
225 				return; // TODO give a warning.
226 			}
227 			WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_OPEN"),
228 			         temp_line.c_str());
229 			Bit32u rombytesize;
230 			FILE *usefile = getFSFile(temp_line.c_str(),
231 			                          &floppysize, &rombytesize);
232 			if (usefile != NULL) {
233 				diskSwap[i].reset(
234 				        new imageDisk(usefile, temp_line.c_str(),
235 				                      floppysize, false));
236 				if (usefile_1 == NULL) {
237 					usefile_1 = usefile;
238 					rombytesize_1 = rombytesize;
239 				} else {
240 					usefile_2 = usefile;
241 					rombytesize_2 = rombytesize;
242 				}
243 			} else {
244 				WriteOut(MSG_Get("PROGRAM_BOOT_IMAGE_NOT_OPEN"),
245 				         temp_line.c_str());
246 				return;
247 			}
248 		}
249 		i++;
250 	}
251 
252 	swapInDisks(0);
253 
254 	if (!imageDiskList[drive_index(drive)]) {
255 		WriteOut(MSG_Get("PROGRAM_BOOT_UNABLE"), drive);
256 		return;
257 	}
258 
259 	bootSector bootarea;
260 	imageDiskList[drive_index(drive)]->Read_Sector(
261 	        0, 0, 1, reinterpret_cast<uint8_t *>(&bootarea));
262 	if ((bootarea.rawdata[0] == 0x50) && (bootarea.rawdata[1] == 0x43) &&
263 	    (bootarea.rawdata[2] == 0x6a) && (bootarea.rawdata[3] == 0x72)) {
264 		if (machine != MCH_PCJR) {
265 			WriteOut(MSG_Get("PROGRAM_BOOT_CART_WO_PCJR"));
266 		} else {
267 			Bit8u rombuf[65536];
268 			Bits cfound_at = -1;
269 			if (!cart_cmd.empty()) {
270 				/* read cartridge data into buffer */
271 				fseek(usefile_1, 0x200L, SEEK_SET);
272 				if (fread(rombuf, 1, rombytesize_1 - 0x200,
273 				          usefile_1) < rombytesize_1 - 0x200) {
274 					LOG_MSG("Failed to read sufficient cartridge data");
275 					fclose(usefile_1);
276 					return;
277 				}
278 				char cmdlist[1024];
279 				cmdlist[0] = 0;
280 				Bitu ct = 6;
281 				Bits clen = rombuf[ct];
282 				char buf[257];
283 				if (cart_cmd == "?") {
284 					while (clen != 0) {
285 						strncpy(buf, (char *)&rombuf[ct + 1],
286 						        clen);
287 						buf[clen] = 0;
288 						upcase(buf);
289 						safe_strcat(cmdlist, " ");
290 						safe_strcat(cmdlist, buf);
291 						ct += 1 + clen + 3;
292 						if (ct > sizeof(cmdlist))
293 							break;
294 						clen = rombuf[ct];
295 					}
296 					if (ct > 6) {
297 						WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),
298 						         cmdlist);
299 					} else {
300 						WriteOut(MSG_Get(
301 						        "PROGRAM_BOOT_CART_NO_CMDS"));
302 					}
303 					for (auto &disk : diskSwap)
304 						disk.reset();
305 					// fclose(usefile_1); //delete diskSwap
306 					// closes the file
307 					return;
308 				} else {
309 					while (clen != 0) {
310 						strncpy(buf, (char *)&rombuf[ct + 1],
311 						        clen);
312 						buf[clen] = 0;
313 						upcase(buf);
314 						safe_strcat(cmdlist, " ");
315 						safe_strcat(cmdlist, buf);
316 						ct += 1 + clen;
317 
318 						if (cart_cmd == buf) {
319 							cfound_at = ct;
320 							break;
321 						}
322 
323 						ct += 3;
324 						if (ct > sizeof(cmdlist))
325 							break;
326 						clen = rombuf[ct];
327 					}
328 					if (cfound_at <= 0) {
329 						if (ct > 6) {
330 							WriteOut(MSG_Get("PROGRAM_BOOT_CART_LIST_CMDS"),
331 							         cmdlist);
332 						} else {
333 							WriteOut(MSG_Get(
334 							        "PROGRAM_BOOT_CART_NO_CMDS"));
335 						}
336 						for (auto &disk : diskSwap)
337 							disk.reset();
338 						// fclose(usefile_1); //Delete
339 						// diskSwap closes the file
340 						return;
341 					}
342 				}
343 			}
344 
345 			disable_umb_ems_xms();
346 			MEM_PreparePCJRCartRom();
347 
348 			if (usefile_1 == NULL)
349 				return;
350 
351 			Bit32u sz1, sz2;
352 			FILE *tfile = getFSFile("system.rom", &sz1, &sz2, true);
353 			if (tfile != NULL) {
354 				fseek(tfile, 0x3000L, SEEK_SET);
355 				Bit32u drd = (Bit32u)fread(rombuf, 1, 0xb000, tfile);
356 				if (drd == 0xb000) {
357 					for (i = 0; i < 0xb000; i++)
358 						phys_writeb(0xf3000 + i, rombuf[i]);
359 				}
360 				fclose(tfile);
361 			}
362 
363 			if (usefile_2 != NULL) {
364 				fseek(usefile_2, 0x0L, SEEK_SET);
365 				if (fread(rombuf, 1, 0x200, usefile_2) < 0x200) {
366 					LOG_MSG("Failed to read sufficient ROM data");
367 					fclose(usefile_2);
368 					return;
369 				}
370 
371 				PhysPt romseg_pt = host_readw(&rombuf[0x1ce]) << 4;
372 
373 				/* read cartridge data into buffer */
374 				fseek(usefile_2, 0x200L, SEEK_SET);
375 				if (fread(rombuf, 1, rombytesize_2 - 0x200,
376 				          usefile_2) < rombytesize_2 - 0x200) {
377 					LOG_MSG("Failed to read sufficient ROM data");
378 					fclose(usefile_2);
379 					return;
380 				}
381 
382 				// fclose(usefile_2); //usefile_2 is in diskSwap
383 				// structure which should be deleted to close
384 				// the file
385 
386 				/* write cartridge data into ROM */
387 				for (i = 0; i < rombytesize_2 - 0x200; i++)
388 					phys_writeb(romseg_pt + i, rombuf[i]);
389 			}
390 
391 			fseek(usefile_1, 0x0L, SEEK_SET);
392 			if (fread(rombuf, 1, 0x200, usefile_1) < 0x200) {
393 				LOG_MSG("Failed to read sufficient cartridge data");
394 				fclose(usefile_1);
395 				return;
396 			}
397 
398 			Bit16u romseg = host_readw(&rombuf[0x1ce]);
399 
400 			/* read cartridge data into buffer */
401 			fseek(usefile_1, 0x200L, SEEK_SET);
402 			if (fread(rombuf, 1, rombytesize_1 - 0x200, usefile_1) <
403 			    rombytesize_1 - 0x200) {
404 				LOG_MSG("Failed to read sufficient cartridge data");
405 				fclose(usefile_1);
406 				return;
407 			}
408 			// fclose(usefile_1); //usefile_1 is in diskSwap
409 			// structure which should be deleted to close the file
410 
411 			/* write cartridge data into ROM */
412 			for (i = 0; i < rombytesize_1 - 0x200; i++)
413 				phys_writeb((romseg << 4) + i, rombuf[i]);
414 
415 			// Close cardridges
416 			for (auto &disk : diskSwap)
417 				disk.reset();
418 
419 			if (cart_cmd.empty()) {
420 				Bit32u old_int18 = mem_readd(0x60);
421 				/* run cartridge setup */
422 				SegSet16(ds, romseg);
423 				SegSet16(es, romseg);
424 				SegSet16(ss, 0x8000);
425 				reg_esp = 0xfffe;
426 				CALLBACK_RunRealFar(romseg, 0x0003);
427 
428 				Bit32u new_int18 = mem_readd(0x60);
429 				if (old_int18 != new_int18) {
430 					/* boot cartridge (int18) */
431 					SegSet16(cs, RealSeg(new_int18));
432 					reg_ip = RealOff(new_int18);
433 				}
434 			} else {
435 				if (cfound_at > 0) {
436 					/* run cartridge setup */
437 					SegSet16(ds, dos.psp());
438 					SegSet16(es, dos.psp());
439 					CALLBACK_RunRealFar(romseg, cfound_at);
440 				}
441 			}
442 		}
443 	} else {
444 		disable_umb_ems_xms();
445 		MEM_RemoveEMSPageFrame();
446 		WriteOut(MSG_Get("PROGRAM_BOOT_BOOT"), drive);
447 		for (i = 0; i < 512; i++)
448 			real_writeb(0, 0x7c00 + i, bootarea.rawdata[i]);
449 
450 		/* create appearance of floppy drive DMA usage (Demon's Forge) */
451 		if (!IS_TANDY_ARCH && floppysize != 0)
452 			GetDMAChannel(2)->tcount = true;
453 
454 		/* revector some dos-allocated interrupts */
455 		real_writed(0, 0x01 * 4, 0xf000ff53);
456 		real_writed(0, 0x03 * 4, 0xf000ff53);
457 
458 		SegSet16(cs, 0);
459 		reg_ip = 0x7c00;
460 		SegSet16(ds, 0);
461 		SegSet16(es, 0);
462 		/* set up stack at a safe place */
463 		SegSet16(ss, 0x7000);
464 		reg_esp = 0x100;
465 		reg_esi = 0;
466 		reg_ecx = 1;
467 		reg_ebp = 0;
468 		reg_eax = 0;
469 		reg_edx = 0;      // Head 0 drive 0
470 		reg_ebx = 0x7c00; // Real code probably uses bx to load the image
471 	}
472 }
473 
BOOT_ProgramStart(Program ** make)474 void BOOT_ProgramStart(Program **make)
475 {
476 	*make = new BOOT;
477 }
478