2 pcboot - bootable PC demo/game kernel
3 Copyright (C) 2018-2019 John Tsiombikas <nuclear@member.fsf.org>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY, without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
27 #define FLOPPY_MOTOR_OFF_TIMEOUT 4000
28 #define DBG_RESET_ON_FAIL
31 unsigned int cyl, head, tsect;
35 unsigned char pktsize;
37 uint16_t num_sectors; /* max 127 on some implementations */
39 uint32_t lba_low, lba_high;
40 } __attribute__((packed));
42 enum {OP_READ, OP_WRITE};
44 #ifdef DBG_RESET_ON_FAIL
45 static int bios_reset_dev(int dev);
47 static int bios_rw_sect_lba(int dev, uint64_t lba, int nsect, int op, void *buf);
48 static int bios_rw_sect_chs(int dev, struct chs *chs, int nsect, int op, void *buf);
49 static int get_drive_chs(int dev, struct chs *chs);
50 static void calc_chs(uint64_t lba, struct chs *chs);
52 static int have_bios_ext;
53 static int bdev_is_floppy;
54 static int num_cyl, num_heads, num_track_sect;
59 struct int86regs regs;
61 memset(®s, 0, sizeof regs);
62 regs.eax = 0x4100; /* function 41h: check int 13h extensions */
64 regs.edx = boot_drive_number;
67 if((regs.flags & FLAGS_CARRY) || (regs.ecx & 1) == 0) {
68 printf("BIOS does not support int13h extensions (LBA access)\n");
71 if(get_drive_chs(boot_drive_number, &chs) == -1) {
72 panic("drive (%d) parameter query failed\n", boot_drive_number);
77 num_track_sect = chs.tsect;
79 printf("Drive params: %d cyl, %d heads, %d sect/track\n", num_cyl,
80 num_heads, num_track_sect);
82 printf("BIOS supports int13h extensions (LBA access)\n");
86 bdev_is_floppy = !(boot_drive_number & 0x80);
91 int bdev_read_sect(uint64_t lba, void *buf)
97 cancel_alarm(floppy_motors_off);
98 set_alarm(FLOPPY_MOTOR_OFF_TIMEOUT, floppy_motors_off);
102 return bios_rw_sect_lba(boot_drive_number, lba, 1, OP_READ, buf);
107 for(i=0; i<NRETRIES; i++) {
108 if(bios_rw_sect_chs(boot_drive_number, &chs, 1, OP_READ, buf) != -1) {
111 #ifdef DBG_RESET_ON_FAIL
112 bios_reset_dev(boot_drive_number);
118 int bdev_write_sect(uint64_t lba, void *buf)
123 cancel_alarm(floppy_motors_off);
124 set_alarm(FLOPPY_MOTOR_OFF_TIMEOUT, floppy_motors_off);
128 return bios_rw_sect_lba(boot_drive_number, lba, 1, OP_WRITE, buf);
132 return bios_rw_sect_chs(boot_drive_number, &chs, 1, OP_WRITE, buf);
135 int bdev_read_range(uint64_t lba, int nsect, void *buf)
141 cancel_alarm(floppy_motors_off);
142 set_alarm(FLOPPY_MOTOR_OFF_TIMEOUT, floppy_motors_off);
146 return bios_rw_sect_lba(boot_drive_number, lba, nsect, OP_READ, buf);
151 for(i=0; i<NRETRIES; i++) {
153 if((rd = bios_rw_sect_chs(boot_drive_number, &chs, nsect, OP_READ, buf)) == nsect) {
159 buf = (char*)buf + rd * 512;
164 #ifdef DBG_RESET_ON_FAIL
165 bios_reset_dev(boot_drive_number);
171 int bdev_write_range(uint64_t lba, int nsect, void *buf)
176 cancel_alarm(floppy_motors_off);
177 set_alarm(FLOPPY_MOTOR_OFF_TIMEOUT, floppy_motors_off);
181 return bios_rw_sect_lba(boot_drive_number, lba, nsect, OP_WRITE, buf);
185 return bios_rw_sect_chs(boot_drive_number, &chs, nsect, OP_WRITE, buf);
189 #ifdef DBG_RESET_ON_FAIL
190 static int bios_reset_dev(int dev)
192 struct int86regs regs;
194 printf("resetting drive %d ...", dev);
196 memset(®s, 0, sizeof regs);
201 printf("%s\n", (regs.flags & FLAGS_CARRY) ? "failed" : "done");
203 return (regs.flags & FLAGS_CARRY) ? -1 : 0;
207 static int bios_rw_sect_lba(int dev, uint64_t lba, int nsect, int op, void *buf)
209 struct int86regs regs;
210 struct disk_access *dap = (struct disk_access*)low_mem_buffer;
211 uint32_t addr = (uint32_t)low_mem_buffer;
212 uint32_t xaddr = (addr + sizeof *dap + 15) & 0xfffffff0;
213 void *xbuf = (void*)xaddr;
217 func = 0x42; /* function 42h: extended read sector (LBA) */
219 func = 0x43; /* function 43h: extended write sector (LBA) */
220 memcpy(xbuf, buf, nsect * 512);
223 dap->pktsize = sizeof *dap;
225 dap->num_sectors = nsect;
227 dap->bseg = xaddr >> 4;
228 dap->lba_low = (uint32_t)lba;
229 dap->lba_high = (uint32_t)(lba >> 32);
231 memset(®s, 0, sizeof regs);
232 regs.eax = func << 8;
239 if(regs.flags & FLAGS_CARRY) {
244 memcpy(buf, xbuf, nsect * 512);
246 return dap->num_sectors;
249 /* TODO: fix: probably can't cross track boundaries, clamp nsect accordingly
250 * and return a short count
252 static int bios_rw_sect_chs(int dev, struct chs *chs, int nsect, int op, void *buf)
254 struct int86regs regs;
255 uint32_t xaddr = (uint32_t)low_mem_buffer;
262 memcpy(low_mem_buffer, buf, nsect * 512);
265 memset(®s, 0, sizeof regs);
266 regs.eax = (func << 8) | nsect; /* 1 sector */
267 regs.es = xaddr >> 4; /* es:bx buffer */
268 regs.ecx = ((chs->cyl << 8) & 0xff00) | ((chs->cyl >> 10) & 0xc0) | chs->tsect;
269 regs.edx = dev | (chs->head << 8);
273 if(regs.flags & FLAGS_CARRY) {
278 memcpy(buf, low_mem_buffer, nsect * 512);
283 static int get_drive_chs(int dev, struct chs *chs)
285 struct int86regs regs;
287 memset(®s, 0, sizeof regs);
293 if(regs.flags & FLAGS_CARRY) {
297 chs->cyl = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x300)) + 1;
298 chs->head = (regs.edx >> 8) + 1;
299 chs->tsect = regs.ecx & 0x3f;
303 static void calc_chs(uint64_t lba, struct chs *chs)
308 /* XXX: 64bit ops require libgcc, and I don't want to link that for
309 * this. CHS I/O is only going to be for floppies and really old systems
310 * anyway, so there's no point in supporting anything more than LBA32 in
313 const char *fmt = "calc_chs only supports 32bit LBA. requested: %llx\n"
314 "If you see this message, please file a bug report at:\n"
315 " https://github.com/jtsiomb/256boss/issues\n"
316 " or by email: nuclear@member.fsf.org\n";
320 lba32 = (uint32_t)lba;
321 trk = lba32 / num_track_sect;
322 chs->tsect = (lba32 % num_track_sect) + 1;
323 chs->cyl = trk / num_heads;
324 chs->head = trk % num_heads;