From bdef8f9596269234f88158fcefcee7cc5b7766fd Mon Sep 17 00:00:00 2001 From: turbocat Date: Tue, 14 Sep 2021 17:39:32 +0000 Subject: [PATCH] Created a branch for low-level work with disks git-svn-id: svn://kolibrios.org@9191 a494cfbc-eb01-0410-851d-a64ba20cac60 --- kernel/branches/kolibri-lldw/COPYING.TXT | 347 + kernel/branches/kolibri-lldw/Makefile | 47 + kernel/branches/kolibri-lldw/Tupfile.lua | 7 + kernel/branches/kolibri-lldw/acpi/acpi.inc | 304 + kernel/branches/kolibri-lldw/asmxygen.py | 2066 ++++++ kernel/branches/kolibri-lldw/blkdev/ahci.inc | 1403 ++++ .../branches/kolibri-lldw/blkdev/bd_drv.inc | 301 + .../branches/kolibri-lldw/blkdev/cd_drv.inc | 1218 ++++ kernel/branches/kolibri-lldw/blkdev/disk.inc | 1666 +++++ .../kolibri-lldw/blkdev/disk_cache.inc | 1387 ++++ kernel/branches/kolibri-lldw/blkdev/fdc.inc | 68 + .../branches/kolibri-lldw/blkdev/flp_drv.inc | 960 +++ .../branches/kolibri-lldw/blkdev/hd_drv.inc | 568 ++ .../kolibri-lldw/blkdev/ide_cache.inc | 202 + kernel/branches/kolibri-lldw/blkdev/rd.inc | 192 + .../branches/kolibri-lldw/blkdev/rdsave.inc | 33 + kernel/branches/kolibri-lldw/boot/ETFONT.FNT | Bin 0 -> 4096 bytes .../branches/kolibri-lldw/boot/bootcode.inc | 1400 ++++ kernel/branches/kolibri-lldw/boot/booten.inc | 107 + kernel/branches/kolibri-lldw/boot/bootet.inc | 106 + kernel/branches/kolibri-lldw/boot/bootge.inc | 107 + kernel/branches/kolibri-lldw/boot/bootru.inc | 106 + kernel/branches/kolibri-lldw/boot/bootsp.inc | 108 + kernel/branches/kolibri-lldw/boot/bootstr.inc | 56 + .../branches/kolibri-lldw/boot/bootvesa.inc | 801 +++ kernel/branches/kolibri-lldw/boot/et.inc | 16 + kernel/branches/kolibri-lldw/boot/parsers.inc | 172 + kernel/branches/kolibri-lldw/boot/preboot.inc | 46 + kernel/branches/kolibri-lldw/boot/rdload.inc | 128 + kernel/branches/kolibri-lldw/boot/ru.inc | 102 + .../branches/kolibri-lldw/boot/shutdown.inc | 408 ++ kernel/branches/kolibri-lldw/bootbios.asm | 124 + .../kolibri-lldw/bootloader/Tupfile.lua | 3 + .../kolibri-lldw/bootloader/boot_fat12.asm | 304 + .../after_win/Tupfile.lua | 2 + .../after_win/build.bat | 2 + .../extended_primary_loader/after_win/fat.inc | 509 ++ .../after_win/kordldr.win.asm | 924 +++ .../after_win/kordldr.win.txt | 391 ++ .../after_win/ntfs.inc | 587 ++ .../extended_primary_loader/cdfs/Tupfile.lua | 2 + .../extended_primary_loader/cdfs/bootsect.asm | 1024 +++ .../extended_primary_loader/cdfs/bootsect.txt | 418 ++ .../extended_primary_loader/cdfs/build.bat | 2 + .../extended_primary_loader/config.ini | 15 + .../extended_primary_loader/fat1x/Tupfile.lua | 3 + .../fat1x/bootsect.asm | 392 ++ .../fat1x/bootsect.txt | 360 + .../extended_primary_loader/fat1x/build.bat | 3 + .../fat1x/kordldr.f1x.asm | 689 ++ .../extended_primary_loader/fat32/Tupfile.lua | 3 + .../fat32/bootsect.asm | 358 + .../fat32/bootsect.txt | 333 + .../extended_primary_loader/fat32/build.bat | 3 + .../fat32/kordldr.f32.asm | 673 ++ .../kolibri-lldw/bootloader/floppy1440.inc | 26 + .../kolibri-lldw/bootloader/floppy1680.inc | 26 + .../kolibri-lldw/bootloader/floppy1743.inc | 26 + .../kolibri-lldw/bootloader/floppy2880.inc | 26 + .../kolibri-lldw/bootloader/grub4kos.asm | 296 + .../branches/kolibri-lldw/bootloader/readme | 50 + .../bootloader/uefi4kos/Tupfile.lua | 3 + .../bootloader/uefi4kos/kolibri.ini | 21 + .../kolibri-lldw/bootloader/uefi4kos/uefi.inc | 272 + .../bootloader/uefi4kos/uefi32.inc | 58 + .../bootloader/uefi4kos/uefi32kos.asm | 990 +++ .../bootloader/uefi4kos/uefi64.inc | 196 + .../bootloader/uefi4kos/uefi64kos.asm | 1249 ++++ kernel/branches/kolibri-lldw/build.bat | 39 + kernel/branches/kolibri-lldw/build.sh | 22 + kernel/branches/kolibri-lldw/bus/pci/PCIe.inc | 119 + .../branches/kolibri-lldw/bus/pci/pci16.inc | 51 + .../branches/kolibri-lldw/bus/pci/pci32.inc | 679 ++ .../branches/kolibri-lldw/bus/usb/common.inc | 462 ++ .../kolibri-lldw/bus/usb/hccommon.inc | 348 + kernel/branches/kolibri-lldw/bus/usb/hub.inc | 1285 ++++ kernel/branches/kolibri-lldw/bus/usb/init.inc | 268 + .../branches/kolibri-lldw/bus/usb/memory.inc | 43 + kernel/branches/kolibri-lldw/bus/usb/pipe.inc | 860 +++ .../kolibri-lldw/bus/usb/protocol.inc | 1019 +++ kernel/branches/kolibri-lldw/const.inc | 989 +++ kernel/branches/kolibri-lldw/core/Tupfile.lua | 3 + kernel/branches/kolibri-lldw/core/apic.inc | 567 ++ .../branches/kolibri-lldw/core/clipboard.inc | 167 + .../kolibri-lldw/core/conf_lib-sp.inc | 21 + .../branches/kolibri-lldw/core/conf_lib.inc | 254 + kernel/branches/kolibri-lldw/core/debug.inc | 455 ++ kernel/branches/kolibri-lldw/core/dll.inc | 1518 +++++ kernel/branches/kolibri-lldw/core/export.inc | 40 + kernel/branches/kolibri-lldw/core/exports.inc | 146 + kernel/branches/kolibri-lldw/core/ext_lib.inc | 476 ++ kernel/branches/kolibri-lldw/core/fpu.inc | 419 ++ kernel/branches/kolibri-lldw/core/heap.inc | 1589 +++++ kernel/branches/kolibri-lldw/core/hpet.inc | 74 + kernel/branches/kolibri-lldw/core/irq.inc | 294 + kernel/branches/kolibri-lldw/core/malloc.inc | 1035 +++ kernel/branches/kolibri-lldw/core/memory.inc | 1364 ++++ kernel/branches/kolibri-lldw/core/mtrr.inc | 931 +++ .../branches/kolibri-lldw/core/mtrrtest.asm | 213 + kernel/branches/kolibri-lldw/core/peload.inc | 295 + kernel/branches/kolibri-lldw/core/sched.inc | 434 ++ kernel/branches/kolibri-lldw/core/slab.inc | 125 + kernel/branches/kolibri-lldw/core/string.inc | 189 + kernel/branches/kolibri-lldw/core/sync.inc | 355 + .../branches/kolibri-lldw/core/sys32-sp.inc | 14 + kernel/branches/kolibri-lldw/core/sys32.inc | 840 +++ kernel/branches/kolibri-lldw/core/syscall.inc | 186 + kernel/branches/kolibri-lldw/core/taskman.inc | 1059 +++ .../kolibri-lldw/core/test_malloc.asm | 250 + kernel/branches/kolibri-lldw/core/timers.inc | 229 + kernel/branches/kolibri-lldw/core/v86.inc | 907 +++ kernel/branches/kolibri-lldw/crc.inc | 42 + kernel/branches/kolibri-lldw/data16.inc | 87 + kernel/branches/kolibri-lldw/data32.inc | 559 ++ kernel/branches/kolibri-lldw/data32et.inc | 48 + kernel/branches/kolibri-lldw/data32sp.inc | 54 + .../branches/kolibri-lldw/detect/biosdisk.inc | 122 + .../branches/kolibri-lldw/detect/biosmem.inc | 46 + .../branches/kolibri-lldw/detect/dev_fd.inc | 38 + .../branches/kolibri-lldw/detect/dev_hdcd.inc | 475 ++ kernel/branches/kolibri-lldw/detect/disks.inc | 15 + .../branches/kolibri-lldw/detect/getcache.inc | 209 + .../branches/kolibri-lldw/detect/init_ata.inc | 492 ++ .../branches/kolibri-lldw/detect/sear_par.inc | 280 + .../branches/kolibri-lldw/detect/vortex86.inc | 158 + kernel/branches/kolibri-lldw/docs/apm.txt | 518 ++ .../kolibri-lldw/docs/doxygen/doxygen.cfg | 2612 ++++++++ .../kolibri-lldw/docs/drivers_api.txt | 105 + .../kolibri-lldw/docs/events_subsystem.ru.txt | 232 + .../kolibri-lldw/docs/events_subsystem.txt | 248 + .../branches/kolibri-lldw/docs/loader_doc.txt | 95 + kernel/branches/kolibri-lldw/docs/stack.txt | 214 + .../branches/kolibri-lldw/docs/sysfuncr.txt | 5089 +++++++++++++++ .../branches/kolibri-lldw/docs/sysfuncs.txt | 5335 +++++++++++++++ kernel/branches/kolibri-lldw/docs/usbapi.txt | 211 + kernel/branches/kolibri-lldw/encoding.inc | 145 + kernel/branches/kolibri-lldw/fdo.inc | 456 ++ kernel/branches/kolibri-lldw/fs/ext.inc | 2708 ++++++++ kernel/branches/kolibri-lldw/fs/fat.inc | 3152 +++++++++ kernel/branches/kolibri-lldw/fs/fs_common.inc | 142 + kernel/branches/kolibri-lldw/fs/fs_lfn.inc | 802 +++ kernel/branches/kolibri-lldw/fs/iso9660.inc | 740 +++ kernel/branches/kolibri-lldw/fs/ntfs.inc | 4146 ++++++++++++ kernel/branches/kolibri-lldw/fs/parse_fn.inc | 340 + kernel/branches/kolibri-lldw/fs/xfs.asm | 2124 ++++++ kernel/branches/kolibri-lldw/fs/xfs.inc | 622 ++ kernel/branches/kolibri-lldw/gui/button.inc | 346 + kernel/branches/kolibri-lldw/gui/char.mt | Bin 0 -> 2304 bytes kernel/branches/kolibri-lldw/gui/charUni.mt | Bin 0 -> 22704 bytes kernel/branches/kolibri-lldw/gui/char_et.mt | Bin 0 -> 2304 bytes kernel/branches/kolibri-lldw/gui/char_sp.mt | Bin 0 -> 2304 bytes kernel/branches/kolibri-lldw/gui/event.inc | 622 ++ kernel/branches/kolibri-lldw/gui/font.inc | 878 +++ kernel/branches/kolibri-lldw/gui/mouse.inc | 687 ++ .../kolibri-lldw/gui/mousepointer.inc | 250 + kernel/branches/kolibri-lldw/gui/skincode.inc | 453 ++ kernel/branches/kolibri-lldw/gui/skindata.inc | 64 + kernel/branches/kolibri-lldw/gui/window.inc | 2496 +++++++ kernel/branches/kolibri-lldw/hid/keyboard.inc | 578 ++ kernel/branches/kolibri-lldw/hid/mousedrv.inc | 567 ++ kernel/branches/kolibri-lldw/hid/set_dtc.inc | 203 + kernel/branches/kolibri-lldw/imports.inc | 27 + kernel/branches/kolibri-lldw/init.inc | 531 ++ kernel/branches/kolibri-lldw/kernel.asm | 5791 +++++++++++++++++ kernel/branches/kolibri-lldw/kernel32.inc | 79 + kernel/branches/kolibri-lldw/kernelsp.inc | 14 + kernel/branches/kolibri-lldw/kglobals.inc | 69 + kernel/branches/kolibri-lldw/macros.inc | 143 + kernel/branches/kolibri-lldw/memmap.inc | 272 + kernel/branches/kolibri-lldw/network/ARP.inc | 676 ++ kernel/branches/kolibri-lldw/network/IPv4.inc | 1161 ++++ kernel/branches/kolibri-lldw/network/IPv6.inc | 290 + .../branches/kolibri-lldw/network/PPPoE.inc | 349 + .../kolibri-lldw/network/ethernet.inc | 346 + kernel/branches/kolibri-lldw/network/icmp.inc | 456 ++ .../kolibri-lldw/network/loopback.inc | 184 + .../branches/kolibri-lldw/network/queue.inc | 120 + .../branches/kolibri-lldw/network/socket.inc | 2534 ++++++++ .../branches/kolibri-lldw/network/stack.inc | 983 +++ kernel/branches/kolibri-lldw/network/tcp.inc | 293 + .../kolibri-lldw/network/tcp_input.inc | 1927 ++++++ .../kolibri-lldw/network/tcp_output.inc | 754 +++ .../kolibri-lldw/network/tcp_subr.inc | 619 ++ .../kolibri-lldw/network/tcp_timer.inc | 192 + .../kolibri-lldw/network/tcp_usreq.inc | 237 + kernel/branches/kolibri-lldw/network/udp.inc | 434 ++ kernel/branches/kolibri-lldw/posix/futex.inc | 245 + kernel/branches/kolibri-lldw/posix/pipe.inc | 339 + kernel/branches/kolibri-lldw/posix/posix.inc | 129 + kernel/branches/kolibri-lldw/proc32.inc | 316 + .../kolibri-lldw/readme-ext-loader.txt | 55 + .../sec_loader/trunk/boot/PrimaryLoader.txt | 91 + .../sec_loader/trunk/boot/after_win/build.bat | 2 + .../sec_loader/trunk/boot/after_win/fat.inc | 509 ++ .../trunk/boot/after_win/kordldr.win.asm | 924 +++ .../trunk/boot/after_win/kordldr.win.txt | 391 ++ .../sec_loader/trunk/boot/after_win/ntfs.inc | 587 ++ .../sec_loader/trunk/boot/build.bat | 20 + .../sec_loader/trunk/boot/cdfs/bootsect.asm | 1024 +++ .../sec_loader/trunk/boot/cdfs/bootsect.txt | 418 ++ .../sec_loader/trunk/boot/cdfs/build.bat | 2 + .../sec_loader/trunk/boot/fat1x/bootsect.asm | 392 ++ .../sec_loader/trunk/boot/fat1x/bootsect.txt | 360 + .../sec_loader/trunk/boot/fat1x/build.bat | 3 + .../trunk/boot/fat1x/kordldr.f1x.asm | 668 ++ .../sec_loader/trunk/boot/fat32/bootsect.asm | 358 + .../sec_loader/trunk/boot/fat32/bootsect.txt | 333 + .../sec_loader/trunk/boot/fat32/build.bat | 3 + .../trunk/boot/fat32/kordldr.f32.asm | 672 ++ .../sec_loader/trunk/boot/floppy.asc | 49 + .../sec_loader/trunk/boot/mkfloppy.inc | 91 + .../kolibri-lldw/sec_loader/trunk/boot_st.inc | 68 + .../sec_loader/trunk/build_ru.bat | 4 + .../sec_loader/trunk/debug_msg.inc | 77 + .../kolibri-lldw/sec_loader/trunk/listing.inc | 635 ++ .../kolibri-lldw/sec_loader/trunk/loader.asm | 317 + .../kolibri-lldw/sec_loader/trunk/loader.lst | 2147 ++++++ .../kolibri-lldw/sec_loader/trunk/parse.inc | 118 + .../sec_loader/trunk/parse_any.inc | 686 ++ .../sec_loader/trunk/parse_dat.inc | 56 + .../sec_loader/trunk/parse_def_sect.inc | 2121 ++++++ .../sec_loader/trunk/parse_err.inc | 66 + .../sec_loader/trunk/parse_loader.inc | 335 + .../kolibri-lldw/sec_loader/trunk/sl_equ.inc | 98 + .../kolibri-lldw/sec_loader/trunk/sl_proc.inc | 528 ++ .../kolibri-lldw/sec_loader/trunk/startos.ini | 98 + .../branches/kolibri-lldw/sound/playnote.inc | 166 + kernel/branches/kolibri-lldw/struct.inc | 246 + kernel/branches/kolibri-lldw/sys.conf | 18 + kernel/branches/kolibri-lldw/unicode.inc | 117 + kernel/branches/kolibri-lldw/unpacker.inc | 534 ++ kernel/branches/kolibri-lldw/video/arrow.cur | Bin 0 -> 766 bytes .../kolibri-lldw/video/arrow_clock.cur | Bin 0 -> 2238 bytes .../branches/kolibri-lldw/video/blitter.inc | 536 ++ .../branches/kolibri-lldw/video/cursors.inc | 1238 ++++ .../kolibri-lldw/video/framebuffer.inc | 250 + kernel/branches/kolibri-lldw/video/vesa12.inc | 1004 +++ kernel/branches/kolibri-lldw/video/vesa20.inc | 2518 +++++++ kernel/branches/kolibri-lldw/video/vga.inc | 534 ++ 239 files changed, 126210 insertions(+) create mode 100644 kernel/branches/kolibri-lldw/COPYING.TXT create mode 100644 kernel/branches/kolibri-lldw/Makefile create mode 100644 kernel/branches/kolibri-lldw/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/acpi/acpi.inc create mode 100644 kernel/branches/kolibri-lldw/asmxygen.py create mode 100644 kernel/branches/kolibri-lldw/blkdev/ahci.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/bd_drv.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/cd_drv.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/disk.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/disk_cache.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/fdc.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/flp_drv.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/hd_drv.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/ide_cache.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/rd.inc create mode 100644 kernel/branches/kolibri-lldw/blkdev/rdsave.inc create mode 100644 kernel/branches/kolibri-lldw/boot/ETFONT.FNT create mode 100644 kernel/branches/kolibri-lldw/boot/bootcode.inc create mode 100644 kernel/branches/kolibri-lldw/boot/booten.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootet.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootge.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootru.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootsp.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootstr.inc create mode 100644 kernel/branches/kolibri-lldw/boot/bootvesa.inc create mode 100644 kernel/branches/kolibri-lldw/boot/et.inc create mode 100644 kernel/branches/kolibri-lldw/boot/parsers.inc create mode 100644 kernel/branches/kolibri-lldw/boot/preboot.inc create mode 100644 kernel/branches/kolibri-lldw/boot/rdload.inc create mode 100644 kernel/branches/kolibri-lldw/boot/ru.inc create mode 100644 kernel/branches/kolibri-lldw/boot/shutdown.inc create mode 100644 kernel/branches/kolibri-lldw/bootbios.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/boot_fat12.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/build.bat create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/fat.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.txt create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/ntfs.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/build.bat create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/config.ini create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/build.bat create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/kordldr.f1x.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/build.bat create mode 100644 kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/kordldr.f32.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/floppy1440.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/floppy1680.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/floppy1743.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/floppy2880.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/grub4kos.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/readme create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/kolibri.ini create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32kos.asm create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64.inc create mode 100644 kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64kos.asm create mode 100644 kernel/branches/kolibri-lldw/build.bat create mode 100755 kernel/branches/kolibri-lldw/build.sh create mode 100644 kernel/branches/kolibri-lldw/bus/pci/PCIe.inc create mode 100644 kernel/branches/kolibri-lldw/bus/pci/pci16.inc create mode 100644 kernel/branches/kolibri-lldw/bus/pci/pci32.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/common.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/hccommon.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/hub.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/init.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/memory.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/pipe.inc create mode 100644 kernel/branches/kolibri-lldw/bus/usb/protocol.inc create mode 100644 kernel/branches/kolibri-lldw/const.inc create mode 100644 kernel/branches/kolibri-lldw/core/Tupfile.lua create mode 100644 kernel/branches/kolibri-lldw/core/apic.inc create mode 100644 kernel/branches/kolibri-lldw/core/clipboard.inc create mode 100644 kernel/branches/kolibri-lldw/core/conf_lib-sp.inc create mode 100644 kernel/branches/kolibri-lldw/core/conf_lib.inc create mode 100644 kernel/branches/kolibri-lldw/core/debug.inc create mode 100644 kernel/branches/kolibri-lldw/core/dll.inc create mode 100644 kernel/branches/kolibri-lldw/core/export.inc create mode 100644 kernel/branches/kolibri-lldw/core/exports.inc create mode 100644 kernel/branches/kolibri-lldw/core/ext_lib.inc create mode 100644 kernel/branches/kolibri-lldw/core/fpu.inc create mode 100644 kernel/branches/kolibri-lldw/core/heap.inc create mode 100644 kernel/branches/kolibri-lldw/core/hpet.inc create mode 100644 kernel/branches/kolibri-lldw/core/irq.inc create mode 100644 kernel/branches/kolibri-lldw/core/malloc.inc create mode 100644 kernel/branches/kolibri-lldw/core/memory.inc create mode 100644 kernel/branches/kolibri-lldw/core/mtrr.inc create mode 100644 kernel/branches/kolibri-lldw/core/mtrrtest.asm create mode 100644 kernel/branches/kolibri-lldw/core/peload.inc create mode 100644 kernel/branches/kolibri-lldw/core/sched.inc create mode 100644 kernel/branches/kolibri-lldw/core/slab.inc create mode 100644 kernel/branches/kolibri-lldw/core/string.inc create mode 100644 kernel/branches/kolibri-lldw/core/sync.inc create mode 100644 kernel/branches/kolibri-lldw/core/sys32-sp.inc create mode 100644 kernel/branches/kolibri-lldw/core/sys32.inc create mode 100644 kernel/branches/kolibri-lldw/core/syscall.inc create mode 100644 kernel/branches/kolibri-lldw/core/taskman.inc create mode 100644 kernel/branches/kolibri-lldw/core/test_malloc.asm create mode 100644 kernel/branches/kolibri-lldw/core/timers.inc create mode 100644 kernel/branches/kolibri-lldw/core/v86.inc create mode 100644 kernel/branches/kolibri-lldw/crc.inc create mode 100644 kernel/branches/kolibri-lldw/data16.inc create mode 100644 kernel/branches/kolibri-lldw/data32.inc create mode 100644 kernel/branches/kolibri-lldw/data32et.inc create mode 100644 kernel/branches/kolibri-lldw/data32sp.inc create mode 100644 kernel/branches/kolibri-lldw/detect/biosdisk.inc create mode 100644 kernel/branches/kolibri-lldw/detect/biosmem.inc create mode 100644 kernel/branches/kolibri-lldw/detect/dev_fd.inc create mode 100644 kernel/branches/kolibri-lldw/detect/dev_hdcd.inc create mode 100644 kernel/branches/kolibri-lldw/detect/disks.inc create mode 100644 kernel/branches/kolibri-lldw/detect/getcache.inc create mode 100644 kernel/branches/kolibri-lldw/detect/init_ata.inc create mode 100644 kernel/branches/kolibri-lldw/detect/sear_par.inc create mode 100644 kernel/branches/kolibri-lldw/detect/vortex86.inc create mode 100644 kernel/branches/kolibri-lldw/docs/apm.txt create mode 100644 kernel/branches/kolibri-lldw/docs/doxygen/doxygen.cfg create mode 100644 kernel/branches/kolibri-lldw/docs/drivers_api.txt create mode 100644 kernel/branches/kolibri-lldw/docs/events_subsystem.ru.txt create mode 100644 kernel/branches/kolibri-lldw/docs/events_subsystem.txt create mode 100644 kernel/branches/kolibri-lldw/docs/loader_doc.txt create mode 100644 kernel/branches/kolibri-lldw/docs/stack.txt create mode 100644 kernel/branches/kolibri-lldw/docs/sysfuncr.txt create mode 100644 kernel/branches/kolibri-lldw/docs/sysfuncs.txt create mode 100644 kernel/branches/kolibri-lldw/docs/usbapi.txt create mode 100644 kernel/branches/kolibri-lldw/encoding.inc create mode 100644 kernel/branches/kolibri-lldw/fdo.inc create mode 100644 kernel/branches/kolibri-lldw/fs/ext.inc create mode 100644 kernel/branches/kolibri-lldw/fs/fat.inc create mode 100644 kernel/branches/kolibri-lldw/fs/fs_common.inc create mode 100644 kernel/branches/kolibri-lldw/fs/fs_lfn.inc create mode 100644 kernel/branches/kolibri-lldw/fs/iso9660.inc create mode 100644 kernel/branches/kolibri-lldw/fs/ntfs.inc create mode 100644 kernel/branches/kolibri-lldw/fs/parse_fn.inc create mode 100644 kernel/branches/kolibri-lldw/fs/xfs.asm create mode 100644 kernel/branches/kolibri-lldw/fs/xfs.inc create mode 100644 kernel/branches/kolibri-lldw/gui/button.inc create mode 100644 kernel/branches/kolibri-lldw/gui/char.mt create mode 100644 kernel/branches/kolibri-lldw/gui/charUni.mt create mode 100644 kernel/branches/kolibri-lldw/gui/char_et.mt create mode 100644 kernel/branches/kolibri-lldw/gui/char_sp.mt create mode 100644 kernel/branches/kolibri-lldw/gui/event.inc create mode 100644 kernel/branches/kolibri-lldw/gui/font.inc create mode 100644 kernel/branches/kolibri-lldw/gui/mouse.inc create mode 100644 kernel/branches/kolibri-lldw/gui/mousepointer.inc create mode 100644 kernel/branches/kolibri-lldw/gui/skincode.inc create mode 100644 kernel/branches/kolibri-lldw/gui/skindata.inc create mode 100644 kernel/branches/kolibri-lldw/gui/window.inc create mode 100644 kernel/branches/kolibri-lldw/hid/keyboard.inc create mode 100644 kernel/branches/kolibri-lldw/hid/mousedrv.inc create mode 100644 kernel/branches/kolibri-lldw/hid/set_dtc.inc create mode 100644 kernel/branches/kolibri-lldw/imports.inc create mode 100644 kernel/branches/kolibri-lldw/init.inc create mode 100644 kernel/branches/kolibri-lldw/kernel.asm create mode 100644 kernel/branches/kolibri-lldw/kernel32.inc create mode 100644 kernel/branches/kolibri-lldw/kernelsp.inc create mode 100644 kernel/branches/kolibri-lldw/kglobals.inc create mode 100644 kernel/branches/kolibri-lldw/macros.inc create mode 100644 kernel/branches/kolibri-lldw/memmap.inc create mode 100644 kernel/branches/kolibri-lldw/network/ARP.inc create mode 100644 kernel/branches/kolibri-lldw/network/IPv4.inc create mode 100644 kernel/branches/kolibri-lldw/network/IPv6.inc create mode 100644 kernel/branches/kolibri-lldw/network/PPPoE.inc create mode 100644 kernel/branches/kolibri-lldw/network/ethernet.inc create mode 100644 kernel/branches/kolibri-lldw/network/icmp.inc create mode 100644 kernel/branches/kolibri-lldw/network/loopback.inc create mode 100644 kernel/branches/kolibri-lldw/network/queue.inc create mode 100644 kernel/branches/kolibri-lldw/network/socket.inc create mode 100644 kernel/branches/kolibri-lldw/network/stack.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp_input.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp_output.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp_subr.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp_timer.inc create mode 100644 kernel/branches/kolibri-lldw/network/tcp_usreq.inc create mode 100644 kernel/branches/kolibri-lldw/network/udp.inc create mode 100644 kernel/branches/kolibri-lldw/posix/futex.inc create mode 100644 kernel/branches/kolibri-lldw/posix/pipe.inc create mode 100644 kernel/branches/kolibri-lldw/posix/posix.inc create mode 100644 kernel/branches/kolibri-lldw/proc32.inc create mode 100644 kernel/branches/kolibri-lldw/readme-ext-loader.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/PrimaryLoader.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/after_win/build.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/after_win/fat.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/after_win/kordldr.win.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/after_win/kordldr.win.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/after_win/ntfs.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/build.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/cdfs/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/cdfs/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/cdfs/build.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat1x/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat1x/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat1x/build.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat1x/kordldr.f1x.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat32/bootsect.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat32/bootsect.txt create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat32/build.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/fat32/kordldr.f32.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/floppy.asc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot/mkfloppy.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/boot_st.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/build_ru.bat create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/debug_msg.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/listing.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/loader.asm create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/loader.lst create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse_any.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse_dat.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse_def_sect.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse_err.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/parse_loader.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/sl_equ.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/sl_proc.inc create mode 100644 kernel/branches/kolibri-lldw/sec_loader/trunk/startos.ini create mode 100644 kernel/branches/kolibri-lldw/sound/playnote.inc create mode 100644 kernel/branches/kolibri-lldw/struct.inc create mode 100644 kernel/branches/kolibri-lldw/sys.conf create mode 100644 kernel/branches/kolibri-lldw/unicode.inc create mode 100644 kernel/branches/kolibri-lldw/unpacker.inc create mode 100644 kernel/branches/kolibri-lldw/video/arrow.cur create mode 100644 kernel/branches/kolibri-lldw/video/arrow_clock.cur create mode 100644 kernel/branches/kolibri-lldw/video/blitter.inc create mode 100644 kernel/branches/kolibri-lldw/video/cursors.inc create mode 100644 kernel/branches/kolibri-lldw/video/framebuffer.inc create mode 100644 kernel/branches/kolibri-lldw/video/vesa12.inc create mode 100644 kernel/branches/kolibri-lldw/video/vesa20.inc create mode 100644 kernel/branches/kolibri-lldw/video/vga.inc diff --git a/kernel/branches/kolibri-lldw/COPYING.TXT b/kernel/branches/kolibri-lldw/COPYING.TXT new file mode 100644 index 000000000..f6213b69c --- /dev/null +++ b/kernel/branches/kolibri-lldw/COPYING.TXT @@ -0,0 +1,347 @@ + + GNU GENERAL PUBLIC LICENSE + + Version 2, June 1991 + + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, USA + + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + + GNU GENERAL PUBLIC LICENSE + + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/kernel/branches/kolibri-lldw/Makefile b/kernel/branches/kolibri-lldw/Makefile new file mode 100644 index 000000000..8f924bca1 --- /dev/null +++ b/kernel/branches/kolibri-lldw/Makefile @@ -0,0 +1,47 @@ +FASM=fasm +FLAGS=-m 65536 +languages=en|ru|ge|et|sp + +.PHONY: all kernel bootloader clean + +all: kernel bootloader bootbios + +kernel: check_lang bootbios + @echo "*** building kernel with language '$(lang)' ..." + @mkdir -p bin + @echo "lang fix $(lang)" > lang.inc + @echo "--- building 'bin/kernel.mnt' ..." + @$(FASM) $(FLAGS) kernel.asm bin/kernel.mnt + @$(FASM) $(FLAGS) -dUEFI=1 kernel.asm bin/kernel.bin + @rm -f lang.inc + +bootbios: check_lang + @echo "*** building bootbios.bin with language '$(lang)' ..." + @mkdir -p bin + @echo "lang fix $(lang)" > lang.inc + @echo "--- building 'bootbios.bin' ..." + @$(FASM) $(FLAGS) bootbios.asm bootbios.bin + @rm -f lang.inc + +bootloader: check_lang + @echo "*** building bootloader with language '$(lang)' ..." + @mkdir -p bin + @echo "lang fix $(lang)" > lang.inc + @echo "--- building 'bin/boot_fat12.bin' ..." + @$(FASM) $(FLAGS) bootloader/boot_fat12.asm bin/boot_fat12.bin + @rm -f lang.inc + + +check_lang: + @case "$(lang)" in \ + $(languages)) \ + ;; \ + *) \ + echo "*** error: language is incorrect or not specified"; \ + exit 1; \ + ;; \ + esac + +clean: + rm -rf bin + rm -f lang.inc diff --git a/kernel/branches/kolibri-lldw/Tupfile.lua b/kernel/branches/kolibri-lldw/Tupfile.lua new file mode 100644 index 000000000..cbed9e8c8 --- /dev/null +++ b/kernel/branches/kolibri-lldw/Tupfile.lua @@ -0,0 +1,7 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("echo lang fix " .. ((tup.getconfig("LANG") == "") and "en" or tup.getconfig("LANG")) .. " > %o", {"lang.inc"}) +tup.rule({"bootbios.asm", extra_inputs = {"lang.inc"}}, "fasm %f %o ", "bootbios.bin") +tup.rule({"bootbios.asm", extra_inputs = {"lang.inc"}}, "fasm %f %o -dextended_primary_loader=1", "bootbios.bin.ext_loader") +tup.rule({"kernel.asm", extra_inputs = {"bootbios.bin", "lang.inc"}}, "fasm -m 65536 %f %o " .. tup.getconfig("KERPACK_CMD"), "kernel.mnt") +tup.rule({"kernel.asm", extra_inputs = {"bootbios.bin.ext_loader", "lang.inc"}}, "fasm -m 65536 %f %o -dextended_primary_loader=1" .. tup.getconfig("KERPACK_CMD"), "kernel.mnt.ext_loader") +tup.rule({"kernel.asm", extra_inputs = {"lang.inc"}}, "fasm -m 65536 %f %o -dUEFI=1 -dextended_primary_loader=1", "kolibri.krn") diff --git a/kernel/branches/kolibri-lldw/acpi/acpi.inc b/kernel/branches/kolibri-lldw/acpi/acpi.inc new file mode 100644 index 000000000..6e901d81b --- /dev/null +++ b/kernel/branches/kolibri-lldw/acpi/acpi.inc @@ -0,0 +1,304 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; ACPI Generic Address Structure +struct GAS + ASID db ? ; address space id + BitWidth db ? + BitOffset db ? + AccessSize db ? + Address DQ ? +ends + +ASID.SYSTEM_MEMORY = 0 +ASID.SYSTEM_IO = 1 +ASID.PCI_CONFIG = 2 +ASID.PCI_EC = 3 +ASID.PCI_SMBUS = 4 + +ACCESS_SIZE.UNDEFINED = 0 +ACCESS_SIZE.BYTE = 1 +ACCESS_SIZE.WORD = 2 +ACCESS_SIZE.DWORD = 3 +ACCESS_SIZE.QWORD = 4 + + +struct ACPI_RSDP + Signature DQ ? + Checksum db ? + OEMID rb 6 + Revision db ? + RsdtAddress dd ? + ; for Revision >= 2 + Length dd ? + XsdtAddress DQ ? + ExtChecksum db ? + Reserved rb 3 +ends + +struct ACPI_TABLE ; DESCRIPTION_HEADER + Signature dd ? + Length dd ? + Revision db ? + Checksum db ? + OEMID rb 6 + OEMTableID rb 8 + OEMRevision rb 4 + CreatorID rb 4 + CreatorRevision rb 4 +ends + +struct ACPI_RSDT ACPI_TABLE + Entry rd (0x1000-sizeof.ACPI_TABLE)/4 +ends + +struct ACPI_HPET ACPI_TABLE + ID dd ? + Base GAS + SeqNumber db ? + MainCounterMinimum dw ? + PageProtectionOEM db ? +ends + +struct ACPI_MADT ACPI_TABLE + Local_IC_Addr dd ? + Flags dd ? + IntController rb 0x1000-sizeof.ACPI_TABLE-ACPI_MADT.IntController +ends + +struct ACPI_FADT ACPI_TABLE + FirmwareCtrl dd ? + DSDT dd ? + db ? + PreferredPMProfile db ? + SCI_INT dw ? + SMI_CMD dd ? + ACPI_ENABLE db ? + ACPI_DISABLE db ? + S4BIOS_REQ db ? + PSTATE_CNT db ? + PM1a_EVT_BLK dd ? + PM1b_EVT_BLK dd ? + PM1a_CNT_BLK dd ? + PM1b_CNT_BLK dd ? + PM2_CNT_BLK dd ? + PM_TMR_BLK dd ? + GPE0_BLK dd ? + GPE1_BLK dd ? + PM1_EVT_LEN db ? + PM1_CNT_LEN db ? + PM2_CNT_LEN db ? + PM_TMR_LEN db ? + GPE0_BLK_LEN db ? + GPE1_BLK_LEN db ? + GPE1_BASE db ? + CST_CNT db ? + P_LVL2_LAT dw ? + P_LVL3_LAT dw ? + FLUSH_SIZE dw ? + FLUSH_STRIDE dw ? + DUTY_OFFSET db ? + DUTY_WIDTH db ? + DAY_ALRM db ? + MON_ALRM db ? + CENTURY db ? + IAPC_BOOT_ARCH dw ? + db ? + Flags dd ? + RESET_REG GAS + RESET_VALUE db ? + ARM_BOOT_ARCH dw ? + FADT_Minor_Version db ? + X_FIRMWARE_CTRL DQ ? + X_DSDT DQ ? + X_PM1a_EVT_BLK GAS + X_PM1b_EVT_BLK GAS + X_PM1a_CNT_BLK GAS + X_PM1b_CNT_BLK GAS + X_PM2_CNT_BLK GAS + X_PM_TMR_BLK GAS + X_GPE0_BLK GAS + X_GPE1_BLK GAS + SLEEP_CONTROL_REG GAS + SLEEP_STATUS_REG GAS + HypervisorVendorID rb 8 +ends + +MAX_SSDTS = 32 + +iglobal +align 4 +acpi_lapic_base dd 0xfee00000 ; default local apic base +endg + +uglobal +align 4 +acpi_dev_data rd 1 +acpi_dev_size rd 1 + +acpi_rsdp_base rd 1 +acpi_rsdt_base rd 1 +acpi_rsdt_size rd 1 +acpi_fadt_base rd 1 +acpi_fadt_size rd 1 +acpi_ssdt_base rd MAX_SSDTS +acpi_ssdt_size rd MAX_SSDTS +acpi_ssdt_cnt rd 1 +acpi_madt_base rd 1 +acpi_madt_size rd 1 +acpi_ioapic_base rd MAX_IOAPICS +acpi_hpet_base rd 1 +acpi_hpet_size rd 1 +cpu_count rd 1 +smpt rd 16 +endg + +align 4 +; @returns ACPI Root System Description Pointer +acpi_get_root_ptr: + mov eax, [acpi_rsdp_base] + ret + +align 4 +rsdt_find: ;ecx= rsdt edx= SIG + push ebx + push esi + + lea ebx, [ecx+ACPI_RSDT.Entry] + mov esi, [ecx+ACPI_RSDT.Length] + add esi, ecx +align 4 +.next: + mov eax, [ebx] + cmp [eax], edx + je .done + + add ebx, 4 + cmp ebx, esi + jb .next + + xor eax, eax + pop esi + pop ebx + ret + +.done: + mov eax, [ebx] + pop esi + pop ebx + ret + +align 4 +check_acpi: + cmp [acpi_rsdp_base], 0 + jz .done + stdcall map_io_mem, [acpi_rsdp_base], sizeof.ACPI_RSDP, \ + PG_GLOBAL+PAT_WB+PG_READ + mov [acpi_rsdp_base], eax +.rsdp_done: + cmp [acpi_rsdt_base], 0 + jz .rsdt_done + stdcall map_io_mem, [acpi_rsdt_base], [acpi_rsdt_size], \ + PG_GLOBAL+PAT_WB+PG_READ + mov [acpi_rsdt_base], eax +.rsdt_done: + cmp [acpi_fadt_base], 0 + jz .fadt_done + stdcall map_io_mem, [acpi_fadt_base], [acpi_fadt_size], \ + PG_GLOBAL+PAT_WB+PG_READ + mov [acpi_fadt_base], eax +.fadt_done: + cmp [acpi_hpet_base], 0 + jz .hpet_done + stdcall map_io_mem, [acpi_hpet_base], [acpi_hpet_size], \ + PG_GLOBAL+PAT_WB+PG_READ + mov [acpi_hpet_base], eax + mov eax, [eax+ACPI_HPET.Base.Address.lo] + mov [hpet_base], eax +.hpet_done: + cmp [acpi_madt_base], 0 + jz .madt_done + stdcall map_io_mem, [acpi_madt_base], [acpi_madt_size], \ + PG_GLOBAL+PAT_WB+PG_READ + mov [acpi_madt_base], eax + + mov ecx, [eax+ACPI_MADT.Local_IC_Addr] + mov [acpi_lapic_base], ecx + push eax + stdcall map_io_mem, ecx, 0x1000, PG_GLOBAL+PG_NOCACHE+PG_SWR + mov [LAPIC_BASE], eax + mov ecx, eax + pop eax + + mov edi, smpt + mov ebx, [ecx+APIC_ID] + shr ebx, 24 ; read APIC ID + + mov [edi], ebx ; bootstrap always first + inc [cpu_count] + add edi, 4 + + mov [ioapic_cnt], 0 + lea edx, [eax+ACPI_MADT.IntController] + mov ecx, [eax+ACPI_MADT.Length] + add ecx, eax +.check: + mov eax, [edx] + cmp al, 0 + je .lapic + cmp al, 1 + je .io_apic + jmp .next +.lapic: + shr eax, 24 ; get APIC ID + cmp eax, ebx ; skip self + je .next + + test [edx+4], byte 1 ; is enabled ? + jz .next + + cmp [cpu_count], 16 + jae .next + + stosd ; store APIC ID + inc [cpu_count] + jmp .next + +.io_apic: + mov eax, [ioapic_cnt] + push dword[edx+4] + pop [acpi_ioapic_base+eax*4] + push dword[edx+8] + pop [ioapic_gsi_base+eax*4] + inc [ioapic_cnt] + jmp .next + +.next: + mov eax, [edx] + movzx eax, ah + add edx, eax + cmp edx, ecx + jb .check +.madt_done: + + xor ecx, ecx +.next_ssdt: + cmp ecx, [acpi_ssdt_cnt] + jz .ssdt_done + push ecx + stdcall map_io_mem, [acpi_ssdt_base+ecx*4], [acpi_ssdt_size+ecx*4], \ + PG_GLOBAL+PAT_WB+PG_READ + pop ecx + mov [acpi_ssdt_base+ecx*4], eax + inc ecx + jmp .next_ssdt +.ssdt_done: + +.done: + ret diff --git a/kernel/branches/kolibri-lldw/asmxygen.py b/kernel/branches/kolibri-lldw/asmxygen.py new file mode 100644 index 000000000..3220aa282 --- /dev/null +++ b/kernel/branches/kolibri-lldw/asmxygen.py @@ -0,0 +1,2066 @@ +import re +import os +import argparse +import sys +import pickle + +# Parameters +# Path to doxygen folder to make doxygen files in: -o +doxygen_src_path = 'docs/doxygen' +# Remove generated doxygen files: --clean +clean_generated_stuff = False +# Dump all defined symbols: --dump +dump_symbols = False +# Print symbol stats: --stats +print_stats = False +# Do not write warnings file: --nowarn +enable_warnings = True + +# Constants +link_root = "http://websvn.kolibrios.org/filedetails.php?repname=Kolibri+OS&path=/kernel/trunk" + +# fasm keywords +keywords = [ + # Generic keywords + "align", + "equ", + "org", + "while", + "load", + "store", + "times", + "repeat", + "virtual", + "display", + "err", + "assert", + "if", + # Instructions + "aaa", + "aad", + "aam", + "aas", + "adc", + "adcx", + "add", + "addpd", + "addps", + "addsd", + "addss", + "addsubpd", + "addsubps", + "adox", + "aesdec", + "aesdeclast", + "aesenc", + "aesenclast", + "aesimc", + "aeskeygenassist", + "and", + "andn", + "andnpd", + "andnps", + "andpd", + "andps", + "arpl", + "bextr", + "blendpd", + "blendps", + "blendvpd", + "blendvps", + "blsi", + "blsmsk", + "blsr", + "bndcl", + "bndcn", + "bndcu", + "bndldx", + "bndmk", + "bndmov", + "bndstx", + "bound", + "bsf", + "bsr", + "bswap", + "bt", + "btc", + "btr", + "bts", + "bzhi", + "call", + "cbw", + "cdq", + "cdqe", + "clac", + "clc", + "cld", + "cldemote", + "clflush", + "clflushopt", + "cli", + "clts", + "clwb", + "cmc", + "cmova", + "cmovae", + "cmovb", + "cmovbe", + "cmovc", + "cmove", + "cmovg", + "cmovge", + "cmovl", + "cmovle", + "cmovna", + "cmovnae", + "cmovnb", + "cmovnbe", + "cmovnc", + "cmovne", + "cmovng", + "cmovnge", + "cmovnl", + "cmovnle", + "cmovno", + "cmovnp", + "cmovns", + "cmovnz", + "cmovo", + "cmovp", + "cmovpe", + "cmovpo", + "cmovs", + "cmovz", + "cmp", + "cmppd", + "cmpps", + "cmps", + "cmpsb", + "cmpsd", + "cmpsd", + "cmpsq", + "cmpss", + "cmpsw", + "cmpxchg", + "cmpxchg16b", + "cmpxchg8b", + "comisd", + "comiss", + "cpuid", + "cqo", + "crc32", + "cvtdq2pd", + "cvtdq2ps", + "cvtpd2dq", + "cvtpd2pi", + "cvtpd2ps", + "cvtpi2pd", + "cvtpi2ps", + "cvtps2dq", + "cvtps2pd", + "cvtps2pi", + "cvtsd2si", + "cvtsd2ss", + "cvtsi2sd", + "cvtsi2ss", + "cvtss2sd", + "cvtss2si", + "cvttpd2dq", + "cvttpd2pi", + "cvttps2dq", + "cvttps2pi", + "cvttsd2si", + "cvttss2si", + "cwd", + "cwde", + "daa", + "das", + "dec", + "div", + "divpd", + "divps", + "divsd", + "divss", + "dppd", + "dpps", + "emms", + "enter", + "extractps", + "f2xm1", + "fabs", + "fadd", + "faddp", + "fbld", + "fbstp", + "fchs", + "fclex", + "fcmova", + "fcmovae", + "fcmovb", + "fcmovbe", + "fcmovc", + "fcmove", + "fcmovg", + "fcmovge", + "fcmovl", + "fcmovle", + "fcmovna", + "fcmovnae", + "fcmovnb", + "fcmovnbe", + "fcmovnc", + "fcmovne", + "fcmovng", + "fcmovnge", + "fcmovnl", + "fcmovnle", + "fcmovno", + "fcmovnp", + "fcmovns", + "fcmovnz", + "fcmovo", + "fcmovp", + "fcmovpe", + "fcmovpo", + "fcmovs", + "fcmovz", + "fcom", + "fcomi", + "fcomip", + "fcomp", + "fcompp", + "fcos", + "fdecstp", + "fdiv", + "fdivp", + "fdivr", + "fdivrp", + "ffree", + "fiadd", + "ficom", + "ficomp", + "fidiv", + "fidivr", + "fild", + "fimul", + "fincstp", + "finit", + "fist", + "fistp", + "fisttp", + "fisub", + "fisubr", + "fld", + "fld1", + "fldcw", + "fldenv", + "fldl2e", + "fldl2t", + "fldlg2", + "fldln2", + "fldpi", + "fldz", + "fmul", + "fmulp", + "fnclex", + "fninit", + "fnop", + "fnsave", + "fnstcw", + "fnstenv", + "fnstsw", + "fpatan", + "fprem", + "fprem1", + "fptan", + "frndint", + "frstor", + "fsave", + "fscale", + "fsin", + "fsincos", + "fsqrt", + "fst", + "fstcw", + "fstenv", + "fstp", + "fstsw", + "fsub", + "fsubp", + "fsubr", + "fsubrp", + "ftst", + "fucom", + "fucomi", + "fucomip", + "fucomp", + "fucompp", + "fwait", + "fxam", + "fxch", + "fxrstor", + "fxsave", + "fxtract", + "fyl2x", + "fyl2xp1", + "gf2p8affineinvqb", + "gf2p8affineqb", + "gf2p8mulb", + "haddpd", + "haddps", + "hlt", + "hsubpd", + "hsubps", + "idiv", + "imul", + "in", + "inc", + "ins", + "insb", + "insd", + "insertps", + "insw", + "int", + "int1", + "int3", + "into", + "invd", + "invlpg", + "invpcid", + "iret", + "iretd", + "jmp", + "ja", + "jae", + "jb", + "jbe", + "jc", + "jcxz", + "jecxz", + "je", + "jg", + "jge", + "jl", + "jle", + "jna", + "jnae", + "jnb", + "jnbe", + "jnc", + "jne", + "jng", + "jnge", + "jnl", + "jnle", + "jno", + "jnp", + "jns", + "jnz", + "jo", + "jp", + "jpe", + "jpo", + "js", + "jz", + "kaddb", + "kaddd", + "kaddq", + "kaddw", + "kandb", + "kandd", + "kandnb", + "kandnd", + "kandnq", + "kandnw", + "kandq", + "kandw", + "kmovb", + "kmovd", + "kmovq", + "kmovw", + "knotb", + "knotd", + "knotq", + "knotw", + "korb", + "kord", + "korq", + "kortestb", + "kortestd", + "kortestq", + "kortestw", + "korw", + "kshiftlb", + "kshiftld", + "kshiftlq", + "kshiftlw", + "kshiftrb", + "kshiftrd", + "kshiftrq", + "kshiftrw", + "ktestb", + "ktestd", + "ktestq", + "ktestw", + "kunpckbw", + "kunpckdq", + "kunpckwd", + "kxnorb", + "kxnord", + "kxnorq", + "kxnorw", + "kxorb", + "kxord", + "kxorq", + "kxorw", + "lahf", + "lar", + "lddqu", + "ldmxcsr", + "lds", + "lea", + "leave", + "les", + "lfence", + "lfs", + "lgdt", + "lgs", + "lidt", + "lldt", + "lmsw", + "lock", + "lods", + "lodsb", + "lodsd", + "lodsq", + "lodsw", + "loop", + "loopa", + "loopae", + "loopb", + "loopbe", + "loopc", + "loope", + "loopg", + "loopge", + "loopl", + "loople", + "loopna", + "loopnae", + "loopnb", + "loopnbe", + "loopnc", + "loopne", + "loopng", + "loopnge", + "loopnl", + "loopnle", + "loopno", + "loopnp", + "loopns", + "loopnz", + "loopo", + "loopp", + "looppe", + "looppo", + "loops", + "loopz", + "lsl", + "lss", + "ltr", + "lzcnt", + "maskmovdqu", + "maskmovq", + "maxpd", + "maxps", + "maxsd", + "maxss", + "mfence", + "minpd", + "minps", + "minsd", + "minss", + "monitor", + "mov", + "movapd", + "movaps", + "movbe", + "movd", + "movddup", + "movdir64b", + "movdiri", + "movdq2q", + "movdqa", + "movdqu", + "movhlps", + "movhpd", + "movhps", + "movlhps", + "movlpd", + "movlps", + "movmskpd", + "movmskps", + "movntdq", + "movntdqa", + "movnti", + "movntpd", + "movntps", + "movntq", + "movq", + "movq", + "movq2dq", + "movs", + "movsb", + "movsd", + "movsd", + "movshdup", + "movsldup", + "movsq", + "movss", + "movsw", + "movsx", + "movsxd", + "movupd", + "movups", + "movzx", + "mpsadbw", + "mul", + "mulpd", + "mulps", + "mulsd", + "mulss", + "mulx", + "mwait", + "neg", + "nop", + "not", + "or", + "orpd", + "orps", + "out", + "outs", + "outsb", + "outsd", + "outsw", + "pabsb", + "pabsd", + "pabsq", + "pabsw", + "packssdw", + "packsswb", + "packusdw", + "packuswb", + "paddb", + "paddd", + "paddq", + "paddsb", + "paddsw", + "paddusb", + "paddusw", + "paddw", + "palignr", + "pand", + "pandn", + "pause", + "pavgb", + "pavgw", + "pblendvb", + "pblendw", + "pclmulqdq", + "pcmpeqb", + "pcmpeqd", + "pcmpeqq", + "pcmpeqw", + "pcmpestri", + "pcmpestrm", + "pcmpgtb", + "pcmpgtd", + "pcmpgtq", + "pcmpgtw", + "pcmpistri", + "pcmpistrm", + "pdep", + "pext", + "pextrb", + "pextrd", + "pextrq", + "pextrw", + "phaddd", + "phaddsw", + "phaddw", + "phminposuw", + "phsubd", + "phsubsw", + "phsubw", + "pinsrb", + "pinsrd", + "pinsrq", + "pinsrw", + "pmaddubsw", + "pmaddwd", + "pmaxsb", + "pmaxsd", + "pmaxsq", + "pmaxsw", + "pmaxub", + "pmaxud", + "pmaxuq", + "pmaxuw", + "pminsb", + "pminsd", + "pminsq", + "pminsw", + "pminub", + "pminud", + "pminuq", + "pminuw", + "pmovmskb", + "pmovsx", + "pmovzx", + "pmuldq", + "pmulhrsw", + "pmulhuw", + "pmulhw", + "pmulld", + "pmullq", + "pmullw", + "pmuludq", + "pop", + "popa", + "popad", + "popcnt", + "popf", + "popfd", + "popfq", + "por", + "prefetchw", + "prefetchh", + "psadbw", + "pshufb", + "pshufd", + "pshufhw", + "pshuflw", + "pshufw", + "psignb", + "psignd", + "psignw", + "pslld", + "pslldq", + "psllq", + "psllw", + "psrad", + "psraq", + "psraw", + "psrld", + "psrldq", + "psrlq", + "psrlw", + "psubb", + "psubd", + "psubq", + "psubsb", + "psubsw", + "psubusb", + "psubusw", + "psubw", + "ptest", + "ptwrite", + "punpckhbw", + "punpckhdq", + "punpckhqdq", + "punpckhwd", + "punpcklbw", + "punpckldq", + "punpcklqdq", + "punpcklwd", + "push", + "pushw", + "pushd", + "pusha", + "pushad", + "pushf", + "pushfd", + "pushfq", + "pxor", + "rcl", + "rcpps", + "rcpss", + "rcr", + "rdfsbase", + "rdgsbase", + "rdmsr", + "rdpid", + "rdpkru", + "rdpmc", + "rdrand", + "rdseed", + "rdtsc", + "rdtscp", + "rep", + "repe", + "repne", + "repnz", + "repz", + "ret", + "rol", + "ror", + "rorx", + "roundpd", + "roundps", + "roundsd", + "roundss", + "rsm", + "rsqrtps", + "rsqrtss", + "sahf", + "sal", + "sar", + "sarx", + "sbb", + "scas", + "scasb", + "scasd", + "scasw", + "seta", + "setae", + "setb", + "setbe", + "setc", + "sete", + "setg", + "setge", + "setl", + "setle", + "setna", + "setnae", + "setnb", + "setnbe", + "setnc", + "setne", + "setng", + "setnge", + "setnl", + "setnle", + "setno", + "setnp", + "setns", + "setnz", + "seto", + "setp", + "setpe", + "setpo", + "sets", + "setz", + "sfence", + "sgdt", + "sha1msg1", + "sha1msg2", + "sha1nexte", + "sha1rnds4", + "sha256msg1", + "sha256msg2", + "sha256rnds2", + "shl", + "shld", + "shlx", + "shr", + "shrd", + "shrx", + "shufpd", + "shufps", + "sidt", + "sldt", + "smsw", + "sqrtpd", + "sqrtps", + "sqrtsd", + "sqrtss", + "stac", + "stc", + "std", + "sti", + "stmxcsr", + "stos", + "stosb", + "stosd", + "stosq", + "stosw", + "str", + "sub", + "subpd", + "subps", + "subsd", + "subss", + "swapgs", + "syscall", + "sysenter", + "sysexit", + "sysret", + "test", + "tpause", + "tzcnt", + "ucomisd", + "ucomiss", + "ud", + "umonitor", + "umwait", + "unpckhpd", + "unpckhps", + "unpcklpd", + "unpcklps", + "valignd", + "valignq", + "vblendmpd", + "vblendmps", + "vbroadcast", + "vcompresspd", + "vcompressps", + "vcvtpd2qq", + "vcvtpd2udq", + "vcvtpd2uqq", + "vcvtph2ps", + "vcvtps2ph", + "vcvtps2qq", + "vcvtps2udq", + "vcvtps2uqq", + "vcvtqq2pd", + "vcvtqq2ps", + "vcvtsd2usi", + "vcvtss2usi", + "vcvttpd2qq", + "vcvttpd2udq", + "vcvttpd2uqq", + "vcvttps2qq", + "vcvttps2udq", + "vcvttps2uqq", + "vcvttsd2usi", + "vcvttss2usi", + "vcvtudq2pd", + "vcvtudq2ps", + "vcvtuqq2pd", + "vcvtuqq2ps", + "vcvtusi2sd", + "vcvtusi2ss", + "vdbpsadbw", + "verr", + "verw", + "vexpandpd", + "vexpandps", + "vextractf128", + "vextractf32x4", + "vextractf32x8", + "vextractf64x2", + "vextractf64x4", + "vextracti128", + "vextracti32x4", + "vextracti32x8", + "vextracti64x2", + "vextracti64x4", + "vfixupimmpd", + "vfixupimmps", + "vfixupimmsd", + "vfixupimmss", + "vfmadd132pd", + "vfmadd132ps", + "vfmadd132sd", + "vfmadd132ss", + "vfmadd213pd", + "vfmadd213ps", + "vfmadd213sd", + "vfmadd213ss", + "vfmadd231pd", + "vfmadd231ps", + "vfmadd231sd", + "vfmadd231ss", + "vfmaddsub132pd", + "vfmaddsub132ps", + "vfmaddsub213pd", + "vfmaddsub213ps", + "vfmaddsub231pd", + "vfmaddsub231ps", + "vfmsub132pd", + "vfmsub132ps", + "vfmsub132sd", + "vfmsub132ss", + "vfmsub213pd", + "vfmsub213ps", + "vfmsub213sd", + "vfmsub213ss", + "vfmsub231pd", + "vfmsub231ps", + "vfmsub231sd", + "vfmsub231ss", + "vfmsubadd132pd", + "vfmsubadd132ps", + "vfmsubadd213pd", + "vfmsubadd213ps", + "vfmsubadd231pd", + "vfmsubadd231ps", + "vfnmadd132pd", + "vfnmadd132ps", + "vfnmadd132sd", + "vfnmadd132ss", + "vfnmadd213pd", + "vfnmadd213ps", + "vfnmadd213sd", + "vfnmadd213ss", + "vfnmadd231pd", + "vfnmadd231ps", + "vfnmadd231sd", + "vfnmadd231ss", + "vfnmsub132pd", + "vfnmsub132ps", + "vfnmsub132sd", + "vfnmsub132ss", + "vfnmsub213pd", + "vfnmsub213ps", + "vfnmsub213sd", + "vfnmsub213ss", + "vfnmsub231pd", + "vfnmsub231ps", + "vfnmsub231sd", + "vfnmsub231ss", + "vfpclasspd", + "vfpclassps", + "vfpclasssd", + "vfpclassss", + "vgatherdpd", + "vgatherdpd", + "vgatherdps", + "vgatherdps", + "vgatherqpd", + "vgatherqpd", + "vgatherqps", + "vgatherqps", + "vgetexppd", + "vgetexpps", + "vgetexpsd", + "vgetexpss", + "vgetmantpd", + "vgetmantps", + "vgetmantsd", + "vgetmantss", + "vinsertf128", + "vinsertf32x4", + "vinsertf32x8", + "vinsertf64x2", + "vinsertf64x4", + "vinserti128", + "vinserti32x4", + "vinserti32x8", + "vinserti64x2", + "vinserti64x4", + "vmaskmov", + "vmovdqa32", + "vmovdqa64", + "vmovdqu16", + "vmovdqu32", + "vmovdqu64", + "vmovdqu8", + "vpblendd", + "vpblendmb", + "vpblendmd", + "vpblendmq", + "vpblendmw", + "vpbroadcast", + "vpbroadcastb", + "vpbroadcastd", + "vpbroadcastm", + "vpbroadcastq", + "vpbroadcastw", + "vpcmpb", + "vpcmpd", + "vpcmpq", + "vpcmpub", + "vpcmpud", + "vpcmpuq", + "vpcmpuw", + "vpcmpw", + "vpcompressd", + "vpcompressq", + "vpconflictd", + "vpconflictq", + "vperm2f128", + "vperm2i128", + "vpermb", + "vpermd", + "vpermi2b", + "vpermi2d", + "vpermi2pd", + "vpermi2ps", + "vpermi2q", + "vpermi2w", + "vpermilpd", + "vpermilps", + "vpermpd", + "vpermps", + "vpermq", + "vpermt2b", + "vpermt2d", + "vpermt2pd", + "vpermt2ps", + "vpermt2q", + "vpermt2w", + "vpermw", + "vpexpandd", + "vpexpandq", + "vpgatherdd", + "vpgatherdd", + "vpgatherdq", + "vpgatherdq", + "vpgatherqd", + "vpgatherqd", + "vpgatherqq", + "vpgatherqq", + "vplzcntd", + "vplzcntq", + "vpmadd52huq", + "vpmadd52luq", + "vpmaskmov", + "vpmovb2m", + "vpmovd2m", + "vpmovdb", + "vpmovdw", + "vpmovm2b", + "vpmovm2d", + "vpmovm2q", + "vpmovm2w", + "vpmovq2m", + "vpmovqb", + "vpmovqd", + "vpmovqw", + "vpmovsdb", + "vpmovsdw", + "vpmovsqb", + "vpmovsqd", + "vpmovsqw", + "vpmovswb", + "vpmovusdb", + "vpmovusdw", + "vpmovusqb", + "vpmovusqd", + "vpmovusqw", + "vpmovuswb", + "vpmovw2m", + "vpmovwb", + "vpmultishiftqb", + "vprold", + "vprolq", + "vprolvd", + "vprolvq", + "vprord", + "vprorq", + "vprorvd", + "vprorvq", + "vpscatterdd", + "vpscatterdq", + "vpscatterqd", + "vpscatterqq", + "vpsllvd", + "vpsllvq", + "vpsllvw", + "vpsravd", + "vpsravq", + "vpsravw", + "vpsrlvd", + "vpsrlvq", + "vpsrlvw", + "vpternlogd", + "vpternlogq", + "vptestmb", + "vptestmd", + "vptestmq", + "vptestmw", + "vptestnmb", + "vptestnmd", + "vptestnmq", + "vptestnmw", + "vrangepd", + "vrangeps", + "vrangesd", + "vrangess", + "vrcp14pd", + "vrcp14ps", + "vrcp14sd", + "vrcp14ss", + "vreducepd", + "vreduceps", + "vreducesd", + "vreducess", + "vrndscalepd", + "vrndscaleps", + "vrndscalesd", + "vrndscaless", + "vrsqrt14pd", + "vrsqrt14ps", + "vrsqrt14sd", + "vrsqrt14ss", + "vscalefpd", + "vscalefps", + "vscalefsd", + "vscalefss", + "vscatterdpd", + "vscatterdps", + "vscatterqpd", + "vscatterqps", + "vshuff32x4", + "vshuff64x2", + "vshufi32x4", + "vshufi64x2", + "vtestpd", + "vtestps", + "vzeroall", + "vzeroupper", + "wait", + "wbinvd", + "wrfsbase", + "wrgsbase", + "wrmsr", + "wrpkru", + "xabort", + "xacquire", + "xadd", + "xbegin", + "xchg", + "xend", + "xgetbv", + "xlat", + "xlatb", + "xor", + "xorpd", + "xorps", + "xrelease", + "xrstor", + "xrstors", + "xsave", + "xsavec", + "xsaveopt", + "xsaves", + "xsetbv", + "xtest" +] + +fasm_types = [ + "db", "rb", + "dw", "rw", + "dd", "rd", + "dp", "rp", + "df", "rf", + "dq", "rq", + "dt", "rt", + "du", +] + +# Dict where an identifier is assicoated with a string +# The string contains characters specifying flags +# Available flags: +# k - Keyword +# m - Macro name +# t - fasm data Type name (db, rq, etc.) +# s - Struct type name +# e - equated constant (name equ value) +# = - set constants (name = value) +ID_KIND_KEYWORD = 'k' +ID_KIND_MACRO_NAME = 'm' +ID_KIND_FASM_TYPE = 't' +ID_KIND_STRUCT_NAME = 's' +ID_KIND_EQUATED_CONSTANT = 'e' +ID_KIND_SET_CONSTANT = '=' +id2kind = {} + +# Add kind flag to identifier in id2kind +def id_add_kind(identifier, kind): + if identifier not in id2kind: + id2kind[identifier] = '' + id2kind[identifier] += kind + +# Remove kind flag of identifier in id2kind +def id_remove_kind(identifier, kind): + if identifier in id2kind: + if kind in id2kind[identifier]: + id2kind[identifier] = id2kind[identifier].replace(kind, '') + +# Get kind of an identifier +def id_get_kind(identifier): + if identifier in id2kind: + return id2kind[identifier] + else: + return '' + +for keyword in keywords: + id_add_kind(keyword, ID_KIND_KEYWORD) + +for fasm_type in fasm_types: + id_add_kind(fasm_type, ID_KIND_FASM_TYPE) + +# Warning list +warnings = "" + +# Parse arguments +parser = argparse.ArgumentParser() +parser.add_argument("-o", help="Doxygen output folder") +parser.add_argument("--clean", help="Remove generated files", action="store_true") +parser.add_argument("--dump", help="Dump all defined symbols", action="store_true") +parser.add_argument("--stats", help="Print symbol stats", action="store_true") +parser.add_argument("--nowarn", help="Do not write warnings file", action="store_true") +parser.add_argument("--noemit", help="Do not emit doxygen files (for testing)", action="store_true") +args = parser.parse_args() +doxygen_src_path = args.o if args.o else 'docs/doxygen' +clean_generated_stuff = args.clean +dump_symbols = args.dump +print_stats = args.stats +enable_warnings = not args.nowarn +noemit = args.noemit + +# Variables, functions, labels, macros, structure types +elements = [] + +class LegacyAsmReader: + def __init__(self, file): + self.file = file + self.lines = open(file, "r", encoding="utf-8").readlines() + self.line_idx = 0 + self.i = 0 + + def curr(self): + try: return self.lines[self.line_idx][self.i] + except: return '' + + def step(self): + c = self.curr() + self.i += 1 + # Wrap the line if '\\' followed by whitespaces and/or comment + while self.curr() == '\\': + i_of_backslash = self.i + self.i += 1 + while self.curr().isspace(): + self.i += 1 + if self.curr() == ';' or self.curr() == '': + self.line_idx += 1 + self.i = 0 + else: + # There's something other than a comment after the backslash + # So don't interpret the backslash as a line wrap + self.i = i_of_backslash + break + return c + + def nextline(self): + c = self.curr() + while c != '': + c = self.step() + self.line_idx += 1 + self.i = 0 + + def no_lines(self): + if self.line_idx >= len(self.lines): + return True + return False + + def location(self): + return f"{self.file}:{self.line_idx + 1}" + + def skip_spaces(self): + while self.curr().isspace(): + self.step() + +class AsmReaderRecognizingStrings(LegacyAsmReader): + def __init__(self, file): + super().__init__(file) + self.in_string = None + self.should_recognize_strings = True + + def step(self): + c = super().step() + if self.should_recognize_strings and (c == '"' or c == "'"): + # If just now we was at the double or single quotation mark + # and we aren't in a string yet + # then say "we are in a string openned with this quotation mark now" + if self.in_string == None: + self.in_string = c + # If just now we was at the double or single quotation mark + # and we are in the string entered with the same quotation mark + # then say "we aren't in a string anymore" + elif self.in_string == c: + self.in_string = None + return c + +class AsmReaderReadingComments(AsmReaderRecognizingStrings): + def __init__(self, file): + super().__init__(file) + self.status = dict() + self.status_reset() + self.comment = '' + + def status_reset(self): + # If the line has non-comment code + self.status_has_code = False + # If the line has a comment at the end + self.status_has_comment = False + # Let it recognize strings further, we are definitely out of a comment + self.should_recognize_strings = True + + def status_set_has_comment(self): + self.status_has_comment = True + # Don't let it recognize strings cause we are in a comment now + self.should_recognize_strings = False + + def status_set_has_code(self): + self.status_has_code = True + + def update_status(self): + # If we aren't in a comment and we aren't in a string - say we are now in a comment if ';' met + if not self.status_has_comment and not self.in_string and self.curr() == ';': + self.status_set_has_comment() + # Else if we are in a comment - collect the comment + elif self.status_has_comment: + self.comment += self.curr() + # Else if there's some non-whitespace character out of a comment + # then the line has code + elif not self.status_has_comment and not self.curr().isspace(): + self.status_set_has_code() + + def step(self): + # Get to the next character + c = super().step() + # Update status of the line according to the next character + self.update_status() + return c + + def nextline(self): + super().nextline() + # If the line we leave was not a comment-only line + # then forget the collected comment + # Otherwise the collected comment should be complemented by comment from next line in step() + if self.status_has_code: + self.comment = '' + # Reset the line status (now it's the status of the new line) + self.status_reset() + # Set new status for this line according to the first character in the line + self.update_status() + +class AsmReaderFetchingIdentifiers(AsmReaderReadingComments): + def __init__(self, file): + super().__init__(file) + + def fetch_identifier(self): + self.skip_spaces() + result = '' + while is_id(self.curr()): + result += self.step() + return result + +class AsmReader(AsmReaderFetchingIdentifiers): + def __init__(self, file): + super().__init__(file) + +created_files = [] + +class AsmElement: + def __init__(self, location, name, comment): + global warnings + + # If the element was constructed during this execution then the element is new + self.new = True + self.location = location + self.file = self.location.split(':')[0].replace('\\', '/') + self.line = self.location.split(':')[1] + self.name = name + self.comment = comment + + if self.comment == '': + warnings += f'{self.location}: Undocumented element\n' + + def dump(self): + print(f"\n{self.location}: {self.name}") + print(f"{self.comment}") + + def emit(self, dest, doxycomment = '', declaration = ''): + # Do not emit anything if the symbol is marked as hidden in its comment + if '@dont_give_a_doxygen' in self.comment: + return + + global warnings + # Redefine default declaration + if declaration == '': + declaration = f'#define {self.name}' + # Check doxycomment + if not doxycomment.endswith('\n'): + doxycomment += '\n' + if doxycomment.split('@brief ')[1][0].islower(): + warnings += f"{self.location}: Brief comment starting from lowercase\n" + # Build contents to emit + contents = '' + contents += '/**\n' + contents += doxycomment + contents += (f"@par Source\n" + + f"{self.file}:{self.line}\n") + contents += '*/\n' + contents += declaration + contents += '\n\n' + # Get path to file to emit this + full_path = dest + '/' + self.file + # Remove the file on first access if it was created by previous generation + if full_path not in created_files: + if os.path.isfile(full_path): + os.remove(full_path) + created_files.append(full_path) + # Create directories need for the file + os.makedirs(os.path.dirname(full_path), exist_ok=True) + f = open(full_path, "a") + contents = ''.join([i if ord(i) < 128 else '?' for i in contents]) + f.write(contents) + f.close() + +class AsmVariable(AsmElement): + def __init__(self, location, name, comment, type, init): + super().__init__(location, name, comment) + self.type = type + self.init = init + + def dump(self): + super().dump() + print(f"(Variable)\n---") + + def emit(self, dest): + # Build doxycomment specific for the variable + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + doxycomment += (f"@par Initial value\n" + + f"{self.init}\n") + # Build the declaration + name = self.name.replace(".", "_") + var_type = self.type.replace(".", "_") + declaration = f"{var_type} {name};" + # Emit this + super().emit(dest, doxycomment, declaration) + +class AsmFunction(AsmElement): + def __init__(self, location, name, comment, calling_convention, args, used_regs): + super().__init__(location, name, comment) + self.calling_convention = calling_convention + self.args = args + self.used_regs = used_regs + + def dump(self): + super().dump() + print(f"(Function)\n---") + + def emit(self, dest): + # Build doxycomment specific for the variable + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + # If there was no arguments, maybe that's just a label + # then parse parameters from its comment + if len(self.args) == 0 and '@param' in self.comment: + i = 0 + while '@param' in self.comment[i:]: + i = self.comment.index('@param', i) + # Skip '@param' + i += len('@param') + # Skip spaces after '@param' + while self.comment[i].isspace(): + i += 1 + # Get the parameter name + name = '' + while is_id(self.comment[i]): + name += self.comment[i] + i += 1 + # Save the parameter + self.args.append((name, 'arg_t')) + # Build the arg list for declaration + arg_list = '(' + if len(self.args) > 0: + argc = 0 + for arg in self.args: + if argc != 0: + arg_list += ", " + arg_list += f"{arg[1]} {arg[0]}" + argc += 1 + arg_list += ')' + # Build the declaration + name = self.name.replace(".", "_") + declaration = f"void {name}{arg_list};" + # Emit this + super().emit(dest, doxycomment, declaration) + +class AsmLabel(AsmElement): + def __init__(self, location, name, comment): + super().__init__(location, name, comment) + + def dump(self): + super().dump() + print(f"(Label)\n---") + + def emit(self, dest): + # Build doxycomment specific for the variable + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + # Build the declaration + name = self.name.replace(".", "_") + declaration = f"label {name};" + # Emit this + super().emit(dest, doxycomment, declaration) + +class AsmMacro(AsmElement): + def __init__(self, location, name, comment, args): + super().__init__(location, name, comment) + self.args = args + + def dump(self): + super().dump() + print(f"(Macro)\n---") + + def emit(self, dest): + # Construct arg list without '['s, ']'s and '*'s + args = [arg for arg in self.args if arg not in "[]*"] + # Construct C-like arg list + arg_list = "" + if len(args) > 0: + arg_list += '(' + argc = 0 + for arg in args: + if argc != 0: + arg_list += ", " + arg_list += arg + argc += 1 + arg_list += ')' + # Build doxycomment + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + # Build declaration + declaration = f"#define {self.name}{arg_list}" + # Emit this + super().emit(dest, doxycomment, declaration) + +class AsmStruct(AsmElement): + def __init__(self, location, name, comment, members): + super().__init__(location, name, comment) + self.members = members + + def dump(self): + super().dump() + print(f"(Struct)\n---") + + def emit(self, dest): + # Build doxycomment + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + doxycomment += '\n' + # Build declaration + declaration = f"struct {self.name}" + " {\n" + for member in self.members: + if type(member) == AsmVariable: + declaration += f'\t{member.type} {member.name}; /**< {member.comment} */\n' + declaration += '};' + # Emit this + super().emit(dest, doxycomment, declaration) + +class AsmUnion(AsmElement): + def __init__(self, location, name, comment, members): + super().__init__(location, name, comment) + self.members = members + + def dump(self): + super().dump() + print(f"(Union)\n---") + + def emit(self, dest): + # Build doxycomment + doxycomment = '' + doxycomment += self.comment + if '@brief' not in doxycomment: + doxycomment = '@brief ' + doxycomment + # Build declaration + declaration = f"union {self.name}" + " {};" + # Emit this + super().emit(dest, doxycomment, declaration) + +class VariableNameIsMacroName: + def __init__(self, name): + self.name = name + +def is_id(c): + return c.isprintable() and c not in "+-/*=<>()[]{};:,|&~#`'\" \n\r\t\v" + +def is_starts_as_id(s): + return not s[0].isdigit() + +def parse_after_macro(r): + location = r.location() + + # Skip spaces after the "macro" keyword + r.skip_spaces() + # Read macro name + name = "" + while is_id(r.curr()) or r.curr() == '#': + name += r.step() + # Skip spaces after macro name + r.skip_spaces() + # Find all arguments + args = [] + arg = '' + while r.curr() and r.curr() != ';' and r.curr() != '{': + # Collect identifier + if is_id(r.curr()): + arg += r.step() + # Save the collected identifier + elif r.curr() == ',': + args.append(arg) + arg = '' + r.step() + # Just push the '[' + elif r.curr() == '[': + args.append(r.step()) + # Just push the identifier and get ']' ready to be pushed on next comma + elif r.curr() == ']': + args.append(arg) + arg = r.step() + # Just push the identifier and get '*' ready to be pushed on next comma + elif r.curr() == '*': + args.append(arg) + arg = r.step() + # Just skip whitespaces + elif r.curr().isspace(): + r.step() + # Something unexpected + else: + raise Exception(f"Unexpected symbol '{r.curr()}' at index #{r.i} " + + f"in the macro declaration at {location} " + + f"(line: {r.lines[r.line_idx]})\n''") + # Append the last argument + if arg != '': + args.append(arg) + # Skip t spaces after the argument list + r.skip_spaces() + # Get a comment if it is: read till the end of the line and get the comment from the reader + while r.curr() != '': + r.step() + comment = r.comment + # Find end of the macro + prev = '' + while True: + if r.curr() == '}' and prev != '\\': + break + elif r.curr() == '': + prev = '' + r.nextline() + continue + prev = r.step() + # Build the output + return AsmMacro(location, name, comment, args) + +def parse_variable(r, first_word = None): + global warnings + location = r.location() + + # Skip spaces before variable name + r.skip_spaces() + # Get variable name + name = "" + # Read it if it was not supplied + if first_word == None: + while is_id(r.curr()): + name += r.step() + # Or use the supplied one instead + else: + name = first_word + # Check the name + # If it's 0 len, that means threr's something else than an identifier at the beginning + if len(name) == 0: + return None + # If it starts from digit or othervice illegally it's illegal + if not is_starts_as_id(name): + return None + # Get kind of the identifier from id2kind table + kind = id_get_kind(name) + # If it's a keyword, that's not a variable declaration + if ID_KIND_KEYWORD in kind: + return None + # If it's a macro name, that's not a variable declaration + if ID_KIND_MACRO_NAME in kind: + return VariableNameIsMacroName(name) + # If it's a datatype or a structure name that's not a variable declaration: that's just a data + # don't document just a data for now + if ID_KIND_STRUCT_NAME in kind or ID_KIND_FASM_TYPE in kind: + return None + # Skip spaces before type name + r.skip_spaces() + # Read type name + var_type = "" + while is_id(r.curr()): + var_type += r.step() + # Check the type name + if len(var_type) == 0: + # If there's no type identifier after the name + # maybe the name is something meaningful for the next parser + # return it + return name + # If it starts from digit or othervice illegally it's illegal + if not is_starts_as_id(var_type): + return None + # Get kind of type identifier + type_kind = id_get_kind(var_type) + # If it's a keyword, that's not a variable declaration + # return the two words of the lexical structure + if ID_KIND_KEYWORD in type_kind: + return (name, var_type) + # Skip spaces before the value + r.skip_spaces() + # Read the value until the comment or end of the line + value = "" + while r.curr() != ';' and r.curr() != '' and r.curr() != '\n': + value += r.step() + # Skip spaces after the value + r.skip_spaces() + # Read till end of the line to get a comment from the reader + while r.curr() != '': + r.step() + # Build the result + return AsmVariable(location, name, r.comment, var_type, value) + +def parse_after_struct(r, as_union = True): + global warnings + location = r.location() + + # Skip spaces after "struct" keyword + r.skip_spaces() + # Read struct name + name = "" + while is_id(r.curr()): + name += r.step() + # Read till end of the line and get the comment from the reader + while r.curr() != '': + r.step() + comment = r.comment + # Get to the next line to parse struct members + r.nextline() + # Parse struct members + members = [] + while True: + r.skip_spaces() + var = parse_variable(r) + if type(var) == AsmVariable: + members.append(var) + elif type(var) == str: + if var == 'union': + # Parse the union as a struct + union = parse_after_struct(r, as_union = True) + members.append(union) + # Skip the ends of the union + r.nextline() + elif r.curr() == ':': + warnings += f"{r.location()}: Skept the label in the struct\n" + else: + raise Exception(f"Garbage in struct member at {location} (got '{var}' identifier)") + elif type(var) == VariableNameIsMacroName: + if var.name == 'ends': + break + r.nextline() + # Return the result + if as_union: + return AsmStruct(location, name, comment, members) + else: + return AsmUnion(location, name, comment, members) + +def parse_after_proc(r): + # Get proc name + name = r.fetch_identifier() + # Next identifier after the proc name + identifier = r.fetch_identifier() + # Check if the id is 'stdcall' or 'c' (calling convention specifier) + # and if so - save the convention and lookup the next identifier + calling_convention = '' + if identifier == 'stdcall' or identifier == 'c': + calling_convention = identifier + # If next is a comma, just skip it + if r.curr() == ',': + r.step() + # Read the next identifier + identifier = r.fetch_identifier() + # Check if the id is 'uses' (used register list specifier) + # and if so save the used register list + used_regs = [] + if identifier == 'uses': + # Read the registers + while True: + reg_name = r.fetch_identifier() + if reg_name != '': + used_regs.append(reg_name) + else: + break + # If next is a comma, just skip it + if r.curr() == ',': + r.step() + # Read the next identifier + identifier = r.fetch_identifier() + # Check if there are argument identifiers + args = [] + while identifier != '': + arg_name = identifier + arg_type = 'arg_t' + # Skip spaces after argument name + r.skip_spaces() + # If there's a ':' after the name - the next identifier is type + if r.curr() == ':': + r.step() + arg_type = r.fetch_identifier() + # If there's a comma - there's one more argument + # else no arguments anymore + if r.curr() == ',': + r.step() + identifier = r.fetch_identifier() + else: + identifier = '' + args.append((arg_name, arg_type)) + # Get to the end of the line and get a comment from the reader + while r.curr() != '': + r.step() + comment = r.comment + # Build the element + return AsmFunction(r.location(), name, comment, calling_convention, args, used_regs) + +def get_declarations(asm_file_contents, asm_file_name): + r = AsmReader(asm_file_name) + + while not r.no_lines(): + # Skip leading spaces + r.skip_spaces() + # Skip the line if it's starting with a comment + if r.curr() == ';': + r.nextline() + continue + # Get first word + first_word = "" + while is_id(r.curr()): + first_word += r.step() + # Match macro declaration + if first_word == "macro": + macro = parse_after_macro(r) + elements.append(macro) + id_add_kind(macro.name, ID_KIND_MACRO_NAME) + # Match structure declaration + elif first_word == "struct": + struct = parse_after_struct(r) + elements.append(struct) + id_add_kind(struct.name, ID_KIND_STRUCT_NAME) + # Match function definition + elif first_word == "proc": + proc = parse_after_proc(r) + elements.append(proc) + elif first_word == 'format': + # Skip the format directive + pass + elif first_word == 'include': + # Skip the include directive + pass + elif first_word == 'if': + # Skip the conditional directive + pass + elif first_word == 'repeat': + # Skip the repeat directive + pass + elif first_word == 'purge': + while True: + # Skip spaces after the 'purge' keyword or after the comma what separated the previous macro name + r.skip_spaces() + # Get the purged macro name + name = '' + while is_id(r.curr()): + name += r.step() + # Remove the purged macro from the macro names list + try: + id_remove_kind(name, ID_KIND_MACRO_NAME) + except: + pass + # Skip spaces after the name + r.skip_spaces() + # If it's comma (',') after then that's not the last purged macro, continue purging + if r.curr() == ',': + r.step() + continue + # Here we purged all the macros should be purged + break + # Match label or a variable + elif len(first_word) != 0: + # Skip spaces after the identifier + r.skip_spaces() + # Match a variable + var = parse_variable(r, first_word) + if type(var) == AsmVariable: + elements.append(var) + # If it wasn't a variable but there was an identifier + # Maybe that's a label and the identifier is the label name + # The parse_variable returns the first found or supplied identifier + # In this case it returns the first_word which is supplied + # If it didn't match a type identifier after the word + elif type(var) == str: + name = var + # Match label beginning (':' after name) + if r.curr() == ':': + # Get to the end of the line and get the coment from the reader + while r.curr() != '': + r.step() + comment = r.comment + # Only handle non-local labels + if name[0] != '.' and name != "@@" and name != "$Revision": + if '@return' in comment or '@param' in comment: + element = AsmFunction(r.location(), name, comment, '', [], []) + else: + element = AsmLabel(r.location(), name, comment) + elements.append(element) + elif r.curr() == '=': + # Save the identifier as a set constant + id_add_kind(first_word, ID_KIND_SET_CONSTANT) + elif type(var) == tuple: + (word_one, word_two) = var + if word_two == 'equ': + # Save the identifier as an equated constant + id_add_kind(word_one, ID_KIND_EQUATED_CONSTANT) + r.nextline() + +def it_neds_to_be_parsed(source_file): + # If there's no symbols file saved - parse it anyway + # cause we need to create the symbols file and use it + # if we gonna generate proper doxygen + if not os.path.isfile('asmxygen.elements.pickle'): + return True + dest = doxygen_src_path + '/' + source_file + # If there's no the doxygen file it should be compiled to + # then yes, we should compile it to doxygen + if not os.path.isfile(dest): + return True + source_change_time = os.path.getmtime(source_file) + dest_change_file = os.path.getmtime(dest) + # If the source is newer than the doxygen it was compiled to + # then the source should be recompiled (existing doxygen is old) + if source_change_time > dest_change_file: + return True + return False + +def handle_file(handled_files, asm_file_name, subdir = "."): + global elements + # Canonicalize the file path and get it relative to cwd + cwd = os.path.abspath(os.path.dirname(sys.argv[0])) + asm_file_name = os.path.realpath(asm_file_name) + asm_file_name = asm_file_name[len(cwd) + 1:] + # If it's lang.inc - skip it + if asm_file_name == 'lang.inc': + return + # If the file was handled in this execution before - skip it + if asm_file_name in handled_files: + return + # Say that the file was handled in this execution + handled_files.append(asm_file_name) + # Check if the file should be parsed (if it was modified or wasn't parsed yet) + should_get_declarations = True + if not it_neds_to_be_parsed(asm_file_name): + print(f"Skipping {asm_file_name} (already newest)") + should_get_declarations = False + else: + print(f"Handling {asm_file_name}") + # Remove elements parsed from this file before if any + elements_to_remove = [x for x in elements if x.location.split(':')[0] == asm_file_name] + elements = [x for x in elements if x.location.split(':')[0] != asm_file_name] + # Forget types of identifiers of names of the removed elements + for element in elements_to_remove: + if type(element) == AsmStruct: + id_remove_kind(element.name, ID_KIND_STRUCT_NAME) + elif type(element) == AsmMacro: + id_remove_kind(element.name, ID_KIND_MACRO_NAME) + # Read the source + asm_file_contents = open(asm_file_name, "r", encoding="utf-8").read() + # Find includes, fix their paths and handle em recoursively + includes = re.findall(r'^include (["\'])(.*)\1', asm_file_contents, flags=re.MULTILINE) + for include in includes: + include = include[1].replace('\\', '/'); + full_path = subdir + '/' + include; + # If the path isn't valid, maybe that's not relative path + if not os.path.isfile(full_path): + full_path = include + new_subdir = full_path.rsplit('/', 1)[0] + handle_file(handled_files, full_path, new_subdir) + # Only collect declarations from the file if it wasn't parsed before + if should_get_declarations and not clean_generated_stuff: + get_declarations(asm_file_contents, asm_file_name) + +kernel_files = [] + +# Load remembered list of symbols +if os.path.isfile('asmxygen.elements.pickle'): + print('Reading existing dump of symbols') + (elements, id2kind) = pickle.load(open('asmxygen.elements.pickle', 'rb')) + +handle_file(kernel_files, "./kernel.asm"); + +if dump_symbols: + stdout = sys.stdout + sys.stdout = open('asmxygen.dump.txt', 'w', encoding = 'utf-8') + for asm_element in elements: + asm_element.dump() + sys.stdout = stdout + +if clean_generated_stuff: + kernel_files_set = set(kernel_files) + for file in kernel_files: + doxygen_file = f"{doxygen_src_path}/{file}" + if (os.path.isfile(doxygen_file)): + print(f"Removing {file}... ", end = '') + os.remove(doxygen_file) + print("Done.") +elif not noemit: + print(f"Writing doumented sources to {doxygen_src_path}") + + i = 0 + new_elements = [x for x in elements if x.new] + for element in new_elements: + print(f"[{i + 1}/{len(new_elements)}] Emitting {element.name} from {element.location}") + element.emit(doxygen_src_path) + i += 1 + + print(f"Writing dump of symbols to asmxygen.elements.pickle") + + # Now when the new elements already was written, there's no new elements anymore + for element in elements: + element.new = False + pickle.dump((elements, id2kind), open('asmxygen.elements.pickle', 'wb')) + +if print_stats: + var_count = 0 + mac_count = 0 + lab_count = 0 + fun_count = 0 + uni_count = 0 + str_count = 0 + for element in elements: + if type(element) == AsmVariable: + var_count += 1 + elif type(element) == AsmMacro: + mac_count += 1 + elif type(element) == AsmLabel: + lab_count += 1 + elif type(element) == AsmFunction: + fun_count += 1 + elif type(element) == AsmUnion: + uni_count += 1 + elif type(element) == AsmStruct: + str_count += 1 + print(f'Parsed variable count: {var_count}') + print(f'Parsed macro count: {mac_count}') + print(f'Parsed label count: {lab_count}') + print(f'Parsed function count: {fun_count}') + print(f'Parsed union type count: {uni_count}') + print(f'Parsed structure type count: {str_count}') + +if enable_warnings: + open('asmxygen.txt', "w", encoding = "utf-8").write(warnings) diff --git a/kernel/branches/kolibri-lldw/blkdev/ahci.inc b/kernel/branches/kolibri-lldw/blkdev/ahci.inc new file mode 100644 index 000000000..622bfe016 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/ahci.inc @@ -0,0 +1,1403 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +PCI_REG_STATUS_COMMAND = 0x0004 +PCI_REG_BAR5 = 0x0024 + +AHCI_DBGLVL = 0 ; debug output verbosity level. 0 - less verbose, 1 - more verbose + +; different SATA device signatures +SATA_SIG_ATA = 0x00000101 ; SATA drive +SATA_SIG_ATAPI = 0xEB140101 ; SATAPI drive +SATA_SIG_SEMB = 0xC33C0101 ; Enclosure management bridge +SATA_SIG_PM = 0x96690101 ; Port multiplier + +; Device type constants +AHCI_DEV_NULL = 0 +AHCI_DEV_SATA = 1 +AHCI_DEV_SEMB = 2 +AHCI_DEV_PM = 3 +AHCI_DEV_SATAPI = 4 + +; ATA commands +ATA_IDENTIFY = 0xEC +ATA_CMD_READ_DMA_EX = 0x25 +ATA_CMD_WRITE_DMA_EX = 0x35 + +; ATA constants +ATA_DEV_BUSY = 0x80 +ATA_DEV_DRQ = 0x08 + +; ATAPI commands +ATAPI_IDENTIFY = 0xA1 + +PRDT_MAX_ENTRIES = 16 ;65535 + +; bit_ prefix means that its index of bit +; format: bit_AHCI_STR_REG_BIT +bit_AHCI_HBA_CAP2_BOH = 0 ; Supports BIOS/OS Handoff + +bit_AHCI_HBA_BOHC_BOS = 0 ; BIOS-Owned Semaphore (BIOS owns controller) +bit_AHCI_HBA_BOHC_OOS = 1 ; OS-Owned Semaphore (OS owns controller) +bit_AHCI_HBA_BOHC_BB = 4 ; BIOS Busy (polling bit while BIOS cleans up + +bit_AHCI_HBA_GHC_AHCI_ENABLE = 31 ; Enable AHCI mode +bit_AHCI_HBA_GHC_RESET = 0 ; Reset HBA +bit_AHCI_HBA_GHC_INTERRUPT_ENABLE = 1 ; Enable interrupts from the HBA + +bit_AHCI_HBA_PxCMD_ST = 0 +bit_AHCI_HBA_PxCMD_FRE = 4 +bit_AHCI_HBA_PxCMD_FR = 14 +bit_AHCI_HBA_PxCMD_CR = 15 +bit_AHCI_HBA_PxIS_TFES = 30 + +AHCI_HBA_PxCMD_ST = 1 shl 0 +AHCI_HBA_PxCMD_FRE = 1 shl 4 +AHCI_HBA_PxCMD_FR = 1 shl 14 +AHCI_HBA_PxCMD_CR = 1 shl 15 + +bit_AHCI_H2D_FLAG_CMD = 7 + +AHCI_HBA_PxSSTS_DET = 0xF +AHCI_HBA_PORT_IPM_ACTIVE = 1 +AHCI_HBA_PxSSTS_DET_PRESENT = 3 + +AHCI_MAX_PORTS = 32 ; +;HBA_MEMORY_SIZE = 0x1100 + +AHCI_PORT_TIMEOUT = 1000000 + +; Frame Information Structure Types +FIS_TYPE_REG_H2D = 0x27 ; Register FIS - host to device +FIS_TYPE_REG_D2H = 0x34 ; Register FIS - device to host +FIS_TYPE_DMA_ACT = 0x39 ; DMA activate FIS - device to host +FIS_TYPE_DMA_SETUP = 0x41 ; DMA setup FIS - bidirectional +FIS_TYPE_DATA = 0x46 ; Data FIS - bidirectional +FIS_TYPE_BIST = 0x58 ; BIST activate FIS - bidirectional +FIS_TYPE_PIO_SETUP = 0x5F ; PIO setup FIS - device to host +FIS_TYPE_DEV_BITS = 0xA1 ; Set device bits FIS - device to host + +struct AHCI_DATA + abar dd ? ; pointer to HBA Memory (BAR5) mapped to virtual kernelspace memory + pcidev dd ? ; pointer to corresponding PCIDEV structure +ends + +; Generic Host Control registers +struct HBA_MEM + cap dd ? ; 0x00, Host capabilities + ghc dd ? ; 0x04, Global host control + is dd ? ; 0x08, Interrupt status + pi dd ? ; 0x0C, Port implemented + version dd ? ; 0x10, Version + ccc_ctl dd ? ; 0x14, Command completion coalescing control + ccc_pts dd ? ; 0x18, Command completion coalescing ports + em_loc dd ? ; 0x1C, Enclosure management location + em_ctl dd ? ; 0x20, Enclosure management control + cap2 dd ? ; 0x24, Host capabilities extended + bohc dd ? ; 0x28, BIOS/OS handoff control and status + reserved rb (0xA0-HBA_MEM.reserved) ; 0x2C - 0x9F, Reserved + vendor rb (0x100-HBA_MEM.vendor) ; 0xA0 - 0xFF, Vendor specific + ports rb (sizeof.HBA_PORT*AHCI_MAX_PORTS) ; 0x100 - 0x10FF, Port control registers, max AHCI_MAX_PORTS +ends + +; Port Control registers +struct HBA_PORT + command_list_base_l dd ? ; 0x00, command list base address, 1K-byte aligned + command_list_base_h dd ? ; 0x04, command list base address upper 32 bits, used on 64 bit systems + fis_base_l dd ? ; 0x08, FIS base address, 256-byte aligned + fis_base_h dd ? ; 0x0C, FIS base address upper 32 bits, used on 64 bit systems + interrupt_status dd ? ; 0x10 + interrupt_enable dd ? ; 0x14 + command dd ? ; 0x18, command and status + reserved0 dd ? ; 0x1C + task_file_data dd ? ; 0x20 + signature dd ? ; 0x24 + sata_status dd ? ; 0x28, SATA status (SCR0:SStatus) + sata_control dd ? ; 0x2C, SATA control (SCR2:SControl) + sata_error dd ? ; 0x30, SATA error (SCR1:SError) + sata_active dd ? ; 0x34, SATA active (SCR3:SActive) + command_issue dd ? ; 0x38 + sata_notification dd ? ; 0x3C, SATA notification (SCR4:SNotification) + fis_based_switch_control dd ? ; 0x40 + reserved1 rd 11 ; 0x44 - 0x6F + vendor rd 4 ; 0x70 - 0x7F, vendor specific +ends + +; Command header structure, size = 32 bytes +struct HBA_CMD_HDR + flags1 db ? ; 0bPWACCCCC, P - Prefetchable, W - Write (1: H2D, 0: D2H) + ; A - ATAPI, C - Command FIS length in DWORDS, 2 ~ 16 + + flags2 db ? ; 0bPPPPRCB(Re), P - Port multiplier port, R - Reserved, + ; C - Clear busy upon R_OK, B - BIST, Re - Reset + + prdtl dw ? ; Physical region descriptor table length in entries + prdbc dd ? ; Physical region descriptor byte count transferred + ctba dd ? ; Command table descriptor base address + ctbau dd ? ; Command table descriptor base address upper 32 bits + rd 4 ; Reserved +ends + +; Physical region descriptor table entry, size = 16 bytes +struct HBA_PRDT_ENTRY + dba dd ? ; Data base address + dbau dd ? ; Data base address upper 32 bits + dd ? ; Reserved + flags dd ? ; 0bIR..RD..D, I (1 bit) - Interrupt on completion, + ; R (9 bits) - Reserved, D (22 bits) - Byte count, 4M max +ends + +struct HBA_CMD_TBL + cfis rb 64 ; 0x00, Command FIS + acmd rb 16 ; 0x40, ATAPI command, 12 or 16 bytes + rb 48 ; 0x50, Reserved + prdt_entry HBA_PRDT_ENTRY ; 0x80, Physical region descriptor table entries, 0 ~ 65535 + ; so, this structure is variable-length +ends + +; Contains virtual mappings for port phys memory regions +struct PORT_DATA + clb dd ? ; Command list base + fb dd ? ; FIS base + ctba_arr rd 32 ; ctba_arr[0] = clb[0].ctba, ... and so on. + port dd ? ; address of correspoding HBA_PORT structure + portno dd ? ; port index, 0..31 + drive_type db ? ; drive type + sector_count dq ? ; number of sectors +ends + +; Register FIS – Host to Device +struct FIS_REG_H2D + fis_type db ? ; FIS_TYPE_REG_H2D + flags db ? ; 0bCRRRPPPP, C - 1: Command, 0: Control + ; R - Reserved, P - Port multiplier + + command db ? ; Command register + featurel db ? ; Feature register, 7:0 + + lba0 db ? ; LBA low register, 7:0 + lba1 db ? ; LBA mid register, 15:8 + lba2 db ? ; LBA high register, 23:16 + device db ? ; Device register + + lba3 db ? ; LBA register, 31:24 + lba4 db ? ; LBA register, 39:32 + lba5 db ? ; LBA register, 47:40 + featureh db ? ; Feature register, 15:8 + + countl db ? ; Count register, 7:0 + counth db ? ; Count register, 15:8 + icc db ? ; Isochronous command completion + control db ? ; Control register + + rb 4 ; Reserved +ends + +; Register FIS – Device to Host +struct FIS_REG_D2H + fis_type db ? ; FIS_TYPE_REG_D2H + + flags db ? ; 0bRIRPPPP, P - Port multiplier, R - Reserved + ; I - Interrupt bit + + status db ? ; Status register + error db ? ; Error register + + lba0 db ? ; LBA low register, 7:0 + lba1 db ? ; LBA mid register, 15:8 + lba2 db ? ; LBA high register, 23:16 + device db ? ; Device register + + lba3 db ? ; LBA register, 31:24 + lba4 db ? ; LBA register, 39:32 + lba5 db ? ; LBA register, 47:40 + db ? ; Reserved + + countl db ? ; Count register, 7:0 + counth db ? ; Count register, 15:8 + rb 2 ; Reserved + + rb 4 ; Reserved +ends + +; Data FIS – Bidirectional +struct FIS_DATA + fis_type db ? ; FIS_TYPE_DATA + flags db ? ; 0bRRRRPPPP, R - Reserved, P - Port multiplier + rb 2 ; Reserved + ; DWORD 1 ~ N (?) + data rd 1 ; Payload +ends + +; PIO Setup – Device to Host +struct FIS_PIO_SETUP + fis_type db ? ; FIS_TYPE_PIO_SETUP + + flags db ? ; 0bRIDRPPPP, P - Port multiplier, R - Reserved + ; I - Interrupt bit, D - Data transfer direction, 1 - device to host + + status db ? ; Status register + error db ? ; Error register + + lba0 db ? ; LBA low register, 7:0 + lba1 db ? ; LBA mid register, 15:8 + lba2 db ? ; LBA high register, 23:16 + device db ? ; Device register + + lba3 db ? ; LBA register, 31:24 + lba4 db ? ; LBA register, 39:32 + lba5 db ? ; LBA register, 47:40 + db ? ; Reserved + + countl db ? ; Count register, 7:0 + counth db ? ; Count register, 15:8 + db ? ; Reserved + e_status db ? ; New value of status register + + tc dw ? ; Transfer count + rb 2 ; Reserved +ends + +; DMA Setup – Device to Host +struct FIS_DMA_SETUP + fis_type db ? ; FIS_TYPE_DMA_SETUP + flags db ? ; 0bAIDRPPPP, A - Auto-activate. Specifies if DMA Activate FIS is needed, + ; I - Interrupt bit, D - Data transfer direction, 1 - device to host, + ; R - Reserved, P - Port multiplier + + rb 2 ; Reserved + DMAbufferID dq ? ; DMA Buffer Identifier. + ; Used to Identify DMA buffer in host memory. + ; SATA Spec says host specific and not in Spec. + ; Trying AHCI spec might work. + + dd ? ; Reserved + DMAbufOffset dd ? ; Byte offset into buffer. First 2 bits must be 0 + TransferCount dd ? ; Number of bytes to transfer. Bit 0 must be 0 + dd ? ; Reserved +ends + +; Set device bits FIS - device to host +struct FIS_DEV_BITS + fis_type db ? ; FIS_TYPE_DEV_BITS + flags db ? ; 0bNIRRPPPP, N - Notification, I - Interrupt, + ; R - Reserved, P - Port multiplier + + status db ? ; Status register + error db ? ; Error register + + protocol dd ? ; Protocol +ends + +struct HBA_FIS + dsfis FIS_DMA_SETUP ; 0x00, DMA Setup FIS + rb 4 ; padding + + psfis FIS_PIO_SETUP ; 0x20, PIO Setup FIS + rb 12 ; padding + + rfis FIS_REG_D2H ; 0x40, Register - Device to Host FIS + rb 4 ; padding + + sdbfis FIS_DEV_BITS ; 0x58, Set Device Bit FIS + + ufis rb 64 ; 0x60 + + rb (0x100 - 0xA0) ; 0xA0, Reserved +ends + +; -------------------------------------------------- +uglobal +align 4 + ahci_controller AHCI_DATA + port_data_arr rb (sizeof.PORT_DATA*AHCI_MAX_PORTS) + ahci_mutex MUTEX +endg + +iglobal +align 4 +ahci_callbacks: + dd ahci_callbacks.end - ahci_callbacks + dd 0 ; no close function + dd 0 ; no closemedia function + dd ahci_querymedia + dd ahci_read + dd ahci_write + dd 0 ; no flush function + dd 0 ; use default cache size +.end: +hd_name db 'hd', 0, 0, 0 +hd_counter dd 0 +endg + +; ----------------------------------------------------------------------- +; detect ahci controller and initialize +align 4 +ahci_init: + mov ecx, ahci_mutex + call mutex_init + + mov ecx, ahci_controller + mov esi, pcidev_list +.find_ahci_ctr: + mov esi, [esi + PCIDEV.fd] + cmp esi, pcidev_list + jz .ahci_ctr_not_found + mov eax, [esi + PCIDEV.class] + ;DEBUGF 1, "K: device class = %x\n", eax + shr eax, 8 ; shift right because lowest 8 bits if ProgIf field + cmp eax, 0x0106 ; 0x01 - Mass Storage Controller class, 0x06 - Serial ATA Controller subclass + jz .ahci_ctr_found + jmp .find_ahci_ctr + +.ahci_ctr_not_found: + DEBUGF 1, "K: AHCI controller not found\n" + ret + +.ahci_ctr_found: + mov [ahci_controller + AHCI_DATA.pcidev], esi + + mov eax, [esi+PCIDEV.class] + movzx ebx, byte [esi+PCIDEV.bus] + movzx ecx, byte [esi+PCIDEV.devfn] + shr ecx, 3 ; get rid of 3 lowest bits (function code), the rest bits is device code + movzx edx, byte [esi+PCIDEV.devfn] + and edx, 00000111b ; get only 3 lowest bits (function code) + DEBUGF 1, "K: found AHCI controller, (class, subcl, progif) = %x, bus = %x, device = %x, function = %x\n", eax, ebx, ecx, edx + + ; get BAR5 value, it is physical address + movzx ebx, [esi + PCIDEV.bus] + movzx ebp, [esi + PCIDEV.devfn] + stdcall pci_read32, ebx, ebp, PCI_REG_BAR5 + DEBUGF 1, "K: AHCI controller MMIO = %x\n", eax + mov edi, eax + + ; get the size of MMIO region + stdcall pci_write32, ebx, ebp, PCI_REG_BAR5, 0xFFFFFFFF + stdcall pci_read32, ebx, ebp, PCI_REG_BAR5 + not eax + inc eax + DEBUGF 1, "K: AHCI: MMIO region size = 0x%x bytes\n", eax + + ; Map MMIO region to virtual memory + stdcall map_io_mem, edi, eax, PG_SWR + PG_NOCACHE + mov [ahci_controller + AHCI_DATA.abar], eax + DEBUGF 1, "K: AHCI controller BAR5 mapped to virtual addr %x\n", eax + + ; Restore the original BAR5 value + stdcall pci_write32, ebx, ebp, PCI_REG_BAR5, edi + + ; Enable dma bus mastering, memory space access, clear the "disable interrupts" bit + ; Usually, it is already done before us + movzx ebx, [esi + PCIDEV.bus] + movzx ebp, [esi + PCIDEV.devfn] + stdcall pci_read32, ebx, ebp, PCI_REG_STATUS_COMMAND + DEBUGF 1, "K: AHCI: pci_status_command = %x\nEnabling interrupts, DMA bus mastering and memory space access\n", eax + or eax, 0x06 ; pci.command |= 0x06 (dma bus mastering + memory space access) + btr eax, 10 ; clear the "disable interrupts" bit + DEBUGF 1, "K: AHCI: pci_status_command = %x\n", eax + stdcall pci_write32, ebx, ebp, PCI_REG_STATUS_COMMAND, eax + + ; ; Print some register values to debug board + ; mov esi, [ahci_controller + AHCI_DATA.abar] + ; DEBUGF 1, "K: AHCI: HBA.cap = %x, HBA.ghc = %x, HBA_MEM.version = %x\n", [esi + HBA_MEM.cap], [esi + HBA_MEM.ghc], [esi + HBA_MEM.version] + + ;------------------------------------------------------- + ; Request BIOS/OS ownership handoff, if supported. (TODO check correctness) + mov esi, [ahci_controller + AHCI_DATA.abar] + ;mov ebx, [esi + HBA_MEM.cap2] + ;DEBUGF 1, "K: AHCI: HBA_MEM.cap2 = %x\n", ebx + bt [esi + HBA_MEM.cap2], bit_AHCI_HBA_CAP2_BOH + jnc .end_handoff + DEBUGF 1, "K: AHCI: requesting AHCI ownership change...\n" + bts [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_OOS + +.wait_not_bos: + bt [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_BOS + jc .wait_not_bos + + mov ebx, 3 + call delay_hs + + ; if Bios Busy is still set after 30 mS, wait 2 seconds. + bt [esi + HBA_MEM.bohc], bit_AHCI_HBA_BOHC_BB + jnc @f + + mov ebx, 200 + call delay_hs +@@: + DEBUGF 1, "K: AHCI: ownership change completed.\n" + +.end_handoff: + ;------------------------------------------------------- + + ; enable the AHCI and reset it + bts [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_AHCI_ENABLE + bts [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_RESET + + ; wait for reset to complete +.wait_reset: + bt [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_RESET + jc .wait_reset + + ; enable the AHCI and interrupts + bts [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_AHCI_ENABLE + bts [esi + HBA_MEM.ghc], bit_AHCI_HBA_GHC_INTERRUPT_ENABLE + mov ebx, 2 + call delay_hs + + DEBUGF 1, "K: AHCI: caps: %x %x, ver: %x, ghc: %x, pi: %x\n", [esi + HBA_MEM.cap], [esi + HBA_MEM.cap2], [esi + HBA_MEM.version], [esi + HBA_MEM.ghc], [esi + HBA_MEM.pi] + + ; TODO: + ; calculate irq line + ; ahciHBA->ghc |= AHCI_GHC_IE; + ; IDT::RegisterInterruptHandler(irq, InterruptHandler); + ; ahciHBA->is = 0xffffffff; + + mov [hd_counter], 0 + xor ebx, ebx +.detect_drives: + cmp ebx, AHCI_MAX_PORTS + jae .end_detect_drives + + ; if port with index ebx is not implemented then go to next + mov ecx, [esi + HBA_MEM.pi] + bt ecx, ebx + jnc .continue_detect_drives + + mov edi, ebx + imul edi, sizeof.HBA_PORT + add edi, HBA_MEM.ports + add edi, esi + ; now edi - base of HBA_MEM.ports[ebx] + + DEBUGF 1, "K: AHCI: port %d, cmd = %x, ssts = %x\n", ebx, [edi + HBA_PORT.command], [edi + HBA_PORT.sata_status] + + ; If port is not idle force it to be idle + mov eax, [edi + HBA_PORT.command] + and eax, (AHCI_HBA_PxCMD_ST or AHCI_HBA_PxCMD_CR or AHCI_HBA_PxCMD_FRE or AHCI_HBA_PxCMD_FR) + test eax, eax + jz @f + + mov eax, edi + DEBUGF 1, "ahci_stop_cmd..\n" + call ahci_stop_cmd +@@: + ; TODO: what is purpose of this block of code ? + ; Reset port, disable slumber and partial state + ; mov [edi + HBA_PORT.sata_control], 0x301 + ; push ebx + ; mov ebx, 5 ; wait 50 ms + ; call delay_hs + ; pop ebx + ; mov [edi + HBA_PORT.sata_control], 0x300 + + ; if(abar->cap & HBA_MEM_CAP_SSS) + ; { + ; abar->ports[i].cmd |= (HBA_PxCMD_SUD | HBA_PxCMD_POD | HBA_PxCMD_ICC); + ; Sleep(10); + ; } + ; rewritten to: + bt [esi + HBA_MEM.cap], 27 ; check Supports Staggered Spin-up bit in capabilities + jnc @f + DEBUGF 1, "Supports Staggered Spin-up, spinning up the port..\n" + or [edi + HBA_PORT.command], (0x0002 or 0x0004 or 0x10000000) + push ebx + mov ebx, 10 ; wait 100 ms + call delay_hs + pop ebx +@@: + ; Clear interrupt status and error status + mov [edi + HBA_PORT.sata_error], 0xFFFFFFFF + mov [edi + HBA_PORT.interrupt_status], 0xFFFFFFFF + + ; ------------------------------------------ + + mov ecx, [edi + HBA_PORT.sata_status] + shr ecx, 8 + and ecx, 0x0F + cmp ecx, AHCI_HBA_PORT_IPM_ACTIVE + jne .continue_detect_drives + + mov ecx, [edi + HBA_PORT.sata_status] + and ecx, AHCI_HBA_PxSSTS_DET + cmp ecx, AHCI_HBA_PxSSTS_DET_PRESENT + jne .continue_detect_drives + + ; DEBUGF 1, "K: AHCI: found drive at port %d, cmd = 0x%x, ssts = 0x%x, signature = 0x%x\n", ebx, [edi + HBA_PORT.command], [edi + HBA_PORT.sata_status], [edi + HBA_PORT.signature] + + mov ecx, ebx + imul ecx, sizeof.PORT_DATA + add ecx, port_data_arr + stdcall ahci_port_rebase, edi, ebx, ecx + + ; DEBUGF 1, "K: AHCI: After REBASING, signature = 0x%x\n", [edi + HBA_PORT.signature] + + ; Determine drive type by checking port signature +.switch_sig: + cmp [edi + HBA_PORT.signature], SATA_SIG_ATA + mov eax, AHCI_DEV_SATA + jz .end_switch_sig + + cmp [edi + HBA_PORT.signature], SATA_SIG_ATAPI + mov eax, AHCI_DEV_SATAPI + jz .end_switch_sig + + cmp [edi + HBA_PORT.signature], SATA_SIG_SEMB + mov eax, AHCI_DEV_SEMB + jz .end_switch_sig + + cmp [edi + HBA_PORT.signature], SATA_SIG_PM + mov eax, AHCI_DEV_PM + jz .end_switch_sig + + DEBUGF 1, "Unknown device signature\n" + mov eax, AHCI_DEV_NULL +.end_switch_sig: + mov [ecx + PORT_DATA.drive_type], al + + DEBUGF 1, "K: AHCI: found drive on port %u: TYPE = %u\n", ebx, [ecx + PORT_DATA.drive_type] + + stdcall ahci_port_identify, ecx + + cmp [ecx + PORT_DATA.drive_type], AHCI_DEV_SATA + jne .after_add_disk ; skip adding disk code + ; register disk in system: + + ;stdcall ahci_read_first_sector, ecx + + push ecx + mov eax, [hd_counter] + inc [hd_counter] + xor edx, edx + mov ecx, 10 + div ecx ; eax = hd_counter / 10, edx = hd_counter % 10 + test eax, eax + jz .concat_one + add al, '0' + mov byte [hd_name + 2], al + add dl, '0' + mov byte [hd_name + 3], dl + jmp .endif1 +.concat_one: + add dl, '0' + mov byte [hd_name + 2], dl +.endif1: + pop ecx + + DEBUGF 1, "adding '%s'\n", hd_name + + push ecx + stdcall disk_add, ahci_callbacks, hd_name, ecx, 0 + pop ecx + test eax, eax + jz .disk_add_fail + push ecx + stdcall disk_media_changed, eax, 1 ; system will scan for partitions on disk + pop ecx + + jmp .after_add_disk + +.disk_add_fail: + DEBUGF 1, "Failed to add disk\n" +.after_add_disk: + +.continue_detect_drives: + inc ebx + jmp .detect_drives + + + +.end_detect_drives: + + + ret +; ------------------------------------------------- + +modelstr rb 42 +; Identify drive on port ; TODO check +; in: pdata - address of PORT_DATA structure +proc ahci_port_identify stdcall, pdata: dword + locals + cmdslot dd ? + cmdheader dd ? + cmdtable dd ? + buf_phys dd ? + buf_virt dd ? + endl + + pushad + + mov esi, [pdata] ; esi - address of PORT_DATA struct of port + mov edi, [esi + PORT_DATA.port] ; edi - address of HBA_PORT struct of port + + mov eax, edi + call ahci_find_cmdslot + + cmp eax, -1 + jne .cmdslot_found + + DEBUGF 1, "No free cmdslot on port %u\n", [esi + PORT_DATA.portno] + jmp .ret + +.cmdslot_found: + mov [cmdslot], eax + ; DEBUGF 1, "Found free cmdslot %u on port %u\n", [cmdslot], [esi + PORT_DATA.portno] + + shl eax, BSF sizeof.HBA_CMD_HDR + add eax, [esi + PORT_DATA.clb] + mov [cmdheader], eax ; address of virtual mapping of command header + mov eax, [cmdslot] + mov eax, [esi + eax*4 + PORT_DATA.ctba_arr] + mov [cmdtable], eax ; address of virtual mapping of command table of command header + + stdcall _memset, eax, 0, sizeof.HBA_CMD_TBL + + call alloc_page + mov [buf_phys], eax + + stdcall map_io_mem, eax, 4096, PG_NOCACHE + PG_SWR ; map to virt memory so we can work with it + mov [buf_virt], eax + + mov eax, [cmdtable] + mov ebx, [buf_phys] + mov dword [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.dba], ebx + mov dword [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.dbau], 0 + and [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], not 0x3FFFFF ; zero out lower 22 bits, they used for byte count + or [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], 512 - 1 ; reason why -1 see in spec on this field + ; or [eax + HBA_CMD_TBL.prdt_entry + HBA_PRDT_ENTRY.flags], 1 shl 31 ; enable interrupt on completion + + mov eax, [cmdheader] + and [eax + HBA_CMD_HDR.flags1], not 0x1F ; zero out lower 5 bits, they will be used for cfl + or [eax + HBA_CMD_HDR.flags1], (sizeof.FIS_REG_H2D / 4) ; set command fis length in dwords + movzx bx, [eax + HBA_CMD_HDR.flags1] + btr bx, 6 ; flag W = 0 + mov [eax + HBA_CMD_HDR.flags1], bl + movzx bx, [eax + HBA_CMD_HDR.flags2] + btr bx, 2 ; flag C = 0 + mov [eax + HBA_CMD_HDR.flags2], bl + mov [eax + HBA_CMD_HDR.prdtl], 1 + + mov eax, [cmdtable] + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.fis_type], FIS_TYPE_REG_H2D + movzx ebx, byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags] + bts ebx, bit_AHCI_H2D_FLAG_CMD ; Set Command bit in H2D FIS. + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags], bl + + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATA_IDENTIFY + cmp [esi + PORT_DATA.drive_type], AHCI_DEV_SATAPI + jne @f + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATAPI_IDENTIFY +@@: + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.device], 0 + + ; Wait on previous command to complete, before issuing new command. + stdcall ahci_port_wait, edi, AHCI_PORT_TIMEOUT + ; DEBUGF 1, "eax = %x\n", eax + ; TODO check eax error value + + mov eax, [cmdslot] + bts [edi + HBA_PORT.command_issue], eax ; Issue the command + + ; Wait for command completion + stdcall ahci_port_cmd_wait, edi, eax;, AHCI_PORT_CMD_TIMEOUT + ; DEBUGF 1, " eax = %x\n", eax + ; TODO check eax error value + + ; DEBUGF 1, "sata_error register = 0x%x\n", [edi + HBA_PORT.sata_error] + + mov esi, [buf_virt] + add esi, 27*2 + mov edi, modelstr + mov ecx, ((46-27)+1)*2 + cld + rep movsb + mov byte [edi], 0 + + stdcall swap_bytes_in_words, modelstr, (46-27)+1 + DEBUGF 1, "IDENTIFICATION RESULT: MODEL = %s\n", modelstr + + mov esi, [buf_virt] + mov eax, [esi + 200] + mov edx, [esi + 200 + 4] + DEBUGF 1, "lba48 mode sector count = 0x%x:%x\n", edx, eax + + mov ebx, [pdata] + mov dword [ebx + PORT_DATA.sector_count], eax + mov dword [ebx + PORT_DATA.sector_count + 4], edx + + shrd eax, edx, 11 ; i.e *512 / 1024 / 1024, 512 - sector size + DEBUGF 1, "disk capacity = %u MiB ", eax + shrd eax, edx, 10 ; / 1024 + DEBUGF 1, "= %u GiB\n", eax +.ret: + popad + ret +endp + +proc ahci_querymedia stdcall, pdata, mediainfo + push ecx edx + mov eax, [mediainfo] + mov edx, [pdata] + mov [eax + DISKMEDIAINFO.Flags], 0 + mov [eax + DISKMEDIAINFO.SectorSize], 512 + mov ecx, dword[edx + PORT_DATA.sector_count] + mov dword [eax + DISKMEDIAINFO.Capacity], ecx + mov ecx, dword[edx + PORT_DATA.sector_count + 4] + mov dword [eax + DISKMEDIAINFO.Capacity + 4], ecx + pop edx ecx + xor eax, eax + ret +endp + +; Read/write sectors +; return value: 0 = success, otherwise = error +proc ahci_rw_sectors stdcall pdata: dword, vbuf: dword, startsector: qword, numsectors: dword, is_write: dword + locals + cmdslot dd ? + cmdheader dd ? + cmdtable dd ? + vbuf_orig dd ? + vbuf_len dd ? + phys_region_start dd ? + new_phys_region_start dd ? + cur_prd dd ? + cur_phys dd ? + dbc dd ? + cur_phys_page dd ? + next_phys_page dd ? + cur_antioffset dd ? + prdt_bytes_total dd ? + endl + + pushad + + DEBUGF AHCI_DBGLVL, " ahci_rw_sectors: buffer = 0x%x, startsector = 0x%x:%x, numsectors = %u, is_write = %u\n", [vbuf], [startsector], [startsector + 4], [numsectors], [is_write]:1 + + mov esi, [pdata] ; esi - address of PORT_DATA struct of port + mov edi, [esi + PORT_DATA.port] ; edi - address of HBA_PORT struct of port + mov eax, edi + call ahci_find_cmdslot + cmp eax, -1 + jne .cmdslot_found + + DEBUGF AHCI_DBGLVL, "No free cmdslot on port %u\n", [esi + PORT_DATA.portno] + jmp .fail + +.cmdslot_found: + mov [cmdslot], eax + DEBUGF AHCI_DBGLVL, "Found free cmdslot %u on port %u\n", [cmdslot], [esi + PORT_DATA.portno] + + shl eax, BSF sizeof.HBA_CMD_HDR + add eax, [esi + PORT_DATA.clb] + mov [cmdheader], eax ; address of virtual mapping of command header + mov eax, [cmdslot] + mov eax, [esi + eax*4 + PORT_DATA.ctba_arr] + mov [cmdtable], eax ; address of virtual mapping of command table of command header + + mov eax, [cmdheader] + and [eax + HBA_CMD_HDR.flags1], not 0x1F ; zero out lower 5 bits, they will be used for cfl + or [eax + HBA_CMD_HDR.flags1], (sizeof.FIS_REG_H2D / 4) ; set command fis length in dwords + movzx bx, [eax + HBA_CMD_HDR.flags1] + btr bx, 6 ; flag W = 0 + cmp [is_write], 1 ; if is_write then set W flag + jne @f + bts bx, 6 +@@: + mov [eax + HBA_CMD_HDR.flags1], bl + movzx bx, [eax + HBA_CMD_HDR.flags2] + btr bx, 2 ; flag C = 0 + mov [eax + HBA_CMD_HDR.flags2], bl + + mov eax, [vbuf] + mov [vbuf_orig], eax + mov ebx, [numsectors] + shl ebx, 9 ; *= 512 + mov [vbuf_len], ebx + DEBUGF AHCI_DBGLVL, "vbuf_len = %u bytes\n", ebx + + mov ebx, [vbuf] + and ebx, 0xFFF + mov eax, [vbuf] + call get_pg_addr + add eax, ebx + mov [phys_region_start], eax + mov [prdt_bytes_total], 0 + mov [cur_prd], 0 +.fill_prdt: + cmp [vbuf_len], 0 + jbe .fill_prdt_end + + mov eax, [vbuf] + call get_pg_addr + mov [cur_phys_page], eax + mov eax, [vbuf] + add eax, 4096 + call get_pg_addr + mov [next_phys_page], eax + mov eax, 4096 + mov ebx, [vbuf] + and ebx, 0xFFF + sub eax, ebx + mov [cur_antioffset], eax + mov eax, [cur_phys_page] + add eax, ebx + mov [cur_phys], eax + +.check_if1: + mov eax, [vbuf_len] + cmp eax, [cur_antioffset] + ja .check_if2 + + mov eax, [cur_phys] + sub eax, [phys_region_start] + add eax, [vbuf_len] + dec eax + mov [dbc], eax + mov eax, [next_phys_page] + mov [new_phys_region_start], eax + jmp .add_prd + +.check_if2: + mov eax, [cur_phys] + add eax, [cur_antioffset] + cmp eax, [next_phys_page] + je .check_if3 + + mov eax, [cur_phys] + add eax, [cur_antioffset] + sub eax, [phys_region_start] + dec eax + mov [dbc], eax + mov eax, [next_phys_page] + mov [new_phys_region_start], eax + jmp .add_prd + +.check_if3: + mov eax, [cur_phys] + add eax, [cur_antioffset] + sub eax, [phys_region_start] + cmp eax, 4*1024*1024 + jb .after_ifs + + mov [dbc], 4*1024*1024 - 1 + mov eax, [phys_region_start] + add eax, 4*1024*1024 + jmp .add_prd + +.after_ifs: + jmp .step_next + +.add_prd: + mov ebx, [cur_prd] + shl ebx, BSF sizeof.HBA_PRDT_ENTRY + add ebx, [cmdtable] + add ebx, HBA_CMD_TBL.prdt_entry ; now ebx - address of 'th prdt_entry + + DEBUGF AHCI_DBGLVL, "Added PRDT entry: dba = 0x%x, dbc = %u\n", [phys_region_start], [dbc] + mov eax, [phys_region_start] + mov [ebx + HBA_PRDT_ENTRY.dba], eax + mov [ebx + HBA_PRDT_ENTRY.dbau], 0 + and [ebx + HBA_PRDT_ENTRY.flags], not 0x3FFFFF ; zero out lower 22 bits, they used for byte count + mov eax, [dbc] + or [ebx + HBA_PRDT_ENTRY.flags], eax + + inc [cur_prd] + mov eax, [dbc] + inc eax + add [prdt_bytes_total], eax + mov eax, [new_phys_region_start] + mov [phys_region_start], eax + cmp [cur_prd], PRDT_MAX_ENTRIES + jne @f + jmp .fill_prdt_end +@@: + +.step_next: + mov eax, [vbuf_len] + cmp eax, [cur_antioffset] + jbe @f + mov eax, [cur_antioffset] +@@: + add [vbuf], eax + sub [vbuf_len], eax + jmp .fill_prdt + +.fill_prdt_end: + + mov eax, [cmdheader] + mov ebx, [cur_prd] + DEBUGF AHCI_DBGLVL, " PRDTL = %u\n", ebx + mov [eax + HBA_CMD_HDR.prdtl], bx + + mov eax, [prdt_bytes_total] + DEBUGF AHCI_DBGLVL, " prdt_bytes_total = %u\n", eax + shr eax, 9 ; /= 512 + mov [numsectors], eax + + mov eax, [cmdtable] + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.fis_type], FIS_TYPE_REG_H2D + movzx ebx, byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags] + bts ebx, bit_AHCI_H2D_FLAG_CMD ; Set Command bit in H2D FIS. + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.flags], bl + + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATA_CMD_READ_DMA_EX + cmp [is_write], 1 + jne @f + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.command], ATA_CMD_WRITE_DMA_EX +@@: + mov ebx, dword [startsector] + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba0], bl + shr ebx, 8 + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba1], bl + shr ebx, 8 + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba2], bl + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.device], 1 shl 6 ; LBA mode + shr ebx, 8 + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba3], bl + mov ebx, dword [startsector + 4] + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba4], bl + shr ebx, 8 + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.lba5], bl + + mov ebx, [numsectors] + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.countl], bl + shr ebx, 8 + mov byte [eax + HBA_CMD_TBL.cfis + FIS_REG_H2D.counth], bl + + ; Wait on previous command to complete, before issuing new command. + stdcall ahci_port_wait, edi, AHCI_PORT_TIMEOUT + + mov eax, [cmdslot] + bts [edi + HBA_PORT.command_issue], eax ; Issue the command + + ; Wait for command completion + stdcall ahci_port_cmd_wait, edi, eax;, AHCI_PORT_CMD_TIMEOUT + + DEBUGF AHCI_DBGLVL, "sata_error register = 0x%x\n", [edi + HBA_PORT.sata_error] + + DEBUGF AHCI_DBGLVL, "R/W completed\n" + +; xor ecx, ecx +; mov esi, [vbuf_orig] +; .print_data: +; cmp ecx, 512 +; jae .end_print_data + +; mov al, byte [esi + ecx] +; mov byte [tmpstr], al +; mov byte [tmpstr + 1], 0 +; DEBUGF 1, "0x%x(%s) ", al:2, tmpstr + +; inc ecx +; jmp .print_data +; .end_print_data: +; DEBUGF 1, "\n" + + popad + ;mov eax, [cmdheader] + ;mov eax, [eax + HBA_CMD_HDR.prdbc] + mov eax, [numsectors] + shl eax, 9 ; *= 512 + ret + +.fail: + popad + xor eax, eax + ret +endp +tmpstr rb 16 + +; Read sectors +; return value: 0 = success, otherwise = error +proc ahci_read stdcall pdata: dword, buffer: dword, startsector: qword, numsectors_ptr:dword + locals + numsectors dd ? + endl + + pushad + + mov ecx, ahci_mutex + call mutex_lock + + mov eax, [numsectors_ptr] + mov eax, [eax] + mov [numsectors], eax + DEBUGF AHCI_DBGLVL, " ahci_read: buffer = 0x%x, startsector = 0x%x:%x, numsectors = %u\n", [buffer], [startsector], [startsector + 4], eax + + xor ecx, ecx ; how many sectors have been read +.read_loop: + cmp ecx, [numsectors] + jae .read_loop_end + + ; mov eax, [buffer] + ; call get_pg_addr + ; DEBUGF 1, "buf phys = 0x%x\n", eax + ; mov eax, [buffer] + ; add eax, 4096 + ; call get_pg_addr + ; DEBUGF 1, "buf + 4096 phys = 0x%x\n", eax + + mov ebx, [numsectors] + sub ebx, ecx + ; DEBUGF 1, "buffer = 0x%x\n", [buffer] + stdcall ahci_rw_sectors, [pdata], [buffer], dword [startsector], dword [startsector + 4], ebx, 0 + ;; TODO check if eax == 0 ? + + DEBUGF AHCI_DBGLVL, " EAX = 0x%x\n", eax + + add [buffer], eax + shr eax, 9 ; /= 512 + add ecx, eax + add dword [startsector], eax + adc dword [startsector + 4], 0 + + jmp .read_loop +.read_loop_end: + + mov ecx, ahci_mutex + call mutex_unlock + + popad + xor eax, eax + ret +endp + +; Write sectors +; return value: 0 = success, otherwise = error +proc ahci_write stdcall pdata: dword, buffer: dword, startsector: qword, numsectors_ptr:dword + locals + numsectors dd ? + endl + + pushad + + mov ecx, ahci_mutex + call mutex_lock + + mov eax, [numsectors_ptr] + mov eax, [eax] + mov [numsectors], eax + DEBUGF AHCI_DBGLVL, " ahci_write: buffer = 0x%x, startsector = 0x%x:%x, numsectors = %u\n", [buffer], [startsector], [startsector + 4], eax + + xor ecx, ecx ; how many sectors have been read +.write_loop: + cmp ecx, [numsectors] + jae .write_loop_end + + mov ebx, [numsectors] + sub ebx, ecx + stdcall ahci_rw_sectors, [pdata], [buffer], dword [startsector], dword [startsector + 4], ebx, 1 + ;; TODO check if eax == 0 ? + + DEBUGF AHCI_DBGLVL, " EAX = 0x%x\n", eax + + add [buffer], eax + shr eax, 9 ; /= 512 + add ecx, eax + add dword [startsector], eax + adc dword [startsector + 4], 0 + + jmp .write_loop +.write_loop_end: + + mov ecx, ahci_mutex + call mutex_unlock + + popad + xor eax, eax + ret +endp + +; Start command engine +; in: eax - address of HBA_PORT structure +ahci_start_cmd: +.wait_cr: ; Wait until CR (bit15) is cleared + bt [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_CR + jc .wait_cr + + ; Set FRE (bit4) and ST (bit0) + bts [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FRE + bts [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_ST + ; maybe here call ahci flush cmd ? TODO (see seakernel) + ret + +; Stop command engine +; in: eax - address of HBA_PORT structure +ahci_stop_cmd: + btr [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_ST ; Clear ST (bit0) + btr [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FRE ; Clear FRE (bit4) +.wait_fr_cr: ; Wait until FR (bit14), CR (bit15) are cleared + bt [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_FR + jc .wait_fr_cr + bt [eax + HBA_PORT.command], bit_AHCI_HBA_PxCMD_CR + jc .wait_fr_cr + + ret + +; waits until the port is no longer busy before issuing a new command +; in: [port] - address of HBA_PORT structure +; [timeout] - timeout (in iterations) +; out: eax = 0 if success, 1 if timeout expired +proc ahci_port_wait stdcall, port: dword, timeout: dword + push ebx ecx + mov ebx, [port] + xor ecx, ecx +.wait: + cmp ecx, [timeout] + jae .wait_end + mov eax, [ebx + HBA_PORT.task_file_data] + and eax, ATA_DEV_BUSY or ATA_DEV_DRQ + test eax, eax + jz .wait_end + inc ecx + jmp .wait +.wait_end: + xor eax, eax + DEBUGF AHCI_DBGLVL, "port wait counter = %u\n", ecx + cmp ecx, [timeout] ; if they equal it means port is hung + setz al + pop ecx ebx + ret +endp + + +; Wait for command completion +; in: [port] - address of HBA_PORT structure +; [cmdslot] - number of command slot +; out: eax = 0 if success, 1 if error +proc ahci_port_cmd_wait stdcall, port: dword, cmdslot: dword ;, timeout: dword + push ebx ecx edx + mov ebx, [port] + mov edx, [cmdslot] + xor eax, eax + xor ecx, ecx +.wait: + bt [ebx + HBA_PORT.command_issue], edx + jnc .wait_end + bt [ebx + HBA_PORT.interrupt_status], bit_AHCI_HBA_PxIS_TFES ; check for Task File Error + jc .error + inc ecx + jmp .wait +.wait_end: + DEBUGF AHCI_DBGLVL, "port cmd wait counter = %u\n", ecx + bt [ebx + HBA_PORT.interrupt_status], bit_AHCI_HBA_PxIS_TFES ; check for Task File Error + jc .error + jmp .ret +.error: + mov eax, 1 +.ret: + pop edx ecx ebx + ret +endp + +; ; The commands may not take effect until the command +; ; register is read again by software, because reasons. +; ; in: eax - address of HBA_PORT structure +; ; out: eax - command register value +; ahci_flush_cmd: +; mov eax, [eax + HBA_PORT.command] +; ret + +; ; Send command to port +; ; in: eax - address of HBA_PORT structure +; ; ebx - index of command slot +; ahci_send_cmd: +; push ecx +; mov [eax + HBA_PORT.interrupt_status], 0xFFFFFFFF + +; mov cl, bl +; mov [eax + HBA_PORT.command_issue], 1 +; shl [eax + HBA_PORT.command_issue], cl + +; call ahci_flush_cmd +; pop ecx +; ret + +; --------------------------------------------------------------------------- +; in: port - address of HBA_PORT structure +; portno - port index (0..31) +; pdata - address of PORT_DATA structure +proc ahci_port_rebase stdcall, port: dword, portno: dword, pdata: dword + locals + phys_page1 dd ? + virt_page1 dd ? + phys_page23 dd ? + virt_page23 dd ? + tmp dd ? + endl + + pushad + + DEBUGF 1, "Rebasing port %u\n", [portno] + + mov eax, [port] + call ahci_stop_cmd + + ; Command list entry size = 32 + ; Command list entry maxim count = 32 + ; Command list maxim size = 32*32 = 1K per port + call alloc_page + mov [phys_page1], eax + + stdcall map_io_mem, eax, 4096, PG_NOCACHE + PG_SWR ; map to virt memory so we can work with it + mov [virt_page1], eax + + mov esi, [port] + mov ebx, [phys_page1] + mov [esi + HBA_PORT.command_list_base_l], ebx ; set the command list base + mov [esi + HBA_PORT.command_list_base_h], 0 ; zero upper 32 bits of addr cause we are 32 bit os + + mov edi, [pdata] + mov ebx, [virt_page1] + mov [edi + PORT_DATA.clb], ebx ; set pdata->clb + + mov eax, [port] + mov [edi + PORT_DATA.port], eax ; set pdata->port + mov eax, [portno] ; set pdata->portno + mov [edi + PORT_DATA.portno], eax + + stdcall _memset, ebx, 0, 1024 ; zero out the command list + + ; FIS entry size = 256 bytes per port + mov eax, [phys_page1] + add eax, 1024 + mov [esi + HBA_PORT.fis_base_l], eax + mov [esi + HBA_PORT.fis_base_h], 0 + + mov eax, [virt_page1] + add eax, 1024 + mov [edi + PORT_DATA.fb], eax ; set pdata->fb + stdcall _memset, eax, 0, 256 ; zero out + + stdcall alloc_pages, 32*(64 + 16 + 48 + PRDT_MAX_ENTRIES*16)/4096 + mov [phys_page23], eax + stdcall map_io_mem, eax, 32*(64 + 16 + 48 + PRDT_MAX_ENTRIES*16), PG_NOCACHE + PG_SWR + mov [virt_page23], eax + + ; Command table size = N*32 per port + mov edx, [edi + PORT_DATA.clb] ; cmdheader array base + xor ecx, ecx + +.for1: + cmp ecx, 32 + jae .for1_end + + mov ebx, ecx + shl ebx, BSF sizeof.HBA_CMD_HDR + add ebx, edx ; ebx = cmdheader[ecx] + + mov [ebx + HBA_CMD_HDR.prdtl], PRDT_MAX_ENTRIES ; prdt entries per command table + + ; bytes per command table = 64+16+48+PRDT_MAX_ENTRIES*16 = N + + push edx + + ; cmdheader[ecx].ctba = phys_page23 + ecx*N + mov [ebx + HBA_CMD_HDR.ctba], ecx + mov edx, [ebx + HBA_CMD_HDR.ctba] + imul edx, (64+16+48+PRDT_MAX_ENTRIES*16) ; *= N + mov [ebx + HBA_CMD_HDR.ctba], edx + mov eax, [ebx + HBA_CMD_HDR.ctba] + mov edx, [phys_page23] + add [ebx + HBA_CMD_HDR.ctba], edx + + add eax, [virt_page23] + mov [tmp], eax ; tmp = virt_page23 + ecx*N + lea eax, [ecx*4 + edi + PORT_DATA.ctba_arr] ; eax = pdata->ctba_arr[ecx] + mov edx, [tmp] + mov [eax], edx ; pdata->ctba_arr[ecx] = virt_page23 + ecx*N + + pop edx + + mov [ebx + HBA_CMD_HDR.ctbau], 0 + stdcall _memset, [eax], 0, 64+16+48+PRDT_MAX_ENTRIES*16 ; zero out + + inc ecx + jmp .for1 +.for1_end: + + mov eax, [port] + call ahci_start_cmd + + DEBUGF 1, "End rebasing port %u\n", [portno] + popad + ret +endp + +; ----------------------------------------------------------- ; TODO check +; Find a free command list slot +; in: eax - address of HBA_PORT structure +; out: eax - if not found -1, else slot index +ahci_find_cmdslot: + push ebx ecx edx esi + ; If not set in SACT and CI, the slot is free + mov ebx, [eax + HBA_PORT.sata_active] + or ebx, [eax + HBA_PORT.command_issue] ; ebx = slots + + mov esi, [ahci_controller + AHCI_DATA.abar] + mov edx, [esi + HBA_MEM.cap] + shr edx, 8 + and edx, 0xf + ; DEBUGF 1, "Number of Command Slots on each port = %u\n", edx + xor ecx, ecx +.for1: + cmp ecx, edx + jae .for1_end + + ; if ((slots&1) == 0) return i; + bt ebx, 0 + jc .cont1 + + mov eax, ecx + jmp .ret + +.cont1: + shr ebx, 1 + inc ecx + jmp .for1 +.for1_end: + DEBUGF 1, "Cannot find free command list entry\n" + mov eax, -1 +.ret: + pop esi edx ecx ebx + ret + + +proc _memset stdcall, dest:dword, val:byte, cnt:dword ; doesnt clobber any registers + ;DEBUGF DBG_INFO, "memset(%x, %u, %u)\n", [dest], [val], [cnt] + push eax ecx edi + mov edi, dword [dest] + mov al, byte [val] + mov ecx, dword [cnt] + rep stosb + pop edi ecx eax + ret +endp + +; Swaps byte order in words +; base - address of first word +; len - how many words to swap bytes in +; doesnt clobber any registers +proc swap_bytes_in_words stdcall, base: dword, len: dword + push eax ebx ecx + xor ecx, ecx + mov ebx, [base] +.loop: + cmp ecx, [len] + jae .loop_end + mov ax, word [ebx + ecx*2] + xchg ah, al + mov word [ebx + ecx*2], ax + inc ecx + jmp .loop +.loop_end: + pop ecx ebx eax + ret +endp diff --git a/kernel/branches/kolibri-lldw/blkdev/bd_drv.inc b/kernel/branches/kolibri-lldw/blkdev/bd_drv.inc new file mode 100644 index 000000000..1e0580329 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/bd_drv.inc @@ -0,0 +1,301 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Disk access through BIOS +iglobal +align 4 +bd_callbacks: + dd bd_callbacks.end - bd_callbacks ; strucsize + dd 0 ; no close function + dd 0 ; no closemedia function + dd bd_querymedia + dd bd_read_interface + dd bd_write_interface + dd 0 ; no flush function + dd 0 ; use default cache size +.end: +endg + +uglobal +bios_hdpos dd 0 +bios_cur_sector dd ? +bios_read_len dd ? +cache_chain_ptr dd ? +int13_regs_in rb sizeof.v86_regs +int13_regs_out rb sizeof.v86_regs +cache_chain_size db ? +endg + +struct BiosDiskData + DriveNumber db ? + IRQ db ? + ATADEVbit dw ? + SectorSize dd ? + Capacity dq ? +ends +;----------------------------------------------------------------- +proc bd_read_interface stdcall uses edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer for data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really read +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and edi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov eax, dword [startsector] + mov edi, [buffer] +; 4. Worker procedures take one sectors per time, so loop over all sectors to read. +.sectors_loop: + call bd_read + cmp [hd_error], 0 + jnz .fail + mov ecx, [numsectors] + inc dword [ecx] ; one more sector is read + dec [sectors_todo] + jz .done + inc eax + jnz .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp +;----------------------------------------------------------------- +proc bd_write_interface stdcall uses esi edi, \ + userdata, buffer, startsector:qword, numsectors + ; userdata = old [hdpos] = 80h + index in NumBiosDisks + ; buffer = pointer to buffer with data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really written +locals +sectors_todo dd ? +endl +; 1. Initialize number of sectors: get number of requested sectors +; and say that no sectors were read yet. + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; 2. Acquire the global lock. + mov ecx, ide_mutex + call mutex_lock +; 3. Convert parameters to the form suitable for worker procedures. +; Underlying procedures do not know about 64-bit sectors. +; Worker procedures use global variables and esi for [buffer]. + cmp dword [startsector+4], 0 + jnz .fail + and [hd_error], 0 + mov eax, [userdata] + mov [hdpos], eax + mov esi, [buffer] + lea edi, [startsector] + mov [cache_chain_ptr], edi +; 4. Worker procedures take max 16 sectors per time, +; loop until all sectors will be processed. +.sectors_loop: + mov ecx, 16 + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [cache_chain_size], cl + call bd_write_cache_chain + cmp [hd_error], 0 + jnz .fail + movzx ecx, [cache_chain_size] + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .done + add [edi], ecx + jc .fail + shl ecx, 9 + add esi, ecx + jmp .sectors_loop +; 5. Loop is done, either due to error or because everything is done. +; Release the global lock and return the corresponding status. +.fail: + mov ecx, ide_mutex + call mutex_unlock + or eax, -1 + ret +.done: + mov ecx, ide_mutex + call mutex_unlock + xor eax, eax + ret +endp +;----------------------------------------------------------------- +proc bd_querymedia stdcall, hd_data, mediainfo + mov edx, [mediainfo] + mov eax, [hd_data] + lea eax, [(eax-80h)*4] + lea eax, [BiosDisksData+eax*4] + mov [edx+DISKMEDIAINFO.Flags], 0 + mov ecx, [eax+BiosDiskData.SectorSize] + mov [edx+DISKMEDIAINFO.SectorSize], ecx + mov ecx, dword [eax+BiosDiskData.Capacity+0] + mov eax, dword [eax+BiosDiskData.Capacity+4] + mov dword [edx+DISKMEDIAINFO.Capacity+0], ecx + mov dword [edx+DISKMEDIAINFO.Capacity+4], eax + xor eax, eax + ret +endp +;----------------------------------------------------------------- +bd_read: + push eax + push edx + mov edx, [bios_hdpos] + cmp edx, [hdpos] + jne .notread + mov edx, [bios_cur_sector] + cmp eax, edx + jb .notread + add edx, [bios_read_len] + dec edx + cmp eax, edx + ja .notread + sub eax, [bios_cur_sector] + shl eax, 9 + add eax, (OS_BASE+0x99000) + push ecx esi + mov esi, eax + mov ecx, 512/4 + cld + rep movsd + pop esi ecx + pop edx + pop eax + ret +.notread: + push ecx + mov dl, 42h + mov ecx, 16 + call int13_call + pop ecx + test eax, eax + jnz .v86err + test edx, edx + jz .readerr + mov [bios_read_len], edx + mov edx, [hdpos] + mov [bios_hdpos], edx + pop edx + pop eax + mov [bios_cur_sector], eax + jmp bd_read +.readerr: +.v86err: + pop edx + pop eax + mov [hd_error], 1 + jmp hd_read_error +;----------------------------------------------------------------- +bd_write_cache_chain: + pusha + mov edi, OS_BASE + 0x99000 + movzx ecx, [cache_chain_size] + push ecx + shl ecx, 9-2 + rep movsd + pop ecx + mov dl, 43h + mov eax, [cache_chain_ptr] + mov eax, [eax] + call int13_call + test eax, eax + jnz .v86err + cmp edx, ecx + jnz .writeerr + popa + ret +.v86err: +.writeerr: + popa + mov [hd_error], 1 + jmp hd_write_error +;----------------------------------------------------------------- +int13_call: +; Because this code uses fixed addresses, +; it can not be run simultaniously by many threads. +; In current implementation it is protected by common mutex 'ide_status' + mov word [OS_BASE + 510h], 10h ; packet length + mov word [OS_BASE + 512h], cx ; number of sectors + mov dword [OS_BASE + 514h], 99000000h ; buffer 9900:0000 + mov dword [OS_BASE + 518h], eax + and dword [OS_BASE + 51Ch], 0 + push ebx ecx esi edi + mov ebx, int13_regs_in + mov edi, ebx + mov ecx, sizeof.v86_regs/4 + xor eax, eax + rep stosd + mov byte [ebx+v86_regs.eax+1], dl + mov eax, [hdpos] + lea eax, [(eax-80h)*4] + lea eax, [BiosDisksData+eax*4] + mov dl, [eax] + mov byte [ebx+v86_regs.edx], dl + movzx edx, byte [eax+1] +; mov dl, 5 + test edx, edx + jnz .hasirq + dec edx + jmp @f +.hasirq: + pushad + stdcall enable_irq, edx + popad +@@: + mov word [ebx+v86_regs.esi], 510h + mov word [ebx+v86_regs.ss], 9000h + mov word [ebx+v86_regs.esp], 09000h + mov word [ebx+v86_regs.eip], 500h + mov [ebx+v86_regs.eflags], 20200h + mov esi, [sys_v86_machine] + mov ecx, 0x502 + push fs + call v86_start + pop fs + and [bios_hdpos], 0 + pop edi esi ecx ebx + movzx edx, byte [OS_BASE + 512h] + test byte [int13_regs_out+v86_regs.eflags], 1 + jnz @f + mov edx, ecx +@@: + ret diff --git a/kernel/branches/kolibri-lldw/blkdev/cd_drv.inc b/kernel/branches/kolibri-lldw/blkdev/cd_drv.inc new file mode 100644 index 000000000..8e8a5b1c0 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/cd_drv.inc @@ -0,0 +1,1218 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +;----------------------------------------------------------------------------- +;********************************************************** +; Direct work with CD (ATAPI) device +;********************************************************** +; Author of a part of the source code - Kulakov Vladimir Gennadievich +; Adaptation, revision and development - Mario79, + +; Maximum number of repeats of a read operation +MaxRetr = 10 +; Maximum waiting time for ready to receive a command +; (in ticks) +BSYWaitTime = 1000 ;2 +NoTickWaitTime = 0xfffff +CDBlockSize = 2048 +;******************************************** +;* READING SECTOR WITH REPEATS * +;* Repeated reads on failures * +;******************************************** +ReadCDWRetr: +;----------------------------------------------------------- +; input : eax = block to read +; ebx = destination +;----------------------------------------------------------- + pushad + mov eax, [CDSectorAddress] + mov ebx, [CDDataBuf_pointer] + call cd_calculate_cache + xor edi, edi + add esi, 8 + inc edi +;-------------------------------------- +align 4 +.hdreadcache: + cmp [esi], eax ; correct sector + je .yeshdcache + + add esi, 8 + inc edi + dec ecx + jnz .hdreadcache + + call find_empty_slot_CD_cache ; ret in edi + + push edi + push eax + call cd_calculate_cache_2 + shl edi, 11 + add edi, eax + mov [CDDataBuf_pointer], edi + pop eax + pop edi + + call ReadCDWRetr_1 + cmp [DevErrorCode], 0 + jne .exit + + mov [CDDataBuf_pointer], ebx + call cd_calculate_cache_1 + lea esi, [edi*8+esi] + mov [esi], eax ; sector number +;-------------------------------------- +.yeshdcache: + mov esi, edi + shl esi, 11 ;9 + push eax + call cd_calculate_cache_2 + add esi, eax + pop eax + mov edi, ebx ;[CDDataBuf_pointer] + mov ecx, 512 ;/4 + cld + rep movsd ; move data +;-------------------------------------- +.exit: + popad + ret +;----------------------------------------------------------------------------- +ReadCDWRetr_1: + pushad +; Loop until the command is successful or the number of attempts is over + mov ecx, MaxRetr +;-------------------------------------- +align 4 +@@NextRetr: +; Send a command +;************************************************* +;* FULL READ OF COMPACT DISK SECTOR * +;* User data, subchannel * +;* information and control information are read * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disc number on channel; * +;* CDSectorAddress - address of reading sector. * +;* The data is read into the CDDataBuf array. * +;************************************************* +;ReadCD: + push ecx +; Flush the packet command buffer + call clear_packet_buffer +; Generate a packet command to read a data sector +; Set the command code Read CD + mov [PacketCommand], byte 0x28 ;0xBE +; Set the sector address + mov ax, word [CDSectorAddress+2] + xchg al, ah + mov word [PacketCommand+2], ax + mov ax, word [CDSectorAddress] + xchg al, ah + mov word [PacketCommand+4], ax +; Set the number of sectors to read + mov [PacketCommand+8], byte 1 +; Send a command + call SendPacketDatCommand + pop ecx + + test eax, eax + jz @@End_4 + + or ecx, ecx ;{SPraid.simba} (for cd load) + jz @@End_4 + + dec ecx + + cmp [timer_ticks_enable], 0 + jne @f + + mov eax, NoTickWaitTime +;-------------------------------------- +align 4 +.wait: + dec eax + jz @@NextRetr + + jmp .wait +;-------------------------------------- +align 4 +@@: + loop @@NextRetr +;-------------------------------------- +@@End_4: + mov dword [DevErrorCode], eax + popad + ret +;----------------------------------------------------------------------------- +; General purpose procedures to execute packet commands in PIO Mode +; Maximum allowable waiting time for the device to respond to a packet command (in ticks) +;----------------------------------------------------------------------------- +MaxCDWaitTime = 1000 ;200 ;10 seconds +uglobal +; Memory area for generating a packet command +PacketCommand: + rb 12 ;DB 12 DUP (?) +; address of reading data sector +CDSectorAddress: dd ? +; Start time of the next disk operation +TickCounter_1 dd 0 +; Time to start waiting for device readiness +WURStartTime dd 0 +; pointer to buffer to read data into +CDDataBuf_pointer dd 0 +endg +;----------------------------------------------------------------------------- +;**************************************************** +;* SEND TO ATAPI DEVICE PACKET COMMAND, * +;* THAT MEANS TRASMIT ONE DATA SECTOR OF SIZE * +;* 2048 BYTE FROM DEVICE TO HOST * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;* PacketCommand - 12-byte command packet; * +;* CDBlockSize - size of receiving data block. * +; return eax DevErrorCode +;**************************************************** +SendPacketDatCommand: + xor eax, eax +; Set CHS mode + mov byte [ATAAddressMode], al +; Send ATA command to send packet command + mov byte [ATAFeatures], al + mov byte [ATASectorCount], al + mov byte [ATASectorNumber], al +; Load the size of the sending block + mov [ATAHead], al + mov [ATACylinder], CDBlockSize + mov [ATACommand], 0xA0 + call SendCommandToHDD_1 + test eax, eax + jnz @@End_8 ; finish, saving the error code +; Waiting for the drive to be ready to receive a packet command + mov dx, [ATABasePortAddr] + add dx, 7 ; port 1x7h + mov ecx, NoTickWaitTime +;-------------------------------------- +align 4 +@@WaitDevice0: + cmp [timer_ticks_enable], 0 + jne @f + + dec ecx + jz @@Err1_1 + + jmp .test +;-------------------------------------- +align 4 +@@: + call change_task + ; Check command execution time + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, BSYWaitTime + ja @@Err1_1 ; time out error + ; Check readiness +;-------------------------------------- +align 4 +.test: + in al, dx + test al, 0x80 ; BSY signal state + jnz @@WaitDevice0 + + test al, 1 ; ERR signal state + jnz @@Err6 + + test al, 0x8 ; DRQ signal state + jz @@WaitDevice0 +; Send a packet command + cli + mov dx, [ATABasePortAddr] + mov ax, [PacketCommand] + out dx, ax + mov ax, [PacketCommand+2] + out dx, ax + mov ax, [PacketCommand+4] + out dx, ax + mov ax, [PacketCommand+6] + out dx, ax + mov ax, [PacketCommand+8] + out dx, ax + mov ax, [PacketCommand+10] + out dx, ax + sti +; Waiting for data to be ready + mov dx, [ATABasePortAddr] + add dx, 7 ; port 1x7h + mov ecx, NoTickWaitTime +;-------------------------------------- +align 4 +@@WaitDevice1: + cmp [timer_ticks_enable], 0 + jne @f + + dec ecx + jz @@Err1_1 + + jmp .test_1 +;-------------------------------------- +align 4 +@@: + call change_task + ; Check command execution time + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, MaxCDWaitTime + ja @@Err1_1 ; time out error + ; Check readiness +;-------------------------------------- +align 4 +.test_1: + in al, dx + test al, 0x80 ; BSY signal state + jnz @@WaitDevice1 + + test al, 1 ; ERR signal state + jnz @@Err6_temp + + test al, 0x8 ; DRQ signal state + jz @@WaitDevice1 +; Receive data block from controller + mov edi, [CDDataBuf_pointer] + ; Load controller's data register address + mov dx, [ATABasePortAddr] + ; Load the block size in bytes into the counter + xor ecx, ecx + mov cx, CDBlockSize + ; Calculate block size in 16-bit words + shr cx, 1 ; divide block size by 2 + ; Receive data block + cli + cld + rep insw + sti +;-------------------------------------- +; Successful completion of data receive +@@End_8: + xor eax, eax + ret +;-------------------------------------- +; Write error code +@@Err1_1: + xor eax, eax + inc eax + ret +;-------------------------------------- +@@Err6_temp: + mov eax, 7 + ret +;-------------------------------------- +@@Err6: + mov eax, 6 + ret +;----------------------------------------------------------------------------- +;*********************************************** +;* SEND TO ATAPI DEVICE PACKET COMMAND, * +;* THAT DOESNT MEAN TRANSMIT DATA * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;* PacketCommand - 12-byte command packet. * +;*********************************************** +SendPacketNoDatCommand: + pushad + xor eax, eax +; Set CHS mode + mov byte [ATAAddressMode], al +; Send ATA command to send packet command + mov byte [ATAFeatures], al + mov byte [ATASectorCount], al + mov byte [ATASectorNumber], al + mov word [ATACylinder], ax + mov byte [ATAHead], al + mov [ATACommand], 0xA0 + call SendCommandToHDD_1 + test eax, eax + jnz @@End_9 ; finish, saving the error code +; Waiting for the drive to be ready to receive a packet command + mov dx, [ATABasePortAddr] + add dx, 7 ; port 1x7h +;-------------------------------------- +align 4 +@@WaitDevice0_1: + call change_task + ; Check waiting time + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, BSYWaitTime + ja @@Err1_3 ; time out error + ; Check readiness + in al, dx + test al, 0x80 ; BSY signal state + jnz @@WaitDevice0_1 + + test al, 1 ; ERR signal state + jnz @@Err6_1 + + test al, 0x8 ; DRQ signal state + jz @@WaitDevice0_1 +; Send packet command +; cli + mov dx, [ATABasePortAddr] + mov ax, word [PacketCommand] + out dx, ax + mov ax, word [PacketCommand+2] + out dx, ax + mov ax, word [PacketCommand+4] + out dx, ax + mov ax, word [PacketCommand+6] + out dx, ax + mov ax, word [PacketCommand+8] + out dx, ax + mov ax, word [PacketCommand+10] + out dx, ax +; sti + cmp [ignore_CD_eject_wait], 1 + je @@clear_DEC +; Waiting for confirmation of command receive + mov dx, [ATABasePortAddr] + add dx, 7 ; port 1x7h +;-------------------------------------- +align 4 +@@WaitDevice1_1: + call change_task + ; Check command execution time + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, MaxCDWaitTime + ja @@Err1_3 ; time out error + ; Wait for device release + in al, dx + test al, 0x80 ; BSY signal state + jnz @@WaitDevice1_1 + + test al, 1 ; ERR signal state + jnz @@Err6_1 + + test al, 0x40 ; DRDY signal state + jz @@WaitDevice1_1 +;-------------------------------------- +@@clear_DEC: + and [DevErrorCode], 0 + popad + ret +;-------------------------------------- +; Write error code +@@Err1_3: + xor eax, eax + inc eax + jmp @@End_9 +;-------------------------------------- +@@Err6_1: + mov eax, 6 +;-------------------------------------- +@@End_9: + mov [DevErrorCode], eax + popad + ret +;----------------------------------------------------------------------------- +;**************************************************** +;* SEND COMMAND TO GIVEN DISK * +;* Input parameters are passed through the global * +;* variables: * +;* ChannelNumber - channel number (1 or 2); * +;* DiskNumber - disk number (0 or 1); * +;* ATAFeatures - "features"; * +;* ATASectorCount - sector count; * +;* ATASectorNumber - initial sector number; * +;* ATACylinder - initial cylinder number; * +;* ATAHead - initial head number; * +;* ATAAddressMode - addressing mode (0-CHS, 1-LBA); * +;* ATACommand - command code. * +;* If the function finished successfully: * +;* in ATABasePortAddr - base address of HDD; * +;* in DevErrorCode - zero. * +;* If error has occured then in DevErrorCode will * +;* be the error code. * +;**************************************************** +SendCommandToHDD_1: +; Check the addressing mode code + cmp [ATAAddressMode], 1 + ja @@Err2_4 +; Check the channel number correctness + movzx ebx, [ChannelNumber] + dec ebx + cmp ebx, 1 + ja @@Err3_4 +; Set the base address + shl ebx, 2 + mov eax, [cdpos] + dec eax + shr eax, 2 + imul eax, sizeof.IDE_DATA + add eax, IDE_controller_1 + add eax, ebx + mov ax, [eax+IDE_DATA.BAR0_val] + mov [ATABasePortAddr], ax +; Waiting for HDD ready to receive a command + ; Choose desired disk + mov dx, [ATABasePortAddr] + add dx, 6 ; address of the heads register + mov al, [DiskNumber] + cmp al, 1 ; check the disk number + ja @@Err4_4 + + shl al, 4 + or al, 10100000b + out dx, al + ; Waiting for disk ready + inc dx + mov eax, [timer_ticks] + mov [TickCounter_1], eax + mov ecx, NoTickWaitTime +;-------------------------------------- +align 4 +@@WaitHDReady_2: + cmp [timer_ticks_enable], 0 + jne @f + + dec ecx + jz @@Err1_4 + + jmp .test +;-------------------------------------- +align 4 +@@: + call change_task + ; Check waiting time + mov eax, [timer_ticks] + sub eax, [TickCounter_1] + cmp eax, BSYWaitTime ;300 ; wait for 3 seconds + ja @@Err1_4 ; time out error +;-------------------------------------- +align 4 +.test: + in al, dx ; Read the state register + ; Check the state of BSY signal + test al, 0x80 + jnz @@WaitHDReady_2 + ; Check the state of DRQ signal + test al, 0x8 + jnz @@WaitHDReady_2 +; load command to controller's registers + cli + mov dx, [ATABasePortAddr] + inc dx ; "features" register + mov al, [ATAFeatures] + out dx, al + inc dx ; sector counter + mov al, [ATASectorCount] + out dx, al + inc dx ; sector number register + mov al, [ATASectorNumber] + out dx, al + inc dx ; cylinder number (low byte) + mov ax, [ATACylinder] + out dx, al + inc dx ; cylinder number (high byte) + mov al, ah + out dx, al + inc dx ; head number / disk number + mov al, [DiskNumber] + shl al, 4 + cmp [ATAHead], 0xF ; check head number + ja @@Err5_4 + + or al, [ATAHead] + or al, 10100000b + mov ah, [ATAAddressMode] + shl ah, 6 + or al, ah + out dx, al +; Send command + mov al, [ATACommand] + inc dx ; command register + out dx, al + sti +;-------------------------------------- +@@End_10: + xor eax, eax + ret +;-------------------------------------- +; Write error code +@@Err1_4: + xor eax, eax + inc eax + ret +;-------------------------------------- +@@Err2_4: + mov eax, 2 + ret +;-------------------------------------- +@@Err3_4: + mov eax, 3 + ret +;-------------------------------------- +@@Err4_4: + mov eax, 4 + ret +;-------------------------------------- +@@Err5_4: + mov eax, 5 + ret +;----------------------------------------------------------------------------- +;************************************************* +;* WAIT FOR THE DEVICE IS READY FOR WORK * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +WaitUnitReady: + pusha +; Remember the peration start time + mov eax, [timer_ticks] + mov [WURStartTime], eax +; Clear the packet command buffer + call clear_packet_buffer +; Generate TEST UNIT READY command + mov [PacketCommand], word 0 +; waiting loop for device readiness + mov ecx, NoTickWaitTime +;-------------------------------------- +align 4 +@@SendCommand: + ; Send readiness check command + call SendPacketNoDatCommand + cmp [timer_ticks_enable], 0 + jne @f + + cmp [DevErrorCode], 0 + je @@End_11 + + dec ecx + jz .Error + + jmp @@SendCommand +;-------------------------------------- +align 4 +@@: + call change_task + ; Check the error code + cmp [DevErrorCode], 0 + je @@End_11 + ; Check waiting time + mov eax, [timer_ticks] + sub eax, [WURStartTime] + cmp eax, MaxCDWaitTime + jb @@SendCommand +;-------------------------------------- +.Error: + ; time out error + mov [DevErrorCode], 1 +;-------------------------------------- +@@End_11: + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +;* FORBID DISK CHANGE * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +prevent_medium_removal: + pusha +; Clear the packet command buffer + call clear_packet_buffer +; Set command code + mov [PacketCommand], byte 0x1E +; Set "Forbid" code + mov [PacketCommand+4], byte 11b +; Send command + call SendPacketNoDatCommand + mov eax, ATAPI_IDE0_lock + add eax, [cdpos] + dec eax + mov [eax], byte 1 + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +;* ALLOW DISK CHANGE * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +allow_medium_removal: + pusha +; Clear the packet command buffer + call clear_packet_buffer +; Set command code + mov [PacketCommand], byte 0x1E +; unset "Forbid" code + mov [PacketCommand+4], byte 0 +; Send command + call SendPacketNoDatCommand + mov eax, ATAPI_IDE0_lock + add eax, [cdpos] + dec eax + mov [eax], byte 0 + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +;* LOAD DISK TO THE DRIVE * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +LoadMedium: + pusha +; Clear the packet command buffer + call clear_packet_buffer +; Generate START/STOP UNIT command + ; Set command code + mov [PacketCommand], word 0x1B + ; Set disk loading operation + mov [PacketCommand+4], word 00000011b +; Send command + call SendPacketNoDatCommand + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +;* REMOVE THE DISK FROM THE DRIVE * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +EjectMedium: + pusha +; Clear the packet command buffer + call clear_packet_buffer +; Generate START/STOP UNIT command + ; Set command code + mov [PacketCommand], word 0x1B + ; Set the operation to eject disk + mov [PacketCommand+4], word 00000010b +; Send command + call SendPacketNoDatCommand + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +;* Check the event of pressing the eject button * +;* * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;************************************************* +proc check_ATAPI_device_event_has_work? + mov eax, [timer_ticks] + sub eax, [timer_ATAPI_check] + cmp eax, 100 + jb .no + + xor eax, eax + inc eax + ret +;-------------------------------------- +.no: + xor eax, eax + ret +endp +;----------------------------------------------------------------------------- +align 4 +check_ATAPI_device_event: + pusha + mov eax, [timer_ticks] + sub eax, [timer_ATAPI_check] + cmp eax, 100 + jb .end_1 + + pushfd + mov al, [DRIVE_DATA+1] + and al, 11b + cmp al, 10b + jz .ide3 +;-------------------------------------- +.ide2_1: + mov al, [DRIVE_DATA+1] + and al, 1100b + cmp al, 1000b + jz .ide2 +;-------------------------------------- +.ide1_1: + mov al, [DRIVE_DATA+1] + and al, 110000b + cmp al, 100000b + jz .ide1 +;-------------------------------------- +.ide0_1: + mov al, [DRIVE_DATA+1] + and al, 11000000b + cmp al, 10000000b + jz .ide0 +;-------------------------------------- +.ide7_1: + mov al, [DRIVE_DATA+6] + and al, 11b + cmp al, 10b + jz .ide7 +;-------------------------------------- +.ide6_1: + mov al, [DRIVE_DATA+6] + and al, 1100b + cmp al, 1000b + jz .ide6 +;-------------------------------------- +.ide5_1: + mov al, [DRIVE_DATA+6] + and al, 110000b + cmp al, 100000b + jz .ide5 +;-------------------------------------- +.ide4_1: + mov al, [DRIVE_DATA+6] + and al, 11000000b + cmp al, 10000000b + jz .ide4 +;-------------------------------------- +.ide11_1: + mov al, [DRIVE_DATA+11] + and al, 11b + cmp al, 10b + jz .ide11 +;-------------------------------------- +.ide10_1: + mov al, [DRIVE_DATA+11] + and al, 1100b + cmp al, 1000b + jz .ide10 +;-------------------------------------- +.ide9_1: + mov al, [DRIVE_DATA+11] + and al, 110000b + cmp al, 100000b + jz .ide9 +;-------------------------------------- +.ide8_1: + mov al, [DRIVE_DATA+11] + and al, 11000000b + cmp al, 10000000b + jz .ide8 +;-------------------------------------- +.end: + popfd + mov eax, [timer_ticks] + mov [timer_ATAPI_check], eax +;-------------------------------------- +.end_1: + popa + ret +;----------------------------------------------------------------------------- +.ide3: + cli + cmp [ATAPI_IDE3_lock], 1 + jne .ide2_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel2_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 1 + mov [cdpos], 4 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide2_1 +;----------------------------------------------------------------------------- +.ide2: + cli + cmp [ATAPI_IDE2_lock], 1 + jne .ide1_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel2_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 0 + mov [cdpos], 3 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide1_1 +;----------------------------------------------------------------------------- +.ide1: + cli + cmp [ATAPI_IDE1_lock], 1 + jne .ide0_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel1_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 1 + mov [cdpos], 2 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide0_1 +;----------------------------------------------------------------------------- +.ide0: + cli + cmp [ATAPI_IDE0_lock], 1 + jne .ide7_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel1_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + mov [cdpos], 1 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide7_1 +;----------------------------------------------------------------------------- +.ide7: + cli + cmp [ATAPI_IDE7_lock], 1 + jne .ide6_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel4_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 1 + mov [cdpos], 8 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide6_1 +;----------------------------------------------------------------------------- +.ide6: + cli + cmp [ATAPI_IDE6_lock], 1 + jne .ide5_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel4_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 0 + mov [cdpos], 7 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide5_1 +;----------------------------------------------------------------------------- +.ide5: + cli + cmp [ATAPI_IDE5_lock], 1 + jne .ide4_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel3_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 1 + mov [cdpos], 6 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide4_1 +;----------------------------------------------------------------------------- +.ide4: + cli + cmp [ATAPI_IDE4_lock], 1 + jne .ide11_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel3_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + mov [cdpos], 5 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide11_1 +;----------------------------------------------------------------------------- +.ide11: + cli + cmp [ATAPI_IDE11_lock], 1 + jne .ide10_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel6_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 1 + mov [cdpos], 12 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide10_1 +;----------------------------------------------------------------------------- +.ide10: + cli + cmp [ATAPI_IDE10_lock], 1 + jne .ide9_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel6_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 2 + mov [DiskNumber], 0 + mov [cdpos], 11 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide9_1 +;----------------------------------------------------------------------------- +.ide9: + cli + cmp [ATAPI_IDE9_lock], 1 + jne .ide8_1 + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel5_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 1 + mov [cdpos], 10 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .ide8_1 +;----------------------------------------------------------------------------- +.ide8: + cli + cmp [ATAPI_IDE8_lock], 1 + jne .end + + cmp [cd_status], 0 + jne .end + + mov ecx, ide_channel5_mutex + call mutex_lock + call reserve_ok2 + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + mov [cdpos], 9 + call GetEvent_StatusNotification + cmp [CDDataBuf+4], byte 1 + jne @f + + call .eject +;-------------------------------------- +@@: + call syscall_cdaudio.free + jmp .end +;----------------------------------------------------------------------------- +.eject: + call clear_CD_cache + call allow_medium_removal + mov [ignore_CD_eject_wait], 1 + call EjectMedium + mov [ignore_CD_eject_wait], 0 + ret +;----------------------------------------------------------------------------- +iglobal +timer_ATAPI_check dd 0 +ATAPI_IDE0_lock db 0 +ATAPI_IDE1_lock db 0 +ATAPI_IDE2_lock db 0 +ATAPI_IDE3_lock db 0 +ATAPI_IDE4_lock db 0 +ATAPI_IDE5_lock db 0 +ATAPI_IDE6_lock db 0 +ATAPI_IDE7_lock db 0 +ATAPI_IDE8_lock db 0 +ATAPI_IDE9_lock db 0 +ATAPI_IDE10_lock db 0 +ATAPI_IDE11_lock db 0 +ignore_CD_eject_wait db 0 +endg +;----------------------------------------------------------------------------- +;************************************************* +;* Get an event or device status message * +;* * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel * +;************************************************* +GetEvent_StatusNotification: + pusha + mov [CDDataBuf_pointer], CDDataBuf +; Clear the packet command buffer + call clear_packet_buffer +; Set command code + mov [PacketCommand], byte 4Ah + mov [PacketCommand+1], byte 00000001b +; Set message class request + mov [PacketCommand+4], byte 00010000b +; Size of allocated area + mov [PacketCommand+7], byte 8h + mov [PacketCommand+8], byte 0h +; Send command + call SendPacketDatCommand + popa + ret +;----------------------------------------------------------------------------- +;************************************************* +; Read information from TOC (Table of contents) * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel * +;************************************************* +Read_TOC: + pusha + mov [CDDataBuf_pointer], CDDataBuf +; Clear the packet command buffer + call clear_packet_buffer +; Generate a packet command to read a data sector + mov [PacketCommand], byte 0x43 + ; Set format + mov [PacketCommand+2], byte 1 +; Size of allocated area + mov [PacketCommand+7], byte 0xFF + mov [PacketCommand+8], byte 0h +; Send a command + call SendPacketDatCommand + popa + ret +;----------------------------------------------------------------------------- +;***************************************************** +;* DETERMINE THE TOTAL NUMBER OF SECTORS ON THE DISK * +;* Input parameters are passed through global * +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel * +;***************************************************** +;ReadCapacity: +; pusha +;; Clear the packet command buffer +; call clear_packet_buffer +;; Set the buffer size in bytes +; mov [CDBlockSize],8 +;; Generate READ CAPACITY command +; mov [PacketCommand],word 25h +;; Send command +; call SendPacketDatCommand +; popa +; ret +;----------------------------------------------------------------------------- +clear_packet_buffer: +; Clear the packet command buffer + and [PacketCommand], dword 0 + and [PacketCommand+4], dword 0 + and [PacketCommand+8], dword 0 + ret +;----------------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-lldw/blkdev/disk.inc b/kernel/branches/kolibri-lldw/blkdev/disk.inc new file mode 100644 index 000000000..16d0c9ba6 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/disk.inc @@ -0,0 +1,1666 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Error codes for callback functions. +DISK_STATUS_OK = 0 ; success +DISK_STATUS_GENERAL_ERROR = -1; if no other code is suitable +DISK_STATUS_INVALID_CALL = 1 ; invalid input parameters +DISK_STATUS_NO_MEDIA = 2 ; no media present +DISK_STATUS_END_OF_MEDIA = 3 ; end of media while reading/writing data +DISK_STATUS_NO_MEMORY = 4 ; insufficient memory for driver operation +; Driver flags. Represent bits in DISK.DriverFlags. +DISK_NO_INSERT_NOTIFICATION = 1 +; Media flags. Represent bits in DISKMEDIAINFO.Flags. +DISK_MEDIA_READONLY = 1 + +; If too many partitions are detected,there is probably an error on the disk. +; 256 partitions should be enough for any reasonable use. +; Also, the same number is limiting the number of MBRs to process; if +; too many MBRs are visible,there probably is a loop in the MBR structure. +MAX_NUM_PARTITIONS = 256 + +; ============================================================================= +; ================================ Structures ================================= +; ============================================================================= +; This structure defines all callback functions for working with the physical +; device. They are implemented by a driver. Objects with this structure reside +; in a driver. +struct DISKFUNC + strucsize dd ? +; Size of the structure. This field is intended for possible extensions of +; this structure. If a new function is added to this structure and a driver +; implements an old version, the caller can detect this by checking .strucsize, +; so the driver remains compatible. + close dd ? +; The pointer to the function which frees all driver-specific resources for +; the disk. +; Optional, may be NULL. +; void close(void* userdata); + closemedia dd ? +; The pointer to the function which informs the driver that the kernel has +; finished all processing with the current media. If media is removed, the +; driver should decline all requests to that media with DISK_STATUS_NO_MEDIA, +; even if new media is inserted, until this function is called. If media is +; removed, a new call to 'disk_media_changed' is not allowed until this +; function is called. +; Optional, may be NULL (if media is not removable). +; void closemedia(void* userdata); + querymedia dd ? +; The pointer to the function which determines capabilities of the media. +; int querymedia(void* userdata, DISKMEDIAINFO* info); +; Return value: one of DISK_STATUS_* + read dd ? +; The pointer to the function which reads data from the device. +; int read(void* userdata, void* buffer, __int64 startsector, int* numsectors); +; input: *numsectors = number of sectors to read +; output: *numsectors = number of sectors which were successfully read +; Return value: one of DISK_STATUS_* + write dd ? +; The pointer to the function which writes data to the device. +; Optional, may be NULL. +; int write(void* userdata, void* buffer, __int64 startsector, int* numsectors); +; input: *numsectors = number of sectors to write +; output: *numsectors = number of sectors which were successfully written +; Return value: one of DISK_STATUS_* + flush dd ? +; The pointer to the function which flushes the internal device cache. +; Optional, may be NULL. +; int flush(void* userdata); +; Return value: one of DISK_STATUS_* +; Note that read/write are called by the cache manager, so a driver should not +; create a software cache. This function is implemented for flushing a hardware +; cache, if it exists. + adjust_cache_size dd ? +; The pointer to the function which returns the cache size for this device. +; Optional, may be NULL. +; unsigned int adjust_cache_size(void* userdata, unsigned int suggested_size); +; Return value: 0 = disable cache, otherwise = used cache size in bytes. +ends + +; This structure holds information on a medium. +; Objects with this structure are allocated by the kernel as a part of the DISK +; structure and are filled by a driver in the 'querymedia' callback. +struct DISKMEDIAINFO + Flags dd ? +; Combination of DISK_MEDIA_* bits. + SectorSize dd ? +; Size of the sector. + Capacity dq ? +; Size of the media in sectors. +ends + +; This structure represents the disk cache. To follow the old implementation, +; there are two distinct caches for a disk, one for "system" data,and the other +; for "application" data. +struct DISKCACHE +; The following fields are inherited from data32.inc:cache_ideX. + pointer dd ? + data_size dd ? ; unused + data dd ? + sad_size dd ? + search_start dd ? + sector_size_log dd ? +ends + +; This structure represents a disk device and its media for the kernel. +; This structure is allocated by the kernel in the 'disk_add' function, +; freed in the 'disk_dereference' function. +struct DISK +; Fields of disk object + Next dd ? + Prev dd ? +; All disk devices are linked in one list with these two fields. +; Head of the list is the 'disk_list' variable. + Functions dd ? +; Pointer to the 'DISKFUNC' structure with driver functions. + Name dd ? +; Pointer to the string used for accesses through the global filesystem. + UserData dd ? +; This field is passed to all callback functions so a driver can decide which +; physical device is addressed. + DriverFlags dd ? +; Bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit is defined. +; If it is set, the driver will never issue 'disk_media_changed' notification +; with argument set to true, so the kernel must try to detect media during +; requests from the file system. + RefCount dd ? +; Count of active references to this structure. One reference is kept during +; the lifetime of the structure between 'disk_add' and 'disk_del'. +; Another reference is taken during any filesystem operation for this disk. +; One reference is added if media is inserted. +; The structure is destroyed when the reference count decrements to zero: +; this usually occurs in 'disk_del', but can be delayed to the end of last +; filesystem operation, if one is active. + MediaLock MUTEX +; Lock to protect the MEDIA structure. See the description after +; 'disk_list_mutex' for the locking strategy. +; Fields of media object + MediaInserted db ? +; 0 if media is not inserted, nonzero otherwise. + MediaUsed db ? +; 0 if media fields are not used, nonzero otherwise. If .MediaRefCount is +; nonzero, this field is nonzero too; however, when .MediaRefCount goes +; to zero, there is some time interval during which media object is still used. + dw ? ; padding +; The following fields are not valid unless either .MediaInserted is nonzero +; or they are accessed from a code which has obtained the reference when +; .MediaInserted was nonzero. + MediaRefCount dd ? +; Count of active references to the media object. One reference is kept during +; the lifetime of the media between two calls to 'disk_media_changed'. +; Another reference is taken during any filesystem operation for this media. +; The callback 'closemedia' is called when the reference count decrements to +; zero: this usually occurs in 'disk_media_changed', but can be delayed to the +; end of the last filesystem operation, if one is active. + MediaInfo DISKMEDIAINFO +; This field keeps information on the current media. + NumPartitions dd ? +; Number of partitions on this media. + Partitions dd ? +; Pointer to array of .NumPartitions pointers to PARTITION structures. + cache_size dd ? +; inherited from cache_ideX_size + CacheLock MUTEX +; Lock to protect both caches. + SysCache DISKCACHE + AppCache DISKCACHE +; Two caches for the disk. +ends + +; This structure represents one partition for the kernel. This is a base +; template, the actual contents after common fields is determined by the +; file system code for this partition. +struct PARTITION + FirstSector dq ? +; First sector of the partition. + Length dq ? +; Length of the partition in sectors. + Disk dd ? +; Pointer to parent DISK structure. + FSUserFunctions dd ? +; Handlers for the sysfunction 70h. This field is a pointer to the following +; array. The first dword is pointer to disconnect handler. +; The first dword is a number of supported subfunctions, other dwords +; point to handlers of corresponding subfunctions. +; ...fs-specific data may follow... +ends + +; This is an external structure, it represents an entry in the partition table. +struct PARTITION_TABLE_ENTRY + Bootable db ? +; 80h = bootable partition, 0 = non-bootable partition, other values = invalid + FirstHead db ? + FirstSector db ? + FirstTrack db ? +; Coordinates of first sector in CHS. + Type db ? +; Partition type, one of predefined constants. 0 = empty, several types denote +; extended partition (see process_partition_table_entry), we are not interested +; in other values. + LastHead db ? + LastSector db ? + LastTrack db ? +; Coordinates of last sector in CHS. + FirstAbsSector dd ? +; Coordinate of first sector in LBA. + Length dd ? +; Length of the partition in sectors. +ends + +; GUID Partition Table Header, UEFI 2.6, Table 18 +struct GPTH + Signature rb 8 +; 'EFI PART' + Revision dd ? +; 0x00010000 + HeaderSize dd ? +; Size of this header in bytes, must fit to one sector. + HeaderCRC32 dd ? +; Set this field to zero, compute CRC32 via 0xEDB88320, compare. + Reserved dd ? +; Must be zero. + MyLBA dq ? +; LBA of the sector containing this GPT header. + AlternateLBA dq ? +; LBA of the sector containing the other GPT header. +; AlternateLBA of Primary GPTH points to Backup one and vice versa. + FirstUsableLBA dq ? +; Only sectors between first and last UsableLBA may form partitions + LastUsableLBA dq ? + DiskGUID rb 16 +; Globally Unique IDentifier + PartitionEntryLBA dq ? +; First LBA of Partition Entry Array. +; Length in bytes is computed as a product of two following fields. + NumberOfPartitionEntries dd ? +; Actual number of partitions depends on the contents of Partition Entry Array. +; A partition entry is unused if zeroed. + SizeOfPartitionEntry dd ? ; in bytes + PartitionEntryArrayCRC32 dd ? +; Same CRC as for GPT header. +ends + +; GPT Partition Entry, UEFI 2.6, Table 19 +struct GPE + PartitionTypeGUID rb 16 + UniquePartitionGUID rb 16 + StartingLBA dq ? + EndingLBA dq ? +; Length in sectors is EndingLBA - StartingLBA + 1. + Attributes dq ? + PartitionName rb 72 +ends + +; ============================================================================= +; ================================ Global data ================================ +; ============================================================================= +iglobal +; The pseudo-item for the list of all DISK structures. +; Initialized to the empty list. +disk_list: + dd disk_list + dd disk_list +endg +uglobal +; This mutex guards all operations with the global list of DISK structures. +disk_list_mutex MUTEX +; * There are two dependent objects, a disk and a media. In the simplest case, +; disk and media are both non-removable. However, in the general case both +; can be removed at any time, simultaneously or only media,and this makes things +; complicated. +; * For efficiency, both disk and media objects are located in the one +; structure named DISK. However, logically they are different. +; * The following operations use data of disk object: adding (disk_add); +; deleting (disk_del); filesystem (fs_lfn which eventually calls +; dyndisk_handler or dyndisk_enum_root). +; * The following operations use data of media object: adding/removing +; (disk_media_changed); filesystem (fs_lfn which eventually calls +; dyndisk_handler; dyndisk_enum_root doesn't work with media). +; * Notifications disk_add, disk_media_changed, disk_del are synchronized +; between themselves, this is a requirement for the driver. However, file +; system operations are asynchronous, can be issued at any time by any +; thread. +; * We must prevent a situation when a filesystem operation thinks that the +; object is still valid but in fact the notification has destroyed the +; object. So we keep a reference counter for both disk and media and destroy +; the object when this counter goes to zero. +; * The driver must know when it is safe to free driver-allocated resources. +; The object can be alive even after death notification has completed. +; We use special callbacks to satisfy both assertions: 'close' for the disk +; and 'closemedia' for the media. The destruction of the object includes +; calling the corresponding callback. +; * Each filesystem operation keeps one reference for the disk and one +; reference for the media. Notification disk_del forces notification on the +; media death, so the reference counter for the disk is always not less than +; the reference counter for the media. +; * Two operations "get the object" and "increment the reference counter" can +; not be done simultaneously. We use a mutex to guard the consistency here. +; It must be a part of the container for the object, so that this mutex can +; be acquired as a part of getting the object from the container. The +; container for disk object is the global list, and this list is guarded by +; 'disk_list_mutex'. The container for media object is the disk object, and +; the corresponding mutex is DISK.MediaLock. +; * Notifications do not change the data of objects, they can only remove +; objects. Thus we don't need another synchronization at this level. If two +; filesystem operations are referencing the same filesystem data, this is +; better resolved at the level of the filesystem. +endg + +iglobal +; The function 'disk_scan_partitions' needs three sector-sized buffers for +; MBR, bootsector and fs-temporary sector data. It can not use the static +; buffers always, since it can be called for two or more disks in parallel. +; However, this case is not typical. We reserve three static 512-byte buffers +; and a flag that these buffers are currently used. If 'disk_scan_partitions' +; detects that the buffers are currently used, it allocates buffers from the +; heap. Also, the heap is used when sector size is other than 512. +; The flag is implemented as a global dword variable. When the static buffers +; are not used, the value is -1. When the static buffers are used, the value +; is normally 0 and temporarily can become greater. The function increments +; this value. If the resulting value is zero, it uses the buffers and +; decrements the value when the job is done. Otherwise, it immediately +; decrements the value and uses buffers from the heap, allocated in the +; beginning and freed in the end. +partition_buffer_users dd -1 +endg +uglobal +; The static buffers for MBR, bootsector and fs-temporary sector data. +align 16 +mbr_buffer rb 512 +bootsect_buffer rb 512 +fs_tmp_buffer rb 512 +endg + +iglobal +; This is the array of default implementations of driver callbacks. +; Same as DRIVERFUNC structure except for the first field; all functions must +; have the default implementations. +align 4 +disk_default_callbacks: + dd disk_default_close + dd disk_default_closemedia + dd disk_default_querymedia + dd disk_default_read + dd disk_default_write + dd disk_default_flush + dd disk_default_adjust_cache_size +endg + +; ============================================================================= +; ================================= Functions ================================= +; ============================================================================= + +; This function registers a disk device. +; This includes: +; - allocating an internal structure describing this device; +; - registering this structure in the global filesystem. +; The function initializes the disk as if there is no media. If a media is +; present, the function 'disk_media_changed' should be called after this +; function succeeds. +; Parameters: +; [esp+4] = pointer to DISKFUNC structure with the callbacks +; [esp+8] = pointer to name (ASCIIZ string) +; [esp+12] = userdata to be passed to the callbacks as is. +; [esp+16] = flags, bitfield. Currently only DISK_NO_INSERT_NOTIFICATION bit +; is defined. +; Return value: +; NULL = operation has failed +; non-NULL = handle of the disk. This handle can be used +; in the operations with other Disk* functions. +; The handle is the pointer to the internal structure DISK. +disk_add: + push ebx esi ; save used registers to be stdcall +; 1. Allocate the DISK structure. +; 1a. Call the heap manager. + movi eax, sizeof.DISK + call malloc +; 1b. Check the result. If allocation failed, return (go to 9) with eax = 0. + test eax, eax + jz .nothing +; 2. Copy the disk name to the DISK structure. +; 2a. Get length of the name, including the terminating zero. + mov ebx, [esp+8+8] ; ebx = pointer to name + push eax ; save allocated pointer to DISK + xor eax, eax ; the argument of malloc() is in eax +@@: + inc eax + cmp byte [ebx+eax-1], 0 + jnz @b +; 2b. Call the heap manager. + call malloc +; 2c. Check the result. If allocation failed, go to 7. + pop esi ; restore allocated pointer to DISK + test eax, eax + jz .free +; 2d. Store the allocated pointer to the DISK structure. + mov [esi+DISK.Name], eax +; 2e. Copy the name. +@@: + mov dl, [ebx] + mov [eax], dl + inc ebx + inc eax + test dl, dl + jnz @b +; 3. Copy other arguments of the function to the DISK structure. + mov eax, [esp+4+8] + mov [esi+DISK.Functions], eax + mov eax, [esp+12+8] + mov [esi+DISK.UserData], eax + mov eax, [esp+16+8] + mov [esi+DISK.DriverFlags], eax +; 4. Initialize other fields of the DISK structure. +; Media is not inserted, reference counter is 1. + lea ecx, [esi+DISK.MediaLock] + call mutex_init + xor eax, eax + mov dword [esi+DISK.MediaInserted], eax + mov [esi+DISK.MediaRefCount], eax + inc eax + mov [esi+DISK.RefCount], eax +; The DISK structure is initialized. +; 5. Insert the new structure to the global list. +; 5a. Acquire the mutex. + mov ecx, disk_list_mutex + call mutex_lock +; 5b. Insert item to the tail of double-linked list. + mov edx, disk_list + list_add_tail esi, edx ;esi= new edx= list head +; 5c. Release the mutex. + call mutex_unlock +; 6. Return with eax = pointer to DISK. + xchg eax, esi + jmp .nothing +.free: +; Memory allocation for DISK structure succeeded, but for disk name failed. +; 7. Free the DISK structure. + xchg eax, esi + call free +; 8. Return with eax = 0. + xor eax, eax +.nothing: +; 9. Return. + pop esi ebx ; restore used registers to be stdcall + ret 16 ; purge 4 dword arguments to be stdcall + +; This function deletes a disk device from the global filesystem. +; This includes: +; - removing a media including all partitions; +; - deleting this structure from the global filesystem; +; - dereferencing the DISK structure and possibly destroying it. +; Parameters: +; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. +; Return value: none. +disk_del: + push esi ; save used registers to be stdcall +; 1. Force media to be removed. If the media is already removed, the +; call does nothing. + mov esi, [esp+4+4] ; esi = handle of the disk + stdcall disk_media_changed, esi, 0 +; 2. Delete the structure from the global list. +; 2a. Acquire the mutex. + mov ecx, disk_list_mutex + call mutex_lock +; 2b. Delete item from double-linked list. + mov eax, [esi+DISK.Next] + mov edx, [esi+DISK.Prev] + mov [eax+DISK.Prev], edx + mov [edx+DISK.Next], eax +; 2c. Release the mutex. + call mutex_unlock +; 3. The structure still has one reference created in disk_add. Remove this +; reference. If there are no other references, disk_dereference will free the +; structure. + call disk_dereference +; 4. Return. + pop esi ; restore used registers to be stdcall + ret 4 ; purge 1 dword argument to be stdcall + +; This is an internal function which removes a previously obtained reference +; to the disk. If this is the last reference, this function lets the driver +; finalize all associated data, and afterwards frees the DISK structure. +; esi = pointer to DISK structure +disk_dereference: +; 1. Decrement reference counter. Use atomic operation to correctly handle +; possible simultaneous calls. + lock dec [esi+DISK.RefCount] +; 2. If the result is nonzero, there are other references, so nothing to do. +; In this case, return (go to 4). + jnz .nothing +; 3. If we are here, we just removed the last reference and must destroy the +; disk object. +; 3a. Call the driver. + mov al, DISKFUNC.close + stdcall disk_call_driver +; 3b. Free the structure. + xchg eax, esi + push ebx + call free + pop ebx +; 4. Return. +.nothing: + ret + +; This is an internal function which removes a previously obtained reference +; to the media. If this is the last reference, this function calls 'closemedia' +; callback to signal the driver that the processing has finished and it is safe +; to inform about a new media. +; esi = pointer to DISK structure +disk_media_dereference: +; 1. Decrement reference counter. Use atomic operation to correctly handle +; possible simultaneous calls. + lock dec [esi+DISK.MediaRefCount] +; 2. If the result is nonzero, there are other references, so nothing to do. +; In this case, return (go to 4). + jnz .nothing +; 3. If we are here, we just removed the last reference and must destroy the +; media object. +; Note that the same place inside the DISK structure is reused for all media +; objects, so we must guarantee that reusing does not happen while freeing. +; Reusing is only possible when someone processes a new media. There are two +; mutually exclusive variants: +; * driver issues media insert notifications (DISK_NO_INSERT_NOTIFICATION bit +; in DISK.DriverFlags is not set). In this case, we require from the driver +; that such notification (except for the first one) can occur only after a +; call to 'closemedia' callback. +; * driver does not issue media insert notifications. In this case, the kernel +; itself must sometimes check whether media is inserted. We have the flag +; DISK.MediaUsed, visible to the kernel. This flag signals to the other parts +; of kernel that the way is free. +; In the first case other parts of the kernel do not use DISK.MediaUsed, so it +; does not matter when this flag is cleared. In the second case this flag must +; be cleared after all other actions, including call to 'closemedia'. +; 3a. Free all partitions. + push esi edi + mov edi, [esi+DISK.NumPartitions] + mov esi, [esi+DISK.Partitions] + test edi, edi + jz .nofree +.freeloop: + lodsd + mov ecx, [eax+PARTITION.FSUserFunctions] + call dword [ecx] + dec edi + jnz .freeloop +.nofree: + pop edi esi +; 3b. Free the cache. + call disk_free_cache +; 3c. Call the driver. + mov al, DISKFUNC.closemedia + stdcall disk_call_driver +; 3d. Clear the flag. + mov [esi+DISK.MediaUsed], 0 +.nothing: + ret + +; This function is called by the driver and informs the kernel that the media +; has changed. If the media is non-removable, it is called exactly once +; immediately after 'disk_add' and once from 'disk_del'. +; Parameters: +; [esp+4] = handle of the disk, i.e. the pointer to the DISK structure. +; [esp+8] = new status of the media: zero = no media, nonzero = media inserted. +disk_media_changed: + push ebx esi edi ; save used registers to be stdcall +; 1. Remove the existing media, if it is present. + mov esi, [esp+4+12] ; esi = pointer to DISK +; 1a. Check whether it is present. Since DISK.MediaInserted is changed only +; in this function and calls to this function are synchronized, no lock is +; required for checking. + cmp [esi+DISK.MediaInserted], 0 + jz .noremove +; We really need to remove the media. +; 1b. Acquire mutex. + lea ecx, [esi+DISK.MediaLock] + call mutex_lock +; 1c. Clear the flag. + mov [esi+DISK.MediaInserted], 0 +; 1d. Release mutex. + call mutex_unlock +; 1e. Remove the "lifetime" reference and possibly destroy the structure. + call disk_media_dereference +.noremove: +; 2. Test whether there is new media. + cmp dword [esp+8+12], 0 + jz .noinsert +; Yep, there is. +; 3. Process the new media. We assume that all media fields are available to +; use, see comments in 'disk_media_dereference' (this covers using by previous +; media referencers) and note that calls to this function are synchronized +; (this covers using by new media referencers). +; 3a. Call the 'querymedia' callback. +; .Flags are set to zero for possible future extensions. + lea edx, [esi+DISK.MediaInfo] + and [edx+DISKMEDIAINFO.Flags], 0 + mov al, DISKFUNC.querymedia + stdcall disk_call_driver, edx +; 3b. Check the result of the callback. Abort if it failed. + test eax, eax + jnz .noinsert +; 3c. Allocate the cache unless disabled by the driver. Abort if failed. + call disk_init_cache + test al, al + jz .noinsert +; 3d. Acquire the lifetime reference for the media object. + inc [esi+DISK.MediaRefCount] +; 3e. Scan for partitions. Ignore result; the list of partitions is valid even +; on errors. + call disk_scan_partitions +; 3f. Media is inserted and available for use. + inc [esi+DISK.MediaInserted] +.noinsert: +; 4. Return. + pop edi esi ebx ; restore used registers to be stdcall + ret 8 ; purge 2 dword arguments to be stdcall + +; This function is a thunk for all functions of a disk driver. +; It checks whether the referenced function is implemented in the driver. +; If so, this function jumps to the function in the driver. +; Otherwise, it jumps to the default implementation. +; al = offset of function in the DISKFUNC structure; +; esi = pointer to the DISK structure; +; stack is the same as for the corresponding function except that the +; first parameter (void* userdata) is prepended automatically. +disk_call_driver: + movzx eax, al ; eax = offset of function in the DISKFUNC structure +; 1. Prepend the first argument to the stack. + pop ecx ; ecx = return address + push [esi+DISK.UserData] ; add argument + push ecx ; save return address +; 2. Check that the required function is inside the table. If not, go to 5. + mov ecx, [esi+DISK.Functions] + cmp eax, [ecx+DISKFUNC.strucsize] + jae .default +; 3. Check that the required function is implemented. If not, go to 5. + mov ecx, [ecx+eax] + test ecx, ecx + jz .default +; 4. Jump to the required function. + jmp ecx +.default: +; 5. Driver does not implement the required function; use default implementation. + jmp dword [disk_default_callbacks+eax-4] + +; The default implementation of DISKFUNC.querymedia. +disk_default_querymedia: + movi eax, DISK_STATUS_INVALID_CALL + ret 8 + +; The default implementation of DISKFUNC.read and DISKFUNC.write. +disk_default_read: +disk_default_write: + movi eax, DISK_STATUS_INVALID_CALL + ret 20 + +; The default implementation of DISKFUNC.close, DISKFUNC.closemedia and +; DISKFUNC.flush. +disk_default_close: +disk_default_closemedia: +disk_default_flush: + xor eax, eax + ret 4 + +; The default implementation of DISKFUNC.adjust_cache_size. +disk_default_adjust_cache_size: + mov eax, [esp+8] + ret 8 + +; This is an internal function called from 'disk_media_changed' when a new media +; is detected. It creates the list of partitions for the media. +; If media is not partitioned, then the list consists of one partition which +; covers all the media. +; esi = pointer to the DISK structure. +disk_scan_partitions: +; 1. Initialize .NumPartitions and .Partitions fields as zeros: empty list. + and [esi+DISK.NumPartitions], 0 + and [esi+DISK.Partitions], 0 +; 2. Acquire the buffer for MBR and bootsector tests. See the comment before +; the 'partition_buffer_users' variable. + mov eax, [esi+DISK.MediaInfo.SectorSize] + cmp eax, 512 + jnz @f + mov ebx, mbr_buffer ; assume the global buffer is free + lock inc [partition_buffer_users] + jz .buffer_acquired ; yes, it is free + lock dec [partition_buffer_users] ; no, we must allocate +@@: + lea eax, [eax*3] + stdcall kernel_alloc, eax + test eax, eax + jz .nothing + xchg eax, ebx +.buffer_acquired: +; MBR/EBRs are organized in the chain. We use a loop over MBR/EBRs, but no +; more than MAX_NUM_PARTITION times. +; 3. Prepare things for the loop. +; ebp will hold the sector number for current MBR/EBR. +; [esp] will hold the sector number for current extended partition, if there +; is one. +; [esp+4] will hold the counter that prevents long loops. + push ebp ; save ebp + push MAX_NUM_PARTITIONS ; the counter of max MBRs to process + xor ebp, ebp ; start from sector zero + push ebp ; no extended partition yet +; 4. MBR is 512 bytes long. If sector size is less than 512 bytes, +; assume no MBR, no partitions and go to 11. + cmp [esi+DISK.MediaInfo.SectorSize], 512 + jb .notmbr +.new_mbr: +; 5. Read the current sector. +; Note that 'read' callback operates with 64-bit sector numbers, so we must +; push additional zero as a high dword of sector number. + mov al, DISKFUNC.read + push 1 + stdcall disk_call_driver, ebx, ebp, 0, esp + pop ecx +; 6. If the read has failed, abort the loop. + dec ecx + jnz .mbr_failed +; 7. Check the MBR/EBR signature. If it is wrong, abort the loop. +; Soon we will access the partition table which starts at ebx+0x1BE, +; so we can fill its address right now. If we do it now, then the addressing +; [ecx+0x40] is shorter than [ebx+0x1fe]: one-byte offset vs 4-bytes offset. + lea ecx, [ebx+0x1be] ; ecx -> partition table + cmp word [ecx+0x40], 0xaa55 + jnz .mbr_failed +; 8. The MBR is treated differently from EBRs. For MBR we additionally need to +; execute step 10 and possibly step 11. + test ebp, ebp + jnz .mbr +; 9. Handle GUID Partition Table +; 9a. Check if MBR is protective + call is_protective_mbr + jnz .no_gpt +; 9b. If so, try to scan GPT headers + call disk_scan_gpt +; 9c. If any GPT header is valid, ignore MBR + jz .done +; Otherwise process legacy/protective MBR +.no_gpt: +; The partition table can be present or not present. In the first case, we just +; read the MBR. In the second case, we just read the bootsector for a +; filesystem. +; The following algorithm is used to distinguish between these cases. +; A. If at least one entry of the partition table is invalid, this is +; a bootsector. See the description of 'is_partition_table_entry' for +; definition of validity. +; B. If all entries are empty (filesystem type field is zero) and the first +; byte is jmp opcode (0EBh or 0E9h), this is a bootsector which happens to +; have zeros in the place of partition table. +; C. Otherwise, this is an MBR. +; 10. Test for MBR vs bootsector. +; 10a. Check entries. If any is invalid, go to 11 (rule A). + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr + add ecx, 10h + call is_partition_table_entry + jc .notmbr +; 10b. Check types of the entries. If at least one is nonzero, go to 12 (rule C). + mov al, [ecx-30h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx-20h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx-10h+PARTITION_TABLE_ENTRY.Type] + or al, [ecx+PARTITION_TABLE_ENTRY.Type] + jnz .mbr +; 10c. Empty partition table or bootsector with many zeroes? (rule B) + cmp byte [ebx], 0EBh + jz .notmbr + cmp byte [ebx], 0E9h + jnz .mbr +.notmbr: +; 11. This is not an MBR. The media is not partitioned. Create one partition +; which covers all the media and abort the loop. + stdcall disk_add_partition, 0, 0, \ + dword [esi+DISK.MediaInfo.Capacity], dword [esi+DISK.MediaInfo.Capacity+4], esi + jmp .done +.mbr: +; 12. Process all entries of the new MBR/EBR + lea ecx, [ebx+0x1be] ; ecx -> partition table + push 0 ; assume no extended partition + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + add ecx, 10h + call process_partition_table_entry + pop ebp +; 13. Test whether we found a new EBR and should continue the loop. +; 13a. If there was no next EBR, return. + test ebp, ebp + jz .done +; Ok, we have EBR. +; 13b. EBRs addresses are relative to the start of extended partition. +; For simplicity, just abort if an 32-bit overflow occurs; large disks +; are most likely partitioned with GPT, not MBR scheme, since the precise +; calculation here would increase limit just twice at the price of big +; compatibility problems. + pop eax ; load extended partition + add ebp, eax + jc .mbr_failed +; 13c. If extended partition has not yet started, start it. + test eax, eax + jnz @f + mov eax, ebp +@@: +; 13d. If the limit is not exceeded, continue the loop. + dec dword [esp] + push eax ; store extended partition + jnz .new_mbr +.mbr_failed: +.done: +; 14. Cleanup after the loop. + pop eax ; not important anymore + pop eax ; not important anymore + pop ebp ; restore ebp +; 15. Release the buffer. +; 15a. Test whether it is the global buffer or we have allocated it. + cmp ebx, mbr_buffer + jz .release_partition_buffer +; 15b. If we have allocated it, free it. + xchg eax, ebx + call free + jmp .nothing +; 15c. Otherwise, release reference. +.release_partition_buffer: + lock dec [partition_buffer_users] +.nothing: +; 16. Return. + ret + + +; This function is called from disk_scan_partitions to validate and parse +; primary and backup GPTs. +proc disk_scan_gpt + push ecx +; Scan primary GPT (second sector) + stdcall scan_gpt, 1, 0 + test eax, eax +; There is no code to restore backup GPT if it's corrupt. +; Therefore just exit if Primary GPT has been parsed successfully. + jz .exit + DEBUGF 1, 'K : Primary GPT is corrupt, trying backup one\n' + mov eax, dword[esi+DISK.MediaInfo.Capacity+0] + mov edx, dword[esi+DISK.MediaInfo.Capacity+4] + sub eax, 1 + sbb edx, 0 +; Scan backup GPT (last sector) + stdcall scan_gpt, eax, edx + test eax, eax + jz .exit + DEBUGF 1, 'K : Backup GPT is also corrupt, fallback to legacy MBR\n' +.exit: +; Return value is ZF + pop ecx + ret +endp + + +; This function is called from disk_scan_gpt to process a single GPT. +proc scan_gpt _mylba:qword +locals + GPEA_len dd ? ; Length of GPT Partition Entry Array in bytes +endl + push ebx edi +; Allocalte memory for GPT header + mov eax, [esi+DISK.MediaInfo.SectorSize] + stdcall kernel_alloc, eax + test eax, eax + jz .fail +; Save pointer to stack, just in case + push eax + mov ebx, eax +; Read GPT header + mov al, DISKFUNC.read + push 1 + stdcall disk_call_driver, ebx, dword[_mylba+0], dword[_mylba+4], esp + pop ecx + test eax, eax + jnz .fail_free_gpt +; Check signature + cmp dword[ebx+GPTH.Signature+0], 'EFI ' + jnz .fail_free_gpt + cmp dword[ebx+GPTH.Signature+4], 'PART' + jnz .fail_free_gpt +; Check Revision + cmp [ebx+GPTH.Revision], 0x00010000 + jnz .fail_free_gpt +; Compute and check CRC32 + xor edx, edx + xchg edx, [ebx+GPTH.HeaderCRC32] + mov eax, -1 + stdcall crc_32, 0xEDB88320, ebx, [ebx+GPTH.HeaderSize] + xor eax, -1 + cmp eax, edx + jnz .fail_free_gpt +; Reserved must be zero + cmp [ebx+GPTH.Reserved], 0 + jnz .fail_free_gpt +; MyLBA of GPT header at LBA X must equal X + mov eax, dword[ebx+GPTH.MyLBA+0] + mov edx, dword[ebx+GPTH.MyLBA+4] + cmp eax, dword[_mylba+0] + jnz .fail_free_gpt + cmp edx, dword[_mylba+4] + jnz .fail_free_gpt +; Capacity - MyLBA = AlternateLBA + mov eax, dword[esi+DISK.MediaInfo.Capacity+0] + mov edx, dword[esi+DISK.MediaInfo.Capacity+4] + sub eax, dword[_mylba+0] + sbb edx, dword[_mylba+4] + cmp eax, dword[ebx+GPTH.AlternateLBA+0] + jnz .fail_free_gpt + cmp edx, dword[ebx+GPTH.AlternateLBA+4] + jnz .fail_free_gpt + +; Compute GPT Partition Entry Array (GPEA) length in bytes + mov eax, [ebx+GPTH.NumberOfPartitionEntries] + mul [ebx+GPTH.SizeOfPartitionEntry] + test edx, edx ; far too big + jnz .fail_free_gpt +; Round up to sector boundary + mov ecx, [esi+DISK.MediaInfo.SectorSize] ; power of two + dec ecx + add eax, ecx + jc .fail_free_gpt ; too big + not ecx + and eax, ecx +; We will need this length to compute CRC32 of GPEA + mov [GPEA_len], eax +; Allocate memory for GPEA + stdcall kernel_alloc, eax + test eax, eax + jz .fail_free_gpt +; Save to not juggle with registers + push eax + mov edi, eax + mov eax, [GPEA_len] + xor edx, edx +; Get the number of sectors GPEA fits into + div [esi+DISK.MediaInfo.SectorSize] + push eax ; esp = pointer to the number of sectors + mov al, DISKFUNC.read + stdcall disk_call_driver, edi, dword[ebx+GPTH.PartitionEntryLBA+0], \ + dword[ebx+GPTH.PartitionEntryLBA+4], esp + test eax, eax + pop eax + jnz .fail_free_gpea_gpt +; Compute and check CRC32 of GPEA + mov eax, -1 + stdcall crc_32, 0xEDB88320, edi, [GPEA_len] + xor eax, -1 + cmp eax, [ebx+GPTH.PartitionEntryArrayCRC32] + jnz .fail_free_gpea_gpt + +; Process partitions, skip zeroed ones. +.next_gpe: + xor eax, eax + mov ecx, [ebx+GPTH.SizeOfPartitionEntry] + repz scasb + jz .skip + add edi, ecx + sub edi, [ebx+GPTH.SizeOfPartitionEntry] +; Length of a partition in sectors is EndingLBA - StartingLBA + 1 + mov eax, dword[edi+GPE.EndingLBA+0] + mov edx, dword[edi+GPE.EndingLBA+4] + sub eax, dword[edi+GPE.StartingLBA+0] + sbb edx, dword[edi+GPE.StartingLBA+4] + add eax, 1 + adc edx, 0 + push ebx + mov ebx, [ebp-8] ; three-sectors-sized buffer + stdcall disk_add_partition, dword[edi+GPE.StartingLBA+0], \ + dword[edi+GPE.StartingLBA+4], eax, edx, esi + pop ebx + add edi, [ebx+GPTH.SizeOfPartitionEntry] +.skip: + dec [ebx+GPTH.NumberOfPartitionEntries] + jnz .next_gpe + +; Pointers to GPT header and GPEA are on the stack + stdcall kernel_free + stdcall kernel_free + pop edi ebx + xor eax, eax + ret +.fail_free_gpea_gpt: + stdcall kernel_free +.fail_free_gpt: + stdcall kernel_free +.fail: + pop edi ebx + xor eax, eax + inc eax + ret +endp + +; ecx = pointer to partition records array (MBR + 446) +is_protective_mbr: + push ecx edi + xor eax, eax + cmp [ecx-2], ax + jnz .exit +; Partition record 0 has specific fields + cmp [ecx+0], al + jnz .exit + cmp byte[ecx+4], 0xEE + jnz .exit + cmp dword[ecx+8], 1 + jnz .exit + mov edi, -1 + cmp [ecx+12], edi + jz @f + add edi, dword[esi+DISK.MediaInfo.Capacity+0] + cmp [ecx+12], edi + jnz .exit +@@: +; Check that partition records 1-3 are filled with zero + lea edi, [ecx+16] + mov ecx, 16*3/2 ; 3 partitions + repz scasw +.exit: + pop edi ecx +; Return value is ZF + ret + +; This is an internal function called from disk_scan_partitions. It checks +; whether the entry pointed to by ecx is a valid entry of partition table. +; The entry is valid if the first byte is 0 or 80h, the first sector plus the +; length is less than twice the size of media. Multiplication by two is +; required since the size mentioned in the partition table can be slightly +; greater than the real size. +is_partition_table_entry: +; 1. Check .Bootable field. + mov al, [ecx+PARTITION_TABLE_ENTRY.Bootable] + and al, 7Fh + jnz .invalid +; 3. Calculate first sector + length. Note that .FirstAbsSector is relative +; to the MBR/EBR, so the real sum is ebp + .FirstAbsSector + .Length. + mov eax, ebp + xor edx, edx + add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + adc edx, 0 + add eax, [ecx+PARTITION_TABLE_ENTRY.Length] + adc edx, 0 +; 4. Divide by two. + shr edx, 1 + rcr eax, 1 +; 5. Compare with capacity. If the subtraction (edx:eax) - .Capacity does not +; overflow, this is bad. + sub eax, dword [esi+DISK.MediaInfo.Capacity] + sbb edx, dword [esi+DISK.MediaInfo.Capacity+4] + jnc .invalid +.valid: +; 5. Return success: CF is cleared. + clc + ret +.invalid: +; 6. Return fail: CF is set. + stc + ret + +; This is an internal function called from disk_scan_partitions. It processes +; the entry pointed to by ecx. +; * If the entry is invalid, just ignore this entry. +; * If the type is zero, just ignore this entry. +; * If the type is one of types for extended partition, store the address +; of this partition as the new MBR in [esp+4]. +; * Otherwise, add the partition to the list of partitions for this disk. +; We don't use the type from the entry to identify the file system; +; fs-specific checks do this more reliably. +process_partition_table_entry: +; 1. Check for valid entry. If invalid, return (go to 5). + call is_partition_table_entry + jc .nothing +; 2. Check for empty entry. If invalid, return (go to 5). + mov al, [ecx+PARTITION_TABLE_ENTRY.Type] + test al, al + jz .nothing +; 3. Check for extended partition. If extended, go to 6. +irp type,\ + 0x05,\ ; DOS: extended partition + 0x0f,\ ; WIN95: extended partition, LBA-mapped + 0xc5,\ ; DRDOS/secured: extended partition + 0xd5 ; Old Multiuser DOS secured: extended partition +{ + cmp al, type + jz .extended +} +; 4. If we are here, that is a normal partition. Add it to the list. +; Note that the first sector is relative to MBR/EBR. + mov eax, ebp + xor edx, edx + add eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + adc edx, 0 + push ecx + stdcall disk_add_partition, eax, edx, \ + [ecx+PARTITION_TABLE_ENTRY.Length], 0, esi + pop ecx +.nothing: +; 5. Return. + ret +.extended: +; 6. If we are here, that is an extended partition. Store the address. + mov eax, [ecx+PARTITION_TABLE_ENTRY.FirstAbsSector] + mov [esp+4], eax + ret + +; This is an internal function called from disk_scan_partitions and +; process_partition_table_entry. It adds one partition to the list of +; partitions for the media. +; Important note: start, length, disk MUST be present and +; MUST be in the same order as in PARTITION structure. +; esi duplicates [disk]. +proc disk_add_partition stdcall uses ebx edi, start:qword, length:qword, disk:dword +; 1. Check that this partition will not exceed the limit on total number. + cmp [esi+DISK.NumPartitions], MAX_NUM_PARTITIONS + jae .nothing +; 2. Check that this partition does not overlap with any already registered +; partition. Since any file system assumes that the disk data will not change +; outside of its control, such overlap could be destructive. +; Since the number of partitions is usually very small and is guaranteed not +; to be large, the simple linear search is sufficient. +; 2a. Prepare the loop: edi will point to the current item of .Partitions +; array, ecx will be the current item, ebx will hold number of items left. + mov edi, [esi+DISK.Partitions] + mov ebx, [esi+DISK.NumPartitions] + test ebx, ebx + jz .partitionok +.scan_existing: +; 2b. Get the next partition. + mov ecx, [edi] + add edi, 4 +; The range [.FirstSector, .FirstSector+.Length) must be either entirely to +; the left of [start, start+length) or entirely to the right. +; 2c. Subtract .FirstSector - start. The possible overflow distinguish between +; cases "to the left" (2e) and "to the right" (2d). + mov eax, dword [ecx+PARTITION.FirstSector] + mov edx, dword [ecx+PARTITION.FirstSector+4] + sub eax, dword [start] + sbb edx, dword [start+4] + jb .less +; 2d. .FirstSector is greater than or equal to start. Check that .FirstSector +; is greater than or equal to start+length; the subtraction +; (.FirstSector-start) - length must not cause overflow. Go to 2g if life is +; good or to 2f in the other case. + sub eax, dword [length] + sbb edx, dword [length+4] + jb .overlap + jmp .next_existing +.less: +; 2e. .FirstSector is less than start. Check that .FirstSector+.Length is less +; than or equal to start. If the addition (.FirstSector-start) + .Length does +; not cause overflow, then .FirstSector + .Length is strictly less than start; +; since the equality is also valid, use decrement preliminarily. Go to 2g or +; 2f depending on the overflow. + sub eax, 1 + sbb edx, 0 + add eax, dword [ecx+PARTITION.Length] + adc edx, dword [ecx+PARTITION.Length+4] + jnc .next_existing +.overlap: +; 2f. The partition overlaps with previously registered partition. Say warning +; and return with nothing done. + dbgstr 'two partitions overlap, ignoring the last one' + jmp .nothing +.next_existing: +; 2g. The partition does not overlap with the current partition. Continue the +; loop. + dec ebx + jnz .scan_existing +.partitionok: +; 3. The partition has passed tests. Reallocate the partitions array for a new +; entry. +; 3a. Call the allocator. + mov eax, [esi+DISK.NumPartitions] + inc eax ; one more entry + shl eax, 2 ; each entry is dword + call malloc +; 3b. Test the result. If failed, return with nothing done. + test eax, eax + jz .nothing +; 3c. Copy the old array to the new array. + mov edi, eax + push esi + mov ecx, [esi+DISK.NumPartitions] + mov esi, [esi+DISK.Partitions] + rep movsd + pop esi +; 3d. Set the field in the DISK structure to the new array. + xchg [esi+DISK.Partitions], eax +; 3e. Free the old array. + call free +; 4. Recognize the file system. +; 4a. Call the filesystem recognizer. It will allocate the PARTITION structure +; with possible filesystem-specific fields. + call disk_detect_partition +; 4b. Check return value. If zero, return with list not changed; so far only +; the array was reallocated, this is ok for other code. + test eax, eax + jz .nothing +; 5. Insert the new partition to the list. + stosd + inc [esi+DISK.NumPartitions] +; 6. Return. +.nothing: + ret +endp + +; This is an internal function called from disk_add_partition. +; It tries to recognize the file system on the partition and allocates the +; corresponding PARTITION structure with filesystem-specific fields. +disk_detect_partition: +; This function inherits the stack frame from disk_add_partition. In stdcall +; with ebp-based frame arguments start from ebp+8, since [ebp]=saved ebp +; and [ebp+4]=return address. +virtual at ebp+8 +.start dq ? +.length dq ? +.disk dd ? +end virtual +; 1. Read the bootsector to the buffer. +; When disk_add_partition is called, ebx contains a pointer to +; a three-sectors-sized buffer. This function saves ebx in the stack +; immediately before ebp. + mov ebx, [ebp-4] ; get buffer + add ebx, [esi+DISK.MediaInfo.SectorSize] ; advance over MBR data to bootsector data + add ebp, 8 ; ebp points to part of PARTITION structure + xor eax, eax ; first sector of the partition + call fs_read32_sys + push eax +; 2. Run tests for all supported filesystems. If at least one test succeeded, +; go to 4. +; For tests: +; ebp -> first three fields of PARTITION structure, .start, .length, .disk; +; [esp] = error code after bootsector read: 0 = ok, otherwise = failed, +; ebx points to the buffer for bootsector, +; ebx+[esi+DISK.MediaInfo.SectorSize] points to sector-sized buffer that can be used for anything. + call fat_create_partition + test eax, eax + jnz .success + call ntfs_create_partition + test eax, eax + jnz .success + call ext2_create_partition + test eax, eax + jnz .success + call xfs_create_partition + test eax, eax + jnz .success +; 3. No file system has recognized the volume, so just allocate the PARTITION +; structure without extra fields. + movi eax, sizeof.PARTITION + call malloc + test eax, eax + jz .nothing + mov edx, dword [ebp+PARTITION.FirstSector] + mov dword [eax+PARTITION.FirstSector], edx + mov edx, dword [ebp+PARTITION.FirstSector+4] + mov dword [eax+PARTITION.FirstSector+4], edx + mov edx, dword [ebp+PARTITION.Length] + mov dword [eax+PARTITION.Length], edx + mov edx, dword [ebp+PARTITION.Length+4] + mov dword [eax+PARTITION.Length+4], edx + mov [eax+PARTITION.Disk], esi + mov [eax+PARTITION.FSUserFunctions], default_fs_functions +.success: +.nothing: + sub ebp, 8 ; restore ebp +; 4. Return with eax = pointer to PARTITION or NULL. + pop ecx + ret + +iglobal +align 4 +default_fs_functions: + dd free + dd (default_fs_functions_end - default_fs_functions - 4) / 4 + dd 0 + dd 0 + dd 0 + dd 0 + dd 0 + dd default_fs_get_file_info +default_fs_functions_end: +endg + +proc default_fs_get_file_info uses edi + movi eax, ERROR_UNSUPPORTED_FS + cmp byte[esi], 0 + jnz .done + movi ecx, 40 ; len of BDFE without filename + cmp [ebx+f70s5arg.xflags], 0 + jz @f + add ecx, 2 ; volume label requested, space for utf16 terminator +@@: + mov ebx, [ebx+f70s5arg.buf] + stdcall is_region_userspace, ebx, ecx + movi eax, ERROR_MEMORY_POINTER + jnz .done + mov edi, ebx + xor eax, eax + rep stosb + mov [ebx+bdfe.attr], 0x10 ; directory flag + mov word[ebx+bdfe.name], 0 ; word because of possible utf16 + mov eax, dword[ebp+PARTITION.Length+DQ.lo] + mov edx, dword[ebp+PARTITION.Length+DQ.hi] + mov ecx, [ebp+PARTITION.Disk] + mov ecx, [ecx+DISK.MediaInfo.SectorSize] + bsf ecx, ecx + shld edx, eax, cl + shl eax, cl + mov [ebx+bdfe.size.lo], eax + mov [ebx+bdfe.size.hi], edx + xor eax, eax +.done: + ret +endp + +; This function is called from file_system_lfn. +; This handler gets the control each time when fn 70 is called +; with unknown item of root subdirectory. +; in: esi = ebp -> path string +; out: if the handler processes path, it must not return in file_system_lfn, +; but instead pop return address and return directly to the caller +; otherwise simply return +dyndisk_handler: + push ebx edi ; save registers used in file_system_lfn +; 1. Acquire the mutex. + mov ecx, disk_list_mutex + call mutex_lock +; 2. Loop over the list of DISK structures. +; 2a. Initialize. + mov ebx, disk_list +.scan: +; 2b. Get the next item. + mov ebx, [ebx+DISK.Next] +; 2c. Check whether the list is done. If so, go to 3. + cmp ebx, disk_list + jz .notfound +; 2d. Compare names. If names match, go to 5. + mov edi, [ebx+DISK.Name] + push esi +@@: +; esi points to the name from fs operation; it is terminated by zero or slash. + lodsb + test al, al + jz .eoin_dec + cmp al, '/' + jz .eoin +; edi points to the disk name. + inc edi +; edi points to lowercase name, this is a requirement for the driver. +; Characters at esi can have any register. Lowercase the current character. +; This lowercasing works for latin letters and digits; since the disk name +; should not contain other symbols, this is ok. + or al, 20h + cmp al, [edi-1] + jz @b +.wrongname: +; 2f. Names don't match. Continue the loop. + pop esi + jmp .scan +.notfound: +; The loop is done and no name matches. +; 3. Release the mutex. + call mutex_unlock +; 4. Return normally. + pop edi ebx ; restore registers used in file_system_lfn + ret +; part of 2d: the name matches partially, but we must check that this is full +; equality. +.eoin_dec: + dec esi +.eoin: + cmp byte [edi], 0 + jnz .wrongname +; We found the addressed DISK structure. +; 5. Reference the disk. + lock inc [ebx+DISK.RefCount] +; 6. Now we are sure that the DISK structure is not going to die at least +; while we are working with it, so release the global mutex. + call mutex_unlock + pop ecx ; pop from the stack saved value of esi +; 7. Acquire the mutex for media object. + pop edi ; restore edi + lea ecx, [ebx+DISK.MediaLock] + call mutex_lock +; 8. Get the media object. If it is not NULL, reference it. + xor edx, edx + cmp [ebx+DISK.MediaInserted], dl + jz @f + mov edx, ebx + inc [ebx+DISK.MediaRefCount] +@@: +; 9. Now we are sure that the media object, if it exists, is not going to die +; at least while we are working with it, so release the mutex for media object. + call mutex_unlock + mov ecx, ebx + pop ebx eax ; restore ebx, pop return address +; 10. Check whether the fs operation wants to enumerate partitions (go to 11) +; or work with some concrete partition (go to 12). + cmp byte [esi], 0 + jnz .haspartition +; 11. The fs operation wants to enumerate partitions. +; Check whether the media is inserted. + mov esi, fs_dyndisk_next_nomedia + test edx, edx + jz @f + mov esi, fs_dyndisk_next +@@: ; Let the procedure from fs_lfn.inc do the job. + jmp file_system_lfn.maindir_noesi + +.root: + pop ecx edx + xor eax, eax + cmp byte [ebx], 9 + jz .cleanup_ecx +.access_denied: + movi eax, ERROR_ACCESS_DENIED +.cleanup_ecx: + mov [esp+32], eax + mov esi, ecx ; disk*dereference assume that esi points to DISK + test edx, edx ; if there are no media, we didn't reference it + jz @f + call disk_media_dereference +@@: + call disk_dereference + stdcall kernel_free, ebp + ret + +.dyndisk_cleanup: + pop ecx edx + movi eax, ERROR_FILE_NOT_FOUND + jmp .cleanup_ecx + +.haspartition: +; 12. The fs operation has specified some partition. + push edx ecx + xor eax, eax + lodsb + sub eax, '0' + jz .dyndisk_cleanup + cmp eax, 10 + jnc .dyndisk_cleanup + mov ecx, eax + lodsb + cmp eax, '/' + jz @f + test eax, eax + jnz .dyndisk_cleanup + dec esi +@@: + cmp byte [esi], 0 + jnz @f + cmp byte [ebx], 1 + jz @f + cmp byte [ebx], 5 + jnz .root +@@: + dec ecx ; convert to zero-based partition index + pop edx ; edx = pointer to DISK, dword [esp] = NULL or edx +; If the driver does not support insert notifications and we are the only fs +; operation with this disk, ask the driver whether the media +; was inserted/removed/changed. Otherwise, assume that media status is valid. + test byte [edx+DISK.DriverFlags], DISK_NO_INSERT_NOTIFICATION + jz .media_accurate + push ecx esi + mov esi, edx + cmp dword [esp+8], 0 + jz .test_no_media + cmp [esi+DISK.MediaRefCount], 2 + jnz .media_accurate_pop + lea edx, [esi+DISK.MediaInfo] + and [edx+DISKMEDIAINFO.Flags], 0 + mov al, DISKFUNC.querymedia + stdcall disk_call_driver, edx + test eax, eax + jz .media_accurate_pop + stdcall disk_media_dereference ; drop our reference so that disk_media_changed could close the media + stdcall disk_media_changed, esi, 0 + and dword [esp+8], 0 ; no media +.test_no_media: + stdcall disk_media_changed, esi, 1 ; issue fake notification +; if querymedia() inside disk_media_changed returns error, the notification is ignored + cmp [esi+DISK.MediaInserted], 0 + jz .media_accurate_pop + lock inc [esi+DISK.MediaRefCount] + mov dword [esp+8], esi +.media_accurate_pop: + mov edx, esi + pop esi ecx +.media_accurate: + pop eax + test eax, eax + jz .nomedia + cmp ecx, [edx+DISK.NumPartitions] + jae .notfound2 + mov eax, [edx+DISK.Partitions] + mov eax, [eax+ecx*4] + mov edi, [eax+PARTITION.FSUserFunctions] + mov ecx, [ebx] + cmp [edi+4], ecx + jbe .unsupported + cmp dword[edi+8+ecx*4], 0 ; user function not implemented + jz .unsupported + pushd edx ebp eax [edi+8+ecx*4] + cmp ecx, 10 + jnz .callFS + or ecx, -1 + mov edi, esi + xor eax, eax + repnz scasb + mov edx, edi + dec edi + mov al, '/' + std + repnz scasb + cld + inc edi + mov [edi], ah + mov ebp, [current_slot] + add ebp, APPDATA.cur_dir + pushd ebx esi edx [ebp] ebp edi + sub esi, 2 + mov [ebp], esi + mov edi, edx + mov esi, [ebx+16] + mov eax, [ebx+20] + cmp eax, 4 + jc @f + xor eax, eax +@@: + call getFullPath + pop edi ebp + mov byte [edi], '/' + popd [ebp] edi esi ebx + add edi, 2 + test eax, eax + jz .errorRename + cmp byte [edi], 0 + jz .errorRename +.callFS: + pop eax ebp + call eax + pop ebp edx + mov dword [esp+20], ebx +.cleanup: + mov dword [esp+32], eax + mov esi, edx + call disk_media_dereference +@@: + call disk_dereference + stdcall kernel_free, ebp + ret + +.unsupported: + movi eax, ERROR_UNKNOWN_FS + cmp edi, default_fs_functions + jz .cleanup + movi eax, ERROR_UNSUPPORTED_FS + jmp .cleanup + +.errorRename: + pop eax eax ebp edx +.notfound2: + movi eax, ERROR_FILE_NOT_FOUND + jmp .cleanup + +.nomedia: + test ecx, ecx + jnz .notfound2 + mov dword [esp+32], ERROR_DEVICE + mov esi, edx + jmp @b + +; This is a callback for enumerating partitions called from +; file_system_lfn.maindir in the case of inserted media. +; It just increments eax until DISK.NumPartitions reached and then +; cleans up. +fs_dyndisk_next: + mov ecx, [esp+8] + cmp eax, [ecx+DISK.NumPartitions] + jae .nomore + inc eax + clc + ret +.nomore: + pusha + mov esi, ecx + call disk_media_dereference + call disk_dereference + popa + stc + ret + +; This is a callback for enumerating partitions called from +; file_system_lfn.maindir in the case of missing media. +; In this case we create one pseudo-partition. +fs_dyndisk_next_nomedia: + cmp eax, 1 + jae .nomore + inc eax + clc + ret +.nomore: + mov ecx, [esp+8] + pusha + mov esi, ecx + call disk_dereference + popa + stc + ret + +; This function is called from file_system_lfn. +; This handler is called when virtual root is enumerated +; and must return all items which can be handled by this. +; It is called several times, first time with eax=0 +; in: eax = 0 for first call, previously returned value for subsequent calls +; out: eax = 0 => no more items +; eax != 0 => buffer pointed to by edi contains name of item +dyndisk_enum_root: + push edx ; save register used in file_system_lfn + mov ecx, disk_list_mutex ; it will be useful +; 1. If this is the first call, acquire the mutex and initialize. + test eax, eax + jnz .notfirst + call mutex_lock + mov eax, disk_list +.notfirst: +; 2. Get next item. + mov eax, [eax+DISK.Next] +; 3. If there are no more items, go to 6. + cmp eax, disk_list + jz .last +; 4. Copy name from the DISK structure to edi. + push eax esi + mov esi, [eax+DISK.Name] +@@: + lodsb + stosb + test al, al + jnz @b + pop esi eax +; 5. Return with eax = item. + pop edx ; restore register used in file_system_lfn + ret +.last: +; 6. Release the mutex and return with eax = 0. + call mutex_unlock + xor eax, eax + pop edx ; restore register used in file_system_lfn + ret diff --git a/kernel/branches/kolibri-lldw/blkdev/disk_cache.inc b/kernel/branches/kolibri-lldw/blkdev/disk_cache.inc new file mode 100644 index 000000000..a6cfd309d --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/disk_cache.inc @@ -0,0 +1,1387 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Read/write functions try to do large operations, +; it is significantly faster than several small operations. +; This requires large buffers. +; We can't use input/output buffers directly - they can be controlled +; by user-mode application, so they can be modified between the operation +; and copying to/from cache, giving invalid data in cache. +; It is unclear how to use cache directly, currently cache items are +; allocated/freed sector-wise, so items for sequential sectors can be +; scattered over all the cache. +; So read/write functions allocate a temporary buffer which is +; 1) not greater than half of free memory and +; 2) not greater than the following constant. +CACHE_MAX_ALLOC_SIZE = 4 shl 20 + +; Legacy interface for filesystems fs_{read,write}32_{sys,app} +; gives only one sector for FS. However, per-sector reading is inefficient, +; so internally fs_read32_{sys,app} reads to the cache several sequential +; sectors, hoping that they will be useful. +; Total number of sectors is given by the following constant. +CACHE_LEGACY_READ_SIZE = 16 + +; This structure describes one item in the cache. +struct CACHE_ITEM +SectorLo dd ? ; low 32 bits of sector +SectorHi dd ? ; high 32 bits of sector +Status dd ? ; one of CACHE_ITEM_* +ends + +; Possible values for CACHE_ITEM_* +CACHE_ITEM_EMPTY = 0 +CACHE_ITEM_COPY = 1 +CACHE_ITEM_MODIFIED = 2 + +; Read several sequential sectors using cache #1. +; in: edx:eax = start sector, relative to start of partition +; in: ecx = number of sectors to read +; in: ebx -> buffer +; in: ebp -> PARTITION +; out: eax = error code, 0 = ok +; out: ecx = number of sectors that were read +fs_read64_sys: +; Save ebx, set ebx to SysCache and let the common part do its work. + push ebx ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.SysCache + jmp fs_read64_common + +; Read several sequential sectors using cache #2. +; in: edx:eax = start sector, relative to start of partition +; in: ecx = number of sectors to read +; in: edi -> buffer +; in: ebp -> PARTITION +; out: eax = error code, 0 = ok +; out: ecx = number of sectors that were read +fs_read64_app: +; Save ebx, set ebx to AppCache and let the common part do its work. + push ebx ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.AppCache + +; Common part of fs_read64_{app,sys}: +; read several sequential sectors using the given cache. +fs_read64_common: +; 1. Setup stack frame. + push esi edi ; save used registers to be stdcall + push 0 ; initialize .error_code + push ebx edx eax ecx ecx ; initialize stack variables +virtual at esp +.local_vars: +.num_sectors_orig dd ? +; Number of sectors that should be read. Used to generate output value of ecx. +.num_sectors dd ? +; Number of sectors that remain to be read. Decreases from .num_sectors_orig to 0. +.sector_lo dd ? ; low 32 bits of the current sector +.sector_hi dd ? ; high 32 bits of the current sector +.cache dd ? ; pointer to DISKCACHE +.error_code dd ? ; current status +.local_vars_size = $ - .local_vars +.saved_regs rd 2 +.buffer dd ? ; filled by fs_read64_{sys,app} +end virtual +; 2. Validate parameters against partition length: +; immediately return error if edx:eax are beyond partition end, +; decrease .num_sectors and .num_sectors_orig, if needed, +; so that the entire operation fits in the partition limits. + mov eax, dword [ebp+PARTITION.Length] + mov edx, dword [ebp+PARTITION.Length+4] + sub eax, [.sector_lo] + sbb edx, [.sector_hi] + jb .end_of_media + jnz .no_end_of_media + cmp ecx, eax + jbe .no_end_of_media +; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA; +; if all subsequent operations would be successful, this would become the final +; status, otherwise this would be rewritten by failed operation. + mov [.num_sectors], eax + mov [.num_sectors_orig], eax + mov [.error_code], DISK_STATUS_END_OF_MEDIA +.no_end_of_media: +; 3. If number of sectors to read is zero, either because zero-sectors operation +; was requested or because it got decreased to zero due to partition limits, +; just return the current status. + cmp [.num_sectors], 0 + jz .return +; 4. Shift sector from partition-relative to absolute. + mov eax, dword [ebp+PARTITION.FirstSector] + mov edx, dword [ebp+PARTITION.FirstSector+4] + add [.sector_lo], eax + adc [.sector_hi], edx +; 5. If the cache is disabled, pass the request directly to the driver. + cmp [ebx+DISKCACHE.pointer], 0 + jz .nocache +; 6. Look for sectors in the cache, sequentially from the beginning. +; Stop at the first sector that is not in the cache +; or when all sectors were read from the cache. +; 6a. Acquire the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_lock +.lookup_in_cache_loop: +; 6b. For each sector, call the lookup function without adding to the cache. + mov eax, [.sector_lo] + mov edx, [.sector_hi] + call cache_lookup_read +; 6c. If it has failed, the sector is not in cache; +; release the lock and go to 7. + jc .not_found_in_cache +; The sector is found in cache. +; 6d. Copy data for the caller, advance [.buffer]. + mov esi, edi + mov edi, [.buffer] + mov eax, 1 + shl eax, cl + mov ecx, eax + shr ecx, 2 + rep movsd + mov [.buffer], edi +; 6e. Advance the sector. + add [.sector_lo], 1 + adc [.sector_hi], 0 +; 6f. Decrement number of sectors left. +; If all sectors were read, release the lock and return. + dec [.num_sectors] + jnz .lookup_in_cache_loop +; Release the lock acquired at 6a. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +.return: + mov eax, [.error_code] + mov ecx, [.num_sectors_orig] + sub ecx, [.num_sectors] +.nothing: + add esp, .local_vars_size + pop edi esi ebx ebx ; restore used registers to be stdcall + ret +.not_found_in_cache: +; Release the lock acquired at 6a. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +; The current sector is not present in the cache. +; Ask the driver to read all requested not-yet-read sectors, +; put results in the cache. +; Also, see the comment before the definition of CACHE_MAX_ALLOC_SIZE. +; 7. Allocate buffer for operations. +; Normally, create buffer that is sufficient for all remaining data. +; However, for extra-large requests make an upper limit: +; do not use more than half of the free memory +; or more than CACHE_MAX_ALLOC_SIZE bytes. + mov ecx, [ebx+DISKCACHE.sector_size_log] + mov ebx, [pg_data.pages_free] + shr ebx, 1 + jz .nomemory + cmp ebx, CACHE_MAX_ALLOC_SIZE shr 12 + jbe @f + mov ebx, CACHE_MAX_ALLOC_SIZE shr 12 +@@: + shl ebx, 12 + shr ebx, cl + jz .nomemory + cmp ebx, [.num_sectors] + jbe @f + mov ebx, [.num_sectors] +@@: + mov eax, ebx + shl eax, cl + stdcall kernel_alloc, eax +; If failed, return the appropriate error code. + test eax, eax + jz .nomemory + mov esi, eax +; Split the request to chunks that fit in the allocated buffer. +.read_loop: +; 8. Get iteration size: either size of allocated buffer in sectors +; or number of sectors left, select what is smaller. + cmp ebx, [.num_sectors] + jbe @f + mov ebx, [.num_sectors] +@@: +; 9. Create second portion of local variables. +; Note that variables here and above are esp-relative; +; it means that all addresses should be corrected when esp is changing. + push ebx esi esi + push ebx +; In particular, num_sectors is now [.num_sectors+.local_vars2_size]. +virtual at esp +.local_vars2: +.current_num_sectors dd ? ; number of sectors that were read +.current_buffer dd ? +; pointer inside .allocated_buffer that points +; to the beginning of not-processed data +.allocated_buffer dd ? ; saved in safe place +.iteration_size dd ? ; saved in safe place +.local_vars2_size = $ - .local_vars2 +end virtual +; 10. Call the driver, reading the next chunk. + push esp ; numsectors + push [.sector_hi+.local_vars2_size+4] ; startsector + push [.sector_lo+.local_vars2_size+8] ; startsector + push esi ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver +; If failed, save error code. + test eax, eax + jz @f + mov [.error_code+.local_vars2_size], eax +@@: +; 11. Copy data for the caller, advance .buffer. + cmp [.current_num_sectors], 0 + jz .copy_done + mov ebx, [.cache+.local_vars2_size] + mov eax, [.current_num_sectors] + mov ecx, [ebx+DISKCACHE.sector_size_log] + shl eax, cl + mov esi, [.allocated_buffer] + mov edi, [.buffer+.local_vars2_size] + mov ecx, eax + shr ecx, 2 + rep movsd + mov [.buffer+.local_vars2_size], edi +; 12. Copy data to the cache. +; 12a. Acquire the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_lock +; 12b. Prepare for the loop: create a local variable that +; stores number of sectors to be copied. + push [.current_num_sectors] +.store_to_cache: +; 12c. For each sector, call the lookup function with adding to the cache, if not yet. + mov eax, [.sector_lo+.local_vars2_size+4] + mov edx, [.sector_hi+.local_vars2_size+4] + call cache_lookup_write + test eax, eax + jnz .cache_error +; 12d. If the sector was already present in the cache as modified, +; data that were read at step 10 for this sector are obsolete, +; so rewrite data for the caller from the cache. + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .not_modified + mov esi, edi + mov edi, [.buffer+.local_vars2_size+4] + mov eax, [esp] + shl eax, cl + sub edi, eax + mov eax, 1 + shl eax, cl + mov ecx, eax + shr ecx, 2 + rep movsd + add [.current_buffer+4], eax + jmp .sector_done +.not_modified: +; 12e. For each not-modified sector, +; copy data, mark the item as not-modified copy of the disk, +; advance .current_buffer and .sector_hi:.sector_lo to the next sector. + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY + mov eax, 1 + shl eax, cl + mov esi, [.current_buffer+4] + mov ecx, eax + shr ecx, 2 + rep movsd + mov [.current_buffer+4], esi +.sector_done: + add [.sector_lo+.local_vars2_size+4], 1 + adc [.sector_hi+.local_vars2_size+4], 0 +; 12f. Continue the loop 12c-12e until all sectors are read. + dec dword [esp] + jnz .store_to_cache +.cache_error: +; 12g. Restore after the loop: pop the local variable. + pop ecx +; 12h. Release the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +.copy_done: +; 13. Remove portion of local variables created at step 9. + pop ecx + pop esi esi ebx +; 14. Continue iterations while number of sectors read by the driver +; is equal to number of sectors requested and there are additional sectors. + cmp ecx, ebx + jnz @f + sub [.num_sectors], ebx + jnz .read_loop +@@: +; 15. Free the buffer allocated at step 7 and return. + stdcall kernel_free, esi + jmp .return + +; Special branches: +.nomemory: +; memory allocation failed at step 7: return the corresponding error + mov [.error_code], DISK_STATUS_NO_MEMORY + jmp .return +.nocache: +; step 5, after correcting number of sectors to fit in partition limits +; and advancing partition-relative sector to absolute, +; sees that cache is disabled: pass corrected request to the driver + lea eax, [.num_sectors] + push eax ; numsectors + push [.sector_hi+4] ; startsector + push [.sector_lo+8] ; startsector + push [.buffer+12] ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + test eax, eax + jnz @f + mov eax, [.error_code] +@@: + mov ecx, [.num_sectors] + jmp .nothing +.end_of_media: +; requested sector is beyond the partition end: return the corresponding error + mov [.error_code], DISK_STATUS_END_OF_MEDIA + jmp .return + +; Write several sequential sectors using cache #1. +; in: edx:eax = start sector +; in: ecx = number of sectors to write +; in: ebx -> buffer +; in: ebp -> PARTITION +; out: eax = error code, 0 = ok +; out: ecx = number of sectors that were written +fs_write64_sys: +; Save ebx, set ebx to SysCache and let the common part do its work. + push ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.SysCache + jmp fs_write64_common + +; Write several sequential sectors using cache #2. +; in: edx:eax = start sector +; in: ecx = number of sectors to write +; in: ebx -> buffer +; in: ebp -> PARTITION +; out: eax = error code, 0 = ok +; out: ecx = number of sectors that were written +fs_write64_app: +; Save ebx, set ebx to AppCache and let the common part do its work. + push ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.AppCache + +; Common part of fs_write64_{app,sys}: +; write several sequential sectors using the given cache. +fs_write64_common: +; 1. Setup stack frame. + push esi edi ; save used registers to be stdcall + push 0 ; initialize .error_code + push edx eax ecx ecx ; initialize stack variables + push [.buffer-4] ; copy [.buffer] to [.cur_buffer] + ; -4 is due to esp-relative addressing +virtual at esp +.local_vars: +.cur_buffer dd ? ; pointer to data that are currently copying +.num_sectors_orig dd ? +; Number of sectors that should be written. Used to generate output value of ecx. +.num_sectors dd ? +; Number of sectors that remain to be written. +.sector_lo dd ? ; low 32 bits of the current sector +.sector_hi dd ? ; high 32 bits of the current sector +.error_code dd ? ; current status +.local_vars_size = $ - .local_vars +.saved_regs rd 2 +.buffer dd ? ; filled by fs_write64_{sys,app} +end virtual +; 2. Validate parameters against partition length: +; immediately return error if edx:eax are beyond partition end, +; decrease .num_sectors and .num_sectors_orig, if needed, +; so that the entire operation fits in the partition limits. + mov eax, dword [ebp+PARTITION.Length] + mov edx, dword [ebp+PARTITION.Length+4] + sub eax, [.sector_lo] + sbb edx, [.sector_hi] + jb .end_of_media + jnz .no_end_of_media + cmp ecx, eax + jbe .no_end_of_media +; If .num_sectors got decreased, set status to DISK_STATUS_END_OF_MEDIA; +; if all subsequent operations would be successful, this would become the final +; status, otherwise this would be rewritten by failed operation. + mov [.num_sectors], eax + mov [.num_sectors_orig], eax + mov [.error_code], DISK_STATUS_END_OF_MEDIA +.no_end_of_media: +; 3. If number of sectors to write is zero, either because zero-sectors operation +; was requested or because it got decreased to zero due to partition limits, +; just return the current status. + cmp [.num_sectors], 0 + jz .return +; 4. Shift sector from partition-relative to absolute. + mov eax, dword [ebp+PARTITION.FirstSector] + mov edx, dword [ebp+PARTITION.FirstSector+4] + add [.sector_lo], eax + adc [.sector_hi], edx +; 5. If the cache is disabled, pass the request directly to the driver. + cmp [ebx+DISKCACHE.pointer], 0 + jz .nocache +; 6. Store sectors in the cache, sequentially from the beginning. +; 6a. Acquire the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_lock +.lookup_in_cache_loop: +; 6b. For each sector, call the lookup function with adding to the cache, if not yet. + mov eax, [.sector_lo] + mov edx, [.sector_hi] + call cache_lookup_write + test eax, eax + jnz .cache_error +; 6c. For each sector, copy data, mark the item as modified and not saved, +; advance .current_buffer to the next sector. + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + mov eax, 1 + shl eax, cl + mov esi, [.cur_buffer] + mov ecx, eax + shr ecx, 2 + rep movsd + mov [.cur_buffer], esi +; 6d. Remove the sector from the other cache. +; Normally it should not be there, but prefetching could put to the app cache +; data that normally should belong to the sys cache and vice versa. +; Note: this requires that both caches must be protected by the same lock. + mov eax, [.sector_lo] + mov edx, [.sector_hi] + push ebx + sub ebx, [ebp+PARTITION.Disk] + xor ebx, DISK.SysCache xor DISK.AppCache + add ebx, [ebp+PARTITION.Disk] + call cache_lookup_read + jc @f + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY +@@: + pop ebx +; 6e. Advance .sector_hi:.sector_lo to the next sector. + add [.sector_lo], 1 + adc [.sector_hi], 0 +; 6f. Continue the loop at 6b-6e until all sectors are processed. + dec [.num_sectors] + jnz .lookup_in_cache_loop +.unlock_return: +; 6g. Release the lock and return. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +.return: + mov eax, [.error_code] + mov ecx, [.num_sectors_orig] + sub ecx, [.num_sectors] +.nothing: + add esp, .local_vars_size + pop edi esi ebx + ret + +; Special branches: +.cache_error: +; error at flushing the cache while adding sector to the cache: +; return the error from the lookup function + mov [.error_code], eax + jmp .unlock_return +.end_of_media: +; requested sector is beyond the partition end: return the corresponding error + mov eax, DISK_STATUS_END_OF_MEDIA + xor ecx, ecx + jmp .nothing +.nocache: +; step 5, after correcting number of sectors to fit in partition limits +; and advancing partition-relative sector to absolute, +; sees that cache is disabled: pass corrected request to the driver + lea eax, [.num_sectors] + push eax ; numsectors + push [.sector_hi+4] ; startsector + push [.sector_lo+8] ; startsector + push [.buffer+12] ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver + mov ecx, [.num_sectors] + jmp .nothing + +; Legacy. Use fs_read64_sys instead. +; This function is intended to replace the old 'hd_read' function when +; [hdd_appl_data] = 0, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_sys: +; Save ebx, set ebx to SysCache and let the common part do its work. + push ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.SysCache + jmp fs_read32_common + +; Legacy. Use fs_read64_app instead. +; This function is intended to replace the old 'hd_read' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_read32_app: +; Save ebx, set ebx to AppCache and let the common part do its work. + push ebx + mov ebx, [ebp+PARTITION.Disk] + add ebx, DISK.AppCache + +; This label is the common part of fs_read32_sys and fs_read32_app. +fs_read32_common: +; 1. Check that the required sector is inside the partition. If no, return +; DISK_STATUS_END_OF_MEDIA. + cmp dword [ebp+PARTITION.Length+4], 0 + jnz @f + cmp dword [ebp+PARTITION.Length], eax + ja @f + mov eax, DISK_STATUS_END_OF_MEDIA + pop ebx + ret +@@: +; 2. Get the absolute sector on the disk. + push ecx edx esi edi + xor edx, edx + add eax, dword [ebp+PARTITION.FirstSector] + adc edx, dword [ebp+PARTITION.FirstSector+4] +; 3. If there is no cache for this disk, just pass the request to the driver. + cmp [ebx+DISKCACHE.pointer], 0 + jnz .scancache + push 1 + push esp ; numsectors + push edx ; startsector + push eax ; startsector + pushd [esp+32]; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + pop edi esi edx ecx + pop ebx + ret +.scancache: + push ebx edx eax +virtual at esp +.local_vars: +.sector_lo dd ? +.sector_hi dd ? +.cache dd ? +.local_vars_size = $ - .local_vars +.saved_regs rd 4 +.buffer dd ? +end virtual +; 4. Scan for the requested sector in the cache. +; If found, copy the data and return. +; 4a. Acquire the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_lock +; 4b. Call the lookup function without adding to the cache. + mov eax, [.sector_lo] + mov edx, [.sector_hi] + call cache_lookup_read +; If not found, go to 5. + jc .not_found_in_cache +.found_in_cache: +; 4c. Copy the data. + mov esi, edi + mov edi, [.buffer] + mov eax, 1 + shl eax, cl + mov ecx, eax + shr ecx, 2 + rep movsd +; 4d. Release the lock and return success. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +.return: + xor eax, eax +.return_eax: + add esp, .local_vars_size + pop edi esi edx ecx + pop ebx + ret +.not_found_in_cache: +; 5. Decide whether we need to prefetch further sectors. +; If so, advance to 6. If not, go to 13. +; Assume that devices < 3MB are floppies which are slow +; (ramdisk does not have a cache, so we don't even get here for ramdisk). +; This is a dirty hack, but the entire function is somewhat hacky. Use fs_read64*. + mov ecx, [ebp+PARTITION.Disk] + cmp dword [ecx+DISK.MediaInfo.Capacity+4], 0 + jnz @f + cmp dword [ecx+DISK.MediaInfo.Capacity], 3 shl (20-9) + jb .floppy +@@: +; We want to prefetch CACHE_LEGACY_READ_SIZE sectors. +; 6. Release the lock acquired at step 4a. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +; 7. Allocate buffer for CACHE_LEGACY_READ_SIZE sectors. + mov eax, CACHE_LEGACY_READ_SIZE + mov ecx, [ebx+DISKCACHE.sector_size_log] + shl eax, cl + stdcall kernel_alloc, eax +; If failed, return the corresponding error code. + test eax, eax + jz .nomemory +; 8. Create second portion of local variables. + push eax eax + push CACHE_LEGACY_READ_SIZE +virtual at esp +.local_vars2: +.num_sectors dd ? ; number of sectors left +.current_buffer dd ? ; pointer to data that are currently copying +.allocated_buffer dd ? ; saved at safe place +.local_vars2_size = $ - .local_vars2 +end virtual +; 9. Call the driver to read CACHE_LEGACY_READ_SIZE sectors. + push esp ; numsectors + push [.sector_hi+.local_vars2_size+4] ; startsector + push [.sector_lo+.local_vars2_size+8] ; startsector + push eax ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver +; Note: we're ok if at least one sector is read, +; read error somewhere after that just limits data to be put in cache. + cmp [.num_sectors], 0 + jz .read_error +; 10. Copy data for the caller. + mov esi, [.allocated_buffer] + mov edi, [.buffer+.local_vars2_size] + mov ecx, [ebx+DISKCACHE.sector_size_log] + mov eax, 1 + shl eax, cl + mov ecx, eax + shr ecx, 2 + rep movsd +; 11. Store all sectors that were successfully read to the cache. +; 11a. Acquire the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_lock +.store_to_cache: +; 11b. For each sector, call the lookup function with adding to the cache, if not yet. + mov eax, [.sector_lo+.local_vars2_size] + mov edx, [.sector_hi+.local_vars2_size] + call cache_lookup_write + test eax, eax + jnz .cache_error +; 11c. Ignore sectors marked as modified: for them the cache is more recent that disk data. + mov eax, 1 + shl eax, cl + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .not_modified + add [.current_buffer], eax + jmp .sector_done +.not_modified: +; 11d. For each sector, copy data, mark the item as not-modified copy of the disk, +; advance .current_buffer and .sector_hi:.sector_lo to the next sector. + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY + mov esi, [.current_buffer] + mov ecx, eax + shr ecx, 2 + rep movsd + mov [.current_buffer], esi +.sector_done: + add [.sector_lo+.local_vars2_size], 1 + adc [.sector_hi+.local_vars2_size], 0 +; 11e. Continue the loop at 11b-11d until all sectors are processed. + dec [.num_sectors] + jnz .store_to_cache +.cache_error: +; 11f. Release the lock. + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + call mutex_unlock +.copy_done: +; 12. Remove portion of local variables created at step 8, +; free the buffer allocated at step 7 and return. + pop ecx ecx + stdcall kernel_free + jmp .return +.read_error: +; If no sectors were read, free the buffer allocated at step 7 +; and pass the error to the caller. + push eax + stdcall kernel_free, [.allocated_buffer+4] + pop eax + add esp, .local_vars2_size + jmp .return_eax +.nomemory: + mov eax, DISK_STATUS_NO_MEMORY + jmp .return_eax +.floppy: +; We don't want to prefetch anything, just read one sector. +; We are still holding the lock acquired at step 4a. +; 13. Call the lookup function adding sector to the cache. + call cache_lookup_write + test eax, eax + jnz .floppy_cache_error + push esi + +; 14. Call the driver to read one sector. + push 1 + push esp + push edx + push [.sector_lo+16] + push edi + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.read + call disk_call_driver + pop ecx + dec ecx + jnz .floppy_read_error +; 15. Get the slot and pointer to the cache item, +; change the status to not-modified copy of the disk +; and go to 4c. + pop esi + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY + mov ecx, [ebx+DISKCACHE.sector_size_log] + jmp .found_in_cache + +; On error at steps 13-14, release the lock +; and pass the error to the caller. +.floppy_read_error: + pop ecx +.floppy_cache_error: + mov ecx, [ebp+PARTITION.Disk] + add ecx, DISK.CacheLock + push eax + call mutex_unlock + pop eax + jmp .return_eax + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 0, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_sys: +; Just call the advanced function. + push ecx edx + xor edx, edx + mov ecx, 1 + call fs_write64_sys + pop edx ecx + ret + +; This function is intended to replace the old 'hd_write' function when +; [hdd_appl_data] = 1, so its input/output parameters are the same, except +; that it can't use the global variables 'hd_error' and 'hdd_appl_data'. +; in: eax = sector, ebx = buffer, ebp = pointer to PARTITION structure +; eax is relative to partition start +; out: eax = error code; 0 = ok +fs_write32_app: +; Just call the advanced function. + push ecx edx + xor edx, edx + mov ecx, 1 + call fs_write64_app + pop edx ecx + ret + +; Lookup for the given sector in the given cache. +; If the sector is not present, return error. +; The caller must acquire the cache lock. +; in: edx:eax = sector +; in: ebx -> DISKCACHE structure +; out: CF set if sector is not in cache +; out: ecx = sector_size_log +; out: esi -> sector:status +; out: edi -> sector data +proc cache_lookup_read + mov esi, [ebx+DISKCACHE.pointer] + add esi, sizeof.CACHE_ITEM + + mov edi, 1 + +.hdreadcache: + + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY + je .nohdcache + + cmp [esi+CACHE_ITEM.SectorLo], eax + jne .nohdcache + cmp [esi+CACHE_ITEM.SectorHi], edx + jne .nohdcache + mov ecx, [ebx+DISKCACHE.sector_size_log] + shl edi, cl + add edi, [ebx+DISKCACHE.data] + clc + ret + +.nohdcache: + + add esi, sizeof.CACHE_ITEM + inc edi + cmp edi, [ebx+DISKCACHE.sad_size] + jbe .hdreadcache + stc + ret +endp + +; Lookup for the given sector in the given cache. +; If the sector is not present, allocate space for it, +; possibly flushing data. +; in: edx:eax = sector +; in: ebx -> DISKCACHE structure +; in: ebp -> PARTITION structure +; out: eax = error code +; out: esi -> sector:status +; out: edi -> sector data +proc cache_lookup_write + call cache_lookup_read + jnc .return0 + push edx eax +;----------------------------------------------------------- +; find empty or read slot, flush cache if next 12.5% is used by write +; output : ecx = cache slot +;----------------------------------------------------------- +; Note: the code is essentially inherited, so probably +; no analysis of efficiency were done. +; However, it works. +.search_again: + mov eax, [ebx+DISKCACHE.sad_size] + mov ecx, [ebx+DISKCACHE.search_start] + shr eax, 3 + lea esi, [ecx*sizeof.CACHE_ITEM/4] + shl esi, 2 + add esi, [ebx+DISKCACHE.pointer] +.search_for_empty: + inc ecx + add esi, sizeof.CACHE_ITEM + cmp ecx, [ebx+DISKCACHE.sad_size] + jbe .inside_cache + mov ecx, 1 + mov esi, [ebx+DISKCACHE.pointer] + add esi, sizeof.CACHE_ITEM +.inside_cache: + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jb .found_slot ; it's empty or read + dec eax + jnz .search_for_empty + stdcall write_cache64, [ebp+PARTITION.Disk] ; no empty slots found, write all + test eax, eax + jne .found_slot_access_denied + jmp .search_again ; and start again +.found_slot: + mov [ebx+DISKCACHE.search_start], ecx + popd [esi+CACHE_ITEM.SectorLo] + popd [esi+CACHE_ITEM.SectorHi] + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_EMPTY + mov edi, ecx + mov ecx, [ebx+DISKCACHE.sector_size_log] + shl edi, cl + add edi, [ebx+DISKCACHE.data] +.return0: + xor eax, eax ; success + ret +.found_slot_access_denied: + add esp, 8 + ret +endp + +; Flush the given cache. +; The caller must acquire the cache lock. +; in: ebx -> DISKCACHE +; in: first argument in stdcall convention -> PARTITION +proc write_cache64 +; 1. Setup stack frame. + push esi edi ; save used registers to be stdcall + sub esp, .local_vars_size ; reserve space for local vars +virtual at esp +.local_vars: +.cache_end dd ? ; item past the end of the cache +.size_left dd ? ; items left to scan +.current_ptr dd ? ; pointer to the current item +; +; Write operations are coalesced in chains, +; one chain describes a sequential interval of sectors, +; they can be sequential or scattered in the cache. +.sequential dd ? +; boolean variable, 1 if the current chain is sequential in the cache, +; 0 if additional buffer is needed to perform the operation +.chain_start_pos dd ? ; data of chain start item +.chain_start_ptr dd ? ; pointer to chain start item +.chain_size dd ? ; chain size (thanks, C.O.) +.iteration_size dd ? +; If the chain size is too large, split the operation to several iterations. +; This is size in sectors for one iterations. +.iteration_buffer dd ? ; temporary buffer for non-sequential chains +.local_vars_size = $ - .local_vars + rd 2 ; saved registers + dd ? ; return address +.disk dd ? ; first argument +end virtual +; 1. If there is no cache for this disk, nothing to do, just return zero. + cmp [ebx+DISKCACHE.pointer], 0 + jz .return0 +; 2. Prepare for the loop: initialize current pointer and .size_left, +; calculate .cache_end. + mov ecx, [ebx+DISKCACHE.sad_size] + mov [.size_left], ecx + lea ecx, [ecx*sizeof.CACHE_ITEM/4] + shl ecx, 2 + mov esi, [ebx+DISKCACHE.pointer] + add esi, sizeof.CACHE_ITEM + add ecx, esi + mov [.cache_end], ecx +; 3. Main loop: go over all items, go to 5 for every modified item. +.look: + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jz .begin_write +.look_next: + add esi, sizeof.CACHE_ITEM + dec [.size_left] + jnz .look +; 4. Return success. +.return0: + xor eax, eax +.return: + add esp, .local_vars_size + pop edi esi ; restore used registers to be stdcall + ret 4 ; return popping one argument +.begin_write: +; We have found a modified item. +; 5. Prepare for chain finding: save the current item, initialize chain variables. + mov [.current_ptr], esi +; Initialize chain as sequential zero-length starting at the current item. + mov [.chain_start_ptr], esi + mov eax, [ebx+DISKCACHE.sad_size] + sub eax, [.size_left] + inc eax + mov ecx, [ebx+DISKCACHE.sector_size_log] + shl eax, cl + add eax, [ebx+DISKCACHE.data] + mov [.chain_start_pos], eax + mov [.chain_size], 0 + mov [.sequential], 1 +; 6. Expand the chain backward. +; Note: the main loop in step 2 looks for items sequentially, +; so the previous item is not modified. If the previous sector +; is present in the cache, it automatically makes the chain scattered. +; 6a. Calculate sector number: one before the sector for the current item. + mov eax, [esi+CACHE_ITEM.SectorLo] + mov edx, [esi+CACHE_ITEM.SectorHi] + sub eax, 1 + sbb edx, 0 +.find_chain_start: +; 6b. For each sector where the previous item does not expand the chain, +; call the lookup function without adding to the cache. + call cache_lookup_read +; 6c. If the sector is not found in cache or is not modified, stop expanding +; and advance to step 7. + jc .found_chain_start + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .found_chain_start +; 6d. We have found a new block that expands the chain backwards. +; It makes the chain non-sequential. +; Normally, sectors come in sequential blocks, so try to look at previous items +; before returning to 6b; if there is a sequential block indeed, this saves some +; time instead of many full-fledged lookups. + mov [.sequential], 0 + mov [.chain_start_pos], edi +.look_backward: +; 6e. For each sector, update chain start pos/ptr, decrement sector number, +; look at the previous item. + mov [.chain_start_ptr], esi + inc [.chain_size] + sub eax, 1 + sbb edx, 0 + sub esi, sizeof.CACHE_ITEM +; If the previous item exists... + cmp esi, [ebx+DISKCACHE.pointer] + jbe .find_chain_start +; ...describes the correct sector... + cmp [esi+CACHE_ITEM.SectorLo], eax + jnz .find_chain_start + cmp [esi+CACHE_ITEM.SectorHi], edx + jnz .find_chain_start +; ...and is modified... + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .found_chain_start +; ...expand the chain one sector backwards and continue the loop at 6e. +; Otherwise, advance to step 7 if the previous item describes the correct sector +; but is not modified, and return to step 6b otherwise. + mov edi, 1 + shl edi, cl + sub [.chain_start_pos], edi + jmp .look_backward +.found_chain_start: +; 7. Expand the chain forward. +; 7a. Prepare for the loop at 7b: +; set esi = pointer to current item, edx:eax = current sector. + mov esi, [.current_ptr] + mov eax, [esi+CACHE_ITEM.SectorLo] + mov edx, [esi+CACHE_ITEM.SectorHi] +.look_forward: +; 7b. First, look at the next item. If it describes the next sector: +; if it is modified, expand the chain with that sector and continue this step, +; if it is not modified, the chain is completed, so advance to step 8. + inc [.chain_size] + add eax, 1 + adc edx, 0 + add esi, sizeof.CACHE_ITEM + cmp esi, [.cache_end] + jae .find_chain_end + cmp [esi+CACHE_ITEM.SectorLo], eax + jnz .find_chain_end + cmp [esi+CACHE_ITEM.SectorHi], edx + jnz .find_chain_end + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .found_chain_end + jmp .look_forward +.find_chain_end: +; 7c. Otherwise, call the lookup function. + call cache_lookup_read +; 7d. If the next sector is present in the cache and is modified, +; mark the chain as non-sequential and continue to step 7b. + jc .found_chain_end + cmp [esi+CACHE_ITEM.Status], CACHE_ITEM_MODIFIED + jnz .found_chain_end + mov [.sequential], 0 + jmp .look_forward +.found_chain_end: +; 8. Decide whether the chain is sequential or scattered. +; Advance to step 9 for sequential chains, go to step 10 for scattered chains. + cmp [.sequential], 0 + jz .write_non_sequential +.write_sequential: +; 9. Write a sequential chain to disk. +; 9a. Pass the entire chain to the driver. + mov eax, [.chain_start_ptr] + lea ecx, [.chain_size] + push ecx ; numsectors + pushd [eax+CACHE_ITEM.SectorHi] ; startsector + pushd [eax+CACHE_ITEM.SectorLo] ; startsector + push [.chain_start_pos+12] ; buffer + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver +; 9b. If failed, pass the error code to the driver. + test eax, eax + jnz .return +; 9c. If succeeded, mark all sectors in the chain as not-modified, +; advance current item and number of items left to skip the chain. + mov esi, [.current_ptr] + mov eax, [.chain_size] + sub [.size_left], eax +@@: + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY + add esi, sizeof.CACHE_ITEM + dec eax + jnz @b +; 9d. Continue the main loop at step 2 if there are more sectors. +; Return success otherwise. + cmp [.size_left], 0 + jnz .look + jmp .return0 +.write_non_sequential: +; Write a non-sequential chain to the disk. +; 10. Allocate a temporary buffer. +; Use [.chain_size] sectors, but +; not greater than CACHE_MAX_ALLOC_SIZE bytes +; and not greater than half of free memory. + mov eax, [pg_data.pages_free] + shr eax, 1 + jz .nomemory + cmp eax, CACHE_MAX_ALLOC_SIZE shr 12 + jbe @f + mov eax, CACHE_MAX_ALLOC_SIZE shr 12 +@@: + shl eax, 12 + shr eax, cl + jz .nomemory + cmp eax, [.chain_size] + jbe @f + mov eax, [.chain_size] +@@: + mov [.iteration_size], eax + shl eax, cl + stdcall kernel_alloc, eax + test eax, eax + jz .nomemory + mov [.iteration_buffer], eax +.write_non_sequential_iteration: +; 11. Split the chain so that each iteration fits in the allocated buffer. +; Iteration size is the minimum of chain size and allocated size. + mov eax, [.chain_size] + cmp eax, [.iteration_size] + jae @f + mov [.iteration_size], eax +@@: +; 12. Prepare arguments for the driver. + mov esi, [.chain_start_ptr] + mov edi, [.iteration_buffer] + push [.iteration_size] + push esp ; numsectors + push [esi+CACHE_ITEM.SectorHi] ; startsector + push [esi+CACHE_ITEM.SectorLo] ; startsector + push edi ; buffer +; 13. Copy data from the cache to the temporary buffer, +; advancing chain_start pos/ptr and marking sectors as not-modified. +; 13a. Prepare for the loop: push number of sectors to process. + push [.iteration_size+20] ; temporary variable +.copy_loop: +; 13b. For each sector, copy the data. +; Note that edi is advanced automatically. + mov esi, [.chain_start_pos+24] + mov ecx, [ebx+DISKCACHE.sector_size_log] + mov eax, 1 + shl eax, cl + mov ecx, eax + shr ecx, 2 + rep movsd + mov ecx, eax ; keep for 13e +; 13c. Mark the item as not-modified. + mov esi, [.chain_start_ptr+24] + mov [esi+CACHE_ITEM.Status], CACHE_ITEM_COPY +; 13d. Check whether the next sector continues the chain. +; If so, advance to 13e. Otherwise, go to 13f. + mov eax, [esi+CACHE_ITEM.SectorLo] + mov edx, [esi+CACHE_ITEM.SectorHi] + add esi, sizeof.CACHE_ITEM + add eax, 1 + adc edx, 0 + cmp esi, [.cache_end+24] + jae .no_forward + cmp [esi+CACHE_ITEM.SectorLo], eax + jnz .no_forward + cmp [esi+CACHE_ITEM.SectorHi], edx + jnz .no_forward +; 13e. Increment position/pointer to the chain and +; continue the loop. + add [.chain_start_pos+24], ecx + mov [.chain_start_ptr+24], esi + dec dword [esp] + jnz .copy_loop + jmp .copy_done +.no_forward: +; 13f. Call the lookup function without adding to the cache. +; Update position/pointer with returned value. +; Note: for the last sector in the chain, edi/esi may contain +; garbage; we are not going to use them in this case. + push edi + call cache_lookup_read + mov [.chain_start_pos+28], edi + mov [.chain_start_ptr+28], esi + pop edi + dec dword [esp] + jnz .copy_loop +.copy_done: +; 13g. Restore the stack after 13a. + pop ecx +; 14. Call the driver. + mov esi, [ebp+PARTITION.Disk] + mov al, DISKFUNC.write + call disk_call_driver + pop ecx ; numsectors +; 15. If the driver has returned an error, free the buffer allocated at step 10 +; and pass the error to the caller. +; Otherwise, remove the processed part from the chain and continue iterations +; starting in step 11 if there are more data to process. + test eax, eax + jnz .nonsequential_error + sub [.chain_size], ecx + jnz .write_non_sequential_iteration +; 16. The chain is written. Free the temporary buffer +; and continue the loop at step 2. + stdcall kernel_free, [.iteration_buffer] + mov esi, [.current_ptr] + jmp .look_next +.nonsequential_error: + push eax + stdcall kernel_free, [.iteration_buffer+4] + pop eax + jmp .return +.nomemory: + mov eax, DISK_STATUS_NO_MEMORY + jmp .return +endp + +; This internal function is called from disk_add to initialize the caching for +; a new DISK. +; The algorithm is inherited from getcache.inc: take 1/32 part of the available +; physical memory, round down to 8 pages, limit by 128K from below and by 1M +; from above. Reserve 1/8 part of the cache for system data and 7/8 for app +; data. +; After the size is calculated, but before the cache is allocated, the device +; driver can adjust the size. In particular, setting size to zero disables +; caching: there is no sense in a cache for a ramdisk. In fact, such action +; is most useful example of a non-trivial adjustment. +; esi = pointer to DISK structure +disk_init_cache: +; 1. Verify sector size. The code requires it to be a power of 2 not less than 4. +; In the name of sanity check that sector size is not too small or too large. + bsf ecx, [esi+DISK.MediaInfo.SectorSize] + jz .invalid_sector_size + mov eax, 1 + shl eax, cl + cmp eax, [esi+DISK.MediaInfo.SectorSize] + jnz .invalid_sector_size + cmp ecx, 6 + jb .invalid_sector_size + cmp ecx, 14 + jbe .normal_sector_size +.invalid_sector_size: + DEBUGF 1,'K : sector size %x is invalid\n',[esi+DISK.MediaInfo.SectorSize] + xor eax, eax + ret +.normal_sector_size: + mov [esi+DISK.SysCache.sector_size_log], ecx + mov [esi+DISK.AppCache.sector_size_log], ecx +; 2. Calculate the suggested cache size. +; 2a. Get the size of free physical memory in pages. + mov eax, [pg_data.pages_free] +; 2b. Use the value to calculate the size. + shl eax, 12 - 5 ; 1/32 of it in bytes + and eax, -8*4096 ; round down to the multiple of 8 pages +; 2c. Force lower and upper limits. + cmp eax, 1024*1024 + jb @f + mov eax, 1024*1024 +@@: + cmp eax, 128*1024 + ja @f + mov eax, 128*1024 +@@: +; 2d. Give a chance to the driver to adjust the size. + push eax + mov al, DISKFUNC.adjust_cache_size + call disk_call_driver +; Cache size calculated. + mov [esi+DISK.cache_size], eax + test eax, eax + jz .nocache +; 3. Allocate memory for the cache. +; 3a. Call the allocator. + stdcall kernel_alloc, eax + test eax, eax + jnz @f +; 3b. If it failed, say a message and return with eax = 0. + dbgstr 'no memory for disk cache' + jmp .nothing +@@: +; 4. Fill two DISKCACHE structures. + mov [esi+DISK.SysCache.pointer], eax + lea ecx, [esi+DISK.CacheLock] + call mutex_init +; The following code is inherited from getcache.inc. + mov edx, [esi+DISK.SysCache.pointer] + and [esi+DISK.SysCache.search_start], 0 + and [esi+DISK.AppCache.search_start], 0 + mov eax, [esi+DISK.cache_size] + shr eax, 3 + mov [esi+DISK.SysCache.data_size], eax + add edx, eax + imul eax, 7 + mov [esi+DISK.AppCache.data_size], eax + mov [esi+DISK.AppCache.pointer], edx + + mov eax, [esi+DISK.SysCache.data_size] + call calculate_cache_slots + add eax, [esi+DISK.SysCache.pointer] + mov [esi+DISK.SysCache.data], eax + mov [esi+DISK.SysCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.SysCache.pointer] + lea ecx, [(ecx+1)*3] + xor eax, eax + rep stosd + pop edi + + mov eax, [esi+DISK.AppCache.data_size] + call calculate_cache_slots + add eax, [esi+DISK.AppCache.pointer] + mov [esi+DISK.AppCache.data], eax + mov [esi+DISK.AppCache.sad_size], ecx + + push edi + mov edi, [esi+DISK.AppCache.pointer] + lea ecx, [(ecx+1)*3] + xor eax, eax + rep stosd + pop edi + +; 5. Return with nonzero al. + mov al, 1 +; 6. Return. +.nothing: + ret +; No caching is required for this driver. Zero cache pointers and return with +; nonzero al. +.nocache: + mov [esi+DISK.SysCache.pointer], eax + mov [esi+DISK.AppCache.pointer], eax + mov al, 1 + ret + +calculate_cache_slots: + push eax + mov ecx, [esi+DISK.MediaInfo.SectorSize] + add ecx, sizeof.CACHE_ITEM + xor edx, edx + div ecx + mov ecx, eax + imul eax, [esi+DISK.MediaInfo.SectorSize] + sub [esp], eax + pop eax + dec ecx + ret + + +; This internal function is called from disk_media_dereference to free the +; allocated cache, if there is one. +; esi = pointer to DISK structure +disk_free_cache: +; The algorithm is straightforward. + mov eax, [esi+DISK.SysCache.pointer] + test eax, eax + jz .nothing + stdcall kernel_free, eax +.nothing: + ret + +; This function flushes all modified data from both caches for the given DISK. +; esi = pointer to DISK +disk_sync: +; The algorithm is straightforward. + cmp [esi+DISK.SysCache.pointer], 0 + jz .nothing + lea ecx, [esi+DISK.CacheLock] + call mutex_lock + push ebx + push esi ; for second write_cache64 + push esi ; for first write_cache64 + lea ebx, [esi+DISK.SysCache] + call write_cache64 + add ebx, DISK.AppCache - DISK.SysCache + call write_cache64 + pop ebx + lea ecx, [esi+DISK.CacheLock] + call mutex_unlock +.nothing: + mov al, DISKFUNC.flush + call disk_call_driver + ret diff --git a/kernel/branches/kolibri-lldw/blkdev/fdc.inc b/kernel/branches/kolibri-lldw/blkdev/fdc.inc new file mode 100644 index 000000000..1f377ab5d --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/fdc.inc @@ -0,0 +1,68 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +uglobal + dmasize db 0x0 + dmamode db 0x0 +endg + +fdc_init: ;start with clean tracks. + mov edi, OS_BASE+0xD201 + mov al, 0 + mov ecx, 160 + rep stosb + ret + +save_image: + cmp [ramdisk_actual_size], FLOPPY_CAPACITY + jnz .fail + pusha + mov ecx, floppy_mutex + call mutex_lock + mov [flp_number], bl + call floppy_read_bootsector + cmp [FDC_Status], 0 + jne .unnecessary_save_image + mov [FDD_Track], 0; Цилиндр + mov [FDD_Head], 0; Сторона + mov [FDD_Sector], 1; Сектор + mov esi, RAMDISK + call SeekTrack +.save_image_1: + call take_data_from_application_1 + call WriteSectWithRetr +; call WriteSector + cmp [FDC_Status], 0 + jne .unnecessary_save_image + inc [FDD_Sector] + cmp [FDD_Sector], 19 + jne .save_image_1 + mov [FDD_Sector], 1 + inc [FDD_Head] + cmp [FDD_Head], 2 + jne .save_image_1 + mov [FDD_Head], 0 + inc [FDD_Track] + call SeekTrack + cmp [FDD_Track], 80 + jne .save_image_1 +.unnecessary_save_image: + cmp [FDC_Status], 0 + pushf + mov ecx, floppy_mutex + call mutex_unlock + popf + popa + jnz .fail + xor eax, eax + ret +.fail: + movi eax, 1 + ret diff --git a/kernel/branches/kolibri-lldw/blkdev/flp_drv.inc b/kernel/branches/kolibri-lldw/blkdev/flp_drv.inc new file mode 100644 index 000000000..4b9d534b3 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/flp_drv.inc @@ -0,0 +1,960 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;********************************************************** +; Direct work with floppy disk drive +;********************************************************** +; Source code author - Kulakov Vladimir Gennadievich. +; Adaptation and improvement - Mario79. + +;give_back_application_data: ; give back to application +; mov edi,[TASK_BASE] +; mov edi,[edi+TASKDATA.mem_start] +; add edi,ecx +give_back_application_data_1: + mov esi, FDD_BUFF;FDD_DataBuffer ;0x40000 + mov ecx, 128 + cld + rep movsd + ret + +;take_data_from_application: ; take from application +; mov esi,[TASK_BASE] +; mov esi,[esi+TASKDATA.mem_start] +; add esi,ecx +take_data_from_application_1: + mov edi, FDD_BUFF;FDD_DataBuffer ;0x40000 + mov ecx, 128 + cld + rep movsd + ret + +; Controller operations result codes (FDC_Status) +FDC_Normal = 0 ; normal finish +FDC_TimeOut = 1 ; time out error +FDC_DiskNotFound = 2 ; no disk in drive +FDC_TrackNotFound = 3 ; track not found +FDC_SectorNotFound = 4 ; sector not found + +; Maximum values of the sector coordinates (specified +; values correspond to the parameters of the standard +; 3-inch 1.44 MB floppy disk) +MAX_Track = 79 +MAX_Head = 1 +MAX_Sector = 18 + +uglobal +; Timer tick counter +TickCounter dd ? +; Operation completion code with the floppy disk drive controller +FDC_Status DB ? +; Interrupt flag from floppy disk drive +FDD_IntFlag DB ? +; The moment of the beginning of the last operation with FDD +FDD_Time DD ? +; Drive number +FDD_Type db 0 +; Sector coordinates +FDD_Track DB ? +FDD_Head DB ? +FDD_Sector DB ? + +; Operation result block +FDC_ST0 DB ? +FDC_ST1 DB ? +FDC_ST2 DB ? +FDC_C DB ? +FDC_H DB ? +FDC_R DB ? +FDC_N DB ? +; Read operation repetition counter +ReadRepCounter DB ? +; Recalibration operation repetition counter +RecalRepCounter DB ? +endg +; Memory area for storing the readed sector +;FDD_DataBuffer: times 512 db 0 ;DB 512 DUP (?) +fdd_motor_status db 0 +timer_fdd_motor dd 0 + +;************************************** +;* INITIALIZATION OF DMA MODE FOR FDD * +;************************************** +Init_FDC_DMA: + pushad + mov al, 0 + out 0x0c, al; reset the flip-flop to a known state. + mov al, 6 ; mask channel 2 so we can reprogram it. + out 0x0a, al + mov al, [dmamode]; 0x46 -> Read from floppy - 0x4A Write to floppy + out 0x0b, al + mov al, 0 + out 0x0c, al; reset the flip-flop to a known state. + mov eax, 0xD000 + out 0x04, al; set the channel 2 starting address to 0 + shr eax, 8 + out 0x04, al + shr eax, 8 + out 0x81, al + mov al, 0 + out 0x0c, al; reset flip-flop + mov al, 0xff;set count (actual size -1) + out 0x5, al + mov al, 0x1;[dmasize] ;(0x1ff = 511 / 0x23ff =9215) + out 0x5, al + mov al, 2 + out 0xa, al + popad + ret + +;*********************************** +;* WRITE BYTE TO FDC DATA PORT * +;* Parameters: * +;* AL - byte to write. * +;*********************************** +FDCDataOutput: +; DEBUGF 1,'K : FDCDataOutput(%x)',al +; pusha + push eax ecx edx + mov AH, AL ; remember byte to AH +; Reset controller state variable + mov [FDC_Status], FDC_Normal +; Check the readiness of the controller to receive data + mov DX, 3F4h ; (FDC state port) + mov ecx, 0x10000 ; set timeout counter +@@TestRS: + in AL, DX ; read the RS register + and AL, 0C0h ; get digits 6 and 7 + cmp AL, 80h ; check digits 6 and 7 + je @@OutByteToFDC + loop @@TestRS +; Time out error +; DEBUGF 1,' timeout\n' + mov [FDC_Status], FDC_TimeOut + jmp @@End_5 +; Write byte to data port +@@OutByteToFDC: + inc DX + mov AL, AH + out DX, AL +; DEBUGF 1,' ok\n' +@@End_5: +; popa + pop edx ecx eax + ret + +;****************************************** +;* READ BYTE FROM FDC DATA PORT * +;* Procedure doesnt have input params. * +;* Output : * +;* AL - byte read. * +;****************************************** +FDCDataInput: + push ECX + push DX +; Reset controller state variable + mov [FDC_Status], FDC_Normal +; Check the readiness of the controller to receive data + mov DX, 3F4h ;(FDC state port) + mov ecx, 0x10000 ; set timeout counter +@@TestRS_1: + in AL, DX ; read the RS register + and AL, 0C0h ; get digits 6 and 7 + cmp AL, 0C0h ; check digits 6 and 7 + je @@GetByteFromFDC + loop @@TestRS_1 +; Time out error +; DEBUGF 1,'K : FDCDataInput: timeout\n' + mov [FDC_Status], FDC_TimeOut + jmp @@End_6 +; Get byte from data port +@@GetByteFromFDC: + inc DX + in AL, DX +; DEBUGF 1,'K : FDCDataInput: %x\n',al +@@End_6: + pop DX + pop ECX + ret + +;********************************************* +;* FDC INTERRUPT HANDLER * +;********************************************* +FDCInterrupt: +; dbgstr 'FDCInterrupt' +; Set the interrupt flag + mov [FDD_IntFlag], 1 + mov al, 1 + ret + +;******************************************* +;* WAIT FOR INTERRUPT FROM FDC * +;******************************************* +WaitFDCInterrupt: + pusha +; Reset operation status byte + mov [FDC_Status], FDC_Normal +; Zero out the tick counter + mov eax, [timer_ticks] + mov [TickCounter], eax +; Wait for the floppy disk interrupt flag to be set +@@TestRS_2: + call change_task + cmp [FDD_IntFlag], 0 + jnz @@End_7 ; interrupt occured + mov eax, [timer_ticks] + sub eax, [TickCounter] + cmp eax, 200;50 ;25 ;5 ; wait 5 ticks + jb @@TestRS_2 +; jl @@TestRS_2 +; Time out error +; dbgstr 'WaitFDCInterrupt: timeout' + mov [FDC_Status], FDC_TimeOut +@@End_7: + popa + ret + +;*********************************** +;* Turn on the motor of drive "A:" * +;*********************************** +FDDMotorON: +; dbgstr 'FDDMotorON' + pusha +; cmp [fdd_motor_status],1 +; je fdd_motor_on + mov al, [flp_number] + cmp [fdd_motor_status], al + je fdd_motor_on +; Reset the FDD controller + mov DX, 3F2h ; motor control port + mov AL, 0 + out DX, AL +; Select and turn on the drive motor + cmp [flp_number], 1 + jne FDDMotorON_B +; call FDDMotorOFF_B + mov AL, 1Ch ; Floppy A + jmp FDDMotorON_1 +FDDMotorON_B: +; call FDDMotorOFF_A + mov AL, 2Dh ; Floppy B +FDDMotorON_1: + out DX, AL +; Zero out the tick counter + mov eax, [timer_ticks] + mov [TickCounter], eax +; wait 0.5 s +@@dT: + call change_task + mov eax, [timer_ticks] + sub eax, [TickCounter] + cmp eax, 50 ;10 + jb @@dT +; Read results of RESET command + push 4 +; DEBUGF 1,'K : floppy reset results:' +@@: + mov al, 8 + call FDCDataOutput + call FDCDataInput +; DEBUGF 1,' %x',al + call FDCDataInput +; DEBUGF 1,' %x',al + dec dword [esp] + jnz @b +; DEBUGF 1,'\n' + pop eax + cmp [flp_number], 1 + jne fdd_motor_on_B + mov [fdd_motor_status], 1 + jmp fdd_motor_on +fdd_motor_on_B: + mov [fdd_motor_status], 2 +fdd_motor_on: + call save_timer_fdd_motor + popa + ret + +;***************************************** +;* SAVING TIME STAMP * +;***************************************** +save_timer_fdd_motor: + mov eax, [timer_ticks] + mov [timer_fdd_motor], eax + ret + +;***************************************** +;* CHECK THE MOTOR SHUTDOWN DELAY * +;***************************************** +proc check_fdd_motor_status_has_work? + cmp [fdd_motor_status], 0 + jz .no + mov eax, [timer_ticks] + sub eax, [timer_fdd_motor] + cmp eax, 500 + jb .no +.yes: + xor eax, eax + inc eax + ret +.no: + xor eax, eax + ret +endp + +align 4 +check_fdd_motor_status: + cmp [fdd_motor_status], 0 + je end_check_fdd_motor_status_1 + mov eax, [timer_ticks] + sub eax, [timer_fdd_motor] + cmp eax, 500 + jb end_check_fdd_motor_status + call FDDMotorOFF + mov [fdd_motor_status], 0 +end_check_fdd_motor_status_1: +end_check_fdd_motor_status: + ret + +;********************************** +;* TURN OFF MOTOR OF DRIVE * +;********************************** +FDDMotorOFF: +; dbgstr 'FDDMotorOFF' + push AX + push DX + cmp [flp_number], 1 + jne FDDMotorOFF_1 + call FDDMotorOFF_A + jmp FDDMotorOFF_2 +FDDMotorOFF_1: + call FDDMotorOFF_B +FDDMotorOFF_2: + pop DX + pop AX + ; clearing caching flags due to information obsolescence + or [floppy_media_flags+0], FLOPPY_MEDIA_NEED_RESCAN + or [floppy_media_flags+1], FLOPPY_MEDIA_NEED_RESCAN + ret + +FDDMotorOFF_A: + mov DX, 3F2h ; motor control port + mov AL, 0Ch ; Floppy A + out DX, AL + ret + +FDDMotorOFF_B: + mov DX, 3F2h ; motor control port + mov AL, 5h ; Floppy B + out DX, AL + ret + +;******************************* +;* RECALIBRATE DRIVE "A:" * +;******************************* +RecalibrateFDD: +; dbgstr 'RecalibrateFDD' + pusha + call save_timer_fdd_motor +; Clear the interrupt flag + mov [FDD_IntFlag], 0 +; Send the "Recalibration" command + mov AL, 07h + call FDCDataOutput + mov AL, [flp_number] + dec AL + call FDCDataOutput +; Wait for the operation to complete + call WaitFDCInterrupt + cmp [FDC_Status], 0 + jne .fail +; Read results of RECALIBRATE command +; DEBUGF 1,'K : floppy recalibrate results:' + mov al, 8 + call FDCDataOutput + call FDCDataInput + push eax +; DEBUGF 1,' %x',al + call FDCDataInput +; DEBUGF 1,' %x',al +; DEBUGF 1,'\n' + pop eax + test al, 0xC0 + jz @f + mov [FDC_Status], FDC_DiskNotFound +@@: +.fail: + call save_timer_fdd_motor + popa + ret + +;***************************************************** +;* TRACK SEARCH * +;* Parameters are passed through global variables: * +;* FDD_Track - track number (0-79); * +;* FDD_Head - head number (0-1). * +;* Result of operation is written to FDC_Status. * +;***************************************************** +SeekTrack: +; dbgstr 'SeekTrack' + pusha + call save_timer_fdd_motor +; Clear the interrupt flag + mov [FDD_IntFlag], 0 +; Send "Search" command + mov AL, 0Fh + call FDCDataOutput + ; Send head / drive number byte + mov AL, [FDD_Head] + shl AL, 2 + call FDCDataOutput + ; Send track number byte + mov AL, [FDD_Track] + call FDCDataOutput +; Wait for the operation to complete + call WaitFDCInterrupt + cmp [FDC_Status], FDC_Normal + jne @@Exit +; Save search result + mov AL, 08h + call FDCDataOutput + call FDCDataInput + mov [FDC_ST0], AL + call FDCDataInput + mov [FDC_C], AL +; Check search result + ; Is search finished? + test [FDC_ST0], 100000b + je @@Err + ; Is the specified track found? + mov AL, [FDC_C] + cmp AL, [FDD_Track] + jne @@Err + ; Does the head number match the specified one? +; The H bit (Head Address) in ST0 will always return a "0" (c) 82077AA datasheet, +; description of SEEK command. So we can not verify the proper head. +; mov AL, [FDC_ST0] +; and AL, 100b +; shr AL, 2 +; cmp AL, [FDD_Head] +; jne @@Err + ; Operation completed successfully +; dbgstr 'SeekTrack: FDC_Normal' + mov [FDC_Status], FDC_Normal + jmp @@Exit +@@Err: ; Track not found +; dbgstr 'SeekTrack: FDC_TrackNotFound' + mov [FDC_Status], FDC_TrackNotFound +@@Exit: + call save_timer_fdd_motor + popa + ret + +;******************************************************* +;* READING A DATA SECTOR * +;* Parameters are passed through global variables: * +;* FDD_Track - track number (0-79); * +;* FDD_Head - head number (0-1); * +;* FDD_Sector - sector number (1-18). * +;* Result of operation is written to FDC_Status. * +;* If the read operation is successful, the contents * +;* of the sector will be written to FDD_DataBuffer. * +;******************************************************* +ReadSector: +; dbgstr 'ReadSector' + pushad + call save_timer_fdd_motor +; Clear the interrupt flag + mov [FDD_IntFlag], 0 +; Set transmit speed to 500 Kb / s + mov AX, 0 + mov DX, 03F7h + out DX, AL +; Initialize the DMA channel + mov [dmamode], 0x46 + call Init_FDC_DMA +; Send "Data read" command + mov AL, 0E6h ; reading in multi-track mode + call FDCDataOutput + mov AL, [FDD_Head] + shl AL, 2 + or AL, [flp_number] + dec AL + call FDCDataOutput + mov AL, [FDD_Track] + call FDCDataOutput + mov AL, [FDD_Head] + call FDCDataOutput + mov AL, [FDD_Sector] + call FDCDataOutput + mov AL, 2 ; sector size code (512 byte) + call FDCDataOutput + mov AL, 18 ;+1; 3Fh ;number of sectors per track + call FDCDataOutput + mov AL, 1Bh ; GPL value + call FDCDataOutput + mov AL, 0FFh; DTL value + call FDCDataOutput +; Waiting for an interrupt at the end of the operation + call WaitFDCInterrupt + cmp [FDC_Status], FDC_Normal + jne @@Exit_1 +; Read the operation completion status + call GetStatusInfo + test [FDC_ST0], 11011000b + jnz @@Err_1 +; dbgstr 'ReadSector: FDC_Normal' + mov [FDC_Status], FDC_Normal + jmp @@Exit_1 +@@Err_1: +; dbgstr 'ReadSector: FDC_SectorNotFound' + mov [FDC_Status], FDC_SectorNotFound +@@Exit_1: + call save_timer_fdd_motor + popad + ret + +;******************************************************* +;* READ SECTOR (WITH RETRY OF OPERATION ON FAILURE) * +;* Parameters are passed through global variables: * +;* FDD_Track - track number (0-79); * +;* FDD_Head - head number (0-1); * +;* FDD_Sector - sector number (1-18). * +;* Result of operation is written to FDC_Status. * +;* If the read operation is successful, the contents * +;* of the sector will be written to FDD_DataBuffer. * +;******************************************************* +ReadSectWithRetr: + pusha +; Reset the recalibration repetition counter + mov [RecalRepCounter], 0 +@@TryAgain: +; Reset the read operation retry counter + mov [ReadRepCounter], 0 +@@ReadSector_1: + call ReadSector + cmp [FDC_Status], 0 + je @@Exit_2 + cmp [FDC_Status], 1 + je @@Err_3 + ; Three times repeat reading + inc [ReadRepCounter] + cmp [ReadRepCounter], 3 + jb @@ReadSector_1 + ; Three times repeat recalibration + call RecalibrateFDD + call SeekTrack + inc [RecalRepCounter] + cmp [RecalRepCounter], 3 + jb @@TryAgain +@@Exit_2: + popa + ret +@@Err_3: + popa + ret + +;******************************************************* +;* WRITE DATA SECTOR * +;* Parameters are passed through global variables: * +;* FDD_Track - track number (0-79); * +;* FDD_Head - head number (0-1); * +;* FDD_Sector - sector number (1-18). * +;* Result of operation is written to FDC_Status. * +;* If the write operation is successful, the contents * +;* of FDD_DataBuffer will be written to the sector * +;******************************************************* +WriteSector: +; dbgstr 'WriteSector' + pushad + call save_timer_fdd_motor +; Clear the interrupt flag + mov [FDD_IntFlag], 0 +; Set transmit speed to 500 Kb / s + mov AX, 0 + mov DX, 03F7h + out DX, AL +; Initialize the DMA channel + mov [dmamode], 0x4A + call Init_FDC_DMA +; Send "Write data" command + mov AL, 0xC5 ;0x45 ; write in multi-track mode + call FDCDataOutput + mov AL, [FDD_Head] + shl AL, 2 + or AL, [flp_number] + dec AL + call FDCDataOutput + mov AL, [FDD_Track] + call FDCDataOutput + mov AL, [FDD_Head] + call FDCDataOutput + mov AL, [FDD_Sector] + call FDCDataOutput + mov AL, 2 ; sector size code (512 bytes) + call FDCDataOutput + mov AL, 18; 3Fh ; sectors per track + call FDCDataOutput + mov AL, 1Bh ; GPL value + call FDCDataOutput + mov AL, 0FFh; DTL value + call FDCDataOutput +; Waiting for an interrupt at the end of the operation + call WaitFDCInterrupt + cmp [FDC_Status], FDC_Normal + jne @@Exit_3 +; Reading the completion status of the operation + call GetStatusInfo + test [FDC_ST0], 11000000b ;11011000b + jnz @@Err_2 + mov [FDC_Status], FDC_Normal + jmp @@Exit_3 +@@Err_2: + mov [FDC_Status], FDC_SectorNotFound +@@Exit_3: + call save_timer_fdd_motor + popad + ret + +;******************************************************* +;* WRITE SECTOR (WITH REPEAT ON FAILURE) * +;* Parameters are passed through global variables: * +;* FDD_Track - track number (0-79); * +;* FDD_Head - head number (0-1); * +;* FDD_Sector - sector number (1-18). * +;* Result of operation is written to FDC_Status. * +;* If the write operation is successful, the contents * +;* of FDD_DataBuffer will be written to the sector * +;******************************************************* +WriteSectWithRetr: + pusha +; Reset the recalibration repetition counter + mov [RecalRepCounter], 0 +@@TryAgain_1: +; Reset the read operation retry counter + mov [ReadRepCounter], 0 +@@WriteSector_1: + call WriteSector + cmp [FDC_Status], 0 + je @@Exit_4 + cmp [FDC_Status], 1 + je @@Err_4 + ; Three times repeat writing + inc [ReadRepCounter] + cmp [ReadRepCounter], 3 + jb @@WriteSector_1 + ; Three times repeat recalibration + call RecalibrateFDD + call SeekTrack + inc [RecalRepCounter] + cmp [RecalRepCounter], 3 + jb @@TryAgain_1 +@@Exit_4: + popa + ret +@@Err_4: + popa + ret + +;********************************************* +;* GET INFORMATION ABOUT THE RESULT OF THE OPERATION +;********************************************* +GetStatusInfo: + push AX + call FDCDataInput + mov [FDC_ST0], AL + call FDCDataInput + mov [FDC_ST1], AL + call FDCDataInput + mov [FDC_ST2], AL + call FDCDataInput + mov [FDC_C], AL + call FDCDataInput + mov [FDC_H], AL + call FDCDataInput + mov [FDC_R], AL + call FDCDataInput + mov [FDC_N], AL + pop AX + ret + +; Interface for disk subsystem. +; Assume fixed capacity for 1.44M. +FLOPPY_CAPACITY = 2880 ; in sectors + +iglobal +align 4 +floppy_functions: + dd .size + dd 0 ; no close() function + dd 0 ; no closemedia() function + dd floppy_querymedia + dd floppy_read + dd floppy_write + dd 0 ; no flush() function + dd 0 ; no adjust_cache_size() function +.size = $ - floppy_functions +endg + +uglobal +floppy_media_flags rb 2 +n_sector dd 0 ; temporary save for sector value +flp_number db 0 ; 1- Floppy A, 2-Floppy B +old_track db 0 ; old value track +flp_label rb 15*2 ; Label and ID of inserted floppy disk +align 4 +; Hardware does not allow to work with two floppies in parallel, +; so there is one mutex guarding access to any floppy. +floppy_mutex MUTEX +endg +; Meaning of bits in floppy_media_flags +FLOPPY_MEDIA_PRESENT = 1 ; media was present when last asked +FLOPPY_MEDIA_NEED_RESCAN = 2 ; media was possibly changed, need to rescan +FLOPPY_MEDIA_LABEL_CHANGED = 4 ; temporary state + +iglobal +floppy1_name db 'fd',0 +floppy2_name db 'fd2',0 +endg + +; This function is called in boot process. +; It creates filesystems /fd and/or /fd2, if the system has one/two floppy drives. +proc floppy_init + mov ecx, floppy_mutex + call mutex_init +; First floppy is present if [DRIVE_DATA] and 0xF0 is nonzero. + test byte [DRIVE_DATA], 0xF0 + jz .no1 + stdcall disk_add, floppy_functions, floppy1_name, 1, DISK_NO_INSERT_NOTIFICATION +.no1: +; Second floppy is present if [DRIVE_DATA] and 0x0F is nonzero. + test byte [DRIVE_DATA], 0x0F + jz .no2 + stdcall disk_add, floppy_functions, floppy2_name, 2, DISK_NO_INSERT_NOTIFICATION +.no2: + ret +endp + +; Returns information about disk media. +; Floppy drives do not support insert notifications, +; DISK_NO_INSERT_NOTIFICATION is set, +; the disk subsystem calls this function before each filesystem operation. +; If the media has changed, return error for the first call as signal +; to finalize work with old media and the true geometry for the second call. +; Assume that media is (possibly) changed anytime when motor is off. +proc floppy_querymedia + virtual at esp+4 + .userdata dd ? + .info dd ? + end virtual +; 1. Acquire the global lock. + mov ecx, floppy_mutex + call mutex_lock + mov edx, [.userdata] ; 1 for /fd, 2 for /fd2 +; 2. If the media was reported and has been changed, forget it and report an error. + mov al, [floppy_media_flags+edx-1] + and al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN + cmp al, FLOPPY_MEDIA_PRESENT + FLOPPY_MEDIA_NEED_RESCAN + jnz .not_reported +.no_media: + mov [floppy_media_flags+edx-1], 0 +.return_no_media: + mov ecx, floppy_mutex + call mutex_unlock + mov eax, DISK_STATUS_NO_MEDIA + retn 8 +.not_reported: +; 3. If we are in the temporary state LABEL_CHANGED, this is the second call +; after intermediate DISK_STATUS_NO_MEDIA due to media change; +; clear the flag and return the current geometry without rereading the bootsector. + cmp [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED + jz .report_geometry +; 4. Try to read the bootsector. + mov [flp_number], dl + mov [FDC_Status], 0 + call floppy_read_bootsector +; 5. If reading bootsector failed, assume that media is not present. + mov edx, [.userdata] + cmp [FDC_Status], 0 + jnz .no_media +; 6. Check whether the previous status is "present". If not, go to 10. + push esi edi + imul edi, edx, 15 + add edi, flp_label-15 + mov esi, FDD_BUFF+39 + mov ecx, 15 + test [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT + jz .set_label +; 7. Compare the old label with the current one. + rep cmpsb +; 8. If the label has not changed, go to 11. + jz .ok +; 9. If the label has changed, store it, enter temporary state LABEL_CHANGED +; and report DISK_STATUS_NO_MEDIA. +; dbgstr 'floppy label changed' + add esi, ecx + add edi, ecx + mov ecx, 15 + sub esi, ecx + sub edi, ecx + rep movsb + mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_LABEL_CHANGED + pop edi esi + jmp .return_no_media +.set_label: +; 10. The previous state was "not present". Copy the label. + rep movsb +.ok: + pop edi esi +.report_geometry: +; 11. Fill DISKMEDIAINFO structure. + mov ecx, [.info] + and [ecx+DISKMEDIAINFO.Flags], 0 + mov [ecx+DISKMEDIAINFO.SectorSize], 512 + mov dword [ecx+DISKMEDIAINFO.Capacity], FLOPPY_CAPACITY + and dword [ecx+DISKMEDIAINFO.Capacity+4], 0 +; 12. Update state: media is present, data are actual. + mov [floppy_media_flags+edx-1], FLOPPY_MEDIA_PRESENT +; 13. Release the global lock and return successful status. + mov ecx, floppy_mutex + call mutex_unlock + xor eax, eax + retn 8 +endp + +proc floppy_read_bootsector + pushad + mov [FDD_Track], 0 ; Cylinder + mov [FDD_Head], 0 ; Head + mov [FDD_Sector], 1 ; Sector + call FDDMotorON + call RecalibrateFDD + cmp [FDC_Status], 0 + jne .nothing + call SeekTrack + cmp [FDC_Status], 0 + jne .nothing + call ReadSectWithRetr +.nothing: + popad + ret +endp + +read_chs_sector: + call calculate_chs + call ReadSectWithRetr + ret + +save_chs_sector: + call calculate_chs + call WriteSectWithRetr + ret + +calculate_chs: + mov bl, [FDD_Track] + mov [old_track], bl + mov ebx, 18 + xor edx, edx + div ebx + inc edx + mov [FDD_Sector], dl + mov edx, eax + shr eax, 1 + and edx, 1 + mov [FDD_Track], al + mov [FDD_Head], dl + mov dl, [old_track] + cmp dl, [FDD_Track] + je no_seek_track_1 + call SeekTrack +no_seek_track_1: + ret + +; Writes one or more sectors to the device. +proc floppy_write + mov dl, 1 + jmp floppy_read_write +endp + +; Reads one or more sectors from the device. +proc floppy_read + mov dl, 0 +endp + +; Common part of floppy_read and floppy_write. +proc floppy_read_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword +virtual at ebp-8 +.sectors_todo dd ? +.operation db ? +end virtual + push edx ; save operation code to [.operation] +; 1. Get number of sectors to read/write +; and zero number of sectors that were actually read/written. + mov eax, [numsectors_ptr] + push dword [eax] ; initialize [.sectors_todo] + and dword [eax], 0 + push ebx esi edi ; save used registers to be stdcall +; 2. Acquire the global lock. + mov ecx, floppy_mutex + call mutex_lock +; 3. Set floppy number for this operation. + mov edx, [userdata] + mov [flp_number], dl +; 4. Read/write sector-by-sector. +.operation_loop: +; 4a. Check that the sector is inside the media. + cmp dword [start_sector+4], 0 + jnz .end_of_media + mov eax, dword [start_sector] + cmp eax, FLOPPY_CAPACITY + jae .end_of_media +; 4b. For read operation, call read_chs_sector and then move data from FDD_BUFF to [buffer]. +; For write operation, move data from [buffer] to FDD_BUFF and then call save_chs_sector. + cmp [.operation], 0 + jz .read + mov esi, [buffer] + mov edi, FDD_BUFF + mov ecx, 512/4 + rep movsd + mov [buffer], esi + call save_chs_sector + jmp @f +.read: + call read_chs_sector + mov esi, FDD_BUFF + mov edi, [buffer] + mov ecx, 512/4 + rep movsd + mov [buffer], edi +@@: +; 4c. If there was an error, propagate it to the caller. + cmp [FDC_Status], 0 + jnz .fail +; 4d. Otherwise, increment number of sectors processed and continue the loop. + mov eax, [numsectors_ptr] + inc dword [eax] + inc dword [start_sector] + dec [.sectors_todo] + jnz .operation_loop +; 5. Release the global lock and return with the correct status. + push 0 +.return: + mov ecx, floppy_mutex + call mutex_unlock + pop eax + pop edi esi ebx ; restore used registers to be stdcall + ret ; this translates to leave/retn N and purges local variables +.fail: + push -1 + jmp .return +.end_of_media: + push DISK_STATUS_END_OF_MEDIA + jmp .return +endp diff --git a/kernel/branches/kolibri-lldw/blkdev/hd_drv.inc b/kernel/branches/kolibri-lldw/blkdev/hd_drv.inc new file mode 100644 index 000000000..73c4b56c5 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/hd_drv.inc @@ -0,0 +1,568 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; HDD driver + +struct HD_DATA +hdpos dw ? +hdid dw ? +hdbase dw ? +hd48 dw ? +sectors dq ? +ends +;----------------------------------------------------------------- +iglobal +align 4 +ide_callbacks: + dd ide_callbacks.end - ide_callbacks + dd 0 ; no close function + dd 0 ; no closemedia function + dd ide_querymedia + dd ide_read + dd ide_write + dd 0 ; no flush function + dd 0 ; use default cache size +.end: + +hd0_data HD_DATA 1, 0 +hd1_data HD_DATA 2, 16 +hd2_data HD_DATA 3, 0 +hd3_data HD_DATA 4, 16 +hd4_data HD_DATA 5, 0 +hd5_data HD_DATA 6, 16 +hd6_data HD_DATA 7, 0 +hd7_data HD_DATA 8, 16 +hd8_data HD_DATA 9, 0 +hd9_data HD_DATA 10, 16 +hd10_data HD_DATA 11, 0 +hd11_data HD_DATA 12, 16 + +ide_mutex_table: + dd ide_channel1_mutex + dd ide_channel2_mutex + dd ide_channel3_mutex + dd ide_channel4_mutex + dd ide_channel5_mutex + dd ide_channel6_mutex +endg +;----------------------------------------------------------------- +uglobal +ide_mutex MUTEX +ide_channel1_mutex MUTEX +ide_channel2_mutex MUTEX +ide_channel3_mutex MUTEX +ide_channel4_mutex MUTEX +ide_channel5_mutex MUTEX +ide_channel6_mutex MUTEX +blockSize: +rb 4 +sector: +rb 6 +allow_dma_access db ? +IDE_common_irq_param db ? +eventPointer dd ? +eventID dd ? +endg +;----------------------------------------------------------------- +ide_read: + mov al, 25h ; READ DMA EXT + jmp ide_read_write + +ide_write: + mov al, 35h ; WRITE DMA EXT +proc ide_read_write stdcall uses esi edi ebx, \ + hd_data, buffer, startsector:qword, numsectors + ; hd_data = pointer to hd*_data + ; buffer = pointer to buffer with/for data + ; startsector = 64-bit start sector + ; numsectors = pointer to number of sectors on input, + ; must be filled with number of sectors really read/written +locals +sectors_todo dd ? +channel_lock dd ? +endl + mov bl, al +; get number of requested sectors and say that no sectors were read yet + mov ecx, [numsectors] + mov eax, [ecx] + mov dword [ecx], 0 + mov [sectors_todo], eax +; acquire the global lock + mov ecx, ide_mutex + call mutex_lock + mov ecx, [hd_data] + movzx ecx, [ecx+HD_DATA.hdpos] + dec ecx + shr ecx, 1 + shl ecx, 2 + mov ecx, [ecx + ide_mutex_table] + mov [channel_lock], ecx + call mutex_lock +; prepare worker procedures variables + mov esi, [buffer] + mov edi, esi + mov ecx, [hd_data] + movzx eax, [ecx+HD_DATA.hdbase] + mov [hdbase], eax + mov ax, [ecx+HD_DATA.hdid] + mov [hdid], eax + mov eax, dword [startsector] + mov [sector], eax + cmp [ecx+HD_DATA.hd48], 0 + jz .LBA28 + mov ax, word [startsector+4] + mov [sector+4], ax + movzx ecx, [ecx+HD_DATA.hdpos] + mov [hdpos], ecx + dec ecx + shr ecx, 2 + imul ecx, sizeof.IDE_DATA + add ecx, IDE_controller_1 + mov [IDE_controller_pointer], ecx + mov eax, [hdpos] + dec eax + and eax, 11b + shr eax, 1 + add eax, ecx + cmp [eax+IDE_DATA.dma_hdd_channel_1], 1 + jz .next + dec ebx ; READ/WRITE SECTOR(S) EXT +; LBA48 supports max 10000h sectors per time +; loop until all sectors will be processed +.next: + mov ecx, 8000h + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [blockSize], ecx + push ecx + call IDE_transfer + pop ecx + jc .out + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .out + add [sector], ecx + adc word [sector+4], 0 + jmp .next + +.LBA28: + add eax, [sectors_todo] + add eax, 0xF0000000 + jc .out + sub bl, 5 ; READ/WRITE SECTOR(S) +; LBA28 supports max 256 sectors per time +; loop until all sectors will be processed +.next28: + mov ecx, 256 + cmp ecx, [sectors_todo] + jbe @f + mov ecx, [sectors_todo] +@@: + mov [blockSize], ecx + push ecx + call IDE_transfer.LBA28 + pop ecx + jc .out + mov eax, [numsectors] + add [eax], ecx + sub [sectors_todo], ecx + jz .out + add [sector], ecx + jmp .next28 + +; loop is done, either due to error or because everything is done +; release the global lock and return the corresponding status +.out: + sbb eax, eax + push eax + mov ecx, [channel_lock] + call mutex_unlock + mov ecx, ide_mutex + call mutex_unlock + pop eax + ret +endp +;----------------------------------------------------------------- +proc ide_querymedia stdcall, hd_data, mediainfo + mov eax, [mediainfo] + mov edx, [hd_data] + mov [eax+DISKMEDIAINFO.Flags], 0 + mov [eax+DISKMEDIAINFO.SectorSize], 512 + mov ecx, dword[edx+HD_DATA.sectors] + mov dword[eax+DISKMEDIAINFO.Capacity], ecx + mov ecx, dword[edx+HD_DATA.sectors+4] + mov dword[eax+DISKMEDIAINFO.Capacity+4], ecx + xor eax, eax + ret +endp +;----------------------------------------------------------------- +; input: esi -> buffer, bl = command, [sector], [blockSize] +; output: esi -> next block in buffer +; for pio read esi equal edi +IDE_transfer: + mov edx, [hdbase] + add edx, 6 + mov al, byte [hdid] + add al, 224 + out dx, al ; select the desired drive + call save_hd_wait_timeout + inc edx +@@: + call check_hd_wait_timeout + jc .hd_error + in al, dx + test al, 128 ; ready for command? + jnz @b + pushfd ; fill the ports + cli + mov edx, [hdbase] + inc edx + inc edx + mov al, [blockSize+1] + out dx, al ; Sector count (15:8) + inc edx + mov eax, [sector+3] + out dx, al ; LBA (31:24) + inc edx + shr eax, 8 + out dx, al ; LBA (39:32) + inc edx + shr eax, 8 + out dx, al ; LBA (47:40) + sub edx, 3 + mov al, [blockSize] + out dx, al ; Sector count (7:0) + inc edx + mov eax, [sector] + out dx, al ; LBA (7:0) + inc edx + shr eax, 8 + out dx, al ; LBA (15:8) + inc edx + shr eax, 8 + out dx, al ; LBA (23:16) + inc edx + mov al, byte [hdid] + add al, 224 + out dx, al + test bl, 1 + jz .PIO +; DMA + mov dword [esp], 0x1000 + call kernel_alloc + mov edi, eax + push eax + shl dword [blockSize], 9 + mov eax, esi + add eax, [blockSize] + push eax +; check buffer pages physical addresses and fill the scatter-gather list +; buffer may be not aligned and may have size not divisible by page size +; [edi] = block physical address, [edi+4] = block size in bytes +; block addresses can not cross 10000h borders + mov ecx, esi + and ecx, 0xFFF + jz .aligned + mov eax, esi + call get_pg_addr + add eax, ecx + neg ecx + add ecx, 0x1000 + mov [edi], eax + cmp ecx, [blockSize] + jnc .end + mov [edi+4], ecx + add esi, 0x1000 + add edi, 8 + sub [blockSize], ecx +.aligned: + mov eax, esi + call get_pg_addr + mov ecx, eax + mov [edi], eax + and ecx, 0xFFFF + neg ecx + add ecx, 0x10000 + cmp [blockSize], ecx + jnc @f + mov ecx, [blockSize] + and ecx, 0xF000 + jz .end +@@: + push ecx +@@: + add esi, 0x1000 + add eax, 0x1000 + sub ecx, 0x1000 + jz @f + mov edx, eax + mov eax, esi + call get_pg_addr + cmp eax, edx + jz @b +@@: + pop edx + sub edx, ecx + mov [edi+4], edx + add edi, 8 + sub [blockSize], edx + jnz .aligned + sub edi, 8 + jmp @f + +.end: + mov ecx, [blockSize] + mov [edi+4], ecx +@@: + mov byte [edi+7], 80h ; list end + pop esi + pop edi +; select controller Primary or Secondary + mov ecx, [IDE_controller_pointer] + mov dx, [ecx+IDE_DATA.RegsBaseAddres] + mov eax, [hdpos] + dec eax + test eax, 10b + jz @f + add edx, 8 +@@: + add edx, 2 ; Bus Master IDE Status register + mov al, 6 + out dx, al ; clear Error bit and Interrupt bit + + add edx, 2 ; Bus Master IDE PRD Table Address + mov eax, edi + call get_pg_addr + out dx, eax ; send scatter-gather list physical address + + push edx + mov edx, [hdbase] + add edx, 7 ; ATACommand + mov al, bl + out dx, al ; Start hard drive + pop edx + + sub edx, 4 ; Bus Master IDE Command register + mov al, 1 ; set direction + cmp bl, 35h ; write + jz @f + add al, 8 ; read +@@: + out dx, al ; Start Bus Master + mov [IDE_common_irq_param], 14 + mov eax, [hdpos] + dec eax + test eax, 10b + jz @f + inc [IDE_common_irq_param] +@@: + push edi esi ebx + xor ecx, ecx + xor esi, esi + call create_event + mov [eventPointer], eax + mov [eventID], edx + sti + mov ebx, edx + mov ecx, 300 + call wait_event_timeout + test eax, eax + jnz @f + dbgstr 'IDE DMA IRQ timeout' + mov [IDE_common_irq_param], 0 + mov eax, [eventPointer] + mov ebx, [eventID] + call destroy_event + mov [eventPointer], 0 +@@: + pop ebx esi + call kernel_free + cmp [eventPointer], 0 + jz .hd_error + ret + +.LBA28: + mov edx, [hdbase] + add edx, 6 + mov al, byte [hdid] + add al, 224 + out dx, al ; select the desired drive + call save_hd_wait_timeout + inc edx +@@: + call check_hd_wait_timeout + jc .hd_error + in al, dx + test al, 128 ; ready for command? + jnz @b + pushfd ; fill the ports + cli + mov edx, [hdbase] + inc edx + inc edx + mov al, [blockSize] + out dx, al ; Sector count (7:0) + inc edx + mov eax, [sector] + out dx, al ; LBA (7:0) + inc edx + shr eax, 8 + out dx, al ; LBA (15:8) + inc edx + shr eax, 8 + out dx, al ; LBA (23:16) + inc edx + shr eax, 8 + add al, byte [hdid] + add al, 224 + out dx, al ; LBA (27:24) +.PIO: + inc edx ; ATACommand + mov al, bl + out dx, al ; Start hard drive + popfd +.sectorTransfer: + call save_hd_wait_timeout + in al, dx + in al, dx + in al, dx + in al, dx +@@: + call check_hd_wait_timeout + jc .hd_error + in al, dx + test al, 8 ; ready for transfer? + jz @b + cmp [hd_setup], 1 ; do not mark error for setup request + jz @f + test al, 1 ; previous command ended up with an error + jnz .pio_error +@@: + pushfd + cli + cld + mov ecx, 256 + mov edx, [hdbase] + cmp bl, 30h + jnc .write + rep insw + jmp @f + +.write: + rep outsw +@@: + popfd + add edx, 7 + dec dword [blockSize] + jnz .sectorTransfer + ret + +.pio_error: + dbgstr 'IDE PIO transfer error' +.hd_error: + cmp bl, 30h + jnc hd_write_error +;----------------------------------------------------------------- +hd_read_error: + dbgstr 'HD read error' + stc + ret +;----------------------------------------------------------------- +hd_write_error: + dbgstr 'HD write error' + stc + ret +;----------------------------------------------------------------- +save_hd_wait_timeout: + mov eax, [timer_ticks] + add eax, 300 ; 3 sec timeout + mov [hd_wait_timeout], eax + ret +;----------------------------------------------------------------- +check_hd_wait_timeout: + mov eax, [timer_ticks] + cmp [hd_wait_timeout], eax + jc @f + ret + +@@: + dbgstr 'IDE device timeout' + stc + ret +;----------------------------------------------------------------- +align 4 +IDE_irq_14_handler: +IDE_irq_15_handler: +IDE_common_irq_handler: +; Most of the time, we are here because we have requested +; a DMA transfer for the corresponding drive. +; However, +; a) we can be here because IDE IRQ is shared with some other device, +; that device has actually raised IRQ, +; it has nothing to do with IDE; +; b) we can be here because IDE controller just does not want +; to be silent and reacts to something even though +; we have, in theory, disabled IRQs. +; If the interrupt corresponds to our current request, +; remove the interrupt request and raise the event for the waiting code. +; In the case a), just return zero - not our interrupt. +; In the case b), remove the interrupt request and hope for the best. +; DEBUGF 1, 'K : IDE_irq_handler %x\n', [IDE_common_irq_param]:2 + mov ecx, [esp+4] + mov dx, [ecx+IDE_DATA.RegsBaseAddres] + add edx, 2 ; Bus Master IDE Status register + in al, dx + test al, 4 + jnz .interrupt_from_primary + add edx, 8 + in al, dx + test al, 4 + jnz .interrupt_from_secondary + xor eax, eax ; not our interrupt + ret + +.interrupt_from_primary: + out dx, al ; clear Interrupt bit + sub edx, 2 + xor eax, eax + out dx, al ; clear Bus Master IDE Command register + mov dx, [ecx+IDE_DATA.BAR0_val] + add edx, 7 + in al, dx ; read status register + cmp [IDE_common_irq_param], 14 + jz .raise +.exit_our: + mov al, 1 + ret + +.interrupt_from_secondary: + out dx, al ; clear Interrupt bit + sub edx, 2 + xor eax, eax + out dx, al ; clear Bus Master IDE Command register + mov dx, [ecx+IDE_DATA.BAR2_val] + add edx, 7 + in al, dx ; read status register + cmp [IDE_common_irq_param], 15 + jnz .exit_our +.raise: + cmp ecx, [IDE_controller_pointer] + jnz .exit_our + pushad + mov eax, [eventPointer] + mov ebx, [eventID] + xor edx, edx + xor esi, esi + call raise_event + popad + mov al, 1 ; remove the interrupt request + ret diff --git a/kernel/branches/kolibri-lldw/blkdev/ide_cache.inc b/kernel/branches/kolibri-lldw/blkdev/ide_cache.inc new file mode 100644 index 000000000..b030fee6c --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/ide_cache.inc @@ -0,0 +1,202 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;************************************************************************** +; +; [cache_ide[X]_pointer] +; or [cache_ide[X]_data_pointer] first entry in cache list +; +; +0 - lba sector +; +4 - state of cache sector +; 0 = empty +; 1 = used for read ( same as in hd ) +; 2 = used for write ( differs from hd ) +; +; [cache_ide[X]_system_data] +; or [cache_ide[x]_appl_data] - cache entries +; +;************************************************************************** + +$Revision$ + +align 4 +find_empty_slot_CD_cache: +;----------------------------------------------------------- +; find empty or read slot, flush cache if next 10% is used by write +; output : edi = cache slot +;----------------------------------------------------------- +.search_again: + call cd_calculate_cache_3 +.search_for_empty: + inc edi + call cd_calculate_cache_4 + jbe .inside_cache + mov edi, 1 +.inside_cache: + call cd_calculate_cache_5 + ret +;-------------------------------------------------------------------- +clear_CD_cache: + DEBUGF 1, 'K : clear_CD_cache\n' + pusha + + mov esi, [cdpos] + dec esi + imul esi, sizeof.IDE_CACHE + add esi, cache_ide0 + + xor eax, eax + + mov [esi+IDE_CACHE.search_start], eax + mov ecx, [esi+IDE_CACHE.system_sad_size] + mov edi, [esi+IDE_CACHE.pointer] + call .clear + + mov [esi+IDE_CACHE.appl_search_start], eax + mov ecx, [esi+IDE_CACHE.appl_sad_size] + mov edi, [esi+IDE_CACHE.data_pointer] + call .clear + + popa + ret +;-------------------------------------- +.clear: + shl ecx, 1 + cld + rep stosd + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache: +; 1 - IDE0 ... 12 - IDE11 + push eax + + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + mov ecx, [eax+IDE_CACHE.system_sad_size] + mov esi, [eax+IDE_CACHE.pointer] + pop eax + ret +;-------------------------------------- +@@: + mov ecx, [eax+IDE_CACHE.appl_sad_size] + mov esi, [eax+IDE_CACHE.data_pointer] + pop eax + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_1: +; 1 - IDE0 ... 12 - IDE11 + push eax + + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + mov esi, [eax+IDE_CACHE.pointer] + pop eax + ret +;-------------------------------------- +@@: + mov esi, [eax+IDE_CACHE.data_pointer] + pop eax + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_2: +; 1 - IDE0 ... 12 - IDE11 + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + mov eax, [eax+IDE_CACHE.system_data] + ret +;-------------------------------------- +@@: + mov eax, [eax+IDE_CACHE.appl_data] + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_3: +; 1 - IDE0 ... 12 - IDE11 + push eax + + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + mov edi, [eax+IDE_CACHE.search_start] + pop eax + ret +;-------------------------------------- +@@: + mov edi, [eax+IDE_CACHE.appl_search_start] + pop eax + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_4: +; 1 - IDE0 ... 12 - IDE11 + push eax + + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + cmp edi, [eax+IDE_CACHE.system_sad_size] + pop eax + ret +;-------------------------------------- +@@: + cmp edi, [eax+IDE_CACHE.appl_sad_size] + pop eax + ret +;-------------------------------------------------------------------- +align 4 +cd_calculate_cache_5: +; 1 - IDE0 ... 12 - IDE11 + push eax + + mov eax, [cdpos] + dec eax + imul eax, sizeof.IDE_CACHE + add eax, cache_ide0 + + cmp [cd_appl_data], 0 + jne @f + + mov [eax+IDE_CACHE.search_start], edi + pop eax + ret +;-------------------------------------- +@@: + mov [eax+IDE_CACHE.appl_search_start], edi + pop eax + ret +;-------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-lldw/blkdev/rd.inc b/kernel/branches/kolibri-lldw/blkdev/rd.inc new file mode 100644 index 000000000..8ae2bccc2 --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/rd.inc @@ -0,0 +1,192 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; RAMDISK functions ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +iglobal +align 4 +ramdisk_functions: + dd .size + dd 0 ; no close() function + dd 0 ; no closemedia() function + dd ramdisk_querymedia + dd ramdisk_read + dd ramdisk_write + dd 0 ; no flush() function + dd ramdisk_adjust_cache_size +.size = $ - ramdisk_functions +endg + +iglobal +align 4 +ramdisk_actual_size dd RAMDISK_CAPACITY +endg + +; This function is called early in boot process. +; It creates filesystem /rd/1 based on raw image data loaded by somebody before +; to memory named as RAMDISK with max size RAMDISK_CAPACITY, may be less. +proc ramdisk_init +iglobal +ramdisk_name db 'rd',0 +endg + push ebx esi ; save used registers to be stdcall +; 1. Register the device and the (always inserted) media in the disk subsystem. + stdcall disk_add, ramdisk_functions, ramdisk_name, 0, 0 + test eax, eax + jz .fail + mov ebx, eax + stdcall disk_media_changed, eax, 1 +; 2. We don't know actual size of loaded image, +; so try to calculate it using partition structure, +; assuming that file systems fill the real size based on contents of the partition. +; 2a. Prepare for loop over partitions. + xor eax, eax + xor ecx, ecx + xor edx, edx +; 2b. Check that at least one partition was recognized. + cmp [ebx+DISK.NumPartitions], ecx + jz .fail +; 2c. Loop over partitions. +.partitions: +; For every partition, set edx to maximum between edx and end of partition. + mov esi, [ebx+DISK.Partitions] + mov esi, [esi+ecx*4] + mov eax, dword [esi+PARTITION.FirstSector] + add eax, dword [esi+PARTITION.Length] + cmp eax, edx + jb @f + mov edx, eax +@@: + inc ecx + cmp ecx, [ebx+DISK.NumPartitions] + jb .partitions +; 3. Reclaim unused memory, if any. + mov [ramdisk_actual_size], edx + add edx, 7 ; aligning up + shr edx, 3 ; 512-byte sectors -> 4096-byte pages + mov esi, RAMDISK_CAPACITY / 8 ; aligning down + sub esi, edx + jbe .no_reclaim + shl edx, 12 + add edx, RAMDISK - OS_BASE +@@: + mov eax, edx + call free_page + add edx, 0x1000 + dec esi + jnz @b +.no_reclaim: + mov eax, ebx + pop esi ebx ; restore used registers to be stdcall + ret +.fail: + dbgstr 'Failed to initialize ramdisk' + pop esi ebx ; restore used registers to be stdcall + ret +endp + +; Returns information about disk media. +proc ramdisk_querymedia + virtual at esp+4 + .userdata dd ? + .info dd ? + end virtual +; Media is always present, sector size is always 512 bytes. + mov edx, [.userdata] + mov ecx, [.info] + mov [ecx+DISKMEDIAINFO.Flags], 0 + mov [ecx+DISKMEDIAINFO.SectorSize], 512 + mov eax, [ramdisk_actual_size] + mov dword [ecx+DISKMEDIAINFO.Capacity], eax + mov dword [ecx+DISKMEDIAINFO.Capacity+4], 0 +; Return zero as an indicator of success. + xor eax, eax + retn 8 +endp + +; Common procedure for reading and writing. +; operation = 0 for reading, operation = 1 for writing. +; Arguments of ramdisk_read and ramdisk_write are the same. +macro ramdisk_read_write operation +{ + push esi edi ; save used registers to be stdcall + mov esi, [userdata] + mov edi, [numsectors_ptr] +; 1. Determine number of sectors to be transferred. +; This is either the requested number of sectors or number of sectors +; up to the disk boundary, depending of what is less. + xor ecx, ecx +; 1a. Test whether [start_sector] is less than RAMDISK_CAPACITY. +; If so, calculate number of sectors between [start_sector] and RAMDISK_CAPACITY. +; Otherwise, the actual number of sectors is zero. + cmp dword [start_sector+4], ecx + jnz .got_number + mov eax, [ramdisk_actual_size] + sub eax, dword [start_sector] + jbe .got_number +; 1b. Get the requested number of sectors. + mov ecx, [edi] +; 1c. If it is greater than number of sectors calculated in 1a, use the value +; from 1a. + cmp ecx, eax + jb .got_number + mov ecx, eax +.got_number: +; 2. Compare the actual number of sectors with requested. If they are +; equal, set eax (it will be the returned value) to zero. Otherwise, +; use DISK_STATUS_END_OF_MEDIA. + xor eax, eax + cmp ecx, [edi] + jz @f + mov al, DISK_STATUS_END_OF_MEDIA +@@: +; 3. Store the actual number of sectors. + mov [edi], ecx +; 4. Calculate source and destination addresses. +if operation = 0 ; reading? + mov esi, dword [start_sector] + shl esi, 9 + add esi, RAMDISK + mov edi, [buffer] +else ; writing? + mov edi, dword [start_sector] + shl edi, 9 + add edi, RAMDISK + mov esi, [buffer] +end if +; 5. Calculate number of dwords to be transferred. + shl ecx, 9-2 +; 6. Copy data. + rep movsd +; 7. Return. The value in eax was calculated in step 2. + pop edi esi ; restore used registers to be stdcall +} + +; Reads one or more sectors from the device. +proc ramdisk_read userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword + ramdisk_read_write 0 + ret +endp + +; Writes one or more sectors to the device. +proc ramdisk_write userdata:dword, buffer:dword, start_sector:qword, numsectors_ptr:dword + ramdisk_read_write 1 + ret +endp + +; The kernel calls this function when initializing cache subsystem for +; the media. This call allows the driver to adjust the cache size. +proc ramdisk_adjust_cache_size + virtual at esp+4 + .userdata dd ? + .suggested_size dd ? + end virtual +; Since ramdisk does not need cache, just return 0. + xor eax, eax + retn 8 +endp diff --git a/kernel/branches/kolibri-lldw/blkdev/rdsave.inc b/kernel/branches/kolibri-lldw/blkdev/rdsave.inc new file mode 100644 index 000000000..722aa49ef --- /dev/null +++ b/kernel/branches/kolibri-lldw/blkdev/rdsave.inc @@ -0,0 +1,33 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +iglobal +saverd_fileinfo: + dd 2 ; subfunction: write + dd 0 ; (reserved) + dd 0 ; (reserved) +.size: + dd 0 + dd RAMDISK + db 0 +.name: + dd ? +endg +sysfn_saveramdisk: ; 18.6 = SAVE FLOPPY IMAGE (HD version only) + mov ebx, saverd_fileinfo + mov [ebx+21], ecx + mov eax, [ramdisk_actual_size] + shl eax, 9 + mov [ebx+12], eax + pushad + call file_system_lfn_protected ;in ebx + popad + mov [esp+32], eax + ret diff --git a/kernel/branches/kolibri-lldw/boot/ETFONT.FNT b/kernel/branches/kolibri-lldw/boot/ETFONT.FNT new file mode 100644 index 0000000000000000000000000000000000000000..114f0609fd8a6364136a3dccfc4224eeb224155d GIT binary patch literal 4096 zcmZu!&5GMr6uzXOm2jpzwHcgE7g>q~98rV^LSLbqu0&GQEJT=g@Nkvk&jj&MM(l z`^)|QV!PifcCKhaWpZRQkCL(vqcq)aw^5WTKT0c1?l6WS5(#(Dw7nn$L_C`2&z|Kn zod+-8fB5kJLiyv-w~yxyXf7Jk9F#^dAo%3TNenu@xf1;5#hVwPU$)l^8Hl3GbemqP z{M*IV&92?uTrJ)*`(7{_PuuBu6x>ts6sn`j9Cl<5guC$HTwY$jen7;yth1!3>mmv3 zQUv%)DHDivh(67yU<|4M^aSb*B3)q{DwlP{S)0W@b#8Jo;brI(2KGkfq46q5es7N>CUO@h7y&Y9+xulAT zV3f?E3#9caYZ0$iCvcDkL15(#kJF-Q;qqAKw$d|t@4O_VE9UqlkqM{UM^W`Z|Y!&1SP+sDDS4kd_=0`8%wD zl2-hk;50u+go|_Cpl(+XGtY~i@Z0?P%lXUfd9L|VoMkt?`J1}VzZXoF*qqv<#mAHi zf6S|)+wa#fHJ`)iweW8;CU39)+_oquNcCUewwsR~=3EohcsV{&q5dgo1k< z635DS=wPSGvF}%@Y@aY>K2=ln{?8TNHjev^MP=q;>G!L<$}-9}7J=7L)>r_RzhhV` zAg=qHgvuJ5!1zh=bH+8&{DMCt@um#9g+qTaBmJ6REX|}}{~&N9YzGkXg2|AV1)%aG zj2V^>=RUPa@Un}0P3o;*vwu{CE*{~RehHX=wx!pzyjveUA1lD-AI0$|n=_c6N704M0HeLmbskxep}l1rp-uwGEM zJ|=yA>~c3)9}(UR35BcmC;2|E$%1qkGDq(8F}1}u&ro~oR}gAulWMhkMe@!~i>7HQ zBOe)xzf>R27K+Cm)MN3tqbp7^q&WZh+f+nk zH_#!*E0`3kV&ai=K*dV%*FOwDKmIZN{Qc|j!m?cJAy`CeZ6UY_N?bn@)azSF0Ayu8gf{~06xfxXGZ z`)l#3oZNcx>iEgQe6LUZi4OC7STCPH{u9)@n142Ob3peGNaY6Zy4iZQTCL@L?&!bO zszEK-Gztv^@k3-P=VQ6va7H{N8_fY zSlhFm#|hSC?z8a6IL=N z{d-|+LXXeBiJ?QHiHC+CzrqWx=gv1z-S`P;Z0hNxU3JH845LZnE5!ZKi`s)WZ-Z#l e_IRI|MWZlvL literal 0 HcmV?d00001 diff --git a/kernel/branches/kolibri-lldw/boot/bootcode.inc b/kernel/branches/kolibri-lldw/boot/bootcode.inc new file mode 100644 index 000000000..e0dc69e98 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootcode.inc @@ -0,0 +1,1400 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; BOOTCODE.INC ;; +;; ;; +;; KolibriOS 16-bit loader, ;; +;; based on bootcode for MenuetOS ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;========================================================================== +; +; 16 BIT FUNCTIONS +; +;========================================================================== + + +putchar: +; in: al=character + mov ah, 0Eh + mov bh, 0 + int 10h + ret + +print: +; in: si->string + mov al, 186 + call putchar + mov al, ' ' + call putchar + +printplain: +; in: si->string + pusha + lodsb +@@: + call putchar + lodsb + test al, al + jnz @b + popa + ret + +getkey: ; Use BIOS INT 16h to read a key from the keyboard +; get number in range [bl,bh] (bl,bh in ['0'..'9']) +; in: bx=range +; out: ax=digit (1..9, 10 for 0) + mov ah, 0 ; If 'int 16h' is called with 'ah' equal to zero, the BIOS will not return control to the caller + int 16h ; until a key is available in the system type ahead buffer. On return, 'al' contains the ASCII + cmp al, 27 ; code for the key read from the buffer and 'ah' contains the keyboard scan code. (27=>ESC) + jz @f ; If ESC is pressed, return (user doesn't want to change any value). + cmp al, bl ; Compare 'al' (ASCII code of key pressed) with 'bl' (lowest accepted char from the range). + jb getkey ; ASCII code is below lowest accepted value => continue waiting for another key. + cmp al, bh ; Compare 'al' (ASCII code of key pressed) with 'bh' (highest accepted char from the range). + ja getkey ; ASCII code is above highest accepted value => continue waiting for another key. + push ax ; If the pressed key is in the accepted range, save it on the stack and echo to screen. + call putchar + pop ax + and ax, 0Fh ; Convert ASCII code to number: '1'->1, '2'->2, etc. 0Fh=1111b. + jnz @f ; ASCII code for '0' is 48 (110000b). (110000b AND 1111b) = 0 + mov al, 10 ; So if key '0' was entered, return 10 in 'ax' +@@: + ret + +setcursor: +; in: dl=column, dh=row + mov ah, 2 + mov bh, 0 + int 10h + ret + +macro _setcursor row,column +{ + mov dx, row*256 + column + call setcursor +} + +macro _ask_question question,range,variable_to_set +{ + _setcursor 16,0 + mov si, question ; Print the question + call print + mov bx, range ; range accepted for answer + call getkey + cmp al, 27 ; If ESC was pressed, do not change the value + jz .esc_pressed + mov [variable_to_set], al +} + +boot_read_floppy: + push si + xor si, si + mov ah, 2 ; read +@@: + push ax + int 0x13 + pop ax + jnc @f + inc si + cmp si, 10 + jb @b +sayerr_badsect: + mov si, badsect +sayerr_plain: + call printplain + jmp $ +@@: + pop si + ret + +; convert abs. sector number (AX) to BIOS T:H:S +; sector number = (abs.sector%BPB_SecPerTrk)+1 +; pre.track number = (abs.sector/BPB_SecPerTrk) +; head number = pre.track number%BPB_NumHeads +; track number = pre.track number/BPB_NumHeads +; Return: cl - sector number +; ch - track number +; dl - drive number (0 = a:) +; dh - head number +conv_abs_to_THS: + push bx + mov bx, word [BPB_SecPerTrk] + xor dx, dx + div bx + inc dx + mov cl, dl ; cl = sector number + mov bx, word [BPB_NumHeads] + xor dx, dx + div bx + ; !!!!!!! ax = track number, dx = head number + mov ch, al ; ch=track number + xchg dh, dl ; dh=head number + mov dl, 0 ; dl=0 (drive 0 (a:)) + pop bx + retn +; needed variables +BPB_SecPerTrk dw 0 ; sectors per track +BPB_NumHeads dw 0 ; number of heads +BPB_FATSz16 dw 0 ; size of FAT +BPB_RootEntCnt dw 0 ; count of root dir. entries +BPB_BytsPerSec dw 0 ; bytes per sector +BPB_RsvdSecCnt dw 0 ; number of reserved sectors +BPB_TotSec16 dw 0 ; count of the sectors on the volume +BPB_SecPerClus db 0 ; number of sectors per cluster +BPB_NumFATs db 0 ; number of FAT tables +abs_sector_adj dw 0 ; adjustment to make abs. sector number +end_of_FAT dw 0 ; end of FAT table +FirstDataSector dw 0 ; begin of data + +;========================================================================= +; +; 16 BIT CODE +; +;========================================================================= + +include 'bootvesa.inc' ;Include source for boot vesa +if defined extended_primary_loader +include 'parsers.inc' +end if + +start_of_code: + +if defined extended_primary_loader +; save data from primary loader + mov word [cs:bootcallback], si + mov word [cs:bootcallback+2], ds + push cs + pop ds + mov [bootdevice], ax + mov [bootfs], bx + +; set up stack + mov ax, (TMP_STACK_TOP and 0xF0000) shr 4 + mov ss, ax + mov sp, TMP_STACK_TOP and 0xFFFF + +; try to load configuration file + mov ax, 1 + mov di, config_file_struct + call [bootcallback] + cld + push cs + pop es +; bx=0 - ok, bx=1 - part of file loaded, assume this is ok + cmp bx, 1 + ja .config_bad +; configuration file was loaded, parse +; if length is too big, use first 0FFFFh bytes + test dx, dx + jz @f + mov ax, 0FFFFh +@@: +; ds:si will be pointer to current data, dx = limit + xchg ax, dx + push 4000h + pop ds + xor si, si +.parse_loop: +; skip spaces + cmp si, dx + jae .parse_done + lodsb + cmp al, ' ' + jbe .parse_loop + dec si +; loop over all possible configuration values + mov bx, config_file_variables +.find_variant: +; get length + mov cx, [es:bx] +; zero length = end of list + jecxz .find_newline +; skip over length + inc bx + inc bx + mov di, bx +; skip over string + add bx, cx +; test whether we have at least cx symbols left + mov ax, cx + add ax, si + jc .next_variant1 + cmp ax, dx + jae .next_variant1 +; save current position + push si +; compare strings + repz cmpsb + jnz .next_variant2 +; strings are equal; look for "=" with possible spaces before and after +@@: + cmp si, dx + jae .next_variant2 + lodsb + cmp al, ' ' + jbe @b + cmp al, '=' + jnz .next_variant2 +; ok, we found the true variant +; ignore saved position on the stack + pop ax +; call the parser + call word [es:bx] +; line parsed, find next +.find_newline: + cmp si, dx + jae .parse_done + lodsb + cmp al, 13 + jz .parse_loop + cmp al, 10 + jz .parse_loop + jmp .find_newline +.next_variant2: +; continue to the next variant, restoring current position + pop si +.next_variant1: +; continue to the next variant +; skip over the parser + inc bx + inc bx + jmp .find_variant +.parse_done: +.config_bad: + +; set up segment registers + push cs + pop ds +else + cld + push 0 + pop es +; \begin{diamond}[02.12.2005] +; if bootloader sets ax = 'KL', then ds:si points to loader block + cmp ax, 'KL' + jnz @f + mov word [cs:cfgmanager.loader_block], si + mov word [cs:cfgmanager.loader_block+2], ds + mov word [es:BOOT_LO.kernel_restart], kernel_restart_bootblock +@@: +; \end{diamond}[02.12.2005] + +; if bootloader sets cx = 'HA' and dx = 'RD', then bx contains identifier of source disk +; (see comment to BOOT_LO.sys_disk and loader_doc.txt) + mov word [es:BOOT_LO.sys_disk], 'r1' ; default value: /rd/1 + cmp cx, 'HA' + jnz no_hd_load + cmp dx, 'RD' + jnz no_hd_load + mov [es:BOOT_LO.sys_disk], bx +no_hd_load: + +; set up stack + mov ax, (TMP_STACK_TOP and 0xF0000) shr 4 + mov ss, ax + mov sp, TMP_STACK_TOP and 0xFFFF +; set up segment registers + push cs + pop ds + push cs + pop es +end if + +; set videomode + mov ax, 3 + int 0x10 + +if lang eq ru + ; Load & set russian VGA font (RU.INC) + mov bp, RU_FNT1 ; RU_FNT1 - First part + mov bx, 1000h ; 768 bytes + mov cx, 30h ; 48 symbols + mov dx, 80h ; 128 - position of first symbol + mov ax, 1100h + int 10h + + mov bp, RU_FNT2 ; RU_FNT2 -Second part + mov bx, 1000h ; 512 bytes + mov cx, 20h ; 32 symbols + mov dx, 0E0h ; 224 - position of first symbol + mov ax, 1100h + int 10h + ; End set VGA russian font +else if lang eq et + mov bp, ET_FNT ; ET_FNT1 + mov bx, 1000h ; + mov cx, 255 ; 256 symbols + xor dx, dx ; 0 - position of first symbol + mov ax, 1100h + int 10h +end if + +; draw frames + push 0xb800 + pop es + xor di, di + mov ah, 1*16+15 + +; draw top + mov si, d80x25_top + mov cx, d80x25_top_num * 80 +@@: + lodsb + stosw + loop @b +; draw spaces + mov si, space_msg + mov dx, 25 - d80x25_top_num - d80x25_bottom_num +dfl1: + push si + mov cx, 80 +@@: + lodsb + stosw + loop @b + pop si + dec dx + jnz dfl1 +; draw bottom + mov si, d80x25_bottom + mov cx, d80x25_bottom_num * 80 +@@: + lodsb + stosw + loop @b + + mov byte [space_msg+80], 0 ; now space_msg is null terminated + + _setcursor d80x25_top_num,0 + + +; TEST FOR 386+ + + mov bx, 0x4000 + pushf + pop ax + mov dx, ax + xor ax, bx + push ax + popf + pushf + pop ax + and ax, bx + and dx, bx + cmp ax, dx + jnz cpugood + mov si, not386 +sayerr: + call print + jmp $ + cpugood: + + push 0 + popf + +; set up esp + movzx esp, sp + + push 0 + pop es + + xor cx, cx +@@: + in al, 64h + test al, 2 + loopnz @b + + mov al, 0xf6 ; Сброс клавиатуры, разрешить сканирование + out 0x60, al + xor cx, cx +@@: + in al, 64h + test al, 1 + loopz @b + in al, 0x60 + +;;;/diamond today 5.02.2008 +; set keyboard typematic rate & delay + mov al, 0xf3 + out 0x60, al + xor cx, cx +@@: + in al, 64h + test al, 1 + loopz @b + in al, 0x60 + mov al, 0 + out 0x60, al + xor cx, cx +@@: + in al, 64h + test al, 1 + loopz @b + in al, 0x60 +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + sti +; --------------- APM --------------------- + and word [es:BOOT_LO.apm_version], 0 ; ver = 0.0 (APM not found) + mov ax, 0x5300 + xor bx, bx + int 0x15 + jc apm_end ; APM not found + test cx, 2 + jz apm_end ; APM 32-bit protected-mode interface not supported + mov [es:BOOT_LO.apm_version], ax ; Save APM Version + mov [es:BOOT_LO.apm_flags], cx ; Save APM flags + + ; Write APM ver ---- + and ax, 0xf0f + add ax, '00' + mov si, msg_apm + mov [si + 5], ah + mov [si + 7], al + _setcursor 0, 3 + call printplain + ; ------------------ + + mov ax, 0x5304 ; Disconnect interface + xor bx, bx + int 0x15 + mov ax, 0x5303 ; Connect 32 bit mode interface + xor bx, bx + int 0x15 + + mov [es:BOOT_LO.apm_entry], ebx + mov [es:BOOT_LO.apm_code_32], ax + mov [es:BOOT_LO.apm_code_16], cx + mov [es:BOOT_LO.apm_data_16], dx + +apm_end: + _setcursor d80x25_top_num, 0 + +if ~ defined extended_primary_loader +;CHECK current of code + cmp [cfgmanager.loader_block], -1 + jz noloaderblock + les bx, [cfgmanager.loader_block] + cmp byte [es:bx], 1 + mov si, loader_block_error + jnz sayerr + push 0 + pop es +end if + +noloaderblock: +; DISPLAY VESA INFORMATION + call print_vesa_info + call calc_vmodes_table + call check_first_parm ;check and enable cursor_pos + +; \begin{diamond}[30.11.2005] +cfgmanager: +; settings: +; a) preboot_graph = graphical mode +; preboot_gprobe = probe this mode? +; b) preboot_biosdisk = use BIOS disks through V86 emulation? // (earlier was: preboot_dma = use DMA access?) +; c) preboot_debug = duplicates kernel debug output to the screen +; d) preboot_launcher = start the first app (right now it's LAUNCHER) after kernel is loaded? +; e) preboot_device = from where to boot? + +; determine default settings +if ~ defined extended_primary_loader + mov [.bSettingsChanged], 0 +end if + +;.preboot_gr_end: + mov di, preboot_device +; if image in memory is present and [preboot_device] is uninitialized, +; set it to use this preloaded image + cmp byte [di], 0 + jnz .preboot_device_inited +if defined extended_primary_loader + inc byte [di] + cmp byte [bootdevice], 'f' ; floppy? + jz .preboot_device_inited + inc byte [di] +else + cmp [.loader_block], -1 + jz @f + les bx, [.loader_block] + test byte [es:bx+1], 1 + jz @f + mov byte [di], 3 + jmp .preboot_device_inited +@@: +; otherwise, set [preboot_device] to 1 (default value - boot from floppy) + mov byte [di], 1 +end if +.preboot_device_inited: +; following 4 lines set variables to 1 if its current value is 0 + cmp byte [di+preboot_dma-preboot_device], 1 + adc byte [di+preboot_dma-preboot_device], 0 + cmp byte [di+preboot_launcher-preboot_device], 1 ; Start LAUNCHER by default + adc byte [di+preboot_launcher-preboot_device], 0 + _setcursor 5,2 + + mov si, linef + call printplain + mov si, start_msg + call print + mov si, time_msg + call print +; get start time + call .gettime + mov [.starttime], eax + mov word [.timer], .newtimer + mov word [.timer+2], cs +.printcfg: + + _setcursor 9,0 + mov si, current_cfg_msg + call print + mov si, curvideo_msg + call print + + call draw_current_vmode + + mov si, usebd_msg + cmp [preboot_biosdisk], 1 + call .say_on_off + mov si, debug_mode_msg + cmp [preboot_debug], 1 + call .say_on_off + mov si, launcher_msg + cmp [preboot_launcher], 1 + call .say_on_off + mov si, preboot_device_msg + call print + mov al, [preboot_device] +if defined extended_primary_loader + and eax, 3 +else + and eax, 7 +end if + mov si, [preboot_device_msgs+eax*2] + call printplain +.show_remarks: +; show remarks in gray color + mov di, ((21-num_remarks)*80 + 2)*2 + push 0xB800 + pop es + mov cx, num_remarks + mov si, remarks +.write_remarks: + lodsw + push si + xchg ax, si + mov ah, 1*16+7 ; background: blue (1), foreground: gray (7) + push di +.write_remark: + lodsb + test al, al + jz @f + stosw + jmp .write_remark +@@: + pop di + pop si + add di, 80*2 + loop .write_remarks +.wait: + _setcursor 25,0 ; out of screen +; set timer interrupt handler + cli + push 0 + pop es + push dword [es:8*4] + pop dword [.oldtimer] + push dword [.timer] + pop dword [es:8*4] +; mov eax, [es:8*4] +; mov [.oldtimer], eax +; mov eax, [.timer] +; mov [es:8*4], eax + sti +; wait for keypressed + xor ax, ax + int 16h + push ax +; restore timer interrupt +; push 0 +; pop es + mov eax, [.oldtimer] + mov [es:8*4], eax + mov [.timer], eax + + _setcursor 7,0 + mov si, space_msg + call printplain +; clear remarks and restore normal attributes + push es + mov di, ((21-num_remarks)*80 + 2)*2 + push 0xB800 + pop es + mov cx, num_remarks + mov ax, ' ' + (1*16 + 15)*100h +@@: + push cx + mov cx, 76 + rep stosw + pop cx + add di, 4*2 + loop @b + pop es + pop ax +; switch on key + cmp al, 13 + jz .continue + or al, 20h + cmp al, 'a' ; select graphical mode + jz .change_a + cmp al, 'q' ; Trick to make 'A' key on azerty keyboard work + je .change_a + cmp al, 'b' ; use BIOS disks? // (selecting YES will make BIOS disks visible as /bd) + jz .change_b + cmp al, 'c' ; load kernel in debug mode? + jz .change_c + cmp al, 'd' ; start launcher after kernel is loaded? + jz .change_d + cmp al, 'e' ; select boot origin + jnz .show_remarks +; e) preboot_device = from where to boot? +if defined extended_primary_loader + _ask_question bdev,'13',preboot_device ; range accepted for answer: 1-3 +else + _ask_question bdev,'14',preboot_device ; range accepted for answer: 1-4 +end if + _setcursor 14,0 + +.d: +if ~ defined extended_primary_loader + mov [.bSettingsChanged], 1 +end if +.esc_pressed: + call clear_vmodes_table ;clear vmodes_table + jmp .printcfg + +.change_a: + call clear_vmodes_table ;clear vmodes_table + + mov si, word [cursor_pos] + mov word [cursor_pos_old], si +.loops: + call draw_vmodes_table + _setcursor 25,0 ; out of screen + xor ax, ax + int 0x16 +; call clear_table_cursor ;clear current position of cursor + + mov si, word [cursor_pos] + + cmp al, 27 ; If ESC was pressed, do not change the value + jnz @f ; Just exit the resolution selection box + + mov si, word [cursor_pos_old] + mov word [cursor_pos], si + jmp .esc_pressed +@@: + cmp ah, 0x48;x,0x48E0 ; up + jne .down + cmp si, modes_table + jbe .loops + sub word [cursor_pos], size_of_step + jmp .loops + +.down: + cmp ah, 0x50;x,0x50E0 ; down + jne .pgup + cmp word[es:si+10], -1 + je .loops + add word [cursor_pos], size_of_step + jmp .loops + +.pgup: + cmp ah, 0x49 ; page up + jne .pgdn + sub si, size_of_step*long_v_table + cmp si, modes_table + jae @f + mov si, modes_table +@@: + mov word [cursor_pos], si + mov si, word [home_cursor] + sub si, size_of_step*long_v_table + cmp si, modes_table + jae @f + mov si, modes_table +@@: + mov word [home_cursor], si + jmp .loops + +.pgdn: + cmp ah, 0x51 ; page down + jne .enter + mov ax, [end_cursor] + add si, size_of_step*long_v_table + cmp si, ax + jb @f + mov si, ax + sub si, size_of_step +@@: + mov word [cursor_pos], si + mov si, word [home_cursor] + sub ax, size_of_step*long_v_table + add si, size_of_step*long_v_table + cmp si, ax + jb @f + mov si, ax +@@: + mov word [home_cursor], si + jmp .loops + +.enter: + cmp al, 0x0D;x,0x1C0D ; enter + jne .loops + push word [cursor_pos] + pop bp + push word [es:bp] + pop word [x_save] + push word [es:bp+2] + pop word [y_save] + push word [es:bp+6] + pop word [number_vm] + mov word [preboot_graph], bp ;save choose + + jmp .d + +.change_b: ; b) preboot_biosdisk = use BIOS disks through V86 emulation? +; _setcursor 16,0 +; mov si, ask_dma // (earlier was: preboot_dma = use DMA access?) +; call print +; mov bx, '13' ; range accepted for answer: 1-3 +; call getkey +; mov [preboot_dma], al + _ask_question ask_bd,'12',preboot_biosdisk ; range accepted for answer: 1-2 + _setcursor 11,0 + jmp .d +.change_c: ; c) preboot_debug = duplicates kernel debug output to the screen + _ask_question ask_debug,'12',preboot_debug ; range accepted for answer: 1-2 + _setcursor 12,0 + jmp .d +.change_d: ; d) preboot_launcher = start the first app (right now it's LAUNCHER) after kernel is loaded? + _ask_question ask_launcher,'12',preboot_launcher ; range accepted for answer: 1-2 + _setcursor 13,0 + jmp .d +;;;;;;;;;;;;;;;;;;;;;;;;;;;; +.say_on_off: + pushf + call print + mov si, on_msg + popf + jz @f + mov si, off_msg +@@: + jmp printplain +; novesa and vervesa strings are not used at the moment of executing this code +virtual at novesa +.oldtimer dd ? +.starttime dd ? +if ~ defined extended_primary_loader +.bSettingsChanged db ? +end if +.timer dd ? +end virtual +if ~ defined extended_primary_loader +.loader_block dd -1 +end if +.gettime: + mov ah, 0 + int 1Ah + xchg ax, cx + shl eax, 10h + xchg ax, dx + ret +.newtimer: + push ds + push cs + pop ds + pushf + call [.oldtimer] + pushad + call .gettime + sub eax, [.starttime] +if defined extended_primary_loader + sub ax, [preboot_timeout] +else + ; bios 0x1A timer runs at ~18 ticks per second + sub ax, 18*PREBOOT_TIMEOUT +end if + jae .timergo + neg ax + add ax, 18-1 + mov bx, 18 + xor dx, dx + div bx +if lang eq ru +; подождите 5 секунд, 4/3/2 секунды, 1 секунду + cmp al, 5 + mov cl, ' ' + jae @f + cmp al, 1 + mov cl, 0xE3 ; 'у' in cp866 + jz @f + mov cl, 0xEB ; 'ы' in cp866 +@@: + mov [time_str+9], cl +else if lang eq et + cmp al, 1 + ja @f + mov byte [time_str+9], ' ' + mov byte [time_str+10], ' ' +@@: +else if lang eq sp +; esperar 5/4/3/2 segundos, 1 segundo + cmp al, 1 + mov cl, 's' + ja @f + mov cl, ' ' +@@: + mov [time_str+10], cl +else +; wait 5/4/3/2 seconds, 1 second + cmp al, 1 + mov cl, 's' + ja @f + mov cl, ' ' +@@: + mov [time_str+9], cl +end if + add al, '0' + mov [time_str+1], al + mov si, time_msg + _setcursor 7,0 + call print + _setcursor 25,0 + popad + pop ds + iret +.timergo: + push 0 + pop es + mov eax, [.oldtimer] + mov [es:8*4], eax + mov sp, 0EC00h +.continue: + sti + _setcursor 6,0 + mov si, space_msg + call printplain + call printplain + _setcursor 6,0 + mov si, loading_msg + call print + _setcursor 16,0 +if ~ defined extended_primary_loader + cmp [.bSettingsChanged], 0 + jz .load + cmp [.loader_block], -1 + jz .load + les bx, [.loader_block] + mov eax, [es:bx+3] + push ds + pop es + test eax, eax + jz .load + push eax + mov si, save_quest + call print +.waityn: + mov ah, 0 + int 16h + or al, 20h + cmp al, 'n' + jz .loadc + if lang eq sp + cmp al, 's' + else + cmp al, 'y' + end if + jnz .waityn + call putchar + mov byte [space_msg+80], 186 + + pop eax + push cs + push .cont + push eax + retf ;call back +.loadc: + pop eax +.cont: + push cs + pop ds + mov si, space_msg + mov byte [si+80], 0 + _setcursor 16,0 + call printplain + _setcursor 16,0 +.load: +end if +; \end{diamond}[02.12.2005] + +; ASK GRAPHICS MODE + + call set_vmode + +; GRAPHICS ACCELERATION +; force yes + mov [es:BOOT_LO.mtrr], byte 1 + +; DMA ACCESS TO HD + + mov al, [preboot_dma] + mov [es:BOOT_LO.dma], al + +; Set kernel DEBUG mode - if nonzero, duplicates debug output to the screen. + mov al, [preboot_debug] + mov [es:BOOT_LO.debug_print], al ;// 0x901E + +; Start the first app (right now it's LAUNCHER) after kernel is loaded? + mov al, [preboot_launcher] + mov [es:BOOT_LO.launcher_start], al ;// 0x901D + +; BOOT DEVICE + + mov al, [preboot_device] +if defined extended_primary_loader + cmp al, RD_LOAD_FROM_MEMORY + jnz @f + mov al, RD_LOAD_FROM_NONE +@@: +end if + mov [es:BOOT_LO.rd_load_from], al + +; /sys path + mov eax, dword[preboot_syspath+0] + mov dword[es:BOOT_LO.syspath+0], eax + mov eax, dword[preboot_syspath+4] + mov dword[es:BOOT_LO.syspath+4], eax + mov eax, dword[preboot_syspath+8] + mov dword[es:BOOT_LO.syspath+8], eax + mov eax, dword[preboot_syspath+12] + mov dword[es:BOOT_LO.syspath+12], eax + + +; GET MEMORY MAP +include '../detect/biosmem.inc' + +; READ DISKETTE TO MEMORY + + cmp byte [es:BOOT_LO.rd_load_from], RD_LOAD_FROM_FLOPPY + jne no_sys_on_floppy + mov si, diskload + call print + xor ax, ax ; reset drive + xor dx, dx + int 0x13 +; do we boot from CD-ROM? + mov ah, 41h + mov bx, 55AAh + xor dx, dx + int 0x13 + jc .nocd + cmp bx, 0AA55h + jnz .nocd + mov ah, 48h + push ds + push es + pop ds + mov si, 0xa000 + mov word [si], 30 + int 0x13 + pop ds + jc .nocd + push ds + lds si, [es:si+26] + test byte [ds:si+10], 40h + pop ds + jz .nocd +; yes - read all floppy by 18 sectors + +; TODO: !!!! read only first sector and set variables !!!!! +; ... +; TODO: !!! then read flippy image track by track + + mov cx, 0x0001 ; startcyl,startsector +.a1: + push cx dx + mov al, 18 + mov bx, 0xa000 + call boot_read_floppy + mov si, movedesc + push es + push ds + pop es + mov cx, 256*18 + mov ah, 0x87 + int 0x15 + pop es + pop dx cx + test ah, ah + jnz sayerr_floppy + add dword [si+8*3+2], 512*18 + inc dh + cmp dh, 2 + jnz .a1 + mov dh, 0 + inc ch + cmp ch, 80 + jae ok_sys_on_floppy + pusha + mov al, ch + shr ch, 2 + add al, ch + aam + xchg al, ah + add ax, '00' + mov si, pros + mov [si], ax + call printplain + popa + jmp .a1 +.nocd: +; no - read only used sectors from floppy +; now load floppy image to memory +; at first load boot sector and first FAT table + +; read only first sector and fill variables + mov cx, 0x0001 ; first logical sector + xor dx, dx ; head = 0, drive = 0 (a:) + mov al, 1 ; read one sector + mov bx, 0xB000 ; es:bx -> data area + call boot_read_floppy +; fill the necessary parameters to work with a floppy + mov ax, word [es:bx+24] + mov word [BPB_SecPerTrk], ax + mov ax, word [es:bx+26] + mov word [BPB_NumHeads], ax + mov ax, word [es:bx+17] + mov word [BPB_RootEntCnt], ax + mov ax, word [es:bx+14] + mov word [BPB_RsvdSecCnt], ax + mov ax, word [es:bx+19] + mov word [BPB_TotSec16], ax + mov al, byte [es:bx+13] + mov byte [BPB_SecPerClus], al + mov al, byte [es:bx+16] + mov byte [BPB_NumFATs], al +; 18.11.2008 + mov ax, word [es:bx+22] + mov word [BPB_FATSz16], ax + mov cx, word [es:bx+11] + mov word [BPB_BytsPerSec], cx + +; count of clusters in FAT12 ((size_of_FAT*2)/3) +; mov ax, word [BPB_FATSz16] +; mov cx, word [BPB_BytsPerSec] +;end 18.11.2008 + xor dx, dx + mul cx + shl ax, 1 + mov cx, 3 + div cx ; now ax - number of clusters in FAT12 + mov word [end_of_FAT], ax + +; load first FAT table + mov cx, 0x0002 ; startcyl,startsector ; TODO!!!!! + xor dx, dx ; starthead,drive + mov al, byte [BPB_FATSz16] ; no of sectors to read + add bx, word [BPB_BytsPerSec] ; es:bx -> data area + call boot_read_floppy + mov bx, 0xB000 + +; and copy them to extended memory + mov si, movedesc + mov [si+8*2+3], bh ; from + + mov ax, word [BPB_BytsPerSec] + shr ax, 1 ; words per sector + mov cx, word [BPB_RsvdSecCnt] + add cx, word [BPB_FATSz16] + mul cx + push ax ; save to stack count of words in boot+FAT + xchg ax, cx + + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + test ah, ah + jz @f +sayerr_floppy: + mov dx, 0x3f2 + mov al, 0 + out dx, al +sayerr_memmove: + mov si, memmovefailed + jmp sayerr_plain +@@: + pop ax ; restore from stack count of words in boot+FAT + shl ax, 1 ; make bytes count from count of words + and eax, 0ffffh + add dword [si+8*3+2], eax + +; copy first FAT to second copy +; TODO: BPB_NumFATs !!!!! + add bx, word [BPB_BytsPerSec] ; !!! TODO: may be need multiply by BPB_RsvdSecCnt !!! + mov byte [si+8*2+3], bh ; bx - begin of FAT + + mov ax, word [BPB_BytsPerSec] + shr ax, 1 ; words per sector + mov cx, word [BPB_FATSz16] + mul cx + mov cx, ax ; cx - count of words in FAT + + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + test ah, ah + jnz sayerr_floppy + + mov ax, cx + shl ax, 1 + and eax, 0ffffh ; ax - count of bytes in FAT + add dword [si+8*3+2], eax + +; reading RootDir +; TODO: BPB_NumFATs + add bx, ax + add bx, 100h + and bx, 0ff00h ; bx - place in buffer to write RootDir + push bx + + mov bx, word [BPB_BytsPerSec] + shr bx, 5 ; divide bx by 32 + mov ax, word [BPB_RootEntCnt] + xor dx, dx + div bx + push ax ; ax - count of RootDir sectors + + mov ax, word [BPB_FATSz16] + xor cx, cx + mov cl, byte [BPB_NumFATs] + mul cx + add ax, word [BPB_RsvdSecCnt] ; ax - first sector of RootDir + + mov word [FirstDataSector], ax + pop bx + push bx + add word [FirstDataSector], bx ; Begin of data region of floppy + +; read RootDir + call conv_abs_to_THS + pop ax + pop bx ; place in buffer to write + push ax + call boot_read_floppy ; read RootDir into buffer +; copy RootDir + mov byte [si+8*2+3], bh ; from buffer + pop ax ; ax = count of RootDir sectors + mov cx, word [BPB_BytsPerSec] + mul cx + shr ax, 1 + mov cx, ax ; count of words to copy + push es + push ds + pop es + mov ah, 0x87 + int 0x15 + pop es + + mov ax, cx + shl ax, 1 + and eax, 0ffffh ; ax - count of bytes in RootDir + add dword [si+8*3+2], eax ; add count of bytes copied + +; Reading data clusters from floppy + mov byte [si+8*2+3], bh + push bx + + mov di, 2 ; First data cluster +.read_loop: + mov bx, di + shr bx, 1 ; bx+di = di*1.5 + jnc .even + test word [es:bx+di+0xB200], 0xFFF0 ; TODO: may not be 0xB200 !!! + jmp @f +.even: + test word [es:bx+di+0xB200], 0xFFF ; TODO: may not be 0xB200 !!! + +@@: + jz .skip +; read cluster di +;.read: + ;conv cluster di to abs. sector ax + ; ax = (N-2) * BPB_SecPerClus + FirstDataSector + mov ax, di + sub ax, 2 + xor bx, bx + mov bl, byte [BPB_SecPerClus] + mul bx + add ax, word [FirstDataSector] + call conv_abs_to_THS + pop bx + push bx + mov al, byte [BPB_SecPerClus] ; number of sectors in cluster + call boot_read_floppy + push es + push ds + pop es + pusha +; + mov ax, word [BPB_BytsPerSec] + xor cx, cx + mov cl, byte [BPB_SecPerClus] + mul cx + shr ax, 1 ; ax = (BPB_BytsPerSec * BPB_SecPerClus)/2 + mov cx, ax ; number of words to copy (count words in cluster) +; + mov ah, 0x87 + int 0x15 ; copy data + test ah, ah + popa + pop es + jnz sayerr_floppy +; skip cluster di +.skip: + mov ax, word [BPB_BytsPerSec] + xor cx, cx + mov cl, byte [BPB_SecPerClus] + mul cx + and eax, 0ffffh ; ax - count of bytes in cluster + add dword [si+8*3+2], eax + + mov ax, word [end_of_FAT] ; max cluster number + pusha +; draw percentage +; total clusters: ax +; read clusters: di + xchg ax, di + mov cx, 100 + mul cx + div di + aam + xchg al, ah + add ax, '00' + mov si, pros + cmp [si], ax + jz @f + mov [si], ax + call printplain +@@: + popa + inc di + cmp di, word [end_of_FAT] ; max number of cluster + jnz .read_loop + pop bx ; clear stack + +ok_sys_on_floppy: + mov si, backspace2 + call printplain + mov si, okt + call printplain +no_sys_on_floppy: + xor ax, ax ; reset drive + xor dx, dx + int 0x13 + mov dx, 0x3f2 ; floppy motor off + mov al, 0 + out dx, al + +if defined extended_primary_loader + cmp [es:BOOT_LO.rd_load_from], RD_LOAD_FROM_HD + jne no_sys_from_primary +; load kolibri.img using callback from primary loader + and word [movedesc + 24 + 2], 0 + mov byte [movedesc + 24 + 4], 10h +; read in blocks of 64K until file is fully loaded + mov ax, 1 +.repeat: + mov di, image_file_struct + call [bootcallback] + push cs + pop ds + push cs + pop es + cmp bx, 1 + ja sayerr_badsect + push bx + mov si, movedesc + and word [si + 16 + 2], 0 + mov byte [si + 16 + 4], 4 + mov ah, 87h + mov cx, 8000h + int 15h + pop bx + test ah, ah + jnz sayerr_memmove + inc byte [si + 24 + 4] + test bx, bx + jz no_sys_from_primary + mov ax, 2 + jmp .repeat +no_sys_from_primary: +end if + +; SET GRAPHICS + + xor ax, ax + mov es, ax + + mov ax, [es:BOOT_LO.vesa_mode] ; vga & 320x200 + mov bx, ax + cmp ax, 0x13 + je setgr + cmp ax, 0x12 + je setgr + mov ax, 0x4f02 ; Vesa +setgr: + int 0x10 + test ah, ah + mov si, fatalsel + jnz v_mode_error +; set mode 0x12 graphics registers: + cmp bx, 0x12 + jne gmok2 + + mov al, 0x05 + mov dx, 0x03ce + push dx + out dx, al ; select GDC mode register + mov al, 0x02 + inc dx + out dx, al ; set write mode 2 + + mov al, 0x02 + mov dx, 0x03c4 + out dx, al ; select VGA sequencer map mask register + mov al, 0x0f + inc dx + out dx, al ; set mask for all planes 0-3 + + mov al, 0x08 + pop dx + out dx, al ; select GDC bit mask register + ; for writes to 0x03cf +gmok2: + push ds + pop es diff --git a/kernel/branches/kolibri-lldw/boot/booten.inc b/kernel/branches/kolibri-lldw/boot/booten.inc new file mode 100644 index 000000000..376c01377 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/booten.inc @@ -0,0 +1,107 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision$ + + +d80x25_bottom: + db 186,' KolibriOS comes with ABSOLUTELY NO WARRANTY. See file COPYING for details ',186 + db 186,' If you find any bugs, please report them at: http://board.kolibrios.org ',186 + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm db " APM x.x ", 0 +novesa db "Display: EGA/CGA",13,10,0 +s_vesa db "Version of VESA: " + .ver db "?.?",13,10,0 + +gr_mode db "Select a videomode: ",13,10,0 + +ask_bd db "Add disks visible by BIOS emulated in V86-mode? [1-yes, 2-no]: ",0 + +if defined extended_primary_loader +bdev db "Load ramdisk from [1-floppy; 2-kolibri.img; 3-don't load]: ",0 +else +bdev db "Load ramdisk from [1-floppy; 2-C:\kolibri.img (FAT32);" + db 13,10,186," " + db "3-use preloaded ram-image from kernel restart;" + db 13,10,186," " + db "4-create blank image]: ",0 +end if + +prnotfnd db "Fatal - Videomode not found.",0 + +not386 db "Fatal - CPU 386+ required.",0 +fatalsel db "Fatal - Graphics mode not supported by hardware.",0 +pres_key db "Press any key to choose a new videomode.",0 +badsect db 13,10,186," Fatal - Bad sector. Replace floppy.",0 +memmovefailed db 13,10,186," Fatal - Int 0x15 move failed.",0 +okt db " ... OK" +linef db 13,10,0 +diskload db "Loading diskette: 00 %",8,8,8,8,0 +pros db "00" +backspace2 db 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg db "Press [abcde] to change settings, press [Enter] to continue booting",13,10,0 +time_msg db " or wait " +time_str db " 5 seconds" + db " to continue automatically",13,10,0 +current_cfg_msg db "Current settings:",13,10,0 +curvideo_msg db " [a] Videomode: ",0 + +mode0 db "320x200, EGA/CGA 256 colors",13,10,0 +mode9 db "640x480, VGA 16 colors",13,10,0 + +usebd_msg db " [b] Add disks visible by BIOS:",0 +on_msg db " on",13,10,0 +off_msg db " off",13,10,0 + +debug_mode_msg db " [c] Duplicate debug output to the screen:",0 +ask_debug db "Duplicate debug output to the screen? [1-yes, 2-no]: ",0 + +launcher_msg db " [d] Start LAUNCHER after kernel is loaded:",0 +ask_launcher db "Start first application (LAUNCHER) after kernel is loaded? [1-yes, 2-no]: ",0 + +preboot_device_msg db " [e] Floppy image: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,0 +pdm1 db "real floppy",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +pdm3 db "do not use floppy image",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 db "real floppy",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +pdm3 db "use already loaded image",13,10,0 +pdm4 db "create blank image",13,10,0 +end if + +loading_msg db "Loading KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest db "Remember current settings? [y/n]: ",0 +loader_block_error db "Bootloader data invalid, I cannot continue. Stopped.",0 +end if + +_st latin1 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1 latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0 +_r2 latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0 +_rs latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt latin1 '║ └───────────────────────────────┴─┘',13,10,0 + +remark1 db "Default values were selected to match most of configurations, but not all.",0 +remark2 db "If the system does not boot, try to disable option [b]. If the system gets",0 +remark3 db "stuck after booting, enable option [c], disable option [d] and make photo.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 diff --git a/kernel/branches/kolibri-lldw/boot/bootet.inc b/kernel/branches/kolibri-lldw/boot/bootet.inc new file mode 100644 index 000000000..57145da1f --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootet.inc @@ -0,0 +1,106 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision$ + + +d80x25_bottom: + latin1 '║ KolibriOS on IGASUGUSE GARANTIITA. Vaata faili COPYING info saamiseks. Kui ║' + latin1 '║ leiate vigu, anna neist palun teada aadressil: http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm latin1 " APM x.x ", 0 +novesa latin1 "Ekraan: EGA/CGA",13,10,0 +s_vesa latin1 "Vesa versioon: " + .ver db "?.?",13,10,0 + +gr_mode latin1 "Vali video resolutsioon: ",13,10,0 + +ask_bd latin1 "Lisa V86 reziimis BIOSle nähtavad kettad? [1-jah, 2-ei]: ",0 + +if defined extended_primary_loader +bdev latin1 "Paigalda mäluketas [1-diskett; 2-kolibri.img]: ",0 +else +bdev latin1 "Paigalda mäluketas [1-diskett; 2-C:\kolibri.img (FAT32);" + latin1 13,10,"║ " + latin1 "3-kasuta eellaaditud mäluketast kerneli restardist;" + latin1 13,10,"║ " + latin1 "4-loo tühi pilt]: ",0 +end if + +prnotfnd latin1 "Fataalne - Video resolutsiooni ei leitud.",0 + +not386 latin1 "Fataalne - CPU 386+ on vajalik.",0 +fatalsel latin1 "Fataalne - Riistvara ei toeta graafilist resolutsiooni.",0 +pres_key latin1 "Vajutage suvalist klahvi, et valida uus videomode.",0 +badsect latin1 13,10,"║ Fataalne - Vigane sektor. Asenda diskett.",0 +memmovefailed latin1 13,10,"║ Fataalne - Int 0x15 liigutamine ebaõnnestus.",0 +okt latin1 " ... OK" +linef latin1 13,10,0 +diskload latin1 "Loen disketti: 00 %",8,8,8,8,0 +pros latin1 "00" +backspace2 latin1 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg latin1 "Vajuta [abcde] seadete muutmiseks, vajuta [Enter] laadimise jätkamiseks",13,10,0 +time_msg latin1 " või oota " +time_str latin1 " 5 sekundit" + latin1 " automaatseks jätkamiseks",13,10,0 +current_cfg_msg latin1 "Praegused seaded:",13,10,0 +curvideo_msg latin1 " [a] Video resolutsioon: ",0 + +mode0 latin1 "320x200, EGA/CGA 256 värvi",0 +mode9 latin1 "640x480, VGA 16 värvi",0 + +usebd_msg latin1 " [b] Lisa BIOSle nähtavad kettad:",0 +on_msg latin1 " sees",13,10,0 +off_msg latin1 " väljas",13,10,0 + +debug_mode_msg latin1 " [c] Dubleeri silumisinfo ekraanile:",0 +ask_debug latin1 "Dubleeri silumisinfo ekraanile? [1-jah, 2-ei]: ",0 + +launcher_msg latin1 " [d] Käivita LAUNCHER pärast kerneli laadimist:",0 +ask_launcher latin1 "Käivita esimese programm (LAUNCHER) peale kerneli laadimist? [1-jah, 2-ei]: ",0 + +preboot_device_msg latin1 " [e] Disketi kujutis: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 latin1 "reaalne diskett",13,10,0 +pdm2 latin1 "kolibri.img",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 latin1 "reaalne diskett",13,10,0 +pdm2 latin1 "C:\kolibri.img (FAT32)",13,10,0 +pdm3 latin1 "kasuta juba laaditud kujutist",13,10,0 +pdm4 latin1 "loo tühi pilt",13,10,0 +end if + +loading_msg latin1 "Laadin KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest latin1 "Jäta meelde praegused seaded? [y/n]: ",0 +loader_block_error latin1 "Alglaaduri andmed vigased, ei saa jätkata. Peatatud.",0 +end if + +_st latin1 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1 latin1 '║ │ 320x200 EGA/CGA 256 värvi │ │',13,10,0 +_r2 latin1 '║ │ 640x480 VGA 16 värvi │ │',13,10,0 +_rs latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt latin1 '║ └───────────────────────────────┴─┘',13,10,0 + +remark1 latin1 "Vaikimisi väärtused on kasutatavad enamikes arvutites, kuid mitte kõigis.",0 +remark2 latin1 "Kui süsteem ei käivitu, proovige lülitada kirje [b] välja. Kui see läheb",0 +remark3 latin1 "kinni pärast käivitamist, võimaldama valik [c], keelake [d] ja teha foto.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/boot/bootge.inc b/kernel/branches/kolibri-lldw/boot/bootge.inc new file mode 100644 index 000000000..b06ce1240 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootge.inc @@ -0,0 +1,107 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +$Revision$ + + +d80x25_bottom: + db 186,' KolibriOS wird ohne jegliche Garantie vertrieben. Details stehen in der ',186 + db 186,' Datei COPYING. Bitte melden Sie Fehler bei: http://board.kolibrios.org ',186 + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm db " APM x.x ", 0 +novesa db "Anzeige: EGA/CGA ",13,10,0 +s_vesa db "Vesa-Version: " + .ver db "?.?",13,10,0 + +gr_mode db "Wahlen Sie einen videomode: ",13,10,0 + +ask_bd db "Add-Festplatten sichtbar BIOS in V86-Modus emuliert? [1-ja, 2 nein]: ",0 + +if defined extended_primary_loader +bdev db "Lade die Ramdisk von [1-Diskette; 2-kolibri.img]: ",0 +else +bdev db "Lade die Ramdisk von [1-Diskette; 2-C:\kolibri.img (FAT32);" + db 13,10,186," " + db "3-benutze ein bereits geladenes Kernel image;" + db 13,10,186," " + db "4-create blank image]: ",0 +end if + +prnotfnd db "Fatal - Videomodus nicht gefunden.",0 + +not386 db "Fatal - CPU 386+ benoetigt.",0 +fatalsel db "Fatal - Grafikmodus nicht unterstuetzt.",0 +pres_key db "Drucken Sie eine beliebige Taste, um eine neue videomode wahlen.",0 +badsect db 13,10,186," Fatal - Sektorfehler, Andere Diskette neutzen.",0 +memmovefailed db 13,10,186," Fatal - Int 0x15 Fehler.",0 +okt db " ... OK" +linef db 13,10,0 +diskload db "Lade Diskette: 00 %",8,8,8,8,0 +pros db "00" +backspace2 db 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg db "Druecke [abcde], um die Einstellungen zu aendern, druecke [Enter] zum starten",13,10,0 +time_msg db " oder warte " +time_str db " 5 Sekunden" + db " bis zum automatischen Start",13,10,0 +current_cfg_msg db "Aktuelle Einstellungen:",13,10,0 +curvideo_msg db " [a] Videomodus: ",0 + +mode0 db "320x200, EGA/CGA 256 colors",13,10,0 +mode9 db "640x480, VGA 16 colors",13,10,0 + +usebd_msg db " [b] Add-Festplatten sichtbar durch das BIOS:",0 +on_msg db " an",13,10,0 +off_msg db " aus",13,10,0 + +debug_mode_msg db " [c] Duplizieren debuggen Ausgabe auf dem Bildschirm:",0 +ask_debug db "Duplizieren debuggen Ausgabe auf dem Bildschirm? [1-ja, 2 nein]: ",0 + +launcher_msg db " [d] Start LAUNCHER nach Kernel geladen wird:",0 +ask_launcher db "Starten erste Anwendung nach Kernel geladen wird? [1-ja, 2 nein]: ",0 + +preboot_device_msg db " [e] Diskettenimage: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 db "Echte Diskette",13,10,0 +pdm2 db "kolibri.img",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 db "Echte Diskette",13,10,0 +pdm2 db "C:\kolibri.img (FAT32)",13,10,0 +pdm3 db "Nutze bereits geladenes Image",13,10,0 +pdm4 db "create blank image",13,10,0 +end if + +loading_msg db "Lade KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest db "Aktuelle Einstellungen speichern? [y/n]: ",0 +loader_block_error db "Bootloader Daten ungueltig, Kann nicht fortfahren. Angehalten.",0 +end if + +_st latin1 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1 latin1 '║ │ 320x200 EGA/CGA 256 colors │ │',13,10,0 +_r2 latin1 '║ │ 640x480 VGA 16 colors │ │',13,10,0 +_rs latin1 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt latin1 '║ └───────────────────────────────┴─┘',13,10,0 + +remark1 db "Die Standardwerte sind fur die meisten gewahlt, aber nicht fur jedermann.",0 +remark2 db "Wenn das System nicht bootet, das Option [b] deaktivieren versuchen. Wenn es",0 +remark3 db "nach dem Booten hangen bleibt, aktivieren Sie Option [c], deaktivieren [d]",0 +remark4 db "und machen Fotos.",0 +remarks dw remark1, remark2, remark3, remark4 +num_remarks = 4 \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/boot/bootru.inc b/kernel/branches/kolibri-lldw/boot/bootru.inc new file mode 100644 index 000000000..1affe59ed --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootru.inc @@ -0,0 +1,106 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;================================================================= +; +; BOOT DATA +; +;================================================================= + +$Revision$ + + +d80x25_bottom: + cp866 '║ KolibriOS НЕ ПРЕДОСТАВЛЯЕТ НИКАКИХ ГАРAНТИЙ. Подробнее смотрите в файле ║' + cp866 '║ COPYING.TXT. О найденных ошибках сообщайте на http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm cp866 " APM x.x ", 0 +novesa cp866 "Видеокарта: EGA/CGA",13,10,0 +s_vesa cp866 "Версия VESA: " + .ver db "?.?",13,10,0 + +gr_mode cp866 "Выберите видеорежим: ",13,10,0 + +ask_bd cp866 "Добавить диски, видимые через BIOS в режиме V86? [1-да, 2-нет]: ",0 + +if defined extended_primary_loader +bdev cp866 "Загрузить образ из [1-дискета; 2-kolibri.img из папки загрузки;",13,10 + cp866 "║ 3-не загружать]: ",0 +else +bdev cp866 "Загрузить образ из [1-дискета; 2-C:\kolibri.img (FAT32);",13,10 + cp866 "║ 3-использовать уже загруженный образ;",13,10 + cp866 "║ 4-создать чистый образ]: ",0 +end if + +prnotfnd cp866 "Ошибка - Видеорежим не найден.",0 + +not386 cp866 "Ошибка - Требуется процессор 386+.",0 +fatalsel cp866 "Ошибка - Выбранный видеорежим не поддерживается.",0 +pres_key cp866 "Нажимите любую клавишу, для перехода в выбор режимов.",0 +badsect cp866 13,10,"║ Ошибка - Дискета повреждена. Попробуйте другую.",0 +memmovefailed cp866 13,10,"║ Ошибка - Int 0x15 move failed.",0 +okt cp866 " ... OK" +linef cp866 13,10,0 +diskload cp866 "Загрузка дискеты: 00 %",8,8,8,8,0 +pros cp866 "00" +backspace2 cp866 8,8,0 +boot_dev db 0 +start_msg cp866 "Нажмите [abcde] для изменения настроек, [Enter] для продолжения загрузки",13,10,0 +time_msg cp866 " или подождите " +time_str cp866 " 5 секунд " + cp866 " до автоматического продолжения",13,10,0 +current_cfg_msg cp866 "Текущие настройки:",13,10,0 +curvideo_msg cp866 " [a] Видеорежим: ",0 + +mode0 cp866 "320x200, EGA/CGA 256 цветов",13,10,0 +mode9 cp866 "640x480, VGA 16 цветов",13,10,0 + +usebd_msg cp866 " [b] Добавить диски, видимые через BIOS:",0 +on_msg cp866 " вкл",13,10,0 +off_msg cp866 " выкл",13,10,0 + +debug_mode_msg cp866 " [c] Дублировать дебаг-вывод на экран монитора:",0 +ask_debug cp866 "Дублировать дебаг-вывод на экран монитора? [1-да, 2-нет]: ",0 + +launcher_msg cp866 " [d] Запустить программу LAUNCHER после загрузки ядра:",0 +ask_launcher cp866 "Запустить первую программу (LAUNCHER) после загрузки ядра? [1-да, 2-нет]: ",0 + +preboot_device_msg cp866 " [e] Образ дискеты: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,0 +pdm1 cp866 "настоящая дискета",13,10,0 +pdm2 cp866 "kolibri.img из папки загрузки",13,10,0 +pdm3 cp866 "не загружать образ рамдиска",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 cp866 "настоящая дискета",13,10,0 +pdm2 cp866 "C:\kolibri.img (FAT32)",13,10,0 +pdm3 cp866 "использовать уже загруженный образ",13,10,0 +pdm4 cp866 "создать чистый образ",13,10,0 +end if + +loading_msg cp866 "Идёт загрузка KolibriOS...",0 + +if ~ defined extended_primary_loader ; saving not supported in this case +save_quest cp866 "Запомнить текущие настройки? [y/n]: ",0 +loader_block_error cp866 "Ошибка в данных начального загрузчика, продолжение невозможно.",0 +end if + +_st cp866 '║ ┌───────────────────────────────┬─┐ ',13,10,0 +_r1 cp866 '║ │ 320x200 EGA/CGA 256 цветов │ │ ',13,10,0 +_r2 cp866 '║ │ 640x480 VGA 16 цветов │ │ ',13,10,0 +_rs cp866 '║ │ ????x????@?? SVGA VESA │ │ ',13,10,0 +_bt cp866 '║ └───────────────────────────────┴─┘ ',13,10,0 + +remark1 cp866 "Значения по умолчанию выбраны для удобства большинства, но не всех. Если у",0 +remark2 cp866 "Вас не грузится система, попробуйте отключить пункт [b]. Если она зависла",0 +remark3 cp866 "после запуска, включите пункт [c], отключите пункт [d] и сделайте фото лога.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 diff --git a/kernel/branches/kolibri-lldw/boot/bootsp.inc b/kernel/branches/kolibri-lldw/boot/bootsp.inc new file mode 100644 index 000000000..ae6480c51 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootsp.inc @@ -0,0 +1,108 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;====================================================================== +; +; BOOT DATA +; +;====================================================================== + +; Para modificar éste archivo es necesario abrirlo con codificación CP850 + +$Revision$ + + +d80x25_bottom: + cp850 '║ KolibriOS viene ABSOLUTAMENTE SIN GARANTíA. Lee el archivo COPYING por más ║' + cp850 '║ detalles. Por favor, informar de los errores en: http://board.kolibrios.org ║' + line_full_bottom +d80x25_bottom_num = 3 + +msg_apm cp850 " APM x.x ", 0 +novesa cp850 "Monitor: EGA/CGA",13,10,0 +s_vesa cp850 "Versión de VESA: " + .ver db "?.?",13,10,0 + +gr_mode cp850 "Selecciona un modo de video: ",13,10,0 + +ask_bd cp850 "¿Agregar discos visibles por el BIOS emulados en modo V86? [1-si, 2-no]: ",0 + +if defined extended_primary_loader +bdev cp850 "Cargar unidad ram desde [1-disquete; 2-kolibri.img]: ",0 +else +bdev cp850 "Cargar unidad ram desde [1-disquete; 2-C:\kolibri.img (FAT32);" + cp850 13,10,"║ " + cp850 "3-usar imagen precargada en el reinicio del núcleo;" + cp850 13,10,"║ " + cp850 "4-crear imagen vacía]: ",0 +end if + +prnotfnd cp850 "Fatal - Modo de video no encontrado.",0 + +not386 cp850 "Fatal - CPU 386+ requerido.",0 +fatalsel cp850 "Fatal - Modo de gráficos no soportado por hardware.",0 +pres_key cp850 "Presiona una tecla para seleccionar otro modo de video.",0 +badsect cp850 13,10,"║ Fatal - Sector mal. Reemplaze el disquete.",0 +memmovefailed cp850 13,10,"║ Fatal - Int 0x15 move failed.",0 +okt cp850 " ... BIEN" +linef cp850 13,10,0 +diskload cp850 "Cargando disquete: 00 %",8,8,8,8,0 +pros cp850 "00" +backspace2 cp850 8,8,0 +boot_dev db 0 ; 0=floppy, 1=hd +start_msg cp850 "Presiona [abcde] para cambiar la configuración, [Enter] para continuar",13,10,0 +time_msg cp850 " o espera " +time_str cp850 " 5 segundos" + cp850 " para que inicie automáticamente",13,10,0 +current_cfg_msg cp850 "Configuración actual:",13,10,0 +curvideo_msg cp850 " [a] Modo de video: ",0 + +mode0 cp850 "320x200, EGA/CGA 256 colores",13,10,0 +mode9 cp850 "640x480, VGA 16 colores",13,10,0 + +usebd_msg cp850 " [b] Agregar discos visibles por el BIOS:",0 +on_msg cp850 " activado",13,10,0 +off_msg cp850 " desactivado",13,10,0 + +debug_mode_msg cp850 " [c] Duplicar depurar salida a la pantalla:",0 +ask_debug cp850 "¿Duplicar depurar la salida a la pantalla? [1-si, 2-no]: ",0 + +launcher_msg cp850 " [d] Iniciar LAUNCHER después de cargar kernel:",0 +ask_launcher cp850 "¿Inicie la primera aplicación después de cargar el kernel? [1-si, 2-no]: ",0 + +preboot_device_msg cp850 " [e] Imagen de disquete: ",0 + +if defined extended_primary_loader +preboot_device_msgs dw 0,pdm1,pdm2,0 +pdm1 cp850 "disquete real",13,10,0 +pdm2 cp850 "C:\kolibri.img (FAT32)",13,10,0 +else +preboot_device_msgs dw 0,pdm1,pdm2,pdm3,pdm4,0 +pdm1 cp850 "disquete real",13,10,0 +pdm2 cp850 "C:\kolibri.img (FAT32)",13,10,0 +pdm3 cp850 "usar imagen ya cargada",13,10,0 +pdm4 cp850 "crear imagen vacía",13,10,0 +end if + +loading_msg cp850 "Cargando KolibriOS...",0 + +if ~ defined extended_primary_loader +save_quest cp850 "¿Recordar configuración actual? [s/n]: ",0 +loader_block_error cp850 "Bootloader inválido, no puedo continuar. Detenido.",0 +end if + +_st cp850 '║ ┌───────────────────────────────┬─┐',13,10,0 +_r1 cp850 '║ │ 320x200 EGA/CGA 256 colores │ │',13,10,0 +_r2 cp850 '║ │ 640x480 VGA 16 colores │ │',13,10,0 +_rs cp850 '║ │ ????x????@?? SVGA VESA │ │',13,10,0 +_bt cp850 '║ └───────────────────────────────┴─┘',13,10,0 + +remark1 cp850 "Los valores por defecto puede que no funcionen en algunas configuraciones.",0 +remark2 cp850 "Si el sistema no inicia, prueba deshabilitar la opción [b]. Si se bloquea",0 +remark3 cp850 "después de arrancar, habilite la opción [c], desactivar [d] y hacer fotos.",0 +remarks dw remark1, remark2, remark3 +num_remarks = 3 diff --git a/kernel/branches/kolibri-lldw/boot/bootstr.inc b/kernel/branches/kolibri-lldw/boot/bootstr.inc new file mode 100644 index 000000000..ba2d7fcf2 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootstr.inc @@ -0,0 +1,56 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; boot data: common strings (for all languages) +macro line_full_top { + db 201 + times 78 db 205 + db 187 +} +macro line_full_bottom { + db 200 + times 78 db 205 + db 188 +} +macro line_half { + db 186,' ' + times 76 db 0xc4 + db ' ',186 +} +macro line_space { + db 186 + times 78 db 32 + db 186 +} +d80x25_top: + line_full_top + cur_line_pos = 72 + ; this signature will be replaced with revision number (in kernel.asm) + store dword '****' at d80x25_top + cur_line_pos + +space_msg: + line_space +verstr: +; line_space +; version string + db 186,32 + repeat 78 + load a byte from version+%-1 + if a = 13 + break + end if + db a + end repeat + repeat 78 - ($-verstr) + db ' ' + end repeat + db 32,186 + line_half +d80x25_top_num = 4 diff --git a/kernel/branches/kolibri-lldw/boot/bootvesa.inc b/kernel/branches/kolibri-lldw/boot/bootvesa.inc new file mode 100644 index 000000000..39a60fe64 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/bootvesa.inc @@ -0,0 +1,801 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +struc VBE_VGAInfo { + .VESASignature dd ? ; char + .VESAVersion dw ? ; short + .OemStringPtr dd ? ; char * + .Capabilities dd ? ; ulong + .VideoModePtr dd ? ; ulong + .TotalMemory dw ? ; short + ; VBE 2.0+ + .OemSoftwareRev db ? ; short + .OemVendorNamePtr dw ? ; char * + .OemProductNamePtr dw ? ; char * + .OemProductRevPtr dw ? ; char * + .reserved rb 222 ; char + .OemData rb 256 ; char +} + +struc VBE_ModeInfo { + .ModeAttributes dw ? ; short + .WinAAttributes db ? ; char + .WinBAttributes db ? ; char + .WinGranularity dw ? ; short + .WinSize dw ? ; short + .WinASegment dw ? ; ushort + .WinBSegment dw ? ; ushort + .WinFuncPtr dd ? ; void * + .BytesPerScanLine dw ? ; short + .XRes dw ? ; short + .YRes dw ? ; short + .XCharSize db ? ; char + .YCharSize db ? ; char + .NumberOfPlanes db ? ; char + .BitsPerPixel db ? ; char + .NumberOfBanks db ? ; char + .MemoryModel db ? ; char + .BankSize db ? ; char + .NumberOfImagePages db ? ; char + .res1 db ? ; char + .RedMaskSize db ? ; char + .RedFieldPosition db ? ; char + .GreenMaskSize db ? ; char + .GreenFieldPosition db ? ; char + .BlueMaskSize db ? ; char + .BlueFieldPosition db ? ; char + .RsvedMaskSize db ? ; char + .RsvedFieldPosition db ? ; char + .DirectColorModeInfo db ? ; char ; MISSED IN THIS TUTORIAL!! SEE ABOVE + ; VBE 2.0+ + .PhysBasePtr dd ? ; ulong + .OffScreenMemOffset dd ? ; ulong + .OffScreenMemSize dw ? ; short + ; VBE 3.0+ + .LinbytesPerScanLine dw ? ; short + .BankNumberOfImagePages db ? ; char + .LinNumberOfImagePages db ? ; char + .LinRedMaskSize db ? ; char + .LinRedFieldPosition db ? ; char + .LingreenMaskSize db ? ; char + .LinGreenFieldPosition db ? ; char + .LinBlueMaskSize db ? ; char + .LinBlueFieldPosition db ? ; char + .LinRsvdMaskSize db ? ; char + .LinRsvdFieldPosition db ? ; char + .MaxPixelClock dd ? ; ulong + .res2 rb 190 ; char +} + +virtual at $A000 + vi VBE_VGAInfo + mi VBE_ModeInfo +modes_table: +end virtual +cursor_pos dw 0 ;временное хранение курсора. +cursor_pos_old dw 0 +home_cursor dw 0 ;current shows rows a table +end_cursor dw 0 ;end of position current shows rows a table +scroll_start dw 0 ;start position of scroll bar +scroll_end dw 0 ;end position of scroll bar +long_v_table = 9 ;long of visible video table +size_of_step = 10 +scroll_area_size = long_v_table - 2 +int2str: + dec bl + jz @f + xor edx, edx + div ecx + push edx + call int2str + pop eax + @@: + or al, 0x30 + mov [ds:di], al + inc di + ret + +int2strnz: + cmp eax, ecx + jb @f + xor edx, edx + div ecx + push edx + call int2strnz + pop eax + @@: + or al, 0x30 + mov [es:di], al + inc di + ret + +;------------------------------------------------------- +;Write message about incorrect v_mode and write message about jmp on swith v_mode +v_mode_error: + _setcursor 19,2 + mov si, fatalsel + call printplain + _setcursor 20,2 + mov si, pres_key + call printplain + xor eax, eax + int 16h + jmp cfgmanager.d +;------------------------------------------------------- +; + + + +;------------------------------------------------------- +print_vesa_info: + _setcursor 5,2 + + mov [es:vi.VESASignature], 'VBE2' + mov ax, 0x4F00 + mov di, vi ;0xa000 + int 0x10 + or ah, ah + jz @f + mov [es:vi.VESASignature], 'VESA' + mov ax, $4F00 + mov di, vi + int 0x10 + or ah, ah + jnz .exit + @@: + cmp [es:vi.VESASignature], 'VESA' + jne .exit + cmp [es:vi.VESAVersion], 0x0100 + jb .exit + jmp .vesaok2 + + .exit: + mov si, novesa + call printplain + ret + + .vesaok2: + mov ax, [es:vi.VESAVersion] + add ax, '00' + + mov [s_vesa.ver], ah + mov [s_vesa.ver+2], al + mov si, s_vesa + call printplain + + _setcursor 4,2 + mov si, word[es:vi.OemStringPtr] + mov di, si + + push ds + mov ds, word[es:vi.OemStringPtr+2] + call printplain + pop ds + + ret +;----------------------------------------------------------------------------- + +calc_vmodes_table: + pushad + +; push 0 +; pop es + + lfs si, [es:vi.VideoModePtr] + + mov bx, modes_table +;save no vesa mode of work 320x200, EGA/CGA 256 梥⮢ and 640x480, VGA 16 梥⮢ + mov word [es:bx], 640 + mov word [es:bx+2], 480 + mov word [es:bx+6], 0x13 + + mov word [es:bx+10], 640 + mov word [es:bx+12], 480 + mov word [es:bx+16], 0x12 + add bx, 20 + .next_mode: + mov cx, word [fs:si]; mode number + cmp cx, -1 + je .modes_ok.2 + + mov ax, 0x4F01 + mov di, mi + int 0x10 + + or ah, ah + jnz .modes_ok.2;vesa_info.exit + + test [es:mi.ModeAttributes], 00000001b ;videomode support ? + jz @f + test [es:mi.ModeAttributes], 00010000b ;picture ? + jz @f + test [es:mi.ModeAttributes], 10000000b ;LFB ? + jz @f + + cmp [es:mi.BitsPerPixel], 16 ;List only supported videomodes (16, 24 and 32 bpp) + jb @f + +; 16 bpp might actually be 15 bpp + cmp [es:mi.BitsPerPixel], 16 + jne .l0 + cmp [es:mi.GreenMaskSize], 5 + jne .l0 +; mov [es:mi.BitsPerPixel],15 + jmp @f ; 15 bpp isnt supported ATM + + +.l0: + cmp [es:mi.XRes], 640 + jb @f + cmp [es:mi.YRes], 480 + jb @f +; cmp [es:mi.BitsPerPixel],8 +; jb @f + + mov ax, [es:mi.XRes] + mov [es:bx+0], ax ; +0[2] : resolution X + mov ax, [es:mi.YRes] + mov [es:bx+2], ax ; +2[2] : resolution Y + mov ax, [es:mi.ModeAttributes] + mov [es:bx+4], ax ; +4[2] : attributes + + cmp [s_vesa.ver], '2' +; jb .lp1 + jb @f ; We do not use Vesa 1.2 mode is now + + or cx, 0x4000 ; use LFB +.lp1: + mov [es:bx+6], cx ; +6 : mode number + movzx ax, byte [es:mi.BitsPerPixel] + mov word [es:bx+8], ax ; +8 : bits per pixel + add bx, size_of_step ; size of record + + @@: + add si, 2 + jmp .next_mode + + .modes_ok.2: + + mov word[es:bx], -1 ;end video table + mov word[end_cursor], bx ;save end cursor position +;;;;;;;;;;;;;;;;;; +;Sort array +; mov si,modes_table +;.new_mode: +; mov ax,word [es:si] +; cmp ax,-1 +; je .exxit +; add ax,word [es:si+2] +; add ax,word [es:si+8] +; mov bp,si +;.again: +; add bp,12 +; mov bx,word [es:bp] +; cmp bx,-1 +; je .exit +; add bx,word [es:bp+2] +; add bx,word [es:bp+8] +; +; cmp ax,bx +; ja .loops +; jmp .again +;.loops: +; push dword [es:si] +; push dword [es:si+4] +; push dword [es:si+8] +; push dword [es:bp] +; push dword [es:bp+4] +; push dword [es:bp+8] +; +; pop dword [es:si+8] +; pop dword [es:si+4] +; pop dword [es:si] +; pop dword [es:bp+8] +; pop dword [es:bp+4] +; pop dword [es:bp] +; jmp .new_mode +; +;.exit: add si,12 +; jmp .new_mode +;.exxit: + popad + ret + +;----------------------------------------------------------------------------- + +draw_current_vmode: + push 0 + pop es + + mov si, word [cursor_pos] + + cmp word [es:si+6], 0x12 + je .no_vesa_0x12 + + cmp word [es:si+6], 0x13 + je .no_vesa_0x13 + +if defined extended_primary_loader + mov di, config_file_variables +else + mov di, loader_block_error +end if + movzx eax, word[es:si+0] + mov ecx, 10 + call int2strnz + mov byte[es:di], 'x' + inc di + movzx eax, word[es:si+2] + call int2strnz + mov byte[es:di], 'x' + inc di + movzx eax, word[es:si+8] + call int2strnz + mov dword[es:di], 0x00000d0a +if defined extended_primary_loader + mov si, config_file_variables +else + mov si, loader_block_error +end if + push ds + push es + pop ds + call printplain + pop ds + ret +.no_vesa_0x13: + mov si, mode0 + jmp .print +.no_vesa_0x12: + mov si, mode9 +.print: + call printplain + ret +;----------------------------------------------------------------------------- +check_first_parm: +if defined extended_primary_loader + mov cx, [number_vm] + jcxz .novbemode + mov si, modes_table +.findvbemode: + cmp [es:si+6], cx + jnz @f + cmp word [es:si+8], 32 + je .ok_found_mode + cmp word [es:si+8], 24 + je .ok_found_mode + cmp word [es:si+8], 16 + je .ok_found_mode +@@: + add si, size_of_step + cmp word [es:si], -1 + jnz .findvbemode +.novbemode: + mov ax, [x_save] + test ax, ax + jz .zerro + mov bx, [y_save] + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode +else + mov si, word [preboot_graph] + test si, si + jnz .no_zero ;if no zero +end if +.zerro: +; mov ax,modes_table +; mov word [cursor_pos],ax +; mov word [home_cursor],ax +; mov word [preboot_graph],ax +;SET default video of mode first probe will fined a move of work 1024x768@32 + mov cx, 32 + .find_mode: + mov ax, 1024 + mov bx, 768 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + mov ax, 800 + mov bx, 600 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + mov ax, 640 + mov bx, 480 + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + sub cx, 8 + jnz .find_mode + + mov si, modes_table +if ~ defined extended_primary_loader + jmp .ok_found_mode + + + +.no_zero: + mov bp, word [number_vm] + cmp bp, word [es:si+6] + jz .ok_found_mode + mov ax, word [x_save] + mov bx, word [y_save] + mov si, modes_table + call .loops + test ax, ax + jz .ok_found_mode + + mov si, modes_table +; cmp ax,modes_table +; jb .zerro ;check on correct if bellow +; cmp ax,word [end_cursor] +; ja .zerro ;check on correct if anymore +end if + +.ok_found_mode: + mov word [home_cursor], si +; mov word [cursor_pos],si + mov word [preboot_graph], si + mov ax, si + + mov ecx, long_v_table + +.loop: + add ax, size_of_step + cmp ax, word [end_cursor] + jae .next_step + loop .loop +.next_step: + sub ax, size_of_step*long_v_table + cmp ax, modes_table + jae @f + mov ax, modes_table +@@: + + mov word [home_cursor], ax + mov si, [preboot_graph] + mov word [cursor_pos], si + + push word [es:si] + pop word [x_save] + push word [es:si+2] + pop word [y_save] + push word [es:si+6] + pop word [number_vm] + + ret +;;;;;;;;;;;;;;;;;;;;;;;;;;; +.loops: + cmp ax, word [es:si] + jne .next + cmp bx, word [es:si+2] + jne .next + jcxz @f + cmp cx, word [es:si+8] + jne .next +@@: + xor ax, ax + ret +.next: + add si, size_of_step + cmp word [es:si], -1 + je .exit + jmp .loops +.exit: + or ax, -1 + ret + + +;----------------------------------------------------------------------------- + +;default_vmode: + +;----------------------------------------------------------------------------- +draw_vmodes_table: + _setcursor 9, 2 + mov si, gr_mode + call printplain + + mov si, _st + call printplain + + push word [cursor_pos] + pop ax + push word [home_cursor] + pop si + mov cx, si + + cmp ax, si + je .ok + jb .low + + + add cx, size_of_step*long_v_table + + cmp ax, cx + jb .ok + + sub cx, size_of_step*long_v_table + add cx, size_of_step + cmp cx, word[end_cursor] + jae .ok + add si, size_of_step + push si + pop word [home_cursor] + jmp .ok + + +.low: + sub cx, size_of_step + cmp cx, modes_table + jb .ok + push cx + push cx + pop word [home_cursor] + pop si + + +.ok: +; calculate scroll position + push si + mov ax, [end_cursor] + sub ax, modes_table + mov bx, size_of_step + cwd + div bx + mov si, ax ; si = size of list + mov ax, [home_cursor] + sub ax, modes_table + cwd + div bx + mov di, ax + mov ax, scroll_area_size*long_v_table + cwd + div si + test ax, ax + jnz @f + inc ax +@@: + cmp al, scroll_area_size + jb @f + mov al, scroll_area_size +@@: + mov cx, ax +; cx = scroll height +; calculate scroll pos + xor bx, bx ; initialize scroll pos + sub al, scroll_area_size+1 + neg al + sub si, long_v_table-1 + jbe @f + mul di + div si + mov bx, ax +@@: + inc bx + imul ax, bx, size_of_step + add ax, [home_cursor] + mov [scroll_start], ax + imul cx, size_of_step + add ax, cx + mov [scroll_end], ax + pop si + mov bp, long_v_table ;show rows +.@@_next_bit: +;clear cursor + mov ax, ' ' + mov word[ds:_r1+21], ax + mov word[ds:_r1+50], ax + + mov word[ds:_r2+21], ax + mov word[ds:_r2+45], ax + + mov word[ds:_rs+21], ax + mov word[ds:_rs+46], ax +; draw string + cmp word [es:si+6], 0x12 + je .show_0x12 + cmp word [es:si+6], 0x13 + je .show_0x13 + + movzx eax, word[es:si] + cmp ax, -1 + je .@@_end + mov di, _rs+23 + mov ecx, 10 + mov bl, 4 + call int2str + movzx eax, word[es:si+2] + inc di + mov bl, 4 + call int2str + + movzx eax, word[es:si+8] + inc di + mov bl, 2 + call int2str + + cmp si, word [cursor_pos] + jne .next +;draw cursor + mov word[ds:_rs+21], '>>' + mov word[ds:_rs+46], '<<' + + + +.next: + push si + mov si, _rs +.@@_sh: +; add to the string pseudographics for scrollbar + pop bx + push bx + mov byte [si+53], ' ' + cmp bx, [scroll_start] + jb @f + cmp bx, [scroll_end] + jae @f + mov byte [si+53], 0xDB ; filled bar +@@: + push bx + add bx, size_of_step + cmp bx, [end_cursor] + jnz @f + mov byte [si+53], 31 ; 'down arrow' symbol +@@: + sub bx, [home_cursor] + cmp bx, size_of_step*long_v_table + jnz @f + mov byte [si+53], 31 ; 'down arrow' symbol +@@: + pop bx + cmp bx, [home_cursor] + jnz @f + mov byte [si+53], 30 ; 'up arrow' symbol +@@: + call printplain + pop si + add si, size_of_step + + dec bp + jnz .@@_next_bit + +.@@_end: + mov si, _bt + call printplain + ret +.show_0x13: + push si + + cmp si, word [cursor_pos] + jne @f + mov word[ds:_r1+21], '>>' + mov word[ds:_r1+50], '<<' +@@: + mov si, _r1 + jmp .@@_sh +.show_0x12: + push si + cmp si, word [cursor_pos] + jne @f + + mov word[ds:_r2+21], '>>' + mov word[ds:_r2+45], '<<' +@@: + mov si, _r2 + jmp .@@_sh + +;----------------------------------------------------------------------------- +;Clear arrea of current video page (0xb800) +clear_vmodes_table: + pusha + ; draw frames + push es + push 0xb800 + pop es + mov di, 1444 + xor ax, ax + mov ah, 1*16+15 + mov cx, 77 + mov bp, 12 +.loop_start: + rep stosw + mov cx, 77 + add di, 6 + dec bp + jns .loop_start + pop es + popa + ret + +;----------------------------------------------------------------------------- + +set_vmode: + push 0 ;0;x1000 + pop es + + mov si, word [preboot_graph] ;[preboot_graph] + mov cx, word [es:si+6] ; number of mode + + + mov ax, word [es:si+0] ; resolution X + mov bx, word [es:si+2] ; resolution Y + + + mov word [es:BOOT_LO.x_res], ax ; resolution X + mov word [es:BOOT_LO.y_res], bx ; resolution Y + mov word [es:BOOT_LO.vesa_mode], cx ; number of mode + + cmp cx, 0x12 + je .mode0x12_0x13 + cmp cx, 0x13 + je .mode0x12_0x13 + + +; cmp byte [s_vesa.ver], '2' +; jb .vesa12 + +; VESA 2 and Vesa 3 + + mov ax, 0x4f01 + and cx, 0xfff + mov di, mi;0xa000 + int 0x10 + ; LFB + mov eax, [es:mi.PhysBasePtr];di+0x28] + mov [es:BOOT_LO.lfb], eax + ; ---- vbe voodoo + BytesPerLine = 0x10 + mov ax, [es:di+BytesPerLine] + mov [es:BOOT_LO.pitch], ax + ; BPP + cmp [es:mi.BitsPerPixel], 16 + jne .l0 + cmp [es:mi.GreenMaskSize], 5 + jne .l0 + mov [es:mi.BitsPerPixel], 15 +.l0: + mov al, byte [es:di+0x19] + mov [es:BOOT_LO.bpp], al + jmp .exit + +.mode0x12_0x13: + mov byte [es:BOOT_LO.bpp], 32 + or dword [es:BOOT_LO.lfb], 0xFFFFFFFF; 0x800000 + + +; VESA 1.2 PM BANK SWITCH ADDRESS + +;.vesa12: +; mov ax, 0x4f0A +; xor bx, bx +; int 0x10 +; xor eax, eax +; xor ebx, ebx +; mov ax, es +; shl eax, 4 +; mov bx, di +; add eax, ebx +; movzx ebx, word[es:di] +; add eax, ebx +; push 0x0000 +; pop es +; mov [es:BOOT_LO.bank_sw], eax + .exit: + ret + +;============================================================================= +;============================================================================= +;============================================================================= + diff --git a/kernel/branches/kolibri-lldw/boot/et.inc b/kernel/branches/kolibri-lldw/boot/et.inc new file mode 100644 index 000000000..585d3368e --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/et.inc @@ -0,0 +1,16 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Full ASCII code font +; only õ,ä,ü added +; Kaitz +ET_FNT: + fontfile file "ETFONT.FNT" + diff --git a/kernel/branches/kolibri-lldw/boot/parsers.inc b/kernel/branches/kolibri-lldw/boot/parsers.inc new file mode 100644 index 000000000..e4cf0a98a --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/parsers.inc @@ -0,0 +1,172 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2011-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; All parsers are called with ds:si -> value of the variable, +; possibly with spaces before, and dx = limit of config file. + +; Three subroutines parse_char, parse_number and parse_bool set CF +; if something has failed, otherwise return the value in al/ax. + +parse_timeout: +; timeout is a number not greater than 9 + call parse_number + jc .nothing + cmp ax, 9 + jbe @f + mov ax, 9 +@@: + imul ax, 18 + mov [es:preboot_timeout], ax +.nothing: + ret + +parse_resolution: +; resolution is *, 'x' can be used instead of '*' +; parse width + call parse_number + jc .nothing +; save width + xchg ax, bx +; test for 'x' or '*' + call parse_char + cmp al, 'x' + jz @f + cmp al, '*' + jnz .nothing +@@: +; parse height + call parse_number + jc .nothing +; write width and height + mov [es:x_save], bx + mov [es:y_save], ax +.nothing: + ret + +parse_vbemode: +; vbemode is a number + call parse_number + jc .nothing + mov [es:number_vm], ax +.nothing: + ret + +parse_biosdisks: +; using biosdisks is a boolean setting + call parse_bool + jc .nothing +; convert 0 to 2, 1 to 1 + inc ax + xor al, 3 + mov [es:preboot_biosdisk], al +.nothing: + ret + +parse_imgfrom: +; boot device (1-floppy 2-kolibri.img using primary loader, 3-don't use ramdisk) + call parse_number + jc .nothing + cmp al, 1 + jb .nothing + cmp al, 3 + ja .nothing + mov [es:preboot_device], al +.nothing: + ret + +parse_syspath: +; everything except spaces + mov bx, preboot_syspath +.next_char: + call parse_char + jc .done + mov [es:bx], al + inc bx + jmp .next_char +.done: + mov byte[es:bx], 0 ; terminator + ret + +parse_char: +; skip spaces and return the next character or CF if EOF. + cmp si, dx + jae .eof + lodsb + cmp al, ' ' + jbe parse_char + ret +.eof: + stc + ret + +parse_number: +; initialize high part of ax to zero + xor ax, ax +; skip spaces + call parse_char + jc .bad +; al should be a digit + sub al, '0' + cmp al, 9 + ja .bad +; accumulate the value in cx + xchg cx, ax +@@: + cmp si, dx + jae .eof + lodsb + sub al, '0' + cmp al, 9 + ja .end + imul cx, 10 + add cx, ax + jmp @b +; if the end is caused by non-digit, unwind the last character +.end: + dec si +.eof: + xchg cx, ax + clc + ret +.bad: + stc + ret + +parse_bool: +; skip spaces + call parse_char + jc .bad +; Boolean false can be represented as 0=no=off, +; boolean true can be represented as 1=yes=on. + cmp al, '0' + jz .false + cmp al, '1' + jz .true + mov ah, al + cmp si, dx + jae .bad + lodsb + cmp ax, 'n'*256 + 'o' + jz .false + cmp ax, 'o'*256 + 'f' + jz .false + cmp ax, 'y'*256 + 'e' + jz .true + cmp ax, 'o'*256 + 'n' + jz .true +.bad: + stc + ret +.true: + xor ax, ax + inc ax + ret +.false: + xor ax, ax + ret diff --git a/kernel/branches/kolibri-lldw/boot/preboot.inc b/kernel/branches/kolibri-lldw/boot/preboot.inc new file mode 100644 index 000000000..1492e181f --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/preboot.inc @@ -0,0 +1,46 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +display_modechg db 0 ; display mode change for text, yes/no (0 or 2) + ; + ; !! Important note !! + ; + ; Must be set to 2, to avoid two screenmode + ; changes within a very short period of time. + +display_atboot db 0 ; show boot screen messages ( 2-no ) + +preboot_graph dw 0 ; graph mode +x_save dw 0 ; x +y_save dw 0 ; y +number_vm dw 0 ; +;pixel_save dw 0 ; per to pixel +preboot_gprobe db 0 ; probe vesa3 videomodes (1-no, 2-yes) +preboot_debug db 0 ; load kernel in debug mode? (1-yes, 2-no) +preboot_launcher db 0 ; start launcher after kernel is loaded? (1-yes, 2-no) +preboot_dma db 0 ; use DMA for access to HDD (1-always, 2-only for read, 3-never) +preboot_device db 0 ; device to load ramdisk from + ; 1-floppy 2-harddisk 3-kernel restart + ; 4-format ram disk 5-don't use ramdisk + ; !!!! 0 - autodetect !!!! +preboot_biosdisk db 0 ; use V86 to access disks through BIOS (1-yes, 2-no) +preboot_syspath db '/RD/1',0 ; path to /sys dir + rb 20-($-preboot_syspath) +if defined extended_primary_loader +; timeout in 1/18th of second for config settings screen +preboot_timeout dw PREBOOT_TIMEOUT*18 +end if + + if $>0x200 +ERROR: + prebooting parameters must fit in first sector!!! + end if +hdsysimage db 'KOLIBRI.IMG',0 ; load from +image_save db 'KOLIBRI.IMG',0 ; save to diff --git a/kernel/branches/kolibri-lldw/boot/rdload.inc b/kernel/branches/kolibri-lldw/boot/rdload.inc new file mode 100644 index 000000000..fd3650efb --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/rdload.inc @@ -0,0 +1,128 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +read_ramdisk: +; READ RAMDISK IMAGE FROM HD (only for IDE0, IDE1, IDE2, IDE3) + + cmp byte [BOOT.rd_load_from], RD_LOAD_FROM_HD + jne no_sys_on_hd.1 + + xor ebp, ebp +.hd_loop: + lea eax, [ebp+'0'] + mov [read_image_fsinfo.name_digit], al + movzx eax, byte [DRIVE_DATA+2+ebp] + test eax, eax + jz .next_hd + push eax + mov esi, 1 +.partition_loop: + mov eax, esi + push -'0' +@@: + xor edx, edx + div [_10] + push edx + test eax, eax + jnz @b + mov edi, read_image_fsinfo.partition +@@: + pop eax + add al, '0' + stosb + jnz @b + mov byte [edi-1], '/' + push esi edi + mov esi, bootpath1 + mov ecx, bootpath1.len + rep movsb + call read_image + test eax, eax + jz .yes + cmp eax, 6 + jz .yes + pop edi + push edi + mov esi, bootpath2 + mov ecx, bootpath2.len + rep movsb + call read_image + test eax, eax + jz .yes + cmp eax, 6 + jz .yes + pop edi esi + inc esi + cmp esi, [esp] + jbe .partition_loop + pop eax +.next_hd: + inc ebp + cmp ebp, 4 + jb .hd_loop + jmp no_sys_on_hd +.yes: + DEBUGF 1, "K : RD found: %s\n", read_image_fsinfo.name + pop edi esi eax + + call register_ramdisk + jmp yes_sys_on_hd +;----------------------------------------------------------------------------- +iglobal +align 4 +read_image_fsinfo: + dd 0 ; function: read + dq 0 ; offset: zero + dd 1474560 ; size + dd RAMDISK ; buffer +.name db '/hd' +.name_digit db '0' + db '/' +.partition: + rb 64 ; should be enough for '255/KOLIBRI/KOLIBRI.IMG' + +bootpath1 db 'KOLIBRI.IMG',0 +.len = $ - bootpath1 +bootpath2 db 'KOLIBRI/KOLIBRI.IMG',0 +.len = $ - bootpath2 +endg + +read_image: + mov ebx, read_image_fsinfo + pushad + call file_system_lfn_protected + popad + ret + +no_sys_on_hd: + DEBUGF 1, "K : RD not found\n" +.1: + ; test_to_format_ram_disk (need if not using ram disk) + cmp byte [BOOT.rd_load_from], RD_LOAD_FROM_FORMAT + jne not_format_ram_disk + ; format_ram_disk + mov edi, RAMDISK + mov ecx, 0x1080 + xor eax, eax +@@: + stosd + loop @b + + mov ecx, 0x58F7F + mov eax, 0xF6F6F6F6 +@@: + stosd + loop @b + + mov [RAMDISK+0x200], dword 0xFFFFF0 ; fat table + mov [RAMDISK+0x4200], dword 0xFFFFF0 + +not_format_ram_disk: +yes_sys_on_hd: diff --git a/kernel/branches/kolibri-lldw/boot/ru.inc b/kernel/branches/kolibri-lldw/boot/ru.inc new file mode 100644 index 000000000..df4089c13 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/ru.inc @@ -0,0 +1,102 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Generated by RUFNT.EXE +; By BadBugsKiller (C) +; Modifyed by BadBugsKiller 12.01.2004 17:45 +; Шрифт уменьшен в размере и теперь состоит из 2-ух частей, +; содержащих только символы русского алфавита. +; символы в кодировке ASCII (ДОС'овская), кодовая страница 866. +RU_FNT1: + db 0x00, 0x00, 0x1E, 0x36, 0x66, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0x62, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0x66, 0x62, 0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xFF, 0xC3, 0x81, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xDB, 0xDB, 0x5A, 0x5A, 0x7E, 0x7E, 0x5A, 0xDB, 0xDB, 0xDB, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x06, 0x3C, 0x06, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xCE, 0xDE, 0xF6, 0xE6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xE6, 0x66, 0x6C, 0x6C, 0x78, 0x78, 0x6C, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x1F, 0x36, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0xCF, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + + db 0x00, 0x00, 0xFC, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC0, 0xC0, 0xC0, 0xC0, 0xC2, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xFF, 0xDB, 0x99, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7E, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0x6C, 0x7C, 0x38, 0x38, 0x7C, 0x6C, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0xF8, 0xF0, 0xB0, 0x30, 0x3E, 0x33, 0x33, 0x33, 0x33, 0x7E, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xC3, 0xC3, 0xC3, 0xC3, 0xF3, 0xDB, 0xDB, 0xDB, 0xDB, 0xF3, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xF0, 0x60, 0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x26, 0x3E, 0x26, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xCE, 0xDB, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xDB, 0xDB, 0xCE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x3F, 0x66, 0x66, 0x66, 0x3E, 0x3E, 0x66, 0x66, 0x66, 0xE7, 0x00, 0x00, 0x00, 0x00 + + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x78, 0x0C, 0x7C, 0xCC, 0xCC, 0xCC, 0x76, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x02, 0x06, 0x7C, 0xC0, 0xC0, 0xFC, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFC, 0x66, 0x66, 0x7C, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x62, 0x62, 0x60, 0x60, 0x60, 0xF0, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0xFF, 0xC3, 0xC3, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xFE, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0x54, 0x7C, 0x54, 0xD6, 0xD6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x3C, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x38, 0xC6, 0xC6, 0xCE, 0xD6, 0xE6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xE6, 0x6C, 0x78, 0x78, 0x6C, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x36, 0x66, 0x66, 0x66, 0x66, 0xE6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xEE, 0xFE, 0xFE, 0xD6, 0xD6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xFE, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x00, 0x00, 0x00, 0x00 + +RU_FNT2: + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xDC, 0x66, 0x66, 0x66, 0x66, 0x66, 0x7C, 0x60, 0x60, 0xF0, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x5A, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00 + db 0x00, 0x00, 0x00, 0x3C, 0x18, 0x7E, 0xDB, 0xDB, 0xDB, 0xDB, 0xDB, 0x7E, 0x18, 0x18, 0x3C, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0x6C, 0x38, 0x38, 0x38, 0x6C, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0xFF, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xD6, 0xFE, 0x03, 0x03, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xB0, 0xB0, 0x3E, 0x33, 0x33, 0x7E, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xC6, 0xC6, 0xC6, 0xF6, 0xDE, 0xDE, 0xF6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xF0, 0x60, 0x60, 0x7C, 0x66, 0x66, 0xFC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0x06, 0x3E, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0xCE, 0xDB, 0xDB, 0xFB, 0xDB, 0xDB, 0xCE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7F, 0xC6, 0xC6, 0x7E, 0x36, 0x66, 0xE7, 0x00, 0x00, 0x00, 0x00 + + db 0x6C, 0x00, 0xFE, 0x66, 0x62, 0x68, 0x78, 0x68, 0x60, 0x62, 0x66, 0xFE, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x00, 0x7C, 0xC6, 0xC6, 0xFC, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xC8, 0xF8, 0xC8, 0xC0, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x7C, 0xC6, 0xC0, 0xF8, 0xC0, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x66, 0x00, 0x3C, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00, 0x00, 0x00, 0x00 + db 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x6C, 0x38, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7E, 0x06, 0x06, 0xC6, 0x7C, 0x00 + db 0x00, 0x38, 0x6C, 0x6C, 0x38, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x0E, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0x0C, 0xEC, 0x6C, 0x3C, 0x1C, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0xCF, 0xCD, 0xEF, 0xEC, 0xFF, 0xDC, 0xDC, 0xCC, 0xCC, 0xCC, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0xC6, 0x7C, 0xC6, 0xC6, 0xC6, 0xC6, 0xC6, 0x7C, 0xC6, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x7E, 0x00, 0x00, 0x00, 0x00, 0x00 + db 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 diff --git a/kernel/branches/kolibri-lldw/boot/shutdown.inc b/kernel/branches/kolibri-lldw/boot/shutdown.inc new file mode 100644 index 000000000..ff5388db1 --- /dev/null +++ b/kernel/branches/kolibri-lldw/boot/shutdown.inc @@ -0,0 +1,408 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2016. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Shutdown for Menuet ;; +;; ;; +;; Distributed under General Public License ;; +;; See file COPYING for details. ;; +;; Copyright 2003 Ville Turjanmaa ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +align 4 +system_shutdown: ; shut down the system + + cmp [BOOT.shutdown_type], SYSTEM_SHUTDOWN + jb @F + cmp [BOOT.shutdown_type], SYSTEM_RESTART + jbe .valid +@@: + ret +.valid: + call stop_all_services + +yes_shutdown_param: +; Shutdown other CPUs, if initialized + cmp [ap_initialized], 0 + jz .no_shutdown_cpus + mov edi, [LAPIC_BASE] + add edi, 300h + mov esi, smpt+4 + mov ebx, [cpu_count] + dec ebx +.shutdown_cpus_loop: + lodsd + push esi + xor esi, esi + inc esi + shl eax, 24 + mov [edi+10h], eax +; assert INIT IPI + mov dword [edi], 0C500h + call delay_ms +@@: + test dword [edi], 1000h + jnz @b +; deassert INIT IPI + mov dword [edi], 8500h + call delay_ms +@@: + test dword [edi], 1000h + jnz @b +; don't send STARTUP IPI: let other CPUs be in wait-for-startup state + pop esi + dec ebx + jnz .shutdown_cpus_loop +.no_shutdown_cpus: + + cli + call IRQ_mask_all + + movzx eax, [BOOT.shutdown_type] + cmp al, SYSTEM_RESTART + jne @F + +; load kernel.mnt to _CLEAN_ZONE + mov ebx, kernel_file_load + pushad + call file_system_lfn + popad +@@: + mov esi, OS_BASE+restart_code_start ; move kernel re-starter to 0x5000:0 + mov edi, OS_BASE+0x50000 + mov ecx, (restart_code_end - restart_code_start)/4 + rep movsd + + cmp [BOOT.shutdown_type], SYSTEM_SHUTDOWN + jne not_power_off + +; system_power_off + + mov ebx, [acpi_fadt_base] + test ebx, ebx + jz no_acpi + cmp [ebx+ACPI_TABLE.Signature], 'FACP' + jne no_acpi + mov esi, [acpi_ssdt_base] ; first SSDT is DSDT + test esi, esi + jz no_acpi + cmp [esi+ACPI_TABLE.Signature], 'DSDT' + jne no_acpi + mov eax, [esi+ACPI_TABLE.Length] + sub eax, 36+4 + jbe no_acpi + add esi, 36 +.scan_dsdt: + cmp dword [esi], '_S5_' + jnz .scan_dsdt_cont + cmp byte [esi+4], 12h ; DefPackage opcode + jnz .scan_dsdt_cont + mov dl, [esi+6] + cmp dl, 4 ; _S5_ package must contain 4 bytes + ; ...in theory; in practice, VirtualBox has 2 bytes + ja .scan_dsdt_cont + cmp dl, 1 + jb .scan_dsdt_cont + lea esi, [esi+7] + xor ecx, ecx + cmp byte [esi], 0 ; 0 means zero byte, 0Ah xx means byte xx + jz @f + cmp byte [esi], 0xA + jnz no_acpi + inc esi + mov cl, [esi] +@@: + inc esi + cmp dl, 2 + jb @f + cmp byte [esi], 0 + jz @f + cmp byte [esi], 0xA + jnz no_acpi + inc esi + mov ch, [esi] +@@: + jmp do_acpi_power_off +.scan_dsdt_cont: + inc esi + dec eax + jnz .scan_dsdt + jmp no_acpi +do_acpi_power_off: + mov edx, [ebx+ACPI_FADT.SMI_CMD] + test edx, edx + jz .nosmi + mov al, [ebx+ACPI_FADT.ACPI_ENABLE] + out dx, al + mov edx, [ebx+ACPI_FADT.PM1a_CNT_BLK] +@@: + in ax, dx + test al, 1 + jz @b +.nosmi: + and cx, 0x0707 + shl cx, 2 + or cx, 0x2020 + mov edx, [ebx+ACPI_FADT.PM1a_CNT_BLK] + in ax, dx + and ax, 203h + or ah, cl + out dx, ax + mov edx, [ebx+ACPI_FADT.PM1b_CNT_BLK] + test edx, edx + jz @f + in ax, dx + and ax, 203h + or ah, ch + out dx, ax +@@: + jmp no_acpi + +not_power_off: + cmp [BOOT.shutdown_type], SYSTEM_REBOOT + jnz not_reboot + ; try to reboot via ACPI fixed features + mov ebx, [acpi_fadt_base] + test ebx, ebx + jz no_acpi + cmp [ebx+ACPI_TABLE.Signature], 'FACP' + jne no_acpi + cmp [ebx+ACPI_FADT.Length], ACPI_FADT.RESET_VALUE + jbe no_acpi + test [ebx+ACPI_FADT.Flags], 1 SHL 10 ; reset_reg_supported + jz no_acpi + cmp [ebx+ACPI_FADT.RESET_REG.ASID], ASID.SYSTEM_IO + jnz no_acpi + cmp [ebx+ACPI_FADT.RESET_REG.BitWidth], 8 + jnz no_acpi + cmp [ebx+ACPI_FADT.RESET_REG.BitOffset], 0 + jnz no_acpi + cmp [ebx+ACPI_FADT.RESET_REG.AccessSize], ACCESS_SIZE.BYTE + ja no_acpi + cmp [ebx+ACPI_FADT.RESET_REG.Address.hi], 0 + jnz no_acpi + ; 'enable' ACPI + mov edx, [ebx+ACPI_FADT.SMI_CMD] + test edx, edx + jz .nosmi + mov al, [ebx+ACPI_FADT.ACPI_ENABLE] + out dx, al + mov edx, [ebx+ACPI_FADT.PM1a_CNT_BLK] +@@: + in ax, dx + test al, 1 + jz @b +.nosmi: + + mov edx, [ebx+ACPI_FADT.RESET_REG.Address.lo] + movzx eax, [ebx+ACPI_FADT.RESET_VALUE] + out dx, al + jmp no_acpi + +not_reboot: +no_acpi: + call create_trampoline_pgmap + mov cr3, eax + jmp @F +org $-OS_BASE +@@: + +;disable paging + + mov eax, cr0 + and eax, 0x7FFFFFFF + mov cr0, eax + mov eax, cr3 + mov cr3, eax + + jmp 0x50000 + +align 4 +restart_code_start: +org 0x50000 + + cmp [BOOT_LO.shutdown_type], SYSTEM_RESTART + jne @F + + mov esi, _CLEAN_ZONE-OS_BASE + mov edi, 0x10000 + mov ecx, 0x31000/4 + cld + rep movsd +@@: + + xor ebx, ebx + xor edx, edx + xor ecx, ecx + xor esi, esi + xor edi, edi + xor ebp, ebp + lidt [.idt] + lgdt [.gdt] + jmp 8:@f +align 8 +.gdt: +; selector 0 - not used + dw 23 + dd .gdt + dw 0 +; selector 8 - code from 5000:0000 to 1000:FFFF + dw 0FFFFh + dw 0 + db 5 + db 10011011b + db 00000000b + db 0 +; selector 10h - data from 1000:0000 to 1000:FFFF + dw 0FFFFh + dw 0 + db 1 + db 10010011b + db 00000000b + db 0 +.idt: + dw 256*4 + dd 0 +org $ - 0x50000 +use16 +@@: + mov ax, 10h + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + mov eax, cr0 + and eax, not 80000001h + mov cr0, eax + jmp 0x5000:.real_mode + +align 4 +.real_mode: + +; setup stack + + mov ax, (TMP_STACK_TOP and 0xF0000) shr 4 + mov ss, ax + mov esp, TMP_STACK_TOP and 0xFFFF + +;remap IRQs + mov al, 0x11 + out 0x20, al + out 0xA0, al + + mov al, 0x08 + out 0x21, al + mov al, 0x70 + out 0xA1, al + + mov al, 0x04 + out 0x21, al + mov al, 0x02 + out 0xA1, al + + mov al, 0x01 + out 0x21, al + out 0xA1, al + + mov al, 0xB8 + out 0x21, al + mov al, 0xBD + out 0xA1, al + + mov al, 00110100b + out 43h, al + mov al, 0xFF + out 40h, al + out 40h, al + + xor ax, ax + mov ds, ax + mov al, [BOOT_LO.shutdown_type] + cmp al, SYSTEM_RESTART + je .restart + + cmp al, SYSTEM_SHUTDOWN + je .APM_PowerOff + + mov word[0x0472], 0x1234 + jmp 0xF000:0xFFF0 + +.APM_PowerOff: + mov ax, 5304h + xor bx, bx + int 15h +;!!!!!!!!!!!!!!!!!!!!!!!! + mov ax, 0x5300 + xor bx, bx + int 0x15 + push ax + + mov ax, 0x5301 + xor bx, bx + int 0x15 + + mov ax, 0x5308 + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x530E + xor bx, bx + pop cx + int 0x15 + + mov ax, 0x530D + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x530F + mov bx, 1 + mov cx, bx + int 0x15 + + mov ax, 0x5307 + mov bx, 1 + mov cx, 3 + int 0x15 +;!!!!!!!!!!!!!!!!!!!!!!!! + jmp $ + +.restart: + +; (hint by Black_mirror) +; We must read data from keyboard port, +; because there may be situation when previous keyboard interrupt is lost +; (due to return to real mode and IRQ reprogramming) +; and next interrupt will not be generated (as keyboard waits for handling) + + mov cx, 16 +@@: + in al, 0x64 + test al, 1 + jz @F + in al, 0x60 + loop @B +@@: + +; bootloader interface + push 0x1000 + pop ds + push 0 + pop es + mov si, [es:BOOT_LO.kernel_restart] + mov ax, 'KL' + jmp 0x1000:0000 + +align 4 +org restart_code_start + $ +restart_code_end: + +org $+OS_BASE +use32 diff --git a/kernel/branches/kolibri-lldw/bootbios.asm b/kernel/branches/kolibri-lldw/bootbios.asm new file mode 100644 index 000000000..7039a6769 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootbios.asm @@ -0,0 +1,124 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; 16 BIT ENTRY FROM BOOTSECTOR ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +include 'macros.inc' +include 'struct.inc' +include 'lang.inc' +include 'encoding.inc' +include 'const.inc' + +os_code = code_l - tmp_gdt +PREBOOT_TIMEOUT = 5 ; seconds + +use16 + org 0x0 + jmp start_of_code + +if lang eq sp +include "kernelsp.inc" ; spanish kernel messages +else if lang eq et +version db 'Kolibri OS versioon 0.7.7.0+ ',13,10,13,10,0 +else +version db 'Kolibri OS version 0.7.7.0+ ',13,10,13,10,0 +end if + +include "boot/bootstr.inc" ; language-independent boot messages +include "boot/preboot.inc" + +if lang eq ge +include "boot/bootge.inc" ; german system boot messages +else if lang eq sp +include "boot/bootsp.inc" ; spanish system boot messages +else if lang eq ru +include "boot/bootru.inc" ; russian system boot messages +include "boot/ru.inc" ; Russian font +else if lang eq et +include "boot/bootet.inc" ; estonian system boot messages +include "boot/et.inc" ; Estonian font +else +include "boot/booten.inc" ; english system boot messages +end if + +include "boot/bootcode.inc" ; 16 bit system boot code +include "bus/pci/pci16.inc" +include "detect/biosdisk.inc" + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SWITCH TO 32 BIT PROTECTED MODE ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +; CR0 Flags - Protected mode and Paging + + mov ecx, CR0_PE+CR0_AM + +; Enabling 32 bit protected mode + + sidt [cs:old_ints_h] + + cli ; disable all irqs + cld + mov al, 255 ; mask all irqs + out 0xa1, al + out 0x21, al + l.5: + in al, 0x64 ; Enable A20 + test al, 2 + jnz l.5 + mov al, 0xD1 + out 0x64, al + l.6: + in al, 0x64 + test al, 2 + jnz l.6 + mov al, 0xDF + out 0x60, al + l.7: + in al, 0x64 + test al, 2 + jnz l.7 + mov al, 0xFF + out 0x64, al + + lgdt [cs:tmp_gdt] ; Load GDT + mov eax, cr0 ; protected mode + or eax, ecx + and eax, 10011111b *65536*256 + 0xffffff ; caching enabled + mov cr0, eax + jmp pword os_code:B32 ; jmp to enable 32 bit mode + +align 8 +tmp_gdt: + + dw 23 + dd tmp_gdt+0x10000 + dw 0 +code_l: + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10011010b + db 0x00 + + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10010010b + db 0x00 + +include "data16.inc" + +if ~ lang eq sp +diff16 "end of bootcode",0,$+0x10000 +end if + +use32 +org $+0x10000 + +align 4 +B32: diff --git a/kernel/branches/kolibri-lldw/bootloader/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/Tupfile.lua new file mode 100644 index 000000000..087b72253 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("echo lang fix " .. ((tup.getconfig("LANG") == "") and "en" or tup.getconfig("LANG")) .. " > %o", {"lang.inc"}) +tup.rule({"boot_fat12.asm", extra_inputs = {"lang.inc"}}, "fasm %f %o", "boot_fat12.bin") diff --git a/kernel/branches/kolibri-lldw/bootloader/boot_fat12.asm b/kernel/branches/kolibri-lldw/bootloader/boot_fat12.asm new file mode 100644 index 000000000..6ad4ef1d7 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/boot_fat12.asm @@ -0,0 +1,304 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; FAT12 boot sector for Kolibri OS +; +; Copyright (C) Alex Nogueira Teixeira +; Copyright (C) Diamond +; Copyright (C) Dmitry Kartashov aka shurf +; +; Distributed under GPL, see file COPYING for details +; +; Version 1.0 + +include "lang.inc" + +lf = 0ah +cr = 0dh + +pos_read_tmp = 0700h ;position for temporary read +boot_program = 07c00h ;position for boot code +seg_read_kernel = 01000h ;segment to kernel read + + jmp start_program + nop + +; Boot Sector and BPB Structure +include 'floppy1440.inc' +;include 'floppy2880.inc' +;include 'floppy1680.inc' +;include 'floppy1743.inc' + +start_program: +; + cld ;clear direction flag for Phoenix BIOS, see next "lodsb" + xor ax, ax + cli + mov ss, ax + mov sp, boot_program + sti +; <\Efremenkov S.V.> + push ss + pop ds + + ; print loading string + mov si, loading+boot_program +loop_loading: + lodsb + or al, al + jz read_root_directory + mov ah, 0eh + mov bx, 7 + int 10h + jmp loop_loading + +read_root_directory: + push ss + pop es + + ; calculate some disk parameters + ; - beginning sector of RootDir + mov ax, word [BPB_FATSz16+boot_program] + xor cx, cx + mov cl, byte [BPB_NumFATs+boot_program] + mul cx + add ax, word [BPB_RsvdSecCnt+boot_program] + mov word [FirstRootDirSecNum+boot_program], ax ; 19 + mov si, ax + + ; - count of sectors in RootDir + mov bx, word [BPB_BytsPerSec+boot_program] + mov cl, 5 ; divide ax by 32 + shr bx, cl ; bx = directory entries per sector + mov ax, word [BPB_RootEntCnt+boot_program] + xor dx, dx + div bx + mov word [RootDirSecs+boot_program], ax ; 14 + + ; - data start + add si, ax ; add beginning sector of RootDir and count sectors in RootDir + mov word [data_start+boot_program], si ; 33 + ; reading root directory + ; al=count root dir sectrors !!!! TODO: al, max 255 sectors !!!! + mov ah, 2 ; read + push ax + + mov ax, word [FirstRootDirSecNum+boot_program] + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) + pop ax + mov bx, pos_read_tmp ; es:bx read buffer + call read_sector + + mov si, bx ; read buffer address: es:si + mov ax, [RootDirSecs+boot_program] + mul word [BPB_BytsPerSec+boot_program] + add ax, si ; AX = end of root dir. in buffer pos_read_tmp + + ; find kernel file in root directory +loop_find_dir_entry: + push si + mov cx, 11 + mov di, kernel_name+boot_program + rep cmpsb ; compare es:si and es:di, cx bytes long + pop si + je found_kernel_file + add si, 32 ; next dir. entry + cmp si, ax ; end of directory + jb loop_find_dir_entry + +file_error_message: + mov si, error_message+boot_program + +loop_error_message: + lodsb + or al, al + jz freeze_pc + mov ah, 0eh + mov bx, 7 + int 10h + jmp loop_error_message + +freeze_pc: + jmp $ ; endless loop + + ; === KERNEL FOUND. LOADING... === + +found_kernel_file: + mov bp, [si+01ah] ; first cluster of kernel file + ; + mov [cluster1st+boot_program], bp ; starting cluster of kernel file + ; <\diamond> + + ; reading first FAT table + mov ax, word [BPB_RsvdSecCnt+boot_program] ; begin first FAT abs sector number + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) + mov bx, pos_read_tmp ; es:bx read position + mov ah, 2 ; ah=2 (read) + mov al, byte [BPB_FATSz16+boot_program] ; FAT size in sectors (TODO: max 255 sectors) + call read_sector + jc file_error_message ; read error + + mov ax, seg_read_kernel + mov es, ax + xor bx, bx ; es:bx = 1000h:0000h + + + ; reading kernel file +loop_obtains_kernel_data: + ; read one cluster of file + call obtain_cluster + jc file_error_message ; read error + + ; add one cluster length to segment:offset + push bx + mov bx, es + mov ax, word [BPB_BytsPerSec+boot_program] ;\ + movsx cx, byte [BPB_SecPerClus+boot_program] ; | !!! TODO: !!! + mul cx ; | out this from loop !!! + shr ax, 4 ;/ + add bx, ax + mov es, bx + pop bx + + mov di, bp + shr di, 1 + pushf + add di, bp ; di = bp * 1.5 + add di, pos_read_tmp + mov ax, [di] ; read next entry from FAT-chain + popf + jc move_4_right + and ax, 0fffh + jmp verify_end_sector +move_4_right: + mov cl, 4 + shr ax, cl +verify_end_sector: + cmp ax, 0ff8h ; last cluster + jae execute_kernel + mov bp, ax + jmp loop_obtains_kernel_data + +execute_kernel: + ; + mov ax, 'KL' + push 0 + pop ds + mov si, loader_block+boot_program + ; + push word seg_read_kernel + push word 0 + retf ; jmp far 1000:0000 + + +;------------------------------------------ + ; loading cluster from file to es:bx +obtain_cluster: + ; bp - cluster number to read + ; carry = 0 -> read OK + ; carry = 1 -> read ERROR + + ; print one dot + push bx + mov ax, 0e2eh ; ah=0eh (teletype), al='.' + xor bh, bh + int 10h + pop bx + +writesec: + ; convert cluster number to sector number + mov ax, bp ; data cluster to read + sub ax, 2 + xor dx, dx + mov dl, byte [BPB_SecPerClus+boot_program] + mul dx + add ax, word [data_start+boot_program] + + call conv_abs_to_THS ; convert abs sector (AX) to BIOS T:H:S (track:head:sector) +patchhere: + mov ah, 2 ; ah=2 (read) + mov al, byte [BPB_SecPerClus+boot_program] ; al=(one cluster) + call read_sector + retn +;------------------------------------------ + +;------------------------------------------ + ; read sector from disk +read_sector: + push bp + mov bp, 20 ; try 20 times +newread: + dec bp + jz file_error_message + push ax bx cx dx + int 13h + pop dx cx bx ax + jc newread + pop bp + retn +;------------------------------------------ + ; convert abs. sector number (AX) to BIOS T:H:S + ; sector number = (abs.sector%BPB_SecPerTrk)+1 + ; pre.track number = (abs.sector/BPB_SecPerTrk) + ; head number = pre.track number%BPB_NumHeads + ; track number = pre.track number/BPB_NumHeads + ; Return: cl - sector number + ; ch - track number + ; dl - drive number (0 = a:) + ; dh - head number +conv_abs_to_THS: + push bx + mov bx, word [BPB_SecPerTrk+boot_program] + xor dx, dx + div bx + inc dx + mov cl, dl ; cl = sector number + mov bx, word [BPB_NumHeads+boot_program] + xor dx, dx + div bx + ; !!!!!!! ax = track number, dx = head number + mov ch, al ; ch=track number + xchg dh, dl ; dh=head number + mov dl, 0 ; dl=0 (drive 0 (a:)) + pop bx + retn +;------------------------------------------ + +if lang eq sp +loading db cr,lf,'Iniciando el sistema ',00h +else +loading db cr,lf,'Starting system ',00h +end if +error_message db 13,10 +kernel_name db 'KERNEL MNT ?',cr,lf,00h +FirstRootDirSecNum dw ? +RootDirSecs dw ? +data_start dw ? + +; +write1st: + push cs + pop ds + mov byte [patchhere+1+boot_program], 3 ; change ah=2 to ah=3 + mov bp, [cluster1st+boot_program] + push 1000h + pop es + xor bx, bx + call writesec + mov byte [patchhere+1+boot_program], 2 ; change back ah=3 to ah=2 + retf +cluster1st dw ? +loader_block: + db 1 + dw 0 + dw write1st+boot_program + dw 0 +; <\diamond> + +times 0x1fe-$ db 00h + + db 55h,0aah ;boot signature diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/Tupfile.lua new file mode 100644 index 000000000..1f704dfbf --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/Tupfile.lua @@ -0,0 +1,2 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("kordldr.win.asm", "fasm %f %o", "kordldr.win.bin") diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/build.bat b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/build.bat new file mode 100644 index 000000000..5419e0d68 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/build.bat @@ -0,0 +1,2 @@ +@fasm -m 65535 kordldr.win.asm kordldr.win +@pause \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/fat.inc b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/fat.inc new file mode 100644 index 000000000..3f4ccda09 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/fat.inc @@ -0,0 +1,509 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + +; in: ss:bp = 0:dat +; in: es:bx = address to load file +; in: ds:si -> ASCIIZ name +; in: cx = limit in sectors +; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file has been loaded, bx=2 - file not found +; out: dx:ax = file size (0xFFFFFFFF if file not found) +load_file_fat: + mov eax, [bp + root_clus - dat] + mov [bp + cur_obj - dat], root_string + push es + push bx + push cx +.parse_dir_loop: +; convert name to FAT name + push [bp + cur_obj - dat] + push ax + mov [bp + cur_obj - dat], si + push ss + pop es +; convert ASCIIZ filename to FAT name + mov di, fat_filename + push di + mov cx, 8+3 + mov al, ' ' + rep stosb + pop di + mov cl, 8 ; 8 symbols per name + mov bl, 1 +.nameloop: + lodsb + test al, al + jz .namedone + cmp al, '/' + jz .namedone + cmp al, '.' + jz .namedot + dec cx + js .badname + cmp al, 'a' + jb @f + cmp al, 'z' + ja @f + sub al, 'a'-'A' +@@: + stosb + jmp .nameloop +.namedot: + inc bx + jp .badname + add di, cx + mov cl, 3 + jmp .nameloop +.badname: + mov si, badname_msg + jmp find_error_si +.namedone: +; scan directory + pop ax ; eax = cluster of directory + ; high word of eax is preserved by operations above + push ds + push si +; read a folder sector-by-sector and scan +; first, try to use the cache + push ss + pop ds + mov bx, -2 + mov cx, [bp + rootcache_size - dat] + cmp [bp + root_clus - dat], eax + jz .lookcache_root + mov di, foldcache_mark + xor bx, bx + mov cx, [bp + cachelimit - dat] +@@: + lea si, [di+bx] + mov edx, dword [foldcache_clus+si-foldcache_mark+bx] + cmp edx, eax + jz .cacheok + test edx, edx + jz .cacheadd ; the cache has place for new entry + inc bx + inc bx + dec cx + js @b +; the folder is not present in the cache, so add it +; the cache is full; find the oldest entry and replace it with the new one + mov bx, -2 + mov dx, [bp + cachelimit - dat] +@@: + inc bx + inc bx + cmp word [di+bx], dx ; marks have values 0 through [cachelimit] + jnz @b +.cacheadd: + or word [di+bx], 0xFFFF ; very big value, it will be changed soon + and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet + lea si, [di+bx] + mov dword [foldcache_clus+si-foldcache_mark+bx], eax +.cacheok: +; update cache marks + mov dx, [di+bx] + mov cx, [foldcache_size+di-foldcache_mark+bx] + mov di, [bp + cachelimit - dat] + add di, di +.cacheupdate: + cmp [foldcache_mark+di], dx + adc [foldcache_mark+di], 0 + dec di + dec di + jns .cacheupdate + and [foldcache_mark+bx], 0 +; done, bx contains (position in cache)*2 +.lookcache_root: +; bx = (position in cache)*2 for non-root folders; bx = -2 for root folder + ;mov dx, bx + ;shl dx, 8 + ;add dx, 0x9200 + lea dx, [bx + 0x92] + xchg dl, dh + mov ds, dx + mov si, fat_filename ; ss:si -> filename in FAT style + call fat_scan_for_filename + jz .lookup_done +; cache miss, read folder data from disk +; we are reading parent directory, it can result in disk read errors; restore [cur_obj] + mov di, sp + mov bx, [bp + cur_obj - dat] + xchg bx, [ss:di+4] + mov [bp + cur_obj - dat], bx + mov bx, cx + add bx, 0xF + shr bx, 4 + shl cx, 5 + mov di, cx ; es:di -> free space in cache entry +; external loop: scan clusters +.folder_next_cluster: +; internal loop: scan sectors in cluster + movzx ecx, byte [ss:0x320D] ; BPB_SecPerClus + push eax +; FAT12/16 root - special handling + test eax, eax + jnz .folder_notroot + mov cx, [ss:0x3211] ; BPB_RootEntCnt + mov dx, cx + add cx, 0xF + rcr cx, 1 + shr cx, 3 + mov eax, [bp + root_start - dat] + jmp .folder_next_sector +.folder_notroot: + mul ecx + add eax, [bp + data_start - dat] +.folder_next_sector: + sub dx, 0x10 +; skip first bx sectors + dec bx + jns .folder_skip_sector + push cx + push es di + push 0x8000 + pop es + xor bx, bx + mov cx, 1 + push es + call read + jc ..found_disk_error +; copy data to the cache... + pop ds + pop di es + cmp di, 0x2000 ; ...if there is free space, of course + jae @f + pusha + mov cx, 0x100 + xor si, si + rep movsw + mov di, es + shr di, 8 + cmp di, 0x90 + jz .update_rootcache_size + add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache + jmp .updated_cachesize +.update_rootcache_size: + mov cl, 0x10 + cmp cx, dx + jb @f + mov cx, dx +@@: + add [bp + rootcache_size - dat], cx +.updated_cachesize: + popa +@@: + push es + mov cl, 0x10 ; ch=0 at this point + cmp cx, dx + jb @f + mov cx, dx +@@: + call fat_scan_for_filename + pop es + pop cx + jz .lookup_done_pop +.folder_skip_sector: + inc eax + loop .folder_next_sector + pop eax ; eax = current cluster + test eax, eax + jz @f + call [bp + get_next_cluster_ptr - dat] + jc .folder_next_cluster +@@: + stc + push eax +.lookup_done_pop: + pop eax +.lookup_done: + pop si +; CF=1 <=> failed + jnc .found + pop ds + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si +.found: + mov eax, [di+20-2] + mov edx, [di+28] + mov ax, [di+26] ; get cluster + test byte [di+11], 10h ; directory? + pop ds + pop [bp + cur_obj - dat] ; forget old [cur_obj] + jz .regular_file + cmp byte [si-1], 0 + jnz .parse_dir_loop +..directory_error: + mov si, directory_string + jmp find_error_si +.regular_file: + cmp byte [si-1], 0 + jz @f +..notdir_error: + mov si, notdir_string + jmp find_error_si +@@: +; ok, we have found a regular file and the caller requested it +; parse FAT chunk + push ss + pop es + push ss + pop ds + mov di, 0x4005 + mov byte [di-5], 1 ; non-resident attribute + mov dword [di-4], 1 + stosd + pop cx + push cx +.parsefat: + call [bp + get_next_cluster_ptr - dat] + jnc .done + mov esi, [di-8] + add esi, [di-4] + cmp eax, esi + jz .contc + mov dword [di], 1 + scasd + stosd + jmp @f +.contc: + inc dword [di-8] +@@: + sub cl, [0x320D] + sbb ch, 0 + ja .parsefat +.done: + xor eax, eax + stosd + mov si, 0x4000 +load_file_common_end: + xor ecx, ecx + pop cx + pop bx + pop es + mov [bp + filesize - dat], edx + mov [bp + sectors_read - dat], ecx + add edx, 0x1FF + shr edx, 9 + mov [bp + filesize_sectors - dat], edx + cmp edx, ecx + seta al + mov ah, 0 + push ax + call read_file_chunk +continue_load_common_end: + mov [bp + cur_chunk_ptr - dat], si + pop bx + mov ax, word [bp + filesize - dat] + mov dx, word [bp + filesize+2 - dat] + jnc @f + mov bl, 3 ; read error +@@: + ret + +continue_load_file: +; es:bx -> buffer for output, ecx = cx = number of sectors + mov si, [bp + cur_chunk_ptr - dat] + push ecx + add ecx, [bp + sectors_read - dat] + mov [bp + sectors_read - dat], ecx + cmp [bp + filesize_sectors - dat], ecx + pop ecx + seta al + mov ah, 0 + push ax + push continue_load_common_end + push ss + pop ds + cmp [bp + cur_chunk_resident - dat], ah + jnz .nonresident +.resident: + mov ax, word [bp + num_sectors - dat] + jmp read_file_chunk.resident.continue +.nonresident: + mov eax, [bp + cur_cluster - dat] + mov edx, [bp + num_sectors - dat] + add eax, [bp + cur_delta - dat] + jmp read_file_chunk.nonresident.continue + +fat_scan_for_filename: +; in: ss:si -> 11-bytes FAT name +; in: ds:0 -> part of directory data +; in: cx = number of entries +; out: if found: CF=0, ZF=1, es:di -> directory entry +; out: if not found, but continue required: CF=1 and ZF=0 +; out: if not found and zero item reached: CF=1 and ZF=1 + push ds + pop es + xor di, di + push cx + jcxz .noent +.loop: + cmp byte [di], 0 + jz .notfound + test byte [di+11], 8 ; volume label? + jnz .cont ; ignore volume labels + pusha + mov cx, 11 + repz cmps byte [ss:si], byte [es:di] + popa + jz .done +.cont: + add di, 0x20 + loop .loop +.noent: + inc cx ; clear ZF flag +.notfound: + stc +.done: + pop cx + ret + +fat12_get_next_cluster: +; in: ax = cluster (high word of eax is zero) +; out: if there is next cluster: CF=1, ax = next cluster +; out: if there is no next cluster: CF=0 + push si + push ds + push 0x6000 + pop ds + mov si, ax + shr si, 1 + add si, ax + test al, 1 + lodsw + jz @f + shr ax, 4 +@@: + and ax, 0xFFF + cmp ax, 0xFF7 + pop ds si + ret + +fat16_get_next_cluster: +; in: ax = cluster (high word of eax is zero) +; out: if there is next cluster: CF=1, ax = next cluster +; out: if there is no next cluster: CF=0 +; each sector contains 200h bytes = 100h FAT entries +; so ah = # of sector, al = offset in sector + push si + mov si, ax + shr si, 8 +; calculate segment for this sector of FAT table +; base for FAT table is 6000:0000, so the sector #si has to be loaded to (60000 + 200*si) +; segment = 6000 + 20*si, offset = 0 + push es + push si + shl si, 5 + add si, 0x6000 + mov es, si + pop si + cmp byte [ss:0x3400+si], 0 ; sector already loaded? + jnz .noread +; load corresponding sector, try all FATs if disk read error detected + pusha + movzx di, byte [ss:0x3210] ; BPB_NumFATs + xor bx, bx + mov ax, [ss:0x320E] ; BPB_RsvdSecCnt + xor dx, dx + add ax, si + adc dx, bx +@@: + push es + push dx ax + pop eax + mov cx, 1 ; read 1 sector + call read + pop es + jnc @f + add ax, [ss:0x3216] ; BPB_FATSz16 + adc dx, bx + dec di + jnz @b +..found_disk_error: + mov si, disk_error_msg + jmp find_error_si +@@: + popa +.noread: + mov si, ax + and si, 0xFF + add si, si + mov ax, [es:si] + pop es + cmp ax, 0xFFF7 + pop si + ret + +fat32_get_next_cluster: +; in: eax = cluster +; out: if there is next cluster: CF=1, eax = next cluster +; out: if there is no next cluster: CF=0 + push di + push ax + shr eax, 7 +; eax = FAT sector number; look in cache + push si + mov si, cache1head + call cache_lookup + pop si + jnc .noread +; read FAT, try all FATs if disk read error detected + push es + pushad + movzx edx, word [ss:0x320E] ; BPB_RsvdSecCnt + add eax, edx + movzx si, byte [ss:0x3210] ; BPB_NumFATs +@@: + lea cx, [di - 0x3400 + (0x6000 shr (9-3))] + shl cx, 9-3 + mov es, cx + xor bx, bx + mov cx, 1 + call read + jnc @f + add eax, [ss:0x3224] ; BPB_FATSz32 + dec si + jnz @b + jmp ..found_disk_error +@@: + popad + pop es +.noread: +; get requested item + lea ax, [di - 0x3400 + (0x6000 shr (9-3))] + pop di + and di, 0x7F + shl di, 2 + shl ax, 9-3 + push ds + mov ds, ax + and byte [di+3], 0x0F + mov eax, [di] + pop ds + pop di + ;and eax, 0x0FFFFFFF + cmp eax, 0x0FFFFFF7 + ret diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.asm new file mode 100644 index 000000000..460e3b7c3 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.asm @@ -0,0 +1,924 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + +; KordOS bootloader, based on mtldr, KolibriOS bootloader, by diamond +; It is used when main bootloader is Windows loader. + +; this code is loaded: +; NT/2k/XP: by ntldr to 0D00:0000 +; 9x: by io.sys from config.sys to xxxx:0100 +; Vista: by bootmgr to 0000:7C00 + format binary + use16 + +; in any case, we relocate this code to 0000:0600 + org 0x600 +; entry point for 9x and Vista booting + call @f + db 'NTFS' +@@: + pop si + sub si, 3 + cmp si, 100h + jnz boot_vista + mov si, load_question + 100h - 600h + call out_string +; mov si, answer + 100h - 0600h ; already is +xxy: + mov ah, 0 + int 16h + or al, 20h + mov [si], al + cmp al, 'y' + jz xxz + cmp al, 'n' + jnz xxy +; continue load Windows +; call out_string +; ret +out_string: + push bx +@@: + lodsb + test al, al + jz @f + mov ah, 0Eh + mov bx, 7 + int 10h + jmp @b +@@: + pop bx + ret +xxz: +; boot KordOS + call out_string +; 9x bootloader has already hooked some interrupts; to correctly remove all DOS handlers, +; issue int 19h (reboot interrupt) and trace its DOS handler until original BIOS handler is reached + xor di, di + mov ds, di + mov word [di+4], new01handler + 100h - 600h + mov [di+6], cs + pushf + pop ax + or ah, 1 + push ax + popf +; we cannot issue INT 19h directly, because INT command clears TF +; int 19h ; don't issue it directly, because INT command clears TF +; so instead we use direct call +; pushf ; there will be no IRET + call far [di + 19h*4] +xxt: + xor di, di + mov ds, di + cmp word [di + 8*4+2], 0F000h + jz @f + les bx, [di + 8*4] + mov eax, [es:bx+1] + mov [di + 8*4], eax +@@: + mov si, 100h +boot_vista: +; relocate cs:si -> 0000:0600 + push cs + pop ds + xor ax, ax + mov es, ax + mov di, 0x600 + mov cx, 2000h/2 + rep movsw + jmp 0:real_entry + +load_question db 'Load KordOS? [y/n]: ',0 +answer db ? + db 13,10,0 + +new01handler: +; [sp]=ip, [sp+2]=cs, [sp+4]=flags + push bp + mov bp, sp + push ds + lds bp, [bp+2] + cmp word [ds:bp], 19cdh + jz xxt + pop ds + pop bp + iret + +; read from hard disk +; in: eax = absolute sector +; cx = number of sectors +; es:bx -> buffer +; out: CF=1 if error +read: + pushad + add eax, [bp + partition_start - dat] + cmp [bp + use_lba - dat], 0 + jz .chs +; LBA read + push ds +.lbado: + push ax + push cx + cmp cx, 0x7F + jbe @f + mov cx, 0x7F +@@: +; create disk address packet on the stack +; dq starting LBA + push 0 + push 0 + push eax +; dd buffer + push es + push bx +; dw number of blocks to transfer (no more than 0x7F) + push cx +; dw packet size in bytes + push 10h +; issue BIOS call + push ss + pop ds + mov si, sp + mov dl, [bp + boot_drive - dat] + mov ah, 42h + int 13h + jc .disk_error_lba + add sp, 10h ; restore stack +; increase current sector & buffer; decrease number of sectors + movzx esi, cx + mov ax, es + shl cx, 5 + add ax, cx + mov es, ax + pop cx + pop ax + add eax, esi + sub cx, si + jnz .lbado + pop ds + popad + ret +.disk_error_lba: + add sp, 14h + pop ds + popad + stc + ret + +.chs: + pusha + pop edi ; loword(edi) = di, hiword(edi) = si + push bx + +; eax / (SectorsPerTrack) -> eax, remainder bx + movzx esi, [bp + sectors - dat] + xor edx, edx + div esi + mov bx, dx ; bx = sector-1 + +; eax -> dx:ax + push eax + pop ax + pop dx +; (dword in dx:ax) / (NumHeads) -> (word in ax), remainder dx + div [bp + heads - dat] + +; number of sectors: read no more than to end of track + sub si, bx + cmp cx, si + jbe @f + mov cx, si +@@: + + inc bx +; now ax=track, dl=head, dh=0, cl=number of sectors, ch=0, bl=sector +; convert to int13 format + movzx edi, cx + mov dh, dl + mov dl, [bp + boot_drive - dat] + shl ah, 6 + mov ch, al + mov al, cl + mov cl, bl + or cl, ah + pop bx + mov si, 3 + mov ah, 2 +@@: + push ax + int 13h + jnc @f + xor ax, ax + int 13h ; reset drive + pop ax + dec si + jnz @b + add sp, 12 + popad + stc + ret +@@: + pop ax + mov ax, es + mov cx, di + shl cx, 5 + add ax, cx + mov es, ax + push edi + popa + add eax, edi + sub cx, di + jnz .chs + popad + ret + +disk_error2 db 'Fatal: cannot read partitions info: ' +disk_error_msg db 'disk read error',0 +disk_params_msg db 'Fatal: cannot get drive parameters',0 +start_msg db 2,' KordOS bootloader',13,10,0 +part_msg db 'looking at partition ' +part_char db '0' ; will be incremented before writing message + db ' ... ',0 +errfs_msg db 'unknown filesystem',13,10,0 +fatxx_msg db 'FATxx' +newline db 13,10,0 +ntfs_msg db 'NTFS',13,10,0 +error_msg db 'Error' +colon db ': ',0 +root_string db '\',0 +nomem_msg db 'No memory',0 +filesys_string db '(filesystem)',0 +directory_string db 'is a directory',0 +notdir_string db 'not a directory',0 + +; entry point for NT/2k/XP booting +; ntldr loads our code to 0D00:0000 and jumps to 0D00:0256 + repeat 600h + 256h - $ + db 1 ; any data can be here; 1 in ASCII is a nice face :) + end repeat +; cs=es=0D00, ds=07C0, ss=0 +; esi=edi=ebp=0, esp=7C00 + xor si, si + jmp boot_vista + +real_entry: +; ax = 0 + mov ds, ax + mov es, ax +; our stack is 4 Kb: memory range 2000-3000 + mov ss, ax + mov sp, 3000h + mov bp, dat + sti ; just for case +; say hi to user + mov si, start_msg + call out_string +; we are booting from hard disk identified by [boot_drive] + mov dl, [bp + boot_drive - dat] +; is LBA supported? + mov [bp + use_lba - dat], 0 + mov ah, 41h + mov bx, 55AAh + int 13h + jc .no_lba + cmp bx, 0AA55h + jnz .no_lba + test cl, 1 + jz .no_lba + inc [bp + use_lba - dat] + jmp disk_params_ok +.no_lba: +; get drive geometry + mov ah, 8 + mov dl, [bp + boot_drive - dat] + int 13h + jnc @f + mov si, disk_params_msg + call out_string + jmp $ +@@: + movzx ax, dh + inc ax + mov [bp + heads - dat], ax + and cx, 3Fh + mov [bp + sectors - dat], cx +disk_params_ok: +; determine size of cache for folders + int 12h ; ax = size of available base memory in Kb + sub ax, 94000h / 1024 + jc nomem + shr ax, 3 + mov [bp + cachelimit - dat], ax ; size of cache - 1 +; scan all partitions +new_partition_ex: + xor eax, eax ; read first sector of current disk area + mov [bp + extended_part_cur - dat], eax ; no extended partition yet + mov [bp + cur_partition_ofs - dat], 31BEh ; start from first partition + push es + mov cx, 1 + mov bx, 3000h + call read + pop es + jnc new_partition + mov si, disk_error2 + call out_string + jmp $ +new_partition: + mov bx, [bp + cur_partition_ofs - dat] + mov al, [bx+4] ; partition type + test al, al + jz next_partition + cmp al, 5 + jz @f + cmp al, 0xF + jnz not_extended +@@: +; extended partition + mov eax, [bx+8] ; partition start + add eax, [bp + extended_part_start - dat] + mov [bp + extended_part_cur - dat], eax +next_partition: + add [bp + cur_partition_ofs - dat], 10h + cmp [bp + cur_partition_ofs - dat], 31FEh + jb new_partition + mov eax, [bp + extended_part_cur - dat] + test eax, eax + jz partitions_done + cmp [bp + extended_part_start - dat], 0 + jnz @f + mov [bp + extended_part_start - dat], eax +@@: + mov [bp + extended_parent - dat], eax + mov [bp + partition_start - dat], eax + jmp new_partition_ex +partitions_done: + mov si, total_kaput + call out_string + jmp $ +not_extended: + mov eax, [bx+8] + add eax, [bp + extended_parent - dat] + mov [bp + partition_start - dat], eax +; try to load from current partition +; inform user + mov si, part_msg + inc [si + part_char - part_msg] + call out_string +; read bootsector + xor eax, eax + mov [bp + cur_obj - dat], filesys_string + push es + mov cx, 1 + mov bx, 3200h + call read + pop es + mov si, disk_error_msg + jc find_error_si + movzx si, byte [bx+13] + mov word [bp + sect_per_clust - dat], si + test si, si + jz unknown_fs + lea ax, [si-1] + test si, ax + jnz unknown_fs +; determine file system +; Number of bytes per sector == 0x200 (this loader assumes that physical sector size is 200h) + cmp word [bx+11], 0x200 + jnz unknown_fs +; is it NTFS? + cmp dword [bx+3], 'NTFS' + jnz not_ntfs + cmp byte [bx+16], bl + jz ntfs +not_ntfs: +; is it FAT? FAT12/FAT16/FAT32? +; get count of sectors to dword in cx:si + mov si, [bx+19] + xor cx, cx + test si, si + jnz @f + mov si, [bx+32] + mov cx, [bx+34] +@@: + xor eax, eax +; subtract size of system area + sub si, [bx+14] ; BPB_ResvdSecCnt + sbb cx, ax + mov ax, [bx+17] ; BPB_RootEntCnt + add ax, 0xF + rcr ax, 1 + shr ax, 3 + sub si, ax + sbb cx, 0 + push cx + push si + mov ax, word [bx+22] + test ax, ax + jnz @f + mov eax, [bx+36] +@@: + movzx ecx, byte [bx+16] + imul ecx, eax + pop eax + sub eax, ecx +; now eax = count of sectors in the data region + xor edx, edx + div [bp + sect_per_clust - dat] +; now eax = count of clusters in the data region + mov si, fatxx_msg + cmp eax, 0xFFF5 + jae test_fat32 +; test magic value in FAT bootsector - FAT12/16 bootsector has it at the offset +38 + cmp byte [bx+38], 0x29 + jnz not_fat + cmp ax, 0xFF5 + jae fat16 +fat12: + mov [bp + get_next_cluster_ptr - dat], fat12_get_next_cluster + mov di, cx ; BPB_NumFATs + mov ax, '12' + push ax ; save for secondary loader + mov word [si+3], ax + call out_string + movzx ecx, word [bx+22] ; BPB_FATSz16 +; FAT12: read entire FAT table (it is no more than 0x1000*3/2 = 0x1800 bytes) +.fatloop: +; if first copy is not readable, try to switch to other copies + push 0x6000 + pop es + xor bx, bx + movzx eax, word [0x320E] ; BPB_RsvdSecCnt + push cx + cmp cx, 12 + jb @f + mov cx, 12 +@@: + call read + pop cx + jnc fat1x_common + add eax, ecx ; switch to next copy of FAT + dec di + jnz .fatloop + mov si, disk_error_msg + jmp find_error_si +fat16: + mov [bp + get_next_cluster_ptr - dat], fat16_get_next_cluster + mov ax, '16' + push ax ; save for secondary loader + mov word [si+3], ax + call out_string +; FAT16: init FAT cache - no sectors loaded + mov di, 0x3400 + xor ax, ax + mov cx, 0x100/2 + rep stosw +fat1x_common: + mov bx, 0x3200 + movzx eax, word [bx+22] ; BPB_FATSz16 + xor esi, esi ; no root cluster + jmp fat_common +test_fat32: +; FAT32 bootsector has it at the offset +66 + cmp byte [bx+66], 0x29 + jnz not_fat + mov [bp + get_next_cluster_ptr - dat], fat32_get_next_cluster + mov ax, '32' + push ax ; save for secondary loader + mov word [si+3], ax + call out_string +; FAT32 - init cache for FAT table: no sectors loaded + lea si, [bp + cache1head - dat] + mov [si], si ; no sectors in cache: + mov [si+2], si ; 'prev' & 'next' links point to self + mov [bp + cache1end - dat], 3400h ; first free item = 3400h + mov [bp + cache1limit - dat], 3C00h + mov eax, [bx+36] ; BPB_FATSz32 + mov esi, [bx+44] ; BPB_RootClus + jmp fat_common +not_fat: +unknown_fs: + mov si, errfs_msg + call out_string + jmp next_partition +fat_common: + push ss + pop es + movzx edx, byte [bx+16] ; BPB_NumFATs + mul edx + mov [bp + root_start - dat], eax ; this is for FAT1x +; eax = total size of all FAT tables, in sectors + movzx ecx, word [bx+17] ; BPB_RootEntCnt + add ecx, 0xF + shr ecx, 4 + add eax, ecx + mov cx, word [bx+14] ; BPB_RsvdSecCnt + add [bp + root_start - dat], ecx ; this is for FAT1x + add eax, ecx +; cluster 2 begins from sector eax + movzx ebx, byte [bx+13] ; BPB_SecPerClus + sub eax, ebx + sub eax, ebx + mov [bp + data_start - dat], eax +; no clusters in folders cache + mov di, foldcache_clus - 2 + xor ax, ax + mov cx, 7*8/2 + 1 + rep stosw + mov [bp + root_clus - dat], esi +; load secondary loader + mov [bp + load_file_ptr - dat], load_file_fat +load_secondary: + push 0x1000 + pop es + xor bx, bx + mov si, kernel_name + mov cx, 0x30000 / 0x200 + call [bp + load_file_ptr - dat] +; say error if needed + mov si, error_too_big + dec bx + js @f + jz find_error_si + mov si, disk_error_msg + jmp find_error_si +@@: +; fill loader information and jump to secondary loader + mov al, 'h' ; boot device: hard drive + mov ah, [bp + boot_drive - dat] + sub ah, 80h ; boot device: identifier + pop bx ; restore file system ID ('12'/'16'/'32'/'nt') + mov si, callback + jmp 1000h:0000h + +nomem: + mov si, nomem_msg + call out_string + jmp $ + +ntfs: + push 'nt' ; save for secondary loader + mov si, ntfs_msg + call out_string + xor eax, eax + mov [bp + data_start - dat], eax + mov ecx, [bx+40h] ; frs_size + cmp cl, al + jg .1 + neg cl + inc ax + shl eax, cl + jmp .2 +.1: + mov eax, ecx + shl eax, 9 +.2: + mov [bp + frs_size - dat], ax +; standard value for frs_size is 0x400 bytes = 1 Kb, and it cannot be set different +; (at least with standard tools) +; we allow extra size, but no more than 0x1000 bytes = 4 Kb + mov si, invalid_volume_msg + cmp eax, 0x1000 + ja find_error_si +; must be multiple of sector size + test ax, 0x1FF + jnz find_error_si + shr ax, 9 + xchg cx, ax +; initialize cache - no data loaded + lea si, [bp + cache1head - dat] + mov [si], si + mov [si+2], si + mov word [si+4], 3400h ; first free item = 3400h + mov word [si+6], 3400h + 8*8 ; 8 items in this cache +; read first MFT record - description of MFT itself + mov [bp + cur_obj - dat], mft_string + mov eax, [bx+30h] ; mft_cluster + mul [bp + sect_per_clust - dat] + push 0x8000 + pop es + xor bx, bx + push es + call read + pop ds + call restore_usa +; scan for unnamed $DATA attribute + mov [bp + freeattr - dat], 4000h + mov ax, 80h + call load_attr + push ss + pop ds + mov si, nodata_string + jc find_error_si +; load secondary loader + mov [bp + load_file_ptr - dat], load_file_ntfs + jmp load_secondary + +find_error_si: + push si +find_error_sp: + cmp [bp + in_callback - dat], 0 + jnz error_in_callback + push ss + pop ds + push ss + pop es + mov si, error_msg + call out_string + mov si, [bp + cur_obj - dat] +@@: + lodsb + test al, al + jz @f + cmp al, '/' + jz @f + mov ah, 0Eh + mov bx, 7 + int 10h + jmp @b +@@: + mov si, colon + call out_string + pop si + call out_string + mov si, newline + call out_string + mov sp, 0x3000 + jmp next_partition +error_in_callback: +; return status: file not found, except for read errors + mov bx, 2 + cmp si, disk_error_msg + jnz @f + inc bx +@@: + mov ax, 0xFFFF + mov dx, ax + mov sp, 3000h - 6 + ret + +callback: +; in: ax = function number; only functions 1 and 2 are defined for now +; save caller's stack + mov dx, ss + mov cx, sp +; set our stack (required because we need ss=0) + xor si, si + mov ss, si + mov sp, 3000h + mov bp, dat + mov [bp + in_callback - dat], 1 + push dx + push cx +; set ds:si -> ASCIIZ name + lea si, [di+6] +; set cx = limit in sectors; 4Kb = 8 sectors + movzx ecx, word [di+4] + shl cx, 3 +; set es:bx = pointer to buffer + les bx, [di] +; call our function + stc ; unsupported function + dec ax + jz callback_readfile + dec ax + jnz callback_ret + call continue_load_file + jmp callback_ret_succ +callback_readfile: +; function 1: read file +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error +; out: dx:ax = file size (0xFFFFFFFF if file was not found) + call [bp + load_file_ptr - dat] +callback_ret_succ: + clc +callback_ret: +; restore caller's stack + pop cx + pop ss + mov sp, cx +; return to caller + retf + +read_file_chunk.resident: +; auxiliary label for read_file_chunk procedure + mov di, bx + lodsw +read_file_chunk.resident.continue: + mov dx, ax + add dx, 0x1FF + shr dx, 9 + cmp dx, cx + jbe @f + mov ax, cx + shl ax, 9 +@@: + xchg ax, cx + rep movsb + xchg ax, cx + clc ; no disk error if no disk requests + mov word [bp + num_sectors - dat], ax + ret + +read_file_chunk: +; in: ds:si -> file chunk +; in: es:bx -> buffer for output +; in: ecx = maximum number of sectors to read (high word must be 0) +; out: CF=1 <=> disk read error + lodsb + mov [bp + cur_chunk_resident - dat], al + test al, al + jz .resident +; normal case: load (non-resident) attribute from disk +.read_block: + lodsd + xchg eax, edx + test edx, edx + jz .ret + lodsd +; eax = start cluster, edx = number of clusters, cx = limit in sectors + imul eax, [bp + sect_per_clust - dat] + add eax, [bp + data_start - dat] + mov [bp + cur_cluster - dat], eax + imul edx, [bp + sect_per_clust - dat] + mov [bp + num_sectors - dat], edx + and [bp + cur_delta - dat], 0 +.nonresident.continue: + cmp edx, ecx + jb @f + mov edx, ecx +@@: + test dx, dx + jz .read_block + add [bp + cur_delta - dat], edx + sub [bp + num_sectors - dat], edx + sub ecx, edx + push cx + mov cx, dx + call read + pop cx + jc .ret + test cx, cx + jnz .read_block +.ret: + ret + +cache_lookup: +; in: eax = value to look, si = pointer to cache structure +; out: di->cache entry; CF=1 <=> the value was not found + push ds bx + push ss + pop ds + mov di, [si+2] +.look: + cmp di, si + jz .not_in_cache + cmp eax, [di+4] + jz .in_cache + mov di, [di+2] + jmp .look +.not_in_cache: +; cache miss +; cache is full? + mov di, [si+4] + cmp di, [si+6] + jnz .cache_not_full +; yes, delete the oldest entry + mov di, [si] + mov bx, [di] + mov [si], bx + push word [di+2] + pop word [bx+2] + jmp .cache_append +.cache_not_full: +; no, allocate new item + add word [si+4], 8 +.cache_append: + mov [di+4], eax + stc + jmp @f +.in_cache: +; delete this sector from the list + push si + mov si, [di] + mov bx, [di+2] + mov [si+2], bx + mov [bx], si + pop si +@@: +; add new sector to the end of list + mov bx, di + xchg bx, [si+2] + push word [bx] + pop word [di] + mov [bx], di + mov [di+2], bx + pop bx ds + ret + +include 'fat.inc' +include 'ntfs.inc' + +total_kaput db 13,10,'Fatal error: cannot load the secondary loader',0 +error_too_big db 'file is too big',0 +nodata_string db '$DATA ' +error_not_found db 'not found',0 +noindex_string db '$INDEX_ROOT not found',0 +badname_msg db 'bad name for FAT',0 +invalid_volume_msg db 'invalid volume',0 +mft_string db '$MFT',0 +fragmented_string db 'too fragmented file',0 +invalid_read_request_string db 'cannot read attribute',0 + +kernel_name db 'kernel.mnt',0 + +align 4 +dat: + +extended_part_start dd 0 ; start sector for main extended partition +extended_part_cur dd ? ; start sector for current extended child +extended_parent dd 0 ; start sector for current extended parent +partition_start dd 0 ; start sector for current logical disk +cur_partition_ofs dw ? ; offset in MBR data for current partition +sect_per_clust dd 0 +; change this variable if you want to boot from other physical drive +boot_drive db 80h +in_callback db 0 + +; uninitialized data +use_lba db ? +cur_chunk_resident db ? +align 2 +heads dw ? +sectors dw ? +cache1head rw 2 +cache1end dw ? +cache1limit dw ? +data_start dd ? +cachelimit dw ? +load_file_ptr dw ? +cur_obj dw ? +missing_slash dw ? +root_clus dd ? +root_start dd ? +get_next_cluster_ptr dw ? +frs_size dw ? +freeattr dw ? +index_root dw ? +index_alloc dw ? +cur_index_seg dw ? +cur_index_cache dw ? +filesize dd ? +filesize_sectors dd ? +cur_cluster dd ? +cur_delta dd ? +num_sectors dd ? +sectors_read dd ? +cur_chunk_ptr dw ? + +rootcache_size dw ? ; must be immediately before foldcache_clus +if $-dat >= 0x80 +warning: + unoptimal data displacement! +end if +foldcache_clus rd 7 +foldcache_mark rw 7 +foldcache_size rw 7 +fat_filename rb 11 + +if $ > 2000h +error: + file is too big +end if + +; for NT/2k/XP, file must be 16 sectors = 0x2000 bytes long +repeat 0x2600 - $ + db 2 ; any data can be here; 2 is another nice face in ASCII :) +end repeat diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.txt b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.txt new file mode 100644 index 000000000..3affd3f39 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/kordldr.win.txt @@ -0,0 +1,391 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + Нет повести печальнее на свете, + Чем повесть о заклинившем Reset'е... + +Загрузчик для FAT- и NTFS-томов для случаев, когда основной бутсектор загружает +Windows, для носителей с размером сектора 512 байт. + +===================================================================== + +Требования для работы: +1) Все используемые файлы должны быть читабельны. +2) Минимальный процессор - 80386. +3) В системе должно быть как минимум 592K свободной базовой памяти. +4) Пути к используемым файлам не должны содержать символических ссылок NTFS + (жёсткие ссылки допускаются). +5) Используемые файлы не должны быть сжатыми или разреженными файлами + (актуально для NTFS, для FAT выполнено автоматически). + +===================================================================== + +Документация в тему (ссылки проверялись на валидность 08.08.2008): + официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf + русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip + спецификация NTFS: file://C:/windows/system32/drivers/ntfs.sys + и file://C:/ntldr либо file://C:/bootmgr + неофициальное описание NTFS: http://sourceforge.net/project/showfiles.php?group_id=13956&package_id=16543 + официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf + то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf + описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html + официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf + официальное описание bcdedit для Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcdedit_reff.mspx + официальное описание работы с базой данных загрузчика Vista: http://www.microsoft.com/whdc/system/platform/firmware/bcd.mspx + формат таблицы разделов жёсткого диска: http://www.microsoft.com/technet/prodtechnol/windows2000serv/reskit/prork/prcb_dis_qxql.mspx + +===================================================================== + +Схема используемой памяти: + 600-2000 код загрузчика (и данные) + 2000-3000 стек + 3000-3200 сектор MBR + 3200-3400 бутсектор логического диска + 3400-3C00 информация о кэше для таблиц FAT16/FAT32: + для FAT16 - массив на 0x100 байт, каждый байт равен + 0 или 1 в зависимости от того, загружен ли + соответствующий сектор таблицы FAT16; + для FAT32 - 100h входов по 8 байт: 4 байта + (две ссылки - вперёд и назад) для организации L2-списка + всех прочитанных секторов в порядке возрастания + последнего времени использования + 4 байта для номера + сектора; при переполнении кэша выкидывается элемент из + головы списка, то есть тот, к которому дольше всех + не было обращений + 3400-3440 информация о кэше для файловых записей NTFS в + таком же формате, как и кэш для FAT32, но на 8 входов + 3480-34C0 заголовки для кэшей записей индекса NTFS + 3500-3D00 информация о кэшах записей индекса NTFS: с каждой + файловой записью связан свой кэш для + соответствующего индекса + 4000-8000 место для информации об атрибутах для NTFS + 60000-80000 таблица FAT12 / место под таблицу FAT16 / + кэш для таблицы FAT32 / кэш для структур NTFS + 80000-90000 текущий рассматриваемый кластер + 90000-92000 FAT: кэш для корневой папки + 92000-... FAT: кэш для некорневых папок (каждой папке отводится + 2000h байт = 100h входов, одновременно в кэше + может находиться не более 7 папок; + точный размер определяется размером доступной + физической памяти - как правило, непосредственно + перед A0000 размещается EBDA, Extended BIOS Data Area) + +===================================================================== + +Основной процесс загрузки. +0a. Загрузка из-под DOS и Win9x: установка kordldr.win осуществляется + размещением команды install=c:\kordldr.win в первой строке config.sys; + при этом основной загрузчик системы загружает kordldr.win как обычный + com-файл, в какой-то сегмент по смещению 100h и передаёт управление + в начало кода (xxxx:0100). +0б. Загрузка из-под WinNT/2000/XP: установка kordldr.win осуществляется + добавлением строки наподобие c:\kordldr.win="KordOS" в секцию + [operating systems] файла boot.ini; если загружаемый файл имеет размер + не менее 8 Кб (0x2000 байт) и по смещению 3 содержит сигнатуру 'NTFS' + (в случае kordldr.win так и есть), то основной загрузчик каждой из + этих систем загружает kordldr.win по адресу 0D00:0000 и передаёт + управление на адрес 0D00:0256. +0в. Загрузка из-под Vista: установка kordldr.win осуществляется манипуляциями + с базой данных основного загрузчика через bcdedit и подробно описана в + инструкции к kordldr.win; основной загрузчик загружает целиком + kordldr.win по адресу 0000:7C00 и передаёт управление в начало кода. +1. При загрузке из-под DOS/9x основной загрузчик не ожидает, что загруженная + им программа окажется в свою очередь загрузчиком, и в этом случае + kordldr.win оказывается в условиях, когда основной загрузчик уже + установил какое-то окружение, в частности, перехватил некоторые + прерывания. Поэтому перед остальными действиями загрузчик должен + восстановить систему в начальное состояние. (При загрузке под + NT-линейкой такой проблемы не возникает, поскольку там основной + загрузчик ничего в системе не трогает.) Поэтому перед собственно + инициализацией KordOS при работе из-под DOS/9x производятся + дополнительные действия. Первым делом kordldr проверяет, какой из + случаев 0а и 0в имеет место (случай 0б отличается тем, что передаёт + управление не на начало кода): определяет значение ip (команда call + помещает в стек адрес следующей после call инструкции, команда pop si + выталкивает его в регистр si), и если оно равно 100h, то kordldr + загружен как com-файл из-под DOS/9x. Тогда он спрашивает подтверждения + у пользователя (поскольку в этой схеме kordldr загружается всегда, + он должен оставить возможность продолжить загрузку DOS/9x). Если + пользователь хочет продолжить обычную загрузку, kordldr завершается. + Иначе используется тот факт, что при выдаче прерывания перезагрузки + int 19h система предварительно снимает все свои перехваты BIOSовских + прерываний, а потом в свою очередь выдаёт int 19h уже BIOSу. Так что + kordldr устанавливает свой обработчик трассировочного прерывания, + устанавливает флаг трассировки и передаёт управление DOSовскому + обработчику. Обработчик трассировочного прерывания ничего не делает + до тех пор, пока следующей инструкцией не оказывается int 19h, а + в этот момент отбирает управление и продолжает загрузку KordOS. + При этом BIOSовские обработчики восстановлены за исключением, + быть может, прерывания таймера int 8, которое, возможно, восстановлено + до команды jmp far на оригинальный обработчик. В последнем случае его + нужно восстановить явно. +2. Загрузчик перемещает свой код на адрес 0000:0600. +3. (метка real_entry) Загрузчик устанавливает сегментные регистры ds = es = 0, + настраивает стек ss:sp = 0000:3000 и устанавливает bp так, чтобы + все данные можно было адресовать через [bp+N] с однобайтовым N + (в дальнейшем они так и будут адресоваться для освобождения ds и + экономии на размере кода). Разрешает прерывания на случай, если + они были запрещены. Выдаёт сообщение о начале загрузки, начинающееся + с весёлой рожицы (символ с ASCII-кодом 2). +4. Определяет характеристики жёсткого диска, указанного в качестве + загрузочного: проверяет поддержку LBA (функция 41h прерывания 13h), + если LBA не поддерживается, то определяет геометрию - число дорожек + и число секторов на дорожке (функция 8 прерывания 13h), эти параметры + нужны функции чтения с диска. +5. (метка new_partition_ex) Устраивает цикл по разделам жёсткого диска. + Цель цикла - для каждого логического диска попытаться загрузиться с + него (действия по загрузке с конкретного логического диска начинаются + с метки not_extended), при ошибке загрузки управление передаётся + назад этому циклу (метка next_partition), и поиск подходящего раздела + продолжается. На выходе заполняется одна переменная partition_start, + имеющая смысл начала текущего рассматриваемого логического диска, + но по ходу дела из-за приколов таблиц разделов используются ещё четыре + переменных. cur_partition_ofs - фактически счётчик цикла, формально + указатель на текущий вход в текущей загрузочной записи. Сама + загрузочная запись считывается в память начиная с адреса 3000h. + Три оставшихся нужны для правильной работы с расширенными разделами. + В каждой загрузочной записи помещается не более 4 записей о разделах. + Поэтому главной загрузочной записи, размещающейся в первом физическом + секторе диска, может не хватить, и обычно создаётся так называемый + расширенный раздел с расширенными загрузочными записями, формат + которых почти идентичен главной. Расширенный раздел может быть только + один, но в нём может быть много логических дисков и расширенных + загрузочных записей. Расширенные загрузочные записи организованы + в односвязный список, в каждой такой записи первый вход указывает + на соответствующий логический диск, а второй - на следующую расширенную + загрузочную запись. + При этом в главной загрузочной записи все адреса разделов являются + абсолютными номерами секторов. В расширенных же записях адреса разделов + относительны, причём с разными базами: адрес логического диска + указывается относительно расширенной записи, а адрес следующей + расширенной записи указывается относительно начала расширенного + раздела. Такой разнобой выглядит несколько странно, но имеет место + быть. Три оставшихся переменных содержат: extended_part_start - + начало расширенного раздела; extended_parent - текущая рассматриваемая + расширенная загрузочная запись; extended_part_cur - следующая + загрузочная запись для рассмотрения. + Цикл выглядит так: просматриваются все разделы, указанные в текущей + (главной или расширенной) загрузочной записи; для нормальных разделов + (они же логические диски) происходит переход на not_extended, где + устанавливается partition_start и начинается собственно загрузка + (последующие шаги); при встрече с разделом, тип которого указывает + на расширенность (5 или 0xF), код запоминает начало этого раздела + (в главной загрузочной записи такой тип означает расширенный раздел, + в расширенной - только указатель на следующую расширенную запись, + в обоих случаях он может встретиться только один раз в данной записи); + когда код доходит до конца списка, все нормальные разделы, описываемые + в этой записи, уже просмотрены, так что код с чистой совестью переходит + к следующей расширенной записи. Если он её не встретил, значит, уже + все логические разделы были подвергнуты попыткам загрузиться, и все + безрезультатно, так что выводится ругательство и работа останавливается + (jmp $). + Может возникнуть вопрос, зачем нужна такая сложная схема и почему + нельзя узнать нужный логический диск заранее или хотя бы ограничиться + первым попавшимся логическим диском, не крутя цикл. Так вот, вариант + с предварительным определением нужного раздела в данном случае не + используется, поскольку повлёк бы за собой нетривиальные лишние + действия по установке (в текущем виде установку можно провести вручную, + и она сводится к указанию системному загрузчику на существование + kordldr); кстати, в альтернативной версии загрузки после + Windows-загрузчика, когда установка осуществляется не вручную, а + специальной программой под Windows, используется модифицированная + версия, в которой как раз начальный физический сектор нужного раздела + прописывается установщиком. Сам kordldr не может установить, с какого + раздела его загрузил Windows-загрузчик (и вообще под NT/2000/XP обязан + быть файлом на диске C:\). Вариант с первым попавшимся логическим + диском был реализован в первой версии загрузчика, но по ходу дела + обнаружилось, что таки нужно крутить цикл: во-вторых, может быть + приятным, что сама система может стоять вовсе не на системном C:\, а и + на других дисках; во-первых, диск C: может и не быть первым логическим + разделом - Vista любит создавать скрытый первичный раздел перед + системным, и тогда диск C: становится вторым логическим. +6. Извещает пользователя о том, что происходит попытка загрузки с очередного + логического диска. +7. Читает первый сектор логического диска и определяет файловую систему. + И в FAT, и в NTFS поле со смещением +11 содержит число байт в секторе + и должно совпадать с характеристикой физического носителя, то есть + 200h байт. И в FAT, и в NTFS поле со смещением +13 содержит число + секторов в кластере и должно быть степенью двойки. + Критерий NTFS: поле со смещением +3 содержит строку NTFS и поле со + смещением +16 нулевое (в FAT оно содержит число таблиц FAT и обязано + быть ненулевым). + Критерий FAT: загрузчик вычисляет число кластеров, определяет + предположительный тип (FAT12/FAT16/FAT32) и проверяет байт по смещению + +38 для FAT12/16, +66 для FAT32 (он должен быть равен 0x29). + После определения типа файловой системы извещает пользователя об + определённом типе. Если файловая система не распознана, выдаёт + соответствующее сообщение и переходит к следующему логическому диску. +8a. Для FAT12-томов: засовывает в стек идентификатор файловой системы - + константу '12'; устанавливает указатель на функцию получения следующего + в цепочке FAT кластера на FAT12-обработчик; считывает в память всю + таблицу FAT12 (она не превосходит 0x1800 байт = 6 Кб), при ошибке + чтения пытается использовать другие копии FAT. +8б. Для FAT16-томов: засовывает в стек идентификатор файловой системы - + константу '16'; устанавливает указатель на функцию получения следующего + в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию + о кэше секторов FAT (массив байт с возможными значениями 0 и 1, + означающими, был ли уже загружен соответствующий сектор - всего в + таблице FAT16 не более 0x100 секторов) - ни один сектор ещё не + загружен, все байты нулевые. +8в. Для FAT32-томов: засовывает в стек идентификатор файловой системы - + константу '32'; устанавливает указатель на функцию получения следующего + в цепочке FAT кластера на FAT16-обработчик; инициализирует информацию + о кэше секторов FAT (формат информации описан выше, в распределении + используемой загрузчиком памяти) - ни один сектор ещё не загружен. +8г. Общее для FAT-томов: определяет значения служебных переменных + root_start (первый сектор корневого каталога в FAT12/16, игнорируется + при обработке FAT32-томов), data_start (начало данных с поправкой, + вводимой для того, чтобы кластер N начинался с сектора + N*sectors_per_cluster+data_start), root_clus (первый кластер корневого + каталога в FAT32, 0 в FAT12/16); устанавливает указатель на функцию + загрузки файла на FAT-обработчик. +8д. Для NTFS-томов: засовывает в стек идентификатор файловой системы - + константу 'nt'; определяет значение служебной переменной frs_size + (размер в байтах файловой записи, File Record Segment), для полной + корректности проверяет, что это значение (равное 0x400 байт на всех + реальных NTFS-томах - единственный способ изменить его заключается + в пересоздании всех системных структур вручную) не превосходит 0x1000 + и кратно размеру сектора 0x200 байт; инициализирует кэш файловых + записей - ничего ещё не загружено; считывает первый кластер $MFT + и загружает информацию о расположении на диске всей таблицы $MFT + (атрибут 0x80, $Data); устанавливает указатель на функцию загрузки + файла на NTFS-обработчик. +9. (метка load_secondary) Вызывает функцию загрузки файла для файла вторичного + загрузчика. При обнаружении ошибки переходит на обработчик ошибок с + соответствующим сообщением. +10. Устанавливает регистры для вторичного загрузчика: al='h' (жёсткий диск), + ah=номер диска (для готового бинарника - 0 (BIOS-идентификатор 80h), + может быть изменён путём модификации константы в исходнике или + специальным установщиком), bx=идентификатор файловой системы (берётся + из стека, куда ранее был засунут на шаге 8), ds:si=указатель на + callback-функцию. +11. Передаёт управление вторичному загрузчику дальним переходом на 1000:0000. + +Функция обратного вызова для вторичного загрузчика: + предоставляет возможность чтения файла. +Вход и выход описаны в спецификации на загрузчик. +Чтение файла: +1. Сохраняет стек вызывающего кода и устанавливает свой стек: + ss:sp = 0:3000, bp=dat: пара ss:bp при работе с остальным + кодом должна указывать на 0:dat. +2. Разбирает переданные параметры и вызывает процедуру загрузки файла. +3. Восстанавливает стек вызывающего кода и возвращает управление. + +Вспомогательные процедуры. +Процедура чтения секторов (read): +на входе должно быть установлено: + ss:bp = 0:dat + es:bx = указатель на начало буфера, куда будут прочитаны данные + eax = стартовый сектор (относительно начала логического диска) + cx = число секторов (должно быть больше нуля) +на выходе: es:bx указывает на конец буфера, в который были прочитаны данные, + флаг CF установлен, если возникла ошибка чтения +1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на + устройстве, прибавляя номер первого сектора логического диска, + найденный при переборе дисков. +2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации + CHS-версия: все читаемые секторы были на одной дорожке. + LBA-версия: число читаемых секторов не превосходило 7Fh (требование + спецификации EDD BIOS). +CHS-версия: +3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как + единица плюс остаток от деления абсолютного номера на число секторов + на дорожке; дорожка рассчитывается как остаток от деления частного, + полученного на предыдущем шаге, на число дорожек, а цилиндр - как + частное от этого же деления. Если число секторов для чтения больше, + чем число секторов до конца дорожки, уменьшает число секторов для + чтения. +4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов, + dh=головка, (младшие 6 бит cl)=сектор, + (старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска + и повторяет попытку чтения, всего делается не более трёх попыток + (несколько попыток нужно в случае дискеты для гарантии того, что + мотор раскрутился). Если все три раза происходит ошибка чтения, + переходит на код обработки ошибок с сообщением "Read error". +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. +LBA-версия: +3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей + итерации) до 7Fh. +4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами + push, причём в обратном порядке: стек - структура LIFO, и данные в + стеке хранятся в обратном порядке по отношению к тому, как их туда + клали). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки + ошибок с сообщением "Read error". Очищает стек от пакета, + сформированного на предыдущем шаге. +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. + +Процедура обработки ошибок (find_error_si и find_error_sp): +на входе: указатель на сообщение об ошибке в si либо на верхушке стека +0. Если вызывается find_error_si, она помещает переданный указатель в стек. +1. Если ошибка произошла в процессе работы callback-функции, то + (метка error_in_callback) обработчик просто возвращает управление + вызвавшему коду, рапортуя о ненайденном файле. +2. Если же ошибка произошла до передачи управления вторичному загрузчику, + обработчик выводит сообщение типа "Error: <текущий объект>: <ошибка>" + и (восстановив стек) переходит к следующему логическому диску. + +Процедура чтения файла/атрибута по известному размещению на диске + (read_file_chunk): +на входе должно быть установлено: + ds:si = указатель на информацию о размещении + es:bx = указатель на начало буфера, куда будут прочитаны данные + ecx = лимит числа секторов для чтения, старшее слово должно быть 0 +на выходе: es:bx указывает на конец буфера, в который были прочитаны данные, + флаг CF установлен, если возникла ошибка чтения +1. Определяет, является ли атрибут резидентным (возможно только в NTFS + и означает, что данные файла/атрибута уже были целиком прочитаны при + обработке информации о файле) или нерезидентным (означает, что данные + хранятся где-то на диске, и имеется информация о том, где именно). +2. Для резидентных атрибутов (метка read_file_chunk.resident) просто копирует + данные по месту назначения (с учётом указанного лимита). +3. Для нерезидентных атрибутов информация состоит из пар <размер очередного + фрагмента файла в кластерах, стартовый кластер фрагмента>; процедура + читает фрагменты, пока файл не закончится или пока не будет достигнут + указанный лимит. + +Процедура просмотра кэша (cache_lookup): +на входе должно быть установлено: + eax = искомое значение + ss:si = указатель на структуру-заголовок кэша +на выходе: ss:di = указатель на вход в кэше; флаг CF установлен, если значение + было только что добавлено, и сброшен, если оно уже было в кэше. +1. Просматривает кэш в поисках указанного значения. Если значение найдено + (при этом флаг CF оказывается сброшенным), переходит к шагу 4. +2. Если кэш уже заполнен, удаляет из кэша самый старый вход (он находится в + голове двусвязного списка), иначе добавляет к кэшу ещё один вход. +3. Устанавливает в полученном входе указанное значение. Устанавливает флаг + CF, последующие шаги не меняют состояния флагов. Переходит к шагу 5. +4. Удаляет вход из списка. +5. Добавляет сектор в конец списка (самый новый вход). diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/ntfs.inc b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/ntfs.inc new file mode 100644 index 000000000..413fe9907 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/after_win/ntfs.inc @@ -0,0 +1,587 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + +restore_usa: +; Update Sequence Array restore +; in: ds:bx -> USA-protected structure + push bx + lea di, [bx+1feh] + mov cx, [bx+6] + add bx, [bx+4] + dec cx +@@: + mov ax, [bx+2] + mov [di], ax + inc bx + inc bx + add di, 200h + loop @b + pop bx + ret + +find_attr: +; in: ds:di->file record, ax=attribute +; out: ds:di->attribute or di=0 if not found + add di, [di+14h] +.1: +; attributes' codes are formally dwords, but all of them fit in word + cmp word [di], -1 + jz .notfound + cmp word [di], ax + jnz .continue +; for $DATA attribute, scan only unnamed + cmp ax, 80h + jnz .found + cmp byte [di+9], 0 + jz .found +.continue: + add di, [di+4] + jmp .1 +.notfound: + xor di, di +.found: + ret + +process_mcb_nonres: +; in: ds:si->attribute, es:di->buffer +; out: es:di->buffer end + pushad + pop di + add si, [si+20h] + xor ebx, ebx +.loop: + lodsb + test al, al + jz .done + push invalid_read_request_string + movzx cx, al + shr cx, 4 + jz find_error_sp + xchg ax, dx + and dx, 0Fh + jz find_error_sp + add si, cx + add si, dx + pop ax + push si + dec si + movsx eax, byte [si] + dec cx + jz .l1e +.l1: + dec si + shl eax, 8 + mov al, [si] + loop .l1 +.l1e: + xchg ebp, eax + dec si + movsx eax, byte [si] + mov cx, dx + dec cx + jz .l2e +.l2: + dec si + shl eax, 8 + mov al, byte [si] + loop .l2 +.l2e: + pop si + add ebx, ebp +; eax=length, ebx=disk block + stosd + mov eax, ebx + stosd + cmp di, 0x8000 - 12 + jbe .loop +..attr_overflow: + mov si, fragmented_string + jmp find_error_si +.done: + xor ax, ax + stosw + stosw + push di + popad + ret + +load_attr: +; in: ax=attribute, ds:bx->base record +; out: if found: CF=0, attribute loaded to [freeattr], [freeattr] updated, +; edx=size of attribute in bytes +; out: if not found: CF=1 + mov di, [bp + freeattr - dat] + push ss + pop es + mov byte [es:di], 1 + inc di + cmp di, 0x8000 - 12 + ja ..attr_overflow + or edx, -1 ; file size is not known yet +; scan for attribute + push di + mov di, bx + add di, [di+14h] +@@: + call find_attr.1 + test di, di + jz .notfound1 + cmp byte [di+8], 0 + jnz .nonresident + mov si, di + pop di + push ds + jmp .resident +.aux_resident: + mov ax, ds + mov si, di + pop di ds bx ds edx + push ss + pop es + push ds + mov ds, ax +; resident attribute +.resident: + dec di + mov al, 0 + stosb + mov ax, [si+10h] + stosw + push di + add di, ax + cmp di, 0x8000 - 12 + pop di + ja ..attr_overflow + movzx edx, ax ; length of attribute + xchg ax, cx + add si, [si+14h] + rep movsb + mov [bp + freeattr - dat], di + pop ds + ret +.nonresident: +; nonresident attribute + cmp dword [di+10h], 0 + jnz @b +; read start of data + mov si, di + mov edx, [di+30h] ; size of attribute + pop di + call process_mcb_nonres + sub di, 4 + push di +.notfound1: + pop di + push edx +; $ATTRIBUTE_LIST is always in base file record + cmp ax, 20h + jz .nofragmented +; try to load $ATTRIBUTE_LIST = 20h + push ax + mov ax, 20h + push [bp + freeattr - dat] + mov [bp + freeattr - dat], di + push di + call load_attr + pop di + pop [bp + freeattr - dat] + pop ax + jc .nofragmented + push ds bx + pusha + mov si, di + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + popa + push ss + pop es + xor bx, bx +.1: + cmp [bx], ax + jnz .continue1 +; only unnamed $DATA attributes! + cmp ax, 80h + jnz @f + cmp byte [bx+6], 0 + jnz .continue1 +@@: + cmp dword [bx+10h], 0 + jz .continue1 + cmp dword [bx+8], 0 + jnz @f + dec di + cmp di, [bp + freeattr - dat] + lea di, [di+1] + jnz .continue1 +@@: + push ds di + push ax + mov eax, [bx+10h] + mov ecx, [bx+8] + call read_file_record + pop ax + mov di, [14h] +.2: + call find_attr.1 + cmp byte [di+8], 0 + jz .aux_resident + cmp dword [di+10h], ecx + jnz .2 + mov si, di + mov di, sp + cmp dword [ss:di+8], -1 + jnz @f + push dword [si+30h] ; size of attribute + pop dword [ss:di+8] +@@: + pop di + call process_mcb_nonres + sub di, 4 + pop ds +.continue1: + add bx, [bx+4] + cmp bx, dx + jb .1 + pop bx ds +.nofragmented: + pop edx + dec di + cmp di, [bp + freeattr - dat] + jnz @f + stc + ret +@@: + inc di + xor ax, ax + stosw + stosw + mov [bp + freeattr - dat], di + ret + +read_file_record: +; in: eax = index of record +; out: ds:0 -> record +; find place in cache + push di + push si + mov si, cache1head + call cache_lookup + pop si + pushf + sub di, 3400h + shl di, 10-3 + add di, 0x6000 + mov ds, di + popf + pop di + jnc .noread +; read file record to ds:0 + pushad + push ds + push es + movzx ecx, [bp + frs_size - dat] + shr cx, 9 + mul ecx + push ds + pop es + push ss + pop ds + mov si, 0x4000 + xor bx, bx + push [bp + cur_obj - dat] + mov [bp + cur_obj - dat], mft_string + push es + call read_attr +; initialize cache for $INDEX_ALLOCATION for this record + pop si + push si + sub si, 0x6000 + mov ax, si + shr si, 10-3 + shr ax, 2 + add si, 3480h + add ax, 3500h + mov [si], si + mov [si+2], si + mov [si+4], ax + pop ds + call restore_usa + pop [bp + cur_obj - dat] + pop es + pop ds + popad +.noread: + ret + +read_attr: +; in: eax = offset in sectors, ecx = size in sectors (<10000h), es:bx -> buffer, ds:si -> attribute + push invalid_read_request_string + cmp byte [si], 0 + jnz .nonresident + cmp eax, 10000h shr 9 + jae find_error_sp + shl ax, 9 + shl cx, 9 + cmp ax, [si+2] + jae find_error_sp + cmp cx, [si+2] + ja find_error_sp + add si, 3 + add si, ax + mov di, bx + rep movsb + pop ax + ret +.nonresident: + inc si +.loop: + mov edx, dword [si] + add si, 8 + test edx, edx + jz find_error_sp + imul edx, [bp + sect_per_clust - dat] + sub eax, edx + jnc .loop + add eax, edx + sub edx, eax + push cx + cmp ecx, edx + jb @f + mov cx, dx +@@: + push bx + mov ebx, [si-4] + imul ebx, [bp + sect_per_clust - dat] + add eax, ebx + pop bx + call read + jc ..found_disk_error + mov dx, cx + pop cx + xor eax, eax + sub cx, dx + jnz .loop + pop ax + ret + +load_file_ntfs: +; in: ss:bp = 0:dat +; in: es:bx = address to load file +; in: ds:si -> ASCIIZ name +; in: cx = limit in sectors +; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part has been loaded, bx=2 - file not found +; out: dx:ax = file size (0xFFFFFFFF if file not found) + push es bx cx + mov eax, 5 ; root cluster + mov [bp + cur_obj - dat], root_string +.parse_dir_loop: + push ds si + call read_file_record +; find attributes $INDEX_ROOT, $INDEX_ALLOCATION, $BITMAP + mov ax, [bp + freeattr - dat] + mov [bp + index_root - dat], ax + mov ax, 90h ; $INDEX_ROOT + xor bx, bx + call load_attr + mov si, noindex_string + jc find_error_si + mov ax, [bp + freeattr - dat] + mov [bp + index_alloc - dat], ax + mov ax, 0A0h ; $INDEX_ALLOCATION + call load_attr + jnc @f + mov [bp + index_alloc - dat], bx +@@: + push ds +; search for entry + mov si, [bp + index_root - dat] + push ss + pop ds + push 0x8100 + pop es + xor ecx, ecx + mov cl, 0x78 + xor bx, bx + push es + call read_file_chunk + pop ds + jc ..found_disk_error + test cx, cx + jz ..attr_overflow + mov si, invalid_read_request_string + cmp word [bx+10], 0 + jnz find_error_si +; calculate number of items in cache + mov di, [bx+8] ; subnode_size + mov ax, 0x4000 + sub ax, word [bp + frs_size - dat] + cwd + div di + test ax, ax + jz find_error_si + mov si, invalid_volume_msg + test di, 0x1FF + jnz find_error_si + pop cx + mov [bp + cur_index_seg - dat], cx + shl ax, 3 + sub cx, 6000h + mov si, cx + shr cx, 2 + shr si, 10-3 + add cx, ax + add si, 3480h + mov [bp + cur_index_cache - dat], si + add cx, 3500h + mov [ss:si+6], cx + mov dx, di + add bx, 10h +.scan_record: + add bx, [bx] +.scan: + test byte [bx+0Ch], 2 + jnz .look_child + movzx cx, byte [bx+50h] ; namelen + lea di, [bx+52h] ; name + push ds + pop es + pop si ds + push ds si + xor ax, ax +.1: + lodsb + cmp al, '/' + jnz @f + mov al, 0 +@@: + cmp al, 'A' + jb .nocapital + cmp al, 'Z' + ja .nocapital + or al, 20h +.nocapital: + cmp al, 'a' + jb .notletter + cmp al, 'z' + ja .notletter + or byte [es:di], 20h +.notletter: + scasw + loopz .1 + jb .look_child + ja @f + cmp byte [si], 0 + jz .file_found + cmp byte [si], '/' + jz .file_found +@@: + push es + pop ds + add bx, [bx+8] + jmp .scan +.look_child: + push es + pop ds + test byte [bx+0Ch], 1 + jz .not_found + mov si, [bp + index_alloc - dat] + test si, si + jz .not_found + add bx, [bx+8] + mov eax, [bx-8] + mov es, [bp + cur_index_seg - dat] + push si + mov si, [bp + cur_index_cache - dat] + call cache_lookup + pop si + pushf + mov bx, di + mov bh, 0 + shr bx, 3 + imul bx, dx + add bx, [bp + frs_size - dat] + popf + jnc .noread + push es + push dx + push ss + pop ds + movzx ecx, dx + shr cx, 9 + mul [bp + sect_per_clust - dat] + call read_attr + pop dx + pop es + push es + pop ds + call restore_usa +.noread: + push es + pop ds + add bx, 18h + jmp .scan_record +.not_found: + pop [bp + cur_obj - dat] + mov si, error_not_found + jmp find_error_si +.file_found: + pop [bp + cur_obj - dat] + pop cx + mov ax, [bp + index_root - dat] + mov [bp + freeattr - dat], ax + mov eax, [es:bx] + test byte [es:bx+48h+3], 10h + jz .regular_file + cmp byte [si], 0 + jz ..directory_error + inc si + jmp .parse_dir_loop +.regular_file: + cmp byte [si], 0 + jnz ..notdir_error +; read entry + call read_file_record + xor bx, bx + mov ax, 80h + call load_attr + mov si, nodata_string + jc find_error_si + mov si, [bp + index_root - dat] + mov [bp + freeattr - dat], si + push ss + pop ds + jmp load_file_common_end diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/Tupfile.lua new file mode 100644 index 000000000..75b9668db --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/Tupfile.lua @@ -0,0 +1,2 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("bootsect.asm", "fasm %f %o ", "bootsect.bin") diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.asm new file mode 100644 index 000000000..4694e005e --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.asm @@ -0,0 +1,1024 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + jmp far 0:real_start +; special text +org $+0x7C00 +real_start: +; initialize + xor ax, ax + mov ss, ax + mov sp, 0x7C00 + mov ds, ax + mov es, ax + cld + sti + mov [bootdrive], dl +; check LBA support + mov ah, 41h + mov bx, 55AAh + int 13h + mov si, aNoLBA + jc err_ + cmp bx, 0AA55h + jnz err_ + test cl, 1 + jz err_ +; get file system information +; scan for Primary Volume Descriptor + db 66h + push 10h-1 + pop eax +pvd_scan_loop: + mov cx, 1 + inc eax + mov bx, 0x1000 + call read_sectors + jnc @f +fatal_read_err: + mov si, aReadError +err_: + call out_string + mov si, aPressAnyKey + call out_string + xor ax, ax + int 16h + int 18h + jmp $ +@@: + push ds + pop es + cmp word [bx+1], 'CD' + jnz pvd_scan_loop + cmp word [bx+3], '00' + jnz pvd_scan_loop + cmp byte [bx+5], '1' + jnz pvd_scan_loop +; we have found ISO9660 descriptor, look for type + cmp byte [bx], 1 ; Primary Volume Descriptor? + jz pvd_found + cmp byte [bx], 0xFF ; Volume Descriptor Set Terminator? + jnz pvd_scan_loop +; Volume Descriptor Set Terminator reached, no PVD found - fatal error + mov si, no_pvd + jmp err_ +pvd_found: + add bx, 80h + mov ax, [bx] + mov [lb_size], ax +; calculate number of logical blocks in one sector + mov ax, 800h + cwd + div word [bx] + mov [lb_per_sec], ax +; get location of root directory + mov di, root_location + movzx eax, byte [bx+1Dh] + add eax, [bx+1Eh] + stosd +; get memory size + int 12h + mov si, nomem_str + cmp ax, 71000h / 400h + jb err_ + shr ax, 1 + sub ax, 60000h / 800h + mov [size_rest], ax + mov [free_ptr], 60000h / 800h +; load path table +; if size > 62K => it's very strange, avoid using it +; if size > (size of cache)/2 => avoid using it too + mov ecx, [bx+4] + cmp ecx, 0x10000 - 0x800 + ja nopathtable + shr ax, 1 + cmp ax, 0x20 + jae @f + shl ax, 11 + cmp cx, ax + ja nopathtable +@@: +; size is ok, try to load it + mov [pathtable_size], cx + mov eax, [bx+12] + xor edx, edx + div dword [lb_per_sec] + imul dx, [bx] + mov [pathtable_start], dx + add cx, dx + call cx_to_sectors + xor bx, bx + push 6000h + pop es + call read_sectors + jc nopathtable +; path table has been loaded + inc [use_path_table] + sub [size_rest], cx + add [free_ptr], cx +nopathtable: +; init cache + mov ax, [size_rest] + mov [cache_size], ax + mov ax, [free_ptr] + mov [cache_start], ax +; load secondary loader + mov di, secondary_loader_info + call load_file + test bx, bx + jnz noloader +; set registers for secondary loader + mov ah, [bootdrive] + mov al, 'c' + mov bx, 'is' + mov si, callback + jmp far [si-callback+secondary_loader_info] ; jump to 1000:0000 + +noloader: + mov si, aKernelNotFound + jmp err_ + +read_sectors: +; es:bx = pointer to data +; eax = first sector +; cx = number of sectors + pushad + push ds +do_read_sectors: + push ax + push cx + cmp cx, 0x7F + jbe @f + mov cx, 0x7F +@@: +; create disk address packet on the stack +; dq starting LBA + db 66h + push 0 + push eax +; dd buffer + push es + push bx +; dw number of blocks to transfer (no more than 0x7F) + push cx +; dw packet size in bytes + push 10h +; issue BIOS call + push ss + pop ds + mov si, sp + mov dl, [cs:bootdrive] + mov ah, 42h + int 13h + jc diskreaderr +; restore stack + add sp, 10h +; increase current sector & buffer; decrease number of sectors + movzx esi, cx + mov ax, es + shl cx, 7 + add ax, cx + mov es, ax + pop cx + pop ax + add eax, esi + sub cx, si + jnz do_read_sectors + pop ds + popad + ret +diskreaderr: + add sp, 10h + 2*2 + pop ds + popad + stc +out_string.ret: + ret + +out_string: +; in: ds:si -> ASCIIZ string + lodsb + test al, al + jz .ret + mov ah, 0Eh + mov bx, 7 + int 10h + jmp out_string + +aNoLBA db 'The drive does not support LBA!',0 +aReadError db 'Read error',0 +no_pvd db 'Primary Volume Descriptor not found!',0 +nomem_str db 'No memory',0 +aPressAnyKey db 13,10,'Press any key...',13,10,0 + +load_file: +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found +; out: dx:ax = file size (0xFFFFFFFF if file not found) +; parse path to the file + lea si, [di+6] + mov eax, [cs:root_location] + cmp [cs:use_path_table], 0 + jz parse_dir +; scan for path in path table + push di + push 6000h + pop es + mov di, [cs:pathtable_start] ; es:di = pointer to current entry in path table + mov dx, 1 ; dx = number of current entry in path table, start from 1 + mov cx, [cs:pathtable_size] +pathtable_newparent: + mov bx, dx ; bx = number of current parent in path table: root = 1 +scan_path_table_e: + call is_last_component + jnc path_table_scanned +scan_path_table_i: + cmp word [es:di+6], bx + jb .next + ja path_table_notfound + call test_filename1 + jc .next +@@: + lodsb + cmp al, '/' + jnz @b + jmp pathtable_newparent +.next: +; go to next entry + inc dx + movzx ax, byte [es:di] + add ax, 8+1 + and al, not 1 + add di, ax + sub cx, ax + ja scan_path_table_i +path_table_notfound: + pop di + mov ax, -1 + mov dx, ax + mov bx, 2 ; file not found + ret +path_table_scanned: + movzx eax, byte [es:di+1] + add eax, [es:di+2] + pop di +parse_dir: +; eax = logical block, ds:di -> information structure, ds:si -> file name +; was the folder already read? + push di ds + push cs + pop ds + mov [cur_desc_end], 2000h + mov bx, cachelist +.scan1: + mov bx, [bx+2] + cmp bx, cachelist + jz .notfound + cmp [bx+4], eax + jnz .scan1 +.found: +; yes; delete this item from the list (the following code will append this item to the tail) + mov di, [bx] + push word [bx+2] + pop word [di+2] + mov di, [bx+2] + push word [bx] + pop word [di] + mov di, bx + jmp .scan +.notfound: +; no; load first sector of the folder to get its size + push eax + push si + mov si, 1 + call load_phys_sector_for_lb_force + mov bx, si + pop si + pop eax + jnc @f +; read error - return +.readerr: + pop ds +.readerr2: + pop di + mov ax, -1 + mov dx, ax + mov bx, 3 + ret +@@: +; first item of the folder describes the folder itself +; do not cache too big folders: size < 64K and size <= (total cache size)/2 + cmp word [bx+12], 0 + jnz .nocache + mov cx, [cache_size] ; cx = cache size in sectors + shr cx, 1 ; cx = (cache size)/2 + cmp cx, 0x20 + jae @f + shl cx, 11 + cmp [bx+10], cx + ja .nocache +@@: +; we want to cache this folder; get space for it + mov cx, [bx+10] + call cx_to_sectors + jnz .yescache +.nocache: + push dword [bx+10] + pop dword [cur_nocache_len] + call lb_to_sector + push ds + pop es + pop ds +.nocache_loop: + push eax + mov dx, 1800h + call scan_for_filename_in_sector + mov cx, dx + pop eax + jnc .j_scandone + sub cx, bx + sub word [es:cur_nocache_len], cx + sbb word [es:cur_nocache_len+2], 0 + jb .j_scandone + ja @f + cmp word [es:cur_nocache_len], 0 + jz .j_scandone +@@: + mov cx, 1 + inc eax + push es + mov bx, 1000h + call read_sectors + pop es + jc .readerr2 + jmp .nocache_loop +.j_scandone: + jmp .scandone +.yescache: + push bx + mov bx, [cachelist.head] +.freeloop: + cmp cx, [size_rest] + jbe .sizeok +@@: +; if we are here: there is not enough free space, so we must delete old folders' data +; N.B. We know that after deleting some folders the space will be available (size <= (total cache size)/2). +; one loop iteration: delete data of one folder + pusha + mov dx, [bx+10] + mov es, dx ; es = segment of folder data to be deleted + xor di, di + mov ax, [bx+8] + add ax, 0x7FF + rcr ax, 1 + shr ax, 10 + push ax + shl ax, 11-4 ; get number of paragraphs in folder data to be deleted + mov cx, [cache_size] + add cx, [cache_start] + push ds + push ax + add ax, dx + mov ds, ax + pop ax + shl cx, 11-4 + sub cx, dx ; cx = number of paragraphs to be moved + push si + xor si, si +; move cx paragraphs from ds:si to es:di to get free space in the end of cache +@@: + sub cx, 1000h + jbe @f + push cx + mov cx, 8000h + rep movsw + mov cx, ds + add cx, 1000h + mov ds, cx + mov cx, es + add cx, 1000h + mov es, cx + pop cx + jmp @b +@@: + add cx, 1000h + shl cx, 3 + rep movsw + pop si + pop ds +; correct positions in cache for existing items + mov cx, 80h + mov di, 8400h +.correct: + cmp [di+10], dx + jbe @f + sub [di+10], ax +@@: + add di, 12 + loop .correct +; some additional space is free now + pop ax + add [size_rest], ax + sub [free_ptr], ax +; add cache item to the list of free items + mov dx, [bx] + mov ax, [free_cache_item] + mov [bx], ax + mov [free_cache_item], bx + mov bx, dx +; current iteration done + popa + jmp .freeloop +.sizeok: + mov [cachelist.head], bx + mov word [bx+2], cachelist +; allocate new item in cache + mov di, [free_cache_item] + test di, di + jz .nofree + push word [di] + pop [free_cache_item] + jmp @f +.nofree: + mov di, [last_cache_item] + add [last_cache_item], 12 +@@: + pop bx + push si di +; mov [di+4], eax ; start of folder + scasd + stosd + push ax + mov ax, [free_ptr] + shl ax, 11-4 + mov [di+10-8], ax + mov es, ax + pop ax + add [free_ptr], cx + sub [size_rest], cx +; read folder data +; first sector is already in memory, 0000:bx + pusha + mov cx, [bx+10] + mov [di+8-8], cx ; folder size in bytes + mov si, bx + xor di, di + mov cx, 0x1800 + sub cx, si + rep movsb + pop ax + push di + popa +; read rest of folder + mov esi, dword [lb_per_sec] + add eax, esi + dec si + not si + and ax, si + mov si, word [bx+10] + mov bx, di + pop di + sub si, bx + jbe @f + mov [cur_limit], esi + call read_many_bytes + pop si + jnc .scan + jmp .readerr +@@: + pop si +.scan: +; now we have required cache item; append it to the end of list + mov bx, [cachelist.tail] + mov [cachelist.tail], di + mov [di+2], bx + mov word [di], cachelist + mov [bx], di +; scan for given filename + mov es, [di+10] + mov dx, [di+8] + pop ds + xor bx, bx + call scan_for_filename_in_sector +.scandone: + push cs + pop es + mov bx, 2000h + cmp bx, [es:cur_desc_end] + jnz filefound +j_notfound: + jmp path_table_notfound +filefound: +@@: + lodsb + test al, al + jz @f + cmp al, '/' + jnz @b +@@: + mov cl, [es:bx+8] + test al, al + jz @f +; parse next component of file name + test cl, 2 ; directory? + jz j_notfound + mov eax, [es:bx] + pop di + jmp parse_dir +@@: + test cl, 2 ; directory? + jnz j_notfound ; do not allow read directories as regular files +; ok, now load the file + pop di + les bx, [di] + call normalize + movzx esi, word [di+4] ; esi = limit in 4K blocks + shl esi, 12 ; esi = limit in bytes + push cs + pop ds + mov [cur_limit], esi + mov di, 2000h +loadloop: + and [cur_start], 0 +.loadnew: + mov esi, [cur_limit] + mov eax, [cur_start] + add esi, eax + mov [overflow], 1 + sub esi, [di+4] + jb @f + xor esi, esi + dec [overflow] +@@: + add esi, [di+4] ; esi = number of bytes to read + mov [cur_start], esi + sub esi, eax + jz .loadcontinue + xor edx, edx + div dword [lb_size] ; eax = number of logical blocks to skip, + mov [first_byte], dx; [first_byte] = number of bytes to skip in 1st block + cmp byte [di+10], 0 + jnz .interleaved + add eax, [di] +; read esi bytes from logical block eax to buffer es:bx + call read_many_bytes.with_first + jc .readerr3 +.loadcontinue: + mov [cur_chunk], di + add di, 11 + cmp di, [cur_desc_end] + jae @f + cmp [cur_limit], 0 + jnz loadloop +@@: + mov bx, [overflow] +.calclen: +; calculate length of file + xor ax, ax + xor dx, dx + mov di, 2000h +@@: + add ax, [di+4] + adc dx, [di+6] + add di, 11 + cmp di, [cur_desc_end] + jb @b + ret +.interleaved: + mov [cur_unit_limit], esi + push esi +; skip first blocks + movzx ecx, byte [di+9] ; Unit Size + movzx esi, byte [di+10] ; Interleave Gap + add si, cx + mov edx, [di] +@@: + sub eax, ecx + jb @f + add edx, esi + jmp @b +@@: + add ecx, eax ; ecx = number of logical blocks to skip + lea eax, [ecx+edx] ; eax = first logical block + pop esi +.interleaved_loop: +; get number of bytes in current file unit + push eax + movzx eax, byte [di+9] + sub ax, cx + imul eax, dword [lb_size] + cmp eax, esi + ja .i2 +.i1: + xchg esi, eax +.i2: + pop eax + sub [cur_unit_limit], esi + push eax +; read esi bytes from logical block eax to buffer es:bx + call read_many_bytes.with_first + pop eax + jnc @f +.readerr3: + mov bx, 3 + jmp .calclen +@@: + mov esi, [cur_unit_limit] + test esi, esi + jz .loadcontinue + movzx ecx, byte [di+9] ; add Unit Size + add cl, byte [di+10] ; add Interleave Gap + adc ch, 0 + add eax, ecx + xor cx, cx + mov [first_byte], cx + jmp .interleaved_loop + +cx_to_sectors: + add cx, 7FFh + rcr cx, 1 + shr cx, 10 + ret + +is_last_component: +; in: ds:si -> name +; out: CF set <=> current component is not last (=> folder) + push si +@@: + lodsb + test al, al + jz @f + cmp al, '/' + jnz @b + stc +@@: + pop si + ret + +test_filename1: +; in: ds:si -> filename, es:di -> path table item +; out: CF set <=> no match + pusha + mov cl, [es:di] + add di, 8 + jmp test_filename2.start +test_filename2: +; in: ds:si -> filename, es:bx -> directory item +; out: CF set <=> no match + pusha + mov cl, [es:bx+32] + lea di, [bx+33] +.start: + mov ch, 0 +@@: + lodsb + test al, al + jz .test1 + cmp al, '/' + jz .test1 + call toupper + mov ah, al + mov al, [es:di] + call toupper + inc di + cmp al, ah + loopz @b + jnz .next1 +; if we have reached this point: current name is done + lodsb + test al, al + jz .ret + cmp al, '/' + jz .ret +; if we have reached this point: current name is done, but input name continues +; so they do not match + jmp .next1 +.test1: +; if we have reached this point: input name is done, but current name continues +; "filename.ext;version" in ISO-9660 represents file "filename.ext" +; "filename." and "filename.;version" are also possible for "filename" + cmp byte [es:di], '.' + jnz @f + inc di + dec cx + jz .ret +@@: + cmp byte [es:di], ';' + jnz .next1 + jmp .ret +.next1: + stc +.ret: + popa + ret + +toupper: +; in: al=symbol +; out: al=symbol in uppercase + cmp al, 'a' + jb .ret + cmp al, 'z' + ja .ret + sub al, 'a'-'A' +.ret: + ret + +scan_for_filename_in_sector: +; in: ds:si->filename, es:bx->folder data, dx=limit +; out: CF=0 if found + push bx +.loope: + push bx +.loop: + cmp bx, dx + jae .notfound + cmp byte [es:bx], 0 + jz .loopd + test byte [es:bx+25], 4 ; ignore files with Associated bit + jnz .next + call test_filename2 + jc .next + push ds es di + push es + pop ds + push cs + pop es + mov di, [es:cur_desc_end] + movzx eax, byte [bx+1] + add eax, [bx+2] + stosd ; first logical block + mov eax, [bx+10] + stosd ; length + mov al, [bx+25] + stosb ; flags + mov ax, [bx+26] + stosw ; File Unit size, Interleave Gap size + mov [es:cur_desc_end], di + cmp di, 3000h + pop di es ds + jae .done + test byte [es:bx+25], 80h + jz .done +.next: + add bl, [es:bx] + adc bh, 0 + jmp .loop +.loopd: + mov ax, bx + pop bx +@@: + add bx, [cs:lb_size] + jz .done2 + cmp bx, ax + jb @b + jmp .loope +.notfound: + stc +.done: + pop bx +.done2: + pop bx + ret + +lb_to_sector: + xor edx, edx + div dword [lb_per_sec] + ret + +load_phys_sector_for_lb_force: +; in: eax = logical block, ds=0 +; in: si=0 - accept 0 logical blocks, otherwise force read at least 1 +; out: 0000:1000 = physical sector data; si -> logical block +; out: eax = next physical sector +; out: CF=1 if read error +; destroys cx +; this procedure reads 0-3 or 1-4 logical blocks, up to the end of physical sector + call lb_to_sector + or si, dx + jnz @f + mov si, 1800h + jmp .done +@@: + mov si, 1000h + imul dx, [lb_size] + add si, dx + mov cx, 1 + push es bx + push ds + pop es + mov bx, 1000h + call read_sectors + pop bx es + inc eax +.done: + ret + +normalize: +; in: es:bx = far pointer +; out: es:bx = normalized pointer (i.e. 0 <= bx < 0x10) + push ax bx + mov ax, es + shr bx, 4 + add ax, bx + mov es, ax + pop bx ax + and bx, 0x0F + ret + +read_many_bytes: + and [first_byte], 0 +read_many_bytes.with_first: +; read esi bytes from logical block dx:ax to buffer es:bx +; out: CF=1 <=> disk error + push di +; load first physical sector + push bx si + mov si, [first_byte] + call load_phys_sector_for_lb_force + jnc @f + pop si bx +.ret: + pop di + ret +@@: + add si, [first_byte] + mov ecx, 1800h + sub cx, si + mov ebx, esi + pop bx + sub ebx, ecx + jnc @f + add cx, bx + xor ebx, ebx +@@: + pop di + sub [cur_limit], ecx + rep movsb + mov esi, ebx + mov bx, di + call normalize +; load other physical sectors +; read esi bytes from physical sector eax to buffer es:bx + test esi, esi + jz .ret + push esi + add esi, 0x7FF + and si, not 0x7FF + cmp esi, [cur_limit] + jbe .okplace +.noplace: + sub esi, 800h +.okplace: + shr esi, 11 ; si = number of sectors + mov cx, si + jz @f + call read_sectors +@@: + pop esi + jc .ret + movzx ecx, cx + add eax, ecx + shl ecx, 11 + sub [cur_limit], ecx + sub esi, ecx + jc .big + jz .nopost + push bx es + push ds + pop es + mov bx, 1000h + mov cx, 1 + call read_sectors + pop es di + jc .ret2 + mov cx, si + mov si, 1000h + sub word [cur_limit], cx + sbb word [cur_limit+2], 0 + rep movsb + mov bx, di + call normalize +.nopost: + clc +.ret2: + pop di + ret +.big: + mov ax, es + sub ax, 80h + mov es, ax + add bx, 800h + add bx, si + call normalize + sub [cur_limit], esi + jmp .nopost + +; Callback function for secondary loader +callback: +; in: ax = function number; only function 1 is defined for now + dec ax + jz callback_readfile + dec ax + jz callback_continueread + stc ; unsupported function + retf + +callback_readfile: +; function 1: read file +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error +; out: dx:ax = file size (0xFFFFFFFF if file was not found) + call load_file + clc ; function is supported + retf + +callback_continueread: +; function 2: continue to read file +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=3 - read error +; out: dx:ax = file size + les bx, [di] + call normalize + movzx esi, word [di+4] ; si = limit in 4K blocks + shl esi, 12 ; bp:si = limit in bytes + push cs + pop ds + mov [cur_limit], esi + mov di, [cur_chunk] + call loadloop.loadnew + clc ; function is supported + retf + +secondary_loader_info: + dw 0, 0x1000 + dw 0x30000 / 0x1000 + db 'kernel.mnt',0 +aKernelNotFound db 'Fatal error: cannot load the kernel',0 + +align 2 +cachelist: +.head dw cachelist +.tail dw cachelist +free_cache_item dw 0 +last_cache_item dw 0x8400 + +use_path_table db 0 +bootdrive db ? +align 2 +lb_size dw ? ; Logical Block size in bytes + dw 0 ; to allow access dword [lb_size] +lb_per_sec dw ? ; Logical Blocks per physical sector + dw 0 ; to allow access dword [lb_per_sec] +free_ptr dw ? ; first free block in cache (cache block = sector = 0x800 bytes) +size_rest dw ? ; free space in cache (in blocks) +cache_size dw ? +cache_start dw ? +pathtable_size dw ? +pathtable_start dw ? +root_location dd ? +cur_desc_end dw ? +cur_nocache_len dd ? +cur_limit dd ? +cur_unit_limit dd ? +overflow dw ? +cur_chunk dw ? +first_byte dw ? +cur_start dd ? + +times 83FCh-$ db 0 + db 43h +; just to make file 2048 bytes long :) + db 'd' xor 'i' xor 'a' xor 'm' xor 'o' xor 'n' xor 'd' + + dw 0xAA55 ; this is not required for CD, but to be consistent... diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.txt b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.txt new file mode 100644 index 000000000..7fedad177 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/bootsect.txt @@ -0,0 +1,418 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + Sector not found. N. N.N.N. N.N.N.N.N.N.N. N.N. N.N.N.N.N.N.? + +Бутсектор для загрузки с CD/DVD с файловой системой ISO-9660. +(ISO-9660 и её расширения - стандарт для CD; DVD может использовать +либо ISO-9660, либо UDF.) + +===================================================================== + +Требования для работы: +1) Сам бутсектор и все используемые файлы должны быть читабельны. +2) Минимальный процессор - 80386. +3) В системе должно быть как минимум 452K свободной базовой памяти. + +===================================================================== + +Документация в тему (ссылки проверялись на валидность 14.09.2008): + стандарт ISO-9660: http://www.ecma-international.org/publications/files/ECMA-ST/Ecma-119.pdf + стандарт загрузочного CD: http://www.phoenix.com/NR/rdonlyres/98D3219C-9CC9-4DF5-B496-A286D893E36A/0/specscdrom.pdf + официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf + то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf + описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html + официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf + +===================================================================== + +Схема используемой памяти: + 1000-1800 временный буфер для чтения одиночных секторов + ...-7C00 стек + 7C00-8400 код бутсектора + 8400-8A00 информация о кэше для папок: массив входов следующего + формата: + dw следующий элемент в L2-списке закэшированных папок, + упорядоченном по времени использования + (голова списка - самый старый); + dw предыдущий элемент в том же списке; + dd первый сектор папки; + dw размер папки в байтах; + dw сегмент кэша + 60000-... содержимое Path Table, если она используется + + кэш для папок; + точный размер определяется размером доступной + физической памяти - как правило, непосредственно + перед A0000 размещается EBDA, Extended BIOS Data Area + +===================================================================== + +Основной процесс загрузки. +Точка входа (start): получает управление от BIOS при загрузке, при этом + dl содержит идентификатор диска, с которого идёт загрузка +1. При передаче управления загрузочному коду в случае CD/DVD пара cs:ip + равна не 0:7C00, а на 07C0:0000. Поэтому сначала загрузчик делает + дальний прыжок на самого себя с целью получить cs=0 (в некоторых + местах используется адресация переменных загрузчика через cs, поскольку + и ds, и es могут быть заняты под другие сегменты). +2. Настраивает стек ss:sp = 0:7C00 (непосредственно перед основным кодом) + и сегментные регистры ds=es=0. Форсирует сброшенный флаг направления + и разрешённые прерывания. Сохраняет идентификатор загрузочного диска + в специальную переменную. +3. Проверяет поддержку LBA. Для CD/DVD носителя BIOS обязана предоставлять + LBA-функции. +4. Ищет описатель тома CD (Primary Volume Descriptor, PVD): по стандарту + ISO9660 со смещения 10h начинается цепочка описателей тома, + завершающаяся специальным описателем (Volume Descriptor Set + Terminator). Код по очереди считывает все сектора, пока не наткнётся + либо на искомый описатель, либо на терминатор. Во втором случае + выдаётся соответствующее сообщение, и загрузка прекращается. +Вообще говоря, в случае мультисессионных CD основной каталог содержимого CD + располагается в последней сессии. И спецификация ElTorito загрузочного + CD оперирует также с последней сессией. Однако на практике оказывается, + что: во-первых, реальные BIOSы не понимают мультисессионных CD и + всегда используют первую сессию; во-вторых, BIOSовский int 13h просто + не позволяет получить информацию о последней сессии. В связи с этим + загрузчик также использует первую сессию. (В-третьих, в одной из BIOS + обнаружилась заготовка, которая в случае запроса сектора 10h, в котором + во всех нормальных случаях и располагается PVD, перенаправляет его + на сектор 10h+(начало сессии). Если бы этот BIOS ещё и грузился с + последней сессии, то благодаря заготовке загрузчик без всяких + модификаций также читал бы последнюю сессию.) +5. (метка pvd_found) Считывает из PVD некоторую информацию о томе во + внутренние переменные: размер логического блока (согласно спецификации, + должен быть степенью двойки от 512 до размера логического сектора, + равного 2048 для CD и DVD); положение на диске корневой папки; + вычисляет число блоков в секторе (из предыдущего примечания следует, + что оно всегда целое и само является степенью двойки). +6. Получает размер базовой памяти вызовом int 12h; на его основе вычисляет + размер пространства, которое может использовать загрузчик (от + адреса 6000:0000 до конца доступной памяти). +7. Загружает таблицу путей CD (Path Table) - область данных, которая содержит + базовую информацию обо всех папках на диске. Если таблица слишком + велика (больше 62K или больше половины доступной памяти), то она + игнорируется. Если таблица путей недоступна, то запрос типа + dir1/dir2/dir3/file приведёт к последовательному разбору корневой + папки и папок dir1,dir2,dir3; если доступна, то достаточно разобрать + саму таблицу путей (где записано положение папки dir1/dir2/dir3) + и папку dir3. Если таблица загружена, то соответственно уменьшается + объём оставшейся доступной памяти и увеличивается указатель на + свободную область. +8. Запоминает общий размер и начало кэша для папок (вся оставшаяся после п.7 + доступная память отводится под этот кэш). +9. Выдаёт запрос на чтение файла вторичного загрузчика kord/loader. При ошибке + печатает соответствующее сообщение и прекращает загрузку с CD. +10. Устанавливает регистры для вторичного загрузчика: al='c' идентифицирует + тип устройства - CD/DVD; ah=BIOS-идентификатор диска; bx='is' + идентифицирует файловую систему ISO-9660; ds:si указывает на + callback-функцию, которую может вызывать вторичный загрузчик. +11. Передаёт управление вторичному загрузчику, совершая дальний прыжок + на адрес, куда kord/loader был загружен. + +Функция обратного вызова для вторичного загрузчика (callback): + предоставляет возможность чтения файла. +Вход и выход описаны в спецификации на загрузчик. +Перенаправляет запрос соответствующей локальной процедуре (load_file при + первом запросе на загрузку файла, loadloop.loadnew при последующих + запросах на продолжение загрузки файла). + +Вспомогательные процедуры. +Код обработки ошибок (err): +1. Выводит строку с сообщением об ошибке. +2. Выводит строку "Press any key...". +3. Ждёт нажатия any key. +4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё. +5. Для подстраховки зацикливается. + +Процедура чтения секторов (read_sectors): +на входе должно быть установлено: + es:bx = указатель на начало буфера, куда будут прочитаны данные + eax = стартовый сектор + cx = число секторов +на выходе: + es:bx указывает на конец буфера, в который были прочитаны данные + если произошла ошибка чтения, флаг CF установлен +1. В цикле (шаги 2-4) читает секторы, следит за тем, чтобы на каждой итерации + число читаемых секторов не превосходило 7Fh (требование спецификации + EDD BIOS). +2. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей + итерации) до 7Fh. +3. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами + push, причём в обратном порядке: стек - структура LIFO, и данные в + стеке хранятся в обратном порядке по отношению к тому, как их туда + клали). +4. Вызывает BIOS. Если BIOS рапортует об ошибке, очищает стек, + устанавливает CF=1 и выходит из процедуры. + Очищает стек от пакета, сформированного на предыдущем шаге. +5. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 2. + +Процедура вывода на экран ASCIIZ-строки (out_string): +на входе: ds:si -> строка +В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh. + +Процедура загрузки файла (load_file): +на входе: + ds:di = указатель на информационную структуру, описанную в спецификации + на загрузчик, а также в комментариях к коду +на выходе: + bx = статус: 0=успех, 1=файл слишком большой, прочитана только часть, + 2=файл не найден, 3=ошибка чтения + dx:ax = размер файла, 0xFFFFFFFF, если файл не найден +1. Если подготовительный код загрузил таблицу путей, то ищет папку в таблице, + иначе переходит сразу к шагу 4, установив eax = начальный блок + корневой папки. +2. Устанавливает es:di на начало таблицы путей. Ограничение на размер + гарантирует, что вся таблица помещается в сегменте 6000h. + Инициализирует dx (в котором будет хранится номер текущего входа в + таблице, считая с 1), cx (размер оставшегося участка таблицы), + bx (номер входа, соответствующего родительской папке для текущего + рассматриваемого участка пути). +3. В цикле ищет вход с нужным родительским элементом и нужным именем. Элементы + таблицы путей упорядочены (подробно о порядке написано в спецификации), + так что если родительский элемент для очередного входа больше нужного, + то нужного входа в таблице нет совсем, и в этом случае происходит + выход из процедуры с bx=2, ax=dx=0xFFFF. Если обнаружился элемент, + соответствующий очередной папке в запрошенном пути, то на рассмотрение + выносится следующая компонента пути. Если эта компонента последняя, + то осталось найти файл в папке, и код переходит к пункту 4, + установив eax = начальный блок этой папки. Если же нет, то эта + компонента должна задавать имя папки, и код возвращается к пункту 3, + скорректировав указатель на имя ds:si и номер родительского входа bx. +4. (parse_dir) На этом шаге заданы начальный логический блок папки в eax + и указатель на имя файла относительно этой папки в ds:si. Если + папку искали по таблице путей, то имя файла уже не содержит подпапок; + если же нет, то подпапки вполне возможны. +5. Файлы в ISO-9660 могут состоять из нескольких кусков (File Section), каждый + из которых задаётся отдельным входом в папке. Информация обо всех + таких кусках при просмотре папки запоминается в области, начинающейся + с адреса 0000:2000. Переменная cur_desc_end содержит указатель на + конец этой области, он же указатель, куда будет помещена информация + при обнаружении следующего входа. (Папки, согласно спецификации, + должны задаваться одним куском.) +6. Код сначала ищет запрошенную папку в кэше папок. +7. (parse_dir.found) Если папка уже есть в кэше, то она удаляется из списка, + отсортированного по давности последнего обращения и код переходит к + п.15. (Следующим действием станет добавление папки в конец списка.) +8. (parse_dir.notfound) Если же папки нет в кэше, то её придётся загружать + с диска. Сначала загружается первый сектор (физический сектор, + содержащий первый логический блок). При ошибке ввода/вывода + происходит немедленный выход из процедуры с bx=3, dx=ax=0xFFFF. + Первый элемент папки содержит информацию о самой этой папке, конкретно + загрузчик интересуется её размером. +9. Если размер папки слишком большой (больше или равен 64K либо больше половины + общего размера кэша), то кэшироваться она не будет. В этом случае код + считывает папку посекторно во временный буфер (0000:1000) и посекторно + сканирует на наличие запрошенного имени, пока не найдёт такого имени + или пока не кончатся данные. (Цикл начинается со сканирования, + поскольку первая часть данных уже прочитана.) В конце код переходит + к п.17. +10. (parse_dir.yescache) Если принято решение о кэшировании папки, то нужно + обеспечить достаточное количество свободного места. Для этого может + понадобиться выкинуть какое-то количество старых данных (цикл + parse_dir.freeloop). Но если просто выкидывать, то, вообще говоря, + свободное пространство окажется разорванным на несколько фрагментов. + Поэтому при выкидывании какой-то папки из кэша загрузчик перемещает + все следующие за ней данные назад по памяти и соответственно + корректирует информацию о местонахождении данных в информации о кэше. + При этом новое пространство всегда добавляется в конец доступной + памяти. Цикл выкидывания продолжается, пока не освободится место, + достаточное для хранения папки. Из-за ограничений на размер кэшируемых + папок в конце концов место найдётся. +11. Выделяется новый элемент кэша. Все удалённые на шаге 10 элементы + организуются в единый список свободных элементов; если он непуст, + то очередной элемент берётся из этого списка; если же пуст, то + берётся совсем новый элемент из области памяти, предназначенной для + элементов кэша. +12. В новом элементе заполняются поля начального блока, сегмента с данными, + размера в байтах. +13. Уже прочитанные данные первого физического сектора пересылаются на + законное место в кэше. +14. Если все данные не исчерпываются первым сектором, то догружаются оставшиеся + данные с диска. При ошибке чтения, как и раньше, происходит выход из + процедуры с bx=3, ax=dx=0xFFFF. +15. (parse_dir.scan) Новый элемент добавляется в конец списка всех элементов + кэша. +16. Загрузчик ищет запрошенное имя в загруженных данных папки. + (Из-за ограничений на размер кэшируемой папки все данные располагаются + в одном сегменте.) +17. (parse_dir.scandone) Если в процессе сканирования папки не было найдено + никаких кусков файла, то cur_desc_end такой же, каким был вначале. + В этом случае процедура рапортует о ненайденном файле и выходит. +18. (filefound) Пропускает текущую компоненту имени. Если она была не последней + (то есть подпапкой, в которой нужно производить дальнейший поиск), + то код проверяет, что найденный вход - действительно подпапка, + устанавливает новый стартовый блок и возвращается к п.4. + Если же последней, то код проверяет, что найденный вход - регулярный + файл и начинает загрузку файла. +19. Нормализует указатель, по которому требуется прочитать файл. Под + нормализацией понимается преобразование типа + 1234:FC08 -> (1234+0FC0):0008, которое не меняет суммарного адреса, + но гарантирует отсутствие переполнений: в приведённом примере попытка + переслать 400h байт по rep movsb приведёт к тому, что последние 8 + байт запишутся не в нужное место, а на 64K раньше. Далее нормализация + будет производиться после каждой пересылки. В cur_limit помещает + предельный размер для чтения в байтах. +20. (loadloop) В цикле по найденным фрагментам файла загружает эти фрагменты + (пункты 21-27). +21. Обнуляет переменную [cur_start], имеющую смысл числа байт, которое + нужно пропустить с начала фрагмента. +22. (loadloop.loadnew) На эту метку управление может попасть либо с предыдущего + шага, либо напрямую из callback-процедуры при запросе на продолжение + чтения. Для этого и нужна вышеупомянутая переменная [cur_start] - + при продолжении чтения, прервавшегося из-за конца буфера посередине + фрагмента, там будет записано соответствующее значение. +23. Определяет текущую длину (хранится в esi) как минимум из длины фрагмента + и максимальной длины остатка. Если второе строго меньше, то + запоминает, что файл слишком большой и прочитан только частично. + Определяет новое значение числа прочитанных байт во фрагменте + для возможных будущих вызовов [cur_start]. +24. Переводит пропускаемое число байт в число логических блоков и байт + в первом блоке, последнее число записывает в переменную [first_byte], + откуда её позднее достанет read_many_bytes.with_first. +25. Если фрагмент записан в обычном режиме (non-interleaved mode), то код + определяет начальный блок фрагмента и вызывает вспомогательную функцию + чтения блоков. При ошибке чтения устанавливает bx=3 (код ошибки чтения) + и выходит из цикла к п.28. +26. Если фрагмент записан в чередуемом режиме (interleaved mode), то сначала + код пропускает нужное количество непрерывных частей, а потом + в цикле загружает непрерывные части с помощью той же функции, + в промежутках между частями увеличивая номер начального блока. + Пока не кончится фрагмент или пока не наберётся запрошенное число байт. + При ошибке чтения делает то же самое, что и в предыдущем случае. +27. (loadloop.loadcontinue) Если фрагменты ещё не кончились и предельный размер + ещё не достигнут, переходит к следующему фрагменту и п.20. В противном + случае устанавливает bx=0 либо bx=1 в зависимости от того, было ли + переполнение в п.23. +28. (loadloop.calclen) Подсчитывает общую длину файла, суммируя длины всех + фрагментов. + +Процедура проверки, является ли текущая компонента имени файла последней + (is_last_component): +на входе: ds:si = указатель на имя +на выходе: флаг CF установлен, если есть последующие компоненты +В цикле загружает символы имени в поисках нулевого и '/'; если нашёлся первый, + то выходит (при этом CF=0); если нашёлся второй, то устанавливает CF + и выходит. + +Процедуры проверки на совпадение текущей компоненты имени файла с именем +текущего элемента (test_filename1 для таблицы путей, test_filename2 для папки): +на входе: ds:si = указатель на имя, es:di = указатель на элемент + таблицы путей для test_filename1, папки для test_filename2 +на выходе: CF установлен, если имена не совпадают +В цикле проверяет совпадение приведённых к верхнему регистру очередных символов + имён файла и элемента. Условия выхода из цикла: закончилось имя файла + в ds:si (то есть, очередной символ - нулевой либо '/') - совпадение + возможно только в ситуации типа имени "filename.ext" и элемента + "filename.ext;1" (в ISO9660 ";1" - версия файла, элементы с одинаковыми + именами в папке отсортированы по убыванию версий); + несовпадение символов - означает, что имена не совпадают; + закончилось имя элемента - нужно проверить, закончилось ли при этом имя + файла, и в зависимости от этого принимать решение о совпадении. + +Процедура приведения символа в верхний регистр (toupper): +на входе: ASCII-символ +на выходе: тот же символ в верхнем регистре (он сам, если понятие регистра к + нему неприменимо) +Из символов в диапазоне 'a' - 'z' включительно вычитает константу 'a'-'A', + остальные символы не трогает. + +Процедура поиска файла в данных папки (scan_for_filename_in_sector): +на входе: + ds:si = указатель на имя файла + es:bx = указатель на начало данных папки + es:dx = указатель на конец данных папки +на выходе: + CF сброшен, если найден финальный фрагмент файла + (и дальше сканировать папку не нужно) + в область для информации о фрагментах файла записывается найденное +В цикле просматривает все входы папки, пропуская те, у которых установлен + бит Associated (это специальные входы, дополняющие основные). Если + имя очередного входа совпадает с именем файла, то запоминает новый + фрагмент. Если фрагмент финальный (не установлен бит Multi-Extent), + то код выходит с CF=0. Если достигнут конец данных, то код выходит + с CF=1. Если очередной вход нулевой (первый байт настоящего входа + содержит длину и не может быть нулём), то процедура переходит к + рассмотрению следующего логического блока. При этом потенциально + возможно переполнение при добавлении размера блока; поскольку такой + сценарий означает, что процедура вызвана для кэшированной папки + с размером почти 64K и началом данных bx=0 (это свойство вызывающего + кода), а размер блока - степень двойки, то после переполнения всегда + bx=0, так что это можно обнаружить по взведённому ZF после сложения; + в этом случае также происходит выход (а после переполнения CF=1). + +Процедура перевода логического блока в номер сектора: +на входе: eax = логический блок +на выходе: eax = физический сектор, dx = номер логического блока в секторе +Осуществляет обычное деление 32-битного числа на 32-битное (число логических + блоков в секторе, хранящееся во внутренней переменной). + +Процедура загрузки физического сектора, содержащего указанный логический блок + (load_phys_sector_for_lb_force): +на входе: eax = логический блок; + si - индикатор, задающий, следует ли читать данные в случае, + если логический блок начинается с начала физического: + si = 0 - не нужно, si ненулевой - нужно +на выходе: + физический сектор загружен по адресу 0000:1000 + si указывает на данные логического блока + CF установлен при ошибке чтения +Преобразует предыдущей процедурой номер логического блока в номер физического + сектора и номер логического блока внутри сектора; если последняя + величина нулевая и никаких действий в этом случае не запрошено (si=0), + то ничего и не делает; иначе устанавливает si в соответствии с ней + и читает сектор. + +Процедуры чтения нужного числа байт из непрерывной цепочки логических блоков + (read_many_bytes и read_many_bytes.with_first): +на входе: + eax = логический блок + esi = число байт для чтения + es:bx = указатель на начало буфера, куда будут прочитаны данные + cur_limit = размер буфера (не меньше esi) +на выходе: + es:bx указывает на конец буфера, в который были прочитаны данные + если произошла ошибка чтения, флаг CF установлен + cur_limit соответствующим образом уменьшен +Отличие двух процедур: вторая дополнительно принимает во внимание переменную + [first_byte], начиная чтение первого блока со смещения [first_byte]; + соответственно, первая читает блок с начала, обнуляя [first_byte] + при входе. +1. Отдельно считывает первый физический сектор во временную область 0000:1000, + если первый логический блок начинается не с начала сектора. При + ошибке чтения выходит из процедуры. +2. Пересылает нужную часть данных (возможно, 0 байт), прочитанных в п.1, + в буфер. Нормализует указатель на буфер. +3. Если все необходимые данные уже прочитаны, выходит из процедуры. +4. Дальнейшие данные находятся в нескольких физических секторах, при этом, + возможно, последний сектор считывать нужно не целиком. +5. Если в буфере есть место для считывания всех секторов, то сразу читаются + все сектора, после чего указатель на буфер нужным образом уменьшается. +6. Если же нет, то считываются все сектора, кроме последнего, после чего + последний сектор считывается отдельно во временную область, и уже + оттуда нужная часть данных копируется в буфер. diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/build.bat b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/build.bat new file mode 100644 index 000000000..93b242702 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/cdfs/build.bat @@ -0,0 +1,2 @@ +@fasm -m 65535 bootsect.asm bootsect.bin +@pause \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/config.ini b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/config.ini new file mode 100644 index 000000000..1db4d25fa --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/config.ini @@ -0,0 +1,15 @@ +; boot time parameters for KolibriOS + +; timeout in seconds for config settings screen +timeout=5 + +; width*height +;resolution=1024*768 + +; where to load ramdisk from +; 2 - /hd0/1/kolibri.img +; 3 - don't load ramdisk +;imgfrom=3 + +; which directory to use as /sys +;syspath=/HD0/1/KOLIBRIOS diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/Tupfile.lua new file mode 100644 index 000000000..d9aa6800c --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("bootsect.asm", "fasm %f %o", "bootsect.bin") +tup.rule("kordldr.f1x.asm", "fasm %f %o", "kordldr.f1x.bin") diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.asm new file mode 100644 index 000000000..9b84a85e8 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.asm @@ -0,0 +1,392 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + +use_lba = 0 + org 0x7C00 + jmp start + nop +; FAT parameters, BPB +; note: they can be changed at install, replaced with real values +; these settings are for most typical 1.44M floppies + db 'KOLIBRI ' ; BS_OEMName, ignored + dw 200h ; BPB_BytsPerSec +BPB_SecsPerClus db 1 +BPB_RsvdSecCnt dw 1 +BPB_NumFATs db 2 +BPB_RootEntCnt dw 0xE0 + dw 2880 ; BPB_TotSec16 + db 0xF0 ; BPB_Media +BPB_FATSz16 dw 9 +BPB_SecPerTrk dw 18 +BPB_NumHeads dw 2 +BPB_HiddSec dd 0 + dd 0 ; BPB_TotSec32 +BS_DrvNum db 0 + db 0 ; BS_Reserved1 + db ')' ; BS_BootSig + dd 12344321h ; BS_VolID +filename: + db 'KORD.OS ' ; BS_VolLab + db 'FAT12 ' ; BS_FilSysType +; Used memory map: +; 8000:0000 - current directory +; 9000:0000 - root directory data [cached] +start: + xor ax, ax + mov ss, ax + mov sp, 0x7C00 + mov ds, ax + mov bp, sp + cld + sti + mov [bp+BS_DrvNum-0x7C00], dl +if use_lba + mov ah, 41h + mov bx, 55AAh + int 13h + mov si, aNoLBA + jc err_ + cmp bx, 0AA55h + jnz err_ + test cx, 1 + jz err_ +else + mov ah, 8 + int 13h + jc @f ; on error, assume that BPB geometry is valid + mov al, dh + mov ah, 0 + inc ax + mov [bp+BPB_NumHeads-0x7C00], ax + and cx, 3Fh + mov [bp+BPB_SecPerTrk-0x7C00], cx +@@: +end if +; get FAT parameters + xor bx, bx + mov al, [bp+BPB_NumFATs-0x7C00] + mov ah, 0 + mul [bp+BPB_FATSz16-0x7C00] + add ax, [bp+BPB_RsvdSecCnt-0x7C00] + adc dx, bx + push dx + push ax ; root directory start = dword [bp-4] + mov cx, [bp+BPB_RootEntCnt-0x7C00] + add cx, 0xF + rcr cx, 1 + shr cx, 3 ; cx = size of root directory in sectors + add ax, cx + adc dx, bx + push dx + push ax ; data start = dword [bp-8] +; load start of root directory (no more than 0x2000 bytes = 0x10 sectors) + cmp cx, 0x10 + jb @f + mov cx, 0x10 +@@: + mov ax, [bp-4] + mov dx, [bp-2] + push 0x9000 + pop es + call read_sectors + add word [bp-4], cx ; dword [bp-4] = start of non-cached root data + adc word [bp-2], bx +; load kordldr.f12 + mov si, main_loader + call lookup_in_root_dir + jc noloader + test byte [es:di+11], 10h ; directory? + jz kordldr_ok +noloader: + mov si, aLoaderNotFound +err_: + call out_string + mov si, aPressAnyKey + call out_string + xor ax, ax + int 16h + int 18h + jmp $ +kordldr_ok: + mov ax, [es:di+26] ; get file cluster + mov bx, 0x7E00 + xor cx, cx + mov es, cx + sub ax, 2 + jc noloader + push bx ; save return address: bx = 7E00 + mov cl, [bp+BPB_SecsPerClus-0x7C00] + mul cx +; fall through - 'ret' in read_sectors will return to 7E00 + +read_sectors2: +; same as read_sectors, but dx:ax is relative to start of data + add ax, [bp-8] + adc dx, [bp-6] +read_sectors: +; ss:bp = 0:7C00 +; es:bx = pointer to data +; dx:ax = first sector +; cx = number of sectors + pusha + add ax, word [bp+BPB_HiddSec-0x7C00] + adc dx, word [bp+BPB_HiddSec+2-0x7C00] +if use_lba + push ds +do_read_sectors: + push ax + push cx + push dx + cmp cx, 0x7F + jbe @f + mov cx, 0x7F +@@: +; create disk address packet on the stack +; dq starting LBA + push 0 + push 0 + push dx + push ax +; dd buffer + push es + push bx +; dw number of blocks to transfer (no more than 0x7F) + push cx +; dw packet size in bytes + push 10h +; issue BIOS call + push ss + pop ds + mov si, sp + mov dl, [bp+BS_DrvNum-0x7C00] + mov ah, 42h + int 13h + mov si, aReadError + jc err_ +; restore stack + add sp, 10h +; increase current sector & buffer; decrease number of sectors + mov si, cx + mov ax, es + shl cx, 5 + add ax, cx + mov es, ax + pop dx + pop cx + pop ax + add ax, si + adc dx, 0 + sub cx, si + jnz do_read_sectors + pop ds + popa + ret +else +do_read_sectors: + pusha + pop di + push bx + +; (dword in dx:ax) / (SectorsPerTrack) -> (dword in dx:ax), remainder bx + mov si, ax + xchg ax, dx + xor dx, dx + div [bp+BPB_SecPerTrk-0x7C00] + push ax + mov ax, si + div [bp+BPB_SecPerTrk-0x7C00] + mov bx, dx ; bx=sector-1 + pop dx + +; (dword in dx:ax) / (NumHeads) -> (word in ax), remainder dx + div [bp+BPB_NumHeads-0x7C00] + +; number of sectors: read no more than to end of track + push bx + sub bx, [bp+BPB_SecPerTrk-0x7C00] + neg bx + cmp cx, bx + jbe @f + mov cx, bx +@@: + pop bx + + inc bx +; now ax=track, dl=head, dh=0, cl=number of sectors, ch=0, bl=sector; convert to int13 format + mov di, cx + mov dh, dl + mov dl, [bp+BS_DrvNum-0x7C00] + shl ah, 6 + mov ch, al + mov al, cl + mov cl, bl + or cl, ah + pop bx + mov si, 3 + mov ah, 2 +@@: + push ax + int 13h + jnc @f + xor ax, ax + int 13h ; reset drive + pop ax + dec si + jnz @b + mov si, aReadError + jmp err_ +@@: + pop ax + mov ax, es + mov cx, di + shl cx, 5 + add ax, cx + mov es, ax + push di + popa + add ax, di + adc dx, 0 + sub cx, di + jnz do_read_sectors + popa + ret +end if + +scan_for_filename: +; in: ds:si -> 11-bytes FAT name +; in: es:0 -> part of directory data +; in: cx = number of entries +; out: if found: CF=0, ZF=1, es:di -> directory entry +; out: if not found, but continue required: CF=1 and ZF=0 +; out: if not found and zero item reached: CF=1 and ZF=1 + xor di, di + push cx +sloop: + cmp byte [es:di], 0 + jz snotfound + test byte [es:di+11], 8 ; volume label? + jnz scont ; ignore volume labels + pusha + mov cx, 11 + repz cmpsb + popa + jz sdone +scont: + add di, 0x20 + loop sloop + inc cx ; clear ZF flag +snotfound: + stc +sdone: + pop cx +lrdret: + ret + +lookup_in_root_dir: +; ss:bp = 0:7C00 +; in: ds:si -> 11-bytes FAT name +; out: if found: CF=0, es:di -> directory entry +; out: if not found: CF=1 + mov cx, [bp+BPB_RootEntCnt-0x7C00] + push cx +; first, look in root directory cache + push 0x9000 + pop es + test ch, ch + jz @f + mov cx, 0x100 +@@: + mov ax, [bp-4] + mov dx, [bp-2] ; dx:ax = starting sector of not cached data of root directory +lrdloop: + call scan_for_filename + pop bx + jz lrdret + sub bx, cx + mov cx, bx + stc + jz lrdret +; read no more than 0x10000 bytes, or 0x10000/0x20 = 0x800 entries + push cx + cmp ch, 0x8 + jb @f + mov cx, 0x800 +@@: + push 0x8000 + pop es + push cx + push es + xor bx, bx + add cx, 0xF + shr cx, 4 + call read_sectors + pop es + add ax, cx + adc dx, bx + pop cx + jmp lrdloop + +out_string: +; in: ds:si -> ASCIIZ string + lodsb + test al, al + jz lrdret + mov ah, 0Eh + mov bx, 7 + int 10h + jmp out_string + +aReadError db 'Read error',0 +if use_lba +aNoLBA db 'The drive does not support LBA!',0 +end if +aLoaderNotFound db 'Loader not found',0 +aPressAnyKey db 13,10,'Press any key...',13,10,0 +main_loader db 'KORDLDR F1X' + +if use_lba + db 0 ; make bootsector 512 bytes in length +end if + +; bootsector signature + dw 0xAA55 + +; display offsets of all procedures used by kordldr.f12.asm +macro show [procedure] +{ + bits = 16 + display `procedure,' = ' + repeat bits/4 + d = '0' + procedure shr (bits - %*4) and 0Fh + if d > '9' + d = d + 'A'-'9'-1 + end if + display d + end repeat + display 13,10 +} + +show read_sectors, read_sectors2, lookup_in_root_dir, scan_for_filename, err_, noloader diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.txt b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.txt new file mode 100644 index 000000000..c0449aa69 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/bootsect.txt @@ -0,0 +1,360 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + Встречаются вирус и FAT. + - Привет, ты кто? + - Я? Вирус. + - A я AFT, то есть TAF, то есть FTA, черт, совсем запутался... + +Бутсектор для FAT12/FAT16-тома на носителе с размером сектора 0x200 = 512 байт. + +===================================================================== + +Есть две версии в зависимости от того, поддерживает ли носитель LBA, +выбор осуществляется установкой константы use_lba в первой строке исходника. +Требования для работы: +1) Сам бутсектор, первая копия FAT и все используемые файлы +должны быть читабельны. +2) Минимальный процессор - 80186. +3) В системе должно быть как минимум 592K свободной базовой памяти. + +===================================================================== + +Документация в тему (ссылки валидны на момент написания этого файла, 15.05.2008): + официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf + русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip + официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf + то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf + описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html + официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf + +===================================================================== + +Максимальное количество кластеров на FAT12-томе - 0xFF4 = 4084; каждый кластер +занимает 12 бит в таблице FAT, так что общий размер не превосходит +0x17EE = 6126 байт. Вся таблица помещается в памяти. +Максимальное количество кластеров на FAT16-томе - 0xFFF4 = 65524; каждый +кластер занимает 16 бит в таблице FAT, так что общий размер не превосходит +0x1FFE8 = 131048 байт. Вся таблица также помещается в памяти, однако в +этом случае несколько нецелесообразно считывать всю таблицу, поскольку +на практике нужна только небольшая её часть. Поэтому место в памяти +резервируется, но данные считываются только в момент, когда к ним +действительно идёт обращение. + +Схема используемой памяти: + ...-7C00 стек + 7C00-7E00 код бутсектора + 7E00-8200 вспомогательный файл загрузчика (kordldr.f1x) + 8200-8300 список загруженных секторов таблицы FAT16 + (1 = соответствующий сектор загружен) + 60000-80000 загруженная таблица FAT12 / место для таблицы FAT16 + 80000-90000 текущий кластер текущей рассматриваемой папки + 90000-92000 кэш для корневой папки + 92000-... кэш для некорневых папок (каждой папке отводится + 2000h байт = 100h входов, одновременно в кэше + может находиться не более 7 папок; + точный размер определяется размером доступной + физической памяти - как правило, непосредственно + перед A0000 размещается EBDA, Extended BIOS Data Area) + +===================================================================== + +Основной процесс загрузки. +Точка входа (start): получает управление от BIOS при загрузке, при этом + dl содержит идентификатор диска, с которого идёт загрузка +1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед + кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало + бутсектора (в дальнейшем данные будут адресоваться через [bp+N] - + это освобождает ds и экономит на размере кода). +2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h + прерывания 13h. Если нет, переходит на код обработки ошибок с + сообщением об отсутствии LBA. +CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и + записывает полученные данные поверх BPB. Если вызов завершился ошибкой, + предполагает уже существующие данные корректными. +3. Вычисляет некоторые параметры FAT-тома: начальный сектор корневой папки + и начальный сектор данных. Кладёт их в стек; впоследствии они + всегда будут лежать в стеке и адресоваться через bp. +4. Считывает начало корневой папки по адресу 9000:0000. Число считываемых + секторов - минимум из размера корневой папки, указанного в BPB, и 16 + (размер кэша для корневой папки - 2000h байт = 16 секторов). +5. Ищет в корневой папке элемент kordldr.f1x. Если не находит, или если + он оказывается папкой, или если файл имеет нулевую длину - + переходит на код обработки ошибок с сообщением о + ненайденном загрузчике. + Замечание: на этом этапе загрузки искать можно только в корневой + папке и только имена, заданные в формате файловой системе FAT + (8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны + быть заглавными, при необходимости имя и расширение дополняются + пробелами, разделяющей точки нет, завершающего нуля нет). +6. Загружает первый кластер файла kordldr.f1x по адресу 0:7E00 и передаёт + ему управление. При этом в регистрах dx:ax оказывается абсолютный + номер первого сектора kordldr.f1x, а в cx - число считанных секторов + (равное размеру кластера). + +Вспомогательные процедуры бутсектора. +Код обработки ошибок (err): +1. Выводит строку с сообщением об ошибке. +2. Выводит строку "Press any key...". +3. Ждёт нажатия any key. +4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё. +5. Для подстраховки зацикливается. + +Процедура чтения секторов (read_sectors и read_sectors2): +на входе должно быть установлено: + ss:bp = 0:7C00 + es:bx = указатель на начало буфера, куда будут прочитаны данные + dx:ax = стартовый сектор (относительно начала логического диска + для read_sectors, относительно начала данных для read_sectors2) + cx = число секторов (должно быть больше нуля) +на выходе: es:bx указывает на конец буфера, в который были прочитаны данные +0. Если вызывается read_sectors2, она переводит указанный ей номер сектора + в номер относительно начала логического диска, прибавляя номер сектора + начала данных, хранящийся в стеке как [bp-8]. +1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на + устройстве, прибавляя значение соответствующего поля из BPB. +2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации + CHS-версия: все читаемые секторы были на одной дорожке. + LBA-версия: число читаемых секторов не превосходило 7Fh (требование + спецификации EDD BIOS). +CHS-версия: +3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как + единица плюс остаток от деления абсолютного номера на число секторов + на дорожке; дорожка рассчитывается как остаток от деления частного, + полученного на предыдущем шаге, на число дорожек, а цилиндр - как + частное от этого же деления. Если число секторов для чтения больше, + чем число секторов до конца дорожки, уменьшает число секторов для + чтения. +4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов, + dh=головка, (младшие 6 бит cl)=сектор, + (старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска + и повторяет попытку чтения, всего делается не более трёх попыток + (несколько попыток нужно в случае дискеты для гарантии того, что + мотор раскрутился). Если все три раза происходит ошибка чтения, + переходит на код обработки ошибок с сообщением "Read error". +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. +LBA-версия: +3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей + итерации) до 7Fh. +4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами + push, причём в обратном порядке: стек - структура LIFO, и данные в + стеке хранятся в обратном порядке по отношению к тому, как их туда + клали). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки + ошибок с сообщением "Read error". Очищает стек от пакета, + сформированного на предыдущем шаге. +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. + +Процедура поиска элемента по имени в уже прочитанных данных папки + (scan_for_filename): +на входе должно быть установлено: + ds:si = указатель на имя файла в формате FAT (11 байт, 8 на имя, + 3 на расширение, все буквы заглавные, если имя/расширение + короче, оно дополняется до максимума пробелами) + es = сегмент данных папки + cx = число элементов в прочитанных данных +на выходе: ZF определяет, нужно ли продолжать разбор данных папки + (ZF=1, если либо найден запрошенный элемент, либо достигнут + конец папки); CF определяет, удалось ли найти элемент с искомым именем + (CF=1, если не удалось); если удалось, то es:di указывает на него. +scan_for_filename считает, что данные папки размещаются начиная с es:0. +Первой командой процедура обнуляет di. Затем просто в цикле по элементам папки +проверяет имена. + +Процедура поиска элемента в корневой папке (lookup_in_root_dir): +на входе должно быть установлено: + ss:bp = 0:7C00 + ds:si = указатель на имя файла в формате FAT (см. выше) +на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то + CF сброшен и es:di указывает на элемент папки +Начинает с просмотра кэшированной (начальной) части корневой папки. В цикле + сканирует элементы; если по результатам сканирования обнаруживает, + что нужно читать папку дальше, то считывает не более 0x10000 = 64K + байт (ограничение введено по двум причинам: во-первых, чтобы заведомо + не вылезти за пределы используемой памяти, во-вторых, сканирование + предполагает, что все обрабатываемые элементы располагаются в одном + сегменте) и продолжает цикл. +Сканирование прекращается в трёх случаях: обнаружен искомый элемент; + кончились элементы в папке (судя по числу элементов, указанному в BPB); + очередной элемент папки сигнализирует о конце (первый байт нулевой). + +Процедура вывода на экран ASCIIZ-строки (out_string): +на входе: ds:si -> строка +В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh. + +===================================================================== + +Работа вспомогательного загрузчика kordldr.f1x: +1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора. + В зависимости от этого устанавливает смещения используемых процедур + бутсектора. Критерий проверки: scan_for_filename должна начинаться + с инструкции 'xor di,di' с кодом 31 FF (вообще-то эта инструкция может + с равным успехом ассемблироваться и как 33 FF, но fasm генерирует + именно такую форму). +2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска + адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с + ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента + место должно быть, отсюда ограничение в 592 Kb (94000h байт). + Замечание: этот размер не может превосходить 0A0000h байт и + на практике оказывается немного (на 1-2 килобайта) меньшим из-за + наличия дополнительной области данных BIOS "вверху" базовой памяти. +3. Определяет тип файловой системы: FAT12 или FAT16. Согласно официальной + спецификации от Microsoft (версия 1.03 спецификации датирована, + к слову, 06 декабря 2000 года), разрядность FAT определяется + исключительно числом кластеров: максимальное число кластеров на + FAT12-томе равно 4094 = 0xFF4. Согласно здравому смыслу, на FAT12 + может быть 0xFF5 кластеров, но не больше: кластеры нумеруются с 2, + а число 0xFF7 не может быть корректным номером кластера. + Win95/98/Me следует здравому смыслу: разграничение FAT12/16 делается + по максимуму 0xFF5. Драйвер FAT в WinNT/2k/XP/Vista вообще поступает + явно неверно, считая, что 0xFF6 (или меньше) кластеров означает + FAT12-том, в результате получается, что последний кластер + (в случае 0xFF6) неадресуем. Основной загрузчик osloader.exe + [встроен в ntldr] для NT/2k/XP делает так же. Первичный загрузчик + [бутсектор FAT12/16 загружает первый сектор ntldr, и разбор FAT-таблицы + лежит на нём] в NT/2k подвержен той же ошибке. В XP её таки исправили + в соответствии со спецификацией. Linux при определении FAT12/FAT16 + честно следует спецификации. + Здесь код основан всё же на спецификации. 9x мертва, а в линейке NT + Microsoft если и будет исправлять ошибки, то согласно собственному + описанию. +4. Для FAT12: загружает в память первую копию таблицы FAT по адресу 6000:0000. + Если размер, указанный в BPB, превосходит 12 секторов, + это означает, что заявленный размер слишком большой (это не считается + ошибкой файловой системы), и читаются только 12 секторов (таблица FAT12 + заведомо влезает в такой объём данных). +Для FAT16: инициализирует внутренние данные, указывая, что никакой сектор + FAT не загружен (они будут подгружаться позднее, когда понадобятся + и только те, которые понадобятся). +5. Если кластер равен сектору, то бутсектор загрузил только часть файла + kordldr.f1x, и загрузчик подгружает вторую свою часть, используя + значения регистров на входе в kordldr.f1x. +6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не + найден, или оказался папкой, или оказался слишком большим, то переходит + на код обработки ошибок из бутсектора с сообщением + "Fatal error: cannot load the secondary loader". + Замечание: на этом этапе имя файла уже можно указывать вместе с путём + и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов + по-прежнему нет. +7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err. + Это нужно, чтобы последующие обращения к коду бутсектора в случае + ошибок чтения не выводил соответствующее сообщение с последующей + перезагрузкой, а рапортовал об ошибке чтения, которую мог бы + как-нибудь обработать вторичный загрузчик. +8. Если загрузочный диск имеет идентификатор меньше 0x80, + то устанавливает al='f' ("floppy"), ah=идентификатор диска, + иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска). + Устанавливает bx='12', если тип файловой системы - FAT12, и + bx='16' в случае FAT16. Устанавливает si=смещение функции обратного + вызова. Поскольку в этот момент ds=0, то ds:si образуют полный адрес. +9. Передаёт управление по адресу 1000:0000. + +Функция обратного вызова для вторичного загрузчика: + предоставляет возможность чтения файла. +Вход и выход описаны в спецификации на загрузчик. +1. Сохраняет стек вызывающего кода и устанавливает свой стек: + ss:sp = 0:(7C00-8), bp=7C00: пара ss:bp при работе с остальным + кодом должна указывать на 0:7C00, а -8 берётся от того, что + инициализирующий код бутсектора уже поместил в стек 2 двойных слова, + и они должны сохраняться в неизменности. +2. Разбирает переданные параметры, выясняет, какое действие запрошено, + и вызывает нужную вспомогательную процедуру. +3. Восстанавливает стек вызывающего кода и возвращает управление. + +Вспомогательные процедуры kordldr.f1x. +Процедура получения следующего кластера в FAT (get_next_cluster): +1. Вспоминает разрядность FAT, вычисленную ранее. +Для FAT12: +2. Устанавливает ds = 0x6000 - сегмент, куда ранее была считана + вся таблица FAT. +3. Подсчитывает si = (кластер) + (кластер)/2 - смещение в этом сегменте + слова, задающего следующий кластер. Загружает слово по этому адресу. +4. Если кластер имеет нечётный номер, то соответствующий ему элемент + располагается в старших 12 битах слова, и слово нужно сдвинуть вправо + на 4 бита; в противном случае - в младших 12 битах, и делать ничего не + надо. +5. Выделяет из получившегося слова 12 бит. Сравнивает их с пределом 0xFF7: + номера нормальных кластеров меньше, и флаг CF устанавливается; + специальные значения EOF и BadClus сбрасывают флаг CF. +Для FAT16: +2. Вычисляет адрес памяти, предназначенной для соответствующего сектора данных + в таблице FAT. +3. Если сектор ещё не загружен, то загружает его. +4. Вычисляет смещение данных для конкретного кластера относительно начала + сектора. +5. Загружает слово в ax из адреса, вычисленному на шагах 1 и 3. +6. Сравнивает его с пределом 0xFFF7: номера нормальных кластеров меньше, и флаг + CF устанавливается; специальные значения EOF и BadClus сбрасывают CF. + +Процедура загрузки файла (load_file): +1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4. +2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты + разделяются символом '/') в FAT-формат 8+3. Если это невозможно + (больше 8 символов в имени, больше 3 символов в расширении или + больше одной точки), возвращается с ошибкой. +3. Ищет элемент с таким именем в текущей рассматриваемой папке. Для корневой + папки используется процедура из бутсектора. Для остальных папок: + a) Проверяет, есть ли такая папка в кэше некорневых папок. + (Идентификация папок осуществляется по номеру начального кластера.) + Если такой папки ещё нет, добавляет её в кэш; если тот переполняется, + выкидывает папку, к которой дольше всего не было обращений. (Для + каждого элемента кэша хранится метка от 0 до (размер кэша)-1, + определяющая его номер при сортировке по давности последнего обращения. + При обращении к какому-то элементу его метка становится нулевой, + а те метки, которые меньше старого значения, увеличиваются на единицу.) + б) Просматривает в поисках запрошенного имени все элементы из кэша, + используя процедуру из бутсектора. Если обнаруживает искомый элемент, + переходит к шагу 4. Если обнаруживает конец папки, возвращается из + процедуры с ошибкой. + в) В цикле считывает папку посекторно. При этом пропускает начальные + секторы, которые уже находятся в кэше и уже были просмотрены. Каждый + прочитанный сектор копирует в кэш, если там ещё остаётся место, + и просматривает в нём все элементы. Работает, пока не случится одно из + трёх событий: найден искомый элемент; кончились кластеры (судя по + цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце + (первый байт нулевой). В двух последних случаях возвращается с ошибкой. +4. Проверяет тип найденного элемента (файл/папка): последний элемент в + запрошенном имени должен быть файлом, все промежуточные - папками. + Если текущий компонент имени - промежуточный, продвигает текущую + рассматриваемую папку и возвращается к пункту 2. +5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный + при вызове буфер последовательными вызовами функции бутсектора; + при этом если несколько кластеров файла расположены на диске + последовательно, то их чтение объединяется в одну операцию. + Следит за тем, чтобы не превысить указанный при вызове процедуры + лимит числа секторов для чтения. + +Процедура продолжения загрузки файла (continue_load_file): встроена + внутрь шага 5 load_file; загружает в регистры нужные значения (ранее + сохранённые из load_file) и продолжает шаг 5. diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/build.bat b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/build.bat new file mode 100644 index 000000000..17c30cac1 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/build.bat @@ -0,0 +1,3 @@ +@fasm -m 65535 bootsect.asm bootsect.bin +@fasm -m 65535 kordldr.f1x.asm kordldr.f1x +@pause \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/kordldr.f1x.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/kordldr.f1x.asm new file mode 100644 index 000000000..d5724bc89 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat1x/kordldr.f1x.asm @@ -0,0 +1,689 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + org 0x7E00 +; the KordOS FAT12/FAT16 bootsector loads first cluster of this file to 0:7E00 and transfers control to here +; ss:bp = 0:7C00 +virtual at bp + rb 3 ; BS_jmpBoot + rb 8 ; BS_OEMName, ignored + dw ? ; BPB_BytsPerSec +BPB_SecsPerClus db ? +BPB_RsvdSecCnt dw ? +BPB_NumFATs db ? +BPB_RootEntCnt dw ? +BPB_TotSec16 dw ? + db ? ; BPB_Media +BPB_FATSz16 dw ? +BPB_SecPerTrk dw ? +BPB_NumHeads dw ? +BPB_HiddSec dd ? +BPB_TotSec32 dd ? +BS_DrvNum db ? +fat_type db ? ; this is BS_Reserved1, + ; we use it to save FS type: 0=FAT12, 1=FAT16 + db ? ; BS_BootSig +num_sectors dd ? ; BS_VolID +; rb 11 ; BS_VolLab +; rb 3 ; BS_FilSysType, first 3 bytes +read_sectors dw ? +read_sectors2 dw ? +lookup_in_root_dir dw ? +scan_for_filename dw ? +err_ dw ? +noloader dw ? +cachelimit dw ? +filesize: ; will be used to save file size + rb 5 ; BS_FilSysType, last 5 bytes +; following variables are located in the place of starting code; +; starting code is no more used at this point +sect_per_clus dw ? +cur_cluster dw ? +next_cluster dw ? +flags dw ? +cur_delta dd ? +end virtual + +; procedures from boot sector +; LBA version +lba_read_sectors = 7CE2h +lba_read_sectors2 = 7CDCh +lba_lookup_in_root_dir = 7D4Fh +lba_scan_for_filename = 7D2Dh +lba_err = 7CB5h +lba_noloader = 7CB2h +; CHS version +chs_read_sectors = 7CDEh +chs_read_sectors2 = 7CD8h +chs_lookup_in_root_dir = 7D70h +chs_scan_for_filename = 7D4Eh +chs_err = 7CB1h +chs_noloader = 7CAEh + + push ax cx ; save our position on disk + push ss + pop es +; determine version of bootsector (LBA vs CHS) +; mov [read_sectors], chs_read_sectors +; mov [read_sectors2], chs_read_sectors2 +; mov [lookup_in_root_dir], chs_lookup_in_root_dir +; mov [scan_for_filename], chs_scan_for_filename +; mov [err], chs_err +; mov [noloader], chs_noloader + lea di, [read_sectors] + mov si, chs_proc_addresses + mov cx, 6*2 + cmp word [chs_scan_for_filename], 0xFF31 ; 'xor di,di' + jz @f + add si, cx +; mov [read_sectors], lba_read_sectors +; mov [read_sectors2], lba_read_sectors2 +; mov [lookup_in_root_dir], lba_lookup_in_root_dir +; mov [scan_for_filename], lba_scan_for_filename +; mov [err], lba_err +; mov [noloader], lba_noloader +@@: + rep movsb + mov cl, [BPB_SecsPerClus] + mov [sect_per_clus], cx + xor bx, bx +; determine size of cache for folders + int 12h ; ax = size of available base memory in Kb + sub ax, 94000h / 1024 + jae @f +nomem: + mov si, nomem_str + jmp [err_] +@@: + shr ax, 3 + mov [cachelimit], ax ; size of cache - 1 +; get type of file system - FAT12 or FAT16? +; calculate number of clusters + mov ax, [BPB_TotSec16] + xor dx, dx + test ax, ax + jnz @f + mov ax, word [BPB_TotSec32] + mov dx, word [BPB_TotSec32+2] +@@: + sub ax, [bp-8] ; dword [bp-8] = first data sector + sbb dx, [bp-6] + jb j_noloader + div [sect_per_clus] +; ax = number of clusters +; note: this is loader for FAT12/FAT16, so 'div' does not overflow on correct volumes + mov [fat_type], ch + cmp ax, 0xFF5 + jb init_fat12 + inc [fat_type] +init_fat16: +; no sectors loaded + mov di, 0x8200 + xor ax, ax + mov cx, 0x100/2 + rep stosw + jmp init_fat_done +init_fat12: +; read FAT + push 0x6000 + pop es + mov ax, [BPB_RsvdSecCnt] + mov cx, [BPB_FATSz16] + cmp cx, 12 + jb @f + mov cx, 12 +@@: + xor dx, dx + call [read_sectors] +init_fat_done: +; if cluster = sector, we need to read second part of our file +; (bootsector loads only first cluster of kordldr.f1x) + pop cx ax ; restore our position on disk + cmp cx, 1 + ja kordldr_full + sub ax, [bp-8] + inc ax + inc ax ; ax = first cluster of kordldr.f12 + call get_next_cluster + jc @f +j_noloader: + jmp [noloader] +@@: + dec ax + dec ax + push 0x800 + pop es + call [read_sectors2] +kordldr_full: +; ...continue loading... + mov di, secondary_loader_info + call load_file + test bx, bx + mov bx, [err_] + jz @f + mov si, aKernelNotFound + jmp bx +@@: +; for subsequent calls to callback function, hook error handler +; mov byte [bx], 0xE9 ; 'jmp' opcode +; mov ax, hooked_err - 3 +; sub ax, bx +; mov word [bx+1], ax +; push hooked_err / ret + mov word [bx], 0x68 + ((hooked_err and 0xFF) shl 8) + mov word [bx+2], (hooked_err shr 8) + (0xC3 shl 8) +; set registers for secondary loader + mov ah, [BS_DrvNum] + mov al, 'f' + test ah, ah + jns @f + sub ah, 80h + mov al, 'h' +@@: + mov bx, '12' + cmp [fat_type], 0 + jz @f + mov bh, '6' +@@: + mov si, callback ; ds:si = far pointer to callback procedure + jmp far [si-callback+secondary_loader_info] ; jump to 1000:0000 + +nomem_str db 'No memory',0 + +chs_proc_addresses: + dw chs_read_sectors + dw chs_read_sectors2 + dw chs_lookup_in_root_dir + dw chs_scan_for_filename + dw chs_err + dw chs_noloader +lba_proc_addresses: + dw lba_read_sectors + dw lba_read_sectors2 + dw lba_lookup_in_root_dir + dw lba_scan_for_filename + dw lba_err + dw lba_noloader + +get_next_cluster: +; in: ax = cluster +; out: if there is next cluster: CF=1, ax = next cluster +; out: if there is no next cluster: CF=0 + push si + cmp [fat_type], 0 + jnz gnc16 +; for FAT12 + push ds + push 0x6000 + pop ds + mov si, ax + shr si, 1 + add si, ax + test al, 1 + lodsw + jz @f + shr ax, 4 +@@: + and ax, 0xFFF + cmp ax, 0xFF7 + pop ds si + ret +; for FAT16 +gnc16: +; each sector contains 200h bytes = 100h FAT entries +; so ah = # of sector, al = offset in sector + mov si, ax + mov ah, 0 + shr si, 8 +; calculate segment for this sector of FAT table +; base for FAT table is 6000:0000, so the sector #si has to be loaded to (60000 + 200*si) +; segment = 6000 + 20*si, offset = 0 + push es + push si + shl si, 5 + add si, 0x6000 + mov es, si + pop si + cmp byte [ss:0x8200+si], ah ; sector already loaded? + jnz @f +; load corresponding sector + pusha + push es + xor bx, bx + mov ax, [BPB_RsvdSecCnt] + xor dx, dx + add ax, si + adc dx, bx + mov cx, 1 ; read 1 sector + call [read_sectors] + pop es + popa +@@: + mov si, ax + add si, si +; mov ax, [es:si] + lods word [es:si] + pop es + cmp ax, 0xFFF7 + pop si + ret + +if $ > 0x8000 +error 'get_next_cluster must fit in first sector of kordldr.f1x!' +end if + +load_file: +; in: ss:bp = 0:7C00 +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found +; out: dx:ax = file size (0xFFFFFFFF if file not found) + xor ax, ax ; start from root directory + mov dx, -1 + mov word [filesize], dx + mov word [filesize+2], dx ; initialize file size with invalid value + lea si, [di+6] +parse_dir_loop: +; convert name to FAT name + push di + push ax + push ss + pop es +; convert ASCIIZ filename to FAT name + mov di, filename + push di + mov cx, 8+3 + mov al, ' ' + rep stosb + pop di + mov cl, 8 ; 8 symbols per name + mov bl, 1 +nameloop: + lodsb + test al, al + jz namedone + cmp al, '/' + jz namedone + cmp al, '.' + jz namedot + dec cx + js badname + cmp al, 'a' + jb @f + cmp al, 'z' + ja @f + sub al, 'a'-'A' +@@: + stosb + jmp nameloop +namedot: + inc bx + jp badname + add di, cx + mov cl, 3 + jmp nameloop +badname: ; do not make direct js/jp to notfound_pop: + ; this generates long forms of conditional jumps and results in longer code + jmp notfound_pop +namedone: +; scan directory + pop ax ; ax = cluster of directory or 0 for root + push ds + push si + push es + pop ds + mov si, filename ; ds:si -> filename in FAT style + test ax, ax + jnz lookup_in_notroot_dir +; for root directory, use the subroutine from bootsector + call [lookup_in_root_dir] + jmp lookup_done +lookup_in_notroot_dir: +; for other directories, read a folder sector-by-sector and scan +; first, try to use the cache + push ds + push cs + pop ds + mov bx, [cachelimit] + add bx, bx + mov di, foldcache_mark +@@: + mov dx, [foldcache_clus+di-foldcache_mark+bx] + cmp dx, ax + jz cacheok + test dx, dx + jz cacheadd ; the cache has place for new entry + dec bx + dec bx + jns @b +; the folder is not present in the cache, so add it +; the cache is full; find the oldest entry and replace it with the new one + mov dx, [cachelimit] +@@: + inc bx + inc bx + cmp word [di+bx], dx ; marks have values 0 through [cachelimit] + jnz @b +cacheadd: + or word [di+bx], 0xFFFF ; very big value, it will be changed soon + mov [foldcache_clus+di-foldcache_mark+bx], ax + and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet +cacheok: +; update cache marks + mov dx, [di+bx] + mov cx, [foldcache_size+di-foldcache_mark+bx] + mov di, [cachelimit] + add di, di +cacheupdate: + cmp [foldcache_mark+di], dx + adc [foldcache_mark+di], 0 + dec di + dec di + jns cacheupdate + and [foldcache_mark+bx], 0 +; done, bx contains (position in cache)*2 + pop ds +; mov dx, bx +; shl dx, 8 ; dx = (position in cache)*0x2000/0x10 +; add dx, 0x9200 + lea dx, [bx+0x92] + xchg dl, dh + mov es, dx + jcxz not_in_cache + call [scan_for_filename] + jz lookup_done +not_in_cache: +; cache miss, read folder data from disk + mov bx, cx + shr bx, 4 + shl cx, 5 + mov di, cx ; es:di -> free space in cache entry +; external loop: scan clusters +folder_next_cluster: +; internal loop: scan sectors in cluster + mov cx, [sect_per_clus] + push ax + dec ax + dec ax + mul cx + add ax, [bp-8] + adc dx, [bp-6] ; dx:ax = absolute sector +folder_next_sector: +; skip first bx sectors + dec bx + jns folder_skip_sector + push cx + push es di + push 0x8000 + pop es + xor bx, bx + mov cx, 1 + push es + call [read_sectors] +; copy data to the cache... + pop ds + pop di es + cmp di, 0x2000 ; ...if there is free space, of course + jae @f + push si di + mov cx, 0x100 + xor si, si + rep movsw + mov di, es + shr di, 8 + add [ss:foldcache_size+di-0x92], 0x10 ; 0x10 new entries in the cache + pop di si +@@: + push es + push 0x8000 + pop es + push cs + pop ds + mov cx, 0x10 + call [scan_for_filename] + pop es + pop cx + jz lookup_done_pop +folder_skip_sector: + inc ax + jnz @f + inc dx +@@: + loop folder_next_sector + pop ax ; ax = current cluster + call get_next_cluster + jc folder_next_cluster + stc + push ax +lookup_done_pop: + pop ax +lookup_done: + pop si + pop ds +; CF=1 <=> failed + jnc found +notfound: + pop di + mov bx, 2 ; file not found + mov ax, 0xFFFF + mov dx, ax ; invalid file size + ret +notfound_pop: + pop ax + jmp notfound +found: + mov ax, [es:di+26] ; get cluster + test byte [es:di+11], 10h ; directory? + jz regular_file + cmp byte [si-1], 0 + jz notfound ; don't read directories as a regular files +; ok, we have found a directory and the caller requested a file into it + pop di + jmp parse_dir_loop ; restart with new cluster in ax +regular_file: + cmp byte [si-1], 0 + jnz notfound ; file does not contain another files +; ok, we have found a regular file and the caller requested it +; save file size + mov dx, [es:di+28] + mov [filesize], dx + mov dx, [es:di+30] + mov [filesize+2], dx + pop di + mov si, [di+4] + shl si, 3 + push si ; [ds:di+4] = limit in 4K blocks + les bx, [di] ; es:bx -> buffer +clusloop: +; ax = first cluster, top of stack contains limit in sectors + mov si, ax ; remember current cluster + xor cx, cx ; cx will contain number of consecutive clusters + mov word [cur_delta], cx + mov word [cur_delta+2], cx + mov di, ax +clusfind: + inc di + inc cx + call get_next_cluster + jnc clusread + cmp ax, di + jz clusfind + stc +clusread: + pop di ; limit in sectors + push ax ; save next cluster + pushf ; save flags +; read cx clusters, starting from si +; calculate number of sectors + xchg ax, cx + mul [sect_per_clus] +; dx:ax = number of sectors; compare with limit + mov word [num_sectors], ax + mov word [num_sectors+2], dx + jmp @f +continue_load_file: + les bx, [di] ; es:bx -> buffer + mov di, [di+4] ; ds:di = limit in 4K blocks + shl di, 3 ; now di = limit in sectors + mov ax, word [num_sectors] + mov dx, word [num_sectors+2] + mov si, [cur_cluster] + push [next_cluster] + push [flags] + or ax, dx + jz nextclus +@@: + test dx, dx + jnz clusdecrease + push dx ; limit was not exceeded + cmp ax, di + jbe @f + pop ax +clusdecrease: + push 1 ; limit was exceeded + mov ax, di +@@: + sub di, ax ; calculate new limit + sub word [num_sectors], ax + sbb word [num_sectors+2], 0 +readloop: + push ax +; buffer should not cross a 64K boundary + push bx + shr bx, 4 + mov cx, es + add bx, cx + neg bx + and bh, 0xF + shr bx, 5 + jnz @f + mov bl, 0x80 +@@: + cmp ax, bx + jbe @f + xchg ax, bx +@@: + pop bx + xchg ax, cx +; calculate starting sector + lea ax, [si-2] + mul [sect_per_clus] + add ax, word [cur_delta] + adc dx, word [cur_delta+2] + add word [cur_delta], cx + adc word [cur_delta+2], 0 +; read + call [read_sectors2] + pop ax + sub ax, cx + jnz readloop + pop dx +; next cluster? +nextclus: + popf + pop ax + mov [cur_cluster], si + mov [next_cluster], ax + pushf + pop [flags] + jnc @f ; no next cluster => return + mov dl, 1 ; dh=0 in any case + test di, di + jz @f ; if there is next cluster but current limit is 0 => return: limit exceeded + push di + jmp clusloop ; all is ok, continue +hooked_err: + mov sp, 7C00h-12-2 ; restore stack + mov dx, 3 ; return: read error +@@: + mov bx, dx + mov ax, [filesize] + mov dx, [filesize+2] + ret + +; Callback function for secondary loader +callback: +; in: ax = function number; only functions 1 and 2 are defined for now +; save caller's stack + mov dx, ss + mov cx, sp +; set our stack (required because we need ss=0) + xor si, si + mov ss, si + mov sp, 7C00h-8 + mov bp, 7C00h + push dx + push cx +; call our function + stc ; unsupported function + dec ax + jz callback_readfile + dec ax + jnz callback_ret +; function 2: continue loading file +; can be called only after function 1 returned value bx=1 (only part of file was loaded) +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; out: bx=0 - ok, bx=1 - still only part of file was loaded, bx=3 - read error +; out: dx:ax = file size + call continue_load_file + jmp callback_ret_succ +callback_readfile: +; function 1: read file +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error +; out: dx:ax = file size (0xFFFFFFFF if file was not found) + call load_file +callback_ret_succ: + clc ; function is supported +callback_ret: +; restore caller's stack + pop cx + pop ss + mov sp, cx +; return to caller + retf + +secondary_loader_info: + dw 0, 0x1000 + dw 0x30000 / 0x1000 + db 'kernel.mnt',0 +aKernelNotFound db 'Fatal error: cannot load the kernel',0 + +foldcache_clus dw 0,0,0,0,0,0,0 ; start with no folders in cache +foldcache_mark rw 7 +foldcache_size rw 7 +filename rb 11 +if $ > 0x8200 +error: + table overwritten +end if diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/Tupfile.lua new file mode 100644 index 000000000..4b40451ef --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("bootsect.asm", "fasm %f %o", "bootsect.bin") +tup.rule("kordldr.f32.asm", "fasm %f %o", "kordldr.f32") diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.asm new file mode 100644 index 000000000..d93c7979a --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.asm @@ -0,0 +1,358 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + +use_lba = 1 + org 0x7C00 + jmp start + nop +; FAT parameters, BPB +; they must be changed at install, replaced with real values + rb 8 ; BS_OEMName, ignored + dw 200h ; BPB_BytsPerSec +BPB_SecsPerClus db ? +BPB_RsvdSecCnt dw ? +BPB_NumFATs db ? +BPB_RootEntCnt dw ? + dw ? ; BPB_TotSec16 + db ? ; BPB_Media + dw ? ; BPB_FATSz16 = 0 for FAT32 +BPB_SecPerTrk dw ? +BPB_NumHeads dw ? +BPB_HiddSec dd ? + dd ? ; BPB_TotSec32 +BPB_FATSz32 dd ? +BPB_ExtFlags dw ? + dw ? ; BPB_FSVer +BPB_RootClus dd ? + dw ? ; BPB_FSInfo +BPB_BkBootSec dw ? + rb 12 ; BPB_Reserved +BS_DrvNum db ? + db ? ; BS_Reserved1 + db ? ; BS_BootSig + dd ? ; BS_VolID + rb 11 ; BS_VolLab + rb 8 ; + +curseg dw 0x8000 + +start: + xor ax, ax + mov ss, ax + mov sp, 0x7C00 + mov ds, ax + mov bp, sp + cld + sti + push dx ; byte [bp-2] = boot drive +if use_lba + mov ah, 41h + mov bx, 55AAh + int 13h + mov si, aNoLBA + jc err_ + cmp bx, 0AA55h + jnz err_ + test cl, 1 + jz err_ +else + mov ah, 8 + int 13h + jc @f + movzx ax, dh + inc ax + mov [bp+BPB_NumHeads-0x7C00], ax + and cx, 3Fh + mov [bp+BPB_SecPerTrk-0x7C00], cx +@@: +end if +; get FAT parameters + xor bx, bx + movzx eax, [bp+BPB_NumFATs-0x7C00] + mul [bp+BPB_FATSz32-0x7C00] + movzx ecx, [bp+BPB_RsvdSecCnt-0x7C00] + push ecx ; FAT start = dword [bp-6] + add eax, ecx + push eax ; data start = dword [bp-10] + ;push dword -1 ; dword [bp-14] = current sector for FAT cache + db 66h + push -1 ; dword [bp-14] = current sector for FAT cache + mov eax, [bp+BPB_RootClus-0x7C00] + mov si, main_loader + call lookup_in_dir + jnc kordldr_ok +noloader: + mov si, aLoaderNotFound +err_: + call out_string + mov si, aPressAnyKey + call out_string + xor ax, ax + int 16h + int 18h + jmp $ +kordldr_ok: + mov eax, [es:di+20-2] ; hiword(eax) = hiword(cluster) + mov ax, [es:di+26] ; loword(eax) = loword(cluster) + mov es, bx ; es = 0 + mov bx, 0x7E00 + push bx ; save return address: bx = 7E00 +; fall through - 'ret' in read_cluster will return to 7E00 + +read_cluster: +; ss:bp = 0:7C00 +; es:bx = pointer to data +; eax = cluster + sub eax, 2 + movzx ecx, [bp+BPB_SecsPerClus-0x7C00] + mul ecx + +read_sectors2: +; same as read_sectors32, but eax is relative to start of data + add eax, [bp-10] +read_sectors32: +; ss:bp = 0:7C00 +; es:bx = pointer to data +; eax = first sector +; cx = number of sectors +; some high words of 32-bit registers are destroyed! + pusha + add eax, [bp+BPB_HiddSec-0x7C00] +if use_lba + push ds +do_read_sectors: + push ax + push cx + cmp cx, 0x7F + jbe @f + mov cx, 0x7F +@@: +; create disk address packet on the stack +; dq starting LBA + push 0 + push 0 + push eax +; dd buffer + push es + push bx +; dw number of blocks to transfer (no more than 0x7F) + push cx +; dw packet size in bytes + push 10h +; issue BIOS call + push ss + pop ds + mov si, sp + mov dl, [bp-2] + mov ah, 42h + int 13h + mov si, aReadError + jc err_ +; restore stack + add sp, 10h +; increase current sector & buffer; decrease number of sectors + movzx esi, cx + mov ax, es + shl cx, 5 + add ax, cx + mov es, ax + pop cx + pop ax + add eax, esi + sub cx, si + jnz do_read_sectors + pop ds + popa + ret +else +do_read_sectors: + pusha + pop edi ; loword(edi) = di, hiword(edi) = si + push bx + +; eax / (SectorsPerTrack) -> eax, remainder bx + movzx esi, [bp+BPB_SecPerTrk-0x7C00] + xor edx, edx + div esi + mov bx, dx ; bx=sector-1 + +; eax -> dx:ax + push eax + pop ax + pop dx +; (dword in dx:ax) / (NumHeads) -> (word in ax), remainder dx + div [bp+BPB_NumHeads-0x7C00] + +; number of sectors: read no more than to end of track + sub si, bx + cmp cx, si + jbe @f + mov cx, si +@@: + + inc bx +; now ax=track, dl=head, dh=0, cl=number of sectors, ch=0, bl=sector; convert to int13 format + movzx edi, cx + mov dh, dl + mov dl, [bp-2] + shl ah, 6 + mov ch, al + mov al, cl + mov cl, bl + or cl, ah + pop bx + mov si, 3 + mov ah, 2 +@@: + push ax + int 13h + jnc @f + xor ax, ax + int 13h ; reset drive + pop ax + dec si + jnz @b + mov si, aReadError + jmp err_ +@@: + pop ax + mov ax, es + mov cx, di + shl cx, 5 + add ax, cx + mov es, ax + push edi + popa + add eax, edi + sub cx, di + jnz do_read_sectors + popa + ret +end if + +lookup_in_dir: +; in: ds:si -> 11-bytes FAT name +; in: eax = cluster +; in: bx = 0 +; out: if found: CF=0, es:di -> directory entry +; out: if not found: CF=1 +; push 0x8000 +; pop es +; read current cluster: first cluster goes to 8000:0000, others - to 8200:0000 + mov es, [bp-7C00h + curseg] + push es + push eax + call read_cluster + mov ax, es + cmp ah, 82h + jb @f + mov ax, 8200h +@@: + mov [bp-7C00h + curseg], ax + pop eax + pop es +; scan for filename + shl cx, 4 + xor di, di +sloop: + cmp byte [es:di], bl + jz snotfound + test byte [es:di+11], 8 ; volume label? + jnz scont ; ignore volume labels + pusha + mov cx, 11 + repz cmpsb + popa + jz sdone +scont: + add di, 0x20 + loop sloop +; next cluster + push 0x6000 + pop es + push es ax + shr eax, 7 + cmp eax, [bp-14] + mov [bp-14], eax + jz @f + add eax, [bp-6] + mov cx, 1 + call read_sectors32 +@@: + pop di es + and di, 0x7F + shl di, 2 + and byte [es:di+3], 0x0F + mov eax, [es:di] + ;and eax, 0x0FFFFFFF + cmp eax, 0x0FFFFFF7 + jb lookup_in_dir +snotfound: + stc +sdone: + ret + +out_string: +; in: ds:si -> ASCIIZ string + lodsb + test al, al + jz sdone + mov ah, 0Eh + mov bx, 7 + int 10h + jmp out_string + +aReadError db 'Read error',0 +if use_lba +aNoLBA db 'The drive does not support LBA!',0 +end if +aLoaderNotFound db 'Loader not found',0 +aPressAnyKey db 13,10,'Press any key...',13,10,0 +main_loader db 'KORDLDR F32' + + db 56h +; just to make file 512 bytes long :) + db 'd' xor 'i' xor 'a' xor 'm' xor 'o' xor 'n' xor 'd' + +; bootsector signature + dw 0xAA55 + +; display offsets of all procedures used by kordldr.f12.asm +macro show [procedure] +{ + bits = 16 + display `procedure,' = ' + repeat bits/4 + d = '0' + procedure shr (bits - %*4) and 0Fh + if d > '9' + d = d + 'A'-'9'-1 + end if + display d + end repeat + display 13,10 +} + +show read_sectors32, read_sectors2, err_, noloader diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.txt b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.txt new file mode 100644 index 000000000..caabcb33f --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/bootsect.txt @@ -0,0 +1,333 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + Читай между строк - там никогда не бывает опечаток. + +Бутсектор для FAT32-тома на носителе с размером сектора 0x200 = 512 байт. + +===================================================================== + +Есть две версии в зависимости от того, поддерживает ли носитель LBA, +выбор осуществляется установкой константы use_lba в первой строке исходника. +Требования для работы: +1) Сам бутсектор, первая копия FAT и все используемые файлы +должны быть читабельны. (Если дело происходит на носителе с разбиением на +разделы и загрузочный код в MBR достаточно умный, то читабельности резервной +копии бутсектора (сектор номер 6 на томе) достаточно вместо читабельности +самого бутсектора). +2) Минимальный процессор - 80386. +3) В системе должно быть как минимум 584K свободной базовой памяти. + +===================================================================== + +Документация в тему (ссылки проверялись на валидность 15.05.2008): + официальная спецификация FAT: http://www.microsoft.com/whdc/system/platform/firmware/fatgen.mspx + в формате PDF: http://staff.washington.edu/dittrich/misc/fatgen103.pdf + русский перевод: http://wasm.ru/docs/11/fatgen103-rus.zip + официальная спецификация расширения EDD BIOS 3.0: http://www.phoenix.com/NR/rdonlyres/19FEBD17-DB40-413C-A0B1-1F3F560E222F/0/specsedd30.pdf + то же, версия 1.1: http://www.phoenix.com/NR/rdonlyres/9BEDED98-6B3F-4DAC-BBB7-FA89FA5C30F0/0/specsedd11.pdf + описание функций BIOS: Interrupt List by Ralf Brown: http://www.cs.cmu.edu/~ralf/files.html + официальная спецификация Boot BIOS: http://www.phoenix.com/NR/rdonlyres/56E38DE2-3E6F-4743-835F-B4A53726ABED/0/specsbbs101.pdf + +===================================================================== + +Схема используемой памяти: + ...-7C00 стек + 7C00-7E00 код бутсектора + 7E00-8200 вспомогательный файл загрузчика (kordldr.f32) + 8400-8C00 информация о кэше для таблицы FAT: 100h входов по 8 + байт: 4 байта (две ссылки - вперёд и назад) для + организации L2-списка всех прочитанных секторов в + порядке возрастания последнего времени использования + + 4 байта для номера сектора; при переполнении кэша + выкидывается элемент из головы списка, то есть тот, + к которому дольше всех не было обращений + 60000-80000 кэш для таблицы FAT (100h секторов) + 80000-90000 текущий кластер текущей рассматриваемой папки + 90000-... кэш для содержимого папок (каждой папке отводится + 2000h байт = 100h входов, одновременно в кэше + может находиться не более 8 папок; + точный размер определяется размером доступной + физической памяти - как правило, непосредственно + перед A0000 размещается EBDA, Extended BIOS Data Area) + +===================================================================== + +Основной процесс загрузки. +Точка входа (start): получает управление от BIOS при загрузке, при этом + dl содержит идентификатор диска, с которого идёт загрузка +1. Настраивает стек ss:sp = 0:7C00 (стек располагается непосредственно перед + кодом), сегмент данных ds = 0, и устанавливает ss:bp на начало + бутсектора (в дальнейшем данные будут адресоваться через [bp+N] - + это освобождает ds и экономит на размере кода). Сохраняет в стеке + идентификатор загрузочного диска для последующего обращения + через byte [bp-2]. +2. LBA-версия: проверяет, поддерживает ли носитель LBA, вызовом функции 41h + прерывания 13h. Если нет, переходит на код обработки ошибок с + сообщением об отсутствии LBA. +CHS-версия: определяет геометрию носителя вызовом функции 8 прерывания 13h и + записывает полученные данные поверх BPB. Если вызов завершился ошибкой, + предполагает уже существующие данные корректными. +3. Вычисляет начало данных FAT-тома, сохраняет его в стек для последующего + обращения через dword [bp-10]. В процессе вычисления узнаёт начало + первой FAT, сохраняет и его в стек для последующего обращения через + dword [bp-6]. +4. (Заканчивая тему параметров в стеке) Помещает в стек dword-значение -1 + для последующего обращения через dword [bp-14] - инициализация + переменной, содержащей текущий сектор, находящийся в кэше FAT + (-1 не является валидным значением для номера сектора FAT). +5. Ищет в корневой папке элемент kordldr.f32. Если не находит - переходит на + код обработки ошибок с сообщением о ненайденном загрузчике. + Замечание: на этом этапе загрузки искать можно только в корневой + папке и только имена, заданные в формате файловой системе FAT + (8+3 - 8 байт на имя, 3 байта на расширение, все буквы должны + быть заглавными, при необходимости имя и расширение дополняются + пробелами, разделяющей точки нет, завершающего нуля нет). +6. Загружает первый кластер файла kordldr.f32 по адресу 0:7E00 и передаёт + ему управление. При этом в регистре eax оказывается абсолютный + номер первого сектора kordldr.f32, а в cx - число считанных секторов + (равное размеру кластера). + +Вспомогательные процедуры бутсектора. +Код обработки ошибок (err): +1. Выводит строку с сообщением об ошибке. +2. Выводит строку "Press any key...". +3. Ждёт нажатия any key. +4. Вызывает int 18h, давая шанс BIOSу попытаться загрузиться откуда-нибудь ещё. +5. Для подстраховки зацикливается. + +Процедура чтения кластера (read_cluster): +на входе должно быть установлено: + ss:bp = 0:7C00 + es:bx = указатель на начало буфера, куда будут прочитаны данные + eax = номер кластера +на выходе: ecx = число прочитанных секторов (размер кластера), + es:bx указывает на конец буфера, в который были прочитаны данные, + eax и старшие слова других 32-битных регистров разрушаются +Загружает в ecx размер кластера, перекодирует номер кластера в номер сектора +и переходит к следующей процедуре. + +Процедура чтения секторов (read_sectors32 и read_sectors2): +на входе должно быть установлено: + ss:bp = 0:7C00 + es:bx = указатель на начало буфера, куда будут прочитаны данные + eax = стартовый сектор (относительно начала логического диска + для read_sectors32, относительно начала данных + для read_sectors2) + cx = число секторов (должно быть больше нуля) +на выходе: es:bx указывает на конец буфера, в который были прочитаны данные + старшие слова 32-битных регистров могут разрушиться +0. Если вызывается read_sectors2, она переводит указанный ей номер сектора + в номер относительно начала логического диска, прибавляя номер сектора + начала данных, хранящийся в стеке как [bp-10]. +1. Переводит стартовый сектор (отсчитываемый от начала тома) в сектор на + устройстве, прибавляя значение соответствующего поля из BPB. +2. В цикле (шаги 3-6) читает секторы, следит за тем, чтобы на каждой итерации + CHS-версия: все читаемые секторы были на одной дорожке. + LBA-версия: число читаемых секторов не превосходило 7Fh (требование + спецификации EDD BIOS). +CHS-версия: +3. Переводит абсолютный номер сектора в CHS-систему: сектор рассчитывается как + единица плюс остаток от деления абсолютного номера на число секторов + на дорожке; дорожка рассчитывается как остаток от деления частного, + полученного на предыдущем шаге, на число дорожек, а цилиндр - как + частное от этого же деления. Если число секторов для чтения больше, + чем число секторов до конца дорожки, уменьшает число секторов для + чтения. +4. Формирует данные для вызова int 13h (ah=2 - чтение, al=число секторов, + dh=головка, (младшие 6 бит cl)=сектор, + (старшие 2 бита cl и весь ch)=дорожка, dl=диск, es:bx->буфер). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, выполняет сброс диска + и повторяет попытку чтения, всего делается не более трёх попыток + (несколько попыток нужно в случае дискеты для гарантии того, что + мотор раскрутился). Если все три раза происходит ошибка чтения, + переходит на код обработки ошибок с сообщением "Read error". +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. +LBA-версия: +3. Если число секторов для чтения больше 7Fh, уменьшает его (для текущей + итерации) до 7Fh. +4. Формирует в стеке пакет для int 13h (кладёт все нужные данные командами + push, причём в обратном порядке: стек - структура LIFO, и данные в + стеке хранятся в обратном порядке по отношению к тому, как их туда + клали). +5. Вызывает BIOS. Если BIOS рапортует об ошибке, переходит на код обработки + ошибок с сообщением "Read error". Очищает стек от пакета, + сформированного на предыдущем шаге. +6. В соответствии с числом прочитанных на текущей итерации секторов + корректирует текущий сектор, число оставшихся секторов и указатель на + буфер (в паре es:bx корректируется es). Если всё прочитано, заканчивает + работу, иначе возвращается на шаг 3. + +Процедура поиска элемента в папке (lookup_in_dir): +на входе должно быть установлено: + ss:bp = 0:7C00 + ds:si = указатель на имя файла в формате FAT (см. выше) + eax = начальный кластер папки + bx = 0 +на выходе: флаг CF определяет, удалось ли найти файл; если удалось, то + CF сброшен и es:di указывает на элемент папки +В цикле считывает кластеры папки и ищет запрошенный элемент в прочитанных +данных. Для чтения кластера использует уже описанную процедуру read_clusters, +для продвижения по цепочке кластеров - описанную далее процедуру +get_next_clusters. Данные читаются в область памяти, начинающуюся с адреса +8000:0000, при этом первые 2000h байт из данных папки (может быть, меньше, +если чтение прервётся раньше) не перекрываются последующими чтениями +(это будет использовано позднее, в системе кэширования из kordldr.f32). +Выход осуществляется в любом из следующих случаев: найден запрошенный элемент; +кончились элементы в папке (первый байт очередного элемента нулевой); +кончились данные папки в соответствии с цепочкой кластеров из FAT. + +Процедура вывода на экран ASCIIZ-строки (out_string): +на входе: ds:si -> строка +В цикле, пока не достигнут завершающий ноль, вызывает функцию int 10h/ah=0Eh. + +===================================================================== + +Работа вспомогательного загрузчика kordldr.f32: +1. Определяет, был ли он загружен CHS- или LBA-версией бутсектора. + В зависимости от этого устанавливает смещения используемых процедур + бутсектора. Критерий проверки: в CHS-версии по адресу err находится + байт 0xE8 (машинная команда call), в LBA-версии по тому же адресу + находится байт 0x14, а адрес процедуры err другой. +2. Узнаёт размер свободной базовой памяти (т.е. свободного непрерывного куска + адресов памяти, начинающегося с 0) вызовом int 12h. В соответствии с + ним вычисляет число элементов в кэше папок. Хотя бы для одного элемента + место должно быть, отсюда ограничение в 592 Kb (94000h байт). + Замечание: этот размер не может превосходить 0A0000h байт и + на практике оказывается немного (на 1-2 килобайта) меньшим из-за + наличия дополнительной области данных BIOS "вверху" базовой памяти. +3. Инициализирует кэширование папок. Бутсектор уже загрузил какую-то часть + данных корневой папки; копирует загруженные данные в кэш и запоминает, + что в кэше есть корневая папка. +4. Инициализирует кэширование FAT. Бутсектор имеет дело с FAT в том и только + том случае, когда ему приходится загружать данные корневой папки, + не поместившиеся в один кластер. В этом случае в памяти присутствует + один сектор FAT (если было несколько обращений - последний из + использованных). +5. Если кластер равен сектору, то бутсектор загрузил только часть файла + kordldr.f32, и загрузчик подгружает вторую свою часть, используя + значения регистров на входе в kordldr.f32. +6. Загружает вторичный загрузчик kord/loader по адресу 1000:0000. Если файл не + найден, или оказался папкой, или оказался слишком большим, то переходит + на код обработки ошибок из бутсектора с сообщением + "Fatal error: cannot load the secondary loader". + Замечание: на этом этапе имя файла уже можно указывать вместе с путём + и в формате ASCIIZ, хотя поддержки длинных имён и неанглийских символов + по-прежнему нет. +7. Изменяет код обработки ошибок бутсектора на переход на метку hooked_err. + Это нужно, чтобы последующие обращения к коду бутсектора в случае + ошибок чтения не выводил соответствующее сообщение с последующей + перезагрузкой, а рапортовал об ошибке чтения, которую могло бы + как-нибудь обработать ядро. +8. Если загрузочный диск имеет идентификатор меньше 0x80, + то устанавливает al='f' ("floppy"), ah=идентификатор диска, + иначе al='h' ("hard"), ah=идентификатор диска-0x80 (номер диска). + (Говорите, дискеток с FAT32 не бывает? В чём-то Вы правы... но + уверены ли Вы, что нет загрузочных устройств, подобных дискетам, + но большего размера, и для которых BIOS-идентификатор меньше 0x80?) + Устанавливает bx='32' (тип файловой системы - FAT32). + Устанавливает si=смещение функции обратного вызова. Поскольку в этот + момент ds=0, то ds:si образуют полный адрес. +9. Передаёт управление по адресу 1000:0000. + +Функция обратного вызова для вторичного загрузчика: + предоставляет возможность чтения файла. +Вход и выход описаны в спецификации на загрузчик. +1. Сохраняет стек вызывающего кода и устанавливает свой стек: + ss:sp = 0:(7C00-10), bp=7C00: пара ss:bp при работе с остальным + кодом должна указывать на 0:7C00, а -10 берётся от того, что + инициализирующий код бутсектора уже поместил в стек 10 байт параметров, + и они должны сохраняться в неизменности. (Значение [ebp-14], + "текущий сектор, находящийся в кэше FAT", не используется после + инициализации кэширования в kordldr.f32.) +2. Разбирает переданные параметры и вызывает нужную из вспомогательных + процедур (загрузки файла либо продолжения загрузки файла). +3. Восстанавливает стек вызывающего кода и возвращает управление. + +Вспомогательные процедуры kordldr.f32. +Процедура получения следующего кластера в FAT (get_next_cluster): +1. Вычисляет номер сектора в FAT, в котором находится запрошенный элемент. + (В секторе 0x200 байт, каждый вход занимает 4 байта.) +2. Проверяет, есть ли сектор в кэше. Если есть, пропускает шаги 3 и 4. +3. Если нет, то в кэш нужно вставить новый элемент. Если кэш ещё не заполнен, + выделяет очередной элемент в конце кэша. Если заполнен, удаляет + самый старый элемент (тот, к которому дольше всего не было обращений); + для того, чтобы отслеживать порядок элементов по времени последнего + обращения, все (выделенные) элементы кэша связаны в двусвязный список, + в котором первым элементом является самый старый, а ссылки вперёд + указывают на следующий по времени последнего обращения. +4. Читает соответствующий сектор FAT с диска. +5. Корректирует список: текущий обрабатываемый элемент удаляется с той позиции, + где он находится, и добавляется в конец. (В случае со свежедобавленными + в кэш элементами удаления не делается, поскольку их в списке ещё нет.) +6. Считывает нужный вход в FAT, сбрасывая старшие 4 бита. +7. Сравнивает прочитанное значение с пределом: если оно строго меньше + 0x0FFFFFF7, то оно задаёт номер следующего кластера в цепочке; + в противном случае цепочка закончилась. + +Процедура загрузки файла (load_file): +1. Текущая рассматриваемая папка - корневая. В цикле выполняет шаги 2-4. +2. Конвертирует имя текущего рассматриваемого компонента имени (компоненты + разделяются символом '/') в FAT-формат 8+3. Если это невозможно + (больше 8 символов в имени, больше 3 символов в расширении или + больше одной точки), возвращается с ошибкой. +3. Ищет элемент с таким именем в текущей рассматриваемой папке. + а) Проверяет, есть ли такая папка в кэше папок. (Идентификация папок + осуществляется по номеру начального кластера.) Если такой папки ещё + нет, добавляет её в кэш; если тот переполняется, выкидывает папку, + к которой дольше всего не было обращений. (Для каждого элемента кэша + хранится метка от 0 до (размер кэша)-1, определяющая его номер при + сортировке по давности последнего обращения. При обращении к какому-то + элементу его метка становится нулевой, а те метки, которые меньше + старого значения, увеличиваются на единицу.) + б) Просматривает в поисках запрошенного имени все элементы из кэша, + используя процедуру из бутсектора. Если обнаруживает искомый элемент, + переходит к шагу 4. Если обнаруживает конец папки, возвращается из + процедуры с ошибкой. + в) В цикле считывает папку посекторно. При этом пропускает начальные + секторы, которые уже находятся в кэше и уже были просмотрены. Каждый + прочитанный сектор копирует в кэш, если там ещё остаётся место, + и просматривает в нём все элементы. Работает, пока не случится одно из + трёх событий: найден искомый элемент; кончились кластеры (судя по + цепочке кластеров в FAT); очередной элемент папки сигнализирует о конце + (первый байт нулевой). В двух последних случаях возвращается с ошибкой. +4. Проверяет тип найденного элемента (файл/папка): последний элемент в + запрошенном имени должен быть файлом, все промежуточные - папками. + Если текущий компонент имени - промежуточный, продвигает текущую + рассматриваемую папку и возвращается к пункту 2. +5. Проходит по цепочке кластеров в FAT и считывает все кластеры в указанный + при вызове буфер последовательными вызовами функции бутсектора; + при этом если несколько кластеров файла расположены на диске + последовательно, то их чтение объединяется в одну операцию. + Следит за тем, чтобы не превысить указанный при вызове процедуры + лимит числа секторов для чтения. + +Процедура продолжения загрузки файла (continue_load_file): встроена + внутрь шага 5 load_file; загружает в регистры нужные значения (ранее + сохранённые из load_file) и продолжает шаг 5. diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/build.bat b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/build.bat new file mode 100644 index 000000000..267c29375 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/build.bat @@ -0,0 +1,3 @@ +@fasm -m 65535 bootsect.asm bootsect.bin +@fasm -m 65535 kordldr.f32.asm kordldr.f32 +@pause \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/kordldr.f32.asm b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/kordldr.f32.asm new file mode 100644 index 000000000..36bfa6475 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/extended_primary_loader/fat32/kordldr.f32.asm @@ -0,0 +1,673 @@ +; Copyright (c) 2008-2009, diamond +; All rights reserved. +; +; Redistribution and use in source and binary forms, with or without +; modification, are permitted provided that the following conditions are met: +; * Redistributions of source code must retain the above copyright +; notice, this list of conditions and the following disclaimer. +; * Redistributions in binary form must reproduce the above copyright +; notice, this list of conditions and the following disclaimer in the +; documentation and/or other materials provided with the distribution. +; * Neither the name of the nor the +; names of its contributors may be used to endorse or promote products +; derived from this software without specific prior written permission. +; +; THIS SOFTWARE IS PROVIDED BY Alexey Teplov aka ''AS IS'' AND ANY +; EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +; WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +; DISCLAIMED. IN NO EVENT SHALL BE LIABLE FOR ANY +; DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +; (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +; ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +; (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +; SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +;***************************************************************************** + + org 0x7E00 +; the KordOS FAT32 bootsector loads first cluster of this file to 0:7E00 and transfers control to here +; ss:bp = 0:7C00 +; ds = 0 +virtual at bp + rb 3 ; BS_jmpBoot + rb 8 ; BS_OEMName, ignored + dw ? ; BPB_BytsPerSec +BPB_SecsPerClus db ? +BPB_RsvdSecCnt dw ? +BPB_NumFATs db ? +BPB_RootEntCnt dw ? + dw ? ; BPB_TotSec16 + db ? ; BPB_Media + dw ? ; BPB_FATSz16 = 0 for FAT32 +BPB_SecPerTrk dw ? +BPB_NumHeads dw ? +BPB_HiddSec dd ? + dd ? ; BPB_TotSec32 +BPB_FATSz32 dd ? +BPB_ExtFlags dw ? + dw ? ; BPB_FSVer +BPB_RootClus dd ? +filesize: + dw ? ; BPB_FSInfo + dw ? ; BPB_BkBootSec + rb 12 ; BPB_Reserved +BS_DrvNum db ? + db ? ; BS_Reserved1 + db ? ; BS_BootSig + dd ? ; BS_VolID +; rb 11 ; BS_VolLab +; rb 5 ; BS_FilSysType, first 5 bytes +read_sectors32 dw ? +read_sectors2 dw ? +err_ dw ? +noloader dw ? +cachelimit dw ? +fatcachehead rw 2 +fatcacheend dw ? + rb 3 ; BS_FilSysType, last 3 bytes +curseg dw ? +num_sectors dd ? +cur_cluster dd ? +next_cluster dd ? +flags dw ? +cur_delta dd ? +end virtual + +; procedures from boot sector +; LBA version +lba_read_sectors2 = 7CD6h +lba_err = 7CAAh +lba_noloader = 7CA7h ; = lba_err - 3 +; CHS version +chs_read_sectors2 = 7CD2h +chs_err = 7CA6h +chs_noloader = 7CA3h ; = chs_err - 3 + + push eax cx ; save our position on disk +; determine version of bootsector (LBA vs CHS) + mov [read_sectors2], chs_read_sectors2 + mov bx, chs_err + mov [err_], bx +; mov [noloader], chs_noloader + cmp byte [bx], 0xE8 ; [chs_err] = 0xE8 for CHS version, 0x14 for LBA version + jz @f + add [read_sectors2], lba_read_sectors2 - chs_read_sectors2 + add [err_], lba_err - chs_err +; mov [noloader], lba_noloader +@@: + xor bx, bx +; determine size of cache for folders + int 12h ; ax = size of available base memory in Kb + sub ax, 92000h / 1024 + jae @f +nomem: + mov si, nomem_str + jmp [err_] +@@: + shr ax, 3 + mov [cachelimit], ax ; size of cache - 1 + mov es, bx +; no folders in cache yet + mov di, foldcache_clus + mov cx, 8*4/2 + 1 + xor ax, ax + rep stosw +; bootsector code caches one FAT sector, [bp-14], in 6000:0000 +; initialize our (more advanced) FAT caching from this + mov di, 8400h + mov cx, di + lea si, [fatcachehead] + mov [si], si ; no sectors in cache: + mov [si+2], si ; 'prev' & 'next' links point to self + mov [fatcacheend], di ; first free item = 8400h + stosw ; 'next cached sector' link + stosw ; 'prev cached sector' link + mov eax, [bp-14] + stosd ; first sector number in cache + test eax, eax + js @f + mov [si], cx ; 'first cached sector' link = 8400h + mov [si+2], cx ; 'next cached sector' link = 8400h + mov [fatcacheend], di ; first free item = 8406h +@@: +; if cluster = sector, we need to read second part of our file +; (bootsector loads only first cluster of kordldr.f32) + pop cx eax ; restore our position on disk + cmp cx, 1 + ja kordldr_full + sub eax, [bp-10] + inc eax + inc eax ; eax = first cluster of kordldr.f32 + call get_next_cluster + jc @f +; jmp [noloader] + mov ax, [err_] + sub ax, 3 + jmp ax +@@: + dec eax + dec eax + push 0x800 + pop es + call [read_sectors2] +kordldr_full: +; bootsector code has read some data of root directory to 8000:0000 +; initialize our folder caching from this + mov eax, [BPB_RootClus] + mov [foldcache_clus], eax + mov cx, [curseg] + mov ax, 8000h + sub cx, ax ; cx = size of data read in paragraphs (0x10 bytes) + shr cx, 1 ; cx = size of folder data read in entries (0x20 bytes) + mov [foldcache_size], cx + shl cx, 4 + push ds + mov ds, ax + push 0x9000 + pop es + xor si, si + xor di, di + rep movsw + pop ds +; ...continue loading... + mov di, secondary_loader_info + call load_file + test bx, bx + mov bx, [err_] + jz @f + mov si, aKernelNotFound + jmp bx +@@: +; for subsequent calls to callback function, hook error handler +; push hooked_err / ret + mov dword [bx], 0x68 + (hooked_err shl 8) + (0xC3 shl 24) +; set registers for secondary loader + mov ah, [bp-2] ; drive id + mov al, 'f' + btr ax, 15 + jnc @f + mov al, 'h' +@@: + mov bx, '32' + mov si, callback + jmp far [si+secondary_loader_info-callback] + +nomem_str db 'No memory',0 + +cluster2sector: + sub eax, 2 +clustersz2sectorsz: + movzx ecx, [BPB_SecsPerClus] + mul ecx + ret + +get_next_cluster: +; in: eax = cluster +; out: if there is next cluster: CF=1, eax = next cluster +; out: if there is no next cluster: CF=0 + push di bx + push ds es + push ss + pop ds + push ss + pop es + push ax + shr eax, 7 +; eax = FAT sector number; look in cache + mov di, 8400h +.cache_lookup: + cmp di, [fatcacheend] + jae .not_in_cache + scasd + scasd + jnz .cache_lookup +.in_cache: + sub di, 8 +; delete this sector from the list + push si + mov si, [di] + mov bx, [di+2] + mov [si+2], bx + mov [bx], si + pop si + jmp @f +.not_in_cache: +; cache miss +; cache is full? + mov di, [fatcacheend] + cmp di, 8C00h + jnz .cache_not_full +; yes, delete the oldest entry + mov di, [fatcachehead] + mov bx, [di] + mov [fatcachehead], bx + push word [di+2] + pop word [bx+2] + jmp .cache_append +.cache_not_full: +; no, allocate new sector + add [fatcacheend], 8 +.cache_append: +; read FAT + mov [di+4], eax + pushad + lea cx, [di + 0x10000 - 0x8400 + (0x6000 shr (9-4-3))] ; +0x10000 - for FASM + shl cx, 9-4-3 + mov es, cx + xor bx, bx + mov cx, 1 + add eax, [bp-6] ; FAT start + sub eax, [bp-10] + call [read_sectors2] + popad +@@: +; add new sector to the end of list + mov bx, di + xchg bx, [fatcachehead+2] + push word [bx] + pop word [di] + mov [bx], di + mov [di+2], bx +; get requested item + lea ax, [di + 0x10000 - 0x8400 + (0x6000 shr (9-4-3))] + pop di + and di, 0x7F + shl di, 2 + shl ax, 9-4-3 + mov ds, ax + and byte [di+3], 0x0F + mov eax, [di] + pop es ds + pop bx di + ;and eax, 0x0FFFFFFF + cmp eax, 0x0FFFFFF7 + ret + +if $ > 0x8000 +error 'get_next_cluster must fit in first sector of kordldr.f32!' +end if + +load_file: +; in: ss:bp = 0:7C00 +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx = status: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found +; out: dx:ax = file size (0xFFFFFFFF if file not found) + mov eax, [BPB_RootClus] ; start from root directory + or dword [filesize], -1 ; initialize file size with invalid value + lea si, [di+6] +parse_dir_loop: +; convert name to FAT name + push di + push ax + push ss + pop es +; convert ASCIIZ filename to FAT name +filename equ bp + mov di, filename + push di + mov cx, 8+3 + mov al, ' ' + rep stosb + pop di + mov cl, 8 ; 8 symbols per name + mov bl, 1 +nameloop: + lodsb + test al, al + jz namedone + cmp al, '/' + jz namedone + cmp al, '.' + jz namedot + dec cx + js badname + cmp al, 'a' + jb @f + cmp al, 'z' + ja @f + sub al, 'a'-'A' +@@: + stosb + jmp nameloop +namedot: + inc bx + jp badname + add di, cx + mov cl, 3 + jmp nameloop +badname: ; do not make direct js/jp to notfound_pop: + ; this generates long forms of conditional jumps and results in longer code + jmp notfound_pop +namedone: +; scan directory + pop ax ; eax = cluster of directory + ; high word of eax is preserved by operations above + push ds + push si +; read a folder sector-by-sector and scan +; first, try to use the cache + push ss + pop ds + mov di, foldcache_mark + xor bx, bx + mov cx, [cachelimit] +@@: + lea si, [di+bx] + mov edx, dword [foldcache_clus+si-foldcache_mark+bx] + cmp edx, eax + jz cacheok + test edx, edx + jz cacheadd ; the cache has place for new entry + inc bx + inc bx + dec cx + jns @b +; the folder is not present in the cache, so add it +; the cache is full; find the oldest entry and replace it with the new one + mov bx, -2 + mov dx, [cachelimit] +@@: + inc bx + inc bx + cmp word [di+bx], dx ; marks have values 0 through [cachelimit] + jnz @b + lea si, [di+bx] +cacheadd: + or word [di+bx], 0xFFFF ; very big value, it will be changed soon + and [foldcache_size+di-foldcache_mark+bx], 0 ; no folder items yet + mov dword [foldcache_clus+si-foldcache_mark+bx], eax +cacheok: +; update cache marks + mov dx, [di+bx] + mov cx, [foldcache_size+di-foldcache_mark+bx] + mov di, [cachelimit] + add di, di +cacheupdate: + cmp [foldcache_mark+di], dx + adc [foldcache_mark+di], 0 + dec di + dec di + jns cacheupdate + and [foldcache_mark+bx], 0 +; done, bx contains (position in cache)*2 + ;mov dx, bx + ;shl dx, 8 ; dx = (position in cache)*0x2000/0x10 + ;add dx, 0x9000 + lea dx, [bx + 0x90] + xchg dl, dh + mov ds, dx + mov si, filename ; ss:si -> filename in FAT style + call scan_for_filename + jz lookup_done +; cache miss, read folder data from disk + mov bx, cx + shr bx, 4 + shl cx, 5 + mov di, cx ; es:di -> free space in cache entry +; external loop: scan clusters +folder_next_cluster: +; internal loop: scan sectors in cluster + push eax + call cluster2sector +folder_next_sector: +; skip first bx sectors + dec bx + jns folder_skip_sector + push cx + push es di + push 0x8000 + pop es + xor bx, bx + mov cx, 1 + push es + push eax + call [read_sectors2] + pop eax +; copy data to the cache... + pop ds + pop di es + cmp di, 0x2000 ; ...if there is free space, of course + jae @f + pusha + mov cx, 0x100 + xor si, si + rep movsw + mov di, es + shr di, 8 + add [ss:foldcache_size+di-0x90], 0x10 ; 0x10 new entries in the cache + popa +@@: + push es + mov cl, 0x10 ; ch=0 at this point + call scan_for_filename + pop es + pop cx + jz lookup_done_pop +folder_skip_sector: + inc eax + loop folder_next_sector + pop eax ; eax = current cluster + call get_next_cluster + jc folder_next_cluster + stc + push eax +lookup_done_pop: + pop eax +lookup_done: + pop si +; CF=1 <=> failed + jnc found + pop ds +notfound: + pop di +notfound2: + mov bx, 2 ; file not found + mov ax, 0xFFFF + mov dx, ax ; invalid file size + ret +notfound_pop: + pop ax + jmp notfound +found: + mov eax, [di+20-2] + mov edx, [di+28] + mov ax, [di+26] ; get cluster + test byte [di+11], 10h ; directory? + pop ds + pop di + jz regular_file + cmp byte [si-1], 0 + jz notfound2 ; don't read directories as regular files +; ok, we have found a directory and the caller requested a file into it + jmp parse_dir_loop ; restart with new cluster in ax +regular_file: + cmp byte [si-1], 0 + jnz notfound2 ; file does not contain another files +; ok, we have found a regular file and the caller requested it +; save file size + mov [filesize], edx + mov si, [di+4] ; [ds:di+4] = limit in 4K blocks + shl si, 3 + push si + les bx, [di] ; es:bx -> buffer +clusloop: +; eax = first cluster, top of stack contains limit in sectors + mov esi, eax ; remember current cluster + xor ecx, ecx ; ecx will contain number of consecutive clusters + mov [cur_delta], ecx + mov edi, eax +clusfind: + inc edi + inc ecx + call get_next_cluster + jnc clusread + cmp eax, edi + jz clusfind + stc +clusread: + pop di ; limit in sectors + movzx edi, di + push eax ; save next cluster + pushf ; save flags +; read cx clusters, starting from si +; calculate number of sectors + xchg eax, ecx + call clustersz2sectorsz + mov [num_sectors], eax + jmp @f +continue_load_file: + les bx, [di] ; es:bx -> buffer + movzx edi, word [di+4] ; di = limit in 4K blocks + shl di, 3 ; now di = limit in sectors + mov eax, [num_sectors] + mov esi, [cur_cluster] + push [next_cluster] + push [flags] + test eax, eax + jz nextclus +@@: +; eax = number of sectors; compare with limit + cmp eax, edi + seta dl + push dx ; limit was exceeded? + jbe @f + mov eax, edi +@@: + sub di, ax ; calculate new limit + sub [num_sectors], eax + mov [cur_cluster], esi +; calculate starting sector + push ax + xchg eax, esi + call cluster2sector + pop cx + add eax, [cur_delta] + add [cur_delta], ecx +; read + call [read_sectors2] + pop dx +; next cluster? +nextclus: + popf + pop eax + mov [next_cluster], eax + pushf + pop [flags] + jnc @f ; no next cluster => return + mov dl, 1 + test di, di + jz @f ; if there is next cluster but current limit is 0 => return: limit exceeded + push di + jmp clusloop ; all is ok, continue +hooked_err: + mov sp, 7C00h-14-2 ; restore stack + mov dl, 3 ; return: read error +@@: + mov bl, dl + mov bh, 0 + mov ax, [filesize] + mov dx, [filesize+2] + ret + +scan_for_filename: +; in: ss:si -> 11-bytes FAT name +; in: ds:0 -> part of directory data +; in: cx = number of entries +; in: bh = 0 +; out: if found: CF=0, ZF=1, es:di -> directory entry +; out: if not found, but continue required: CF=1 and ZF=0 +; out: if not found and zero item reached: CF=1 and ZF=1 + push ds + pop es + xor di, di + push cx + jcxz snoent +sloop: + cmp byte [di], bh + jz snotfound + test byte [di+11], 8 ; volume label? + jnz scont ; ignore volume labels + pusha + mov cx, 11 + repz cmps byte [ss:si], byte [es:di] + popa + jz sdone +scont: + add di, 0x20 + loop sloop +snoent: + inc cx ; clear ZF flag +snotfound: + stc +sdone: + pop cx +lrdret: + ret + +; Callback function for secondary loader +callback: +; in: ax = function number; only functions 1 and 2 are defined for now +; save caller's stack + mov dx, ss + mov cx, sp +; set our stack (required because we need ss=0) + xor si, si + mov ss, si + mov sp, 7C00h-10 + mov bp, 7C00h + push dx + push cx +; call our function + stc ; unsupported function + dec ax + jz callback_readfile + dec ax + jnz callback_ret +; function 2: continue loading file +; can be called only after function 1 returned value bx=1 (only part of file was loaded) +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; out: bx=0 - ok, bx=1 - still only part of file was loaded, bx=3 - read error +; out: dx:ax = file size + call continue_load_file + jmp callback_ret_succ +callback_readfile: +; function 1: read file +; in: ds:di -> information structure +; dw:dw address +; dw limit in 4Kb blocks (0x1000 bytes) (must be non-zero and not greater than 0x100) +; ASCIIZ name +; out: bx=0 - ok, bx=1 - file is too big, only part of file was loaded, bx=2 - file not found, bx=3 - read error +; out: dx:ax = file size (0xFFFFFFFF if file was not found) + call load_file +callback_ret_succ: + clc ; function is supported +callback_ret: +; restore caller's stack + pop cx + pop ss + mov sp, cx +; return to caller + retf + +secondary_loader_info: + dw 0, 0x1000 + dw 0x30000 / 0x1000 + db 'kernel.mnt',0 +aKernelNotFound db 'Fatal error: cannot load the kernel',0 + +;if $ > 0x8200 +;error 'total size of kordldr.f32 must not exceed 1024 bytes!' +;end if + +;foldcache_clus dd 0,0,0,0,0,0,0,0 ; start with no folders in cache +;foldcache_mark dw 0 +; rw 7 +;foldcache_size rw 8 +foldcache_clus rd 8 +foldcache_mark rw 8 +foldcache_size rw 8 diff --git a/kernel/branches/kolibri-lldw/bootloader/floppy1440.inc b/kernel/branches/kolibri-lldw/bootloader/floppy1440.inc new file mode 100644 index 000000000..aaa247a3d --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/floppy1440.inc @@ -0,0 +1,26 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) + BPB_TotSec16 dw 2880 ; count of sectors on the volume (2880 for 1.44 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 18 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/kolibri-lldw/bootloader/floppy1680.inc b/kernel/branches/kolibri-lldw/bootloader/floppy1680.inc new file mode 100644 index 000000000..b0c561f3e --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/floppy1680.inc @@ -0,0 +1,26 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 112 ; count of 32-byte dir. entries (112*32 = 7 sectors) + BPB_TotSec16 dw 3360 ; count of sectors on the volume (3360 for 1.68 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 10 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 21 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/kolibri-lldw/bootloader/floppy1743.inc b/kernel/branches/kolibri-lldw/bootloader/floppy1743.inc new file mode 100644 index 000000000..148e966b0 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/floppy1743.inc @@ -0,0 +1,26 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 1 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 224 ; count of 32-byte dir. entries (224*32 = 14 sectors) + BPB_TotSec16 dw 3486 ; count of sectors on the volume (3486 for 1.74 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 11 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 21 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/kolibri-lldw/bootloader/floppy2880.inc b/kernel/branches/kolibri-lldw/bootloader/floppy2880.inc new file mode 100644 index 000000000..48a205788 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/floppy2880.inc @@ -0,0 +1,26 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + BS_OEMName db 'KOLIBRI ' ; db 8 + BPB_BytsPerSec dw 512 ; bytes per sector + BPB_SecPerClus db 2 ; sectors per cluster + BPB_RsvdSecCnt dw 1 ; number of reserver sectors + BPB_NumFATs db 2 ; count of FAT data structures + BPB_RootEntCnt dw 240 ; count of 32-byte dir. entries (240*32 = 15 sectors) + BPB_TotSec16 dw 5760 ; count of sectors on the volume (5760 for 2.88 mbytes disk) + BPB_Media db 0f0h ; f0 - used for removable media + BPB_FATSz16 dw 9 ; count of sectors by one copy of FAT + BPB_SecPerTrk dw 36 ; sectors per track + BPB_NumHeads dw 2 ; number of heads + BPB_HiddSec dd 0 ; count of hidden sectors + BPB_TotSec32 dd 0 ; count of sectors on the volume (if > 65535) + BS_DrvNum db 0 ; int 13h drive number + BS_Reserved db 0 ; reserved + BS_BootSig db 29h ; Extended boot signature + BS_VolID dd 0 ; Volume serial number + BS_VolLab db 'KOLIBRI ' ; Volume label (db 11) + BS_FilSysType db 'FAT12 ' ; file system type (db 8) diff --git a/kernel/branches/kolibri-lldw/bootloader/grub4kos.asm b/kernel/branches/kolibri-lldw/bootloader/grub4kos.asm new file mode 100644 index 000000000..7d81ab0a7 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/grub4kos.asm @@ -0,0 +1,296 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2014-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Kolibri OS support loader for GRUB +; +; Copyright (C) Alex Nogueira Teixeira +; Copyright (C) Diamond +; Copyright (C) Dmitry Kartashov aka shurf +; Copyright (C) Serge +; +; Distributed under GPL, see file COPYING for details +; +; Version 1.0 + +lf = 0x0A +cr = 0x0D + +use32 + + +org 0x100000 + +mboot: + dd 0x1BADB002 + dd 0x00010003 + dd -(0x1BADB002 + 0x00010003) + dd mboot + dd 0x100000 + dd __edata + dd __end + dd __start + +align 16 +__start: + +virtual at ebp+3 +.BS_OEMName rb 8 +.BPB_BytsPerSec rw 1 ; bytes per sector +.BPB_SecPerClus rb 1 ; sectors per cluster +.BPB_RsvdSecCnt rw 1 ; number of reserver sectors +.BPB_NumFATs rb 1 ; count of FAT data structures +.BPB_RootEntCnt rw 1 ; count of 32-byte dir. entries (224*32 = 14 sectors) +.BPB_TotSec16 rw 1 ; count of sectors on the volume (2880 for 1.44 mbytes disk) +.BPB_Media rb 1 ; f0 - used for removable media +.BPB_FATSz16 rw 1 ; count of sectors by one copy of FAT +.BPB_SecPerTrk rw 1 ; sectors per track +.BPB_NumHeads rw 1 ; number of heads +.BPB_HiddSec rd 1 ; count of hidden sectors +.BPB_TotSec32 rd 1 ; count of sectors on the volume (if > 65535) +end virtual + + cld + mov esi, mboot + mov edi, 0x80000 + mov ecx, 624/4 ;magic value + rep movsd + jmp .check_mbi + +org $-0x80000 +align 4 +.check_mbi: + cmp eax, 0x2BADB002 + mov esi, sz_invboot + jne .panic + + bt dword [ebx], 3 + mov esi, sz_nomods + jnc .panic + + mov edx, [ebx+20] ;mods_count + mov edi, [ebx+24] ;mods_addr + cmp edx, 1 + mov esi, sz_nomods + jne .panic + +.scan_mod: + mov ebp, [edi] ;image start + mov ecx, [edi+4] ;image end + sub ecx, ebp ;image size + cmp ecx, 512*18*80*2 ;1.44 floppy + mov esi, sz_image + jne .panic + + mov [_image_start], ebp + mov [_image_size], ecx + +; calculate some disk parameters +; - beginning sector of RootDir + + movzx eax, word [.BPB_FATSz16] + movzx ecx, byte [.BPB_NumFATs] + mul ecx + add ax, [.BPB_RsvdSecCnt] + mov [FirstRootDirSecNum], eax + mov esi, eax + +; - count of sectors in RootDir + movzx ebx, word [.BPB_BytsPerSec] + mov cl, 5 ; divide ax by 32 + shr ebx, cl ; bx = directory entries per sector + movzx eax, word [.BPB_RootEntCnt] + xor edx, edx + div ebx + mov [RootDirSecs], eax + + ; - data start + add esi, eax ; add beginning sector of RootDir and count sectors in RootDir + mov [data_start], esi + +; reading root directory +; al=count root dir sectrors !!!! TODO: al, max 255 sectors !!!! + + mov eax, [FirstRootDirSecNum] + mul word [.BPB_BytsPerSec] + lea esi, [ebp+eax] + + mov eax, [RootDirSecs] + mul word [.BPB_BytsPerSec] + add eax, esi ; EAX = end of root dir. in buffer pos_read_tmp + +; find kernel file in root directory + +.loop_find_dir_entry: + push esi + mov ecx, 11 + mov edi, kernel_name + rep cmpsb ; compare es:si and es:di, cx bytes long + pop esi + je .found_kernel_file + add esi, 32 ; next dir. entry + cmp esi, eax ; end of directory + jb .loop_find_dir_entry + + mov esi, sz_kernel + jmp .panic + + ; === KERNEL FOUND. LOADING... === + +.found_kernel_file: + + movzx ecx, word [esi+01ah] ; first cluster of kernel file + + ; reading first FAT table + movzx eax, word [.BPB_RsvdSecCnt] ; begin first FAT abs sector number + mul word [.BPB_BytsPerSec] + lea ebx, [ebp+eax] ; FAT address + +;ebx = FAT +;ecx = cluster +;esi = src +;edi = dst +;ebp = image + +; copy kernel file + + movzx eax, word [.BPB_BytsPerSec] + movsx edx, byte [.BPB_SecPerClus] + mul edx + shr eax, 2 + mov [cluster_size], eax + + mov edi, 0x10000 ;kernel base address + +.copy_kernel: + + ; convert cluster number to sector number + mov eax, ecx ; data cluster to read + sub eax, 2 + movzx edx, byte [.BPB_SecPerClus] + mul edx + add eax, [data_start] + movzx edx, word [.BPB_BytsPerSec] + mul edx + + lea esi, [ebp+eax] + mov edx, ecx + mov ecx, [cluster_size] + rep movsd + mov ecx, edx + + shr edx, 1 + pushf + add edx, ecx ; di = bp * 1.5 + mov ax, word [ebx+edx] ; read next entry from FAT-chain + popf + jc .move_4_right + and ax, 0fffh + jmp .verify_end_sector +.move_4_right: + shr ax, 4 +.verify_end_sector: + cmp ax, 0ff8h ; last cluster + jae .execute_kernel + movzx ecx, ax + jmp .copy_kernel + +.execute_kernel: + + mov edi, 0x100000 + mov esi, [_image_start] + mov ecx, [_image_size] + shr ecx, 2 + rep movsd + xor eax, eax + mov ecx, 1024 + rep stosd + + xor ebx, ebx + xor ecx, ecx + xor edx, edx + xor esi, esi + xor edi, edi + xor ebp, ebp + xor esp, esp + + lgdt [.tmp_gdt] + jmp far 0x08:.mode_16 and 0xFFFF + +.panic: + mov ebx, sz_halt + mov edx, 0xb8000+160*10+2 + mov ah, 0x07 +.line: + mov edi, edx +.print: + lodsb + test al, al + jz .print_next + stosw + jmp .print + +.print_next: + test ebx, ebx + jz ._hlt + + mov esi, ebx + xor ebx, ebx + add edx, 160 + jmp .line + +._hlt: + hlt + jmp ._hlt + +align 8 +.tmp_gdt: dw 15 + dd .tmp_gdt + dw 0 + +.code16: dw 0xFFFF + dw 0 + db 8 + db 10011010b + dw 0 + +use16 +.mode_16: + mov eax, cr0 + and eax, not 0x80000001 + mov cr0, eax + jmp far 0x8000:.real_mode and 0xFFFF + +.real_mode: + xor eax, eax + mov ds, ax + mov es, ax + mov ss, ax + mov gs, ax + mov fs, ax + jmp far 0x1000:0000 + + +sz_invboot db 'Invalid multiboot loader magic value',0 +sz_nomods db 'No image loaded',0 +sz_image db 'Image size invalid',0 +sz_halt db 'Halted',0 + +sz_kernel db cr +kernel_name db 'KERNEL MNT ?',0 + +org $+0x80000 +__edata: + +align 4 +_image_start rd 1 +_image_size rd 1 + +FirstRootDirSecNum rd 1 +RootDirSecs rd 1 +data_start rd 1 +cluster_size rd 1 +__end: diff --git a/kernel/branches/kolibri-lldw/bootloader/readme b/kernel/branches/kolibri-lldw/bootloader/readme new file mode 100644 index 000000000..b68c8bac0 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/readme @@ -0,0 +1,50 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2011. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +Загрузочный сектор для ОС Колибри (FAT12, дискета) + +- Описание + Позволяет загружать KERNEL.MNT с дискет/образов + объёмом 1.44M, 1.68M, 1.72M и 2.88M + Для выбора объёма диска, для которого надо собрать + загрузочный сектор, необходимо в файле boot_fat12.asm + раскомментировать строку вида: + include 'floppy????.inc' + для необходимого объёма диска. Доступные варианты: + floppy1440.inc, + floppy1680.inc, + floppy1743.inc и floppy2880.inc + +- Сборка + fasm boot_fat12.asm + +- Для записи загрузочного сектора на диск/образ под Linux + можно воспользоваться следующей командой: + dd if=boot_fat12.bin of=288.img bs=512 count=1 conv=notrunc + +--------------------------------------------------------------------- + +Floppy FAT12 boot sector for KolibriOS. + +- Description + Allows booting KERNEL.MNT floppies/images + with volumes of 1.44M, 1.68M, 1.72M and 2.88M + To select the volume of the disk, which should gather + boot sector, it was necessary in file boot_fat12.asm + uncomment line: + include 'floppy????. inc' + for the necessary disk volume. Available options is: + floppy1440.inc, + floppy1680.inc, + floppy1743.inc and floppy2880.inc + +- Compile + fasm boot_fat12.asm + +- To write boot sector to the floppy/image under Linux + you can use the following command: + dd if=boot_fat12.bin of=288.img bs=512 count=1 conv=notrunc diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/Tupfile.lua b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/Tupfile.lua new file mode 100644 index 000000000..ef45904ae --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("uefi64kos.asm", "fasm -dUEFI=1 -dextended_primary_loader=1 %f %o", "bootx64.efi") +tup.rule("uefi32kos.asm", "fasm -dUEFI=1 -dextended_primary_loader=1 %f %o", "bootia32.efi") diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/kolibri.ini b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/kolibri.ini new file mode 100644 index 000000000..aa023f9b7 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/kolibri.ini @@ -0,0 +1,21 @@ +; Screen resolution +resolution=1024x768 + +; Duplicate debug output to the screen +debug_print=0 + +; Start LAUNCHER app after kernel is loaded +launcher_start=1 + +; Configure MTRR's +mtrr=1 + +; 3: use ramdisk loaded from kolibri.img +; 5: don't use ramdisk, use /sys directory +imgfrom=3 + +; Path to /sys directory, only internal +; disk drives are alowed (no usbdisk). +; Example: syspath=/HD0/1/KOLIBRIOS +syspath=/rd/1 + diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi.inc b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi.inc new file mode 100644 index 000000000..edb37bdfb --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi.inc @@ -0,0 +1,272 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Version 2, or (at your option) any later version. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Based on UEFI library for fasm by bzt, Public Domain. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +EFI_LOCATE_SEARCH_TYPE: +.AllHandles = 0 +.ByRegisterNotify = 1 +.ByProtocol = 2 + +; EFI_MEMORY_TYPE +EFI_RESERVED_MEMORY_TYPE = 0 +EFI_LOADER_CODE = 1 +EFI_LOADER_DATA = 2 +EFI_BOOT_SERVICES_CODE = 3 +EFI_BOOT_SERVICES_DATA = 4 +EFI_RUNTIME_SERVICES_CODE = 5 +EFI_RUNTIME_SERVICES_DATA = 6 +EFI_CONVENTIONAL_MEMORY = 7 +EFI_UNUSABLE_MEMORY = 8 +EFI_ACPI_RECLAIM_MEMORY = 9 +EFI_ACPI_MEMORY_NVS = 10 +EFI_MEMORY_MAPPED_IO = 11 +EFI_MEMORY_MAPPED_IO_PORT_SPACE = 12 +EFI_PAL_CODE = 13 +EFI_PERSISTENT_MEMORY = 14 +EFI_MAX_MEMORY_TYPE = 15 + +; EFI_ALLOCATE_TYPE +EFI_ALLOCATE_ANY_PAGES = 0 +EFI_ALLOCATE_MAX_ADDRESS = 1 +EFI_ALLOCATE_ADDRESS = 2 + +EFI_MEMORY_UC = 0x00000001 +EFI_MEMORY_WC = 0x00000002 +EFI_MEMORY_WT = 0x00000004 +EFI_MEMORY_WB = 0x00000008 +EFI_MEMORY_UCE = 0x00000010 +EFI_MEMORY_WP = 0x00001000 +EFI_MEMORY_RP = 0x00002000 +EFI_MEMORY_XP = 0x00004000 +EFI_MEMORY_NV = 0x00008000 +EFI_MEMORY_MORE_RELIABLE = 0x00010000 +EFI_MEMORY_RO = 0x00020000 + +EFI_SUCCESS = 0 +EFI_LOAD_ERROR = EFIERR or 1 +EFI_INVALID_PARAMETER = EFIERR or 2 +EFI_UNSUPPORTED = EFIERR or 3 +EFI_BAD_BUFFER_SIZE = EFIERR or 4 +EFI_BUFFER_TOO_SMALL = EFIERR or 5 +EFI_NOT_READY = EFIERR or 6 +EFI_DEVICE_ERROR = EFIERR or 7 +EFI_WRITE_PROTECTED = EFIERR or 8 +EFI_OUT_OF_RESOURCES = EFIERR or 9 +EFI_VOLUME_CORRUPTED = EFIERR or 10 +EFI_VOLUME_FULL = EFIERR or 11 +EFI_NO_MEDIA = EFIERR or 12 +EFI_MEDIA_CHANGED = EFIERR or 13 +EFI_NOT_FOUND = EFIERR or 14 +EFI_ACCESS_DENIED = EFIERR or 15 +EFI_NO_RESPONSE = EFIERR or 16 +EFI_NO_MAPPING = EFIERR or 17 +EFI_TIMEOUT = EFIERR or 18 +EFI_NOT_STARTED = EFIERR or 19 +EFI_ALREADY_STARTED = EFIERR or 20 +EFI_ABORTED = EFIERR or 21 +EFI_ICMP_ERROR = EFIERR or 22 +EFI_TFTP_ERROR = EFIERR or 23 +EFI_PROTOCOL_ERROR = EFIERR or 24 + + +EFI_FILE_SYSTEM_INFO_ID equ 0x93,0x6e,0x57,0x09,0x3f,0x6d,0xd2,0x11, \ + 0x39,0x8e,0x00,0xa0,0xc9,0x69,0x72,0x3b + +EFI_SYSTEM_TABLE_SIGNATURE equ 0x49,0x42,0x49,0x20,0x53,0x59,0x53,0x54 + +EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID equ 0x22,0x5b,0x4e,0x96, \ + 0x59,0x64,0xd2,0x11, \ + 0x8e,0x39,0x00,0xa0, \ + 0xc9,0x69,0x72,0x3b + +EFI_LOADED_IMAGE_PROTOCOL_GUID equ 0xA1,0x31,0x1b,0x5b,0x62,0x95,0xd2,0x11, \ + 0x8E,0x3F,0x00,0xA0,0xC9,0x69,0x72,0x3B + +EFI_BLOCK_IO_PROTOCOL_GUID equ 0x21,0x5b,0x4e,0x96,0x59,0x64,0xd2,0x11, \ + 0x8e,0x39,0x00,0xa0,0xc9,0x69,0x72,0x3b + +EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID equ 0xde,0xa9,0x42,0x90,0xdc,0x23,0x38,0x4a, \ + 0x96,0xfb,0x7a,0xde,0xd0,0x80,0x51,0x6a + +EFI_FILE_MODE_READ = 1 +EFI_FILE_MODE_WRITE = 2 +EFI_FILE_MODE_CREATE = 0x8000000000000000 + +struct EFI_MEMORY_DESCRIPTOR + Type dd ? + dd ? ; align + PhysicalStart DQ ? + VirtualStart DQ ? + NumberOfPages DQ ? + Attribute DQ ? +ends + +struct EFI_FILE_SYSTEM_INFO + Size DQ ? + ReadOnly db ? + rb 7 + VolumeSize DQ ? + FreeSpace DQ ? + BlockSize dd ? + VolumeLabel rw 32 +ends + +struct EFI_TABLE_HEADER + Signature DQ ? + Revision dd ? + HeaderSize dd ? + CRC32 dd ? + Reserved dd ? +ends + +struct EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL + Reset DN ? + OutputString DN ? + TestString DN ? + QueryMode DN ? + SetMode DN ? + SetAttribute DN ? + ClearScreen DN ? + SetCursorPosition DN ? + EnableCursor DN ? + Mode DN ? +ends + + +struct SIMPLE_INPUT_INTERFACE + Reset DN ? + ReadKeyStroke DN ? + WaitForKey DN ? +ends + +struct EFI_BOOT_SERVICES + Hdr EFI_TABLE_HEADER + RaisePriority DN ? + RestorePriority DN ? + AllocatePages DN ? + FreePages DN ? + GetMemoryMap DN ? + AllocatePool DN ? + FreePool DN ? + CreateEvent DN ? + SetTimer DN ? + WaitForEvent DN ? + SignalEvent DN ? + CloseEvent DN ? + CheckEvent DN ? + InstallProtocolInterface DN ? + ReInstallProtocolInterface DN ? + UnInstallProtocolInterface DN ? + HandleProtocol DN ? + Reserved DN ? + RegisterProtocolNotify DN ? + LocateHandle DN ? + LocateDevicePath DN ? + InstallConfigurationTable DN ? + ImageLoad DN ? + ImageStart DN ? + Exit DN ? + ImageUnLoad DN ? + ExitBootServices DN ? + GetNextMonotonicCount DN ? + Stall DN ? + SetWatchdogTimer DN ? + ConnectController DN ? + DisConnectController DN ? + OpenProtocol DN ? + CloseProtocol DN ? + OpenProtocolInformation DN ? + ProtocolsPerHandle DN ? + LocateHandleBuffer DN ? + LocateProtocol DN ? + InstallMultipleProtocolInterfaces DN ? + UnInstallMultipleProtocolInterfaces DN ? + CalculateCrc32 DN ? + CopyMem DN ? + SetMem DN ? +ends + +struct EFI_RUNTIME_SERVICES + Hdr EFI_TABLE_HEADER + GetTime DN ? + SetTime DN ? + GetWakeUpTime DN ? + SetWakeUpTime DN ? + SetVirtualAddressMap DN ? + ConvertPointer DN ? + GetVariable DN ? + GetNextVariableName DN ? + SetVariable DN ? + GetNextHighMonoCount DN ? + ResetSystem DN ? +ends + +struct EFI_SIMPLE_FILE_SYSTEM_PROTOCOL + Revision DQ ? + OpenVolume DN ? +ends + +struct EFI_FILE_PROTOCOL + Revision DQ ? + Open DN ? + Close DN ? + Delete DN ? + Read DN ? + Write DN ? + GetPosition DN ? + SetPosition DN ? + GetInfo DN ? + SetInfo DN ? + Flush DN ? + OpenEx DN ? + ReadEx DN ? + WriteEx DN ? + FlushEx DN ? +ends + +struct EFI_GRAPHICS_OUTPUT_PROTOCOL + QueryMode DN ? + SetMode DN ? + Blt DN ? + Mode DN ? +ends + +struct EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE + MaxMode dd ? + Mode dd ? + Info DN ? + SizeOfInfo DN ? + FrameBufferBase DQ ? + FrameBufferSize DN ? +ends + +EFI_GRAPHICS_PIXEL_FORMAT: +.PixelRedGreenBlueReserved8BitPerColor = 0 +.PixelBlueGreenRedReserved8BitPerColor = 1 +.PixelBitMask = 2 +.PixelBltOnly = 3 +.PixelFormatMax = 4 + +struct EFI_PIXEL_BITMASK + RedMask dd ? + GreenMask dd ? + BlueMask dd ? + ReservedMask dd ? +ends + +struct EFI_GRAPHICS_OUTPUT_MODE_INFORMATION + Version dd ? + HorizontalResolution dd ? + VerticalResolution dd ? + PixelFormat dd ? + PixelInformation EFI_PIXEL_BITMASK + PixelsPerScanLine dd ? +ends diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32.inc b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32.inc new file mode 100644 index 000000000..7b55a6b56 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32.inc @@ -0,0 +1,58 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Version 2, or (at your option) any later version. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Based on UEFI library for fasm by bzt, Public Domain. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +DN fix dd ; native + +include "uefi.inc" + +EFIERR = 0x80000000 + +struct EFI_SYSTEM_TABLE + Hdr EFI_TABLE_HEADER + FirmwareVendor dd ? + FirmwareRevision dd ? + ConsoleInHandle dd ? + ConIn dd ? + ConsoleOutHandle dd ? + ConOut dd ? + StandardErrorHandle dd ? + StdErr dd ? + RuntimeServices dd ? + BootServices dd ? + NumberOfTableEntries dd ? + ConfigurationTable dd ? +ends + +struct EFI_CONFIGURATION_TABLE + VendorGUID rd 4 + VendorTable dd ? +ends + +struct EFI_LOADED_IMAGE_PROTOCOL + Revision dd ? + ParentHandle dd ? + SystemTable dd ? + DeviceHandle dd ? + FilePath dd ? + Reserved dd ? + LoadOptionsSize dd ? + ImageBase dd ? + ImageSize DQ ? + ImageCodeType dd ? + ImageDataType dd ? + UnLoad dd ? +ends + +section '.text' code executable readable + +uefifunc: + ret diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32kos.asm b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32kos.asm new file mode 100644 index 000000000..a4be7bf01 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi32kos.asm @@ -0,0 +1,990 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Version 2, or (at your option) any later version. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +format pe efi +entry main + +section '.text' code executable readable + +include '../../struct.inc' +include '../../macros.inc' +include '../../proc32.inc' +include '../../const.inc' +include 'uefi32.inc' + +MEMORY_MAP_SIZE = 0x4000 +GOP_BUFFER_SIZE = 0x100 +LIP_BUFFER_SIZE = 0x100 +FILE_BUFFER_SIZE = 0x1000 + +KERNEL_BASE = 0x10000 +RAMDISK_BASE = 0x100000 +MAX_FILE_SIZE = 0x10000000 + +CODE_32_SELECTOR = 8 +DATA_32_SELECTOR = 16 + +; linux/arch/x86/include/uapi/asm/e820.h +E820_RAM = 1 +E820_RESERVED = 2 +E820_ACPI = 3 +E820_NVS = 4 +E820_UNUSABLE = 5 +E820_PMEM = 7 + +proc load_file stdcall uses ebx esi edi, _root, _name, _buffer, _size, _fatal + mov eax, [_root] + ccall [eax+EFI_FILE_PROTOCOL.Open], eax, file_handle, [_name], \ + EFI_FILE_MODE_READ, 0 + test eax, eax + jz @f + xor eax, eax + cmp [_fatal], 1 + jnz .done + mov ebx, [efi_table] + mov ebx, [ebx+EFI_SYSTEM_TABLE.ConOut] + ccall [ebx+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], ebx, \ + msg_error_open_file + ccall [ebx+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], ebx, \ + [_name] + jmp $ +@@: + mov eax, [file_handle] + lea ecx, [_size] + ccall [eax+EFI_FILE_PROTOCOL.Read], eax, ecx, [_buffer] + mov eax, [file_handle] + ccall [eax+EFI_FILE_PROTOCOL.Close], eax + mov eax, [_size] +.done: + ret +endp + +proc skip_whitespace +.next_char: + cmp byte[esi], 0 + jz .done + cmp byte[esi], 0x20 ; ' ' + jz .whitespace + cmp byte[esi], 9 ; '\t' + jz .whitespace + jmp .done +.whitespace: + inc esi + jmp .next_char +.done: + ret +endp + +proc skip_until_newline +.next_char: + cmp byte[esi], 0 + jz .done + cmp byte[esi], 0xd ; '\r' + jz .done + cmp byte[esi], 0xa ; '\n' + jz .done + inc esi + jmp .next_char +.done: + ret +endp + +proc skip_newline +.next_char: + cmp byte[esi], 0xd ; '\r' + jz .newline + cmp byte[esi], 0xa ; '\n' + jz .newline + jmp .done +.newline: + inc esi + jmp .next_char +.done: + ret +endp + +proc skip_line + call skip_until_newline + call skip_newline + ret +endp + +proc dec2bin + mov edx, 0 +.next_char: + movzx eax, byte[esi] + test eax, eax + jz .done + sub eax, '0' + jb .done + cmp eax, 9 + ja .done + inc esi + imul edx, 10 + add edx, eax + jmp .next_char +.done: + mov eax, edx + ret +endp + +proc parse_option + mov ebx, config_options-3*4 +.try_next_option: + add ebx, 3*4 + mov edi, esi + mov edx, [ebx] ; option name + test edx, edx + jz .done +.next_char: + cmp byte[edx], 0 + jnz @f + cmp byte[edi], '=' + jz .opt_name_ok +@@: + cmp byte[edi], 0 + jz .done + movzx eax, byte[edi] + cmp [edx], al + jnz .try_next_option + inc edi + inc edx + jmp .next_char +.opt_name_ok: + inc edi + mov esi, edi + call dword[ebx+4] +.done: + ret +endp + +proc parse_line +.next_line: + cmp byte[esi], 0 + jz .done + cmp byte[esi], 0xd ; '\r' + jz .skip + cmp byte[esi], 0xa ; '\n' + jz .skip + cmp byte[esi], '#' + jz .skip + call parse_option + call skip_line + jmp .next_line +.skip: + call skip_line + jmp .next_line +.done: + ret +endp + +proc cfg_opt_func_resolution + call dec2bin + xor edx, edx + mov [edx+BOOT_LO.x_res], ax + cmp byte[esi], 'x' + jz @f + cmp byte[esi], '*' + jz @f + jmp .done +@@: + inc esi + call dec2bin + xor edx, edx + mov [edx+BOOT_LO.y_res], ax + mov [cfg_opt_used_resolution], 1 +.done: + ret +endp + +proc cfg_opt_func_acpi + call dec2bin + mov [cfg_opt_used_acpi], 1 + mov [cfg_opt_value_acpi], al + ret +endp + +proc cfg_opt_func_debug_print + call dec2bin + mov [cfg_opt_used_debug_print], 1 + mov [cfg_opt_value_debug_print], al + ret +endp + +proc cfg_opt_func_launcher_start + call dec2bin + mov [cfg_opt_used_launcher_start], 1 + mov [cfg_opt_value_launcher_start], al + ret +endp + +proc cfg_opt_func_mtrr + call dec2bin + mov [cfg_opt_used_mtrr], 1 + mov [cfg_opt_value_mtrr], al + ret +endp + +proc cfg_opt_func_ask_params + call dec2bin + mov [cfg_opt_used_ask_params], 1 + mov [cfg_opt_value_ask_params], al + ret +endp + +proc cfg_opt_func_imgfrom + call dec2bin + mov [cfg_opt_used_imgfrom], 1 + mov [cfg_opt_value_imgfrom], al + ret +endp + +proc cfg_opt_func_syspath + mov edi, cfg_opt_value_syspath +.next_char: + movzx eax, byte[esi] + cmp al, 0xd ; \r + jz .done + cmp al, 0xa ; \n + jz .done + inc esi + stosb + jmp .next_char +.done: + mov byte[edi], 0 + ret +endp + +proc parse_config stdcall uses ebx esi edi, _buffer +; mov esi, [_buffer] + mov esi, KERNEL_BASE +.next_line: + call parse_line + cmp byte[esi], 0 + jnz .next_line + ret +endp + +proc read_options_from_config stdcall uses ebx esi edi + mov ebx, [efi_table] + mov ebx, [ebx+EFI_SYSTEM_TABLE.BootServices] + ccall [ebx+EFI_BOOT_SERVICES.HandleProtocol], [efi_handle], \ + lipuuid, lip_interface + test eax, eax + jnz .error + mov eax, [lip_interface] + + mov ebx, [efi_table] + mov ebx, [ebx+EFI_SYSTEM_TABLE.BootServices] + ccall [ebx+EFI_BOOT_SERVICES.HandleProtocol], \ + [eax+EFI_LOADED_IMAGE_PROTOCOL.DeviceHandle], sfspguid, \ + sfsp_interface + test eax, eax + jnz .error + + mov eax, [sfsp_interface] + ccall [eax+EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume], eax, esp_root + test eax, eax + jnz .error + + stdcall load_file, [esp_root], file_name, KERNEL_BASE, \ + FILE_BUFFER_SIZE, 0 + test eax, eax + jz @f + stdcall parse_config, KERNEL_BASE +@@: +.error: + ret +endp + +proc print_vmode uses eax ebx ecx esi edi, _gop_if + mov ebx, [_gop_if] + call clearbuf + mov eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + mov edi, msg + call num2dec + mov eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + mov edi, msg+8*2 + call num2dec + + mov eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat] + mov edi, msg+16*2 + call num2dec + + mov eax, [ebx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine] + mov edi, msg+24*2 + call num2dec + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + ret +endp + +proc find_vmode_index_by_resolution uses ebx esi edi + mov [cfg_opt_value_vmode], 0 +.next_mode: +; mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] +; ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ +; msg_query_vmode + + movzx ecx, [cfg_opt_value_vmode] + mov eax, [gop_interface] + ccall [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode], eax, ecx, \ + gop_info_size, gop_info + test eax, eax + jz @f + call clearbuf + mov edi, msg + call num2hex + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + jmp .skip_mode +@@: + mov ecx, [gop_info] + stdcall print_vmode, ecx + ; PixelBlueGreenRedReserved8BitPerColor + cmp [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1 + jnz .skip_mode + xor edx, edx + mov eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + cmp ax, [edx+BOOT_LO.x_res] + jnz .skip_mode + mov eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + cmp ax, [edx+BOOT_LO.y_res] + jnz .skip_mode + jmp .done +.skip_mode: + inc [cfg_opt_value_vmode] + movzx eax, [cfg_opt_value_vmode] + mov ecx, [gop_interface] + mov edx, [ecx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode] + cmp eax, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode] + jnz .next_mode + mov [cfg_opt_used_resolution], 0 + mov [cfg_opt_value_ask_params], 1 + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_error_no_such_vmode + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg_error + jmp $ +.error: +.done: + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_vmode_found + ret +endp + +proc ask_for_params + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_ask_for_params + jmp $ +.error: +.done: + ret +endp + +main: + mov esi, [esp+4] + mov [efi_handle], esi + mov esi, [esp+8] + mov [efi_table], esi + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset], eax, 1 + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_u4k_loaded + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_read_options + call read_options_from_config + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_load_kernel + stdcall load_file, [esp_root], kernel_name, KERNEL_BASE, MAX_FILE_SIZE, 1 + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_load_ramdisk + stdcall load_file, [esp_root], ramdisk_name, RAMDISK_BASE, MAX_FILE_SIZE, 1 + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_alloc_devicesdat + + mov eax, [esi+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.AllocatePages], \ + EFI_ALLOCATE_MAX_ADDRESS, EFI_RESERVED_MEMORY_TYPE, 1, \ + devicesdat_data + call halt_on_error + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_load_devicesdat + + ccall load_file, [esp_root], devicesdat_name, [devicesdat_data], \ + [devicesdat_size], 0 + mov [devicesdat_size], eax + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_locate_gop_handlers + + mov eax, [esi+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.LocateHandle], \ + EFI_LOCATE_SEARCH_TYPE.ByProtocol, gopuuid, 0, \ + gop_buffer_size, gop_buffer + mov [status], eax + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_gop_buffer_size + call clearbuf + mov eax, [gop_buffer_size] + mov edi, msg + call num2hex + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + + mov eax, [status] + call halt_on_error + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_look_for_gop_handler + + mov ebx, gop_buffer +.next_gop_handle: + mov eax, ebx + sub eax, gop_buffer + cmp eax, [gop_buffer_size] + jb @f + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_error_out_of_handlers + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg_error + jmp $ +@@: + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_query_handler + + mov eax, [esi+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.HandleProtocol], \ + [ebx], gopuuid, gop_interface +;mov eax, 0x80000003 + test eax, eax + jz @f + call clearbuf + mov edi, msg + call num2hex + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + + add ebx, 4 + jmp .next_gop_handle +@@: + + call find_rsdp + + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_acpi_tables_done + + cmp [cfg_opt_used_resolution], 0 + jz .not_used_resolution + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_opt_resolution + call clearbuf + xor edx, edx + movzx eax, [edx+BOOT_LO.x_res] + mov edi, msg + call num2dec + xor edx, edx + movzx eax, [edx+BOOT_LO.y_res] + mov edi, msg+8*2 + call num2dec + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + + call find_vmode_index_by_resolution +.not_used_resolution: + cmp [cfg_opt_used_debug_print], 0 + jz .not_used_debug_print + movzx eax, [cfg_opt_value_debug_print] + xor edx, edx + mov [edx+BOOT_LO.debug_print], al +.not_used_debug_print: + + cmp [cfg_opt_value_ask_params], 0 + jz @f + call ask_for_params +@@: + + movzx ecx, [cfg_opt_value_vmode] + mov eax, [gop_interface] + ccall [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode], eax, ecx + call halt_on_error + + mov ecx, [gop_interface] + mov edx, [ecx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode] + mov edi, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.FrameBufferBase.lo] + mov [fb_base], edi + + + mov ebx, [edx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.Mode] + mov eax, [gop_interface] + ccall [eax+EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode], eax, ebx, \ + gop_info_size, gop_info + test eax, eax + jnz .error + mov ecx, [gop_info] + mov eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + xor edx, edx + mov [edx+BOOT_LO.x_res], ax + mov eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + mov [edx+BOOT_LO.y_res], ax + mov eax, [ecx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine] + shl eax, 2 + mov [edx+BOOT_LO.pitch], ax + + mov byte[edx+BOOT_LO.pci_data+0], 1 ; PCI access mechanism + mov byte[edx+BOOT_LO.pci_data+1], 8 ; last bus, don't know how to count them + mov byte[edx+BOOT_LO.pci_data+2], 0x10 ; PCI version + mov byte[edx+BOOT_LO.pci_data+3], 0x02 + mov dword[edx+BOOT_LO.pci_data+4], 0xe3 + + ; kernel +; eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \ +; 450000/0x1000, EFI_ALLOCATE_ADDRESS + + ; ramdisk +; eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \ +; 2880*512/0x1000, EFI_ALLOCATE_ADDRESS + + call calc_memmap +; call dump_memmap + + mov eax, [efi_table] + mov eax, [eax+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.ExitBootServices], [efi_handle], \ + [memory_map_key] + call halt_on_error + + cli + + xor edx, edx + xor esi, esi + mov [esi+BOOT_LO.bpp], 32 + mov [esi+BOOT_LO.vesa_mode], dx + mov [esi+BOOT_LO.bank_switch], edx + mov edi, [fb_base] + mov [esi+BOOT_LO.lfb], edi + + movzx eax, [cfg_opt_value_mtrr] + mov [esi+BOOT_LO.mtrr], al + + movzx eax, [cfg_opt_value_launcher_start] + mov [esi+BOOT_LO.launcher_start], al + + movzx eax, [cfg_opt_value_debug_print] + mov [esi+BOOT_LO.debug_print], al + + mov [esi+BOOT_LO.dma], dl +; mov qword[esi+BOOT_LO.pci_data], 0 + mov [esi+BOOT_LO.apm_entry], edx + mov [esi+BOOT_LO.apm_version], dx + mov [esi+BOOT_LO.apm_flags], dx + mov [esi+BOOT_LO.apm_code_32], dx + mov [esi+BOOT_LO.apm_code_16], dx + mov [esi+BOOT_LO.apm_data_16], dx + mov [esi+BOOT_LO.bios_hd_cnt], dl + + movzx eax, [cfg_opt_value_imgfrom] + mov [esi+BOOT_LO.rd_load_from], al + + mov eax, dword[devicesdat_size] + mov [edx+BOOT_LO.devicesdat_size], eax + mov eax, dword[devicesdat_data] + mov [edx+BOOT_LO.devicesdat_data], eax + + mov esi, cfg_opt_value_syspath + mov edi, BOOT_LO.syspath + mov ecx, 0x17 + rep movsb + + lgdt [cs:GDTR] + + mov ax, DATA_32_SELECTOR + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + push CODE_32_SELECTOR + lea eax, [.next] + push eax + retf + +.next: + mov eax, cr0 + and eax, not CR0_PG + mov cr0, eax + + mov eax, cr4 + and eax, not CR4_PAE + mov cr4, eax + + push KERNEL_BASE + retn + +.error: + mov esi, [efi_table] + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_error + jmp $ + +halt_on_error: + test eax, eax + jz @f + call clearbuf + mov edi, msg + call num2hex + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_error + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, msg + jmp $ +@@: + ret + +proc find_rsdp + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_look_for_rsdp + + mov edi, [esi+EFI_SYSTEM_TABLE.ConfigurationTable] + mov ecx, [esi+EFI_SYSTEM_TABLE.NumberOfTableEntries] +.next_table: + dec ecx + js .all_tables_done + ; EFI_ACPI_TABLE_GUID + cmp dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x0], 0x8868e871 + jnz .not_acpi20 + cmp dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x4], 0x11d3e4f1 + jnz .not_acpi20 + cmp dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0x8], 0x800022bc + jnz .not_acpi20 + cmp dword[edi+EFI_CONFIGURATION_TABLE.VendorGUID+0xc], 0x81883cc7 + jnz .not_acpi20 + mov eax, [edi+EFI_CONFIGURATION_TABLE.VendorTable] + mov edx, BOOT_LO.acpi_rsdp + mov [edx], eax + mov eax, [esi+EFI_SYSTEM_TABLE.ConOut] + ccall [eax+EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString], eax, \ + msg_rsdp_found + jmp .all_tables_done +.not_acpi20: + add edi, sizeof.EFI_CONFIGURATION_TABLE + jmp .next_table +.all_tables_done: + ret +endp + +proc calc_memmap + mov eax, [esi+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.AllocatePages], EFI_ALLOCATE_ANY_PAGES, \ + EFI_RESERVED_MEMORY_TYPE, MEMORY_MAP_SIZE/0x1000, memory_map + call halt_on_error + + mov eax, [esi+EFI_SYSTEM_TABLE.BootServices] + ccall [eax+EFI_BOOT_SERVICES.GetMemoryMap], memory_map_size, \ + [memory_map], memory_map_key, descriptor_size, descriptor_ver + call halt_on_error + + push esi + mov edi, BOOT_LO.memmap_blocks + mov dword[edi-4], 0 ; memmap_block_cnt + mov esi, [memory_map] + mov ebx, esi + add ebx, [memory_map_size] +.next_descr: + call add_uefi_memmap + add esi, [descriptor_size] + cmp esi, ebx + jb .next_descr + pop esi + ret +endp + +; linux/arch/x86/platform/efi/efi.c +; do_add_efi_memmap +proc add_uefi_memmap + cmp [BOOT_LO.memmap_block_cnt], MAX_MEMMAP_BLOCKS + jz .done + + mov eax, [esi+EFI_MEMORY_DESCRIPTOR.PhysicalStart.lo] + mov edx, [esi+EFI_MEMORY_DESCRIPTOR.PhysicalStart.hi] + mov [edi+e820entry.addr.lo], eax + mov [edi+e820entry.addr.hi], edx + + mov eax, [esi+EFI_MEMORY_DESCRIPTOR.NumberOfPages.lo] + mov edx, [esi+EFI_MEMORY_DESCRIPTOR.NumberOfPages.hi] + shld edx, eax, 12 + shl eax, 12 + mov [edi+e820entry.size.lo], eax + mov [edi+e820entry.size.hi], edx + + mov ecx, [esi+EFI_MEMORY_DESCRIPTOR.Type] + cmp ecx, EFI_LOADER_CODE + jz .mem_ram_if_wb + cmp ecx, EFI_LOADER_DATA + jz .mem_ram_if_wb + cmp ecx, EFI_BOOT_SERVICES_CODE + jz .mem_ram_if_wb + cmp ecx, EFI_BOOT_SERVICES_DATA + jz .mem_ram_if_wb + cmp ecx, EFI_CONVENTIONAL_MEMORY + jz .mem_ram_if_wb + cmp ecx, EFI_ACPI_RECLAIM_MEMORY + mov eax, E820_ACPI + jz .type_done + cmp ecx, EFI_ACPI_MEMORY_NVS + mov eax, E820_NVS + jz .type_done + cmp ecx, EFI_UNUSABLE_MEMORY + mov eax, E820_UNUSABLE + jz .type_done + cmp ecx, EFI_PERSISTENT_MEMORY + mov eax, E820_PMEM + jz .type_done + jmp .reserved +.mem_ram_if_wb: + test [esi+EFI_MEMORY_DESCRIPTOR.Attribute.lo], EFI_MEMORY_WB + mov eax, E820_RAM + jnz .type_done +.reserved: + mov eax, E820_RESERVED +.type_done: + mov [edi+e820entry.type], eax + cmp eax, E820_RAM + jnz @f + inc [BOOT_LO.memmap_block_cnt] + add edi, sizeof.e820entry +@@: +.done: + ret +endp + + +proc num2dec + pushad + + xor ecx, ecx + mov ebx, 10 +.next_digit: + xor edx, edx + div ebx + push edx + inc ecx + test eax, eax + jnz .next_digit + +.next_char: + pop eax + add eax, '0' + stosw + loop .next_char + + popad + ret +endp + + +proc num2hex + pushad + + xchg edx, eax + mov ecx, 8 +.next_tetra: + rol edx, 4 + movzx eax, dl + and eax, 0x0f + movzx eax, byte[hex+eax] + stosw + loop .next_tetra + + popad + ret +endp + + +hex db '0123456789ABCDEF' + +proc clearbuf + pushad + mov eax, 0x0020 + mov ecx, 79 + mov edi, msg + rep stosw + popad + ret +endp + +section '.rodata' data readable +align 16 +GDTR: + dw 3*8-1 + dq GDT +align 16 +GDT: + dw 0, 0, 0, 0 + dw 0FFFFh,0,9A00h,0CFh ; 32-bit code + dw 0FFFFh,0,9200h,0CFh ; flat data + +gopuuid db EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID +lipuuid db EFI_LOADED_IMAGE_PROTOCOL_GUID +sfspguid db EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID + +file_name du "\EFI\KOLIBRIOS\KOLIBRI.INI",0 +kernel_name du "\EFI\KOLIBRIOS\KOLIBRI.KRN",0 +ramdisk_name du "\EFI\KOLIBRIOS\KOLIBRI.IMG",0 +devicesdat_name du "\EFI\KOLIBRIOS\DEVICES.DAT",0 + +config_options dd cfg_opt_name_resolution, cfg_opt_func_resolution, \ + cfg_opt_cmnt_resolution, \ + cfg_opt_name_acpi, cfg_opt_func_acpi, cfg_opt_cmnt_acpi, \ + cfg_opt_name_debug_print, cfg_opt_func_debug_print, \ + cfg_opt_cmnt_debug_print, \ + cfg_opt_name_launcher_start, cfg_opt_func_launcher_start, \ + cfg_opt_cmnt_launcher_start, \ + cfg_opt_name_mtrr, cfg_opt_func_mtrr, cfg_opt_cmnt_mtrr, \ + cfg_opt_name_ask_params, cfg_opt_func_ask_params, \ + cfg_opt_cmnt_ask_params, \ + cfg_opt_name_imgfrom, cfg_opt_func_imgfrom, \ + cfg_opt_cmnt_imgfrom, \ + cfg_opt_name_syspath, cfg_opt_func_syspath, \ + cfg_opt_cmnt_syspath, \ + 0 + +cfg_opt_name_resolution db "resolution",0 +cfg_opt_name_acpi db "acpi",0 +cfg_opt_name_debug_print db "debug_print",0 +cfg_opt_name_launcher_start db "launcher_start",0 +cfg_opt_name_mtrr db "mtrr",0 +cfg_opt_name_ask_params db "ask_params",0 +cfg_opt_name_imgfrom db "imgfrom",0 +cfg_opt_name_syspath db "syspath",0 + +cfg_opt_cmnt_resolution db "# Graphic mode",0 +cfg_opt_cmnt_acpi db "# ACPI settings",0xa, \ + "# 0: don't use",0xa, \ + "# 1: parse ACPI tables",0xa, \ + "# 2: + call _PIC method",0xa, \ + "# 3: + get APIC interrupts",0xa,0 +cfg_opt_cmnt_debug_print db "# Duplicate debug output to the screen",0 +cfg_opt_cmnt_launcher_start db "# Start LAUNCHER app after kernel is loaded",0 +cfg_opt_cmnt_mtrr db "# Configure MTRR's",0 +cfg_opt_cmnt_ask_params db "# Interrupt booting to ask the user for boot", \ + " params",0 +cfg_opt_cmnt_imgfrom db "# Where to load ramdisk image from",0 +cfg_opt_cmnt_syspath db "# Path to /sys directory",0 + +msg_u4k_loaded du "uefi32kos loaded",13,10,0 +msg_read_options du "Read options from config file",13,10,0 +msg_load_kernel du "Load kernel",13,10,0 +msg_load_ramdisk du "Load ramdisk",13,10,0 +msg_load_devicesdat du "Load DEVICES.DAT",13,10,0 +msg_alloc_devicesdat du "Allocate memory for DEVICES.DAT",13,10,0 +msg_locate_gop_handlers du "Locate GOP handlers",13,10,0 +msg_look_for_gop_handler du "Look for GOP handler",13,10,0 +msg_query_handler du "Query handler",13,10,0 +msg_query_vmode du "Query vmode",13,10,0 +msg_vmode_found du "Video mode found",13,10,0 +msg_look_for_rsdp du "Look for RSDP",13,10,0 +msg_rsdp_found du "RSDP found",13,10,0 +msg_acpi_tables_done du "ACPI tables done",13,10,0 +msg_ask_for_params du "Ask for params",13,10,0 +msg_set_graphic_mode du "Set graphic mode",13,10,0 +msg_success du "Success!",13,10,0 +msg_gop_buffer_size du "GOP buffer size",13,10,0 +msg_opt_resolution du "option resolution: ",0 +msg_error du "Error!",13,10,0 +msg_error_no_such_vmode du "No such vmode",13,10,0 +msg_error_out_of_handlers du "Out of handlers",13,10,0 +msg_error_open_file du "Error: can't open file ",0 +msg du 79 dup " ",13,10,0 + + +section '.data' data readable writeable +efi_handle dd 0 +efi_table dd 0 + +fb_base dd 0 + +gop_buffer_size dd GOP_BUFFER_SIZE +gop_handle dd 0 +gop_interface dd 0 +gop_info_size dd 0 +gop_info dd 0 + +lip_buffer_size dd LIP_BUFFER_SIZE +lip_handle dd 0 +lip_interface dd 0 + +sfsp_interface dd 0 + +esp_root dd ? +file_handle dd ? +file_buffer_size dd FILE_BUFFER_SIZE-1 ; leave the last byte for \0 + +cfg_opt_used_resolution db 0 +cfg_opt_used_acpi db 0 +cfg_opt_used_debug_print db 0 +cfg_opt_used_launcher_start db 0 +cfg_opt_used_mtrr db 0 +cfg_opt_used_ask_params db 0 +cfg_opt_used_imgfrom db 0 +cfg_opt_used_syspath db 0 + +cfg_opt_value_vmode db 0 +cfg_opt_value_acpi db 0 +cfg_opt_value_debug_print db 0 +cfg_opt_value_launcher_start db 1 +cfg_opt_value_mtrr db 0 +cfg_opt_value_ask_params db 0 +cfg_opt_value_imgfrom db RD_LOAD_FROM_MEMORY +cfg_opt_value_syspath db "/RD/1",0 + rb 20 + +memory_map_key dd 0 +descriptor_size dd 0 +descriptor_ver dd 0 +memory_map_size dd MEMORY_MAP_SIZE + +efi_fs_info_id db EFI_FILE_SYSTEM_INFO_ID +efi_fs_info_size dq sizeof.EFI_FILE_SYSTEM_INFO +efi_fs_info EFI_FILE_SYSTEM_INFO + +memory_map dd ? +gop_buffer rd GOP_BUFFER_SIZE/4 +devicesdat_data dd 0xffffffff +devicesdat_size dd 0x1000 +status dd ? + +section '.reloc' fixups data discardable diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64.inc b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64.inc new file mode 100644 index 000000000..b565bae0c --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64.inc @@ -0,0 +1,196 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Version 2, or (at your option) any later version. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Based on UEFI library for fasm by bzt, Public Domain. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +DN fix dq ; native + +include "uefi.inc" + +EFIERR = 0x8000000000000000 + +struct EFI_SYSTEM_TABLE + Hdr EFI_TABLE_HEADER + FirmwareVendor dq ? + FirmwareRevision dd ? + dd ? + ConsoleInHandle dq ? + ConIn dq ? + ConsoleOutHandle dq ? + ConOut dq ? + StandardErrorHandle dq ? + StdErr dq ? + RuntimeServices dq ? + BootServices dq ? + NumberOfTableEntries dq ? + ConfigurationTable dq ? +ends + +struct EFI_LOADED_IMAGE_PROTOCOL + Revision dd ? + dd ? + ParentHandle dq ? + SystemTable dq ? + DeviceHandle dq ? + FilePath dq ? + Reserved dq ? + LoadOptionsSize dd ? + dd ? + ImageBase dq ? + ImageSize dq ? + ImageCodeType dd ? + ImageDataType dd ? + UnLoad dq ? +ends + +macro eficall interface,function,arg1,arg2,arg3,arg4,arg5,arg6,arg7,arg8,arg9,arg10,arg11 +{ +numarg = 0 + +if ~ arg11 eq + numarg = numarg + 1 + if ~ arg11 eq rdi + mov rdi, arg11 + end if +end if + +if ~ arg10 eq + numarg = numarg + 1 + if ~ arg10 eq rsi + mov rsi, arg10 + end if +end if + +if ~ arg9 eq + numarg = numarg + 1 + if ~ arg9 eq r14 + mov r14, arg9 + end if +end if + +if ~ arg8 eq + numarg = numarg + 1 + if ~ arg8 eq r13 + mov r13, arg8 + end if +end if + +if ~ arg7 eq + numarg = numarg + 1 + if ~ arg7 eq r12 + mov r12, arg7 + end if +end if + +if ~ arg6 eq + numarg = numarg + 1 + if ~ arg6 eq r11 + mov r11, arg6 + end if +end if + +if ~ arg5 eq + numarg = numarg + 1 + if ~ arg5 eq r10 + mov r10, arg5 + end if +end if + +if ~ arg4 eq + numarg = numarg + 1 + if ~ arg4 eq r9 + mov r9, arg4 + end if +end if + +if ~ arg3 eq + numarg = numarg + 1 + if ~ arg3 eq r8 + mov r8, arg3 + end if +end if + +if ~ arg2 eq + numarg = numarg + 1 + if ~ arg2 eq rdx + mov rdx, arg2 + end if +end if + +if ~ arg1 eq + numarg = numarg + 1 + if ~ arg1 eq rcx + mov rcx, arg1 + end if +end if + + xor eax, eax + mov al, numarg + +if ~ interface eq rbx + mov rbx, interface +end if + + mov rbx, [rbx + function] + call uefifunc +} + +section '.text' code executable readable + +uefifunc: + ;save stack pointer + mov [uefi_rsptmp], rsp + ;set up new aligned stack + and esp, 0xFFFFFFF0 + ;alignment check on arguments + bt eax, 0 + jnc @f + push rax + ;arguments +@@: + cmp al, 11 + jb @f + push rdi +@@: + cmp al, 10 + jb @f + push rsi +@@: + cmp al, 9 + jb @f + push r14 +@@: + cmp al, 8 + jb @f + push r13 +@@: + cmp al, 7 + jb @f + push r12 +@@: + cmp al, 6 + jb @f + push r11 +@@: + cmp al, 5 + jb @f + push r10 +@@: + ;space for + ;r9 + ;r8 + ;rdx + ;rcx + sub rsp, 4*8 + ;call function + call rbx + ;restore old stack + mov rsp, [uefi_rsptmp] + ret diff --git a/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64kos.asm b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64kos.asm new file mode 100644 index 000000000..927b0d6e0 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bootloader/uefi4kos/uefi64kos.asm @@ -0,0 +1,1249 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Version 2, or (at your option) any later version. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +format pe64 efi +entry main + +section '.text' code executable readable + +include '../../struct.inc' +include '../../macros.inc' +include '../../const.inc' + +purge DQ +include 'uefi64.inc' + +MEMORY_MAP_SIZE = 0x10000 +GOP_BUFFER_SIZE = 0x100 +LIP_BUFFER_SIZE = 0x100 +FILE_BUFFER_SIZE = 0x1000 + +KERNEL_TRAMPOLINE = 0x8f80 ; just before BOOT_LO +KERNEL_BASE = 0x10000 +RAMDISK_BASE = 0x100000 +MAX_FILE_SIZE = 0x10000000 + +CODE_32_SELECTOR = 8 +DATA_32_SELECTOR = 16 +CODE_64_SELECTOR = 24 + +; linux/arch/x86/include/uapi/asm/e820.h +E820_RAM = 1 +E820_RESERVED = 2 +E820_ACPI = 3 +E820_NVS = 4 +E820_UNUSABLE = 5 +E820_PMEM = 7 + +load_file: +virtual at rsp+8 + .root dq ? + .name dq ? + .buffer dq ? + .size dq ? + .fatal dq ? +end virtual + eficall [.root], EFI_FILE_PROTOCOL.Open, [.root], file_handle, \ + [.name], EFI_FILE_MODE_READ, 0 + test eax, eax + jz @f + xor eax, eax + cmp [.fatal], 1 + jnz .done + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_open_file + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + [.name] + jmp $ +@@: + + lea rax, [.size] + eficall [file_handle], EFI_FILE_PROTOCOL.Read, [file_handle], rax, \ + [.buffer] + eficall [file_handle], EFI_FILE_PROTOCOL.Close, [file_handle] + mov rax, [.size] +.done: + push rax + call clearbuf + mov rdi, msg + call num2dec + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_file_size + pop rbx + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + pop rbx + pop rax + ret 8*5 + +skip_whitespace: +.next_char: + cmp byte[rsi], 0 + jz .done + cmp byte[rsi], 0x20 ; ' ' + jz .whitespace + cmp byte[rsi], 9 ; '\t' + jz .whitespace + jmp .done +.whitespace: + inc rsi + jmp .next_char +.done: + ret + +skip_until_newline: +.next_char: + cmp byte[rsi], 0 + jz .done + cmp byte[rsi], 0xd ; '\r' + jz .done + cmp byte[rsi], 0xa ; '\n' + jz .done + inc rsi + jmp .next_char +.done: + ret + +skip_newline: +.next_char: + cmp byte[rsi], 0xd ; '\r' + jz .newline + cmp byte[rsi], 0xa ; '\n' + jz .newline + jmp .done +.newline: + inc rsi + jmp .next_char +.done: + ret + +skip_line: + call skip_until_newline + call skip_newline + ret + +dec2bin: + mov edx, 0 +.next_char: + movzx eax, byte[rsi] + test eax, eax + jz .done + sub eax, '0' + jb .done + cmp eax, 9 + ja .done + inc rsi + imul edx, 10 + add edx, eax + jmp .next_char +.done: + mov eax, edx + ret + +parse_option: + mov rbx, config_options-3*8 +.try_next_option: + add rbx, 3*8 + mov rdi, rsi + mov rdx, [rbx] ; option name + test rdx, rdx + jz .done +.next_char: + cmp byte[rdx], 0 + jnz @f + cmp byte[rdi], '=' + jz .opt_name_ok +@@: + cmp byte[rdi], 0 + jz .done + movzx eax, byte[rdi] + cmp [rdx], al + jnz .try_next_option + inc rdi + inc rdx + jmp .next_char +.opt_name_ok: + inc rdi + mov rsi, rdi + call qword[rbx+8] +.done: + ret + +parse_line: +.next_line: + cmp byte[rsi], 0 + jz .done + cmp byte[rsi], 0xd ; '\r' + jz .skip + cmp byte[rsi], 0xa ; '\n' + jz .skip + cmp byte[rsi], '#' + jz .skip + call parse_option + call skip_line + jmp .next_line +.skip: + call skip_line + jmp .next_line +.done: + ret + +cfg_opt_func_resolution: + call dec2bin + xor edx, edx + mov [rdx+BOOT_LO.x_res], ax + cmp byte[rsi], 'x' + jz @f + cmp byte[rsi], '*' + jz @f + jmp .done +@@: + inc rsi + call dec2bin + xor edx, edx + mov [rdx+BOOT_LO.y_res], ax + mov [cfg_opt_used_resolution], 1 +.done: + ret + +cfg_opt_func_acpi: + call dec2bin + mov [cfg_opt_used_acpi], 1 + mov [cfg_opt_value_acpi], al + ret + +cfg_opt_func_debug_print: + call dec2bin + mov [cfg_opt_used_debug_print], 1 + mov [cfg_opt_value_debug_print], al + ret + +cfg_opt_func_launcher_start: + call dec2bin + mov [cfg_opt_used_launcher_start], 1 + mov [cfg_opt_value_launcher_start], al + ret + +cfg_opt_func_mtrr: + call dec2bin + mov [cfg_opt_used_mtrr], 1 + mov [cfg_opt_value_mtrr], al + ret + +cfg_opt_func_ask_params: + call dec2bin + mov [cfg_opt_used_ask_params], 1 + mov [cfg_opt_value_ask_params], al + ret + +cfg_opt_func_imgfrom: + call dec2bin + mov [cfg_opt_used_imgfrom], 1 + mov [cfg_opt_value_imgfrom], al + ret + +cfg_opt_func_syspath: + mov rdi, cfg_opt_value_syspath +.next_char: + movzx eax, byte[rsi] + cmp al, 0xd ; \r + jz .done + cmp al, 0xa ; \n + jz .done + inc rsi + stosb + jmp .next_char +.done: + mov byte[rdi], 0 + ret + +parse_config: +virtual at rsp+8 + .buffer dq ? +end virtual +; mov rsi, [.buffer] + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_parsing_config + pop rbx + mov rsi, KERNEL_BASE +.next_line: + call parse_line + cmp byte[rsi], 0 + jnz .next_line + ret 1*8 + +read_options_from_config: + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [efi_handle], lipuuid, \ + lip_interface + test eax, eax + jz @f + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_efi_lip_handle + pop rbx + jmp $ +@@: + mov rax, [lip_interface] + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, \ + [rax+EFI_LOADED_IMAGE_PROTOCOL.DeviceHandle], sfspguid, \ + sfsp_interface + test eax, eax + jz @f + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_lip_dev_sfsp + pop rbx + jmp $ +@@: + eficall [sfsp_interface], EFI_SIMPLE_FILE_SYSTEM_PROTOCOL.OpenVolume, \ + [sfsp_interface], esp_root + test eax, eax + jz @f + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_sfsp_openvolume + pop rbx + jmp $ +@@: + push 0 ; not fatal, i.e. it's ok to not find this file + push FILE_BUFFER_SIZE + push KERNEL_BASE +; push file_name + mov rax, file_name + push rax + push [esp_root] + call load_file + + test eax, eax + jz @f + push KERNEL_BASE + call parse_config +@@: + +.error: + ret + +print_vmode: + push rax rbx rcx rdx rsi rdi + mov rbx, rcx + call clearbuf + mov eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + mov rdi, msg + call num2dec + mov eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + mov rdi, msg+8*2 + call num2dec + + mov eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat] + mov rdi, msg+16*2 + call num2dec + + mov eax, [rbx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine] + mov rdi, msg+24*2 + call num2dec + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + pop rdi rsi rdx rcx rbx rax + ret + +find_vmode_index_by_resolution: + mov [cfg_opt_used_resolution], 1 + mov [cfg_opt_value_vmode], 0 +.next_mode: + movzx eax, [cfg_opt_value_vmode] + eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \ + [gop_interface], rax, gop_info_size, gop_info + cmp rax, EFI_SUCCESS + jnz .error + mov rcx, [gop_info] + call print_vmode + ; PixelBlueGreenRedReserved8BitPerColor + cmp [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1 + jnz .skip_mode + xor edx, edx + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + cmp ax, [rdx+BOOT_LO.x_res] + jnz .skip_mode + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + cmp ax, [rdx+BOOT_LO.y_res] + jnz .skip_mode + jmp .done +.skip_mode: + inc [cfg_opt_value_vmode] + movzx eax, [cfg_opt_value_vmode] + mov rcx, [gop_interface] + mov rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode] + cmp eax, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode] + jnz .next_mode + mov [cfg_opt_used_resolution], 0 + mov [cfg_opt_value_ask_params], 1 + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_no_such_vmode + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error + jmp $ +.error: +.done: + ret + +ask_for_params: + ret + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [rax], gopuuid, \ + msg_ask_for_params + jmp $ + + xor ebx, ebx +.next_mode: + call clearbuf + mov eax, ebx + lea rdi, [msg] + call num2dec + + push rbx + eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \ + [gop_interface], rbx, gop_info_size, gop_info + cmp rax, EFI_SUCCESS + jnz .error + mov rcx, [gop_info] + ; PixelBlueGreenRedReserved8BitPerColor + cmp [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelFormat], 1 + jnz .skip + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + lea rdi, [msg+4*2] + call num2dec + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + lea rdi, [msg+9*2] + call num2dec +; mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine] +; lea rdi, [msg+14*2] +; call num2dec +.skip: + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + cmp rax, EFI_SUCCESS + jnz .error + + pop rbx + inc rbx + mov rcx, [gop_interface] + mov rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode] + cmp ebx, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.MaxMode] + jnz .next_mode + + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConIn] + eficall rbx, SIMPLE_INPUT_INTERFACE.Reset, rbx, 1 + cmp rax, EFI_SUCCESS + jnz .error + xor ecx, ecx + @@: + push rcx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConIn] + eficall rbx, SIMPLE_INPUT_INTERFACE.ReadKeyStroke, rbx, msg + pop rcx + mov rdx, EFI_DEVICE_ERROR + cmp rax, rdx + jz .error + mov rdx, EFI_NOT_READY + cmp rax, rdx + jz @b +; cmp rax, EFI_SUCCESS + movzx eax, word[msg+2] +;jmp .key_done + cmp al, 0x0D + jz .key_done + imul ecx, 10 + sub eax, '0' + add ecx, eax + jmp @b +.key_done: + mov [cfg_opt_value_vmode], cl +.error: +.done: + ret + +main: + mov [efi_handle], rcx + mov [efi_table], rdx + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.Reset, rbx, 1 + test eax, eax + jz @f + jmp $ ; what can I do here? +@@: + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_u4k_loaded + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_read_options + call read_options_from_config + + ; read kernel file + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_load_kernel + push 1 ; fatal + push MAX_FILE_SIZE + push KERNEL_BASE +; push kernel_name + mov rax, kernel_name + push rax + push [esp_root] + call load_file + + ; read ramdisk image + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_load_ramdisk + push 1 ; fatal + push MAX_FILE_SIZE + push RAMDISK_BASE +; push ramdisk_name + mov rax, ramdisk_name + push rax + push [esp_root] + call load_file + + ; alloc buffer for devices.dat + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_alloc_devicesdat + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.AllocatePages, \ + EFI_ALLOCATE_MAX_ADDRESS, EFI_RESERVED_MEMORY_TYPE, 1, \ + devicesdat_data + cmp eax, EFI_SUCCESS + jnz .error + + ; read devices.dat + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_load_devicesdat + + push 0 ; not fatal + push [devicesdat_size] + push [devicesdat_data] +; push devicesdat_name + mov rax, devicesdat_name + push rax + push [esp_root] + call load_file + mov [devicesdat_size], rax + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_locate_gop_handlers + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.LocateHandle, \ + EFI_LOCATE_SEARCH_TYPE.ByProtocol, gopuuid, 0, \ + gop_buffer_size, gop_buffer + mov [status], rax + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_gop_buffer_size + call clearbuf + mov rax, [gop_buffer_size] + mov rdi, msg + call num2hex + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + + mov rax, [status] + test eax, eax + jz @f + call clearbuf + mov rdi, msg + call num2hex + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + jmp $ +@@: + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_look_for_gop_handler + + mov rbx, gop_buffer +.next_gop_handle: + mov rax, rbx + mov rcx, gop_buffer + sub rax, rcx + cmp rax, [gop_buffer_size] + jb @f + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error_out_of_handlers + pop rbx + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg_error + pop rbx + jmp $ +@@: + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_query_handler + pop rbx + + mov rax, rbx + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.HandleProtocol, [rax], gopuuid, \ + gop_interface + pop rbx +;mov rax, 0x8000_0000_0000_0003 + test eax, eax + jz @f + call clearbuf + mov rdi, msg + call num2hex + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + pop rbx + + add rbx, 8 + jmp .next_gop_handle +@@: + + call find_rsdp + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_acpi_tables_done + + cmp [cfg_opt_used_resolution], 0 + jz .not_used_resolution + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_opt_resolution + call clearbuf + xor edx, edx + movzx eax, [rdx+BOOT_LO.x_res] + mov rdi, msg + call num2dec + xor edx, edx + movzx eax, [rdx+BOOT_LO.y_res] + mov rdi, msg+8*2 + call num2dec + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + call find_vmode_index_by_resolution +.not_used_resolution: + cmp [cfg_opt_used_debug_print], 0 + jz .not_used_debug_print + movzx eax, [cfg_opt_value_debug_print] + xor edx, edx + mov [rdx+BOOT_LO.debug_print], al +.not_used_debug_print: + + cmp [cfg_opt_value_ask_params], 0 + jz @f + call ask_for_params +@@: + + movzx ecx, [cfg_opt_value_vmode] + eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.SetMode, \ + [gop_interface], rcx + test eax, eax + jz @f + call clearbuf + mov rdi, msg + call num2hex + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error + jmp $ +@@: + + mov rcx, [gop_interface] + mov rdx, [rcx+EFI_GRAPHICS_OUTPUT_PROTOCOL.Mode] + mov rdi, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.FrameBufferBase] + mov [fb_base], rdi + + mov ebx, [rdx+EFI_GRAPHICS_OUTPUT_PROTOCOL_MODE.Mode] + eficall [gop_interface], EFI_GRAPHICS_OUTPUT_PROTOCOL.QueryMode, \ + [gop_interface], rbx, gop_info_size, gop_info + test eax, eax + jz @f + jmp .error +@@: + mov rcx, [gop_info] + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.HorizontalResolution] + xor rdx, rdx + mov [rdx+BOOT_LO.x_res], ax + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.VerticalResolution] + mov [rdx+BOOT_LO.y_res], ax + mov eax, [rcx+EFI_GRAPHICS_OUTPUT_MODE_INFORMATION.PixelsPerScanLine] + shl eax, 2 + mov [rdx+BOOT_LO.pitch], ax + + mov byte[rdx+BOOT_LO.pci_data+0], 1 ; PCI access mechanism + mov byte[rdx+BOOT_LO.pci_data+1], 8 ; last bus, don't know how to count them + mov byte[rdx+BOOT_LO.pci_data+2], 0x10 ; PCI version + mov byte[rdx+BOOT_LO.pci_data+3], 0x02 + mov dword[rdx+BOOT_LO.pci_data+4], 0xe3 + + ; kernel +; eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \ +; 450000/0x1000, EFI_ALLOCATE_ADDRESS + + ; ramdisk +; eficall BootServices, AllocatePages, EFI_RESERVED_MEMORY_TYPE, \ +; 2880*512/0x1000, EFI_ALLOCATE_ADDRESS + + call calc_memmap +; call dump_memmap + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.ExitBootServices, [efi_handle], \ + [memory_map_key] + call halt_on_error + + cli + + xor edx, edx + xor esi, esi + mov [esi+BOOT_LO.bpp], 32 + mov [esi+BOOT_LO.vesa_mode], dx + mov [esi+BOOT_LO.bank_switch], edx + mov rdi, [fb_base] + mov [esi+BOOT_LO.lfb], edi + + movzx eax, [cfg_opt_value_mtrr] + mov [esi+BOOT_LO.mtrr], al + + movzx eax, [cfg_opt_value_launcher_start] + mov [esi+BOOT_LO.launcher_start], al + + movzx eax, [cfg_opt_value_debug_print] + mov [esi+BOOT_LO.debug_print], al + + mov [esi+BOOT_LO.dma], dl +; mov qword[esi+BOOT_LO.pci_data], 0 + mov [esi+BOOT_LO.apm_entry], edx + mov [esi+BOOT_LO.apm_version], dx + mov [esi+BOOT_LO.apm_flags], dx + mov [esi+BOOT_LO.apm_code_32], dx + mov [esi+BOOT_LO.apm_code_16], dx + mov [esi+BOOT_LO.apm_data_16], dx + mov [esi+BOOT_LO.bios_hd_cnt], dl + + movzx eax, [cfg_opt_value_imgfrom] + mov [esi+BOOT_LO.rd_load_from], al + + mov eax, dword[devicesdat_size] + mov [rdx+BOOT_LO.devicesdat_size], eax + mov eax, dword[devicesdat_data] + mov [rdx+BOOT_LO.devicesdat_data], eax + + mov rsi, cfg_opt_value_syspath + mov rdi, BOOT_LO.syspath + mov ecx, 0x17 + rep movsb + + ; kernel trampoline + mov rsi, kernel_trampoline + mov rdi, KERNEL_TRAMPOLINE + mov ecx, kernel_trampoline.size + rep movsb + + mov rax, GDTR + lgdt [cs:rax] + + mov ax, DATA_32_SELECTOR + mov ds, ax + mov es, ax + mov fs, ax + mov gs, ax + mov ss, ax + + push CODE_32_SELECTOR + mov rax, KERNEL_TRAMPOLINE + push rax + retf + +.error: + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error + jmp $ + + +halt_on_error: + test eax, eax + jz @f + call clearbuf + mov rdi, msg + call num2hex + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_error + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + jmp $ +@@: + ret + +find_rsdp: + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_look_for_rsdp + pop rbx + + mov rbx, [efi_table] + mov rdi, [rbx+EFI_SYSTEM_TABLE.ConfigurationTable] + mov rcx, [rbx+EFI_SYSTEM_TABLE.NumberOfTableEntries] + mov rax, 0x11d3e4f18868e871 + mov rdx, 0x81883cc7800022bc +.next_table: + dec ecx + js .all_tables_done + cmp [rdi+0], rax + jnz .not_acpi20 + cmp [rdi+8], rdx + jnz .not_acpi20 + mov rax, [rdi+16] + mov rdx, BOOT_LO.acpi_rsdp + mov [rdx], eax + jmp .all_tables_done +.not_acpi20: + add rdi, 24 + jmp .next_table +.all_tables_done: + ret + +calc_memmap: + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.AllocatePages, EFI_ALLOCATE_ANY_PAGES, \ + EFI_RESERVED_MEMORY_TYPE, MEMORY_MAP_SIZE/0x1000, memory_map + call halt_on_error + + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.BootServices] + eficall rbx, EFI_BOOT_SERVICES.GetMemoryMap, memory_map_size, \ + [memory_map], memory_map_key, descriptor_size, descriptor_ver + call halt_on_error + + mov rdi, BOOT_LO.memmap_blocks + mov dword[rdi-4], 0 ; memmap_block_cnt + mov rsi, [memory_map] + mov rbx, rsi + add rbx, [memory_map_size] +.next_descr: + call add_uefi_memmap + add rsi, [descriptor_size] + cmp rsi, rbx + jb .next_descr + ret + +dump_memmap: + xor eax, eax + mov rsi, BOOT_LO.memmap_blocks + mov ebx, [rax+BOOT_LO.memmap_block_cnt] + + call clearbuf + mov eax, ebx + mov rdi, msg + call num2dec + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, \ + msg_memmap + pop rbx + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + pop rbx + call clearbuf +.next_mapping: + dec ebx + js .done + mov rax, rsi + mov rcx, BOOT_LO.memmap_blocks + sub rax, rcx + mov ecx, sizeof.e820entry + xor edx, edx + div ecx + mov rdi, msg + call num2dec + mov rax, [rsi+e820entry.addr] + mov rdi, msg+4*2 + call num2hex + mov rax, [rsi+e820entry.size] + mov rdi, msg+24*2 + call num2hex + push rbx + mov rbx, [efi_table] + mov rbx, [rbx+EFI_SYSTEM_TABLE.ConOut] + eficall rbx, EFI_SIMPLE_TEXT_OUTPUT_PROTOCOL.OutputString, rbx, msg + pop rbx + add rsi, sizeof.e820entry + jmp .next_mapping +.done: + ret + + +; linux/arch/x86/platform/efi/efi.c +; do_add_efi_memmap +add_uefi_memmap: + xor eax, eax + cmp [rax+BOOT_LO.memmap_block_cnt], MAX_MEMMAP_BLOCKS + jz .done + + mov rax, [rsi+EFI_MEMORY_DESCRIPTOR.PhysicalStart] + mov [rdi+e820entry.addr], rax + + mov rax, [rsi+EFI_MEMORY_DESCRIPTOR.NumberOfPages] + shl rax, 12 + mov [rdi+e820entry.size], rax + + mov ecx, [rsi+EFI_MEMORY_DESCRIPTOR.Type] + cmp ecx, EFI_LOADER_CODE + jz .mem_ram_if_wb + cmp ecx, EFI_LOADER_DATA + jz .mem_ram_if_wb + cmp ecx, EFI_BOOT_SERVICES_CODE + jz .mem_ram_if_wb + cmp ecx, EFI_BOOT_SERVICES_DATA + jz .mem_ram_if_wb + cmp ecx, EFI_CONVENTIONAL_MEMORY + jz .mem_ram_if_wb + cmp ecx, EFI_ACPI_RECLAIM_MEMORY + mov eax, E820_ACPI + jz .type_done + cmp ecx, EFI_ACPI_MEMORY_NVS + mov eax, E820_NVS + jz .type_done + cmp ecx, EFI_UNUSABLE_MEMORY + mov eax, E820_UNUSABLE + jz .type_done + cmp ecx, EFI_PERSISTENT_MEMORY + mov eax, E820_PMEM + jz .type_done + jmp .reserved +.mem_ram_if_wb: + test [rsi+EFI_MEMORY_DESCRIPTOR.Attribute], dword EFI_MEMORY_WB + mov eax, E820_RAM + jnz .type_done +.reserved: + mov eax, E820_RESERVED +.type_done: + mov [rdi+e820entry.type], eax + cmp eax, E820_RAM + jnz @f + xor eax, eax + inc [rax+BOOT_LO.memmap_block_cnt] + add rdi, sizeof.e820entry +@@: +.done: + ret + + +num2dec: + push rax rbx rcx rdx rsi rdi + + xor ecx, ecx + mov ebx, 10 +.next_digit: + xor edx, edx + div ebx + push rdx + inc ecx + test eax, eax + jnz .next_digit + +.next_char: + pop rax + add eax, '0' + stosw + loop .next_char + + pop rdi rsi rdx rcx rbx rax + ret + + +num2hex: + push rax rbx rcx rdx rsi rdi + + xchg rdx, rax + mov ecx, 16 +.next_tetra: + rol rdx, 4 + movzx eax, dl + and eax, 0x0f + movzx eax, byte[hex+eax] + stosw + loop .next_tetra + + pop rdi rsi rdx rcx rbx rax + ret + +hex db '0123456789ABCDEF' + +clearbuf: + push rax rbx rcx rdx rsi rdi + mov eax, 0x0020 + mov ecx, 79 + mov rdi, msg + rep stosw + pop rdi rsi rdx rcx rbx rax + ret + +use32 +kernel_trampoline: +org KERNEL_TRAMPOLINE + mov eax, cr0 + and eax, not CR0_PG + mov cr0, eax + + mov ecx, MSR_AMD_EFER + rdmsr + btr eax, 8 ; LME + wrmsr + + mov eax, cr4 + and eax, not CR4_PAE + mov cr4, eax + + push KERNEL_BASE + retn + +align 16 +GDTR: + dw 4*8-1 + dq GDT +align 16 +GDT: + dw 0, 0, 0, 0 + dw 0FFFFh,0,9A00h,0CFh ; 32-bit code + dw 0FFFFh,0,9200h,0CFh ; flat data + dw 0FFFFh,0,9A00h,0AFh ; 64-bit code +assert $ < BOOT_LO +kernel_trampoline.size = $ - KERNEL_TRAMPOLINE + +section '.rodata' data readable +gopuuid db EFI_GRAPHICS_OUTPUT_PROTOCOL_GUID +lipuuid db EFI_LOADED_IMAGE_PROTOCOL_GUID +sfspguid db EFI_SIMPLE_FILE_SYSTEM_PROTOCOL_GUID + +file_name du '\EFI\KOLIBRIOS\KOLIBRI.INI',0 +kernel_name du '\EFI\KOLIBRIOS\KOLIBRI.KRN',0 +ramdisk_name du '\EFI\KOLIBRIOS\KOLIBRI.IMG',0 +devicesdat_name du '\EFI\KOLIBRIOS\DEVICES.DAT',0 + +config_options dq cfg_opt_name_resolution, cfg_opt_func_resolution, \ + cfg_opt_cmnt_resolution, \ + cfg_opt_name_acpi, cfg_opt_func_acpi, cfg_opt_cmnt_acpi, \ + cfg_opt_name_debug_print, cfg_opt_func_debug_print, \ + cfg_opt_cmnt_debug_print, \ + cfg_opt_name_launcher_start, cfg_opt_func_launcher_start, \ + cfg_opt_cmnt_launcher_start, \ + cfg_opt_name_mtrr, cfg_opt_func_mtrr, cfg_opt_cmnt_mtrr, \ + cfg_opt_name_ask_params, cfg_opt_func_ask_params, \ + cfg_opt_cmnt_ask_params, \ + cfg_opt_name_imgfrom, cfg_opt_func_imgfrom, \ + cfg_opt_cmnt_imgfrom, \ + cfg_opt_name_syspath, cfg_opt_func_syspath, \ + cfg_opt_cmnt_syspath, \ + 0 + +cfg_opt_name_resolution db "resolution",0 +cfg_opt_name_acpi db "acpi",0 +cfg_opt_name_debug_print db "debug_print",0 +cfg_opt_name_launcher_start db "launcher_start",0 +cfg_opt_name_mtrr db "mtrr",0 +cfg_opt_name_ask_params db "ask_params",0 +cfg_opt_name_imgfrom db "imgfrom",0 +cfg_opt_name_syspath db "syspath",0 + +cfg_opt_cmnt_resolution db "# Graphic mode",0 +cfg_opt_cmnt_acpi db "# ACPI settings",0xa, \ + "# 0: don't use",0xa, \ + "# 1: parse ACPI tables",0xa, \ + "# 2: + call _PIC method",0xa, \ + "# 3: + get APIC interrupts",0xa,0 +cfg_opt_cmnt_debug_print db "# Duplicate debug output to the screen",0 +cfg_opt_cmnt_launcher_start db "# Start LAUNCHER app after kernel is loaded",0 +cfg_opt_cmnt_mtrr db "# Configure MTRR's",0 +cfg_opt_cmnt_ask_params db "# Interrupt booting to ask the user for boot", \ + " params",0 +cfg_opt_cmnt_imgfrom db "# Where to load ramdisk image from",0 +cfg_opt_cmnt_syspath db "# Path to /sys directory",0 + +msg_u4k_loaded du "uefi64kos loaded",13,10,0 +msg_read_options du "Read options from config file",13,10,0 +msg_file_size du "File size:",13,10,0 +msg_parsing_config du "Parsing config file",13,10,0 +msg_load_kernel du "Load kernel",13,10,0 +msg_load_ramdisk du "Load ramdisk",13,10,0 +msg_load_devicesdat du "Load DEVICES.DAT",13,10,0 +msg_alloc_devicesdat du "Allocate memory for DEVICES.DAT",13,10,0 +msg_locate_gop_handlers du "Locate GOP handlers",13,10,0 +msg_look_for_gop_handler du "Look for GOP handler",13,10,0 +msg_query_handler du "Query handler",13,10,0 +msg_query_vmode du "Query vmode",13,10,0 +msg_vmode_found du "Video mode found",13,10,0 +msg_look_for_rsdp du "Look for RSDP",13,10,0 +msg_rsdp_found du "RSDP found",13,10,0 +msg_acpi_tables_done du "ACPI tables done",13,10,0 +msg_ask_for_params du "Ask for params",13,10,0 +msg_set_graphic_mode du "Set graphic mode",13,10,0 +msg_success du "Success!",13,10,0 +msg_gop_buffer_size du "GOP buffer size",13,10,0 +msg_opt_resolution du "Option resolution: ",0 +msg_memmap du "Memmap",13,10,0 +msg_error du "Error!",13,10,0 +msg_error_efi_lip_handle du "efi_handle can't handle LIP",13,10,0 +msg_error_lip_dev_sfsp du "LIP device handle can't handle SFSP",13,10,0 +msg_error_sfsp_openvolume du "SFSP OpenVolume failed",13,10,0 +msg_error_no_such_vmode du "No such vmode",13,10,0 +msg_error_out_of_handlers du "Out of handlers",13,10,0 +msg_error_open_file du "Error: can't open file ",0 +msg_error_exit_boot_services du "Error: Exit boot services",13,10,0 +msg du 79 dup " ",13,10,0 + + +section '.data' data readable writeable +efi_handle dq 0 +efi_table dq 0 +uefi_rsptmp dq 0 + +fb_base dq 0 + +gop_buffer_size dq GOP_BUFFER_SIZE +gop_handle dq 0 +gop_interface dq 0 +gop_info_size dq 0 +gop_info dq 0 + +lip_buffer_size dq LIP_BUFFER_SIZE +lip_handle dq 0 +lip_interface dq 0 + +sfsp_interface dq 0 + +esp_root dq ? +file_handle dq ? +file_buffer_size dq FILE_BUFFER_SIZE-1 ; leave the last byte for \0 + +cfg_opt_used_resolution db 0 +cfg_opt_used_acpi db 0 +cfg_opt_used_debug_print db 0 +cfg_opt_used_launcher_start db 0 +cfg_opt_used_mtrr db 0 +cfg_opt_used_ask_params db 0 +cfg_opt_used_imgfrom db 0 +cfg_opt_used_syspath db 0 + +cfg_opt_value_vmode db 0 +cfg_opt_value_acpi db 0 +cfg_opt_value_debug_print db 0 +cfg_opt_value_launcher_start db 1 +cfg_opt_value_mtrr db 0 +cfg_opt_value_ask_params db 0 +cfg_opt_value_imgfrom db RD_LOAD_FROM_MEMORY +cfg_opt_value_syspath db "/RD/1",0 + rb 20 + +memory_map_key dq 0 +descriptor_size dq 0 +descriptor_ver dq 0 +memory_map_size dq MEMORY_MAP_SIZE + +efi_fs_info_id db EFI_FILE_SYSTEM_INFO_ID +efi_fs_info_size dq sizeof.EFI_FILE_SYSTEM_INFO +efi_fs_info EFI_FILE_SYSTEM_INFO + +memory_map dq ? +gop_buffer rq GOP_BUFFER_SIZE/8 +devicesdat_data dq 0xffffffff +devicesdat_size dq 0x1000 +status dq ? + +section '.reloc' fixups data discardable diff --git a/kernel/branches/kolibri-lldw/build.bat b/kernel/branches/kolibri-lldw/build.bat new file mode 100644 index 000000000..1e8981838 --- /dev/null +++ b/kernel/branches/kolibri-lldw/build.bat @@ -0,0 +1,39 @@ +@echo off +cls + +call :Target_kernel + +if ERRORLEVEL 0 goto Exit_OK + +echo There was an error executing script. +echo For any help, please send a report. +pause +goto :eof + +:Target_kernel + rem valid languages: en ru ge et sp + set lang=en + + echo *** building kernel with language '%lang%' ... + + echo lang fix %lang% > lang.inc + fasm -m 65536 bootbios.asm bootbios.bin + fasm -m 65536 kernel.asm kernel.mnt + fasm -m 65536 kernel.asm kernel.bin -dUEFI=1 + if not %errorlevel%==0 goto :Error_FasmFailed + erase lang.inc +goto :eof + + +:Error_FasmFailed +echo error: fasm execution failed +erase lang.inc >nul 2>&1 +echo. +pause +exit 1 + +:Exit_OK +echo. +echo all operations have been done +pause +exit 0 diff --git a/kernel/branches/kolibri-lldw/build.sh b/kernel/branches/kolibri-lldw/build.sh new file mode 100755 index 000000000..bdd23245b --- /dev/null +++ b/kernel/branches/kolibri-lldw/build.sh @@ -0,0 +1,22 @@ +#!/bin/sh +# Compile the KolibriOS kernel on Linux +# 2017, The KolibriOS team + +KERPACK=$HOME/kolibrios/programs/other/kpack/kerpack_linux/kerpack +KOLIBRI_IMG=$HOME/nightly/kolibri.img + +replace=0; # Replace kernel in the image file? +echo 'lang fix en' > lang.inc +fasm -m 65536 bootbios.asm bootbios.bin +fasm -m 65536 kernel.asm kernel.mnt +$KERPACK kernel.mnt kernel.mnt + +[[ $replace -eq 1 ]] && { + mntpt=$(mktemp -d) + + sudo mount -o loop $KOLIBRI_IMG $mntpt + sudo mount -o remount,rw $mntpt + sudo cp kernel.mnt ${mntpt}/KERNEL.MNT + sudo umount $mntpt + rmdir $mntpt +} diff --git a/kernel/branches/kolibri-lldw/bus/pci/PCIe.inc b/kernel/branches/kolibri-lldw/bus/pci/PCIe.inc new file mode 100644 index 000000000..fe984eb60 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/pci/PCIe.inc @@ -0,0 +1,119 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2010-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ;; +;; PCIe.INC ;; +;; ;; +;; Extended PCI express services ;; +;; ;; +;; art_zh ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +;*************************************************************************** +; Function +; pci_ext_config: +; +; Description +; PCIe extended (memory-mapped) config space detection +; +; WARNINGs: +; 1) Very Experimental! +; 2) direct HT-detection (no ACPI or BIOS service used) +; 3) Only AMD/HT processors currently supported +; +;*************************************************************************** + +PCIe_CONFIG_SPACE = 0xF0000000 ; to be moved to const.inc +mmio_pcie_cfg_addr dd 0x0 ; intel pcie space may be defined here +mmio_pcie_cfg_lim dd 0x0 ; upper pcie space address + + +align 4 + +pci_ext_config: + + mov ebx, [mmio_pcie_cfg_addr] + or ebx, ebx + jz @f + or ebx, 0x7FFFFFFF ; required by PCI-SIG standards + jnz .pcie_failed + add ebx, 0x0FFFFC + cmp ebx, [mmio_pcie_cfg_lim]; is the space limit correct? + ja .pcie_failed + jmp .pcie_cfg_mapped +@@: + mov ebx, [cpu_vendor] + cmp ebx, dword [AMD_str] + jne .pcie_failed + mov bx, 0xC184 ; dev = 24, fn = 01, reg = 84h + +.check_HT_mmio: + mov cx, bx + mov ax, 0x0002 ; bus = 0, 1dword to read + call pci_read_reg + mov bx, cx + sub bl, 4 + and al, 0x80 ; check the NP bit + jz .no_pcie_cfg + shl eax, 8 ; bus:[27..20], dev:[19:15] + or eax, 0x00007FFC ; fun:[14..12], reg:[11:2] + mov [mmio_pcie_cfg_lim], eax + mov cl, bl + mov ax, 0x0002 ; bus = 0, 1dword to read + call pci_read_reg + mov bx, cx + test al, 0x03 ; MMIO Base RW enabled? + jz .no_pcie_cfg + test al, 0x0C ; MMIO Base locked? + jnz .no_pcie_cfg + xor al, al + shl eax, 8 + test eax, 0x000F0000 ; MMIO Base must be bus0-aligned + jnz .no_pcie_cfg + mov [mmio_pcie_cfg_addr], eax + add eax, 0x000FFFFC + sub eax, [mmio_pcie_cfg_lim]; MMIO must cover at least one bus + ja .no_pcie_cfg + +; -- it looks like a true PCIe config space; + mov eax, [mmio_pcie_cfg_addr] ; physical address + or eax, (PG_SHARED + PG_LARGE + PG_USER) + mov ebx, PCIe_CONFIG_SPACE ; linear address + mov ecx, ebx + shr ebx, 20 + add ebx, sys_pgdir ; PgDir entry @ +@@: + mov dword[ebx], eax ; map 4 buses + invlpg [ecx] + cmp bl, 4 + jz .pcie_cfg_mapped ; fix it later + add bl, 4 ; next PgDir entry + add eax, 0x400000 ; eax += 4M + add ecx, 0x400000 + jmp @b + +.pcie_cfg_mapped: + +; -- glad to have the extended PCIe config field found +; mov esi, boot_pcie_ok +; call boot_log + ret ; <<<<<<<<<<< OK >>>>>>>>>>> + +.no_pcie_cfg: + + xor eax, eax + mov [mmio_pcie_cfg_addr], eax + mov [mmio_pcie_cfg_lim], eax + add bl, 12 + cmp bl, 0xC0 ; MMIO regs lay below this offset + jb .check_HT_mmio +.pcie_failed: +; mov esi, boot_pcie_fail +; call boot_log + ret ; <<<<<<<<< FAILURE >>>>>>>>> + diff --git a/kernel/branches/kolibri-lldw/bus/pci/pci16.inc b/kernel/branches/kolibri-lldw/bus/pci/pci16.inc new file mode 100644 index 000000000..9040d5f88 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/pci/pci16.inc @@ -0,0 +1,51 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; PCI16.INC ;; +;; ;; +;; 16 bit PCI driver code ;; +;; ;; +;; Version 0.2 December 21st, 2002 ;; +;; ;; +;; Author: Victor Prodan, victorprodan@yahoo.com ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +init_pci_16: + + pushad + + xor ax, ax + mov es, ax + mov byte [es:BOOT_LO.pci_data], 1;default mechanism:1 + mov ax, 0xb101 + int 0x1a + or ah, ah + jnz pci16skip + + mov [es:BOOT_LO.pci_data+1], cl;last PCI bus in system + mov word[es:BOOT_LO.pci_data+2], bx + mov dword[es:BOOT_LO.pci_data+4], edi + +; we have a PCI BIOS, so check which configuration mechanism(s) +; it supports +; AL = PCI hardware characteristics (bit0 => mechanism1, bit1 => mechanism2) + test al, 1 + jnz pci16skip + test al, 2 + jz pci16skip + mov byte [es:BOOT_LO.pci_data], 2; if (al&3)==2 => mechanism 2 + +pci16skip: + + mov ax, 0x1000 + mov es, ax + + popad diff --git a/kernel/branches/kolibri-lldw/bus/pci/pci32.inc b/kernel/branches/kolibri-lldw/bus/pci/pci32.inc new file mode 100644 index 000000000..132cc92b2 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/pci/pci32.inc @@ -0,0 +1,679 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; ;; +;; PCI32.INC ;; +;; ;; +;; 32 bit PCI driver code ;; +;; ;; +;; Version 0.3 April 9, 2007 ;; +;; Version 0.2 December 21st, 2002 ;; +;; ;; +;; Author: Victor Prodan, victorprodan@yahoo.com ;; +;; Mihailov Ilia, ghost.nsk@gmail.com ;; +;; Credits: ;; +;; Ralf Brown ;; +;; Mike Hibbett, mikeh@oceanfree.net ;; +;; ;; +;; See file COPYING for details ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +;*************************************************************************** +; Function +; pci_api: +; +; Description +; entry point for system PCI calls +;*************************************************************************** +;mmio_pci_addr = 0x400 ; set actual PCI address here to activate user-MMIO + +iglobal +align 4 +f62call: + dd pci_fn_0 + dd pci_fn_1 + dd pci_fn_2 + dd pci_service_not_supported ;3 + dd pci_read_reg ;4 byte + dd pci_read_reg ;5 word + dd pci_read_reg ;6 dword + dd pci_service_not_supported ;7 + dd pci_write_reg ;8 byte + dd pci_write_reg ;9 word + dd pci_write_reg ;10 dword +if defined mmio_pci_addr + dd pci_mmio_init ;11 + dd pci_mmio_map ;12 + dd pci_mmio_unmap ;13 +end if + +endg + +align 4 + +pci_api: + +;cross + mov eax, ebx + mov ebx, ecx + mov ecx, edx + + cmp [pci_access_enabled], 1 + jne pci_service_not_supported + + movzx edx, al + +if defined mmio_pci_addr + cmp al, 13 + ja pci_service_not_supported +else + cmp al, 10 + ja pci_service_not_supported +end if + + call dword [f62call+edx*4] + mov dword [esp+32], eax + ret + + +align 4 +pci_api_drv: + + cmp [pci_access_enabled], 1 + jne .fail + + cmp eax, 2 + ja .fail + + jmp dword [f62call+eax*4] + +.fail: + or eax, -1 + ret + + +;; ============================================ + +pci_fn_0: +; PCI function 0: get pci version (AH.AL) + movzx eax, word [BOOT.pci_data+2] + ret + +pci_fn_1: +; PCI function 1: get last bus in AL + mov al, [BOOT.pci_data+1] + ret + +pci_fn_2: +; PCI function 2: get pci access mechanism + mov al, [BOOT.pci_data] + ret + +pci_service_not_supported: + or eax, -1 + mov dword [esp+32], eax + ret + +;*************************************************************************** +; Function +; pci_make_config_cmd +; +; Description +; creates a command dword for use with the PCI bus +; bus # in ah +; device+func in bh (dddddfff) +; register in bl +; +; command dword returned in eax ( 10000000 bbbbbbbb dddddfff rrrrrr00 ) +;*************************************************************************** + +align 4 + +pci_make_config_cmd: + shl eax, 8 ; move bus to bits 16-23 + mov ax, bx ; combine all + and eax, 0xffffff + or eax, 0x80000000 + ret + +;*************************************************************************** +; Function +; pci_read_reg: +; +; Description +; read a register from the PCI config space into EAX/AX/AL +; IN: ah=bus,device+func=bh,register address=bl +; number of bytes to read (1,2,4) coded into AL, bits 0-1 +; (0 - byte, 1 - word, 2 - dword) +;*************************************************************************** + +align 4 + +pci_read_reg: + push ebx esi + cmp byte [BOOT.pci_data], 2;what mechanism will we use? + je pci_read_reg_2 + + ; mechanism 1 + mov esi, eax ; save register size into ESI + and esi, 3 + + call pci_make_config_cmd + mov ebx, eax + mov dx, 0xcf8 + ; set up addressing to config data + mov eax, ebx + and al, 0xfc; make address dword-aligned + out dx, eax + ; get requested DWORD of config data + mov dl, 0xfc + and bl, 3 + or dl, bl ; add to port address first 2 bits of register address + + or esi, esi + jz pci_read_byte1 + cmp esi, 1 + jz pci_read_word1 + cmp esi, 2 + jz pci_read_dword1 + jmp pci_fin_read1 + +pci_read_byte1: + in al, dx + jmp pci_fin_read1 +pci_read_word1: + in ax, dx + jmp pci_fin_read1 +pci_read_dword1: + in eax, dx +pci_fin_read1: + pop esi ebx + ret +pci_read_reg_2: + + test bh, 128 ;mech#2 only supports 16 devices per bus + jnz pci_read_reg_err + + mov esi, eax ; save register size into ESI + and esi, 3 + + mov dx, 0xcfa + + ; out 0xcfa,bus + mov al, ah + out dx, al + ; out 0xcf8,0x80 + mov dl, 0xf8 + mov al, 0x80 + out dx, al + ; compute addr + shr bh, 3; func is ignored in mechanism 2 + or bh, 0xc0 + mov dx, bx + + or esi, esi + jz pci_read_byte2 + cmp esi, 1 + jz pci_read_word2 + cmp esi, 2 + jz pci_read_dword2 + jmp pci_fin_read2 + +pci_read_byte2: + in al, dx + jmp pci_fin_read2 +pci_read_word2: + in ax, dx + jmp pci_fin_read2 +pci_read_dword2: + in eax, dx +pci_fin_read2: + + pop esi ebx + ret + +pci_read_reg_err: + xor eax, eax + dec eax + pop esi ebx + ret + + +;*************************************************************************** +; Function +; pci_write_reg: +; +; Description +; write a register from ECX/CX/CL into the PCI config space +; IN: ah=bus,device+func=bh,register address (dword aligned)=bl, +; value to write in ecx +; number of bytes to write (1,2,4) coded into AL, bits 0-1 +; (0 - byte, 1 - word, 2 - dword) +;*************************************************************************** + +align 4 + +pci_write_reg: + push esi ebx + cmp byte [BOOT.pci_data], 2;what mechanism will we use? + je pci_write_reg_2 + + ; mechanism 1 + mov esi, eax ; save register size into ESI + and esi, 3 + + call pci_make_config_cmd + mov ebx, eax + mov dx, 0xcf8 + ; set up addressing to config data + mov eax, ebx + and al, 0xfc; make address dword-aligned + out dx, eax + ; write DWORD of config data + mov dl, 0xfc + and bl, 3 + or dl, bl + mov eax, ecx + + or esi, esi + jz pci_write_byte1 + cmp esi, 1 + jz pci_write_word1 + cmp esi, 2 + jz pci_write_dword1 + jmp pci_fin_write1 + +pci_write_byte1: + out dx, al + jmp pci_fin_write1 +pci_write_word1: + out dx, ax + jmp pci_fin_write1 +pci_write_dword1: + out dx, eax +pci_fin_write1: + + xor eax, eax + pop ebx esi + + ret +pci_write_reg_2: + + test bh, 128 ;mech#2 only supports 16 devices per bus + jnz pci_write_reg_err + + + mov esi, eax ; save register size into ESI + and esi, 3 + + mov dx, 0xcfa + ; out 0xcfa,bus + mov al, ah + out dx, al + ; out 0xcf8,0x80 + mov dl, 0xf8 + mov al, 0x80 + out dx, al + ; compute addr + shr bh, 3; func is ignored in mechanism 2 + or bh, 0xc0 + mov dx, bx + ; write register + mov eax, ecx + + or esi, esi + jz pci_write_byte2 + cmp esi, 1 + jz pci_write_word2 + cmp esi, 2 + jz pci_write_dword2 + jmp pci_fin_write2 + +pci_write_byte2: + out dx, al + jmp pci_fin_write2 +pci_write_word2: + out dx, ax + jmp pci_fin_write2 +pci_write_dword2: + out dx, eax +pci_fin_write2: + + xor eax, eax + pop ebx esi + ret + +pci_write_reg_err: + xor eax, eax + dec eax + pop ebx esi + ret + +if defined mmio_pci_addr ; must be set above +;*************************************************************************** +; Function +; pci_mmio_init +; +; Description +; IN: bx = device's PCI bus address (bbbbbbbbdddddfff) +; Returns eax = user heap space available (bytes) +; Error codes +; eax = -1 : PCI user access blocked, +; eax = -2 : device not registered for uMMIO service +; eax = -3 : user heap initialization failure +;*************************************************************************** +pci_mmio_init: + cmp bx, mmio_pci_addr + jz @f + mov eax, -2 + ret +@@: + call init_heap ; (if not initialized yet) + or eax, eax + jz @f + ret +@@: + mov eax, -3 + ret + + +;*************************************************************************** +; Function +; pci_mmio_map +; +; Description +; maps a block of PCI memory to user-accessible linear address +; +; WARNING! This VERY EXPERIMENTAL service is for one chosen PCI device only! +; The target device address should be set in kernel var mmio_pci_addr +; +; IN: ah = BAR#; +; IN: ebx = block size (bytes); +; IN: ecx = offset in MMIO block (in 4K-pages, to avoid misaligned pages); +; +; Returns eax = MMIO block's linear address in the userspace (if no error) +; +; +; Error codes +; eax = -1 : user access to PCI blocked, +; eax = -2 : an invalid BAR register referred +; eax = -3 : no i/o space on that BAR +; eax = -4 : a port i/o BAR register referred +; eax = -5 : dynamic userspace allocation problem +;*************************************************************************** + +pci_mmio_map: + and edx, 0x0ffff + cmp ah, 6 + jc .bar_0_5 + jz .bar_rom + mov eax, -2 + ret +.bar_rom: + mov ah, 8 ; bar6 = Expansion ROM base address +.bar_0_5: + push ecx + add ebx, 4095 + and ebx, -4096 + push ebx + mov bl, ah ; bl = BAR# (0..5), however bl=8 for BAR6 + shl bl, 1 + shl bl, 1 + add bl, 0x10; now bl = BAR offset in PCI config. space + mov ax, mmio_pci_addr + mov bh, al ; bh = dddddfff + mov al, 2 ; al : DW to read + call pci_read_reg + or eax, eax + jnz @f + mov eax, -3 ; empty I/O space + jmp mmio_ret_fail +@@: + test eax, 1 + jz @f + mov eax, -4 ; damned ports (not MMIO space) + jmp mmio_ret_fail +@@: + pop ecx ; ecx = block size, bytes (expanded to whole page) + mov ebx, ecx; user_alloc destroys eax, ecx, edx, but saves ebx + and eax, 0xFFFFFFF0 + push eax ; store MMIO physical address + keep 2DWords in the stack + stdcall user_alloc, ecx + or eax, eax + jnz mmio_map_over + mov eax, -5 ; problem with page allocation + +mmio_ret_fail: + pop ecx + pop edx + ret + +mmio_map_over: + mov ecx, ebx; ecx = size (bytes, expanded to whole page) + shr ecx, 12 ; ecx = number of pages + mov ebx, eax; ebx = linear address + pop eax ; eax = MMIO start + pop edx ; edx = MMIO shift (pages) + shl edx, 12 ; edx = MMIO shift (bytes) + add eax, edx; eax = uMMIO physical address + or eax, PG_SHARED + or eax, PG_UW + or eax, PG_NOCACHE + mov edi, ebx + call commit_pages + mov eax, edi + ret + +;*************************************************************************** +; Function +; pci_mmio_unmap_page +; +; Description +; unmaps the linear space previously tied to a PCI memory block +; +; IN: ebx = linear address of space previously allocated by pci_mmio_map +; returns eax = 1 if successfully unmapped +; +; Error codes +; eax = -1 if no user PCI access allowed, +; eax = 0 if unmapping failed +;*************************************************************************** + +pci_mmio_unmap: + stdcall user_free, ebx + ret + +end if + +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +uglobal +align 4 +; VendID (2), DevID (2), Revision = 0 (1), Class Code (3), FNum (1), Bus (1) +pci_emu_dat: + times 30*10 db 0 +endg +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +align 4 +sys_pcibios: + cmp [pci_access_enabled], 1 + jne .unsupported_func + cmp [pci_bios_entry], 0 + jz .emulate_bios + + push ds + mov ax, pci_data_sel + mov ds, ax + mov eax, ebp + mov ah, 0B1h + call pword [cs:pci_bios_entry] + pop ds + + jmp .return + ;-=-=-=-=-=-=-=-= +.emulate_bios: + cmp ebp, 1 ; PCI_FUNCTION_ID + jnz .not_PCI_BIOS_PRESENT + mov edx, 'PCI ' + mov al, [BOOT.pci_data] + mov bx, word[BOOT.pci_data + 2] + mov cl, [BOOT.pci_data + 1] + xor ah, ah + jmp .return_abcd + +.not_PCI_BIOS_PRESENT: + cmp ebp, 2 ; FIND_PCI_DEVICE + jne .not_FIND_PCI_DEVICE + mov ebx, pci_emu_dat +..nxt: + cmp [ebx], dx + jne ..no + cmp [ebx + 2], cx + jne ..no + dec si + jns ..no + mov bx, [ebx + 4] + xor ah, ah + jmp .return_ab +..no: + cmp word[ebx], 0 + je ..dev_not_found + add ebx, 10 + jmp ..nxt +..dev_not_found: + mov ah, 0x86 ; DEVICE_NOT_FOUND + jmp .return_a + +.not_FIND_PCI_DEVICE: + cmp ebp, 3 ; FIND_PCI_CLASS_CODE + jne .not_FIND_PCI_CLASS_CODE + mov esi, pci_emu_dat + shl ecx, 8 +..nxt2: + cmp [esi], ecx + jne ..no2 + mov bx, [esi] + xor ah, ah + jmp .return_ab +..no2: + cmp dword[esi], 0 + je ..dev_not_found + add esi, 10 + jmp ..nxt2 + +.not_FIND_PCI_CLASS_CODE: + cmp ebp, 8 ; READ_CONFIG_* + jb .not_READ_CONFIG + cmp ebp, 0x0A + ja .not_READ_CONFIG + mov eax, ebp + mov ah, bh + mov edx, edi + mov bh, bl + mov bl, dl + call pci_read_reg + mov ecx, eax + xor ah, ah ; SUCCESSFUL + jmp .return_abc +.not_READ_CONFIG: + cmp ebp, 0x0B ; WRITE_CONFIG_* + jb .not_WRITE_CONFIG + cmp ebp, 0x0D + ja .not_WRITE_CONFIG + lea eax, [ebp+1] + mov ah, bh + mov edx, edi + mov bh, bl + mov bl, dl + call pci_write_reg + xor ah, ah ; SUCCESSFUL + jmp .return_abc +.not_WRITE_CONFIG: +.unsupported_func: + mov ah, 0x81 ; FUNC_NOT_SUPPORTED +.return: + mov dword[esp + 4 ], edi + mov dword[esp + 8], esi +.return_abcd: + mov dword[esp + 24], edx +.return_abc: + mov dword[esp + 28], ecx +.return_ab: + mov dword[esp + 20], ebx +.return_a: + mov dword[esp + 32], eax + ret + +proc pci_enum + push ebp + mov ebp, esp + push 0 +virtual at ebp-4 +.devfn db ? +.bus db ? +end virtual +.loop: + mov ah, [.bus] + mov al, 2 + mov bh, [.devfn] + mov bl, 0 + call pci_read_reg + cmp eax, 0xFFFFFFFF + jnz .has_device + test byte [.devfn], 7 + jnz .next_func + jmp .no_device +.has_device: + push eax + movi eax, sizeof.PCIDEV + call malloc + pop ecx + test eax, eax + jz .nomemory + mov edi, eax + mov [edi+PCIDEV.vendor_device_id], ecx + mov eax, pcidev_list + mov ecx, [eax+PCIDEV.bk] + mov [edi+PCIDEV.bk], ecx + mov [edi+PCIDEV.fd], eax + mov [ecx+PCIDEV.fd], edi + mov [eax+PCIDEV.bk], edi + mov eax, dword [.devfn] + mov dword [edi+PCIDEV.devfn], eax + mov dword [edi+PCIDEV.owner], 0 + mov bh, al + mov al, 2 + mov bl, 8 + call pci_read_reg + shr eax, 8 + mov [edi+PCIDEV.class], eax + test byte [.devfn], 7 + jnz .next_func + mov ah, [.bus] + mov al, 0 + mov bh, [.devfn] + mov bl, 0Eh + call pci_read_reg + test al, al + js .next_func +.no_device: + or byte [.devfn], 7 +.next_func: + inc dword [.devfn] + mov ah, [.bus] + cmp ah, [BOOT.pci_data+1] + jbe .loop +.nomemory: + leave + ret +endp + +; Export for drivers. Just returns the pointer to the pci-devices list. +proc get_pcidev_list + mov eax, pcidev_list + ret +endp diff --git a/kernel/branches/kolibri-lldw/bus/usb/common.inc b/kernel/branches/kolibri-lldw/bus/usb/common.inc new file mode 100644 index 000000000..cf59e30cb --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/common.inc @@ -0,0 +1,462 @@ +; Constants and structures that are shared between different parts of +; USB subsystem and *HCI drivers. + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Version of all structures related to host controllers. +; Must be the same in kernel and *hci-drivers. +USBHC_VERSION = 2 + +; USB device must have at least 100ms of stable power before initializing can +; proceed; one timer tick is 10ms, so enforce delay in 10 ticks +USB_CONNECT_DELAY = 10 +; USB requires at least 10 ms for reset signalling. Normally, this is one timer +; tick. However, it is possible that we start reset signalling in the end of +; interval between timer ticks and then we test time in the start of the next +; interval; in this case, the delta between [timer_ticks] is 1, but the real +; time passed is significantly less than 10 ms. To avoid this, we add an extra +; tick; this guarantees that at least 10 ms have passed. +USB_RESET_TIME = 2 +; USB requires at least 10 ms of reset recovery, a delay between reset +; signalling and any commands to device. Add an extra tick for the same reasons +; as with the previous constant. +USB_RESET_RECOVERY_TIME = 2 + +; USB pipe types +CONTROL_PIPE = 0 +ISOCHRONOUS_PIPE = 1 +BULK_PIPE = 2 +INTERRUPT_PIPE = 3 + +; Status codes for transfer callbacks. +; Taken from OHCI as most verbose controller in this sense. +USB_STATUS_OK = 0 ; no error +USB_STATUS_CRC = 1 ; CRC error +USB_STATUS_BITSTUFF = 2 ; bit stuffing violation +USB_STATUS_TOGGLE = 3 ; data toggle mismatch +USB_STATUS_STALL = 4 ; device returned STALL +USB_STATUS_NORESPONSE = 5 ; device not responding +USB_STATUS_PIDCHECK = 6 ; invalid PID check bits +USB_STATUS_WRONGPID = 7 ; unexpected PID value +USB_STATUS_OVERRUN = 8 ; too many data from endpoint +USB_STATUS_UNDERRUN = 9 ; too few data from endpoint +USB_STATUS_BUFOVERRUN = 12 ; overflow of internal controller buffer +USB_STATUS_BUFUNDERRUN = 13 ; underflow of internal controller buffer +USB_STATUS_CLOSED = 16 ; pipe closed + ; either explicitly with USBClosePipe + ; or implicitly due to device disconnect +USB_STATUS_CANCELLED = 17 ; transfer cancelled with USBAbortPipe + +; Possible speeds of USB devices +USB_SPEED_FS = 0 ; full-speed +USB_SPEED_LS = 1 ; low-speed +USB_SPEED_HS = 2 ; high-speed + +; flags for usb_pipe.Flags +USB_FLAG_CLOSED = 1 ; pipe is closed, no new transfers +; pipe is closed, return error instead of submitting any new transfer +USB_FLAG_CAN_FREE = 2 +; pipe is closed via explicit call to USBClosePipe, so it can be freed without +; any driver notification; if this flag is not set, then the pipe is closed due +; to device disconnect, so it must remain valid until return from disconnect +; callback provided by the driver +USB_FLAG_EXTRA_WAIT = 4 +; The pipe was in wait list, while another event occured; +; when the first wait will be done, reinsert the pipe to wait list +USB_FLAG_DISABLED = 8 +; The pipe is temporarily disabled so that it is not visible to hardware +; but still remains in software list. Used for usb_abort_pipe. +USB_FLAG_CLOSED_BIT = 0 ; USB_FLAG_CLOSED = 1 shl USB_FLAG_CLOSED_BIT + +; ============================================================================= +; ================================ Structures ================================= +; ============================================================================= + +; Description of controller-specific data and functions. +struct usb_hardware_func +Version dd ? ; must be USBHC_VERSION +ID dd ? ; '*HCI' +DataSize dd ? ; sizeof(*hci_controller) +BeforeInit dd ? +; Early initialization: take ownership from BIOS. +; in: [ebp-4] = (bus shl 8) + devfn +Init dd ? +; Initialize controller-specific part of controller data. +; in: eax -> *hci_controller to initialize, [ebp-4] = (bus shl 8) + devfn +; out: eax = 0 <=> failed, otherwise eax -> usb_controller +ProcessDeferred dd ? +; Called regularly from the main loop of USB thread +; (either due to timeout from a previous call, or due to explicit wakeup). +; in: esi -> usb_controller +; out: eax = maximum timeout for next call (-1 = infinity) +SetDeviceAddress dd ? +; in: esi -> usb_controller, ebx -> usb_pipe, cl = address +GetDeviceAddress dd ? +; in: esi -> usb_controller, ebx -> usb_pipe +; out: eax = address +PortDisable dd ? +; Disable the given port in the root hub. +; in: esi -> usb_controller, ecx = port (zero-based) +InitiateReset dd ? +; Start reset signalling on the given port. +; in: esi -> usb_controller, ecx = port (zero-based) +SetEndpointPacketSize dd ? +; in: esi -> usb_controller, ebx -> usb_pipe, ecx = packet size +AllocPipe dd ? +; out: eax = pointer to allocated usb_pipe +FreePipe dd ? +; void stdcall with one argument = pointer to previously allocated usb_pipe +InitPipe dd ? +; in: edi -> usb_pipe for target, ecx -> usb_pipe for config pipe, +; esi -> usb_controller, eax -> usb_gtd for the first TD, +; [ebp+12] = endpoint, [ebp+16] = maxpacket, [ebp+20] = type +UnlinkPipe dd ? +; esi -> usb_controller, ebx -> usb_pipe +AllocTD dd ? +; out: eax = pointer to allocated usb_gtd +FreeTD dd ? +; void stdcall with one argument = pointer to previously allocated usb_gtd +AllocTransfer dd ? +; Allocate and initialize one stage of a transfer. +; ebx -> usb_pipe, other parameters are passed through the stack: +; buffer,size = data to transfer +; flags = same as in usb_open_pipe: +; bit 0 = allow short transfer, other bits reserved +; td = pointer to the current end-of-queue descriptor +; direction = +; 0000b for normal transfers, +; 1000b for control SETUP transfer, +; 1101b for control OUT transfer, +; 1110b for control IN transfer +; returns eax = pointer to the new end-of-queue descriptor +; (not included in the queue itself) or 0 on error +InsertTransfer dd ? +; Activate previously initialized transfer (maybe with multiple stages). +; esi -> usb_controller, ebx -> usb_pipe, +; [esp+4] -> first usb_gtd for the transfer, +; ecx -> last descriptor for the transfer +NewDevice dd ? +; Initiate configuration of a new device (create pseudo-pipe describing that +; device and call usb_new_device). +; esi -> usb_controller, eax = speed (one of USB_SPEED_* constants). +DisablePipe dd ? +; This procedure temporarily removes the given pipe from hardware queue. +; esi -> usb_controller, ebx -> usb_pipe +EnablePipe dd ? +; This procedure reinserts the given pipe to hardware queue +; after DisablePipe, with clearing transfer queue. +; esi -> usb_controller, ebx -> usb_pipe +; edx -> current descriptor, eax -> new last descriptor +ends + +; pointers to kernel API functions that are called from *HCI-drivers +struct usbhc_func +usb_process_gtd dd ? +usb_init_static_endpoint dd ? +usb_wakeup_if_needed dd ? +usb_subscribe_control dd ? +usb_subscription_done dd ? +usb_allocate_common dd ? +usb_free_common dd ? +usb_td_to_virt dd ? +usb_init_transfer dd ? +usb_undo_tds dd ? +usb_test_pending_port dd ? +usb_get_tt dd ? +usb_get_tt_think_time dd ? +usb_new_device dd ? +usb_disconnect_stage2 dd ? +usb_process_wait_lists dd ? +usb_unlink_td dd ? +usb_is_final_packet dd ? +usb_find_ehci_companion dd ? +ends + +; Controller descriptor. +; This structure represents the common (controller-independent) part +; of a controller for the USB code. The corresponding controller-dependent +; part *hci_controller is located immediately before usb_controller. +struct usb_controller +; Two following fields organize all controllers in the global linked list. +Next dd ? +Prev dd ? +HardwareFunc dd ? +; Pointer to usb_hardware_func structure with controller-specific functions. +NumPorts dd ? +; Number of ports in the root hub. +PCICoordinates dd ? +; Device:function and bus number from PCI. +; +; The hardware is allowed to cache some data from hardware structures. +; Regular operations are designed considering this, +; but sometimes it is required to wait for synchronization of hardware cache +; with modified structures in memory. +; The code keeps two queues of pipes waiting for synchronization, +; one for asynchronous (bulk/control) pipes, one for periodic pipes, hardware +; cache is invalidated under different conditions for those types. +; Both queues are organized in the same way, as single-linked lists. +; There are three special positions: the head of list (new pipes are added +; here), the first pipe to be synchronized at the current iteration, +; the tail of list (all pipes starting from here are synchronized). +WaitPipeListAsync dd ? +WaitPipeListPeriodic dd ? +; List heads. +WaitPipeRequestAsync dd ? +WaitPipeRequestPeriodic dd ? +; Pending request to hardware to refresh cache for items from WaitPipeList*. +; (Pointers to some items in WaitPipeList* or NULLs). +ReadyPipeHeadAsync dd ? +ReadyPipeHeadPeriodic dd ? +; Items of RemovingList* which were released by hardware and are ready +; for further processing. +; (Pointers to some items in WaitPipeList* or NULLs). +NewConnected dd ? +; bit mask of recently connected ports of the root hub, +; bit set = a device was recently connected to the corresponding port; +; after USB_CONNECT_DELAY ticks of stable status these ports are moved to +; PendingPorts +NewDisconnected dd ? +; bit mask of disconnected ports of the root hub, +; bit set = a device in the corresponding port was disconnected, +; disconnect processing is required. +PendingPorts dd ? +; bit mask of ports which are ready to be initialized +ControlLock MUTEX ? +; mutex which guards all operations with control queue +BulkLock MUTEX ? +; mutex which guards all operations with bulk queue +PeriodicLock MUTEX ? +; mutex which guards all operations with periodic queues +WaitSpinlock: +; spinlock guarding WaitPipeRequest/ReadyPipeHead (but not WaitPipeList) +StartWaitFrame dd ? +; USB frame number when WaitPipeRequest* was registered. +ResettingHub dd ? +; Pointer to usb_hub responsible for the currently resetting port, if any. +; NULL for the root hub. +ResettingPort db ? +; Port that is currently resetting, 0-based. +ResettingSpeed db ? +; Speed of currently resetting device. +ResettingStatus db ? +; Status of port reset. 0 = no port is resetting, -1 = reset failed, +; 1 = reset in progress, 2 = reset recovery in progress. + rb 1 ; alignment +ResetTime dd ? +; Time when reset signalling or reset recovery has been started. +SetAddressBuffer rb 8 +; Buffer for USB control command SET_ADDRESS. +ExistingAddresses rd 128/32 +; Bitmask for 128 bits; bit i is cleared <=> address i is free for allocating +; for new devices. Bit 0 is always set. +ConnectedTime rd 16 +; Time, in timer ticks, when the port i has signalled the connect event. +; Valid only if bit i in NewConnected is set. +DevicesByPort rd 16 +; Pointer to usb_pipe for zero endpoint (which serves as device handle) +; for each port. +ends + +; Pipe descriptor. +; * An USB pipe is described by two structures, for hardware and for software. +; * This is the software part. The hardware part is defined in a driver +; of the corresponding controller. +; * The hardware part is located immediately before usb_pipe, +; both are allocated at once by controller-specific code +; (it knows the total length, which depends on the hardware part). +struct usb_pipe +Controller dd ? +; Pointer to usb_controller structure corresponding to this pipe. +; Must be the first dword after hardware part, see *hci_new_device. +; +; Every endpoint is included into one of processing lists: +; * Bulk list contains all Bulk endpoints. +; * Control list contains all Control endpoints. +; * Several Periodic lists serve Interrupt endpoints with different interval. +; - There are N=2^n "leaf" periodic lists for N ms interval, one is processed +; in the frames 0,N,2N,..., another is processed in the frames +; 1,1+N,1+2N,... and so on. The hardware starts processing of periodic +; endpoints in every frame from the list identified by lower n bits of the +; frame number; the addresses of these N lists are written to the +; controller data area during the initialization. +; - We assume that n=5, N=32 to simplify the code and compact the data. +; OHCI works in this way. UHCI and EHCI actually have n=10, N=1024, +; but this is an overkill for interrupt endpoints; the large value of N is +; useful only for isochronous transfers in UHCI and EHCI. UHCI/EHCI code +; initializes "leaf" lists k,k+32,k+64,...,k+(1024-32) to the same value, +; giving essentially N=32. +; This restriction means that the actual maximum interval of polling any +; interrupt endpoint is 32ms, which seems to be a reasonable value. +; - Similarly, there are 16 lists for 16-ms interval, 8 lists for 8-ms +; interval and so on. Finally, there is one list for 1ms interval. Their +; addresses are not directly known to the controller. +; - The hardware serves endpoints following a physical link from the hardware +; part. +; - The hardware links are organized as follows. If the list item is not the +; last, it's hardware link points to the next item. The hardware link of +; the last item points to the first item of the "next" list. +; - The "next" list for k-th and (k+M)-th periodic lists for interval 2M ms +; is the k-th periodic list for interval M ms, M >= 1. In this scheme, +; if two "previous" lists are served in the frames k,k+2M,k+4M,... +; and k+M,k+3M,k+5M,... correspondingly, the "next" list is served in +; the frames k,k+M,k+2M,k+3M,k+4M,k+5M,..., which is exactly what we want. +; - The links between Periodic, Control, Bulk lists and the processing of +; Isochronous endpoints are controller-specific. +; * The head of every processing list is a static entry which does not +; correspond to any real pipe. It is described by usb_static_ep +; structure, not usb_pipe. For OHCI and UHCI, sizeof.usb_static_ep plus +; sizeof hardware part is 20h, the total number of lists is +; 32+16+8+4+2+1+1+1 = 65, so all these structures fit in one page, +; leaving space for other data. This is another reason for 32ms limit. +; * Static endpoint descriptors are kept in *hci_controller structure. +; * All items in every processing list, including the static head, are +; organized in a double-linked list using .NextVirt and .PrevVirt fields. +; * [[item.NextVirt].PrevVirt] = [[item.PrevVirt].NextVirt] for all items. +NextVirt dd ? +; Next endpoint in the processing list. +; See also PrevVirt field and the description before NextVirt field. +PrevVirt dd ? +; Previous endpoint in the processing list. +; See also NextVirt field and the description before NextVirt field. +BaseList dd ? +; Pointer to head of the processing list. +; +; Every pipe has the associated transfer queue, that is, the double-linked +; list of Transfer Descriptors aka TD. For Control, Bulk and Interrupt +; endpoints this list consists of usb_gtd structures +; (GTD = General Transfer Descriptors), for Isochronous endpoints +; this list consists of usb_itd structures, which are not developed yet. +; The pipe needs to know only the last TD; the first TD can be +; obtained as [[pipe.LastTD].NextVirt]. +LastTD dd ? +; Last TD in the transfer queue. +; +; All opened pipes corresponding to the same physical device are organized in +; the double-linked list using .NextSibling and .PrevSibling fields. +; The head of this list is kept in usb_device_data structure (OpenedPipeList). +; This list is used when the device is disconnected and all pipes for the +; device should be closed. +; Also, all pipes closed due to disconnect must remain valid at least until +; driver-provided disconnect function returns; all should-be-freed-but-not-now +; pipes for one device are organized in another double-linked list with +; the head in usb_device_data.ClosedPipeList; this list uses the same link +; fields, one pipe can never be in both lists. +NextSibling dd ? +; Next pipe for the physical device. +PrevSibling dd ? +; Previous pipe for the physical device. +; +; When hardware part of pipe is changed, some time is needed before further +; actions so that hardware reacts on this change. During that time, +; all changed pipes are organized in single-linked list with the head +; usb_controller.WaitPipeList* and link field NextWait. +; Currently there are two possible reasons to change: +; change of address/packet size in initial configuration, +; close of the pipe. They are distinguished by USB_FLAG_CLOSED. +NextWait dd ? +Lock MUTEX +; Mutex that guards operations with transfer queue for this pipe. +Type db ? +; Type of pipe, one of {CONTROL,ISOCHRONOUS,BULK,INTERRUPT}_PIPE. +Flags db ? +; Combination of flags, USB_FLAG_*. + rb 2 ; dword alignment +DeviceData dd ? +; Pointer to usb_device_data, common for all pipes for one device. +ends + +; This structure describes the static head of every list of pipes. +struct usb_static_ep +; software fields +Bandwidth dd ? +; valid only for interrupt/isochronous USB1 lists +; The offsets of the following two fields must be the same in this structure +; and in usb_pipe. +NextVirt dd ? +PrevVirt dd ? +ends + +; This structure represents one transfer descriptor +; ('g' stands for "general" as opposed to isochronous usb_itd). +; Note that one transfer can have several descriptors: +; a control transfer has three stages. +; Additionally, every controller has a limit on transfer length with +; one descriptor (packet size for UHCI, 1K for OHCI, 4K for EHCI), +; large transfers must be split into individual packets according to that limit. +struct usb_gtd +Callback dd ? +; Zero for intermediate descriptors, pointer to callback function +; for final descriptor. See the docs for description of the callback. +UserData dd ? +; Dword which is passed to Callback as is, not used by USB code itself. +; Two following fields organize all descriptors for one pipe in +; the linked list. +NextVirt dd ? +PrevVirt dd ? +Pipe dd ? +; Pointer to the parent usb_pipe. +Buffer dd ? +; Pointer to data for this descriptor. +Length dd ? +; Length of data for this descriptor. +ends + +; Interface-specific data. Several interfaces of one device can operate +; independently, each is controlled by some driver and is identified by +; some driver-specific data passed as is to the driver. +struct usb_interface_data +DriverData dd ? +; Passed as is to the driver. +DriverFunc dd ? +; Pointer to USBSRV structure for the driver. +ends + +; Device-specific data. +struct usb_device_data +PipeListLock MUTEX +; Lock guarding OpenedPipeList. Must be the first item of the structure, +; the code passes pointer to usb_device_data as is to mutex_lock/unlock. +OpenedPipeList rd 2 +; List of all opened pipes for the device. +; Used when the device is disconnected, so all pipes should be closed. +ClosedPipeList rd 2 +; List of all closed, but still valid pipes for the device. +; A pipe closed with USBClosePipe is just deallocated, +; but a pipe closed due to disconnect must remain valid until driver-provided +; disconnect handler returns; this list links all such pipes to deallocate them +; after disconnect processing. +NumPipes dd ? +; Number of not-yet-closed pipes. +Hub dd ? +; NULL if connected to the root hub, pointer to usb_hub otherwise. +TTHub dd ? +; Pointer to usb_hub for (the) hub with Transaction Translator for the device, +; NULL if the device operates in the same speed as the controller. +Port db ? +; Port on the hub, zero-based. +TTPort db ? +; Port on the TTHub, zero-based. +DeviceDescrSize db ? +; Size of device descriptor. +Speed db ? +; Device speed, one of USB_SPEED_*. +Timer dd ? +; Handle of timer that handles request timeout. +NumInterfaces dd ? +; Number of interfaces. +ConfigDataSize dd ? +; Total size of data associated with the configuration descriptor +; (including the configuration descriptor itself). +Interfaces dd ? +; Offset from the beginning of this structure to Interfaces field. +; Variable-length fields: +; DeviceDescriptor: +; device descriptor starts here +; ConfigDescriptor = DeviceDescriptor + DeviceDescrSize +; configuration descriptor with all associated data +; Interfaces = ALIGN_UP(ConfigDescriptor + ConfigDataSize, 4) +; array of NumInterfaces elements of type usb_interface_data +ends + +usb_device_data.DeviceDescriptor = sizeof.usb_device_data diff --git a/kernel/branches/kolibri-lldw/bus/usb/hccommon.inc b/kernel/branches/kolibri-lldw/bus/usb/hccommon.inc new file mode 100644 index 000000000..90eacfab5 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/hccommon.inc @@ -0,0 +1,348 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; USB Host Controller support code: hardware-independent part, +; common for all controller types. + +iglobal +; USB HC support: some functions interesting only for *HCI-drivers. +align 4 +usb_hc_func: + dd usb_process_gtd + dd usb_init_static_endpoint + dd usb_wakeup_if_needed + dd usb_subscribe_control + dd usb_subscription_done + dd slab_alloc + dd slab_free + dd usb_td_to_virt + dd usb_init_transfer + dd usb_undo_tds + dd usb_test_pending_port + dd usb_get_tt + dd usb_get_tt_think_time + dd usb_new_device + dd usb_disconnect_stage2 + dd usb_process_wait_lists + dd usb_unlink_td + dd usb_is_final_packet + dd usb_find_ehci_companion +endg + +; Initializes one controller, called by usb_init for every controller. +; eax -> PCIDEV structure for the device. +proc usb_init_controller + push ebp + mov ebp, esp +; 1. Store in the stack PCI coordinates and save pointer to PCIDEV: +; make [ebp-4] = (bus shl 8) + devfn, used by controller-specific Init funcs. + push dword [eax+PCIDEV.devfn] + push eax + mov edi, [eax+PCIDEV.owner] + test edi, edi + jz .nothing + mov edi, [edi+USBSRV.usb_func] +; 2. Allocate *hci_controller + usb_controller. + mov ebx, [edi+usb_hardware_func.DataSize] + add ebx, sizeof.usb_controller + stdcall kernel_alloc, ebx + test eax, eax + jz .nothing +; 3. Zero-initialize both structures. + push edi eax + mov ecx, ebx + shr ecx, 2 + xchg edi, eax + xor eax, eax + rep stosd +; 4. Initialize usb_controller structure, +; except data known only to controller-specific code (like NumPorts) +; and link fields +; (this structure will be inserted to the overall list at step 6). + dec eax + mov [edi+usb_controller.ExistingAddresses+4-sizeof.usb_controller], eax + mov [edi+usb_controller.ExistingAddresses+8-sizeof.usb_controller], eax + mov [edi+usb_controller.ExistingAddresses+12-sizeof.usb_controller], eax + mov [edi+usb_controller.ResettingPort-sizeof.usb_controller], al ; no resetting port + dec eax ; don't allocate zero address + mov [edi+usb_controller.ExistingAddresses-sizeof.usb_controller], eax + mov eax, [ebp-4] + mov [edi+usb_controller.PCICoordinates-sizeof.usb_controller], eax + lea ecx, [edi+usb_controller.PeriodicLock-sizeof.usb_controller] + call mutex_init + add ecx, usb_controller.ControlLock - usb_controller.PeriodicLock + call mutex_init + add ecx, usb_controller.BulkLock - usb_controller.ControlLock + call mutex_init + pop eax edi + mov [eax+ebx-sizeof.usb_controller+usb_controller.HardwareFunc], edi + push eax +; 5. Call controller-specific initialization. +; If failed, free memory allocated in step 2 and return. + call [edi+usb_hardware_func.Init] + test eax, eax + jz .fail + pop ecx +; 6. Insert the controller to the global list. + xchg eax, ebx + mov ecx, usb_controllers_list_mutex + call mutex_lock + mov edx, usb_controllers_list + mov eax, [edx+usb_controller.Prev] + mov [ebx+usb_controller.Next], edx + mov [ebx+usb_controller.Prev], eax + mov [edx+usb_controller.Prev], ebx + mov [eax+usb_controller.Next], ebx + call mutex_unlock +; 7. Wakeup USB thread to call ProcessDeferred. + call usb_wakeup +.nothing: +; 8. Restore pointer to PCIDEV saved in step 1 and return. + pop eax + leave + ret +.fail: + call kernel_free + jmp .nothing +endp + +; Helper function, calculates physical address including offset in page. +proc get_phys_addr + push ecx + mov ecx, eax + and ecx, 0xFFF + call get_pg_addr + add eax, ecx + pop ecx + ret +endp + +; Put the given control/bulk pipe in the wait list; +; called when the pipe structure is changed and a possible hardware cache +; needs to be synchronized. When it will be known that the cache is updated, +; usb_subscription_done procedure will be called. +proc usb_subscribe_control + cmp [ebx+usb_pipe.NextWait], -1 + jnz @f + mov eax, [esi+usb_controller.WaitPipeListAsync] + mov [ebx+usb_pipe.NextWait], eax + mov [esi+usb_controller.WaitPipeListAsync], ebx +@@: + ret +endp + +; Same as usb_subscribe_control, but for interrupt/isochronous pipe. +proc usb_subscribe_periodic + cmp [ebx+usb_pipe.NextWait], -1 + jnz @f + mov eax, [esi+usb_controller.WaitPipeListPeriodic] + mov [ebx+usb_pipe.NextWait], eax + mov [esi+usb_controller.WaitPipeListPeriodic], ebx +@@: + ret +endp + +; Called after synchronization of hardware cache with software changes. +; Continues process of device enumeration based on when it was delayed +; due to call to usb_subscribe_control. +proc usb_subscription_done + mov eax, [ebx+usb_pipe.DeviceData] + cmp [eax+usb_device_data.DeviceDescrSize], 0 + jz usb_after_set_address + jmp usb_after_set_endpoint_size +endp + +; This function is called when a new device has either passed +; or failed first stages of configuration, so the next device +; can enter configuration process. +proc usb_test_pending_port + mov [esi+usb_controller.ResettingPort], -1 + cmp [esi+usb_controller.PendingPorts], 0 + jz .nothing + bsf ecx, [esi+usb_controller.PendingPorts] + btr [esi+usb_controller.PendingPorts], ecx + mov eax, [esi+usb_controller.HardwareFunc] + jmp [eax+usb_hardware_func.InitiateReset] +.nothing: + ret +endp + +; This procedure is regularly called from controller-specific ProcessDeferred, +; it checks whether there are disconnected events and if so, process them. +proc usb_disconnect_stage2 + bsf ecx, [esi+usb_controller.NewDisconnected] + jz .nothing + lock btr [esi+usb_controller.NewDisconnected], ecx + btr [esi+usb_controller.PendingPorts], ecx + xor ebx, ebx + xchg ebx, [esi+usb_controller.DevicesByPort+ecx*4] + test ebx, ebx + jz usb_disconnect_stage2 + call usb_device_disconnected + jmp usb_disconnect_stage2 +.nothing: + ret +endp + +; Initial stage of disconnect processing: called when device is disconnected. +proc usb_device_disconnected +; Loop over all pipes, close everything, wait until hardware reacts. +; The final handling is done in usb_pipe_closed. + push ebx + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_lock + lea eax, [ecx+usb_device_data.OpenedPipeList-usb_pipe.NextSibling] + push eax + mov ebx, [eax+usb_pipe.NextSibling] +.pipe_loop: + call usb_close_pipe_nolock + mov ebx, [ebx+usb_pipe.NextSibling] + cmp ebx, [esp] + jnz .pipe_loop + pop eax + pop ebx + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_unlock + ret +endp + +; Called from controller-specific ProcessDeferred, +; processes wait-pipe-done notifications, +; returns whether there are more items in wait queues. +; in: esi -> usb_controller +; out: eax = bitmask of pipe types with non-empty wait queue +proc usb_process_wait_lists + xor edx, edx + push edx + call usb_process_one_wait_list + jnc @f + or byte [esp], 1 shl CONTROL_PIPE +@@: + movi edx, 4 + call usb_process_one_wait_list + jnc @f + or byte [esp], 1 shl INTERRUPT_PIPE +@@: + xor edx, edx + call usb_process_one_wait_list + jnc @f + or byte [esp], 1 shl CONTROL_PIPE +@@: + pop eax + ret +endp + +; Helper procedure for usb_process_wait_lists; +; does the same for one wait queue. +; in: esi -> usb_controller, +; edx=0 for *Async, edx=4 for *Periodic list +; out: CF = issue new request +proc usb_process_one_wait_list +; 1. Check whether there is a pending request. If so, do nothing. + mov ebx, [esi+usb_controller.WaitPipeRequestAsync+edx] + cmp ebx, [esi+usb_controller.ReadyPipeHeadAsync+edx] + clc + jnz .nothing +; 2. Check whether there are new data. If so, issue a new request. + cmp ebx, [esi+usb_controller.WaitPipeListAsync+edx] + stc + jnz .nothing + test ebx, ebx + jz .nothing +; 3. Clear all lists. + xor ecx, ecx + mov [esi+usb_controller.WaitPipeListAsync+edx], ecx + mov [esi+usb_controller.WaitPipeRequestAsync+edx], ecx + mov [esi+usb_controller.ReadyPipeHeadAsync+edx], ecx +; 4. Loop over all pipes from the wait list. +.pipe_loop: +; For every pipe: +; 5. Save edx and next pipe in the list. + push edx + push [ebx+usb_pipe.NextWait] +; 6. If USB_FLAG_EXTRA_WAIT is set, reinsert the pipe to the list and continue. + test [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT + jz .process + mov eax, [esi+usb_controller.WaitPipeListAsync+edx] + mov [ebx+usb_pipe.NextWait], eax + mov [esi+usb_controller.WaitPipeListAsync+edx], ebx + jmp .continue +.process: +; 7. Call the handler depending on USB_FLAG_CLOSED and USB_FLAG_DISABLED. + or [ebx+usb_pipe.NextWait], -1 + test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + jz .nodisconnect + call usb_pipe_closed + jmp .continue +.nodisconnect: + test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED + jz .nodisabled + call usb_pipe_disabled + jmp .continue +.nodisabled: + call usb_subscription_done +.continue: +; 8. Restore edx and next pipe saved in step 5 and continue the loop. + pop ebx + pop edx + test ebx, ebx + jnz .pipe_loop +.check_new_work: +; 9. Set CF depending on whether WaitPipeList* is nonzero. + cmp [esi+usb_controller.WaitPipeListAsync+edx], 1 + cmc +.nothing: + ret +endp + +; Called from USB1 controller-specific initialization. +; Finds EHCI companion controller for given USB1 controller. +; in: bl = PCI device:function for USB1 controller, bh = PCI bus +; out: eax -> usb_controller for EHCI companion +proc usb_find_ehci_companion +; 1. Loop over all registered controllers. + mov eax, usb_controllers_list +.next: + mov eax, [eax+usb_controller.Next] + cmp eax, usb_controllers_list + jz .notfound +; 2. For every controller, check the type, ignore everything that is not EHCI. + mov edx, [eax+usb_controller.HardwareFunc] + cmp [edx+usb_hardware_func.ID], 'EHCI' + jnz .next +; 3. For EHCI controller, compare PCI coordinates with input data: +; bus and device must be the same, function can be different. + mov edx, [eax+usb_controller.PCICoordinates] + xor edx, ebx + cmp dx, 8 + jae .next + ret +.notfound: + xor eax, eax + ret +endp + +; Find Transaction Translator hub and port for the given device. +; in: edx = parent hub for the device, ecx = port for the device +; out: edx = TT hub for the device, ecx = TT port for the device. +proc usb_get_tt +; If the parent hub is high-speed, it is TT for the device. +; Otherwise, the parent hub itself is behind TT, and the device +; has the same TT hub+port as the parent hub. + mov eax, [edx+usb_hub.ConfigPipe] + mov eax, [eax+usb_pipe.DeviceData] + cmp [eax+usb_device_data.Speed], USB_SPEED_HS + jz @f + movzx ecx, [eax+usb_device_data.TTPort] + mov edx, [eax+usb_device_data.TTHub] +@@: + mov edx, [edx+usb_hub.ConfigPipe] + ret +endp diff --git a/kernel/branches/kolibri-lldw/bus/usb/hub.inc b/kernel/branches/kolibri-lldw/bus/usb/hub.inc new file mode 100644 index 000000000..40cfaa5b6 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/hub.inc @@ -0,0 +1,1285 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Support for USB (non-root) hubs: +; powering up/resetting/disabling ports, +; watching for adding/removing devices. + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; Hub constants +; USB hub descriptor type +USB_HUB_DESCRIPTOR = 29h + +; Features for CLEAR_FEATURE commands to the hub. +C_HUB_LOCAL_POWER = 0 +C_HUB_OVER_CURRENT = 1 + +; Bits in result of GET_STATUS command for a port. +; Also suitable for CLEAR_FEATURE/SET_FEATURE commands, where applicable, +; except TEST/INDICATOR. +PORT_CONNECTION = 0 +PORT_ENABLE = 1 +PORT_SUSPEND = 2 +PORT_OVER_CURRENT = 3 +PORT_RESET = 4 +PORT_POWER = 8 +PORT_LOW_SPEED = 9 +PORT_HIGH_SPEED = 10 +PORT_TEST_BIT = 11 +PORT_INDICATOR_BIT = 12 +C_PORT_CONNECTION = 16 +C_PORT_ENABLE = 17 +C_PORT_SUSPEND = 18 +C_PORT_OVER_CURRENT = 19 +C_PORT_RESET = 20 +PORT_TEST_FEATURE = 21 +PORT_INDICATOR_FEATURE = 22 + +; Internal constants +; Bits in usb_hub.Actions +HUB_WAIT_POWERED = 1 +; ports were powered, wait until power is stable +HUB_WAIT_CONNECT = 2 +; some device was connected, wait initial debounce interval +HUB_RESET_IN_PROGRESS = 4 +; reset in progress, so buffer for config requests is owned +; by reset process; this includes all stages from initial disconnect test +; to end of setting address (fail on any stage should lead to disabling port, +; which requires a config request) +HUB_RESET_WAITING = 8 +; the port is ready for reset, but another device somewhere on the bus +; is resetting. Implies HUB_RESET_IN_PROGRESS +HUB_RESET_SIGNAL = 10h +; reset signalling is active for some port in the hub +; Implies HUB_RESET_IN_PROGRESS +HUB_RESET_RECOVERY = 20h +; reset recovery is active for some port in the hub +; Implies HUB_RESET_IN_PROGRESS + +; Well, I think that those 5 flags WAIT_CONNECT and RESET_* require additional +; comments. So that is the overview of what happens with a new device assuming +; no errors. +; * device is connected; +; * hub notifies us about connect event; after some processing +; usb_hub_port_change finally processes that event, setting the flag +; HUB_WAIT_CONNECT and storing time when the device was connected; +; * 100 ms delay; +; * usb_hub_process_deferred clears HUB_WAIT_CONNECT, +; sets HUB_RESET_IN_PROGRESS, stores the port index in ConfigBuffer and asks +; the hub whether there was a disconnect event for that port during those +; 100 ms (on the hardware level notifications are obtained using polling +; with some intervals, so it is possible that the corresponding notification +; has not arrived yet); +; * usb_hub_connect_port_status checks that there was no disconnect event +; and sets HUB_RESET_WAITING flag (HUB_RESET_IN_PROGRESS is still set, +; ConfigBuffer still contains the port index); +; * usb_hub_process_deferred checks whether there is another device currently +; resetting. If so, it waits until reset is done +; (with HUB_RESET_WAITING and HUB_RESET_IN_PROGRESS bits set); +; * usb_hub_process_deferred clears HUB_RESET_WAITING, sets HUB_RESET_SIGNAL +; and initiates reset signalling on the port; +; * usb_hub_process_deferred checks the status every tick; +; when reset signalling is stopped by the hub, usb_hub_resetting_port_status +; callback clears HUB_RESET_SIGNAL and sets HUB_RESET_RECOVERY; +; * 10 ms (at least) delay; +; * usb_hub_process_deferred clears HUB_RESET_RECOVERY and notifies other code +; that the new device is ready to be configured; +; * when it is possible to reset another device, the protocol layer +; clears HUB_RESET_IN_PROGRESS bit. + +; ============================================================================= +; ================================ Structures ================================= +; ============================================================================= +; This structure contains all used data for one hub. +struct usb_hub +; All configured hubs are organized in the global usb_hub_list. +; Two following fields give next/prev items in that list. +; While the hub is unconfigured, they point to usb_hub itself. +Next dd ? +Prev dd ? +Controller dd ? +; Pointer to usb_controller for the bus. +; +; Handles of two pipes: configuration control pipe for zero endpoint opened by +; the common code and status interrupt pipe opened by us. +ConfigPipe dd ? +StatusPipe dd ? +NumPorts dd ? +; Number of downstream ports; from 1 to 255. +MaxPacketSize dd ? +; Maximum packet size for interrupt endpoint. +; Usually equals ceil((1+NumPorts)/8), but some hubs give additional bytes. +Actions dd ? +; Bitfield with HUB_* constants. +PoweredOnTime dd ? +; Time (in ticks) when all downstream ports were powered up. +ResetTime dd ? +; Time (in ticks) when the current port was reset; +; when a port is resetting, contains the last tick of status check; +; when reset recovery for a port is active, contains the time when +; reset was completed. +; +; There are two possible reasons for configuration requests: +; synchronous, when certain time is passed after something, +; and asynchronous, when the hub is notifying about some change and +; config request needs to be issued in order to query details. +; Use two different buffers to avoid unnecessary dependencies. +ConfigBuffer rb 8 +; Buffer for configuration requests for synchronous events. +ChangeConfigBuffer rb 8 +; Buffer for configuration requests for status changes. +AccStatusChange db ? +; Accumulated status change. See 11.12.3 of USB2 spec or comments in code. +HubCharacteristics dw ? +; Copy of usb_hub_descr.wHubCharacteristics. +PowerOnInterval db ? +; Copy of usb_hub_descr.bPwrOn2PwrGood. +; +; Two following fields are written at once by GET_STATUS request +; and must remain in this order. +StatusData dw ? +; Bitfield with 1 shl PORT_* indicating status of the current port. +StatusChange dw ? +; Bitfield with 1 shl PORT_* indicating change in status of the current port. +; Two following fields are written at once by GET_STATUS request +; and must remain in this order. +; The meaning is the same as of StatusData/StatusChange; two following fields +; are used by the synchronous requests to avoid unnecessary interactions with +; the asynchronous handler. +ResetStatusData dw ? +ResetStatusChange dw ? +StatusChangePtr dd ? +; Pointer to StatusChangeBuf. +ConnectedDevicesPtr dd ? +; Pointer to ConnectedDevices. +ConnectedTimePtr dd ? +; Pointer to ConnectedTime. +; +; Variable-length parts: +; DeviceRemovable rb (NumPorts+8)/8 +; Bit i+1 = device at port i (zero-based) is non-removable. +; StatusChangeBuf rb (NumPorts+8)/8 +; Buffer for status interrupt pipe. Bit 0 = hub status change, +; other bits = status change of the corresponding ports. +; ConnectedDevices rd NumPorts +; Pointers to config pipes for connected devices or zero if no device connected. +; ConnectedTime rd NumPorts +; For initial debounce interval: +; time (in ticks) when a device was connected at that port. +; Normally: -1 +ends + +; Hub descriptor. +struct usb_hub_descr usb_descr +bNbrPorts db ? +; Number of downstream ports. +wHubCharacteristics dw ? +; Bit 0: 0 = all ports are powered at once, 1 = individual port power switching +; Bit 1: reserved, must be zero +; Bit 2: 1 = the hub is part of a compound device +; Bits 3-4: 00 = global overcurrent protection, +; 01 = individual port overcurrent protection, +; 1x = no overcurrent protection +; Bits 5-6: Transaction Translator Think Time, 8*(value+1) full-speed bit times +; Bit 7: 1 = port indicators supported +; Other bits are reserved. +bPwrOn2PwrGood db ? +; Time in 2ms intervals between powering up a port and a port becoming ready. +bHubContrCurrent db ? +; Maximum current requirements of the Hub Controller electronics in mA. +; DeviceRemovable - variable length +; Bit 0 is reserved, bit i+1 = device at port i is non-removable. +; PortPwrCtrlMask - variable length +; Obsolete, exists for compatibility. We ignore it. +ends + +iglobal +align 4 +; Implementation of struct USBFUNC for hubs. +usb_hub_callbacks: + dd usb_hub_callbacks_end - usb_hub_callbacks + dd usb_hub_init + dd usb_hub_disconnect +usb_hub_callbacks_end: +usb_hub_pseudosrv dd usb_hub_callbacks +endg + +; This procedure is called when new hub is detected. +; It initializes the device. +; Technically, initialization implies sending several USB queries, +; so it is split in several procedures. The first is usb_hub_init, +; other are callbacks which will be called at some time in the future, +; when the device will respond. +; edx = usb_interface_descr, ecx = length rest +proc usb_hub_init + push ebx esi ; save used registers to be stdcall +virtual at esp + rd 2 ; saved registers + dd ? ; return address +.pipe dd ? ; handle of the config pipe +.config dd ? ; pointer to usb_config_descr +.interface dd ? ; pointer to usb_interface_descr +end virtual +; 1. Check that the maximal nesting is not exceeded: +; 5 non-root hubs is the maximum according to the spec. + mov ebx, [.pipe] + push 5 + mov eax, ebx +.count_parents: + mov eax, [eax+usb_pipe.DeviceData] + mov eax, [eax+usb_device_data.Hub] + test eax, eax + jz .depth_ok + mov eax, [eax+usb_hub.ConfigPipe] + dec dword [esp] + jnz .count_parents + pop eax + dbgstr 'Hub chain is too long' + jmp .return0 +.depth_ok: + pop eax +; Hubs use one IN interrupt endpoint for polling the device +; 2. Locate the descriptor of the interrupt endpoint. +; Loop over all descriptors owned by this interface. +.lookep: +; 2a. Skip the current descriptor. + movzx eax, [edx+usb_descr.bLength] + add edx, eax + sub ecx, eax + jb .errorep +; 2b. Length of data left must be at least sizeof.usb_endpoint_descr. + cmp ecx, sizeof.usb_endpoint_descr + jb .errorep +; 2c. If we have found another interface descriptor but not found our endpoint, +; this is an error: all subsequent descriptors belong to that interface +; (or further interfaces). + cmp [edx+usb_endpoint_descr.bDescriptorType], USB_INTERFACE_DESCR + jz .errorep +; 2d. Ignore all interface-related descriptors except endpoint descriptor. + cmp [edx+usb_endpoint_descr.bDescriptorType], USB_ENDPOINT_DESCR + jnz .lookep +; 2e. Length of endpoint descriptor must be at least sizeof.usb_endpoint_descr. + cmp [edx+usb_endpoint_descr.bLength], sizeof.usb_endpoint_descr + jb .errorep +; 2f. Ignore all endpoints except for INTERRUPT IN. + cmp [edx+usb_endpoint_descr.bEndpointAddress], 0 + jge .lookep + mov al, [edx+usb_endpoint_descr.bmAttributes] + and al, 3 + cmp al, INTERRUPT_PIPE + jnz .lookep +; We have located the descriptor for INTERRUPT IN endpoint, +; the pointer is in edx. +; 3. Allocate memory for the hub descriptor. +; Maximum length (assuming 255 downstream ports) is 40 bytes. +; Allocate 4 extra bytes to keep wMaxPacketSize. +; 3a. Save registers. + push edx +; 3b. Call the allocator. + movi eax, 44 + call malloc +; 3c. Restore registers. + pop ecx +; 3d. If failed, say something to the debug board and return error. + test eax, eax + jz .nomemory +; 3e. Store the pointer in esi. xchg eax,r32 is one byte shorter than mov. + xchg esi, eax +; 4. Open a pipe for the status endpoint with descriptor found in step 1. + movzx eax, [ecx+usb_endpoint_descr.bEndpointAddress] + movzx edx, [ecx+usb_endpoint_descr.bInterval] + movzx ecx, [ecx+usb_endpoint_descr.wMaxPacketSize] + test ecx, (1 shl 11) - 1 + jz .free + push ecx + stdcall usb_open_pipe, ebx, eax, ecx, INTERRUPT_PIPE, edx + pop ecx +; If failed, free the memory allocated in step 3, +; say something to the debug board and return error. + test eax, eax + jz .free +; 5. Send control query for the hub descriptor, +; pass status pipe as a callback parameter, +; allow short packets. + and ecx, (1 shl 11) - 1 + mov [esi+40], ecx + mov dword [esi], 0xA0 + \ ; class-specific request + (USB_GET_DESCRIPTOR shl 8) + \ + (0 shl 16) + \ ; descriptor index 0 + (USB_HUB_DESCRIPTOR shl 24) + mov dword [esi+4], 40 shl 16 + stdcall usb_control_async, ebx, esi, esi, 40, usb_hub_got_config, eax, 1 +; 6. If failed, free the memory allocated in step 3, +; say something to the debug board and return error. + test eax, eax + jz .free +; Otherwise, return 1. usb_hub_got_config will overwrite it later. + xor eax, eax + inc eax + jmp .nothing +.free: + xchg eax, esi + call free + jmp .return0 +.errorep: + dbgstr 'Invalid config descriptor for a hub' + jmp .return0 +.nomemory: + dbgstr 'No memory for USB hub data' +.return0: + xor eax, eax +.nothing: + pop esi ebx ; restore used registers to be stdcall + retn 12 +endp + +; This procedure is called when the request for the hub descriptor initiated +; by usb_hub_init is finished, either successfully or unsuccessfully. +proc usb_hub_got_config stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword + push ebx ; save used registers to be stdcall +; 1. If failed, say something to the debug board, free the buffer +; and stop the initialization. + cmp [status], 0 + jnz .invalid +; 2. The length must be at least sizeof.usb_hub_descr. +; Note that [length] includes 8 bytes of setup packet. + cmp [length], 8 + sizeof.usb_hub_descr + jb .invalid +; 3. Sanity checks for the hub descriptor. + mov eax, [buffer] +if USB_DUMP_DESCRIPTORS + mov ecx, [length] + sub ecx, 8 + DEBUGF 1,'K : hub config:' + push eax +@@: + DEBUGF 1,' %x',[eax]:2 + inc eax + dec ecx + jnz @b + DEBUGF 1,'\n' + pop eax +end if + cmp [eax+usb_hub_descr.bLength], sizeof.usb_hub_descr + jb .invalid + cmp [eax+usb_hub_descr.bDescriptorType], USB_HUB_DESCRIPTOR + jnz .invalid + movzx ecx, [eax+usb_hub_descr.bNbrPorts] + test ecx, ecx + jz .invalid +; 4. We use sizeof.usb_hub_descr bytes plus DeviceRemovable info; +; size of DeviceRemovable is (NumPorts+1) bits, this gives +; floor(NumPorts/8)+1 bytes. Check that all data are present in the +; descriptor and were successfully read. + mov edx, ecx + shr edx, 3 + add edx, sizeof.usb_hub_descr + 1 + cmp [eax+usb_hub_descr.bLength], dl + jb .invalid + sub [length], 8 + cmp [length], edx + jb .invalid +; 5. Allocate the memory for usb_hub structure. +; Total size of variable-length data is ALIGN_UP(floor(NumPorts/8)+1+MaxPacketSize,4)+8*NumPorts. + add edx, [eax+40] + add edx, sizeof.usb_hub - sizeof.usb_hub_descr + 3 + and edx, not 3 + lea eax, [edx+ecx*8] + push ecx edx + call malloc + pop edx ecx + test eax, eax + jz .nomemory + xchg eax, ebx +; 6. Fill usb_hub structure. + mov [ebx+usb_hub.NumPorts], ecx + add edx, ebx + mov [ebx+usb_hub.ConnectedDevicesPtr], edx + mov eax, [pipe] + mov [ebx+usb_hub.ConfigPipe], eax + mov edx, [eax+usb_pipe.Controller] + mov [ebx+usb_hub.Controller], edx + mov eax, [calldata] + mov [ebx+usb_hub.StatusPipe], eax + push esi edi + mov esi, [buffer] + mov eax, [esi+40] + mov [ebx+usb_hub.MaxPacketSize], eax +; The following commands load bNbrPorts, wHubCharacteristics, bPwrOn2PwrGood. + mov edx, dword [esi+usb_hub_descr.bNbrPorts] + mov dl, 0 +; The following command zeroes AccStatusChange and stores +; HubCharacteristics and PowerOnInterval. + mov dword [ebx+usb_hub.AccStatusChange], edx + xor eax, eax + mov [ebx+usb_hub.Actions], eax +; Copy DeviceRemovable data. + lea edi, [ebx+sizeof.usb_hub] + add esi, sizeof.usb_hub_descr + mov edx, ecx + shr ecx, 3 + inc ecx + rep movsb + mov [ebx+usb_hub.StatusChangePtr], edi +; Zero ConnectedDevices. + mov edi, [ebx+usb_hub.ConnectedDevicesPtr] + mov ecx, edx + rep stosd + mov [ebx+usb_hub.ConnectedTimePtr], edi +; Set ConnectedTime to -1. + dec eax + mov ecx, edx + rep stosd + pop edi esi +; 7. Replace value of 1 returned from usb_hub_init to the real value. +; Note: hubs are part of the core USB code, so this code can work with +; internals of other parts. Another way, the only possible one for external +; drivers, is to use two memory allocations: one (returned from AddDevice and +; fixed after that) for pointer, another for real data. That would work also, +; but wastes one allocation. + mov eax, [pipe] + mov eax, [eax+usb_pipe.DeviceData] + add eax, [eax+usb_device_data.Interfaces] +.scan: + cmp [eax+usb_interface_data.DriverData], 1 + jnz @f + cmp [eax+usb_interface_data.DriverFunc], usb_hub_pseudosrv - USBSRV.usb_func + jz .scan_found +@@: + add eax, sizeof.usb_interface_data + jmp .scan +.scan_found: + mov [eax+usb_interface_data.DriverData], ebx +; 8. Insert the hub structure to the tail of the overall list of all hubs. + mov ecx, usb_hubs_list + mov edx, [ecx+usb_hub.Prev] + mov [ecx+usb_hub.Prev], ebx + mov [edx+usb_hub.Next], ebx + mov [ebx+usb_hub.Prev], edx + mov [ebx+usb_hub.Next], ecx +; 9. Start powering up all ports. + DEBUGF 1,'K : found hub with %d ports\n',[ebx+usb_hub.NumPorts] + lea eax, [ebx+usb_hub.ConfigBuffer] + xor ecx, ecx + mov dword [eax], 23h + \ ; class-specific request to hub port + (USB_SET_FEATURE shl 8) + \ + (PORT_POWER shl 16) + mov edx, [ebx+usb_hub.NumPorts] + mov dword [eax+4], edx + stdcall usb_control_async, [ebx+usb_hub.ConfigPipe], eax, ecx, ecx, usb_hub_port_powered, ebx, ecx +.freebuf: +; 10. Free the buffer for hub descriptor and return. + mov eax, [buffer] + call free + pop ebx ; restore used registers to be stdcall + ret +.nomemory: + dbgstr 'No memory for USB hub data' + jmp .freebuf +.invalid: + dbgstr 'Invalid hub descriptor' + jmp .freebuf +endp + +; This procedure is called when the request to power up some port is completed, +; either successfully or unsuccessfully. +proc usb_hub_port_powered stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. Check whether the operation was successful. +; If not, say something to the debug board and ssstop the initialization. + cmp [status], 0 + jnz .invalid +; 2. Check whether all ports were powered. +; If so, go to 4. Otherwise, proceed to 3. + mov eax, [calldata] + dec dword [eax+usb_hub.ConfigBuffer+4] + jz .done +; 3. Power up the next port and return. + lea edx, [eax+usb_hub.ConfigBuffer] + xor ecx, ecx + stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, ecx, usb_hub_port_powered, eax, ecx +.nothing: + ret +.done: +; 4. All ports were powered. +; The hub requires some delay until power will be stable, the delay value +; is provided in the hub descriptor; we have copied that value to +; usb_hub.PowerOnInterval. Note the time and set the corresponding flag +; for usb_hub_process_deferred. + mov ecx, [timer_ticks] + mov [eax+usb_hub.PoweredOnTime], ecx + or [eax+usb_hub.Actions], HUB_WAIT_POWERED + jmp .nothing +.invalid: + dbgstr 'Error while powering hub ports' + jmp .nothing +endp + +; Requests notification about any changes in hub/ports configuration. +; Called when initial configuration is done and when a previous notification +; has been processed. +proc usb_hub_wait_change + stdcall usb_normal_transfer_async, [eax+usb_hub.StatusPipe], \ + [eax+usb_hub.StatusChangePtr], [eax+usb_hub.MaxPacketSize], usb_hub_changed, eax, 1 + ret +endp + +; This procedure is called when something has changed on the hub. +proc usb_hub_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; DEBUGF 1,'K : [%d] int pipe for hub %x\n',[timer_ticks],[calldata] +; 1. Check whether our request has failed. +; If so, say something to the debug board and stop processing notifications. + xor ecx, ecx + cmp [status], ecx + jnz .failed +; 2. If no data were retrieved, restart waiting. + mov eax, [calldata] + cmp [length], ecx + jz .continue +; 3. If size of data retrieved is less than maximal, pad with zeroes; +; this corresponds to 'state of other ports was not changed' + mov ecx, [eax+usb_hub.NumPorts] + shr ecx, 3 + inc ecx + sub ecx, [length] + jbe .restart + push eax edi + mov edi, [buffer] + add edi, [length] + xor eax, eax + rep stosb + pop edi eax +.restart: +; State of some elements of the hub was changed. +; Find the first element that was changed, +; ask the hub about nature of the change, +; clear the corresponding change, +; reask the hub about status+change (it is possible that another change +; occurs between the first ask and clearing the change; we won't see that +; change, so we need to query the status after clearing the change), +; continue two previous steps until nothing changes, +; process all changes which were registered. +; When all changes for one element will be processed, return to here and look +; for other changed elements. + mov edx, [eax+usb_hub.StatusChangePtr] +; We keep all observed changes in the special var usb_hub.AccStatusChange; +; it will be logical OR of all observed StatusChange's. +; 4. No observed changes yet, zero usb_hub.AccStatusChange. + xor ecx, ecx + mov [eax+usb_hub.AccStatusChange], cl +; 5. Test whether there was a change in the hub itself. +; If so, query hub state. + btr dword [edx], ecx + jnc .no_hub_change +.next_hub_change: +; DEBUGF 1,'K : [%d] querying status of hub %x\n',[timer_ticks],eax + lea edx, [eax+usb_hub.ChangeConfigBuffer] + lea ecx, [eax+usb_hub.StatusData] + mov dword [edx], 0A0h + \ ; class-specific request from hub itself + (USB_GET_STATUS shl 8) + mov dword [edx+4], 4 shl 16 ; get 4 bytes + stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_status, eax, 0 + jmp .nothing +.no_hub_change: +; 6. Find the first port with changed state and clear the corresponding bit +; (so next scan after .restart will not consider this port again). +; If found, go to 8. Otherwise, advance to 7. + inc ecx +.test_port_change: + btr [edx], ecx + jc .found_port_change + inc ecx + cmp ecx, [eax+usb_hub.NumPorts] + jbe .test_port_change +.continue: +; 7. All changes have been processed. Wait for next notification. + call usb_hub_wait_change +.nothing: + ret +.found_port_change: + mov dword [eax+usb_hub.ChangeConfigBuffer+4], ecx +.next_port_change: +; 8. Query port state. Continue work in usb_hub_port_status callback. +; movzx ecx, [eax+usb_hub.ChangeConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] querying status of hub %x port %d\n',[timer_ticks],eax,ecx + lea edx, [eax+usb_hub.ChangeConfigBuffer] + mov dword [edx], 0A3h + \ ; class-specific request from hub port + (USB_GET_STATUS shl 8) + mov byte [edx+6], 4 ; data length = 4 bytes + lea ecx, [eax+usb_hub.StatusData] + stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, ecx, 4, usb_hub_port_status, eax, 0 + jmp .nothing +.failed: + cmp [status], USB_STATUS_CLOSED + jz .nothing + dbgstr 'Querying hub notification failed' + jmp .nothing +endp + +; This procedure is called when the request of hub status is completed, +; either successfully or unsuccessfully. +proc usb_hub_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. Check whether our request has failed. +; If so, say something to the debug board and stop processing notifications. + cmp [status], 0 + jnz .failed +; 2. Accumulate observed changes. + mov eax, [calldata] + mov dl, byte [eax+usb_hub.StatusChange] + or [eax+usb_hub.AccStatusChange], dl +.next_change: +; 3. Find the first change. If found, advance to 4. Otherwise, go to 5. + mov cl, C_HUB_OVER_CURRENT + btr dword [eax+usb_hub.StatusChange], 1 + jc .clear_hub_change + mov cl, C_HUB_LOCAL_POWER + btr dword [eax+usb_hub.StatusChange], 0 + jnc .final +.clear_hub_change: +; 4. Clear the change and continue in usb_hub_change_cleared callback. + lea edx, [eax+usb_hub.ChangeConfigBuffer] + mov dword [edx], 20h + \ ; class-specific request to hub itself + (USB_CLEAR_FEATURE shl 8) + mov [edx+2], cl ; feature selector + and dword [edx+4], 0 + stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_change_cleared, eax, 0 +.nothing: + ret +.final: +; 5. All changes cleared and accumulated, now process them. +; Note: that needs work. + DEBUGF 1,'K : hub status %x\n',[eax+usb_hub.AccStatusChange]:2 + test [eax+usb_hub.AccStatusChange], 1 + jz .no_local_power + test [eax+usb_hub.StatusData], 1 + jz .local_power_lost + dbgstr 'Hub local power is now good' + jmp .no_local_power +.local_power_lost: + dbgstr 'Hub local power is now lost' +.no_local_power: + test [eax+usb_hub.AccStatusChange], 2 + jz .no_overcurrent + test [eax+usb_hub.StatusData], 2 + jz .no_overcurrent + dbgstr 'Hub global overcurrent' +.no_overcurrent: +; 6. Process possible changes for other ports. + jmp usb_hub_changed.restart +.failed: + dbgstr 'Querying hub status failed' + jmp .nothing +endp + +; This procedure is called when the request to clear hub change is completed, +; either successfully or unsuccessfully. +proc usb_hub_change_cleared stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. Check whether our request has failed. +; If so, say something to the debug board and stop processing notifications. + cmp [status], 0 + jnz .failed +; 2. If there is a change which was observed, but not yet cleared, +; go to the code which clears it. + mov eax, [calldata] + cmp [eax+usb_hub.StatusChange], 0 + jnz usb_hub_status.next_change +; 3. Otherwise, go to the code which queries the status. + jmp usb_hub_changed.next_hub_change +.failed: + dbgstr 'Clearing hub change failed' + ret +endp + +; This procedure is called when the request of port status is completed, +; either successfully or unsuccessfully. +proc usb_hub_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. Check whether our request has failed. +; If so, say something to the debug board and stop processing notifications. + cmp [status], 0 + jnz .failed +; 2. Accumulate observed changes. + mov eax, [calldata] +; movzx ecx, [eax+usb_hub.ChangeConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.StatusChange]:4 + mov dl, byte [eax+usb_hub.StatusChange] + or [eax+usb_hub.AccStatusChange], dl +.next_change: +; 3. Find the first change. If found, advance to 4. Otherwise, go to 5. +; Ignore change in reset status; it is cleared by synchronous code +; (usb_hub_process_deferred), so avoid unnecessary interference. +; mov cl, C_PORT_RESET + btr dword [eax+usb_hub.StatusChange], PORT_RESET +; jc .clear_port_change + mov cl, C_PORT_OVER_CURRENT + btr dword [eax+usb_hub.StatusChange], PORT_OVER_CURRENT + jc .clear_port_change + mov cl, C_PORT_SUSPEND + btr dword [eax+usb_hub.StatusChange], PORT_SUSPEND + jc .clear_port_change + mov cl, C_PORT_ENABLE + btr dword [eax+usb_hub.StatusChange], PORT_ENABLE + jc .clear_port_change + mov cl, C_PORT_CONNECTION + btr dword [eax+usb_hub.StatusChange], PORT_CONNECTION + jnc .final +.clear_port_change: +; 4. Clear the change and continue in usb_hub_port_changed callback. + call usb_hub_clear_port_change + jmp .nothing +.final: +; All changes cleared and accumulated, now process them. + movzx ecx, byte [eax+usb_hub.ChangeConfigBuffer+4] + dec ecx + DEBUGF 1,'K : final: hub %x port %d status %x change %x\n',eax,ecx,[eax+usb_hub.StatusData]:4,[eax+usb_hub.AccStatusChange]:2 +; 5. Process connect/disconnect events. +; 5a. Test whether there is such event. + test byte [eax+usb_hub.AccStatusChange], 1 shl PORT_CONNECTION + jz .nodisconnect +; 5b. If there was a connected device, notify the main code about disconnect. + push ebx + mov edx, [eax+usb_hub.ConnectedDevicesPtr] + xor ebx, ebx + xchg ebx, [edx+ecx*4] + test ebx, ebx + jz @f + push eax ecx + call usb_device_disconnected + pop ecx eax +@@: + pop ebx +; 5c. If the disconnect event corresponds to the port which is currently +; resetting, then another request from synchronous code could be in the fly, +; so aborting reset immediately would lead to problems with those requests. +; Thus, just set the corresponding status and let the synchronous code process. + test byte [eax+usb_hub.Actions], (HUB_RESET_SIGNAL or HUB_RESET_RECOVERY) + jz @f + mov edx, [eax+usb_hub.Controller] + cmp [edx+usb_controller.ResettingPort], cl + jnz @f + mov [edx+usb_controller.ResettingStatus], -1 +@@: +; 5d. If the current status is 'connected', store the current time as connect +; time and set the corresponding bit for usb_hub_process_deferred. +; Otherwise, set connect time to -1. +; If current time is -1, pretend that the event occured one tick later and +; store zero. + mov edx, [eax+usb_hub.ConnectedTimePtr] + test byte [eax+usb_hub.StatusData], 1 shl PORT_CONNECTION + jz .disconnected + or [eax+usb_hub.Actions], HUB_WAIT_CONNECT + push eax + call usb_hub_store_connected_time + pop eax + jmp @f +.disconnected: + or dword [edx+ecx*4], -1 +@@: +.nodisconnect: +; 6. Process port disabling. + test [eax+usb_hub.AccStatusChange], 1 shl PORT_ENABLE + jz .nodisable + test byte [eax+usb_hub.StatusData], 1 shl PORT_ENABLE + jnz .nodisable +; Note: that needs work. + dbgstr 'Port disabled' +.nodisable: +; 7. Process port overcurrent. + test [eax+usb_hub.AccStatusChange], 1 shl PORT_OVER_CURRENT + jz .noovercurrent + test byte [eax+usb_hub.StatusData], 1 shl PORT_OVER_CURRENT + jz .noovercurrent +; Note: that needs work. + dbgstr 'Port over-current' +.noovercurrent: +; 8. Process possible changes for other ports. + jmp usb_hub_changed.restart +.failed: + dbgstr 'Querying port status failed' +.nothing: + ret +endp + +; Helper procedure to store current time in ConnectedTime, +; advancing -1 to zero if needed. +proc usb_hub_store_connected_time + mov eax, [timer_ticks] +; transform -1 to 0, leave other values as is + cmp eax, -1 + sbb eax, -1 + mov [edx+ecx*4], eax + ret +endp + +; Helper procedure for several parts of hub code. +; Sends a request to clear the given feature of the port. +; eax -> usb_hub, cl = feature; +; as is should be called from async code, sync code should set +; edx to ConfigBuffer and call usb_hub_clear_port_change.buffer; +; port number (1-based) should be filled in [edx+4] by previous requests. +proc usb_hub_clear_port_change + lea edx, [eax+usb_hub.ChangeConfigBuffer] +.buffer: +; push edx +; movzx edx, byte [edx+4] +; dec edx +; DEBUGF 1,'K : [%d] hub %x port %d clear feature %d\n',[timer_ticks],eax,edx,cl +; pop edx + mov dword [edx], 23h + \ ; class-specific request to hub port + (USB_CLEAR_FEATURE shl 8) + mov byte [edx+2], cl + and dword [edx+4], 0xFF + stdcall usb_control_async, [eax+usb_hub.ConfigPipe], edx, edx, 0, usb_hub_port_changed, eax, 0 + ret +endp + +; This procedure is called when the request to clear port change is completed, +; either successfully or unsuccessfully. +proc usb_hub_port_changed stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. Check whether our request has failed. +; If so, say something to the debug board and stop processing notifications. + cmp [status], 0 + jnz .failed +; 2. If the request was originated by synchronous code, no further processing +; is required. + mov eax, [calldata] + lea edx, [eax+usb_hub.ConfigBuffer] + cmp [buffer], edx + jz .nothing +; 3. If there is a change which was observed, but not yet cleared, +; go to the code which clears it. + cmp [eax+usb_hub.StatusChange], 0 + jnz usb_hub_port_status.next_change +; 4. Otherwise, go to the code which queries the status. + jmp usb_hub_changed.next_port_change +.failed: + dbgstr 'Clearing port change failed' +.nothing: + ret +endp + +; This procedure is called in the USB thread from usb_thread_proc, +; contains synchronous code which should be activated at certain time +; (e.g. reset a recently connected device after debounce interval 100ms). +; Returns the number of ticks when it should be called next time. +proc usb_hub_process_deferred +; 1. Top-of-stack will contain return value; initialize to infinite timeout. + push -1 +; 2. If wait for stable power is active, then +; either reschedule wakeup (if time is not over) +; or start processing notifications. + test byte [esi+usb_hub.Actions], HUB_WAIT_POWERED + jz .no_powered + movzx eax, [esi+usb_hub.PowerOnInterval] +; three following instructions are equivalent to edx = ceil(eax / 5) + 1 +; 1 extra tick is added to make sure that the interval is at least as needed +; (it is possible that PoweredOnTime was set just before timer interrupt, and +; this test goes on just after timer interrupt) + add eax, 9 +; two following instructions are equivalent to edx = floor(eax / 5) +; for any 0 <= eax < 40000000h + mov ecx, 33333334h + mul ecx + mov eax, [timer_ticks] + sub eax, [esi+usb_hub.PoweredOnTime] + sub eax, edx + jge .powered_on + neg eax + pop ecx + push eax + jmp .no_powered +.powered_on: + and [esi+usb_hub.Actions], not HUB_WAIT_POWERED + mov eax, esi + call usb_hub_wait_change +.no_powered: +; 3. If reset is pending, check whether we can start it and start it, if so. + test byte [esi+usb_hub.Actions], HUB_RESET_WAITING + jz .no_wait_reset + mov eax, [esi+usb_hub.Controller] + cmp [eax+usb_controller.ResettingPort], -1 + jnz .no_wait_reset + call usb_hub_initiate_reset +.no_wait_reset: +; 4. If reset signalling is active, wait for end of reset signalling +; and schedule wakeup in 1 tick. + test byte [esi+usb_hub.Actions], HUB_RESET_SIGNAL + jz .no_resetting_port +; It has no sense to query status several times per tick. + mov eax, [timer_ticks] + cmp eax, [esi+usb_hub.ResetTime] + jz @f + mov [esi+usb_hub.ResetTime], eax + movzx ecx, byte [esi+usb_hub.ConfigBuffer+4] + mov eax, usb_hub_resetting_port_status + call usb_hub_query_port_status +@@: + pop eax + push 1 +.no_resetting_port: +; 5. If reset recovery is active and time is not over, reschedule wakeup. + test byte [esi+usb_hub.Actions], HUB_RESET_RECOVERY + jz .no_reset_recovery + mov eax, [timer_ticks] + sub eax, [esi+usb_hub.ResetTime] + sub eax, USB_RESET_RECOVERY_TIME + jge .reset_done + neg eax + cmp [esp], eax + jb @f + mov [esp], eax +@@: + jmp .no_reset_recovery +.reset_done: +; 6. If reset recovery is active and time is over, clear 'reset recovery' flag, +; notify other code about a new device and let it do further steps. +; If that fails, stop reset process for this port and disable that port. + and [esi+usb_hub.Actions], not HUB_RESET_RECOVERY +; Bits 9-10 of port status encode port speed. +; If PORT_LOW_SPEED is set, the device is low-speed. Otherwise, +; PORT_HIGH_SPEED bit distinguishes full-speed and high-speed devices. +; This corresponds to values of USB_SPEED_FS=0, USB_SPEED_LS=1, USB_SPEED_HS=2. + mov eax, dword [esi+usb_hub.ResetStatusData] + shr eax, PORT_LOW_SPEED + and eax, 3 + test al, 1 + jz @f + mov al, 1 +@@: +; movzx ecx, [esi+usb_hub.ConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] hub %x port %d speed %d\n',[timer_ticks],esi,ecx,eax + push esi + mov esi, [esi+usb_hub.Controller] + cmp [esi+usb_controller.ResettingStatus], -1 + jz .disconnected_while_reset + mov edx, [esi+usb_controller.HardwareFunc] + call [edx+usb_hardware_func.NewDevice] + pop esi + test eax, eax + jnz .no_reset_recovery + mov eax, esi + call usb_hub_disable_resetting_port + jmp .no_reset_recovery +.disconnected_while_reset: + pop esi + mov eax, esi + call usb_hub_reset_aborted +.no_reset_recovery: +; 7. Handle recent connection events. +; Note: that should be done after step 6, because step 6 can clear +; HUB_RESET_IN_PROGRESS flag. +; 7a. Test whether there is such an event pending. If no, skip this step. + test byte [esi+usb_hub.Actions], HUB_WAIT_CONNECT + jz .no_wait_connect +; 7b. If we have started reset process for another port in the same hub, +; skip this step: the buffer for config requests can be used for that port. + test byte [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS + jnz .no_wait_connect +; 7c. Clear flag 'there are connection events which should be processed'. +; If there are another connection events, this flag will be set again. + and [esi+usb_hub.Actions], not HUB_WAIT_CONNECT +; 7d. Prepare for loop over all ports. + xor ecx, ecx +.test_wait_connect: +; 7e. For every port test for recent connection event. +; If none, continue the loop for the next port. + mov edx, [esi+usb_hub.ConnectedTimePtr] + mov eax, [edx+ecx*4] + cmp eax, -1 + jz .next_wait_connect + or [esi+usb_hub.Actions], HUB_WAIT_CONNECT +; 7f. Test whether initial delay is over. + sub eax, [timer_ticks] + neg eax + sub eax, USB_CONNECT_DELAY + jge .connect_delay_over +; 7g. The initial delay is not over; +; set the corresponding flag again, reschedule wakeup and continue the loop. + neg eax + cmp [esp], eax + jb @f + mov [esp], eax +@@: + jmp .next_wait_connect +.connect_delay_over: +; The initial delay is over. +; It is possible that there was disconnect event during that delay, probably +; with connect event after that. If so, we should restart the waiting. However, +; on the hardware level connect/disconnect events from hubs are implemented +; using polling with interval selected by the hub, so it is possible that +; we have not yet observed that disconnect event. +; Thus, we query port status+change data before all further processing. +; 7h. Send the request for status+change data. + push ecx +; Hub requests expect 1-based port number, not zero-based we operate with. + inc ecx + mov eax, usb_hub_connect_port_status + call usb_hub_query_port_status + pop ecx +; 3i. If request has been submitted successfully, set the flag +; 'reset in progress, config buffer is owned by reset process' and break +; from the loop. + test eax, eax + jz .next_wait_connect + or [esi+usb_hub.Actions], HUB_RESET_IN_PROGRESS + jmp .no_wait_connect +.next_wait_connect: +; 7j. Continue the loop for next port. + inc ecx + cmp ecx, [esi+usb_hub.NumPorts] + jb .test_wait_connect +.no_wait_connect: +; 8. Pop return value from top-of-stack and return. + pop eax + ret +endp + +; Helper procedure for other code. Called when reset process is aborted. +proc usb_hub_reset_aborted +; Clear 'reset in progress' flag and test for other devices which could be +; waiting for reset. + and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS + push esi + mov esi, [eax+usb_hub.Controller] + call usb_test_pending_port + pop esi + ret +endp + +; Helper procedure for usb_hub_process_deferred. +; Sends a request to query port status. +; esi -> usb_hub, eax = callback, ecx = 1-based port. +proc usb_hub_query_port_status +; dec ecx +; DEBUGF 1,'K : [%d] [main] hub %x port %d query status\n',[timer_ticks],esi,ecx +; inc ecx + add ecx, 4 shl 16 ; data length = 4 + lea edx, [esi+usb_hub.ConfigBuffer] + mov dword [edx], 0A3h + \ ; class-specific request from hub port + (USB_GET_STATUS shl 8) + mov dword [edx+4], ecx + lea ecx, [esi+usb_hub.ResetStatusData] + stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, ecx, 4, eax, esi, 0 + ret +endp + +; This procedure is called when the request to query port status +; initiated by usb_hub_process_deferred for testing connection is completed, +; either successfully or unsuccessfully. +proc usb_hub_connect_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword + push esi ; save used register to be stdcall + mov eax, [calldata] + mov esi, [pipe] +; movzx ecx, [eax+usb_hub.ConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] [connect test] hub %x port %d status %x change %x\n',[timer_ticks],eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4 +; 1. In any case, clear 'reset in progress' flag. +; If everything is ok, it would be set again. + and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS +; 2. If the request has failed, stop reset process. + cmp [status], 0 + jnz .nothing + mov edx, [eax+usb_hub.ConnectedTimePtr] + movzx ecx, byte [eax+usb_hub.ConfigBuffer+4] + dec ecx +; 3. Test whether there was a disconnect event. + test byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION + jz .reset +; 4. There was a disconnect event. +; There is another handler of connect/disconnect events, usb_hub_port_status. +; However, we do not know whether it has already processed this event +; or it will process it sometime later. +; If ConnectedTime is -1, then another handler has already run, +; there was no connection event, so just leave the value as -1. +; Otherwise, there are two possibilities: either another handler has not yet +; run (which is quite likely), or there was a connection event and the other +; handler has run exactly while our request was processed (otherwise our +; request would not been submitted; this is quite unlikely due to timing +; requirements, but not impossible). In this case, set ConnectedTime to the +; current time: in the likely case it prevents usb_hub_process_deferred from immediate +; issuing of another requests (which would be just waste of time); +; in the unlikely case it is still correct (although slightly increases +; the debounce interval). + cmp dword [edx+ecx*4], -1 + jz .nothing + call usb_hub_store_connected_time + jmp .nothing +.reset: +; 5. The device remained connected for the entire debounce interval; +; we can proceed with initialization. +; Clear connected time for this port and notify usb_hub_process_deferred that +; the new port is waiting for reset. + or dword [edx+ecx*4], -1 + or [eax+usb_hub.Actions], HUB_RESET_IN_PROGRESS + HUB_RESET_WAITING +.nothing: + pop esi ; restore used register to be stdcall + ret +endp + +; This procedure is called when the request to query port status +; initiated by usb_hub_process_deferred for testing reset status is completed, +; either successfully or unsuccessfully. +proc usb_hub_resetting_port_status stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; 1. If the request has failed, do nothing. + cmp [status], 0 + jnz .nothing +; 2. If reset signalling is still active, do nothing. + mov eax, [calldata] +; movzx ecx, [eax+usb_hub.ConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : hub %x port %d ResetStatusData = %x change = %x\n',eax,ecx,[eax+usb_hub.ResetStatusData]:4,[eax+usb_hub.ResetStatusChange]:4 + test byte [eax+usb_hub.ResetStatusData], 1 shl PORT_RESET + jnz .nothing +; 3. Store the current time to start reset recovery interval +; and clear 'reset signalling active' flag. + mov edx, [timer_ticks] + mov [eax+usb_hub.ResetTime], edx + and [eax+usb_hub.Actions], not HUB_RESET_SIGNAL +; 4. If the device has not been disconnected, set 'reset recovery active' bit. +; Otherwise, terminate reset process. + test byte [eax+usb_hub.ResetStatusChange], 1 shl PORT_CONNECTION + jnz .disconnected + or [eax+usb_hub.Actions], HUB_RESET_RECOVERY +.common: +; In any case, clear change of resetting status. + lea edx, [eax+usb_hub.ConfigBuffer] + mov cl, C_PORT_RESET + call usb_hub_clear_port_change.buffer +.nothing: + ret +.disconnected: + call usb_hub_reset_aborted + jmp .common +endp + +; Helper procedure for usb_hub_process_deferred. Initiates reset signalling +; on the current port (given by 1-based value [ConfigBuffer+4]). +; esi -> usb_hub, eax -> usb_controller +proc usb_hub_initiate_reset +; 1. Store hub+port data in the controller structure. + movzx ecx, [esi+usb_hub.ConfigBuffer+4] + dec ecx + mov [eax+usb_controller.ResettingPort], cl + mov [eax+usb_controller.ResettingHub], esi +; 2. Store the current time and set 'reset signalling active' flag. + mov eax, [timer_ticks] + mov [esi+usb_hub.ResetTime], eax + and [esi+usb_hub.Actions], not HUB_RESET_WAITING + or [esi+usb_hub.Actions], HUB_RESET_SIGNAL +; 3. Send request to the hub to initiate request signalling. + lea edx, [esi+usb_hub.ConfigBuffer] +; DEBUGF 1,'K : [%d] hub %x port %d initiate reset\n',[timer_ticks],esi,ecx + mov dword [edx], 23h + \ + (USB_SET_FEATURE shl 8) + \ + (PORT_RESET shl 16) + and dword [edx+4], 0xFF + stdcall usb_control_async, [esi+usb_hub.ConfigPipe], edx, 0, 0, usb_hub_reset_started, esi, 0 + test eax, eax + jnz @f + mov eax, esi + call usb_hub_reset_aborted +@@: + ret +endp + +; This procedure is called when the request to start reset signalling initiated +; by usb_hub_initiate_reset is completed, either successfully or unsuccessfully. +proc usb_hub_reset_started stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; If the request is successful, do nothing. +; Otherwise, clear 'reset signalling' flag and abort reset process. + mov eax, [calldata] +; movzx ecx, [eax+usb_hub.ConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] hub %x port %d reset started\n',[timer_ticks],eax,ecx + cmp [status], 0 + jz .nothing + and [eax+usb_hub.Actions], not HUB_RESET_SIGNAL + dbgstr 'Failed to reset hub port' + call usb_hub_reset_aborted +.nothing: + ret +endp + +; This procedure is called by the protocol layer if something has failed during +; initial stages of the configuration process, so the device should be disabled +; at hub level. +proc usb_hub_disable_resetting_port + and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS +; movzx ecx, [eax+usb_hub.ConfigBuffer+4] +; dec ecx +; DEBUGF 1,'K : [%d] hub %x port %d disable\n',[timer_ticks],eax,ecx + lea edx, [eax+usb_hub.ConfigBuffer] + mov cl, PORT_ENABLE + jmp usb_hub_clear_port_change.buffer +endp + +; This procedure is called when the hub is disconnected. +proc usb_hub_disconnect +virtual at esp + dd ? ; return address +.hubdata dd ? +end virtual +; 1. If the hub is disconnected during initial configuration, +; 1 is stored as hub data and there is nothing to do. + mov eax, [.hubdata] + cmp eax, 1 + jz .nothing +; 2. Remove the hub from the overall list. + mov ecx, [eax+usb_hub.Next] + mov edx, [eax+usb_hub.Prev] + mov [ecx+usb_hub.Prev], edx + mov [edx+usb_hub.Next], ecx +; 3. If some child is in reset process, abort reset. + push esi + mov esi, [eax+usb_hub.Controller] + cmp [esi+usb_controller.ResettingHub], eax + jnz @f + cmp [esi+usb_controller.ResettingPort], -1 + jz @f + push eax + call usb_test_pending_port + pop eax +@@: + pop esi +; 4. Loop over all children and notify other code that they were disconnected. + push ebx + xor ecx, ecx +.disconnect_children: + mov ebx, [eax+usb_hub.ConnectedDevicesPtr] + mov ebx, [ebx+ecx*4] + test ebx, ebx + jz @f + push eax ecx + call usb_device_disconnected + pop ecx eax +@@: + inc ecx + cmp ecx, [eax+usb_hub.NumPorts] + jb .disconnect_children +; 4. Free memory allocated for the hub data. + call free + pop ebx +.nothing: + retn 4 +endp + +; Helper function for USB2 scheduler. +; in: eax -> usb_hub +; out: ecx = TT think time for the hub in FS-bytes +proc usb_get_tt_think_time + movzx ecx, [eax+usb_hub.HubCharacteristics] + shr ecx, 5 + and ecx, 3 + inc ecx + ret +endp diff --git a/kernel/branches/kolibri-lldw/bus/usb/init.inc b/kernel/branches/kolibri-lldw/bus/usb/init.inc new file mode 100644 index 000000000..fa438437a --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/init.inc @@ -0,0 +1,268 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Initialization of the USB subsystem. +; Provides usb_init procedure, includes all needed files. + +; General notes: +; * There is one entry point for external kernel code: usb_init is called +; from initialization code and initializes USB subsystem. +; * There are several entry points for API; see the docs for description. +; * There are several functions which are called from controller-specific +; parts of USB subsystem. The most important is usb_new_device, +; which is called when a new device has been connected (over some time), +; has been reset and is ready to start configuring. +; * IRQ handlers are very restricted. They can not take any locks, +; since otherwise a deadlock is possible: imagine that a code has taken the +; lock and was interrupted by IRQ handler. Now IRQ handler would wait for +; releasing the lock, and a lock owner would wait for exiting IRQ handler +; to get the control. +; * Thus, there is the special USB thread which processes almost all activity. +; IRQ handlers do the minimal processing and wake this thread. +; * Also the USB thread wakes occasionally to process tasks which can be +; predicted without interrupts. These include e.g. a periodic roothub +; scanning in UHCI and initializing in USB_CONNECT_DELAY ticks +; after connecting a new device. +; * The main procedure of USB thread, usb_thread_proc, does all its work +; by querying usb_hardware_func.ProcessDeferred for every controller +; and usb_hub_process_deferred for every hub. +; ProcessDeferred does controller-specific actions and calculates the time +; when it should be invoked again, possibly infinite. +; usb_thread_proc selects the minimum from all times returned by +; ProcessDeferred and sleeps until this moment is reached or the thread +; is awakened by IRQ handler. + +iglobal +uhci_service_name: + db 'UHCI',0 +ohci_service_name: + db 'OHCI',0 +ehci_service_name: + db 'EHCI',0 +endg + +; Initializes the USB subsystem. +proc usb_init +; 1. Initialize all locks. + mov ecx, usb_controllers_list_mutex + call mutex_init +; 2. Kick off BIOS from all USB controllers, calling the corresponding function +; *hci_kickoff_bios. Also count USB controllers for the next step. +; Note: USB1 companion(s) must go before the corresponding EHCI controller, +; otherwise BIOS could see a device moving from EHCI to a companion; +; first, this always wastes time; +; second, some BIOSes are buggy, do not expect that move and try to refer to +; previously-assigned controller instead of actual; sometimes that leads to +; hangoff. +; Thus, process controllers in PCI order. + mov esi, pcidev_list + push 0 +.kickoff: + mov esi, [esi+PCIDEV.fd] + cmp esi, pcidev_list + jz .done_kickoff + cmp word [esi+PCIDEV.class+1], 0x0C03 + jnz .kickoff + mov ebx, uhci_service_name + cmp byte [esi+PCIDEV.class], 0x00 + jz .do_kickoff + mov ebx, ohci_service_name + cmp byte [esi+PCIDEV.class], 0x10 + jz .do_kickoff + mov ebx, ehci_service_name + cmp byte [esi+PCIDEV.class], 0x20 + jnz .kickoff +.do_kickoff: + inc dword [esp] + push ebx esi + stdcall get_service, ebx + pop esi ebx + test eax, eax + jz .driver_fail + mov edx, [eax+USBSRV.usb_func] + cmp [edx+usb_hardware_func.Version], USBHC_VERSION + jnz .driver_invalid + mov [esi+PCIDEV.owner], eax + call [edx+usb_hardware_func.BeforeInit] + jmp .kickoff +.driver_fail: + DEBUGF 1,'K : failed to load driver %s\n',ebx + jmp .kickoff +.driver_invalid: + DEBUGF 1,'K : driver %s has wrong version\n',ebx + jmp .kickoff +.done_kickoff: + pop eax +; 3. If no controllers were found, exit. +; Otherwise, run the USB thread. + test eax, eax + jz .nothing + call create_usb_thread + jz .nothing +; 4. Initialize all USB controllers, calling usb_init_controller for each. +; Note: USB1 companion(s) should go before the corresponding EHCI controller, +; although this is not strictly necessary (this way, a companion would not try +; to initialize high-speed device only to see a disconnect when EHCI takes +; control). +; Thus, process all EHCI controllers in the first loop, all USB1 controllers +; in the second loop. (One loop in reversed PCI order could also be used, +; but seems less natural.) +; 4a. Loop over all PCI devices, call usb_init_controller +; for all EHCI controllers. + mov eax, pcidev_list +.scan_ehci: + mov eax, [eax+PCIDEV.fd] + cmp eax, pcidev_list + jz .done_ehci + cmp [eax+PCIDEV.class], 0x0C0320 + jnz .scan_ehci + call usb_init_controller + jmp .scan_ehci +.done_ehci: +; 4b. Loop over all PCI devices, call usb_init_controller +; for all UHCI and OHCI controllers. + mov eax, pcidev_list +.scan_usb1: + mov eax, [eax+PCIDEV.fd] + cmp eax, pcidev_list + jz .done_usb1 + cmp [eax+PCIDEV.class], 0x0C0300 + jz @f + cmp [eax+PCIDEV.class], 0x0C0310 + jnz .scan_usb1 +@@: + call usb_init_controller + jmp .scan_usb1 +.done_usb1: +.nothing: + ret +endp + +uglobal +align 4 +usb_event dd ? +endg + +; Helper function for usb_init. Creates and initializes the USB thread. +proc create_usb_thread +; 1. Create the thread. + push edi + movi ebx, 1 + mov ecx, usb_thread_proc + xor edx, edx + call new_sys_threads + pop edi +; If failed, say something to the debug board and return with ZF set. + test eax, eax + jns @f + DEBUGF 1,'K : cannot create kernel thread for USB, error %d\n',eax +.clear: + xor eax, eax + jmp .nothing +@@: +; 2. Wait while the USB thread initializes itself. +@@: + call change_task + cmp [usb_event], 0 + jz @b +; 3. If initialization failed, the USB thread sets [usb_event] to -1. +; Return with ZF set or cleared corresponding to the result. + cmp [usb_event], -1 + jz .clear +.nothing: + ret +endp + +; Helper function for IRQ handlers. Wakes the USB thread if ebx is nonzero. +proc usb_wakeup_if_needed + test ebx, ebx + jz usb_wakeup.nothing +usb_wakeup: + xor edx, edx + mov eax, [usb_event] + mov ebx, [eax+EVENT.id] + xor esi, esi + call raise_event +.nothing: + ret +endp + +; Main loop of the USB thread. +proc usb_thread_proc +; 1. Initialize: create event to allow wakeup by interrupt handlers. + xor esi, esi + mov ecx, MANUAL_DESTROY + call create_event + test eax, eax + jnz @f +; If failed, set [usb_event] to -1 and terminate myself. + dbgstr 'cannot create event for USB thread' + or [usb_event], -1 + jmp sys_end +@@: + mov [usb_event], eax + push -1 ; initial timeout: infinite +usb_thread_wait: +; 2. Main loop: wait for either wakeup event or timeout. + pop ecx ; get timeout + mov eax, [usb_event] + mov ebx, [eax+EVENT.id] + call wait_event_timeout + push -1 ; default timeout: infinite +; 3. Main loop: call worker functions of all controllers; +; if some function schedules wakeup in timeout less than the current value, +; replace that value with the returned timeout. + mov esi, usb_controllers_list +@@: + mov esi, [esi+usb_controller.Next] + cmp esi, usb_controllers_list + jz .controllers_done + mov eax, [esi+usb_controller.HardwareFunc] + call [eax+usb_hardware_func.ProcessDeferred] + cmp [esp], eax + jb @b + mov [esp], eax + jmp @b +.controllers_done: +; 4. Main loop: call hub worker function for all hubs, +; similarly calculating minimum of all returned timeouts. +; When done, continue to 2. + mov esi, usb_hubs_list +@@: + mov esi, [esi+usb_hub.Next] + cmp esi, usb_hubs_list + jz usb_thread_wait + call usb_hub_process_deferred + cmp [esp], eax + jb @b + mov [esp], eax + jmp @b +endp + +iglobal +align 4 +usb_controllers_list: + dd usb_controllers_list + dd usb_controllers_list +usb_hubs_list: + dd usb_hubs_list + dd usb_hubs_list +endg +uglobal +align 4 +usb_controllers_list_mutex MUTEX +endg + +include "memory.inc" +include "common.inc" +include "hccommon.inc" +include "pipe.inc" +include "protocol.inc" +include "hub.inc" diff --git a/kernel/branches/kolibri-lldw/bus/usb/memory.inc b/kernel/branches/kolibri-lldw/bus/usb/memory.inc new file mode 100644 index 000000000..1a68336ac --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/memory.inc @@ -0,0 +1,43 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Memory management for USB structures. +; Protocol layer uses the common kernel heap malloc/free. +; Hardware layer has special requirements: +; * memory blocks should be properly aligned +; * memory blocks should not cross page boundary +; Hardware layer allocates fixed-size blocks. +; Thus, hardware layer uses the system slab allocator. + +; Helper procedure: translate physical address in ecx +; of some transfer descriptor to linear address. +; in: eax = address of first page +proc usb_td_to_virt +; Traverse all pages used for transfer descriptors, looking for the one +; with physical address as in ecx. +@@: + test eax, eax + jz .zero + push eax + call get_pg_addr + sub eax, ecx + jz .found + cmp eax, -0x1000 + ja .found + pop eax + mov eax, [eax+0x1000-4] + jmp @b +.found: +; When found, combine page address from eax with page offset from ecx. + pop eax + and ecx, 0xFFF + add eax, ecx +.zero: + ret +endp diff --git a/kernel/branches/kolibri-lldw/bus/usb/pipe.inc b/kernel/branches/kolibri-lldw/bus/usb/pipe.inc new file mode 100644 index 000000000..312594f4b --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/pipe.inc @@ -0,0 +1,860 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Functions for USB pipe manipulation: opening/closing, sending data etc. +; +USB_STDCALL_VERIFY = 1 +macro stdcall_verify [arg] +{ +common +if USB_STDCALL_VERIFY + pushad + stdcall arg + call verify_regs + popad +else + stdcall arg +end if +} +if USB_STDCALL_VERIFY +STDCALL_VERIFY_EXTRA = 20h +else +STDCALL_VERIFY_EXTRA = 0 +end if + +; Initialization of usb_static_ep structure, +; called from controller-specific initialization; edi -> usb_static_ep +proc usb_init_static_endpoint + mov [edi+usb_static_ep.NextVirt], edi + mov [edi+usb_static_ep.PrevVirt], edi + ret +endp + +; Part of API for drivers, see documentation for USBOpenPipe. +proc usb_open_pipe stdcall uses ebx esi edi,\ + config_pipe:dword, endpoint:dword, maxpacket:dword, type:dword, interval:dword +locals +tt_vars rd 24 ; should be enough for ehci_select_tt_interrupt_list +targetsmask dd ? ; S-Mask for USB2 +bandwidth dd ? +target dd ? +endl +; 1. Verify type of pipe: it must be one of *_PIPE constants. +; Isochronous pipes are not supported yet. + mov eax, [type] + cmp eax, INTERRUPT_PIPE + ja .badtype + cmp al, ISOCHRONOUS_PIPE + jnz .goodtype +.badtype: + dbgstr 'unsupported type of USB pipe' + jmp .return0 +.goodtype: +; 2. Allocate memory for pipe and transfer queue. +; Empty transfer queue consists of one inactive TD. + mov ebx, [config_pipe] + mov esi, [ebx+usb_pipe.Controller] + mov edx, [esi+usb_controller.HardwareFunc] + call [edx+usb_hardware_func.AllocPipe] + test eax, eax + jz .nothing + mov edi, eax + mov edx, [esi+usb_controller.HardwareFunc] + call [edx+usb_hardware_func.AllocTD] + test eax, eax + jz .free_and_return0 +; 3. Initialize transfer queue: pointer to transfer descriptor, +; pointers in transfer descriptor, queue lock. + mov [edi+usb_pipe.LastTD], eax + mov [eax+usb_gtd.NextVirt], eax + mov [eax+usb_gtd.PrevVirt], eax + mov [eax+usb_gtd.Pipe], edi + lea ecx, [edi+usb_pipe.Lock] + call mutex_init +; 4. Initialize software part of pipe structure, except device-related fields. + mov al, byte [type] + mov [edi+usb_pipe.Type], al + xor eax, eax + mov [edi+usb_pipe.Flags], al + mov [edi+usb_pipe.DeviceData], eax + mov [edi+usb_pipe.Controller], esi + or [edi+usb_pipe.NextWait], -1 +; 5. Initialize device-related fields: +; for zero endpoint, set .NextSibling = .PrevSibling = this; +; for other endpoins, copy device data, take the lock guarding pipe list +; for the device and verify that disconnect processing has not yet started +; for the device. (Since disconnect processing also takes that lock, +; either it has completed or it will not start until we release the lock.) +; Note: usb_device_disconnected should not see the new pipe until +; initialization is complete, so that lock will be held during next steps +; (disconnect processing should either not see it at all, or see fully +; initialized pipe). + cmp [endpoint], eax + jz .zero_endpoint + mov ecx, [ebx+usb_pipe.DeviceData] + mov [edi+usb_pipe.DeviceData], ecx + call mutex_lock + test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + jz .common +.fail: +; If disconnect processing has completed, unlock the mutex, free memory +; allocated in step 2 and return zero. + call mutex_unlock + mov edx, [esi+usb_controller.HardwareFunc] + stdcall [edx+usb_hardware_func.FreeTD], [edi+usb_pipe.LastTD] +.free_and_return0: + mov edx, [esi+usb_controller.HardwareFunc] + stdcall [edx+usb_hardware_func.FreePipe], edi +.return0: + xor eax, eax + jmp .nothing +.zero_endpoint: + mov [edi+usb_pipe.NextSibling], edi + mov [edi+usb_pipe.PrevSibling], edi +.common: +; 6. Initialize hardware part of pipe structure. +; 6a. Acquire the corresponding mutex. + lea ecx, [esi+usb_controller.ControlLock] + cmp [type], BULK_PIPE + jb @f ; control pipe + lea ecx, [esi+usb_controller.BulkLock] + jz @f ; bulk pipe + lea ecx, [esi+usb_controller.PeriodicLock] +@@: + call mutex_lock +; 6b. Let the controller-specific code do its job. + push ecx + mov edx, [esi+usb_controller.HardwareFunc] + mov eax, [edi+usb_pipe.LastTD] + mov ecx, [config_pipe] + call [edx+usb_hardware_func.InitPipe] + pop ecx +; 6c. Release the mutex. + push eax + call mutex_unlock + pop eax +; 6d. If controller-specific code indicates failure, +; release the lock taken in step 5, free memory allocated in step 2 +; and return zero. + test eax, eax + jz .fail +; 7. The pipe is initialized. If this is not the first pipe for the device, +; insert it to the tail of pipe list for the device, +; increment number of pipes, +; release the lock taken at step 5. + mov ecx, [edi+usb_pipe.DeviceData] + test ecx, ecx + jz @f + mov eax, [ebx+usb_pipe.PrevSibling] + mov [edi+usb_pipe.NextSibling], ebx + mov [edi+usb_pipe.PrevSibling], eax + mov [ebx+usb_pipe.PrevSibling], edi + mov [eax+usb_pipe.NextSibling], edi + inc [ecx+usb_device_data.NumPipes] + call mutex_unlock +@@: +; 8. Return pointer to usb_pipe. + mov eax, edi +.nothing: + ret +endp + +; This procedure is called several times during initial device configuration, +; when usb_device_data structure is reallocated. +; It (re)initializes all pointers in usb_device_data. +; ebx -> usb_pipe +proc usb_reinit_pipe_list + push eax +; 1. (Re)initialize the lock guarding pipe list. + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_init +; 2. Initialize list of opened pipes: two entries, the head and ebx. + add ecx, usb_device_data.OpenedPipeList - usb_pipe.NextSibling + mov [ecx+usb_pipe.NextSibling], ebx + mov [ecx+usb_pipe.PrevSibling], ebx + mov [ebx+usb_pipe.NextSibling], ecx + mov [ebx+usb_pipe.PrevSibling], ecx +; 3. Initialize list of closed pipes: empty list, only the head is present. + add ecx, usb_device_data.ClosedPipeList - usb_device_data.OpenedPipeList + mov [ecx+usb_pipe.NextSibling], ecx + mov [ecx+usb_pipe.PrevSibling], ecx + pop eax + ret +endp + +; Part of API for drivers, see documentation for USBClosePipe. +proc usb_close_pipe + push ebx esi ; save used registers to be stdcall +virtual at esp + rd 2 ; saved registers + dd ? ; return address +.pipe dd ? +end virtual +; 1. Lock the pipe list for the device. + mov ebx, [.pipe] + mov esi, [ebx+usb_pipe.Controller] + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_lock +; 2. Set the flag "the driver has abandoned this pipe, free it at any time". + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock + or [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE + call mutex_unlock +; 3. Call the worker function. + call usb_close_pipe_nolock +; 4. Unlock the pipe list for the device. + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_unlock +; 5. Wakeup the USB thread so that it can proceed with releasing that pipe. + push edi + call usb_wakeup + pop edi +; 6. Return. + pop esi ebx ; restore used registers to be stdcall + retn 4 +endp + +; Worker function for pipe closing. Called by usb_close_pipe API and +; from disconnect processing. +; The lock guarding pipe list for the device should be held by the caller. +; ebx -> usb_pipe, esi -> usb_controller +proc usb_close_pipe_nolock +; 1. Set the flag "pipe is closed, ignore new transfers". +; If it was already set, do nothing. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock + bts dword [ebx+usb_pipe.Flags], USB_FLAG_CLOSED_BIT + jc .closed + call mutex_unlock +; 2. Remove the pipe from the list of opened pipes. + mov eax, [ebx+usb_pipe.NextSibling] + mov edx, [ebx+usb_pipe.PrevSibling] + mov [eax+usb_pipe.PrevSibling], edx + mov [edx+usb_pipe.NextSibling], eax +; 3. Unlink the pipe from hardware structures. +; 3a. Acquire the corresponding lock. + lea edx, [esi+usb_controller.WaitPipeListAsync] + lea ecx, [esi+usb_controller.ControlLock] + cmp [ebx+usb_pipe.Type], BULK_PIPE + jb @f ; control pipe + lea ecx, [esi+usb_controller.BulkLock] + jz @f ; bulk pipe + add edx, usb_controller.WaitPipeListPeriodic - usb_controller.WaitPipeListAsync + lea ecx, [esi+usb_controller.PeriodicLock] +@@: + push edx + call mutex_lock + push ecx +; 3b. Let the controller-specific code do its job. + test [ebx+usb_pipe.Flags], USB_FLAG_DISABLED + jnz @f + mov eax, [esi+usb_controller.HardwareFunc] + call [eax+usb_hardware_func.DisablePipe] +@@: + mov eax, [esi+usb_controller.HardwareFunc] + call [eax+usb_hardware_func.UnlinkPipe] + mov edx, [ebx+usb_pipe.NextVirt] + mov eax, [ebx+usb_pipe.PrevVirt] + mov [edx+usb_pipe.PrevVirt], eax + mov [eax+usb_pipe.NextVirt], edx +; 3c. Release the corresponding lock. + pop ecx + call mutex_unlock +; 4. Put the pipe into wait queue. + pop edx + cmp [ebx+usb_pipe.NextWait], -1 + jz .insert_new + or [ebx+usb_pipe.Flags], USB_FLAG_EXTRA_WAIT + jmp .inserted +.insert_new: + mov eax, [edx] + mov [ebx+usb_pipe.NextWait], eax + mov [edx], ebx +.inserted: +; 5. Return. + ret +.closed: + call mutex_unlock + xor eax, eax + ret +endp + +; This procedure is called when all transfers are aborted +; either due to call to usb_abort_pipe or due to pipe closing. +; It notifies all callbacks and frees all transfer descriptors. +; ebx -> usb_pipe, esi -> usb_controller, edi -> usb_hardware_func +; three stack parameters: status code for callback functions +; and descriptors where to start and stop. +proc usb_pipe_aborted +virtual at esp + dd ? ; return address +.status dd ? ; USB_STATUS_CLOSED or USB_STATUS_CANCELLED +.first_td dd ? +.last_td dd ? +end virtual +; Loop over all transfers, calling the driver with the given status +; and freeing all descriptors except the last one. +.loop: + mov edx, [.first_td] + cmp edx, [.last_td] + jz .done + mov ecx, [edx+usb_gtd.Callback] + test ecx, ecx + jz .no_callback + stdcall_verify ecx, ebx, [.status+12+STDCALL_VERIFY_EXTRA], \ + [edx+usb_gtd.Buffer], 0, [edx+usb_gtd.UserData] + mov edx, [.first_td] +.no_callback: + mov eax, [edx+usb_gtd.NextVirt] + mov [.first_td], eax + stdcall [edi+usb_hardware_func.FreeTD], edx + jmp .loop +.done: + ret 12 +endp + +; This procedure is called when a pipe with USB_FLAG_CLOSED is removed from the +; corresponding wait list. It means that the hardware has fully forgot about it. +; ebx -> usb_pipe, esi -> usb_controller +proc usb_pipe_closed + push edi + mov edi, [esi+usb_controller.HardwareFunc] +; 1. Notify all registered callbacks with status USB_STATUS_CLOSED, if any, +; and free all transfer descriptors, including the last one. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock + mov edx, [ebx+usb_pipe.LastTD] + test edx, edx + jz .no_transfer + mov eax, [edx+usb_gtd.NextVirt] + push edx + push eax + call mutex_unlock + push USB_STATUS_CLOSED + call usb_pipe_aborted +; It is safe to free LastTD here: +; usb_*_transfer_async do not enqueue new transfers if USB_FLAG_CLOSED is set. + stdcall [edi+usb_hardware_func.FreeTD], [ebx+usb_pipe.LastTD] + jmp @f +.no_transfer: + call mutex_unlock +@@: +; 2. Decrement number of pipes for the device. +; If this pipe is the last pipe, go to 5. + mov ecx, [ebx+usb_pipe.DeviceData] + call mutex_lock + dec [ecx+usb_device_data.NumPipes] + jz .last_pipe + call mutex_unlock +; 3. If the flag "the driver has abandoned this pipe" is set, +; free memory and return. + test [ebx+usb_pipe.Flags], USB_FLAG_CAN_FREE + jz .nofree + stdcall [edi+usb_hardware_func.FreePipe], ebx + pop edi + ret +; 4. Otherwise, add it to the list of closed pipes and return. +.nofree: + add ecx, usb_device_data.ClosedPipeList - usb_pipe.NextSibling + mov edx, [ecx+usb_pipe.PrevSibling] + mov [ebx+usb_pipe.NextSibling], ecx + mov [ebx+usb_pipe.PrevSibling], edx + mov [ecx+usb_pipe.PrevSibling], ebx + mov [edx+usb_pipe.NextSibling], ebx + pop edi + ret +.last_pipe: +; That was the last pipe for the device. +; 5. Notify device driver(s) about disconnect. + call mutex_unlock + mov eax, [ecx+usb_device_data.NumInterfaces] + test eax, eax + jz .notify_done + add ecx, [ecx+usb_device_data.Interfaces] +.notify_loop: + mov edx, [ecx+usb_interface_data.DriverFunc] + test edx, edx + jz @f + mov edx, [edx+USBSRV.usb_func] + cmp [edx+USBFUNC.strucsize], USBFUNC.device_disconnect + 4 + jb @f + mov edx, [edx+USBFUNC.device_disconnect] + test edx, edx + jz @f + push eax ecx + stdcall_verify edx, [ecx+usb_interface_data.DriverData] + pop ecx eax +@@: + add ecx, sizeof.usb_interface_data + dec eax + jnz .notify_loop +.notify_done: +; 6. Kill the timer, if active. +; (Usually not; possible if device is disconnected +; while processing SET_ADDRESS request). + mov eax, [ebx+usb_pipe.DeviceData] + cmp [eax+usb_device_data.Timer], 0 + jz @f + stdcall cancel_timer_hs, [eax+usb_device_data.Timer] + mov [eax+usb_device_data.Timer], 0 +@@: +; 7. Bus address, if assigned, can now be reused. + call [edi+usb_hardware_func.GetDeviceAddress] + test eax, eax + jz @f + bts [esi+usb_controller.ExistingAddresses], eax +@@: + dbgstr 'USB device disconnected' +; 8. All drivers have returned from disconnect callback, +; so all drivers should not use any device-related pipes. +; Free the remaining pipes. + mov eax, [ebx+usb_pipe.DeviceData] + add eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling + push eax + mov eax, [eax+usb_pipe.NextSibling] +.free_loop: + cmp eax, [esp] + jz .free_done + push [eax+usb_pipe.NextSibling] + stdcall [edi+usb_hardware_func.FreePipe], eax + pop eax + jmp .free_loop +.free_done: + stdcall [edi+usb_hardware_func.FreePipe], ebx + pop eax +; 9. Free the usb_device_data structure. + sub eax, usb_device_data.ClosedPipeList - usb_pipe.NextSibling + call free +; 10. Return. +.nothing: + pop edi + ret +endp + +; This procedure is called when a pipe with USB_FLAG_DISABLED is removed from the +; corresponding wait list. It means that the hardware has fully forgot about it. +; ebx -> usb_pipe, esi -> usb_controller +proc usb_pipe_disabled + push edi + mov edi, [esi+usb_controller.HardwareFunc] +; 1. Acquire pipe lock. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock +; 2. Clear USB_FLAG_DISABLED in pipe state. + and [ebx+usb_pipe.Flags], not USB_FLAG_DISABLED +; 3. Sanity check: ignore uninitialized pipes. + cmp [ebx+usb_pipe.LastTD], 0 + jz .no_transfer +; 4. Acquire the first and last to-be-cancelled transfer descriptor, +; save them in stack for the step 6, +; ask the controller driver to enable the pipe for hardware, +; removing transfers between first and last to-be-cancelled descriptors. + lea ecx, [esi+usb_controller.ControlLock] + cmp [ebx+usb_pipe.Type], BULK_PIPE + jb @f ; control pipe + lea ecx, [esi+usb_controller.BulkLock] + jz @f ; bulk pipe + lea ecx, [esi+usb_controller.PeriodicLock] +@@: + call mutex_lock + mov eax, [ebx+usb_pipe.BaseList] + mov edx, [eax+usb_pipe.NextVirt] + mov [ebx+usb_pipe.NextVirt], edx + mov [ebx+usb_pipe.PrevVirt], eax + mov [edx+usb_pipe.PrevVirt], ebx + mov [eax+usb_pipe.NextVirt], ebx + mov eax, [ebx+usb_pipe.LastTD] + mov edx, [eax+usb_gtd.NextVirt] + mov [eax+usb_gtd.NextVirt], eax + mov [eax+usb_gtd.PrevVirt], eax + push eax + push edx + push ecx + call [edi+usb_hardware_func.EnablePipe] + pop ecx + call mutex_unlock +; 5. Release pipe lock acquired at step 1. +; Callbacks called at step 6 can insert new transfers, +; so we cannot call usb_pipe_aborted while holding pipe lock. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_unlock +; 6. Notify all registered callbacks with status USB_STATUS_CANCELLED, if any. +; Two arguments describing transfers range were pushed at step 4. + push USB_STATUS_CANCELLED + call usb_pipe_aborted + pop edi + ret +.no_transfer: + call mutex_unlock + pop edi + ret +endp + +; Part of API for drivers, see documentation for USBNormalTransferAsync. +proc usb_normal_transfer_async stdcall uses ebx edi,\ + pipe:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword +; 1. Sanity check: callback must be nonzero. +; (It is important for other parts of code.) + xor eax, eax + cmp [callback], eax + jz .nothing +; 2. Lock the transfer queue. + mov ebx, [pipe] + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock +; 3. If the pipe has already been closed (presumably due to device disconnect), +; release the lock taken in step 2 and return zero. + xor eax, eax + test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + jnz .unlock +; 4. Allocate and initialize TDs for the transfer. + mov edx, [ebx+usb_pipe.Controller] + mov edi, [edx+usb_controller.HardwareFunc] + stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], [ebx+usb_pipe.LastTD], 0 +; If failed, release the lock taken in step 2 and return zero. + test eax, eax + jz .unlock +; 5. Store callback and its parameters in the last descriptor for this transfer. + mov ecx, [eax+usb_gtd.PrevVirt] + mov edx, [callback] + mov [ecx+usb_gtd.Callback], edx + mov edx, [calldata] + mov [ecx+usb_gtd.UserData], edx + mov edx, [buffer] + mov [ecx+usb_gtd.Buffer], edx +; 6. Advance LastTD pointer and activate transfer. + push [ebx+usb_pipe.LastTD] + mov [ebx+usb_pipe.LastTD], eax + call [edi+usb_hardware_func.InsertTransfer] + pop eax +; 7. Release the lock taken in step 2 and +; return pointer to the first descriptor for the new transfer. +.unlock: + push eax + lea ecx, [ebx+usb_pipe.Lock] + call mutex_unlock + pop eax +.nothing: + ret +endp + +; Part of API for drivers, see documentation for USBControlTransferAsync. +proc usb_control_async stdcall uses ebx edi,\ + pipe:dword, config:dword, buffer:dword, size:dword, callback:dword, calldata:dword, flags:dword +locals +last_td dd ? +endl +; 1. Sanity check: callback must be nonzero. +; (It is important for other parts of code.) + cmp [callback], 0 + jz .return0 +; 2. Lock the transfer queue. + mov ebx, [pipe] + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock +; 3. If the pipe has already been closed (presumably due to device disconnect), +; release the lock taken in step 2 and return zero. + test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + jnz .unlock_return0 +; A control transfer contains two or three stages: +; Setup stage, optional Data stage, Status stage. +; 4. Allocate and initialize TDs for the Setup stage. +; Payload is 8 bytes from [config]. + mov edx, [ebx+usb_pipe.Controller] + mov edi, [edx+usb_controller.HardwareFunc] + stdcall [edi+usb_hardware_func.AllocTransfer], [config], 8, 0, [ebx+usb_pipe.LastTD], (2 shl 2) + 0 + ; short transfer is an error, direction is DATA0, token is SETUP + mov [last_td], eax + test eax, eax + jz .fail +; 5. Allocate and initialize TDs for the Data stage, if [size] is nonzero. +; Payload is [size] bytes from [buffer]. + mov edx, [config] + mov ecx, (3 shl 2) + 1 ; DATA1, token is OUT + cmp byte [edx], 0 + jns @f + cmp [size], 0 + jz @f + inc ecx ; token is IN +@@: + cmp [size], 0 + jz .nodata + push ecx + stdcall [edi+usb_hardware_func.AllocTransfer], [buffer], [size], [flags], eax, ecx + pop ecx + test eax, eax + jz .fail + mov [last_td], eax +.nodata: +; 6. Allocate and initialize TDs for the Status stage. +; No payload. + xor ecx, 3 ; IN becomes OUT, OUT becomes IN + stdcall [edi+usb_hardware_func.AllocTransfer], 0, 0, 0, eax, ecx + test eax, eax + jz .fail +; 7. Store callback and its parameters in the last descriptor for this transfer. + mov ecx, [eax+usb_gtd.PrevVirt] + mov edx, [callback] + mov [ecx+usb_gtd.Callback], edx + mov edx, [calldata] + mov [ecx+usb_gtd.UserData], edx + mov edx, [buffer] + mov [ecx+usb_gtd.Buffer], edx +; 8. Advance LastTD pointer and activate transfer. + push [ebx+usb_pipe.LastTD] + mov [ebx+usb_pipe.LastTD], eax + call [edi+usb_hardware_func.InsertTransfer] +; 9. Release the lock taken in step 2 and +; return pointer to the first descriptor for the new transfer. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_unlock + pop eax + ret +.fail: + mov eax, [last_td] + test eax, eax + jz .unlock_return0 + stdcall usb_undo_tds, [ebx+usb_pipe.LastTD] +.unlock_return0: + lea ecx, [ebx+usb_pipe.Lock] + call mutex_unlock +.return0: + xor eax, eax + ret +endp + +; Part of API for drivers, see documentation for USBAbortPipe. +proc usb_abort_pipe + push ebx esi ; save used registers to be stdcall +virtual at esp + rd 2 ; saved registers + dd ? ; return address +.pipe dd ? +end virtual + mov ebx, [.pipe] +; 1. Acquire pipe lock. + lea ecx, [ebx+usb_pipe.Lock] + call mutex_lock +; 2. If the pipe is already closed or abort is in progress, +; just release pipe lock and return. + test [ebx+usb_pipe.Flags], USB_FLAG_CLOSED + USB_FLAG_DISABLED + jnz .nothing +; 3. Mark the pipe as aborting. + or [ebx+usb_pipe.Flags], USB_FLAG_DISABLED +; 4. We cannot do anything except adding new transfers concurrently with hardware. +; Ask the controller driver to (temporarily) remove the pipe from hardware queue. + mov esi, [ebx+usb_pipe.Controller] +; 4a. Acquire queue lock. + lea ecx, [esi+usb_controller.ControlLock] + cmp [ebx+usb_pipe.Type], BULK_PIPE + jb @f ; control pipe + lea ecx, [esi+usb_controller.BulkLock] + jz @f ; bulk pipe + lea ecx, [esi+usb_controller.PeriodicLock] +@@: + call mutex_lock + push ecx +; 4b. Call the driver. + mov eax, [esi+usb_controller.HardwareFunc] + call [eax+usb_hardware_func.DisablePipe] +; 4c. Remove the pipe from software list. + mov eax, [ebx+usb_pipe.NextVirt] + mov edx, [ebx+usb_pipe.PrevVirt] + mov [eax+usb_pipe.PrevVirt], edx + mov [edx+usb_pipe.NextVirt], eax +; 4c. Register the pipe in corresponding wait list. + test [ebx+usb_pipe.Type], 1 + jz .control_bulk + call usb_subscribe_periodic + jmp @f +.control_bulk: + call usb_subscribe_control +@@: +; 4d. Release queue lock. + pop ecx + call mutex_unlock +; 4e. Notify the USB thread about new work. + push ebx esi edi + call usb_wakeup + pop edi esi ebx +; That's all for now. To be continued in usb_pipe_disabled. +; 5. Release pipe lock acquired at step 1 and return. +.nothing: + lea ecx, [ebx+usb_pipe.Lock] + call mutex_unlock + pop esi ebx + ret 4 +endp + +; Part of API for drivers, see documentation for USBGetParam. +proc usb_get_param +virtual at esp + dd ? ; return address +.pipe dd ? +.param dd ? +end virtual + mov edx, [.param] + mov ecx, [.pipe] + mov eax, [ecx+usb_pipe.DeviceData] + test edx, edx + jz .get_device_descriptor + dec edx + jz .get_config_descriptor + dec edx + jz .get_speed + or eax, -1 + ret 8 +.get_device_descriptor: + add eax, usb_device_data.DeviceDescriptor + ret 8 +.get_config_descriptor: + movzx ecx, [eax+usb_device_data.DeviceDescrSize] + lea eax, [eax+ecx+usb_device_data.DeviceDescriptor] + ret 8 +.get_speed: + movzx eax, [eax+usb_device_data.Speed] + ret 8 +endp + +; Initialize software part of usb_gtd. Called from controller-specific code +; somewhere in AllocTransfer with eax -> next (inactive) usb_gtd, +; ebx -> usb_pipe, ebp frame from call to AllocTransfer with [.td] -> +; current (initializing) usb_gtd. +; Returns ecx = [.td]. +proc usb_init_transfer +virtual at ebp-4 +.Size dd ? + rd 2 +.Buffer dd ? + dd ? +.Flags dd ? +.td dd ? +end virtual + mov [eax+usb_gtd.Pipe], ebx + mov ecx, [.td] + mov [eax+usb_gtd.PrevVirt], ecx + mov edx, [ecx+usb_gtd.NextVirt] + mov [ecx+usb_gtd.NextVirt], eax + mov [eax+usb_gtd.NextVirt], edx + mov [edx+usb_gtd.PrevVirt], eax + mov edx, [.Size] + mov [ecx+usb_gtd.Length], edx + xor edx, edx + mov [ecx+usb_gtd.Callback], edx + mov [ecx+usb_gtd.UserData], edx + ret +endp + +; Free all TDs for the current transfer if something has failed +; during initialization (e.g. no memory for the next TD). +; Stdcall with one stack argument = first TD for the transfer +; and eax = last initialized TD for the transfer. +proc usb_undo_tds + push [eax+usb_gtd.NextVirt] +@@: + cmp eax, [esp+8] + jz @f + push [eax+usb_gtd.PrevVirt] + stdcall [edi+usb_hardware_func.FreeTD], eax + pop eax + jmp @b +@@: + pop ecx + mov [eax+usb_gtd.NextVirt], ecx + mov [ecx+usb_gtd.PrevVirt], eax + ret 4 +endp + +; Helper procedure for handling short packets in controller-specific code. +; Returns with CF cleared if this is the final packet in some stage: +; for control transfers that means one of Data and Status stages, +; for other transfers - the final packet in the only stage. +proc usb_is_final_packet + cmp [ebx+usb_gtd.Callback], 0 + jnz .nothing + mov eax, [ebx+usb_gtd.NextVirt] + cmp [eax+usb_gtd.Callback], 0 + jz .stc + mov eax, [ebx+usb_gtd.Pipe] + cmp [eax+usb_pipe.Type], CONTROL_PIPE + jz .nothing +.stc: + stc +.nothing: + ret +endp + +; Helper procedure for controller-specific code: +; removes one TD from the transfer queue, ebx -> usb_gtd to remove. +proc usb_unlink_td + mov ecx, [ebx+usb_gtd.Pipe] + add ecx, usb_pipe.Lock + call mutex_lock + mov eax, [ebx+usb_gtd.PrevVirt] + mov edx, [ebx+usb_gtd.NextVirt] + mov [edx+usb_gtd.PrevVirt], eax + mov [eax+usb_gtd.NextVirt], edx + call mutex_unlock + ret +endp + +; One part of transfer is completed, run the associated callback +; or update total length in the next part of transfer. +; in: ebx -> usb_gtd, ecx = status, edx = length +proc usb_process_gtd +; 1. Test whether it is the last descriptor in the transfer +; <=> it has an associated callback. + mov eax, [ebx+usb_gtd.Callback] + test eax, eax + jz .nocallback +; 2. It has an associated callback; call it with corresponding parameters. + stdcall_verify eax, [ebx+usb_gtd.Pipe], ecx, \ + [ebx+usb_gtd.Buffer], edx, [ebx+usb_gtd.UserData] + ret +.nocallback: +; 3. It is an intermediate descriptor. Add its length to the length +; in the following descriptor. + mov eax, [ebx+usb_gtd.NextVirt] + add [eax+usb_gtd.Length], edx + ret +endp + +if USB_STDCALL_VERIFY +proc verify_regs +virtual at esp + dd ? ; return address +.edi dd ? +.esi dd ? +.ebp dd ? +.esp dd ? +.ebx dd ? +.edx dd ? +.ecx dd ? +.eax dd ? +end virtual + cmp ebx, [.ebx] + jz @f + dbgstr 'ERROR!!! ebx changed' +@@: + cmp esi, [.esi] + jz @f + dbgstr 'ERROR!!! esi changed' +@@: + cmp edi, [.edi] + jz @f + dbgstr 'ERROR!!! edi changed' +@@: + cmp ebp, [.ebp] + jz @f + dbgstr 'ERROR!!! ebp changed' +@@: + ret +endp +end if diff --git a/kernel/branches/kolibri-lldw/bus/usb/protocol.inc b/kernel/branches/kolibri-lldw/bus/usb/protocol.inc new file mode 100644 index 000000000..011f07b93 --- /dev/null +++ b/kernel/branches/kolibri-lldw/bus/usb/protocol.inc @@ -0,0 +1,1019 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Implementation of the USB protocol for device enumeration. +; Manage a USB device when it becomes ready for USB commands: +; configure, enumerate, load the corresponding driver(s), +; pass device information to the driver. + +; ============================================================================= +; ================================= Constants ================================= +; ============================================================================= +; USB standard request codes +USB_GET_STATUS = 0 +USB_CLEAR_FEATURE = 1 +USB_SET_FEATURE = 3 +USB_SET_ADDRESS = 5 +USB_GET_DESCRIPTOR = 6 +USB_SET_DESCRIPTOR = 7 +USB_GET_CONFIGURATION = 8 +USB_SET_CONFIGURATION = 9 +USB_GET_INTERFACE = 10 +USB_SET_INTERFACE = 11 +USB_SYNCH_FRAME = 12 + +; USB standard descriptor types +USB_DEVICE_DESCR = 1 +USB_CONFIG_DESCR = 2 +USB_STRING_DESCR = 3 +USB_INTERFACE_DESCR = 4 +USB_ENDPOINT_DESCR = 5 +USB_DEVICE_QUALIFIER_DESCR = 6 +USB_OTHER_SPEED_CONFIG_DESCR = 7 +USB_INTERFACE_POWER_DESCR = 8 + +; Compile-time setting. If set, the code will dump all descriptors as they are +; read to the debug board. +USB_DUMP_DESCRIPTORS = 1 + +; According to the USB specification (9.2.6.3), +; any device must response to SET_ADDRESS in 50 ms, or 5 timer ticks. +; Of course, our world is far from ideal. +; I have seen devices that just NAK everything when being reset from working +; state, but start to work after second reset. +; Our strategy is as follows: give 2 seconds for the first attempt, +; this should be enough for normal devices and not too long to detect buggy ones. +; If the device continues NAKing, reset it and retry several times, +; doubling the interval: 2s -> 4s -> 8s -> 16s. Give up after that. +; Numbers are quite arbitrary. +TIMEOUT_SET_ADDRESS_INITIAL = 200 +TIMEOUT_SET_ADDRESS_LAST = 1600 + +; ============================================================================= +; ================================ Structures ================================= +; ============================================================================= +; USB descriptors. See USB specification for detailed explanations. +; First two bytes of every descriptor have the same meaning. +struct usb_descr +bLength db ? +; Size of this descriptor in bytes +bDescriptorType db ? +; One of USB_*_DESCR constants. +ends + +; USB device descriptor +struct usb_device_descr usb_descr +bcdUSB dw ? +; USB Specification Release number in BCD, e.g. 110h = USB 1.1 +bDeviceClass db ? +; USB Device Class Code +bDeviceSubClass db ? +; USB Device Subclass Code +bDeviceProtocol db ? +; USB Device Protocol Code +bMaxPacketSize0 db ? +; Maximum packet size for zero endpoint +idVendor dw ? +; Vendor ID +idProduct dw ? +; Product ID +bcdDevice dw ? +; Device release number in BCD +iManufacturer db ? +; Index of string descriptor describing manufacturer +iProduct db ? +; Index of string descriptor describing product +iSerialNumber db ? +; Index of string descriptor describing serial number +bNumConfigurations db ? +; Number of possible configurations +ends + +; USB configuration descriptor +struct usb_config_descr usb_descr +wTotalLength dw ? +; Total length of data returned for this configuration +bNumInterfaces db ? +; Number of interfaces in this configuration +bConfigurationValue db ? +; Value for SET_CONFIGURATION control request +iConfiguration db ? +; Index of string descriptor describing this configuration +bmAttributes db ? +; Bit 6 is SelfPowered, bit 5 is RemoteWakeupSupported, +; bit 7 must be 1, other bits must be 0 +bMaxPower db ? +; Maximum power consumption from the bus in 2mA units +ends + +; USB interface descriptor +struct usb_interface_descr usb_descr +; The following two fields work in pair. Sometimes one interface can work +; in different modes; e.g. videostream from web-cameras requires different +; bandwidth depending on resolution/quality/compression settings. +; Each mode of each interface has its own descriptor with its own endpoints +; following; all descriptors for one interface have the same bInterfaceNumber, +; and different bAlternateSetting. +; By default, any interface operates in mode with bAlternateSetting = 0. +; Often this is the only mode. If there are another modes, the active mode +; is selected by SET_INTERFACE(bAlternateSetting) control request. +bInterfaceNumber db ? +bAlternateSetting db ? +bNumEndpoints db ? +; Number of endpoints used by this interface, excluding zero endpoint +bInterfaceClass db ? +; USB Interface Class Code +bInterfaceSubClass db ? +; USB Interface Subclass Code +bInterfaceProtocol db ? +; USB Interface Protocol Code +iInterface db ? +; Index of string descriptor describing this interface +ends + +; USB endpoint descriptor +struct usb_endpoint_descr usb_descr +bEndpointAddress db ? +; Lower 4 bits form endpoint number, +; upper bit is 0 for OUT endpoints and 1 for IN endpoints, +; other bits must be zero +bmAttributes db ? +; Lower 2 bits form transfer type, one of *_PIPE, +; other bits must be zero for non-isochronous endpoints; +; refer to the USB specification for meaning in isochronous case +wMaxPacketSize dw ? +; Lower 11 bits form maximum packet size, +; next two bits specify the number of additional transactions per microframe +; for high-speed periodic endpoints, other bits must be zero. +bInterval db ? +; Interval for polling endpoint for data transfers. +; Isochronous and high-speed interrupt endpoints: poll every 2^(bInterval-1) +; (micro)frames +; Full/low-speed interrupt endpoints: poll every bInterval frames +; High-speed bulk/control OUT endpoints: maximum NAK rate +ends + +; ============================================================================= +; =================================== Code ==================================== +; ============================================================================= + +; When a new device is ready to be configured, a controller-specific code +; calls usb_new_device. +; The sequence of further actions: +; * open pipe for the zero endpoint (usb_new_device); +; maximum packet size is not known yet, but it must be at least 8 bytes, +; so it is safe to send packets with <= 8 bytes +; * issue SET_ADDRESS control request (usb_new_device) +; * set the new device address in the pipe (usb_set_address_callback) +; * notify a controller-specific code that initialization of other ports +; can be started (usb_set_address_callback) +; * issue GET_DESCRIPTOR control request for first 8 bytes of device descriptor +; (usb_after_set_address) +; * first 8 bytes of device descriptor contain the true packet size for zero +; endpoint, so set the true packet size (usb_get_descr8_callback) +; * first 8 bytes of a descriptor contain the full size of this descriptor, +; issue GET_DESCRIPTOR control request for the full device descriptor +; (usb_after_set_endpoint_size) +; * issue GET_DESCRIPTOR control request for first 8 bytes of configuration +; descriptor (usb_get_descr_callback) +; * issue GET_DESCRIPTOR control request for full configuration descriptor +; (usb_know_length_callback) +; * issue SET_CONFIGURATION control request (usb_set_config_callback) +; * parse configuration descriptor, load the corresponding driver(s), +; pass the configuration descriptor to the driver and let the driver do +; the further work (usb_got_config_callback) + +; This function is called from controller-specific part +; when a new device is ready to be configured. +; in: ecx -> pseudo-pipe, part of usb_pipe +; in: esi -> usb_controller +; in: [esi+usb_controller.ResettingHub] is the pointer to usb_hub for device, +; NULL if the device is connected to the root hub +; in: [esi+usb_controller.ResettingPort] is the port for the device, zero-based +; in: [esi+usb_controller.ResettingSpeed] is the speed of the device, one of +; USB_SPEED_xx. +; out: eax = 0 <=> failed, the caller should disable the port. +proc usb_new_device + push ebx edi ; save used registers to be stdcall +; 1. Check whether we're here because we were trying to reset +; already-registered device in hope to fix something serious. +; If so, skip allocation and go to 6. + movzx eax, [esi+usb_controller.ResettingPort] + mov edx, [esi+usb_controller.ResettingHub] + test edx, edx + jz .test_roothub + mov edx, [edx+usb_hub.ConnectedDevicesPtr] + mov ebx, [edx+eax*4] + jmp @f +.test_roothub: + mov ebx, [esi+usb_controller.DevicesByPort+eax*4] +@@: + test ebx, ebx + jnz .try_set_address +; 2. Allocate resources. Any device uses the following resources: +; - device address in the bus +; - memory for device data +; - pipe for zero endpoint +; If some allocation fails, we must undo our actions. Closing the pipe +; is a hard task, so we avoid it and open the pipe as the last resource. +; The order for other two allocations is quite arbitrary. +; 2a. Allocate a bus address. + push ecx + call usb_set_address_request + pop ecx +; 2b. If failed, just return zero. + test eax, eax + jz .nothing +; 2c. Allocate memory for device data. +; For now, we need sizeof.usb_device_data and extra 8 bytes for GET_DESCRIPTOR +; input and output, see usb_after_set_address. Later we will reallocate it +; to actual size needed for descriptors. + movi eax, sizeof.usb_device_data + 8 + push ecx + call malloc + pop ecx +; 2d. If failed, free the bus address and return zero. + test eax, eax + jz .nomemory +; 2e. Open pipe for endpoint zero. +; For now, we do not know the actual maximum packet size; +; for full-speed devices it can be any of 8, 16, 32, 64 bytes, +; low-speed devices must have 8 bytes, high-speed devices must have 64 bytes. +; Thus, we must use some fake "maximum packet size" until the actual size +; will be known. However, the maximum packet size must be at least 8, and +; initial stages of the configuration process involves only packets of <= 8 +; bytes, they will be transferred correctly as long as +; the fake "maximum packet size" is also at least 8. +; Thus, any number >= 8 is suitable for actual hardware. +; However, software emulation of EHCI in VirtualBox assumes that high-speed +; control transfers are those originating from pipes with max packet size = 64, +; even on early stages of the configuration process. This is incorrect, +; but we have no specific preferences, so let VirtualBox be happy and use 64 +; as the fake "maximum packet size". + push eax +; We will need many zeroes. +; "push edi" is one byte, "push 0" is two bytes; save space, use edi. + xor edi, edi + stdcall usb_open_pipe, ecx, edi, 64, edi, edi +; Put pointer to pipe into ebx. "xchg eax,reg" is one byte, mov is two bytes. + xchg eax, ebx + pop eax +; 2f. If failed, free the memory, the bus address and return zero. + test ebx, ebx + jz .freememory +; 3. Store pointer to device data in the pipe structure. + mov [ebx+usb_pipe.DeviceData], eax +; 4. Init device data, using usb_controller.Resetting* variables. + mov [eax+usb_device_data.Timer], edi + mov dword [eax+usb_device_data.DeviceDescriptor], TIMEOUT_SET_ADDRESS_INITIAL + mov [eax+usb_device_data.TTHub], edi + mov [eax+usb_device_data.TTPort], 0 + mov [eax+usb_device_data.NumInterfaces], edi + mov [eax+usb_device_data.DeviceDescrSize], 0 + mov dl, [esi+usb_controller.ResettingSpeed] + mov [eax+usb_device_data.Speed], dl + mov [eax+usb_device_data.NumPipes], 1 + push ebx + cmp dl, USB_SPEED_HS + jz .nott + mov ebx, [esi+usb_controller.ResettingHub] + test ebx, ebx + jz .nott + mov cl, [esi+usb_controller.ResettingPort] + mov edx, [ebx+usb_hub.ConfigPipe] + mov edx, [edx+usb_pipe.DeviceData] + cmp [edx+usb_device_data.TTHub], 0 + jz @f + mov cl, [edx+usb_device_data.TTPort] + mov ebx, [edx+usb_device_data.TTHub] + jmp .has_tt +@@: + cmp [edx+usb_device_data.Speed], USB_SPEED_HS + jnz .nott +.has_tt: + mov [eax+usb_device_data.TTHub], ebx + mov [eax+usb_device_data.TTPort], cl +.nott: + pop ebx + mov [eax+usb_device_data.ConfigDataSize], edi + mov [eax+usb_device_data.Interfaces], edi + movzx ecx, [esi+usb_controller.ResettingPort] + mov [eax+usb_device_data.Port], cl + mov edx, [esi+usb_controller.ResettingHub] + mov [eax+usb_device_data.Hub], edx +; 5. Store pointer to the config pipe in the hub data. +; Config pipe serves as device identifier. +; Root hubs use the array inside usb_controller structure, +; non-root hubs use the array immediately after usb_hub structure. + test edx, edx + jz .roothub + mov edx, [edx+usb_hub.ConnectedDevicesPtr] + mov [edx+ecx*4], ebx + jmp @f +.roothub: + mov [esi+usb_controller.DevicesByPort+ecx*4], ebx +@@: + call usb_reinit_pipe_list +; 6. Issue SET_ADDRESS control request, using buffer filled in step 2a. +; 6a. Configure timer to force reset after timeout. +; Note: we can't use self-destructing timer, because we need to be able to cancel it, +; and for self-destructing timer we could have race condition in cancelling/destructing. +; DEBUGF 1,'K : pipe %x\n',ebx +.try_set_address: + xor edi, edi + mov edx, [ebx+usb_pipe.DeviceData] + stdcall timer_hs, [edx+usb_device_data.DeviceDescriptor], 7FFFFFFFh, usb_abort_pipe, ebx + test eax, eax + jz .nothing + mov edx, [ebx+usb_pipe.DeviceData] + mov [edx+usb_device_data.Timer], eax +; 6b. If it succeeded, setup timer to configure wait timeout. + lea eax, [esi+usb_controller.SetAddressBuffer] + stdcall usb_control_async, ebx, eax, edi, edi, usb_set_address_callback, edi, edi +; Use the return value from usb_control_async as our return value; +; if it is zero, then something has failed. +.nothing: +; 7. Return. + pop edi ebx ; restore used registers to be stdcall + ret +; Handlers of failures in steps 2b, 2d, 2f. +.freememory: + call free + jmp .freeaddr +.nomemory: + dbgstr 'No memory for device data' +.freeaddr: + mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] + bts [esi+usb_controller.ExistingAddresses], ecx + xor eax, eax + jmp .nothing +endp + +; Helper procedure for usb_new_device. +; Allocates a new USB address and fills usb_controller.SetAddressBuffer +; with data for SET_ADDRESS(allocated_address) request. +; out: eax = 0 <=> failed +; Destroys edi. +proc usb_set_address_request +; There are 128 bits, one for each possible address. +; Note: only the USB thread works with usb_controller.ExistingAddresses, +; so there is no need for synchronization. +; We must find a bit set to 1 and clear it. +; 1. Find the first dword which has a nonzero bit = which is nonzero. + mov ecx, 128/32 + lea edi, [esi+usb_controller.ExistingAddresses] + xor eax, eax + repz scasd +; 2. If all dwords are zero, return an error. + jz .error +; 3. The dword at [edi-4] is nonzero. Find the lowest nonzero bit. + bsf eax, [edi-4] +; Now eax = bit number inside the dword at [edi-4]. +; 4. Clear the bit. + btr [edi-4], eax +; 5. Generate the address by edi = memory address and eax = bit inside dword. +; Address = eax + 8 * (edi-4 - (esi+usb_controller.ExistingAddress)). + sub edi, esi + lea edi, [eax+(edi-4-usb_controller.ExistingAddresses)*8] +; 6. Store the allocated address in SetAddressBuffer and fill remaining fields. +; Note that usb_controller is zeroed at allocation, so only command byte needs +; to be filled. + mov byte [esi+usb_controller.SetAddressBuffer+1], USB_SET_ADDRESS + mov dword [esi+usb_controller.SetAddressBuffer+2], edi +; 7. Return non-zero value in eax. + inc eax +.nothing: + ret +.error: + dbgstr 'cannot allocate USB address' + xor eax, eax + jmp .nothing +endp + +; This procedure is called by USB stack when SET_ADDRESS request initiated by +; usb_new_device is completed, either successfully or unsuccessfully. +; Note that USB stack uses esi = pointer to usb_controller. +proc usb_set_address_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword + push ebx ; save ebx to be stdcall + mov ebx, [pipe] +; 1. In any case, cancel the timer. + mov eax, [ebx+usb_pipe.DeviceData] + stdcall cancel_timer_hs, [eax+usb_device_data.Timer] + mov eax, [ebx+usb_pipe.DeviceData] + mov [eax+usb_device_data.Timer], 0 +; Load data to registers for further references. + mov ecx, dword [esi+usb_controller.SetAddressBuffer+2] + mov eax, [esi+usb_controller.HardwareFunc] +; 2. Check whether the device has accepted new address. If so, proceed to 3. +; Otherwise, go to 4 if killed by usb_set_address_timeout or to 5 otherwise. + cmp [status], USB_STATUS_CANCELLED + jz .timeout + cmp [status], 0 + jnz .error +; 3. Address accepted. +; 3a. The controller-specific structure for the control pipe still uses +; zero address. Call the controller-specific function to change it to +; the actual address. +; Note that the hardware could cache the controller-specific structure, +; so setting the address could take some time until the cache is evicted. +; Thus, the call is asynchronous; meet us in usb_after_set_address when it will +; be safe to continue. +; dbgstr 'address set in device' + call [eax+usb_hardware_func.SetDeviceAddress] +; 3b. If the port is in non-root hub, clear 'reset in progress' flag. +; In any case, proceed to 6. + mov eax, [esi+usb_controller.ResettingHub] + test eax, eax + jz .return + and [eax+usb_hub.Actions], not HUB_RESET_IN_PROGRESS +.return: +; 6. Address configuration done, we can proceed with other ports. +; Call the worker function for that. + call usb_test_pending_port +.wakeup: + push esi edi + call usb_wakeup + pop edi esi +.nothing: + pop ebx ; restore ebx to be stdcall + ret +.timeout: +; 4. Device continues to NAK the request. Reset it and retry. + mov edx, [ebx+usb_pipe.DeviceData] + mov ecx, [edx+usb_device_data.DeviceDescriptor] + add ecx, ecx + cmp ecx, TIMEOUT_SET_ADDRESS_LAST + ja .error + mov [edx+usb_device_data.DeviceDescriptor], ecx + dbgstr 'Timeout in USB device initialization, trying to reset...' + cmp [esi+usb_controller.ResettingHub], 0 + jz .reset_roothub + push esi + mov esi, [esi+usb_controller.ResettingHub] + call usb_hub_initiate_reset + pop esi + jmp .nothing +.reset_roothub: + movzx ecx, [esi+usb_controller.ResettingPort] + call [eax+usb_hardware_func.InitiateReset] + jmp .wakeup +.error: +; 5. Device error: device not responding, disconnect etc. + DEBUGF 1,'K : error %d in SET_ADDRESS, USB device disabled\n',[status] +; 5a. The address has not been accepted. Mark it as free. + bts dword [esi+usb_controller.ExistingAddresses], ecx +; 5b. Disable the port with bad device. +; For the root hub, call the controller-specific function and go to 6. +; For non-root hubs, let the hub code do its work and return (the request +; could take some time, the hub code is responsible for proceeding). + cmp [esi+usb_controller.ResettingHub], 0 + jz .roothub + mov eax, [esi+usb_controller.ResettingHub] + call usb_hub_disable_resetting_port + jmp .nothing +.roothub: + movzx ecx, [esi+usb_controller.ResettingPort] + call [eax+usb_hardware_func.PortDisable] + jmp .return +endp + +; This procedure is called from usb_subscription_done when the hardware cache +; is cleared after request from usb_set_address_callback. +; in: ebx -> usb_pipe +proc usb_after_set_address +; dbgstr 'address set for controller' +; Issue control transfer GET_DESCRIPTOR(DEVICE_DESCR) for first 8 bytes. +; Remember, we still do not know the actual packet size; +; 8-bytes-request is safe. +; usb_new_device has allocated 8 extra bytes besides sizeof.usb_device_data; +; use them for both input and output. + mov eax, [ebx+usb_pipe.DeviceData] + add eax, usb_device_data.DeviceDescriptor + mov dword [eax], \ + 80h + \ ; device-to-host, standard, device-wide + (USB_GET_DESCRIPTOR shl 8) + \ ; request + (0 shl 16) + \ ; descriptor index: there is only one + (USB_DEVICE_DESCR shl 24) ; descriptor type + mov dword [eax+4], 8 shl 16 ; data length + stdcall usb_control_async, ebx, eax, eax, 8, usb_get_descr8_callback, eax, 0 + ret +endp + +; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE_DESCR) +; request initiated by usb_after_set_address is completed, either successfully +; or unsuccessfully. +; Note that USB stack uses esi = pointer to usb_controller. +proc usb_get_descr8_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; mov eax, [buffer] +; DEBUGF 1,'K : descr8: l=%x; %x %x %x %x %x %x %x %x\n',[length],\ +; [eax]:2,[eax+1]:2,[eax+2]:2,[eax+3]:2,[eax+4]:2,[eax+5]:2,[eax+6]:2,[eax+7]:2 + push edi ebx ; save used registers to be stdcall + mov ebx, [pipe] +; 1. Check whether the operation was successful. +; If not, say something to the debug board and stop the initialization. + cmp [status], 0 + jnz .error +; 2. Length of descriptor must be at least sizeof.usb_device_descr bytes. +; If not, say something to the debug board and stop the initialization. + mov eax, [ebx+usb_pipe.DeviceData] + cmp [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength], sizeof.usb_device_descr + jb .error +; 3. Now first 8 bytes of device descriptor are known; +; set DeviceDescrSize accordingly. + mov [eax+usb_device_data.DeviceDescrSize], 8 +; 4. The controller-specific structure for the control pipe still uses +; the fake "maximum packet size". Call the controller-specific function to +; change it to the actual packet size from the device. +; Note that the hardware could cache the controller-specific structure, +; so changing it could take some time until the cache is evicted. +; Thus, the call is asynchronous; meet us in usb_after_set_endpoint_size +; when it will be safe to continue. + movzx ecx, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bMaxPacketSize0] + mov eax, [esi+usb_controller.HardwareFunc] + call [eax+usb_hardware_func.SetEndpointPacketSize] +.nothing: +; 5. Return. + pop ebx edi ; restore used registers to be stdcall + ret +.error: + dbgstr 'error with USB device descriptor' + jmp .nothing +endp + +; This procedure is called from usb_subscription_done when the hardware cache +; is cleared after request from usb_get_descr8_callback. +; in: ebx -> usb_pipe +proc usb_after_set_endpoint_size +; 1. Reallocate memory for device data: +; add memory for now-known size of device descriptor and extra 8 bytes +; for further actions. +; 1a. Allocate new memory. + mov eax, [ebx+usb_pipe.DeviceData] + movzx eax, [eax+usb_device_data.DeviceDescriptor+usb_device_descr.bLength] +; save length for step 2 + push eax + add eax, sizeof.usb_device_data + 8 + call malloc +; 1b. If failed, say something to the debug board and stop the initialization. + test eax, eax + jz .nomemory +; 1c. Copy data from old memory to new memory and switch the pointer in usb_pipe. + push eax + push esi edi + mov esi, [ebx+usb_pipe.DeviceData] + mov [ebx+usb_pipe.DeviceData], eax + mov edi, eax + mov eax, esi + mov ecx, sizeof.usb_device_data / 4 + rep movsd + pop edi esi + call usb_reinit_pipe_list +; 1d. Free the old memory. + call free + pop eax +; 2. Issue control transfer GET_DESCRIPTOR(DEVICE) for full descriptor. +; restore length saved in step 1a + pop edx + add eax, sizeof.usb_device_data + mov dword [eax], \ + 80h + \ ; device-to-host, standard, device-wide + (USB_GET_DESCRIPTOR shl 8) + \ ; request + (0 shl 16) + \ ; descriptor index: there is only one + (USB_DEVICE_DESCR shl 24) ; descriptor type + and dword [eax+4], 0 + mov [eax+6], dl ; data length + stdcall usb_control_async, ebx, eax, eax, edx, usb_get_descr_callback, eax, 0 +; 3. Return. + ret +.nomemory: + dbgstr 'No memory for device data' + ret +endp + +; This procedure is called by USB stack when GET_DESCRIPTOR(DEVICE) +; request initiated by usb_after_set_endpoint_size is completed, +; either successfully or unsuccessfully. +proc usb_get_descr_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; Note: the prolog is the same as in usb_get_descr8_callback. + push edi ebx ; save used registers to be stdcall +; 1. Check whether the operation was successful. +; If not, say something to the debug board and stop the initialization. + cmp [status], 0 + jnz usb_get_descr8_callback.error +; The full descriptor is known, dump it if specified by compile-time option. +if USB_DUMP_DESCRIPTORS + mov eax, [buffer] + mov ecx, [length] + sub ecx, 8 + jbe .skipdebug + DEBUGF 1,'K : device descriptor:' +@@: + DEBUGF 1,' %x',[eax]:2 + inc eax + dec ecx + jnz @b + DEBUGF 1,'\n' +.skipdebug: +end if +; 2. Check that bLength is the same as was in the previous request. +; If not, say something to the debug board and stop the initialization. +; It is important, because usb_after_set_endpoint_size has allocated memory +; according to the old bLength. Note that [length] for control transfers +; includes 8 bytes of setup packet, so data length = [length] - 8. + mov eax, [buffer] + movzx ecx, [eax+usb_device_descr.bLength] + add ecx, 8 + cmp [length], ecx + jnz usb_get_descr8_callback.error +; Amuse the user if she is watching the debug board. + mov cl, [eax+usb_device_descr.bNumConfigurations] + DEBUGF 1,'K : found USB device with ID %x:%x, %d configuration(s)\n',\ + [eax+usb_device_descr.idVendor]:4,\ + [eax+usb_device_descr.idProduct]:4,\ + cl +; 3. If there are no configurations, stop the initialization. + cmp [eax+usb_device_descr.bNumConfigurations], 0 + jz .nothing +; 4. Copy length of device descriptor to device data structure. + movzx edx, [eax+usb_device_descr.bLength] + mov [eax+usb_device_data.DeviceDescrSize-usb_device_data.DeviceDescriptor], dl +; 5. Issue control transfer GET_DESCRIPTOR(CONFIGURATION). We do not know +; the full length of that descriptor, so start with first 8 bytes, they contain +; the full length. +; usb_after_set_endpoint_size has allocated 8 extra bytes after the +; device descriptor, use them for both input and output. + add eax, edx + mov dword [eax], \ + 80h + \ ; device-to-host, standard, device-wide + (USB_GET_DESCRIPTOR shl 8) + \ ; request + (0 shl 16) + \ ; descriptor index: there is only one + (USB_CONFIG_DESCR shl 24) ; descriptor type + mov dword [eax+4], 8 shl 16 ; data length + stdcall usb_control_async, [pipe], eax, eax, 8, usb_know_length_callback, eax, 0 +.nothing: +; 6. Return. + pop ebx edi ; restore used registers to be stdcall + ret +endp + +; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION) +; request initiated by usb_get_descr_callback is completed, +; either successfully or unsuccessfully. +proc usb_know_length_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword + push ebx ; save used registers to be stdcall +; 1. Check whether the operation was successful. +; If not, say something to the debug board and stop the initialization. + cmp [status], 0 + jnz .error +; 2. Get the total length of data associated with config descriptor and store +; it in device data structure. The total length must be at least +; sizeof.usb_config_descr bytes; if not, say something to the debug board and +; stop the initialization. + mov eax, [buffer] + mov edx, [pipe] + movzx ecx, [eax+usb_config_descr.wTotalLength] + mov eax, [edx+usb_pipe.DeviceData] + cmp ecx, sizeof.usb_config_descr + jb .error + mov [eax+usb_device_data.ConfigDataSize], ecx +; 3. Reallocate memory for device data: +; include usb_device_data structure, device descriptor, +; config descriptor with all associated data, and extra bytes +; sufficient for 8 bytes control packet and for one usb_interface_data struc. +; Align extra bytes to dword boundary. +if sizeof.usb_interface_data > 8 +.extra_size = sizeof.usb_interface_data +else +.extra_size = 8 +end if +; 3a. Allocate new memory. + movzx edx, [eax+usb_device_data.DeviceDescrSize] + lea eax, [ecx+edx+sizeof.usb_device_data+.extra_size+3] + and eax, not 3 + push eax + call malloc + pop edx +; 3b. If failed, say something to the debug board and stop the initialization. + test eax, eax + jz .nomemory +; 3c. Copy data from old memory to new memory and switch the pointer in usb_pipe. + push eax + mov ebx, [pipe] + push esi edi + mov esi, [ebx+usb_pipe.DeviceData] + mov edi, eax + mov [ebx+usb_pipe.DeviceData], eax + mov eax, esi + movzx ecx, [esi+usb_device_data.DeviceDescrSize] + sub edx, .extra_size + mov [esi+usb_device_data.Interfaces], edx + add ecx, sizeof.usb_device_data + 8 + mov edx, ecx + shr ecx, 2 + and edx, 3 + rep movsd + mov ecx, edx + rep movsb + pop edi esi + call usb_reinit_pipe_list +; 3d. Free old memory. + call free + pop eax +; 4. Issue control transfer GET_DESCRIPTOR(CONFIGURATION) for full descriptor. + movzx ecx, [eax+usb_device_data.DeviceDescrSize] + mov edx, [eax+usb_device_data.ConfigDataSize] + lea eax, [eax+ecx+sizeof.usb_device_data] + mov dword [eax], \ + 80h + \ ; device-to-host, standard, device-wide + (USB_GET_DESCRIPTOR shl 8) + \ ; request + (0 shl 16) + \ ; descriptor index: there is only one + (USB_CONFIG_DESCR shl 24) ; descriptor type + and dword [eax+4], 0 + mov word [eax+6], dx ; data length + stdcall usb_control_async, [pipe], eax, eax, edx, usb_set_config_callback, eax, 0 +.nothing: +; 5. Return. + pop ebx ; restore used registers to be stdcall + ret +.error: + dbgstr 'error with USB configuration descriptor' + jmp .nothing +.nomemory: + dbgstr 'No memory for device data' + jmp .nothing +endp + +; This procedure is called by USB stack when GET_DESCRIPTOR(CONFIGURATION) +; request initiated by usb_know_length_callback is completed, +; either successfully or unsuccessfully. +proc usb_set_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +; Note that the prolog is the same as in usb_know_length_callback. + push ebx ; save used registers to be stdcall +; 1. Check whether the operation was successful. +; If not, say something to the debug board and stop the initialization. + xor ecx, ecx + mov ebx, [pipe] + cmp [status], ecx + jnz usb_know_length_callback.error +; The full descriptor is known, dump it if specified by compile-time option. +if USB_DUMP_DESCRIPTORS + mov eax, [buffer] + mov ecx, [length] + sub ecx, 8 + jbe .skip_debug + DEBUGF 1,'K : config descriptor:' +@@: + DEBUGF 1,' %x',[eax]:2 + inc eax + dec ecx + jnz @b + DEBUGF 1,'\n' +.skip_debug: + xor ecx, ecx +end if +; 2. Issue control transfer SET_CONFIGURATION to activate this configuration. +; Usually this is the only configuration. +; Use extra bytes allocated by usb_know_length_callback; +; offset from device data start is stored in Interfaces. + mov eax, [ebx+usb_pipe.DeviceData] + mov edx, [buffer] + add eax, [eax+usb_device_data.Interfaces] + mov dl, [edx+usb_config_descr.bConfigurationValue] + mov dword [eax], USB_SET_CONFIGURATION shl 8 + mov dword [eax+4], ecx + mov byte [eax+2], dl + stdcall usb_control_async, [pipe], eax, ecx, ecx, usb_got_config_callback, [buffer], ecx + pop ebx ; restore used registers to be stdcall + ret +endp + +; This procedure is called by USB stack when SET_CONFIGURATION +; request initiated by usb_set_config_callback is completed, +; either successfully or unsuccessfully. +; If successfully, the device is configured and ready to work, +; pass the device to the corresponding driver(s). +proc usb_got_config_callback stdcall, pipe:dword, status:dword, buffer:dword, length:dword, calldata:dword +locals +InterfacesData dd ? +NumInterfaces dd ? +driver dd ? +endl +; 1. If there was an error, say something to the debug board and stop the +; initialization. + cmp [status], 0 + jz @f + dbgstr 'USB error in SET_CONFIGURATION' + ret +@@: + push ebx edi ; save used registers to be stdcall +; 2. Sanity checks: the total length must be the same as before (because we +; have allocated memory assuming the old value), length of config descriptor +; must be at least sizeof.usb_config_descr (we use fields from it), +; there must be at least one interface. + mov ebx, [pipe] + mov ebx, [ebx+usb_pipe.DeviceData] + mov eax, [calldata] + mov edx, [ebx+usb_device_data.ConfigDataSize] + cmp [eax+usb_config_descr.wTotalLength], dx + jnz .invalid + cmp [eax+usb_config_descr.bLength], 9 + jb .invalid + movzx edx, [eax+usb_config_descr.bNumInterfaces] + test edx, edx + jnz @f +.invalid: + dbgstr 'error: invalid configuration descriptor' + jmp .nothing +@@: +; 3. Store the number of interfaces in device data structure. + mov [ebx+usb_device_data.NumInterfaces], edx +; 4. If there is only one interface (which happens quite often), +; the memory allocated in usb_know_length_callback is sufficient. +; Otherwise (which also happens quite often), reallocate device data. +; 4a. Check whether there is only one interface. If so, skip this step. + cmp edx, 1 + jz .has_memory +; 4b. Allocate new memory. + mov eax, [ebx+usb_device_data.Interfaces] + lea eax, [eax+edx*sizeof.usb_interface_data] + call malloc +; 4c. If failed, say something to the debug board and +; stop the initialization. + test eax, eax + jnz @f + dbgstr 'No memory for device data' + jmp .nothing +@@: +; 4d. Copy data from old memory to new memory and switch the pointer in usb_pipe. + push eax + push esi + mov ebx, [pipe] + mov edi, eax + mov esi, [ebx+usb_pipe.DeviceData] + mov [ebx+usb_pipe.DeviceData], eax + mov eax, esi + mov ecx, [esi+usb_device_data.Interfaces] + shr ecx, 2 + rep movsd + pop esi + call usb_reinit_pipe_list +; 4e. Free old memory. + call free + pop ebx +.has_memory: +; 5. Initialize interfaces table: zero all contents. + mov edi, [ebx+usb_device_data.Interfaces] + add edi, ebx + mov [InterfacesData], edi + mov ecx, [ebx+usb_device_data.NumInterfaces] +if sizeof.usb_interface_data <> 8 +You have changed sizeof.usb_interface_data? Modify this place too. +end if + add ecx, ecx + xor eax, eax + rep stosd +; No interfaces are found yet. + mov [NumInterfaces], eax +; 6. Get the pointer to config descriptor data. +; Note: if there was reallocation, [buffer] is not valid anymore, +; so calculate value based on usb_device_data. + movzx eax, [ebx+usb_device_data.DeviceDescrSize] + lea eax, [eax+ebx+sizeof.usb_device_data] + mov [calldata], eax + mov ecx, [ebx+usb_device_data.ConfigDataSize] +; 7. Loop over all descriptors, +; scan for interface descriptors with bAlternateSetting = 0, +; load the corresponding driver, call its AddDevice function. +.descriptor_loop: +; While in loop: eax points to the current descriptor, +; ecx = number of bytes left, the iteration starts only if ecx is nonzero, +; edx = size of the current descriptor. +; 7a. The first byte is always accessible; it contains the length of +; the current descriptor. Validate that the length is at least 2 bytes, +; and the entire descriptor is readable (the length is at most number of +; bytes left). + movzx edx, [eax+usb_descr.bLength] + cmp edx, sizeof.usb_descr + jb .invalid + cmp ecx, edx + jb .invalid +; 7b. Check descriptor type. Ignore all non-INTERFACE descriptor. + cmp byte [eax+usb_descr.bDescriptorType], USB_INTERFACE_DESCR + jz .interface +.next_descriptor: +; 7c. Advance pointer, decrease length left, if there is still something left, +; continue the loop. + add eax, edx + sub ecx, edx + jnz .descriptor_loop +.done: +.nothing: + pop edi ebx ; restore used registers to be stdcall + ret +.interface: +; 7d. Validate the descriptor length. + cmp edx, sizeof.usb_interface_descr + jb .next_descriptor +; 7e. If bAlternateSetting is nonzero, this descriptor actually describes +; another mode of already known interface and belongs to the already loaded +; driver; amuse the user and continue to 7c. + cmp byte [eax+usb_interface_descr.bAlternateSetting], 0 + jz @f + DEBUGF 1,'K : note: alternate setting with %x/%x/%x\n',\ + [eax+usb_interface_descr.bInterfaceClass]:2,\ + [eax+usb_interface_descr.bInterfaceSubClass]:2,\ + [eax+usb_interface_descr.bInterfaceProtocol]:2 + jmp .next_descriptor +@@: +; 7f. Check that the new interface does not overflow allocated table. + mov edx, [NumInterfaces] + inc edx + cmp edx, [ebx+usb_device_data.NumInterfaces] + ja .invalid +; 7g. We have found a new interface. Advance bookkeeping vars. + mov [NumInterfaces], edx + add [InterfacesData], sizeof.usb_interface_data +; 7h. Save length left and pointer to the current interface descriptor. + push ecx eax +; Amuse the user if she is watching the debug board. + DEBUGF 1,'K : USB interface class/subclass/protocol = %x/%x/%x\n',\ + [eax+usb_interface_descr.bInterfaceClass]:2,\ + [eax+usb_interface_descr.bInterfaceSubClass]:2,\ + [eax+usb_interface_descr.bInterfaceProtocol]:2 +; 7i. Select the correct driver based on interface class. +; For hubs, go to 7j. Otherwise, go to 7k. +; Note: this should be rewritten as table-based lookup when more drivers will +; be available. + cmp byte [eax+usb_interface_descr.bInterfaceClass], 9 + jz .found_hub + mov edx, usb_hid_name + cmp byte [eax+usb_interface_descr.bInterfaceClass], 3 + jz .load_driver + mov edx, usb_print_name + cmp byte [eax+usb_interface_descr.bInterfaceClass], 7 + jz .load_driver + mov edx, usb_stor_name + cmp byte [eax+usb_interface_descr.bInterfaceClass], 8 + jz .load_driver + mov edx, usb_other_name + jmp .load_driver +.found_hub: +; 7j. Hubs are a part of USB stack, thus, integrated into the kernel. +; Use the pointer to hub callbacks and go to 7m. + mov eax, usb_hub_pseudosrv - USBSRV.usb_func + jmp .driver_loaded +.load_driver: +; 7k. Load the corresponding driver. + push ebx esi edi + stdcall get_service, edx + pop edi esi ebx +; 7l. If failed, say something to the debug board and go to 7p. + test eax, eax + jnz .driver_loaded + dbgstr 'failed to load class driver' + jmp .next_descriptor2 +.driver_loaded: +; 7m. Call AddDevice function of the driver. +; Note that top of stack contains a pointer to the current interface, +; saved by step 7h. + mov [driver], eax + mov eax, [eax+USBSRV.usb_func] + pop edx + push edx +; Note: usb_hub_init assumes that edx points to usb_interface_descr, +; ecx = length rest; if you change the code, modify usb_hub_init also. + stdcall [eax+USBFUNC.add_device], [pipe], [calldata], edx +; 7n. If failed, say something to the debug board and go to 7p. + test eax, eax + jnz .store_data + dbgstr 'USB device initialization failed' + jmp .next_descriptor2 +.store_data: +; 7o. Store the returned value and the driver handle to InterfacesData. +; Note that step 7g has already advanced InterfacesData. + mov edx, [InterfacesData] + mov [edx+usb_interface_data.DriverData-sizeof.usb_interface_data], eax + mov eax, [driver] + mov [edx+usb_interface_data.DriverFunc-sizeof.usb_interface_data], eax +.next_descriptor2: +; 7p. Restore registers saved in step 7h, get the descriptor length and +; continue to 7c. + pop eax ecx + movzx edx, byte [eax+usb_descr.bLength] + jmp .next_descriptor +endp + +; Driver names, see step 7i of usb_got_config_callback. +iglobal +usb_hid_name db 'usbhid',0 +usb_stor_name db 'usbstor',0 +usb_print_name db 'usbprint',0 +usb_other_name db 'usbother',0 +endg diff --git a/kernel/branches/kolibri-lldw/const.inc b/kernel/branches/kolibri-lldw/const.inc new file mode 100644 index 000000000..2e7b331b9 --- /dev/null +++ b/kernel/branches/kolibri-lldw/const.inc @@ -0,0 +1,989 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +dpl0 = 10010000b ; data read dpl0 +drw0 = 10010010b ; data read/write dpl0 +drw3 = 11110010b ; data read/write dpl3 +cpl0 = 10011010b ; code read dpl0 +cpl3 = 11111010b ; code read dpl3 + +D32 = 01000000b ; 32bit segment +G32 = 10000000b ; page gran + +;;;;;;;;;;; task manager errors ;;;;;;;;;; + +TASKMAN_ERROR_OUT_OF_MEMORY = 30 ; 0x1E +TASKMAN_ERROR_NOT_A_EXECUTABLE = 31 ; 0x1F +TASKMAN_ERROR_TOO_MANY_PROCESSES = 32 ; 0x20 + +;;;;;;;;;;;;cpu_caps flags;;;;;;;;;;;;;;;; + +CPU_386 = 3 +CPU_486 = 4 +CPU_PENTIUM = 5 +CPU_P6 = 6 +CPU_PENTIUM4 = 0x0F + +CAPS_FPU = 00 ;on-chip x87 floating point unit +CAPS_VME = 01 ;virtual-mode enhancements +CAPS_DE = 02 ;debugging extensions +CAPS_PSE = 03 ;page-size extensions +CAPS_TSC = 04 ;time stamp counter +CAPS_MSR = 05 ;model-specific registers +CAPS_PAE = 06 ;physical-address extensions +CAPS_MCE = 07 ;machine check exception +CAPS_CX8 = 08 ;CMPXCHG8B instruction +CAPS_APIC = 09 ;on-chip advanced programmable + ;interrupt controller +; 10 ;unused +CAPS_SEP = 11 ;SYSENTER and SYSEXIT instructions +CAPS_MTRR = 12 ;memory-type range registers +CAPS_PGE = 13 ;page global extension +CAPS_MCA = 14 ;machine check architecture +CAPS_CMOV = 15 ;conditional move instructions +CAPS_PAT = 16 ;page attribute table + +CAPS_PSE36 = 17 ;page-size extensions +CAPS_PSN = 18 ;processor serial number +CAPS_CLFLUSH = 19 ;CLFUSH instruction + +CAPS_DS = 21 ;debug store +CAPS_ACPI = 22 ;thermal monitor and software + ;controlled clock supported +CAPS_MMX = 23 ;MMX instructions +CAPS_FXSR = 24 ;FXSAVE and FXRSTOR instructions +CAPS_SSE = 25 ;SSE instructions +CAPS_SSE2 = 26 ;SSE2 instructions +CAPS_SS = 27 ;self-snoop +CAPS_HTT = 28 ;hyper-threading technology +CAPS_TM = 29 ;thermal monitor supported +CAPS_IA64 = 30 ;IA64 capabilities +CAPS_PBE = 31 ;pending break enable + +;ecx +CAPS_SSE3 = 32 ;SSE3 instructions +; 33 +; 34 +CAPS_MONITOR = 35 ;MONITOR/MWAIT instructions +CAPS_DS_CPL = 36 ; +CAPS_VMX = 37 ;virtual mode extensions +; 38 ; +CAPS_EST = 39 ;enhansed speed step +CAPS_TM2 = 40 ;thermal monitor2 supported +; 41 +CAPS_CID = 42 ; +; 43 +; 44 +CAPS_CX16 = 45 ;CMPXCHG16B instruction +CAPS_xTPR = 46 ; +CAPS_XSAVE = 32 + 26 ; XSAVE and XRSTOR instructions +CAPS_OSXSAVE = 32 + 27 +; A value of 1 indicates that the OS has set CR4.OSXSAVE[bit 18] to enable +; XSETBV/XGETBV instructions to access XCR0 and to support processor extended +; state management using XSAVE/XRSTOR. +CAPS_AVX = 32 + 28 ; not AVX2 +; +;reserved +; +;ext edx /ecx +CAPS_SYSCAL = 64 ; +CAPS_XD = 65 ;execution disable +CAPS_FFXSR = 66 ; +CAPS_RDTSCP = 67 ; +CAPS_X64 = 68 ; +CAPS_3DNOW = 69 ; +CAPS_3DNOWEXT = 70 ; +CAPS_LAHF = 71 ; +CAPS_CMP_LEG = 72 ; +CAPS_SVM = 73 ;secure virual machine +CAPS_ALTMOVCR8 = 74 ; + +; CPU MSR names +MSR_SYSENTER_CS = 0x174 +MSR_SYSENTER_ESP = 0x175 +MSR_SYSENTER_EIP = 0x176 +MSR_CR_PAT = 0x277 +MSR_MTRR_DEF_TYPE = 0x2FF + +MSR_AMD_EFER = 0xC0000080 ; Extended Feature Enable Register +MSR_AMD_STAR = 0xC0000081 ; SYSCALL/SYSRET Target Address Register + +CR0_PE = 0x00000001 ;protected mode +CR0_MP = 0x00000002 ;monitor fpu +CR0_EM = 0x00000004 ;fpu emulation +CR0_TS = 0x00000008 ;task switch +CR0_ET = 0x00000010 ;extension type hardcoded to 1 +CR0_NE = 0x00000020 ;numeric error +CR0_WP = 0x00010000 ;write protect +CR0_AM = 0x00040000 ;alignment check +CR0_NW = 0x20000000 ;not write-through +CR0_CD = 0x40000000 ;cache disable +CR0_PG = 0x80000000 ;paging + + +CR4_VME = 0x000001 +CR4_PVI = 0x000002 +CR4_TSD = 0x000004 +CR4_DE = 0x000008 +CR4_PSE = 0x000010 +CR4_PAE = 0x000020 +CR4_MCE = 0x000040 +CR4_PGE = 0x000080 +CR4_PCE = 0x000100 +CR4_OSFXSR = 0x000200 +CR4_OSXMMEXPT = 0x000400 +CR4_OSXSAVE = 0x040000 + +XCR0_FPU_MMX = 0x0001 +XCR0_SSE = 0x0002 +XCR0_AVX = 0x0004 +XCR0_MPX = 0x0018 +XCR0_AVX512 = 0x00e0 + +MXCSR_IE = 0x0001 +MXCSR_DE = 0x0002 +MXCSR_ZE = 0x0004 +MXCSR_OE = 0x0008 +MXCSR_UE = 0x0010 +MXCSR_PE = 0x0020 +MXCSR_DAZ = 0x0040 +MXCSR_IM = 0x0080 +MXCSR_DM = 0x0100 +MXCSR_ZM = 0x0200 +MXCSR_OM = 0x0400 +MXCSR_UM = 0x0800 +MXCSR_PM = 0x1000 +MXCSR_FZ = 0x8000 + +MXCSR_INIT = MXCSR_IM + MXCSR_DM + MXCSR_ZM + MXCSR_OM + MXCSR_UM + MXCSR_PM + +EFLAGS_CF = 0x000001 ; carry flag +EFLAGS_PF = 0x000004 ; parity flag +EFLAGS_AF = 0x000010 ; auxiliary flag +EFLAGS_ZF = 0x000040 ; zero flag +EFLAGS_SF = 0x000080 ; sign flag +EFLAGS_TF = 0x000100 ; trap flag +EFLAGS_IF = 0x000200 ; interrupt flag +EFLAGS_DF = 0x000400 ; direction flag +EFLAGS_OF = 0x000800 ; overflow flag +EFLAGS_IOPL = 0x003000 ; i/o priviledge level +EFLAGS_NT = 0x004000 ; nested task flag +EFLAGS_RF = 0x010000 ; resume flag +EFLAGS_VM = 0x020000 ; virtual 8086 mode flag +EFLAGS_AC = 0x040000 ; alignment check flag +EFLAGS_VIF = 0x080000 ; virtual interrupt flag +EFLAGS_VIP = 0x100000 ; virtual interrupt pending +EFLAGS_ID = 0x200000 ; id flag + +IRQ_PIC = 0 +IRQ_APIC = 1 + +struct TSS + _back rw 2 + _esp0 rd 1 + _ss0 rw 2 + _esp1 rd 1 + _ss1 rw 2 + _esp2 rd 1 + _ss2 rw 2 + _cr3 rd 1 + _eip rd 1 + _eflags rd 1 + _eax rd 1 + _ecx rd 1 + _edx rd 1 + _ebx rd 1 + _esp rd 1 + _ebp rd 1 + _esi rd 1 + _edi rd 1 + _es rw 2 + _cs rw 2 + _ss rw 2 + _ds rw 2 + _fs rw 2 + _gs rw 2 + _ldt rw 2 + _trap rw 1 + _io rw 1 + rb 24 + _io_map_0 rb 4096 + _io_map_1 rb 4096 +ends + +DRIVE_DATA_SIZE = 16 + +OS_BASE = 0x80000000 + +window_data = OS_BASE + 0x0001000 + +TASK_TABLE = OS_BASE + 0x0003000 +;CURRENT_TASK = OS_BASE + 0x0003000 +;TASK_COUNT = OS_BASE + 0x0003004 +TASK_BASE = OS_BASE + 0x0003010 +TASK_DATA = OS_BASE + 0x0003020 +;TASK_EVENT = OS_BASE + 0x0003020 + +CDDataBuf = OS_BASE + 0x0005000 + +;unused 0x6000 - 0x8fff + +BOOT_VARS = 0x9000 + +idts = OS_BASE + 0x000B100 +WIN_STACK = OS_BASE + 0x000C000 +WIN_POS = OS_BASE + 0x000C400 +FDD_BUFF = OS_BASE + 0x000D000 ;512 + +WIN_TEMP_XY = OS_BASE + 0x000F300 +KEY_COUNT = OS_BASE + 0x000F400 +KEY_BUFF = OS_BASE + 0x000F401 ; 120*2 + 2*2 = 244 bytes, actually 255 bytes + +BTN_COUNT = OS_BASE + 0x000F500 +BTN_BUFF = OS_BASE + 0x000F501 + + +BTN_ADDR = OS_BASE + 0x000FE88 +MEM_AMOUNT = OS_BASE + 0x000FE8C + +SYS_SHUTDOWN = OS_BASE + 0x000FF00 + + +TMP_STACK_TOP = 0x007CC00 + +sys_proc = OS_BASE + 0x007E000 + +SLOT_BASE = OS_BASE + 0x0080000 + +VGABasePtr = OS_BASE + 0x00A0000 + +virtual at OS_BASE + 0x05FFF80 + tss TSS +end virtual + +HEAP_BASE = OS_BASE + 0x0800000 +HEAP_MIN_SIZE = 0x01000000 + +page_tabs = 0xFDC00000 +app_page_tabs = 0xFDC00000 +kernel_tabs = page_tabs + (OS_BASE shr 10) ;0xFDE00000 +master_tab = page_tabs + (page_tabs shr 10) ;0xFDFF70000 + +LFB_BASE = 0xFE000000 + + +new_app_base = 0; + +twdw = TASK_TABLE - window_data + +std_application_base_address = new_app_base +RING0_STACK_SIZE = 0x2000 + +REG_SS = RING0_STACK_SIZE - 4 +REG_APP_ESP = RING0_STACK_SIZE - 8 +REG_EFLAGS = RING0_STACK_SIZE - 12 +REG_CS = RING0_STACK_SIZE - 16 +REG_EIP = RING0_STACK_SIZE - 20 +REG_EAX = RING0_STACK_SIZE - 24 +REG_ECX = RING0_STACK_SIZE - 28 +REG_EDX = RING0_STACK_SIZE - 32 +REG_EBX = RING0_STACK_SIZE - 36 +REG_ESP = RING0_STACK_SIZE - 40 ;RING0_STACK_SIZE-20 +REG_EBP = RING0_STACK_SIZE - 44 +REG_ESI = RING0_STACK_SIZE - 48 +REG_EDI = RING0_STACK_SIZE - 52 +REG_RET = RING0_STACK_SIZE - 56 ;irq0.return + + +PAGE_SIZE = 4096 + +PG_UNMAP = 0x000 +PG_READ = 0x001 +PG_WRITE = 0x002 +PG_USER = 0x004 +PG_PCD = 0x008 +PG_PWT = 0x010 +PG_ACCESSED = 0x020 +PG_DIRTY = 0x040 +PG_PAT = 0x080 +PG_GLOBAL = 0x100 +PG_SHARED = 0x200 + +PG_SWR = 0x003 ; PG_WRITE + PG_READ +PG_UR = 0x005 ; PG_USER + PG_READ +PG_UWR = 0x007 ; PG_USER + PG_WRITE + PG_READ +PG_NOCACHE = 0x018 ; PG_PCD + PG_PWT + +PDE_LARGE = 0x080 + +MEM_WB = 6 ; write-back memory +MEM_WC = 1 ; write combined memory +MEM_UC = 0 ; uncached memory + +PAT_WB = 0x000 +PAT_WC = 0x008 +PAT_UCM = 0x010 +PAT_UC = 0x018 + +PAT_TYPE_UC = 0 +PAT_TYPE_WC = 1 +PAT_TYPE_WB = 6 +PAT_TYPE_UCM = 7 + +PAT_VALUE = 0x00070106; (UC<<24)|(UCM<<16)|(WC<<8)|WB + +MAX_MEMMAP_BLOCKS = 32 + +EVENT_REDRAW = 0x00000001 +EVENT_KEY = 0x00000002 +EVENT_BUTTON = 0x00000004 +EVENT_BACKGROUND = 0x00000010 +EVENT_MOUSE = 0x00000020 +EVENT_IPC = 0x00000040 +EVENT_NETWORK = 0x00000080 +EVENT_DEBUG = 0x00000100 +EVENT_NETWORK2 = 0x00000200 +EVENT_EXTENDED = 0x00000400 + +EV_INTR = 1 + +STDIN_FILENO = 0 +STDOUT_FILENO = 1 +STDERR_FILENO = 2 + +SYSTEM_SHUTDOWN = 2 +SYSTEM_REBOOT = 3 +SYSTEM_RESTART = 4 + +BLIT_CLIENT_RELATIVE = 0x20000000 + +struct SYSCALL_STACK + _eip dd ? + _edi dd ? ; +4 + _esi dd ? ; +8 + _ebp dd ? ; +12 + _esp dd ? ; +16 + _ebx dd ? ; +20 + _edx dd ? ; +24 + _ecx dd ? ; +28 + _eax dd ? ; +32 +ends + +struct LHEAD + next dd ? ;next object in list + prev dd ? ;prev object in list +ends + +struct MUTEX_WAITER + list LHEAD + task dd ? + type dd ? +ends + +struct MUTEX + wait_list LHEAD + count dd ? +ends + +struct RWSEM + wait_list LHEAD + count dd ? +ends + +struct FUTEX + list LHEAD + magic dd ? + handle dd ? + destroy dd ? + + wait_list LHEAD + pointer dd ? + flags dd ? +ends + +FUTEX_INIT = 0 +FUTEX_DESTROY = 1 +FUTEX_WAIT = 2 +FUTEX_WAKE = 3 + +struct FILED + list LHEAD + magic rd 1 + handle rd 1 + destroy rd 1 + mode rd 1 + file rd 1 +ends + +struct PIPE + pipe_ops rd 1 + buffer rd 1 + readers rd 1 + writers rd 1 + + pipe_lock MUTEX + count rd 1 + + read_end rd 1 + write_end rd 1 + rlist LHEAD + wlist LHEAD +ends + +struct PROC + list LHEAD + thr_list LHEAD + heap_lock MUTEX + heap_base rd 1 + heap_top rd 1 + mem_used rd 1 + dlls_list_ptr rd 1 + pdt_0_phys rd 1 + pdt_1_phys rd 1 + io_map_0 rd 1 + io_map_1 rd 1 + + ht_lock rd 1 + ht_free rd 1 ;htab[0] stdin + ht_next rd 1 ;htab[1] stdout + htab rd 1024-PROC.htab/4 ;htab[2] stderr + pdt_0 rd 1024 +ends + +struct DBG_REGS + dr0 dd ? + dr1 dd ? + dr2 dd ? + dr3 dd ? + dr7 dd ? +ends + +struct POINT + x dd ? + y dd ? +ends + +struct RECT + left dd ? + top dd ? + right dd ? + bottom dd ? +ends + +struct BOX + left dd ? + top dd ? + width dd ? + height dd ? +ends + +; Fields, marked as R now not used, but will be used soon, +; when legacy TASKDATA structure will be deleted +struct APPDATA + app_name rb 11 + rb 5 + + list LHEAD ;+16 + process dd ? ;+24 + fpu_state dd ? ;+28 + exc_handler dd ? ;+32 + except_mask dd ? ;+36 + pl0_stack dd ? ;+40 + cursor dd ? ;+44 + fd_ev dd ? ;+48 + bk_ev dd ? ;+52 + fd_obj dd ? ;+56 + bk_obj dd ? ;+60 + saved_esp dd ? ;+64 + io_map rd 2 ;+68 + dbg_state dd ? ;+76 + cur_dir dd ? ;+80 + wait_timeout dd ? ;+84 + saved_esp0 dd ? ;+88 + wait_begin dd ? ;+92 +++ + wait_test dd ? ;+96 +++ + wait_param dd ? ;+100 +++ + tls_base dd ? ;+104 + event_mask dd ? ;+108 ; R stores event types allowed for task + tid dd ? ;+112 ; R thread id + draw_bgr_x dd ? ;+116 + draw_bgr_y dd ? ;+120 + state db ? ;+124 ; R thread state + db ? ;+125 + dw ? ;+126 + wnd_shape dd ? ;+128 + wnd_shape_scale dd ? ;+132 + mem_start dd ? ;+136 ; R + counter_sum dd ? ;+140 ; R + saved_box BOX ;+144 + ipc_start dd ? ;+160 + ipc_size dd ? ;+164 + occurred_events dd ? ;+168 ; mask which accumulates occurred events + debugger_slot dd ? ;+172 + terminate_protection dd ? ;+176 + keyboard_mode db ? ;+180 + captionEncoding db ? + rb 2 + exec_params dd ? ;+184 + dbg_event_mem dd ? ;+188 + dbg_regs DBG_REGS ;+192 + wnd_caption dd ? ;+212 + wnd_clientbox BOX ;+216 + priority dd ? ;+232 + in_schedule LHEAD ;+236 + counter_add dd ? ;+244 ; R + cpu_usage dd ? ;+248 ; R + dd ? ;+252 +ends + +assert sizeof.APPDATA = 256 + +APP_OBJ_OFFSET = 48 +APP_EV_OFFSET = 40 + +; Note: in future TASKDATA will be merged into APPDATA +struct TASKDATA + event_mask dd ? ;+0 mask which stores event types allowed for task + pid dd ? ;+4 + dw ? ;+8 + state db ? ;+10 + db ? ;+11 + dw ? ;+12 + wnd_number db ? ;+14 + db ? ;+15 + mem_start dd ? ;+16 + counter_sum dd ? ;+20 + counter_add dd ? ;+24 + cpu_usage dd ? ;+28 +ends + +; Thread states: +TSTATE_RUNNING = 0 +TSTATE_RUN_SUSPENDED = 1 +TSTATE_WAIT_SUSPENDED = 2 +TSTATE_ZOMBIE = 3 +TSTATE_TERMINATING = 4 +TSTATE_WAITING = 5 +TSTATE_FREE = 9 + +; Window constants: +WSTATE_NORMAL = 00000000b +WSTATE_MAXIMIZED = 00000001b +WSTATE_MINIMIZED = 00000010b +WSTATE_ROLLEDUP = 00000100b + +WSTATE_REDRAW = 00000001b +WSTATE_WNDDRAWN = 00000010b + +WSTYLE_HASCAPTION = 00010000b +WSTYLE_CLIENTRELATIVE = 00100000b + +ZPOS_DESKTOP = -2 +ZPOS_ALWAYS_BACK = -1 +ZPOS_NORMAL = 0 +ZPOS_ALWAYS_TOP = 1 ;ZPOS_ALWAYS_TOP is always last and has max number! + +; Window structure: +struct WDATA + box BOX + cl_workarea dd ? + cl_titlebar dd ? + cl_frames dd ? + z_modif db ? + fl_wstate db ? + fl_wdrawn db ? + fl_redraw db ? +ends + +label WDATA.fl_wstyle byte at WDATA.cl_workarea + 3 + +assert sizeof.WDATA = 32 + +struct SYS_VARS + bpp dd ? + scanline dd ? + vesa_mode dd ? + x_res dd ? + y_res dd ? +ends + +struct APPOBJ ; common object header + magic dd ? ; + destroy dd ? ; internal destructor + fd dd ? ; next object in list + bk dd ? ; prev object in list + pid dd ? ; owner id +ends + +struct CURSOR APPOBJ + base dd ? ;allocated memory + hot_x dd ? ;hotspot coords + hot_y dd ? + + list_next dd ? ;next cursor in cursor list + list_prev dd ? ;prev cursor in cursor list + dev_obj dd ? ;device depended data +ends + + +struct EVENT APPOBJ + id dd ? ;event uid + state dd ? ;internal flags + code dd ? + rd 5 +ends + + +struct SMEM + bk dd ? + fd dd ? ;+4 + base dd ? ;+8 + size dd ? ;+12 + access dd ? ;+16 + refcount dd ? ;+20 + name rb 32 ;+24 +ends + +struct SMAP APPOBJ + base dd ? ;mapped base + parent dd ? ;SMEM +ends + +struct DLLDESCR + bk dd ? + fd dd ? ;+4 + data dd ? ;+8 + size dd ? ;+12 + timestamp dq ? + refcount dd ? + defaultbase dd ? + coff_hdr dd ? + symbols_ptr dd ? + symbols_num dd ? + symbols_lim dd ? + exports dd ? ;export table + name rb 260 +ends + +struct HDLL + fd dd ? ;next object in list + bk dd ? ;prev object in list + pid dd ? ;owner id + + base dd ? ;mapped base + size dd ? ;mapped size + refcount dd ? ;reference counter for this process and this lib + parent dd ? ;DLLDESCR +ends + +struct DQ + lo dd ? + hi dd ? +ends + +struct e820entry + addr DQ ? + size DQ ? + type dd ? +ends + +RD_LOAD_FROM_FLOPPY = 1 +RD_LOAD_FROM_HD = 2 +RD_LOAD_FROM_MEMORY = 3 +RD_LOAD_FROM_FORMAT = 4 +RD_LOAD_FROM_NONE = 5 + +struct boot_data + bpp db ? ; bits per pixel + pitch dw ? ; scanline length + db ? + dd ? + vesa_mode dw ? + x_res dw ? + y_res dw ? + dw ? + dd ? + bank_switch dd ? ; Vesa 1.2 pm bank switch + lfb dd ? ; Vesa 2.0 LFB address + mtrr db ? ; 0 or 1: enable MTRR graphics acceleration + launcher_start db ? ; 0 or 1: start the first app (right now it's + ; LAUNCHER) after kernel is loaded + debug_print db ? ; if nonzero, duplicates debug output to the screen + dma db ? ; DMA write: 1=yes, 2=no + pci_data rb 8 + rb 8 + shutdown_type db ? ; see sysfn 18.9 + rb 15 + apm_entry dd ? ; entry point of APM BIOS + apm_version dw ? ; BCD + apm_flags dw ? + rb 8 + apm_code_32 dw ? + apm_code_16 dw ? + apm_data_16 dw ? + rd_load_from db ? ; Device to load ramdisk from, RD_LOAD_FROM_* + db ? + kernel_restart dw ? + sys_disk dw ? ; Device to mount on /sys/, see loader_doc.txt for details + acpi_rsdp dd ? + syspath rb 0x17 + devicesdat_data dd ? + devicesdat_size dd ? + bios_hd_cnt db ? ; number of BIOS hard disks + bios_hd rb 0x80 ; BIOS hard disks + memmap_block_cnt dd ? ; available physical memory map: number of blocks + memmap_blocks e820entry + rb sizeof.e820entry * (MAX_MEMMAP_BLOCKS - 1) +ends + +virtual at BOOT_VARS + BOOT_LO boot_data +end virtual +virtual at OS_BASE + BOOT_VARS + BOOT boot_data +end virtual + +MAX_SCREEN_WIDTH = 3840 +MAX_SCREEN_HEIGHT = 2160 + +struct display_t + x dd ? + y dd ? + width dd ? + height dd ? + bits_per_pixel dd ? + vrefresh dd ? + current_lfb dd ? + lfb_pitch dd ? + + win_map_lock RWSEM + win_map dd ? + win_map_pitch dd ? + win_map_size dd ? + + modes dd ? + ddev dd ? + connector dd ? + crtc dd ? + + cr_list.next dd ? + cr_list.prev dd ? + + cursor dd ? + + init_cursor dd ? + select_cursor dd ? + show_cursor dd ? + move_cursor dd ? + restore_cursor dd ? + disable_mouse dd ? + mask_seqno dd ? + check_mouse dd ? + check_m_pixel dd ? + + bytes_per_pixel dd ? +ends + +struct DISPMODE + width dw ? + height dw ? + bpp dw ? + freq dw ? +ends + + +struct PCIDEV + bk dd ? + fd dd ? + vendor_device_id dd ? + class dd ? + devfn db ? + bus db ? + rb 2 + owner dd ? ; pointer to SRV or 0 +ends + +struct IDE_DATA + ProgrammingInterface dd ? + Interrupt dw ? + RegsBaseAddres dw ? + BAR0_val dw ? + BAR1_val dw ? + BAR2_val dw ? + BAR3_val dw ? + dma_hdd_channel_1 db ? + dma_hdd_channel_2 db ? + pcidev dd ? ; pointer to corresponding PCIDEV structure +ends + +struct IDE_CACHE + pointer dd ? + size dd ? ; not use + data_pointer dd ? + system_data_size dd ? ; not use + appl_data_size dd ? ; not use + system_data dd ? + appl_data dd ? + system_sad_size dd ? + appl_sad_size dd ? + search_start dd ? + appl_search_start dd ? +ends + +struct IDE_DEVICE + UDMA_possible_modes db ? + UDMA_set_mode db ? +ends + +; The following macro assume that we are on uniprocessor machine. +; Serious work is needed for multiprocessor machines. +macro spin_lock_irqsave spinlock +{ + pushf + cli +} +macro spin_unlock_irqrestore spinlock +{ + popf +} +macro spin_lock_irq spinlock +{ + cli +} +macro spin_unlock_irq spinlock +{ + sti +} + +struct MEM_STATE + mutex MUTEX + smallmap dd ? + treemap dd ? + topsize dd ? + top dd ? + smallbins rd 4*32 + treebins rd 32 +ends + +struct PG_DATA + mem_amount dd ? + vesa_mem dd ? + pages_count dd ? + pages_free dd ? + pages_faults dd ? + pagemap_size dd ? + kernel_pages dd ? + kernel_tables dd ? + sys_page_dir dd ? + mutex MUTEX +ends + +struct SRV + srv_name rb 16 ;ASCIIZ string + magic dd ? ;+0x10 ;'SRV ' + size dd ? ;+0x14 ;size of structure SRV + fd dd ? ;+0x18 ;next SRV descriptor + bk dd ? ;+0x1C ;prev SRV descriptor + base dd ? ;+0x20 ;service base address + entry dd ? ;+0x24 ;service START function + srv_proc dd ? ;+0x28 ;user mode service handler + srv_proc_ex dd ? ;+0x2C ;kernel mode service handler +ends + +struct USBSRV + srv SRV + usb_func dd ? +ends + +struct USBFUNC + strucsize dd ? + add_device dd ? + device_disconnect dd ? +ends + +DRV_ENTRY = 1 +DRV_EXIT = -1 + +struct COFF_HEADER + machine dw ? + nSections dw ? + DataTime dd ? + pSymTable dd ? + nSymbols dd ? + optHeader dw ? + flags dw ? +ends + +struct COFF_SECTION + Name rb 8 + VirtualSize dd ? + VirtualAddress dd ? + SizeOfRawData dd ? + PtrRawData dd ? + PtrReloc dd ? + PtrLinenumbers dd ? + NumReloc dw ? + NumLinenum dw ? + Characteristics dd ? +ends + +struct COFF_RELOC + VirtualAddress dd ? + SymIndex dd ? + Type dw ? +ends + +struct COFF_SYM + Name rb 8 + Value dd ? + SectionNumber dw ? + Type dw ? + StorageClass db ? + NumAuxSymbols db ? +ends + +struct STRIPPED_PE_HEADER + Signature dw ? + Characteristics dw ? + AddressOfEntryPoint dd ? + ImageBase dd ? + SectionAlignmentLog db ? + FileAlignmentLog db ? + MajorOSVersion db ? + MinorOSVersion db ? + SizeOfImage dd ? + SizeOfStackReserve dd ? + SizeOfHeapReserve dd ? + SizeOfHeaders dd ? + Subsystem db ? + NumberOfRvaAndSizes db ? + NumberOfSections dw ? +ends +STRIPPED_PE_SIGNATURE = 0x4503 ; 'PE' xor 'S' +SPE_DIRECTORY_IMPORT = 0 +SPE_DIRECTORY_EXPORT = 1 +SPE_DIRECTORY_BASERELOC = 2 + +struct IOCTL + handle dd ? + io_code dd ? + input dd ? + inp_size dd ? + output dd ? + out_size dd ? +ends + +struct IRQH + list LHEAD + handler dd ? ;handler roututine + data dd ? ;user-specific data + num_ints dd ? ;how many times handled +ends + diff --git a/kernel/branches/kolibri-lldw/core/Tupfile.lua b/kernel/branches/kolibri-lldw/core/Tupfile.lua new file mode 100644 index 000000000..7e1761c88 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/Tupfile.lua @@ -0,0 +1,3 @@ +if tup.getconfig("NO_FASM") ~= "" then return end +tup.rule("mtrrtest.asm", "fasm %f %o", "mtrrtest.exe") +tup.rule("test_malloc.asm", "fasm %f %o", "test_malloc") diff --git a/kernel/branches/kolibri-lldw/core/apic.inc b/kernel/branches/kolibri-lldw/core/apic.inc new file mode 100644 index 000000000..21fa3c4b3 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/apic.inc @@ -0,0 +1,567 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +MAX_IOAPICS = 2 + +uglobal +IRQ_COUNT rd MAX_IOAPICS +irq_mode rd 1 ; PIC/(IO)APIC +IOAPIC_base rd MAX_IOAPICS +ioapic_gsi_base rd MAX_IOAPICS ; zero-based, i.e. not vector +ioapic_cnt dd ? ; from MADT aka APIC table +ioapic_cur dd ? +LAPIC_BASE rd 1 +endg + +APIC_ID = 0x20 +APIC_TPR = 0x80 +APIC_EOI = 0xb0 +APIC_LDR = 0xd0 +APIC_DFR = 0xe0 +APIC_SVR = 0xf0 +APIC_ISR = 0x100 +APIC_ESR = 0x280 +APIC_ICRL = 0x300 +APIC_ICRH = 0x310 +APIC_LVT_LINT0 = 0x350 +APIC_LVT_LINT1 = 0x360 +APIC_LVT_err = 0x370 + +; APIC timer +APIC_LVT_timer = 0x320 +APIC_timer_div = 0x3e0 +APIC_timer_init = 0x380 +APIC_timer_cur = 0x390 +; IOAPIC +IOAPIC_ID = 0x0 +IOAPIC_VER = 0x1 +IOAPIC_ARB = 0x2 +IOAPIC_REDTBL = 0x10 + +align 4 +APIC_init: + push ebx + mov [irq_mode], IRQ_PIC + + cmp [acpi_ioapic_base], 0 + jz .no_apic + + cmp [acpi_lapic_base], 0 + jz .no_apic + +; non-UEFI loaders don't load DEVICES.DAT and don't initialize [acpi_dev_size] +if defined UEFI + cmp [acpi_dev_size], 0 + jz @f + stdcall map_io_mem, [acpi_dev_data], [acpi_dev_size], PG_SWR + mov [acpi_dev_data], eax + jmp .loaded +@@: +end if + + stdcall load_file, dev_data_path + test eax, eax + jz .no_apic + + mov [acpi_dev_data], eax + mov [acpi_dev_size], ebx +.loaded: + +; IOAPIC init + xor ebx, ebx +@@: + stdcall map_io_mem, [acpi_ioapic_base+ebx*4], 0x20, PG_GLOBAL+PG_NOCACHE+PG_SWR + mov [IOAPIC_base+ebx*4], eax + inc ebx + cmp ebx, [ioapic_cnt] + jnz @b + + call IRQ_mask_all + mov [ioapic_cur], 0 +.next_ioapic: + mov edx, [ioapic_cur] + mov eax, IOAPIC_VER + call IOAPIC_read + shr eax, 16 + inc al + movzx eax, al + mov ecx, [ioapic_gsi_base+edx*4] + cmp ecx, IRQ_RESERVED + jae .lapic_init + add ecx, eax + sub ecx, IRQ_RESERVED + jbe @f + sub eax, ecx +@@: + mov [IRQ_COUNT+edx*4], eax + + ; Reroute IOAPIC & mask all interrupts + xor ecx, ecx + mov eax, IOAPIC_REDTBL +.next_irq: + mov ebx, eax + call IOAPIC_read + mov ah, 0x08; Delivery Mode: Fixed, Destination Mode: Logical + mov al, cl + add al, 0x20; vector + add eax, [ioapic_gsi_base+edx*4] + or eax, 0x1a000; Mask Interrupt, level-triggered active-low + cmp [ioapic_cur], 0 + jnz @f + cmp ecx, 16 + jae @f + and eax, NOT 0xa000 ; edge-triggered active-high for IRQ0-15 +@@: + xchg eax, ebx + call IOAPIC_write + inc eax + mov ebx, eax + call IOAPIC_read + or eax, 0xff000000; Destination Field + xchg eax, ebx + call IOAPIC_write + inc eax + inc ecx + cmp ecx, [IRQ_COUNT+edx*4] + jb .next_irq + + inc [ioapic_cur] + inc edx + cmp edx, [ioapic_cnt] + jnz .next_ioapic + +.lapic_init: + call LAPIC_init + + mov [irq_mode], IRQ_APIC + + mov al, 0x70 + out 0x22, al + mov al, 1 + out 0x23, al + + call pci_irq_fixup +.no_apic: + pop ebx + ret + +;=========================================================== +align 4 +LAPIC_init: + + mov eax, [LAPIC_BASE] + test eax, eax + jnz @f + stdcall map_io_mem, [acpi_lapic_base], 0x1000, PG_GLOBAL+PG_NOCACHE+PG_SWR + mov [LAPIC_BASE], eax +@@: + mov esi, eax + + ; Program Destination Format Register for Flat mode. + mov eax, [esi + APIC_DFR] + or eax, 0xf0000000 + mov [esi + APIC_DFR], eax + + ; Program Logical Destination Register. + mov eax, [esi + APIC_LDR] + ;and eax, 0xff000000 + and eax, 0x00ffffff + or eax, 0x01000000;!!!!!!!!!!!! + mov [esi + APIC_LDR], eax + + ; Task Priority Register initialization. + mov eax, [esi + APIC_TPR] + and eax, 0xffffff00 + mov [esi + APIC_TPR], eax + + ; Flush the queue + mov edx, 0 +.nxt2: + mov ecx, 32 + mov eax, [esi + APIC_ISR + edx] +.nxt: + shr eax, 1 + jnc @f + mov dword [esi + APIC_EOI], 0; EOI +@@: + loop .nxt + + add edx, 0x10 + cmp edx, 0x170 + jbe .nxt2 + + ; Spurious-Interrupt Vector Register initialization. + mov eax, [esi + APIC_SVR] + or eax, 0x1ff + and eax, 0xfffffdff + mov [esi + APIC_SVR], eax + + ; Initialize LVT LINT0 register. (INTR) + mov eax, 0x00700 + ; mov eax, 0x10700 + mov [esi + APIC_LVT_LINT0], eax + + ; Initialize LVT LINT1 register. (NMI) + mov eax, 0x00400 + mov [esi + APIC_LVT_LINT1], eax + + ; Initialize LVT Error register. + mov eax, [esi + APIC_LVT_err] + or eax, 0x10000; bit 16 + mov [esi + APIC_LVT_err], eax + + ; LAPIC timer + ; pre init + mov dword[esi + APIC_timer_div], 1011b; 1 + mov dword[esi + APIC_timer_init], 0xffffffff; max val + push esi + mov esi, 640 ; wait 0.64 sec + call delay_ms + pop esi + mov eax, [esi + APIC_timer_cur]; read current tick couner + xor eax, 0xffffffff ; eax = 0xffffffff - eax + shr eax, 6 ; eax /= 64; APIC ticks per 0.01 sec + + ; Start (every 0.01 sec) + mov dword[esi + APIC_LVT_timer], 0x30020; periodic int 0x20 + mov dword[esi + APIC_timer_init], eax + +.done: + ret + +;=========================================================== +; IOAPIC implementation +align 4 +IOAPIC_read: +; in : EAX - IOAPIC register +; out: EAX - readed value + push esi + mov esi, [ioapic_cur] + mov esi, [IOAPIC_base+esi*4] + mov [esi], eax + mov eax, [esi + 0x10] + pop esi + ret + +align 4 +IOAPIC_write: +; in : EAX - IOAPIC register +; EBX - value +; out: none + push esi + mov esi, [ioapic_cur] + mov esi, [IOAPIC_base+esi*4] + mov [esi], eax + mov [esi + 0x10], ebx + pop esi + ret +;=========================================================== +; Remap all IRQ to 0x20+ Vectors +; IRQ0 to vector 0x20, IRQ1 to vector 0x21.... +align 4 +PIC_init: + mov [IRQ_COUNT], 16 + cli + mov al, 0x11 ; icw4, edge triggered + out 0x20, al + out 0xA0, al + + mov al, 0x20 ; generate 0x20 + + out 0x21, al + mov al, 0x28 ; generate 0x28 + + out 0xA1, al + + mov al, 0x04 ; slave at irq2 + out 0x21, al + mov al, 0x02 ; at irq9 + out 0xA1, al + + mov al, 0x01 ; 8086 mode + out 0x21, al + out 0xA1, al + + call IRQ_mask_all + ret + +; ----------------------------------------- +; TIMER SET TO 1/100 S +align 4 +PIT_init: + mov al, 0x34 ; set to 100Hz + out 0x43, al + mov al, 0x9b ; lsb 1193180 / 1193 + out 0x40, al + mov al, 0x2e ; msb + out 0x40, al + ret + +; ----------------------------------------- +align 4 +unmask_timer: + cmp [irq_mode], IRQ_APIC + je @f + + stdcall enable_irq, 0 + ret +@@: + ; use PIT + ; in some systems PIT no connected to IOAPIC + ; mov eax, 0x14 + ; call IOAPIC_read + ; mov ah, 0x09 ; Delivery Mode: Lowest Priority, Destination Mode: Logical + ; mov al, 0x20 + ; or eax, 0x10000 ; Mask Interrupt + ; mov ebx, eax + ; mov eax, 0x14 + ; call IOAPIC_write + ; stdcall enable_irq, 2 + ; ret + + ; use LAPIC timer + mov esi, [LAPIC_BASE] + mov eax, [esi + APIC_LVT_timer] + and eax, 0xfffeffff + mov [esi + APIC_LVT_timer], eax + ret + +; ----------------------------------------- +; Disable all IRQ +align 4 +IRQ_mask_all: + cmp [irq_mode], IRQ_APIC + je .APIC + + mov al, 0xFF + out 0x21, al + out 0xA1, al + mov ecx, 0x1000 + ret +.APIC: + cmp [IOAPIC_base], 0 + jz .done + mov [ioapic_cur], 0 +.next_ioapic: + mov edx, [ioapic_cur] + mov ecx, [IRQ_COUNT+edx*4] + mov eax, 0x10 +@@: + mov ebx, eax + call IOAPIC_read + or eax, 0x10000; bit 16 + xchg eax, ebx + call IOAPIC_write + inc eax + inc eax + loop @b + + inc [ioapic_cur] + inc edx + cmp edx, [ioapic_cnt] + jnz .next_ioapic +.done: + ret + +; ----------------------------------------- +; End Of Interrupt +; cl - IRQ number +align 4 +irq_eoi: ; __fastcall + cmp [irq_mode], IRQ_APIC + je .APIC + + cmp cl, 8 + mov al, 0x20 + jb @f + out 0xa0, al +@@: + out 0x20, al + ret + +.APIC: + mov eax, [LAPIC_BASE] + mov dword [eax + APIC_EOI], 0; EOI + ret + +; ----------------------------------------- +; from dll.inc +align 4 +proc enable_irq stdcall, irq_line:dword + mov ebx, [irq_line] + cmp [irq_mode], IRQ_APIC + je .APIC + + mov edx, 0x21 + cmp ebx, 8 + jb @F + + mov edx, 0xA1 + sub ebx, 8 +@@: + in al, dx + btr eax, ebx + out dx, al + ret +.APIC: + push [ioapic_cur] + xor eax, eax +.next_ioapic: + mov ecx, [ioapic_gsi_base+eax*4] + add ecx, [IRQ_COUNT+eax*4] + cmp ebx, ecx + jb .found + inc eax + cmp eax, [ioapic_cnt] + jnz .next_ioapic + jmp .done +.found: + mov [ioapic_cur], eax + sub ebx, [ioapic_gsi_base+eax*4] + shl ebx, 1 + add ebx, 0x10 + mov eax, ebx + call IOAPIC_read + and eax, 0xfffeffff; bit 16 + xchg eax, ebx + call IOAPIC_write +.done: + pop [ioapic_cur] + ret +endp + +proc disable_irq stdcall, irq_line:dword + mov ebx, [irq_line] + cmp [irq_mode], IRQ_APIC + je .APIC + + mov edx, 0x21 + cmp ebx, 8 + jb @F + + mov edx, 0xA1 + sub ebx, 8 +@@: + in al, dx + bts eax, ebx + out dx, al + ret +.APIC: + push [ioapic_cur] + xor eax, eax +.next_ioapic: + mov ecx, [ioapic_gsi_base+eax*4] + add ecx, [IRQ_COUNT+eax*4] + cmp ebx, ecx + jae .found + inc eax + cmp eax, [ioapic_cnt] + jnz .next_ioapic + jmp .done +.found: + mov [ioapic_cur], eax + sub ebx, [ioapic_gsi_base+eax*4] + shl ebx, 1 + add ebx, 0x10 + mov eax, ebx + call IOAPIC_read + or eax, 0x10000; bit 16 + xchg eax, ebx + call IOAPIC_write +.done: + pop [ioapic_cur] + ret +endp + +align 4 +pci_irq_fixup: + + push ebp + + mov esi, [acpi_dev_data] + mov ebx, [acpi_dev_size] + + lea edi, [esi+ebx] + +.iterate: + + cmp esi, edi + jae .done + + mov eax, [esi] + + cmp eax, -1 + je .done + + movzx ebx, al + movzx ebp, ah + + stdcall pci_read32, ebp, ebx, 0 + + cmp eax, [esi+4] + jne .skip + + mov eax, [esi+8] + stdcall pci_write8, ebp, ebx, 0x3C, eax +.skip: + add esi, 16 + jmp .iterate + +.done: +.fail: + pop ebp + ret + +align 4 +get_clock_ns: + + mov eax, [hpet_base] + test eax, eax + jz .old_tics + + push ebx + push esi + pushfd + cli + + mov ebx, eax +@@: + mov edx, [ebx+0xF4] + mov eax, [ebx+0xF0] + mov ecx, [ebx+0xF4] + cmp ecx, edx + jne @B + popfd + +;96-bit arithmetic +;ebx - low dword +;esi - medium dword +;edx - high dword + + mul [hpet_period] + mov ebx, eax + mov esi, edx + + mov eax, ecx + mul [hpet_period] + add esi, eax + adc edx, 0 + mov eax, ebx + mov ebx, esi + shrd eax, ebx, 10 + shrd esi, edx, 10 + mov edx, esi + + pop esi + pop ebx + ret + +.old_tics: + mov eax, [timer_ticks] + mov edx, 10000000 + mul edx + ret diff --git a/kernel/branches/kolibri-lldw/core/clipboard.inc b/kernel/branches/kolibri-lldw/core/clipboard.inc new file mode 100644 index 000000000..ffb70abbe --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/clipboard.inc @@ -0,0 +1,167 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;------------------------------------------------------------------------------ +align 4 +sys_clipboard: + xor eax, eax + dec eax +; check availability of main list + cmp [clipboard_main_list], eax + je .exit_1 ; main list area not found + + test ebx, ebx ; 0 - Get the number of slots in the clipboard + jnz .1 +; get the number of slots + mov eax, [clipboard_slots] + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.1: + dec ebx ; 1 - Read the data from the clipboard + jnz .2 +; verify the existence of slot + cmp ecx, [clipboard_slots] + jae .exit_2 +; get a pointer to the data of slot + shl ecx, 2 + add ecx, [clipboard_main_list] + mov esi, [ecx] + mov ecx, [esi] +; allocate memory for application for copy the data of slots + push ecx + stdcall user_alloc, ecx + pop ecx +; copying data of slots + mov edi, eax + cld + rep movsb + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.2: + dec ebx ; 2 - Write the data to the clipboard + jnz .3 + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + stdcall is_region_userspace, edx, ecx + jz @f + mov eax, -1 + jmp .exit_1 +@@: +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + jne .exit_2 +; lock last slot + inc eax + mov [ebx], eax +; check the overflow pointer of slots + cmp [clipboard_slots], 1024 + jae .exit_3 +; get memory for new slot + push ebx ecx edx + stdcall kernel_alloc, ecx + pop edx ecx ebx + test eax, eax + jz .exit_3 +; create a new slot + mov edi, eax + mov eax, [clipboard_slots] + shl eax, 2 + add eax, [clipboard_main_list] + mov [eax], edi +; copy the data into the slot + mov esi, edx + mov eax, ecx + cld + stosd ; store size of slot + sub ecx, 4 + add esi, 4 + rep movsb ; store slot data +; increase the counter of slots + inc [clipboard_slots] +; unlock last slot + xor eax, eax + mov [ebx], eax + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.3: + dec ebx ; 3 - Delete the last slot in the clipboard + jnz .4 +; check the availability of slots + mov eax, [clipboard_slots] + test eax, eax + jz .exit_2 +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + jne .exit_2 +; lock last slot + inc eax + mov [ebx], eax +; decrease the counter of slots + mov eax, clipboard_slots + dec dword [eax] +; free of kernel memory allocated for the slot + mov eax, [eax] + shl eax, 2 + add eax, [clipboard_main_list] + mov eax, [eax] + push ebx + stdcall kernel_free, eax + pop ebx +; unlock last slot + xor eax, eax + mov [ebx], eax + jmp .exit_1 +;------------------------------------------------------------------------------ +align 4 +.4: + dec ebx ; 4 - Emergency discharge of clipboard + jnz .exit +; check the lock + mov ebx, clipboard_write_lock + xor eax, eax + cmp [ebx], eax + je .exit_2 + +; there should be a procedure for checking the integrity of the slots +; and I will do so in the future + +; unlock last slot + mov [ebx], eax + jmp .exit +;------------------------------------------------------------------------------ +align 4 +.exit_3: +; unlock last slot + xor eax, eax + mov [ebx], eax +.exit_2: + xor eax, eax + inc eax ; error +.exit_1: + mov [esp + 32], eax +.exit: + ret +;------------------------------------------------------------------------------ +uglobal +align 4 +clipboard_slots dd ? +clipboard_main_list dd ? +clipboard_write_lock dd ? +endg +;------------------------------------------------------------------------------ diff --git a/kernel/branches/kolibri-lldw/core/conf_lib-sp.inc b/kernel/branches/kolibri-lldw/core/conf_lib-sp.inc new file mode 100644 index 000000000..396c91fbc --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/conf_lib-sp.inc @@ -0,0 +1,21 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Éste archivo debe ser editado con codificación CP866 + +ugui_mouse_speed cp850 'velocidad del ratón',0 +ugui_mouse_delay cp850 'demora del ratón',0 + +udev cp850 'disp',0 +unet cp850 'red',0 +unet_active cp850 'activa',0 +unet_addr cp850 'direc',0 +unet_mask cp850 'másc',0 +unet_gate cp850 'puer',0 diff --git a/kernel/branches/kolibri-lldw/core/conf_lib.inc b/kernel/branches/kolibri-lldw/core/conf_lib.inc new file mode 100644 index 000000000..fe56a3ac6 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/conf_lib.inc @@ -0,0 +1,254 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +;------------------------------------------------------------------------- +;Loading configuration from ini file +; {SPraid.simba} +;------------------------------------------------------------------------- + +$Revision$ + +iglobal +conf_path_sect: + db 'path',0 + +conf_fname db '/sys/sys.conf',0 +endg +; set soke kernel configuration +proc set_kernel_conf + locals + par db 30 dup(?) + endl + + pushad +;[gui] +;mouse_speed + + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, ugui, ugui_mouse_speed, \ + eax,30, ugui_mouse_speed_def + pop eax + stdcall strtoint, eax + mov [mouse_speed_factor], ax + +;mouse_delay + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, ugui, ugui_mouse_delay, \ + eax,30, ugui_mouse_delay_def + pop eax + stdcall strtoint, eax + mov [mouse_delay], eax + + +;midibase + lea eax, [par] + push eax + invoke ini.get_str, conf_fname, udev, udev_midibase, eax, 30, udev_midibase_def + pop eax + stdcall strtoint, eax + + cmp eax, 0x100 + jb @f + cmp eax, 0x10000 + jae @f + mov [midi_base], ax + mov [mididp], eax + inc eax + mov [midisp], eax +@@: + popad + ret +endp +iglobal +ugui db 'gui',0 +ugui_mouse_speed_def db '2',0 +ugui_mouse_delay_def db '0x00A',0 +udev_midibase db 'midibase',0 +udev_midibase_def db '0x320',0 +endg + +iglobal +if lang eq sp + include 'core/conf_lib-sp.inc' +else + ugui_mouse_speed db 'mouse_speed',0 + ugui_mouse_delay db 'mouse_delay',0 + udev db 'dev',0 + unet db 'net',0 + unet_active db 'active',0 + unet_addr db 'addr',0 + unet_mask db 'mask',0 + unet_gate db 'gate',0 +end if +unet_def db 0 +endg +; convert string to DWord +proc strtoint stdcall,strs + pushad + + mov eax, [strs] + inc eax + mov bl, [eax] + cmp bl, 'x' + je .hex + cmp bl, 'X' + je .hex + jmp .dec +.hex: + inc eax + stdcall strtoint_hex, eax + jmp .exit +.dec: + dec eax + stdcall strtoint_dec, eax +.exit: + mov [esp+28], eax + popad + ret +endp + +; convert string to DWord for decimal value +proc strtoint_dec stdcall,strs + pushad + xor edx, edx + ; поиск конца + mov esi, [strs] +@@: + lodsb + or al, al + jnz @b + mov ebx, esi + mov esi, [strs] + dec ebx + sub ebx, esi + mov ecx, 1 + +@@: + dec ebx + or ebx, ebx + jz @f + imul ecx, ecx, 10; порядок + jmp @b +@@: + + xchg ebx, ecx + + + xor ecx, ecx + + +@@: + xor eax, eax + lodsb + cmp al, 0 + je .eend + + sub al, 30h + imul ebx + add ecx, eax + push ecx + xchg eax, ebx + mov ecx, 10 + div ecx + xchg eax, ebx + pop ecx + jmp @b + +.eend: + mov [esp+28], ecx + popad + ret +endp + +;convert string to DWord for hex value +proc strtoint_hex stdcall,strs + pushad + xor edx, edx + + mov esi, [strs] + mov ebx, 1 + add esi, 1 + +@@: + lodsb + or al, al + jz @f + shl ebx, 4 + jmp @b +@@: + xor ecx, ecx + mov esi, [strs] + +@@: + xor eax, eax + lodsb + cmp al, 0 + je .eend + + cmp al, 'a' + jae .bm + cmp al, 'A' + jae .bb + jmp .cc +.bm: ; 57h + sub al, 57h + jmp .do + +.bb: ; 37h + sub al, 37h + jmp .do + +.cc: ; 30h + sub al, 30h + +.do: + imul ebx + add ecx, eax + shr ebx, 4 + + jmp @b + +.eend: + mov [esp+28], ecx + popad + ret +endp + + +; convert string to DWord for IP addres +proc do_inet_adr stdcall,strs + pushad + + mov esi, [strs] + mov ebx, 0 +.next: + push esi +@@: + lodsb + or al, al + jz @f + cmp al, '.' + jz @f + jmp @b +@@: + mov cl, al + mov [esi-1], byte 0 + ;pop eax + call strtoint_dec + rol eax, 24 + ror ebx, 8 + add ebx, eax + or cl, cl + jz @f + jmp .next +@@: + mov [esp+28], ebx + popad + ret +endp diff --git a/kernel/branches/kolibri-lldw/core/debug.inc b/kernel/branches/kolibri-lldw/core/debug.inc new file mode 100644 index 000000000..35a1345fc --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/debug.inc @@ -0,0 +1,455 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; diamond, 2006 +sys_debug_services: + cmp ebx, 9 + ja @f + jmp dword [sys_debug_services_table+ebx*4] +@@: + ret +iglobal +align 4 +sys_debug_services_table: + dd debug_set_event_data + dd debug_getcontext + dd debug_setcontext + dd debug_detach + dd debug_suspend + dd debug_resume + dd debug_read_process_memory + dd debug_write_process_memory + dd debug_terminate + dd debug_set_drx +endg +debug_set_event_data: +; in: ecx = pointer +; destroys eax + mov eax, [current_slot] + mov [eax+APPDATA.dbg_event_mem], ecx + ret + +get_debuggee_slot: +; in: ecx=PID +; out: CF=1 if error +; CF=0 and eax=slot*0x20 if ok +; out: interrupts disabled + cli + mov eax, ecx + call pid_to_slot + test eax, eax + jz .ret_bad + shl eax, 5 + push ebx + mov ebx, [current_slot_idx] + cmp [SLOT_BASE+eax*8+APPDATA.debugger_slot], ebx + pop ebx + jnz .ret_bad +; clc ; automatically + ret +.ret_bad: + stc + ret + +debug_detach: +; in: ecx=pid +; destroys eax,ebx + call get_debuggee_slot + jc .ret + and dword [eax*8+SLOT_BASE+APPDATA.debugger_slot], 0 + call do_resume +.ret: + sti + ret + +debug_terminate: +; in: ecx=pid + call get_debuggee_slot + jc debug_detach.ret + mov ecx, eax + shr ecx, 5 +; push 2 +; pop ebx + mov edx, esi + jmp sysfn_terminate + +debug_suspend: +; in: ecx=pid +; destroys eax,ecx +; { Patch by Coldy (rev. 7125), reason: http://board.kolibrios.org/viewtopic.php?f=1&t=1712&p=75957#p75957 +; cli +; mov eax, ecx +; call pid_to_slot +; shl eax, 5 +; jz .ret + call get_debuggee_slot + jc .ret +; } End patch + mov cl, [TASK_TABLE+eax+TASKDATA.state] ; process state + test cl, cl + jz .1 + cmp cl, 5 + jnz .ret + mov cl, 2 +.2: + mov [TASK_TABLE+eax+TASKDATA.state], cl +.ret: + sti + ret +.1: + inc ecx + jmp .2 + +do_resume: + mov cl, [TASK_TABLE+eax+TASKDATA.state] + cmp cl, 1 + jz .1 + cmp cl, 2 + jnz .ret + mov cl, 5 +.2: + mov [TASK_TABLE+eax+TASKDATA.state], cl +.ret: + ret +.1: + dec ecx + jmp .2 + +debug_resume: +; in: ecx=pid +; destroys eax,ebx + cli + mov eax, ecx + call pid_to_slot + shl eax, 5 + jz .ret + call do_resume +.ret: + sti + ret + +debug_getcontext: +; in: +; ecx=pid +; edx=sizeof(CONTEXT) +; esi->CONTEXT +; destroys eax,ebx,ecx,edx,esi,edi + + xor ebx, ebx ; 0 - get only gp regs + cmp edx, 40 + je .std_ctx + + cmp edx, 48+288 + jne .ret + + inc ebx ; 1 - get sse context + ; TODO legacy 32-bit FPU/MMX context +.std_ctx: + call get_debuggee_slot + jc .ret + + shr eax, 5 + cmp eax, [fpu_owner] + jne @f + inc bh ; set swap context flag +@@: + shl eax, 8 + mov edi, esi + mov eax, [eax+SLOT_BASE+APPDATA.pl0_stack] + lea esi, [eax+RING0_STACK_SIZE] + +.ring0: +; note that following code assumes that all interrupt/exception handlers +; save ring-3 context by pushad in this order +; top of ring0 stack: ring3 stack ptr (ss+esp), iret data (cs+eip+eflags), pushad + sub esi, 8+12+20h + lodsd ;edi + mov [edi+24h], eax + lodsd ;esi + mov [edi+20h], eax + lodsd ; ebp + mov [edi+1Ch], eax + lodsd ;esp + lodsd ;ebx + mov [edi+14h], eax + lodsd ;edx + mov [edi+10h], eax + lodsd ;ecx + mov [edi+0Ch], eax + lodsd ;eax + mov [edi+8], eax + lodsd ;eip + mov [edi], eax + lodsd ;cs + lodsd ;eflags + mov [edi+4], eax + lodsd ;esp + mov [edi+18h], eax + + dec bl + js .ret + dec bl + jns .ret + + test bh, bh ; check swap flag + jz @F + + ffree st0 ; swap context +@@: + + add esi, 4 ;top of ring0 stack + ;fpu/sse context saved here + add edi, 40 + mov eax, 1 ;sse context + stosd + xor eax, eax ;reserved dword + stosd + + mov ecx, 288/4 + rep movsd ;copy sse context + +.ret: + sti + ret + +debug_setcontext: +; in: +; ecx=pid +; edx=sizeof(CONTEXT) +; esi->CONTEXT +; destroys eax,ecx,edx,esi,edi + cmp edx, 28h + jnz .ret + + call get_debuggee_slot + jc .stiret +; mov esi, edx + mov eax, [eax*8+SLOT_BASE+APPDATA.pl0_stack] + lea edi, [eax+RING0_STACK_SIZE] + +.ring0: + sub edi, 8+12+20h + mov eax, [esi+24h] ;edi + stosd + mov eax, [esi+20h] ;esi + stosd + mov eax, [esi+1Ch] ;ebp + stosd + scasd + mov eax, [esi+14h] ;ebx + stosd + mov eax, [esi+10h] ;edx + stosd + mov eax, [esi+0Ch] ;ecx + stosd + mov eax, [esi+8] ;eax + stosd + mov eax, [esi] ;eip + stosd + scasd + mov eax, [esi+4] ;eflags + stosd + mov eax, [esi+18h] ;esp + stosd +.stiret: + sti +.ret: + ret + +debug_set_drx: + call get_debuggee_slot + jc .errret + mov ebp, eax + lea eax, [eax*8+SLOT_BASE+APPDATA.dbg_regs] +; [eax]=dr0, [eax+4]=dr1, [eax+8]=dr2, [eax+C]=dr3 +; [eax+10]=dr7 + cmp esi, OS_BASE + jae .errret + cmp dl, 3 + ja .errret + mov ecx, dr7 +;fix me + xchg ecx, edx + shr edx, cl + shr edx, cl + xchg ecx, edx + + test ecx, 2 ; bit 1+2*index = G0..G3, global break enable + jnz .errret2 + test dh, dh + jns .new +; clear breakpoint + movzx edx, dl + add edx, edx + and dword [eax+edx*2], 0 ; clear DR + btr dword [eax+10h], edx ; clear L bit + test byte [eax+10h], 55h + jnz .okret +; imul eax, ebp, tss_step/32 +; and byte [eax + tss_data + TSS._trap], not 1 + and [ebp*8 + SLOT_BASE+APPDATA.dbg_state], not 1 +.okret: + and dword [esp+32], 0 + sti + ret +.errret: + sti + mov dword [esp+32], 1 + ret +.errret2: + sti + mov dword [esp+32], 2 + ret +.new: +; add new breakpoint +; dl=index; dh=flags; esi=address + test dh, 0xF0 + jnz .errret + mov cl, dh + and cl, 3 + cmp cl, 2 + jz .errret + mov cl, dh + shr cl, 2 + cmp cl, 2 + jz .errret + + mov ebx, esi + test bl, dl + + jnz .errret + or byte [eax+10h+1], 3 ; set GE and LE flags + + movzx edx, dh + movzx ecx, dl + add ecx, ecx + bts dword [eax+10h], ecx ; set L flag + add ecx, ecx + mov [eax+ecx], ebx;esi ; set DR + shl edx, cl + mov ebx, 0xF + shl ebx, cl + not ebx + and [eax+10h+2], bx + or [eax+10h+2], dx ; set R/W and LEN fields +; imul eax, ebp, tss_step/32 +; or byte [eax + tss_data + TSS._trap], 1 + or [ebp*8 + SLOT_BASE+APPDATA.dbg_state], 1 + jmp .okret + +debug_read_process_memory: +; in: +; ecx=pid +; edx=length +; edi->buffer in debugger +; esi=address in debuggee +; out: [esp+36]=sizeof(read) +; destroys all + call get_debuggee_slot + jc .err + shr eax, 5 + mov ecx, edi + call read_process_memory + sti + mov dword [esp+32], eax + ret +.err: + or dword [esp+32], -1 + ret + +debug_write_process_memory: +; in: +; ecx=pid +; edx=length +; edi->buffer in debugger +; esi=address in debuggee +; out: [esp+36]=sizeof(write) +; destroys all + call get_debuggee_slot + jc debug_read_process_memory.err + shr eax, 5 + mov ecx, edi + call write_process_memory + sti + mov [esp+32], eax + ret + +debugger_notify: +; in: eax=debugger slot +; ecx=size of debug message +; [esp+4]..[esp+4+ecx]=message +; interrupts must be disabled! +; destroys all general registers +; interrupts remain disabled + xchg ebp, eax + mov edi, [timer_ticks] + add edi, 500 ; 5 sec timeout +.1: + mov eax, ebp + shl eax, 8 + mov esi, [SLOT_BASE+eax+APPDATA.dbg_event_mem] + test esi, esi + jz .ret +; read buffer header + push ecx + push eax + push eax + mov eax, ebp + mov ecx, esp + mov edx, 8 + call read_process_memory + cmp eax, edx + jz @f + add esp, 12 + jmp .ret +@@: + cmp dword [ecx], 0 + jg @f +.2: + pop ecx + pop ecx + pop ecx + cmp dword [current_slot_idx], 1 + jnz .notos + cmp [timer_ticks], edi + jae .ret +.notos: + sti + call change_task + cli + jmp .1 +@@: + mov edx, [ecx+8] + add edx, [ecx+4] + cmp edx, [ecx] + ja .2 +; advance buffer position + push edx + mov edx, 4 + sub ecx, edx + mov eax, ebp + add esi, edx + call write_process_memory + pop eax +; write message + mov eax, ebp + add esi, edx + add esi, [ecx+8] + add ecx, 20 + pop edx + pop edx + pop edx + call write_process_memory +; new debug event + mov eax, ebp + shl eax, BSF sizeof.APPDATA + or [SLOT_BASE+eax+APPDATA.occurred_events], EVENT_DEBUG +.ret: + ret diff --git a/kernel/branches/kolibri-lldw/core/dll.inc b/kernel/branches/kolibri-lldw/core/dll.inc new file mode 100644 index 000000000..5083255dc --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/dll.inc @@ -0,0 +1,1518 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +DRV_COMPAT = 5 ;minimal required drivers version +DRV_CURRENT = 6 ;current drivers model version + +DRV_VERSION = (DRV_COMPAT shl 16) or DRV_CURRENT +PID_KERNEL = 1 ;os_idle thread + + + +align 4 +proc get_notify stdcall, p_ev:dword + +.wait: + mov ebx, [current_slot] + test dword [ebx+APPDATA.event_mask], EVENT_NOTIFY + jz @f + and dword [ebx+APPDATA.event_mask], not EVENT_NOTIFY + mov edi, [p_ev] + mov dword [edi], EV_INTR + mov eax, [ebx+APPDATA.event] + mov dword [edi+4], eax + ret +@@: + call change_task + jmp .wait +endp + +align 4 +proc pci_read32 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 6 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_read16 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 5 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_read8 stdcall, bus:dword, devfn:dword, reg:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 4 + mov bh, byte [devfn] + mov bl, byte [reg] + call pci_read_reg + pop ebx + ret +endp + +align 4 +proc pci_write8 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 8 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +align 4 +proc pci_write16 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 9 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +align 4 +proc pci_write32 stdcall, bus:dword, devfn:dword, reg:dword, val:dword + push ebx + xor eax, eax + xor ebx, ebx + mov ah, byte [bus] + mov al, 10 + mov bh, byte [devfn] + mov bl, byte [reg] + mov ecx, [val] + call pci_write_reg + pop ebx + ret +endp + +handle equ IOCTL.handle +io_code equ IOCTL.io_code +input equ IOCTL.input +inp_size equ IOCTL.inp_size +output equ IOCTL.output +out_size equ IOCTL.out_size + + +align 4 +proc srv_handler stdcall, ioctl:dword + mov esi, [ioctl] + test esi, esi + jz .err + + mov edi, [esi+handle] + cmp [edi+SRV.magic], ' SRV' + jne .fail + + cmp [edi+SRV.size], sizeof.SRV + jne .fail + +; stdcall [edi+SRV.srv_proc], esi + mov eax, [edi+SRV.srv_proc] + test eax, eax + jz .fail + stdcall eax, esi + ret +.fail: + xor eax, eax + not eax + mov [esi+output], eax + mov [esi+out_size], 4 + ret +.err: + xor eax, eax + not eax + ret +endp + +; param +; ecx= io_control +; +; retval +; eax= error code + +align 4 +srv_handlerEx: + cmp ecx, OS_BASE + jae .fail + + mov eax, [ecx+handle] + cmp [eax+SRV.magic], ' SRV' + jne .fail + + cmp [eax+SRV.size], sizeof.SRV + jne .fail + +; stdcall [eax+SRV.srv_proc], ecx + mov eax, [eax+SRV.srv_proc] + test eax, eax + jz .fail + stdcall eax, ecx + ret +.fail: + or eax, -1 + ret + +restore handle +restore io_code +restore input +restore inp_size +restore output +restore out_size + +align 4 +proc get_service stdcall, sz_name:dword + mov eax, [sz_name] + test eax, eax + jnz @F + ret +@@: + mov edx, [srv.fd] +@@: + cmp edx, srv.fd-SRV.fd + je .not_load + + stdcall strncmp, edx, [sz_name], 16 + test eax, eax + mov eax, edx + je .nothing + + mov edx, [edx+SRV.fd] + jmp @B +.not_load: + mov eax, [sz_name] + push edi + sub esp, 36 + mov edi, esp + mov dword [edi], '/sys' + mov dword [edi+4], '/dri' + mov dword [edi+8], 'vers' + mov byte [edi+12], '/' +@@: + mov dl, [eax] + mov [edi+13], dl + inc eax + inc edi + test dl, dl + jnz @b + mov dword [edi+12], '.sys' + mov byte [edi+16], 0 + mov edi, esp + stdcall load_pe_driver, edi, 0 + add esp, 36 + pop edi +.nothing: + ret +endp + +reg_service: + xor eax, eax + mov ecx, [esp+8] + jecxz .nothing + push sizeof.SRV + push ecx + pushd [esp+12] + call reg_service_ex +.nothing: + ret 8 + +reg_usb_driver: + push sizeof.USBSRV + pushd [esp+12] + pushd [esp+12] + call reg_service_ex + test eax, eax + jz .nothing + mov ecx, [esp+12] + mov [eax+USBSRV.usb_func], ecx +.nothing: + ret 12 + +proc reg_service_ex stdcall, name:dword, handler:dword, srvsize:dword + + push ebx + + xor eax, eax + + cmp [name], eax + je .fail + +; cmp [handler], eax +; je .fail + + mov eax, [srvsize] + call malloc + test eax, eax + jz .fail + + push esi + push edi + mov edi, eax + mov esi, [name] + movsd + movsd + movsd + movsd + pop edi + pop esi + + mov [eax+SRV.magic], ' SRV' + mov [eax+SRV.size], sizeof.SRV + + mov ebx, srv.fd-SRV.fd + mov edx, [ebx+SRV.fd] + mov [eax+SRV.fd], edx + mov [eax+SRV.bk], ebx + mov [ebx+SRV.fd], eax + mov [edx+SRV.bk], eax + + mov ecx, [handler] + mov [eax+SRV.srv_proc], ecx + pop ebx + ret +.fail: + xor eax, eax + pop ebx + ret +endp + +align 4 +proc get_proc stdcall, exp:dword, sz_name:dword + + mov edx, [exp] +.next: + mov eax, [edx] + test eax, eax + jz .end + + push edx + stdcall strncmp, eax, [sz_name], 16 + pop edx + test eax, eax + jz .ok + + add edx, 8 + jmp .next +.ok: + mov eax, [edx+4] +.end: + ret +endp + +align 4 +proc get_coff_sym stdcall, pSym:dword,count:dword, sz_sym:dword + +@@: + stdcall strncmp, [pSym], [sz_sym], 8 + test eax, eax + jz .ok + add [pSym], 18 + dec [count] + jnz @b + xor eax, eax + ret +.ok: + mov eax, [pSym] + mov eax, [eax+8] + ret +endp + +align 4 +proc get_curr_task + mov eax, [current_slot_idx] + shl eax, 8 + ret +endp + +align 4 +proc get_fileinfo stdcall, file_name:dword, info:dword + locals + cmd dd ? + offset dd ? + dd ? + count dd ? + buff dd ? + db ? + name dd ? + endl + + xor eax, eax + mov ebx, [file_name] + mov ecx, [info] + + mov [cmd], 5 + mov [offset], eax + mov [offset+4], eax + mov [count], eax + mov [buff], ecx + mov byte [buff+4], al + mov [name], ebx + + mov eax, 70 + lea ebx, [cmd] + pushad + cld + call protect_from_terminate + call file_system_lfn + call unprotect_from_terminate + popad + ret +endp + +align 4 +proc read_file stdcall,file_name:dword, buffer:dword, off:dword,\ + bytes:dword + locals + cmd dd ? + offset dd ? + dd ? + count dd ? + buff dd ? + db ? + name dd ? + endl + + xor eax, eax + mov ebx, [file_name] + mov ecx, [off] + mov edx, [bytes] + mov esi, [buffer] + + mov [cmd], eax + mov [offset], ecx + mov [offset+4], eax + mov [count], edx + mov [buff], esi + mov byte [buff+4], al + mov [name], ebx + + pushad + lea ebx, [cmd] + call file_system_lfn_protected + popad + ret +endp + +align 4 +; @brief Allocate kernel memory and loads the specified file +; +; @param file_name Path to file +; +; @returns File image in kernel memory in `eax` and size of file in `ebx` +; +; @warning You must call kernel_free() to delete each file loaded by the +; load_file() function +proc load_file stdcall, file_name:dword + locals + attr dd ? + flags dd ? + cr_time dd ? + cr_date dd ? + acc_time dd ? + acc_date dd ? + mod_time dd ? + mod_date dd ? + file_size dd ? + + file dd ? + file2 dd ? + endl + + push esi + push edi + + lea eax, [attr] + stdcall get_fileinfo, [file_name], eax + test eax, eax + jnz .fail + + mov eax, [file_size] + cmp eax, 1024*1024*16 + ja .fail + + stdcall kernel_alloc, [file_size] + mov [file], eax + test eax, eax + jz .fail + + stdcall read_file, [file_name], eax, dword 0, [file_size] + cmp ebx, [file_size] + jne .cleanup + + mov eax, [file] + cmp dword [eax], 'KPCK' + jne .exit + mov ebx, [eax+4] + mov [file_size], ebx + stdcall kernel_alloc, ebx + + test eax, eax + jz .cleanup + + mov [file2], eax + + pushad + mov ecx, unpack_mutex + call mutex_lock + popad + + stdcall unpack, [file], eax + + pushad + mov ecx, unpack_mutex + call mutex_unlock + popad + + stdcall kernel_free, [file] + mov eax, [file2] + mov ebx, [file_size] +.exit: + push eax + lea edi, [eax+ebx] ;cleanup remain space + mov ecx, 4096 ;from file end + and ebx, 4095 + jz @f + sub ecx, ebx + xor eax, eax + cld + rep stosb +@@: + mov ebx, [file_size] + pop eax + pop edi + pop esi + ret +.cleanup: + stdcall kernel_free, [file] +.fail: + xor eax, eax + xor ebx, ebx + pop edi + pop esi + ret +endp + +; description +; allocate user memory and loads the specified file +; +; param +; file_name= path to file +; +; retval +; eax= file image in user memory +; ebx= size of file +; +; warging +; You mast call kernel_free() to delete each file +; loaded by the load_file() function + +align 4 +proc load_file_umode stdcall, file_name:dword + locals + attr dd ? + flags dd ? + cr_time dd ? + cr_date dd ? + acc_time dd ? + acc_date dd ? + mod_time dd ? + mod_date dd ? + file_size dd ? + + km_file dd ? + um_file dd ? + endl + + push esi + push edi + push ebx + + lea eax, [attr] + stdcall get_fileinfo, [file_name], eax ;find file and get info + test eax, eax + jnz .err_1 + + mov eax, [file_size] + cmp eax, 1024*1024*16 ;to be enough for anybody (c) + ja .err_1 + ;it is very likely that the file is packed + stdcall kernel_alloc, [file_size] ;with kpack, so allocate memory from kernel heap + mov [km_file], eax + test eax, eax + jz .err_1 + + stdcall read_file, [file_name], eax, dword 0, [file_size] + cmp ebx, [file_size] + + jne .err_2 + + mov eax, [km_file] + cmp dword [eax], 'KPCK' ; check kpack signature + jne .raw_file + + mov ebx, [eax+4] ;get real size of file + mov [file_size], ebx + stdcall user_alloc, ebx ;and allocate space from user heap + mov [um_file], eax + test eax, eax + jz .err_2 + + mov edx, [file_size] ;preallocate page memory + shr eax, 10 + lea edi, [page_tabs+eax] + add edx, 4095 + shr edx, 12 +@@: + call alloc_page + test eax, eax + jz .err_3 + + or eax, PG_UWR + stosd + dec edx + jnz @B + + pushad + mov ecx, unpack_mutex + call mutex_lock + + stdcall unpack, [km_file], [um_file] + + mov ecx, unpack_mutex + call mutex_unlock + popad + + stdcall kernel_free, [km_file] ;we don't need packed file anymore +.exit: + + mov edi, [um_file] + mov esi, [um_file] + mov eax, [file_size] + mov edx, eax + + add edi, eax ;cleanup remain space + mov ecx, 4096 ;from file end + and eax, 4095 + jz @f + sub ecx, eax + xor eax, eax + cld + rep stosb +@@: + mov eax, [um_file] + + pop ebx + pop edi + pop esi + ret + +.raw_file: ; sometimes we load unpacked file + stdcall user_alloc, ebx ; allocate space from user heap + mov [um_file], eax + + test eax, eax + jz .err_2 + + shr eax, 10 ; and remap pages. + + mov ecx, [file_size] + add ecx, 4095 + shr ecx, 12 + + mov esi, [km_file] + shr esi, 10 + add esi, page_tabs + + lea edi, [page_tabs+eax] + + cld +@@: + lodsd + and eax, 0xFFFFF000 + or eax, PG_UWR + stosd + loop @B + + stdcall free_kernel_space, [km_file] ; release allocated kernel space + jmp .exit ; physical pages still in use +.err_3: + stdcall user_free, [um_file] +.err_2: + stdcall kernel_free, [km_file] +.err_1: + xor eax, eax + xor edx, edx + + pop ebx + pop edi + pop esi + ret +endp + + +uglobal +align 4 +unpack_mutex MUTEX +endg + +align 4 +proc get_proc_ex stdcall uses ebx esi, proc_name:dword, imports:dword + mov ebx, [imports] + test ebx, ebx + jz .end + xor esi, esi +.look_up: + + mov eax, [ebx+32] + mov eax, [OS_BASE+eax+esi*4] + add eax, OS_BASE + stdcall strncmp, eax, [proc_name], 256 + test eax, eax + jz .ok + + inc esi + cmp esi, [ebx+24] + jb .look_up +.end: + xor eax, eax + ret +.ok: + mov eax, [ebx+28] + mov eax, [OS_BASE+eax+esi*4] + add eax, OS_BASE + ret +endp + +align 4 +proc fix_coff_symbols stdcall uses ebx esi, sec:dword, symbols:dword,\ + sym_count:dword, strings:dword, imports:dword + locals + retval dd ? + endl + + mov edi, [symbols] + mov [retval], 1 +.fix: + movzx ebx, [edi+COFF_SYM.SectionNumber] + test ebx, ebx + jnz .internal + mov eax, dword [edi+COFF_SYM.Name] + test eax, eax + jnz @F + + mov edi, [edi+4] + add edi, [strings] +@@: + push edi + stdcall get_proc_ex, edi, [imports] + pop edi + + xor ebx, ebx + test eax, eax + jnz @F + + ; disable debug msg + ;mov esi, msg_unresolved + ;call sys_msg_board_str + ;mov esi, edi + ;call sys_msg_board_str + ;mov esi, msg_CR + ;call sys_msg_board_str + + mov [retval], 0 +@@: + mov edi, [symbols] + mov [edi+COFF_SYM.Value], eax + jmp .next +.internal: + cmp bx, -1 + je .next + cmp bx, -2 + je .next + + dec ebx + shl ebx, 3 + lea ebx, [ebx+ebx*4] + add ebx, [sec] + + mov eax, [ebx+COFF_SECTION.VirtualAddress] + add [edi+COFF_SYM.Value], eax +.next: + add edi, sizeof.COFF_SYM + mov [symbols], edi + dec [sym_count] + jnz .fix + mov eax, [retval] + ret +endp + +align 4 +proc fix_coff_relocs stdcall uses ebx esi, coff:dword, sym:dword, \ + delta:dword + locals + n_sec dd ? + endl + + mov eax, [coff] + movzx ebx, [eax+COFF_HEADER.nSections] + mov [n_sec], ebx + lea esi, [eax+20] +.fix_sec: + mov edi, [esi+COFF_SECTION.PtrReloc] + add edi, [coff] + + movzx ecx, [esi+COFF_SECTION.NumReloc] + test ecx, ecx + jz .next +.reloc_loop: + mov ebx, [edi+COFF_RELOC.SymIndex] + add ebx, ebx + lea ebx, [ebx+ebx*8] + add ebx, [sym] + + mov edx, [ebx+COFF_SYM.Value] + + cmp [edi+COFF_RELOC.Type], 6 + je .dir_32 + + cmp [edi+COFF_RELOC.Type], 20 + jne .next_reloc +.rel_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] + sub edx, eax + sub edx, 4 + jmp .fix +.dir_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] +.fix: + add eax, [delta] + add [eax], edx +.next_reloc: + add edi, 10 + dec ecx + jnz .reloc_loop +.next: + add esi, sizeof.COFF_SECTION + dec [n_sec] + jnz .fix_sec +.exit: + ret +endp + +align 4 +proc rebase_coff stdcall uses ebx esi, coff:dword, sym:dword, \ + delta:dword + locals + n_sec dd ? + endl + + mov eax, [coff] + movzx ebx, [eax+COFF_HEADER.nSections] + mov [n_sec], ebx + lea esi, [eax+20] + mov edx, [delta] +.fix_sec: + mov edi, [esi+COFF_SECTION.PtrReloc] + add edi, [coff] + + movzx ecx, [esi+COFF_SECTION.NumReloc] + test ecx, ecx + jz .next +.reloc_loop: + cmp [edi+COFF_RELOC.Type], 6 + jne .next_reloc +.dir_32: + mov eax, [edi+COFF_RELOC.VirtualAddress] + add eax, [esi+COFF_SECTION.VirtualAddress] + add [eax+edx], edx +.next_reloc: + add edi, 10 + dec ecx + jnz .reloc_loop +.next: + add esi, sizeof.COFF_SECTION + dec [n_sec] + jnz .fix_sec +.exit: + ret +endp + +; in: edx -> COFF_SECTION struct +; out: eax = alignment as mask for bits to drop +coff_get_align: +; Rules: +; - if alignment is not given, use default = 4K; +; - if alignment is given and is no more than 4K, use it; +; - if alignment is more than 4K, revert to 4K. + push ecx + mov cl, byte [edx+COFF_SECTION.Characteristics+2] + mov eax, 1 + shr cl, 4 + dec cl + js .default + cmp cl, 12 + jbe @f +.default: + mov cl, 12 +@@: + shl eax, cl + pop ecx + dec eax + ret + +align 4 +proc load_library stdcall, file_name:dword, encoding:dword + locals + fullname dd ? + fileinfo rb 40 + coff dd ? + img_base dd ? + endl + +; resolve file name + stdcall kernel_alloc, maxPathLength + mov [fullname], eax + mov edi, eax + mov esi, [file_name] + mov eax, [encoding] + push ebp + call getFullPath + pop ebp + test eax, eax + jz .fail +; scan for required DLL in list of already loaded for this process, +; ignore timestamp + cli + mov esi, [current_process] + mov edi, [fullname] + mov ebx, [esi+PROC.dlls_list_ptr] + test ebx, ebx + jz .not_in_process + mov esi, [ebx+HDLL.fd] +.scan_in_process: + cmp esi, ebx + jz .not_in_process + mov eax, [esi+HDLL.parent] + add eax, DLLDESCR.name + stdcall strncmp, eax, edi, -1 + test eax, eax + jnz .next_in_process +; simple variant: load DLL which is already loaded in this process +; just increment reference counters and return address of exports table + inc [esi+HDLL.refcount] + mov ecx, [esi+HDLL.parent] + inc [ecx+DLLDESCR.refcount] + mov eax, [ecx+DLLDESCR.exports] + sub eax, [ecx+DLLDESCR.defaultbase] + add eax, [esi+HDLL.base] + sti + push eax + stdcall kernel_free, [fullname] + pop eax + ret + +.next_in_process: + mov esi, [esi+HDLL.fd] + jmp .scan_in_process + +.not_in_process: +; scan in full list, compare timestamp + sti + lea eax, [fileinfo] + stdcall get_fileinfo, edi, eax + test eax, eax + jnz .fail + cli + mov esi, [dll_list.fd] +.scan_for_dlls: + cmp esi, dll_list + jz .load_new + lea eax, [esi+DLLDESCR.name] + stdcall strncmp, eax, edi, -1 + test eax, eax + jnz .continue_scan +.test_prev_dll: + mov eax, dword [fileinfo+24]; last modified time + mov edx, dword [fileinfo+28]; last modified date + cmp dword [esi+DLLDESCR.timestamp], eax + jnz .continue_scan + cmp dword [esi+DLLDESCR.timestamp+4], edx + jz .dll_already_loaded +.continue_scan: + mov esi, [esi+DLLDESCR.fd] + jmp .scan_for_dlls + +; new DLL +.load_new: + sti +; load file + stdcall load_file, edi + test eax, eax + jz .fail + mov [coff], eax + mov dword [fileinfo+32], ebx + +; allocate DLLDESCR struct; size is DLLDESCR.sizeof plus size of DLL name + mov esi, edi + mov ecx, -1 + xor eax, eax + repnz scasb + not ecx + lea eax, [ecx+sizeof.DLLDESCR] + push ecx + call malloc + pop ecx + test eax, eax + jz .fail_and_free_coff +; save timestamp + lea edi, [eax+DLLDESCR.name] + rep movsb + mov esi, eax + mov eax, dword [fileinfo+24] + mov dword [esi+DLLDESCR.timestamp], eax + mov eax, dword [fileinfo+28] + mov dword [esi+DLLDESCR.timestamp+4], eax + +; calculate size of loaded DLL + mov edx, [coff] + movzx ecx, [edx+COFF_HEADER.nSections] + xor ebx, ebx + + add edx, 20 +@@: + call coff_get_align + add ebx, eax + not eax + and ebx, eax + add ebx, [edx+COFF_SECTION.SizeOfRawData] + add edx, sizeof.COFF_SECTION + dec ecx + jnz @B +; it must be nonzero and not too big + mov [esi+DLLDESCR.size], ebx + test ebx, ebx + jz .fail_and_free_dll + cmp ebx, MAX_DEFAULT_DLL_ADDR-MIN_DEFAULT_DLL_ADDR + ja .fail_and_free_dll +; allocate memory for kernel-side image + stdcall kernel_alloc, ebx + test eax, eax + jz .fail_and_free_dll + mov [esi+DLLDESCR.data], eax +; calculate preferred base address + add ebx, 0x1FFF + and ebx, not 0xFFF + mov ecx, [dll_cur_addr] + lea edx, [ecx+ebx] + cmp edx, MAX_DEFAULT_DLL_ADDR + jb @f + mov ecx, MIN_DEFAULT_DLL_ADDR + lea edx, [ecx+ebx] +@@: + mov [esi+DLLDESCR.defaultbase], ecx + mov [dll_cur_addr], edx + +; copy sections and set correct values for VirtualAddress'es in headers + push esi + mov edx, [coff] + movzx ebx, [edx+COFF_HEADER.nSections] + mov edi, eax + add edx, 20 + cld +@@: + call coff_get_align + add ecx, eax + add edi, eax + not eax + and ecx, eax + and edi, eax + mov [edx+COFF_SECTION.VirtualAddress], ecx + add ecx, [edx+COFF_SECTION.SizeOfRawData] + mov esi, [edx+COFF_SECTION.PtrRawData] + push ecx + mov ecx, [edx+COFF_SECTION.SizeOfRawData] + test esi, esi + jnz .copy + xor eax, eax + rep stosb + jmp .next +.copy: + add esi, [coff] + rep movsb +.next: + pop ecx + add edx, sizeof.COFF_SECTION + dec ebx + jnz @B + pop esi + +; save some additional data from COFF file +; later we will use COFF header, headers for sections and symbol table +; and also relocations table for all sections + mov edx, [coff] + mov ebx, [edx+COFF_HEADER.pSymTable] + mov edi, dword [fileinfo+32] + sub edi, ebx + jc .fail_and_free_data + mov [esi+DLLDESCR.symbols_lim], edi + add ebx, edx + movzx ecx, [edx+COFF_HEADER.nSections] + lea ecx, [ecx*5] + lea edi, [edi+ecx*8+20] + add edx, 20 +@@: + movzx eax, [edx+COFF_SECTION.NumReloc] + lea eax, [eax*5] + lea edi, [edi+eax*2] + add edx, sizeof.COFF_SECTION + sub ecx, 5 + jnz @b + stdcall kernel_alloc, edi + test eax, eax + jz .fail_and_free_data + mov edx, [coff] + movzx ecx, [edx+COFF_HEADER.nSections] + lea ecx, [ecx*5] + lea ecx, [ecx*2+5] + mov [esi+DLLDESCR.coff_hdr], eax + push esi + mov esi, edx + mov edi, eax + rep movsd + pop esi + mov [esi+DLLDESCR.symbols_ptr], edi + push esi + mov ecx, [edx+COFF_HEADER.nSymbols] + mov [esi+DLLDESCR.symbols_num], ecx + mov ecx, [esi+DLLDESCR.symbols_lim] + mov esi, ebx + rep movsb + pop esi + mov ebx, [esi+DLLDESCR.coff_hdr] + push esi + movzx eax, [edx+COFF_HEADER.nSections] + lea edx, [ebx+20] +@@: + movzx ecx, [edx+COFF_SECTION.NumReloc] + lea ecx, [ecx*5] + mov esi, [edx+COFF_SECTION.PtrReloc] + mov [edx+COFF_SECTION.PtrReloc], edi + sub [edx+COFF_SECTION.PtrReloc], ebx + add esi, [coff] + shr ecx, 1 + rep movsd + adc ecx, ecx + rep movsw + add edx, sizeof.COFF_SECTION + dec eax + jnz @b + pop esi + +; fixup symbols + mov edx, ebx + mov eax, [ebx+COFF_HEADER.nSymbols] + add edx, 20 + mov ecx, [esi+DLLDESCR.symbols_num] + lea ecx, [ecx*9] + add ecx, ecx + add ecx, [esi+DLLDESCR.symbols_ptr] + + stdcall fix_coff_symbols, edx, [esi+DLLDESCR.symbols_ptr], eax, \ + ecx, 0 +; test eax, eax +; jnz @F +; +;@@: + + stdcall get_coff_sym, [esi+DLLDESCR.symbols_ptr], [ebx+COFF_HEADER.nSymbols], szEXPORTS + test eax, eax + jnz @F + + stdcall get_coff_sym, [esi+DLLDESCR.symbols_ptr], [ebx+COFF_HEADER.nSymbols], sz_EXPORTS +@@: + mov [esi+DLLDESCR.exports], eax + +; fix relocs in the hidden copy in kernel memory to default address +; it is first fix; usually this will be enough, but second fix +; can be necessary if real load address will not equal assumption + mov eax, [esi+DLLDESCR.data] + sub eax, [esi+DLLDESCR.defaultbase] + stdcall fix_coff_relocs, ebx, [esi+DLLDESCR.symbols_ptr], eax + + stdcall kernel_free, [coff] + + cli +; initialize DLLDESCR struct + and dword [esi+DLLDESCR.refcount], 0; no HDLLs yet; later it will be incremented + mov [esi+DLLDESCR.fd], dll_list + mov eax, [dll_list.bk] + mov [dll_list.bk], esi + mov [esi+DLLDESCR.bk], eax + mov [eax+DLLDESCR.fd], esi +.dll_already_loaded: + stdcall kernel_free, [fullname] + inc [esi+DLLDESCR.refcount] + push esi + call init_heap + pop esi + mov edi, [esi+DLLDESCR.size] + stdcall user_alloc_at, [esi+DLLDESCR.defaultbase], edi + test eax, eax + jnz @f + stdcall user_alloc, edi + test eax, eax + jz .fail_and_dereference +@@: + mov [img_base], eax + mov eax, sizeof.HDLL + call malloc + test eax, eax + jz .fail_and_free_user + mov ebx, [current_slot_idx] + shl ebx, 5 + mov edx, [TASK_TABLE+ebx+TASKDATA.pid] + mov [eax+HDLL.pid], edx + push eax + call init_dlls_in_thread + pop ebx + test eax, eax + jz .fail_and_free_user + mov edx, [eax+HDLL.fd] + mov [ebx+HDLL.fd], edx + mov [ebx+HDLL.bk], eax + mov [eax+HDLL.fd], ebx + mov [edx+HDLL.bk], ebx + mov eax, ebx + mov ebx, [img_base] + mov [eax+HDLL.base], ebx + mov [eax+HDLL.size], edi + mov [eax+HDLL.refcount], 1 + mov [eax+HDLL.parent], esi + mov edx, ebx + shr edx, 12 + or dword [page_tabs+(edx-1)*4], MEM_BLOCK_DONT_FREE +; copy entries of page table from kernel-side image to usermode +; use copy-on-write for user-mode image, so map as readonly + xor edi, edi + mov ecx, [esi+DLLDESCR.data] + shr ecx, 12 +.map_pages_loop: + mov eax, [page_tabs+ecx*4] + and eax, not 0xFFF + or al, PG_UR + xchg eax, [page_tabs+edx*4] + test al, 1 + jz @f + call free_page +@@: + invlpg [ebx+edi] + inc ecx + inc edx + add edi, 0x1000 + cmp edi, [esi+DLLDESCR.size] + jb .map_pages_loop + +; if real user-mode base is not equal to preferred base, relocate image + sub ebx, [esi+DLLDESCR.defaultbase] + jz @f + stdcall rebase_coff, [esi+DLLDESCR.coff_hdr], [esi+DLLDESCR.symbols_ptr], ebx +@@: + + mov eax, [esi+DLLDESCR.exports] + sub eax, [esi+DLLDESCR.defaultbase] + add eax, [img_base] + sti + ret + +.fail_and_free_data: + stdcall kernel_free, [esi+DLLDESCR.data] +.fail_and_free_dll: + mov eax, esi + call free +.fail_and_free_coff: + stdcall kernel_free, [coff] +.fail: + stdcall kernel_free, [fullname] + xor eax, eax + ret + +.fail_and_free_user: + stdcall user_free, [img_base] +.fail_and_dereference: + mov eax, 1 ; delete 1 reference + call dereference_dll + sti + xor eax, eax + ret +endp + +; initialize [APPDATA.dlls_list_ptr] for given thread +; DLL is per-process object, so APPDATA.dlls_list_ptr must be +; kept in sync for all threads of one process. +; out: eax = APPDATA.dlls_list_ptr if all is OK, +; NULL if memory allocation failed +init_dlls_in_thread: + mov ebx, [current_process] + mov eax, [ebx+PROC.dlls_list_ptr] + test eax, eax + jnz .ret + + mov eax, 8 + call malloc ; FIXME + test eax, eax + jz .ret + + mov [eax], eax + mov [eax+4], eax + + mov ebx, [current_process] + mov [ebx+PROC.dlls_list_ptr], eax +.ret: + ret + +; in: eax = number of references to delete, esi -> DLLDESCR struc +dereference_dll: + sub [esi+DLLDESCR.refcount], eax + jnz .ret + mov eax, [esi+DLLDESCR.fd] + mov edx, [esi+DLLDESCR.bk] + mov [eax+DLLDESCR.bk], edx + mov [edx+DLLDESCR.fd], eax + stdcall kernel_free, [esi+DLLDESCR.coff_hdr] + stdcall kernel_free, [esi+DLLDESCR.data] + mov eax, esi + call free +.ret: + ret + +destroy_hdll: + push ebx ecx esi edi + mov ebx, [eax+HDLL.base] + mov esi, [eax+HDLL.parent] + mov edx, [esi+DLLDESCR.size] + + push eax + mov esi, [eax+HDLL.parent] + mov eax, [eax+HDLL.refcount] + call dereference_dll + pop eax + mov edx, [eax+HDLL.bk] + mov ebx, [eax+HDLL.fd] + mov [ebx+HDLL.bk], edx + mov [edx+HDLL.fd], ebx + call free + pop edi esi ecx ebx + ret + +; ecx -> APPDATA for slot, esi = dlls_list_ptr +destroy_all_hdlls: + test esi, esi + jz .ret +.loop: + mov eax, [esi+HDLL.fd] + cmp eax, esi + jz free + call destroy_hdll + jmp .loop +.ret: + ret + +align 4 +stop_all_services: + push ebp + mov edx, [srv.fd] +.next: + cmp edx, srv.fd-SRV.fd + je .done + cmp [edx+SRV.magic], ' SRV' + jne .next + cmp [edx+SRV.size], sizeof.SRV + jne .next + + mov ebx, [edx+SRV.entry] + mov edx, [edx+SRV.fd] + test ebx, ebx + jz .next + + push edx + mov ebp, esp + push 0 + push -1 + call ebx + mov esp, ebp + pop edx + jmp .next +.done: + pop ebp + ret + +; param +; eax= size +; ebx= pid + +align 4 +create_kernel_object: + + push ebx + call malloc + pop ebx + test eax, eax + jz .fail + + mov ecx, [current_slot] + add ecx, APP_OBJ_OFFSET + + pushfd + cli + mov edx, [ecx+APPOBJ.fd] + mov [eax+APPOBJ.fd], edx + mov [eax+APPOBJ.bk], ecx + mov [eax+APPOBJ.pid], ebx + + mov [ecx+APPOBJ.fd], eax + mov [edx+APPOBJ.bk], eax + popfd +.fail: + ret + +; param +; eax= object + +align 4 +destroy_kernel_object: + + pushfd + cli + mov ebx, [eax+APPOBJ.fd] + mov ecx, [eax+APPOBJ.bk] + mov [ebx+APPOBJ.bk], ecx + mov [ecx+APPOBJ.fd], ebx + popfd + + xor edx, edx ;clear common header + mov [eax], edx + mov [eax+4], edx + mov [eax+8], edx + mov [eax+12], edx + mov [eax+16], edx + + call free ;release object memory + ret + + +;void* __fastcall create_object(size_t size) +; param +; ecx= size + +align 4 +create_object: + + push esi + push edi + pushfd + cli + + mov esi, [current_process] + mov eax, [esi+PROC.ht_free] + mov edi, [esi+PROC.ht_next] + dec eax + js .err0 + + mov [esi+PROC.ht_free], eax + mov eax, [esi+PROC.htab+edi*4] + mov [esi+PROC.ht_next], eax + popfd + + mov eax, ecx + call malloc + test eax, eax + jz .err1 + + mov [eax+FUTEX.handle], edi + mov [esi+PROC.htab+edi*4], eax + pop edi + pop esi + ret + +.err1: + pushfd + cli + + mov eax, [esi+PROC.ht_next] + mov [esi+PROC.htab+edi*4], eax + mov [esi+PROC.ht_next], edi + inc [esi+PROC.ht_free] +.err0: + popfd + pop edi + pop esi + xor eax, eax + ret + + +;int __fastcall destroy_object(struct object *obj) + +align 4 +destroy_object: + push esi + mov esi, [current_process] + mov edx, [ecx+FUTEX.handle] + + pushfd + cli + + mov eax, [esi+PROC.ht_next] + mov [esi+PROC.htab+edx*4], eax + mov [esi+PROC.ht_next], edx + inc [esi+PROC.ht_free] + + popfd + pop esi + + mov eax, ecx + call free + xor eax, eax + ret +.fail: + popfd + pop esi + mov eax, -1 + ret + diff --git a/kernel/branches/kolibri-lldw/core/export.inc b/kernel/branches/kolibri-lldw/core/export.inc new file mode 100644 index 000000000..5fb9a9005 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/export.inc @@ -0,0 +1,40 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Macroinstruction for making export section + + +macro export dllname,[label,string] + { common + local module,addresses,names,ordinal,count + count = 0 + forward + count = count+1 + common + dd 0,0,0, (module-OS_BASE) , 1 + dd count,count,(addresses-OS_BASE),(names-OS_BASE),(ordinal-OS_BASE) + addresses: + forward + dd (label-OS_BASE) + common + names: + forward + local name + dd (name-OS_BASE) + common + ordinal: + count = 0 + forward + dw count + count = count+1 + common + module db dllname,0 + forward + name db string,0 + } diff --git a/kernel/branches/kolibri-lldw/core/exports.inc b/kernel/branches/kolibri-lldw/core/exports.inc new file mode 100644 index 000000000..f0a77f3e5 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/exports.inc @@ -0,0 +1,146 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + align 4 +__exports: + export 'KERNEL', \ + alloc_kernel_space, 'AllocKernelSpace', \ ; stdcall + alloc_page, 'AllocPage', \ ; gcc ABI + alloc_pages, 'AllocPages', \ ; stdcall + commit_pages, 'CommitPages', \ ; eax, ebx, ecx +\ + disk_add, 'DiskAdd', \ ;stdcall + disk_del, 'DiskDel', \ + disk_media_changed, 'DiskMediaChanged', \ ;stdcall +\ + create_event, 'CreateEvent', \ ; ecx, esi + destroy_event, 'DestroyEvent', \ ; + raise_event, 'RaiseEvent', \ ; eax, ebx, edx, esi + wait_event, 'WaitEvent', \ ; eax, ebx + wait_event_timeout, 'WaitEventTimeout', \ ; eax, ebx, ecx + get_event_ex, 'GetEvent', \ ; edi + clear_event, 'ClearEvent', \ ;see EVENT.inc for specification + send_event, 'SendEvent', \ ;see EVENT.inc for specification +\ + create_kernel_object, 'CreateObject', \ + create_ring_buffer, 'CreateRingBuffer', \ ; stdcall + destroy_kernel_object, 'DestroyObject', \ + free_kernel_space, 'FreeKernelSpace', \ ; stdcall + free_page, 'FreePage', \ ; eax + kernel_alloc, 'KernelAlloc', \ ; stdcall + kernel_free, 'KernelFree', \ ; stdcall + malloc, 'Kmalloc', \ + free, 'Kfree', \ + map_io_mem, 'MapIoMem', \ ; stdcall + map_page, 'MapPage', \ ; stdcall + get_pg_addr, 'GetPgAddr', \ ; eax + get_phys_addr, 'GetPhysAddr', \ ; eax + map_space, 'MapSpace', \ + release_pages, 'ReleasePages', \ + alloc_dma24, 'AllocDMA24', \ ; stdcall +\ + init_rwsem, 'InitRwsem', \ ; gcc fastcall + down_read, 'DownRead', \ ; gcc fastcall + down_write, 'DownWrite', \ ; gcc fastcall + up_read, 'UpRead', \ ; gcc fastcall + up_write, 'UpWrite', \ ; gcc fastacll + mutex_init, 'MutexInit', \ ; gcc fastcall + mutex_lock, 'MutexLock', \ ; gcc fastcall + mutex_unlock, 'MutexUnlock', \ ; gcc fastcall +\ + get_display, 'GetDisplay', \ + set_screen, 'SetScreen', \ + set_framebuffer, 'SetFramebuffer', \ ; gcc fastcall + window._.get_rect, 'GetWindowRect', \ ; gcc fastcall + pci_api_drv, 'PciApi', \ + pci_read8, 'PciRead8', \ ; stdcall + pci_read16, 'PciRead16', \ ; stdcall + pci_read32, 'PciRead32', \ ; stdcall + pci_write8, 'PciWrite8', \ ; stdcall + pci_write16, 'PciWrite16', \ ; stdcall + pci_write32, 'PciWrite32', \ ; stdcall +\ + get_pid, 'GetPid', \ + get_service, 'GetService', \ ; + reg_service, 'RegService', \ ; stdcall + attach_int_handler, 'AttachIntHandler', \ ; stdcall + user_alloc, 'UserAlloc', \ ; stdcall + user_alloc_at, 'UserAllocAt', \ ; stdcall + user_free, 'UserFree', \ ; stdcall + unmap_pages, 'UnmapPages', \ ; eax, ecx + sys_msg_board_str, 'SysMsgBoardStr', \ + sys_msg_board, 'SysMsgBoard', \ + get_clock_ns, 'GetClockNs', \ ;retval edx:eax 64-bit value + get_timer_ticks, 'GetTimerTicks', \ + get_stack_base, 'GetStackBase', \ + delay_hs, 'Delay', \ ; ebx + set_mouse_data, 'SetMouseData', \ ; + set_keyboard_data, 'SetKeyboardData', \ ; gcc fastcall + register_keyboard, 'RegKeyboard', \ + delete_keyboard, 'DelKeyboard', \ + get_cpu_freq, 'GetCpuFreq', \ +\ + new_sys_threads, 'CreateThread', \ ; ebx, ecx, edx +\ + srv_handler, 'ServiceHandler', \ + fpu_save, 'FpuSave', \ + fpu_restore, 'FpuRestore', \ + avx_save_size, 'AvxSaveSize', \ + avx_save, 'AvxSave', \ + avx_restore, 'AvxRestore', \ + r_f_port_area, 'ReservePortArea', \ + boot_log, 'Boot_Log', \ +\ + load_cursor, 'LoadCursor', \ ;stdcall +\ + get_curr_task, 'GetCurrentTask', \ + change_task, 'ChangeTask', \ + load_file, 'LoadFile', \ ;retval eax, ebx + delay_ms, 'Sleep', \ +\ + strncat, 'strncat', \ + strncpy, 'strncpy', \ + strncmp, 'strncmp', \ + strnlen, 'strnlen', \ + strchr, 'strchr', \ + strrchr, 'strrchr', \ +\ + timer_hs, 'TimerHS', \ + timer_hs, 'TimerHs', \ ; shit happens + cancel_timer_hs, 'CancelTimerHS', \ +\ + reg_usb_driver, 'RegUSBDriver', \ + usb_open_pipe, 'USBOpenPipe', \ + usb_close_pipe, 'USBClosePipe', \ + usb_normal_transfer_async, 'USBNormalTransferAsync', \ + usb_control_async, 'USBControlTransferAsync', \ + usb_get_param, 'USBGetParam', \ + usb_hc_func, 'USBHCFunc', \ +\ +\ ; The intrakernel function of working with the file system. +\ ; Duplicates system call 70. + file_system_lfn_protected_registers, 'FS_Service', \ +\ + net_add_device, 'NetRegDev', \ + net_remove_device, 'NetUnRegDev', \ + net_ptr_to_num, 'NetPtrToNum', \ + net_link_changed, 'NetLinkChanged', \ + eth_input, 'EthInput', \ + net_buff_alloc, 'NetAlloc', \ + net_buff_free, 'NetFree', \ +\ + get_pcidev_list, 'GetPCIList', \ +\ + acpi_get_root_ptr, 'AcpiGetRootPtr', \ +\ + 0, 'LFBAddress' ; must be the last one + +load kernel_exports_count dword from __exports + 24 +load kernel_exports_addresses dword from __exports + 28 +exp_lfb = OS_BASE + kernel_exports_addresses + (kernel_exports_count - 1) * 4 - 4 diff --git a/kernel/branches/kolibri-lldw/core/ext_lib.inc b/kernel/branches/kolibri-lldw/core/ext_lib.inc new file mode 100644 index 000000000..6398729e6 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/ext_lib.inc @@ -0,0 +1,476 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; External kernel dependencies (libraries) loading. +; The code currently does not work, requires correcting dll.inc. + +$Revision$ + +if 0 +iglobal +tmp_file_name_size dd 1 +endg + +uglobal +tmp_file_name_table dd ? +s_libname rb 64 +def_val_1 db ? +endg + +macro library [name,fname] +{ + forward + dd __#name#_library_table__,__#name#_library_name__ + common + dd 0 + forward + __#name#_library_name__ db fname,0 +} + +macro import lname,[name,sname] +{ + common + align 4 + __#lname#_library_table__: + forward + name dd __#name#_import_name__ + common + dd 0 + forward + __#name#_import_name__ db sname,0 +} + +macro export [name,sname] +{ +align 4 + forward + dd __#name#_export_name__,name + common + dd 0 + forward + __#name#_export_name__ db sname,0 +} + + + +align 4 ; loading library (use kernel functions) +proc load_k_library stdcall, file_name:dword + locals + coff dd ? + sym dd ? + strings dd ? + img_size dd ? + img_base dd ? + exports dd ? + endl + + cli + + stdcall load_file, [file_name] + test eax, eax + jz .fail + + mov [coff], eax + movzx ecx, [eax+CFH.nSections] + xor ebx, ebx + + lea edx, [eax+20] +@@: + add ebx, [edx+CFS.SizeOfRawData] + add ebx, 15 + and ebx, not 15 + add edx, COFF_SECTION_SIZE + dec ecx + jnz @B + mov [img_size], ebx + + stdcall kernel_alloc, [img_size] + + test eax, eax + jz .fail + mov [img_base], eax + + mov edx, [coff] + movzx ebx, [edx+CFH.nSections] + mov edi, [img_base] + lea eax, [edx+20] +@@: + mov [eax+CFS.VirtualAddress], edi + mov esi, [eax+CFS.PtrRawData] + test esi, esi + jnz .copy + add edi, [eax+CFS.SizeOfRawData] + jmp .next +.copy: + add esi, edx + mov ecx, [eax+CFS.SizeOfRawData] + cld + rep movsb +.next: + add edi, 15 + and edi, not 15 + add eax, COFF_SECTION_SIZE + dec ebx + jnz @B + + mov ebx, [edx+CFH.pSymTable] + add ebx, edx + mov [sym], ebx + mov ecx, [edx+CFH.nSymbols] + add ecx, ecx + lea ecx, [ecx+ecx*8];ecx*=18 = nSymbols*CSYM_SIZE + add ecx, [sym] + mov [strings], ecx + + lea eax, [edx+20] + + stdcall fix_coff_symbols, eax, [sym], [edx+CFH.nSymbols], \ + [strings], dword 0 + test eax, eax + jnz @F + +@@: + mov edx, [coff] + movzx ebx, [edx+CFH.nSections] + mov edi, 0 + lea eax, [edx+20] +@@: + add [eax+CFS.VirtualAddress], edi ;patch user space offset + add eax, COFF_SECTION_SIZE + dec ebx + jnz @B + + add edx, 20 + stdcall fix_coff_relocs, [coff], edx, [sym] + + mov ebx, [coff] + stdcall get_coff_sym, [sym], [ebx+CFH.nSymbols], szEXPORTS + mov [exports], eax + + stdcall kernel_free, [coff] + + mov eax, [exports] + ret +.fail: + xor eax, eax + ret +endp + + +proc dll.Load, import_table:dword + mov esi, [import_table] + .next_lib: + mov edx, [esi] + or edx, edx + jz .exit + push esi + + mov edi, s_libname + + mov al, '/' + stosb + mov esi, sysdir_path + @@: + lodsb + stosb + or al, al + jnz @b + dec edi + mov [edi], dword '/lib' + mov [edi+4], byte '/' + add edi, 5 + pop esi + push esi + mov esi, [esi+4] + @@: + lodsb + stosb + or al, al + jnz @b + + pushad + stdcall load_k_library, s_libname + mov [esp+28], eax + popad + or eax, eax + jz .fail + stdcall dll.Link, eax, edx + stdcall dll.Init, [eax+4] + pop esi + add esi, 8 + jmp .next_lib + .exit: + xor eax, eax + ret + .fail: + add esp, 4 + xor eax, eax + inc eax + ret +endp + +proc dll.Link, exp:dword,imp:dword + push eax + mov esi, [imp] + test esi, esi + jz .done + .next: + lodsd + test eax, eax + jz .done + stdcall dll.GetProcAddress, [exp], eax + or eax, eax + jz @f + mov [esi-4], eax + jmp .next + @@: + mov dword[esp], 0 + .done: + pop eax + ret +endp + +proc dll.Init, dllentry:dword + pushad + mov eax, mem.Alloc + mov ebx, mem.Free + mov ecx, mem.ReAlloc + mov edx, dll.Load + stdcall [dllentry] + popad + ret +endp + +proc dll.GetProcAddress, exp:dword,sz_name:dword + mov edx, [exp] + .next: + test edx, edx + jz .end + stdcall strncmp, [edx], [sz_name], dword -1 + test eax, eax + jz .ok + add edx, 8 + jmp .next + .ok: + mov eax, [edx+4] + .end: + ret +endp + +;----------------------------------------------------------------------------- +proc mem.Alloc size ;///////////////////////////////////////////////////////// +;----------------------------------------------------------------------------- + push ebx ecx +; mov eax,[size] +; lea ecx,[eax+4+4095] +; and ecx,not 4095 +; stdcall kernel_alloc, ecx +; add ecx,-4 +; mov [eax],ecx +; add eax,4 + + stdcall kernel_alloc, [size] + + pop ecx ebx + ret +endp + +;----------------------------------------------------------------------------- +proc mem.ReAlloc mptr,size;/////////////////////////////////////////////////// +;----------------------------------------------------------------------------- + push ebx ecx esi edi eax + mov eax, [mptr] + mov ebx, [size] + or eax, eax + jz @f + lea ecx, [ebx+4+4095] + and ecx, not 4095 + add ecx, -4 + cmp ecx, [eax-4] + je .exit + @@: + mov eax, ebx + call mem.Alloc + xchg eax, [esp] + or eax, eax + jz .exit + mov esi, eax + xchg eax, [esp] + mov edi, eax + mov ecx, [esi-4] + cmp ecx, [edi-4] + jbe @f + mov ecx, [edi-4] + @@: + add ecx, 3 + shr ecx, 2 + cld + rep movsd + xchg eax, [esp] + call mem.Free + .exit: + pop eax edi esi ecx ebx + ret +endp + +;----------------------------------------------------------------------------- +proc mem.Free mptr ;////////////////////////////////////////////////////////// +;----------------------------------------------------------------------------- +; mov eax,[mptr] +; or eax,eax +; jz @f +; push ebx ecx +; lea ecx,[eax-4] +; stdcall kernel_free, ecx +; pop ecx ebx +; @@: ret + stdcall kernel_free, [mptr] + ret +endp + +proc load_file_parse_table + stdcall kernel_alloc, 0x1000 + mov [tmp_file_name_table], eax + mov edi, eax + mov esi, sysdir_name + mov ecx, 128/4 + rep movsd + invoke ini.enum_keys, conf_fname, conf_path_sect, get_every_key + mov eax, [tmp_file_name_table] + mov [full_file_name_table], eax + mov eax, [tmp_file_name_size] + mov [full_file_name_table.size], eax + ret +endp + +proc get_every_key stdcall, f_name, sec_name, key_name + mov esi, [key_name] + mov ecx, esi + cmp byte [esi], '/' + jnz @f + inc esi +@@: + mov edi, [tmp_file_name_size] + shl edi, 7 + cmp edi, 0x1000 + jae .stop_parse + add edi, [tmp_file_name_table] + lea ebx, [edi+64] +@@: + cmp edi, ebx + jae .skip_this_key + lodsb + test al, al + jz @f + or al, 20h + stosb + jmp @b + +.stop_parse: + xor eax, eax + ret + +@@: + stosb + invoke ini.get_str, [f_name], [sec_name], ecx, ebx, 64, def_val_1 + cmp byte [ebx], '/' + jnz @f + lea esi, [ebx+1] + mov edi, ebx + mov ecx, 63 + rep movsb +@@: + push ebp + mov ebp, [tmp_file_name_table] + mov ecx, [tmp_file_name_size] + jecxz .noreplace + mov eax, ecx + dec eax + shl eax, 7 + add ebp, eax +.replace_loop: + mov edi, ebx + mov esi, ebp +@@: + lodsb + test al, al + jz .doreplace + mov dl, [edi] + inc edi + test dl, dl + jz .replace_loop_cont + or dl, 20h + cmp al, dl + jz @b + jmp .replace_loop_cont + +.doreplace: + cmp byte [edi], 0 + jz @f + cmp byte [edi], '/' + jnz .replace_loop_cont +@@: + lea esi, [ebp+64] + call .replace + jc .skip_this_key2 +.replace_loop_cont: + sub ebp, 128 + loop .replace_loop +.noreplace: + pop ebp + inc [tmp_file_name_size] +.skip_this_key: + xor eax, eax + inc eax + ret + +.skip_this_key2: + pop ebp + jmp .skip_this_key +endp + +proc get_every_key.replace +; in: ebx->destination, esi->first part of name, edi->second part of name +; maximum length is 64 bytes +; out: CF=1 <=> overflow + sub esp, 64 ; allocate temporary buffer in stack + push esi + lea esi, [esp+4] ; esi->tmp buffer + xchg esi, edi ; edi->tmp buffer, esi->source +@@: ; save second part of name to temporary buffer + lodsb + stosb + test al, al + jnz @b + pop esi + mov edi, ebx +@@: ; copy first part of name to destination + lodsb + test al, al + jz @f + stosb + jmp @b + +@@: ; restore second part of name from temporary buffer to destination + lea edx, [ebx+64] ; limit of destination + mov esi, esp +@@: + cmp edi, edx + jae .overflow + lodsb + stosb + test al, al + jnz @b + add esp, 64 ; CF is cleared + ret + +.overflow: ; name is too long + add esp, 64 + stc + ret +endp +end if diff --git a/kernel/branches/kolibri-lldw/core/fpu.inc b/kernel/branches/kolibri-lldw/core/fpu.inc new file mode 100644 index 000000000..6c109999b --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/fpu.inc @@ -0,0 +1,419 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2017. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +init_fpu: + clts + fninit + + bt [cpu_caps+(CAPS_XSAVE/32)*4], CAPS_XSAVE mod 32 + jnc .no_xsave + + mov ecx, cr4 + or ecx, CR4_OSXSAVE + mov cr4, ecx + ; don't call cpuid again + bts [cpu_caps+(CAPS_OSXSAVE/32)*4], CAPS_OSXSAVE mod 32 + + ; zero xsave header + mov ecx, 64/4 + xor eax, eax + mov edi, fpu_data + 512 ; skip legacy region + rep stosd + + mov eax, 0x0d ; extended state enumeration main leaf + xor ecx, ecx + cpuid + and eax, XCR0_FPU_MMX + XCR0_SSE + XCR0_AVX + XCR0_AVX512 + xor edx, edx + mov [xsave_eax], eax + mov [xsave_edx], edx + xor ecx, ecx + xsetbv + + mov eax, 0x0d + xor ecx, ecx + cpuid + add ebx, 63 + and ebx, NOT 63 + mov [xsave_area_size], ebx + cmp ebx, fpu_data_size + ja $ + + test eax, XCR0_AVX512 + jz @f + call init_avx512 + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [fpu_data] + ret +@@: + test eax, XCR0_AVX + jz @f + call init_avx + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [fpu_data] + ret +@@: + test eax, XCR0_SSE + jz $ + call init_sse + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [fpu_data] + ret +.no_xsave: + mov [xsave_area_size], 512 ; enough for FPU/MMX and SSE + bt [cpu_caps], CAPS_SSE + jnc .fpu_mmx +.sse: + call init_sse + fxsave [fpu_data] + ret +.fpu_mmx: + call init_fpu_mmx + fnsave [fpu_data] + ret + +init_fpu_mmx: + mov ecx, cr0 + and ecx, not CR0_EM + or ecx, CR0_MP + CR0_NE + mov cr0, ecx + ret + +init_sse: + mov ebx, cr4 + mov ecx, cr0 + or ebx, CR4_OSFXSR + CR4_OSXMMEXPT + mov cr4, ebx + + and ecx, not (CR0_EM + CR0_MP) + or ecx, CR0_NE + mov cr0, ecx + + mov dword [esp-4], MXCSR_INIT + ldmxcsr [esp-4] + + xorps xmm0, xmm0 + xorps xmm1, xmm1 + xorps xmm2, xmm2 + xorps xmm3, xmm3 + xorps xmm4, xmm4 + xorps xmm5, xmm5 + xorps xmm6, xmm6 + xorps xmm7, xmm7 + ret + +init_avx: + mov ebx, cr4 + or ebx, CR4_OSFXSR + CR4_OSXMMEXPT + mov cr4, ebx + + mov ecx, cr0 + and ecx, not (CR0_EM + CR0_MP) + or ecx, CR0_NE + mov cr0, ecx + + mov dword [esp-4], MXCSR_INIT + vldmxcsr [esp-4] + + vzeroall + ret + +init_avx512: + mov ebx, cr4 + or ebx, CR4_OSFXSR + CR4_OSXMMEXPT + mov cr4, ebx + + mov ecx, cr0 + and ecx, not (CR0_EM + CR0_MP) + or ecx, CR0_NE + mov cr0, ecx + + mov dword [esp-4], MXCSR_INIT + vldmxcsr [esp-4] + + vpxorq zmm0, zmm0, zmm0 + vpxorq zmm1, zmm1, zmm1 + vpxorq zmm2, zmm2, zmm2 + vpxorq zmm3, zmm3, zmm3 + vpxorq zmm4, zmm4, zmm4 + vpxorq zmm5, zmm5, zmm5 + vpxorq zmm6, zmm6, zmm6 + vpxorq zmm7, zmm7, zmm7 + + ret + +; param +; eax= 512 bytes memory area aligned on a 16-byte boundary + +align 4 +fpu_save: + push ecx + push esi + push edi + + pushfd + cli + + clts + mov edi, eax + + mov ecx, [fpu_owner] + mov esi, [current_slot_idx] + cmp ecx, esi + jne .save + + call save_fpu_context + jmp .exit +.save: + mov [fpu_owner], esi + + shl ecx, 8 + mov eax, [ecx+SLOT_BASE+APPDATA.fpu_state] + + call save_context + +; first 512 bytes of XSAVE area have the same format as FXSAVE + shl esi, 8 + mov esi, [esi+SLOT_BASE+APPDATA.fpu_state] + mov ecx, 512/4 + cld + rep movsd + fninit +.exit: + popfd + pop edi + pop esi + pop ecx + ret + +avx_save_size: + mov eax, [xsave_area_size] + ret + +; param +; eax= avx_save_size() bytes memory area aligned on a 64-byte boundary + +align 4 +avx_save: + push ecx + push esi + push edi + + pushfd + cli + + clts + mov edi, eax + + mov ecx, [fpu_owner] + mov esi, [current_slot_idx] + cmp ecx, esi + jne .save + + call save_context + jmp .exit +.save: + mov [fpu_owner], esi + + shl ecx, 8 + mov eax, [ecx+SLOT_BASE+APPDATA.fpu_state] + + call save_context + + shl esi, 8 + mov esi, [esi+SLOT_BASE+APPDATA.fpu_state] + mov ecx, [xsave_area_size] + add ecx, 3 + shr ecx, 2 + rep movsd + fninit +.exit: + popfd + pop edi + pop esi + pop ecx + ret + +align 4 +save_context: + bt [cpu_caps+(CAPS_OSXSAVE/32)*4], CAPS_OSXSAVE mod 32 + jnc save_fpu_context + push eax edx + mov ecx, eax + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [ecx] + pop edx eax + ret +save_fpu_context: + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + fxsave [eax] + ret +.no_SSE: + fnsave [eax] + ret + + +align 4 +fpu_restore: + push ecx + push esi + + mov esi, eax + + pushfd + cli + + mov ecx, [fpu_owner] + mov eax, [current_slot_idx] + cmp ecx, eax + jne .copy + + clts + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxrstor [esi] + popfd + pop esi + pop ecx + ret +.no_SSE: + fnclex ;fix possible problems + frstor [esi] + popfd + pop esi + pop ecx + ret +.copy: + shl eax, 8 + mov edi, [eax+SLOT_BASE+APPDATA.fpu_state] + mov ecx, 512/4 + cld + rep movsd + popfd + pop esi + pop ecx + ret + +align 4 +avx_restore: + push ecx + push esi + + mov esi, eax + + pushfd + cli + + mov ecx, [fpu_owner] + mov eax, [current_slot_idx] + cmp ecx, eax + jne .copy + + clts + bt [cpu_caps+(CAPS_OSXSAVE/32)*4], CAPS_OSXSAVE mod 32 + jnc .no_xsave + push edx + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xrstor [esi] + pop edx + popfd + pop esi + pop ecx + ret +.no_xsave: + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxrstor [esi] + popfd + pop esi + pop ecx + ret +.no_SSE: + fnclex ;fix possible problems + frstor [esi] + popfd + pop esi + pop ecx + ret +.copy: + shl eax, 8 + mov edi, [eax+SLOT_BASE+APPDATA.fpu_state] + mov ecx, [xsave_area_size] + add ecx, 3 + shr ecx, 2 + cld + rep movsd + popfd + pop esi + pop ecx + ret + +align 4 +except_7: ;#NM exception handler + save_ring3_context + clts + mov ax, app_data; + mov ds, ax + mov es, ax + + mov ebx, [fpu_owner] + cmp ebx, [current_slot_idx] + je .exit + + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + bt [cpu_caps+(CAPS_OSXSAVE/32)*4], CAPS_OSXSAVE mod 32 + jnc .no_xsave + mov ecx, eax + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [ecx] + mov ebx, [current_slot_idx] + mov [fpu_owner], ebx + shl ebx, 8 + mov ecx, [ebx+SLOT_BASE+APPDATA.fpu_state] + xrstor [ecx] +.exit: + restore_ring3_context + iret +.no_xsave: + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + + fxsave [eax] + mov ebx, [current_slot_idx] + mov [fpu_owner], ebx + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + fxrstor [eax] + restore_ring3_context + iret + +.no_SSE: + fnsave [eax] + mov ebx, [current_slot_idx] + mov [fpu_owner], ebx + shl ebx, 8 + mov eax, [ebx+SLOT_BASE+APPDATA.fpu_state] + frstor [eax] + restore_ring3_context + iret + +iglobal + fpu_owner dd 2 +endg diff --git a/kernel/branches/kolibri-lldw/core/heap.inc b/kernel/branches/kolibri-lldw/core/heap.inc new file mode 100644 index 000000000..444f0ffef --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/heap.inc @@ -0,0 +1,1589 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +struct MEM_BLOCK + list LHEAD + next_block dd ? ;+8 + prev_block dd ? ;+4 + base dd ? ;+16 + size dd ? ;+20 + flags dd ? ;+24 + handle dd ? ;+28 +ends + +MEM_BLOCK_RESERVED = 0x02 ; Will be allocated on first access (lazy allocation) +MEM_BLOCK_FREE = 0x04 +MEM_BLOCK_USED = 0x08 +MEM_BLOCK_DONT_FREE = 0x10 + +macro calc_index op +{ shr op, 12 + dec op + cmp op, 63 + jna @f + mov op, 63 +@@: +} + +align 4 +md: +.add_to_used: + mov eax, [esi + MEM_BLOCK.base] + mov ebx, [esi + MEM_BLOCK.base] + shr ebx, 6 + add eax, ebx + shr ebx, 6 + add eax, ebx + shr eax, 12 + and eax, 63 + inc [mem_hash_cnt + eax*4] + + lea ecx, [mem_used_list + eax*8] + list_add esi, ecx + mov [esi + MEM_BLOCK.flags], MEM_BLOCK_USED + mov eax, [esi + MEM_BLOCK.size] + sub [heap_free], eax + ret +align 4 +.find_used: + mov ecx, eax + mov ebx, eax + shr ebx, 6 + add ecx, ebx + shr ebx, 6 + add ecx, ebx + shr ecx, 12 + and ecx, 63 + + lea ebx, [mem_used_list + ecx*8] + mov esi, ebx +.next: + mov esi, [esi + MEM_BLOCK.list.next] + cmp esi, ebx + je .fail + + cmp eax, [esi + MEM_BLOCK.base] + jne .next + + ret +.fail: + xor esi, esi + ret + +align 4 +.del_from_used: + call .find_used + test esi, esi + jz .done + + cmp [esi + MEM_BLOCK.flags], MEM_BLOCK_USED + jne .fatal + + dec [mem_hash_cnt + ecx*4] + list_del esi +.done: + ret +.fatal: ;FIXME panic here + xor esi, esi + ret + +;Initial heap state +; +; + heap_size terminator MEM_BLOCK_USED +; + 4096*MEM_BLOCK.sizeof free space MEM_BLOCK_FREE +;HEAP_BASE heap_descriptors MEM_BLOCK_USED +; + +align 4 +proc init_kernel_heap + + mov ecx, 64 + mov edi, mem_block_list + @@: + mov eax, edi + stosd + stosd + loop @B + + mov ecx, 64 + mov edi, mem_used_list + @@: + mov eax, edi + stosd + stosd + loop @B + + stdcall alloc_pages, dword 32 + + or eax, PG_SWR + mov ebx, HEAP_BASE + mov ecx, 32 + call commit_pages + + mov edi, HEAP_BASE ;descriptors + mov ebx, HEAP_BASE + sizeof.MEM_BLOCK ;free space + mov ecx, HEAP_BASE + sizeof.MEM_BLOCK*2 ;terminator + + xor eax, eax + mov [edi + MEM_BLOCK.next_block], ebx + mov [edi + MEM_BLOCK.prev_block], eax + mov [edi + MEM_BLOCK.list.next], eax + mov [edi + MEM_BLOCK.list.prev], eax + mov [edi + MEM_BLOCK.base], HEAP_BASE + mov [edi + MEM_BLOCK.size], 4096*sizeof.MEM_BLOCK + mov [edi + MEM_BLOCK.flags], MEM_BLOCK_USED + + mov [ecx + MEM_BLOCK.next_block], eax + mov [ecx + MEM_BLOCK.prev_block], ebx + mov [ecx + MEM_BLOCK.list.next], eax + mov [ecx + MEM_BLOCK.list.prev], eax + mov [ecx + MEM_BLOCK.base], eax + mov [ecx + MEM_BLOCK.size], eax + mov [ecx + MEM_BLOCK.flags], MEM_BLOCK_USED + + mov [ebx + MEM_BLOCK.next_block], ecx + mov [ebx + MEM_BLOCK.prev_block], edi + mov [ebx + MEM_BLOCK.base], HEAP_BASE + 4096*sizeof.MEM_BLOCK + + mov ecx, [pg_data.kernel_pages] + shl ecx, 12 + sub ecx, HEAP_BASE-OS_BASE + 4096*sizeof.MEM_BLOCK + mov [heap_size], ecx + mov [heap_free], ecx + mov [ebx + MEM_BLOCK.size], ecx + mov [ebx + MEM_BLOCK.flags], MEM_BLOCK_FREE + + mov [mem_block_mask], eax + mov [mem_block_mask + 4], 0x80000000 + + mov ecx, mem_block_list + 63*8 + list_add ebx, ecx + + mov ecx, 4096-3-1 + mov eax, HEAP_BASE + sizeof.MEM_BLOCK*4 + + mov [next_memblock], HEAP_BASE + sizeof.MEM_BLOCK *3 + @@: + mov [eax-sizeof.MEM_BLOCK], eax + add eax, sizeof.MEM_BLOCK + loop @B + + mov dword[eax-sizeof.MEM_BLOCK], 0 + + mov ecx, heap_mutex + call mutex_init + mov [heap_blocks], 4094 + mov [free_blocks], 4093 + ret +endp + +; param +; eax= required size +; +; retval +; edi= memory block descriptor +; ebx= descriptor index + +align 4 +get_small_block: + mov ecx, eax + shr ecx, 12 + dec ecx + cmp ecx, 63 + jle .get_index + mov ecx, 63 + .get_index: + lea esi, [mem_block_mask] + xor ebx, ebx + or edx, -1 + + cmp ecx, 32 + jb .bit_test + + sub ecx, 32 + add ebx, 32 + add esi, 4 + .bit_test: + shl edx, cl + and edx, [esi] + .find: + bsf edi, edx + jz .high_mask + add ebx, edi + lea ecx, [mem_block_list + ebx*8] + mov edi, ecx + .next: + mov edi, [edi + MEM_BLOCK.list.next] + cmp edi, ecx + je .err + cmp eax, [edi + MEM_BLOCK.size] + ja .next + ret + .err: + xor edi, edi + ret + + .high_mask: + add esi, 4 + cmp esi, mem_block_mask + 8 + jae .err + add ebx, 32 + mov edx, [esi] + jmp .find + + +align 4 +free_mem_block: + + mov ebx, [next_memblock] + mov [eax], ebx + mov [next_memblock], eax + + xor ebx, ebx + mov dword[eax + 4], ebx + mov dword[eax + 8], ebx + mov dword[eax + 12], ebx + mov dword[eax + 16], ebx +; mov dword[eax + 20], 0 ;don't clear block size + mov dword[eax + 24], ebx + mov dword[eax + 28], ebx + + inc [free_blocks] + + ret + +align 4 +proc alloc_kernel_space stdcall, size:dword + local block_ind:DWORD + + push ebx + push esi + push edi + + mov eax, [size] + add eax, 4095 + and eax, not 4095 + mov [size], eax + + cmp eax, [heap_free] + ja .error + + spin_lock_irqsave heap_mutex + + mov eax, [size] + + call get_small_block ; eax + test edi, edi + jz .error_unlock + + cmp [edi + MEM_BLOCK.flags], MEM_BLOCK_FREE + jne .error_unlock + + mov [block_ind], ebx ;index of allocated block + + mov eax, [edi + MEM_BLOCK.size] + cmp eax, [size] + je .m_eq_size + + mov esi, [next_memblock] ;new memory block + test esi, esi + jz .error_unlock + + dec [free_blocks] + mov eax, [esi] + mov [next_memblock], eax + + mov [esi + MEM_BLOCK.next_block], edi + mov eax, [edi + MEM_BLOCK.prev_block] + mov [esi + MEM_BLOCK.prev_block], eax + mov [edi + MEM_BLOCK.prev_block], esi + mov [esi + MEM_BLOCK.list.next], 0 + mov [esi + MEM_BLOCK.list.prev], 0 + mov [eax + MEM_BLOCK.next_block], esi + + mov ebx, [edi + MEM_BLOCK.base] + mov [esi + MEM_BLOCK.base], ebx + mov edx, [size] + mov [esi + MEM_BLOCK.size], edx + add [edi + MEM_BLOCK.base], edx + sub [edi + MEM_BLOCK.size], edx + + mov eax, [edi + MEM_BLOCK.size] + calc_index eax + cmp eax, [block_ind] + je .add_used + + list_del edi + + mov ecx, [block_ind] + lea edx, [mem_block_list + ecx*8] + cmp edx, [edx] + jnz @f + btr [mem_block_mask], ecx +@@: + bts [mem_block_mask], eax + lea edx, [mem_block_list + eax*8] ;edx= list head + list_add edi, edx +.add_used: + + call md.add_to_used + + spin_unlock_irqrestore heap_mutex + mov eax, [esi + MEM_BLOCK.base] + pop edi + pop esi + pop ebx + ret + +.m_eq_size: + list_del edi + lea edx, [mem_block_list + ebx*8] + cmp edx, [edx] + jnz @f + btr [mem_block_mask], ebx +@@: + mov esi, edi + jmp .add_used + +.error_unlock: + spin_unlock_irqrestore heap_mutex +.error: + xor eax, eax + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc free_kernel_space stdcall uses ebx ecx edx esi edi, base:dword + + spin_lock_irqsave heap_mutex + + mov eax, [base] + + call md.del_from_used + test esi, esi + jz .fail + + mov eax, [esi + MEM_BLOCK.size] + add [heap_free], eax + + mov edi, [esi + MEM_BLOCK.next_block] + cmp [edi + MEM_BLOCK.flags], MEM_BLOCK_FREE + jne .prev + + list_del edi + + mov edx, [edi + MEM_BLOCK.next_block] + mov [esi + MEM_BLOCK.next_block], edx + mov [edx + MEM_BLOCK.prev_block], esi + mov ecx, [edi + MEM_BLOCK.size] + add [esi + MEM_BLOCK.size], ecx + + calc_index ecx + + lea edx, [mem_block_list + ecx*8] + cmp edx, [edx] + jne @F + btr [mem_block_mask], ecx +@@: + mov eax, edi + call free_mem_block +.prev: + mov edi, [esi + MEM_BLOCK.prev_block] + cmp [edi + MEM_BLOCK.flags], MEM_BLOCK_FREE + jne .insert + + mov edx, [esi + MEM_BLOCK.next_block] + mov [edi + MEM_BLOCK.next_block], edx + mov [edx + MEM_BLOCK.prev_block], edi + + mov eax, esi + call free_mem_block + + mov ecx, [edi + MEM_BLOCK.size] + mov eax, [esi + MEM_BLOCK.size] + add eax, ecx + mov [edi + MEM_BLOCK.size], eax + + calc_index eax ;new index + calc_index ecx ;old index + cmp eax, ecx + je .m_eq + + push ecx + list_del edi + pop ecx + + lea edx, [mem_block_list + ecx*8] + cmp edx, [edx] + jne .add_block + btr [mem_block_mask], ecx + +.add_block: + bts [mem_block_mask], eax + lea edx, [mem_block_list + eax*8] + list_add edi, edx +.m_eq: + spin_unlock_irqrestore heap_mutex + xor eax, eax + not eax + ret +.insert: + mov [esi + MEM_BLOCK.flags], MEM_BLOCK_FREE + mov eax, [esi + MEM_BLOCK.size] + calc_index eax + mov edi, esi + jmp .add_block + +.fail: + spin_unlock_irqrestore heap_mutex + xor eax, eax + ret +endp + +align 4 +proc kernel_alloc stdcall, size:dword + locals + lin_addr dd ? + pages_count dd ? + endl + + push ebx + push edi + + mov eax, [size] + add eax, 4095 + and eax, not 4095; + mov [size], eax + and eax, eax + jz .err + mov ebx, eax + shr ebx, 12 + mov [pages_count], ebx + + stdcall alloc_kernel_space, eax + mov [lin_addr], eax + mov ebx, [pages_count] + test eax, eax + jz .err + + mov edx, eax + + shr ebx, 3 + jz .tail + + shl ebx, 3 + stdcall alloc_pages, ebx + test eax, eax + jz .err + + mov ecx, ebx + or eax, PG_GLOBAL + PG_SWR + mov ebx, [lin_addr] + call commit_pages + + mov edx, ebx ; this dirty hack +.tail: + mov ebx, [pages_count] + and ebx, 7 + jz .end +@@: + call alloc_page + test eax, eax + jz .err + + stdcall map_page, edx, eax, dword (PG_GLOBAL + PG_SWR) + add edx, 0x1000 + dec ebx + jnz @B +.end: + mov eax, [lin_addr] + pop edi + pop ebx + ret +.err: + xor eax, eax + pop edi + pop ebx + ret +endp + +align 4 +proc kernel_free stdcall, base:dword + + push ebx esi + + spin_lock_irqsave heap_mutex + + mov eax, [base] + call md.find_used + + cmp [esi + MEM_BLOCK.flags], MEM_BLOCK_USED + jne .fail + + spin_unlock_irqrestore heap_mutex + + mov eax, [esi + MEM_BLOCK.base] + mov ecx, [esi + MEM_BLOCK.size] + shr ecx, 12 + call release_pages ;eax, ecx + stdcall free_kernel_space, [base] + pop esi ebx + ret +.fail: + spin_unlock_irqrestore heap_mutex + xor eax, eax + pop esi ebx + ret +endp + +;;;;;;;;;;;;;; USER HEAP ;;;;;;;;;;;;;;;;; + +HEAP_TOP = 0x80000000 + +align 4 +proc init_heap + + mov ebx, [current_process] + mov eax, [ebx + PROC.heap_top] + test eax, eax + jz @F + sub eax, [ebx + PROC.heap_base] + sub eax, PAGE_SIZE + ret +@@: + lea ecx, [ebx + PROC.heap_lock] + call mutex_init + + mov esi, [ebx + PROC.mem_used] + add esi, 4095 + and esi, not 4095 + mov [ebx + PROC.mem_used], esi + mov eax, HEAP_TOP + mov [ebx + PROC.heap_base], esi + mov [ebx + PROC.heap_top], eax + + sub eax, esi + shr esi, 10 + mov ecx, eax + sub eax, PAGE_SIZE + or ecx, MEM_BLOCK_FREE + mov [page_tabs + esi], ecx + ret +endp + +align 4 +proc user_alloc stdcall, alloc_size:dword + + push ebx esi edi + + mov ebx, [current_process] + lea ecx, [ebx + PROC.heap_lock] + call mutex_lock + + mov ecx, [alloc_size] + add ecx, (4095 + PAGE_SIZE) + and ecx, not 4095 + mov esi, [ebx + PROC.heap_base] + mov edi, [ebx + PROC.heap_top] + .scan: + cmp esi, edi + jae .m_exit + + mov ebx, esi + shr ebx, 12 + mov eax, [page_tabs + ebx*4] + test al, MEM_BLOCK_FREE + jz .test_used + and eax, 0xFFFFF000 + cmp eax, ecx ;alloc_size + jb .m_next + jz @f + + lea edx, [esi + ecx] + sub eax, ecx + or al, MEM_BLOCK_FREE + shr edx, 12 + mov [page_tabs + edx*4], eax + @@: + or ecx, MEM_BLOCK_USED + mov [page_tabs + ebx*4], ecx + shr ecx, 12 + inc ebx + dec ecx + jz .no + @@: + mov dword [page_tabs + ebx*4], MEM_BLOCK_RESERVED + inc ebx + dec ecx + jnz @B + .no: + + mov edx, [current_process] + mov ebx, [alloc_size] + add ebx, 0xFFF + and ebx, not 0xFFF + add [edx + PROC.mem_used], ebx + + lea ecx, [edx + PROC.heap_lock] + call mutex_unlock + + lea eax, [esi + 4096] + + pop edi + pop esi + pop ebx + ret +.test_used: + test al, MEM_BLOCK_USED + jz .m_exit + + and eax, 0xFFFFF000 ; not PAGESIZE +.m_next: + add esi, eax + jmp .scan +.m_exit: + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + + xor eax, eax + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc user_alloc_at stdcall, address:dword, alloc_size:dword + + push ebx + push esi + push edi + + mov ebx, [current_process] + lea ecx, [ebx + PROC.heap_lock] + call mutex_lock + + mov edx, [address] + and edx, not 0xFFF + mov [address], edx + sub edx, 0x1000 + jb .error + mov esi, [ebx + PROC.heap_base] + mov edi, [ebx + PROC.heap_top] + cmp edx, esi + jb .error +.scan: + cmp esi, edi + jae .error + mov ebx, esi + shr ebx, 12 + mov eax, [page_tabs + ebx*4] + mov ecx, eax + and ecx, 0xFFFFF000 + add ecx, esi + cmp edx, ecx + jb .found + mov esi, ecx + jmp .scan +.error: + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + + xor eax, eax + pop edi + pop esi + pop ebx + ret +.found: + test al, MEM_BLOCK_FREE + jz .error + mov eax, ecx + sub eax, edx + sub eax, 0x1000 + cmp eax, [alloc_size] + jb .error + +; Here we have 1 big free block which includes requested area. +; In general, 3 other blocks must be created instead: +; free at [esi, edx); +; busy at [edx, edx + 0x1000 + ALIGN_UP(alloc_size,0x1000)); +; free at [edx + 0x1000 + ALIGN_UP(alloc_size,0x1000), ecx) +; First or third block (or both) may be absent. + mov eax, edx + sub eax, esi + jz .nofirst + or al, MEM_BLOCK_FREE + mov [page_tabs + ebx*4], eax + .nofirst: + mov eax, [alloc_size] + add eax, 0x1FFF + and eax, not 0xFFF + mov ebx, edx + add edx, eax + shr ebx, 12 + or al, MEM_BLOCK_USED + mov [page_tabs + ebx*4], eax + shr eax, 12 + dec eax + jz .second_nofill + inc ebx + .fill: + mov dword [page_tabs + ebx*4], MEM_BLOCK_RESERVED + inc ebx + dec eax + jnz .fill + + .second_nofill: + sub ecx, edx + jz .nothird + or cl, MEM_BLOCK_FREE + mov [page_tabs + ebx*4], ecx + + .nothird: + mov edx, [current_process] + mov ebx, [alloc_size] + add ebx, 0xFFF + and ebx, not 0xFFF + add [edx + PROC.mem_used], ebx + + lea ecx, [edx + PROC.heap_lock] + call mutex_unlock + + mov eax, [address] + + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc user_free stdcall, base:dword + + push esi + + mov esi, [base] + test esi, esi + jz .fail + + push ebx + + mov ebx, [current_process] + lea ecx, [ebx + PROC.heap_lock] + call mutex_lock + + xor ebx, ebx + shr esi, 12 + mov eax, [page_tabs + (esi-1)*4] + test al, MEM_BLOCK_USED + jz .cantfree + test al, MEM_BLOCK_DONT_FREE + jnz .cantfree + + and eax, not 4095 + mov ecx, eax + or al, MEM_BLOCK_FREE + mov [page_tabs + (esi-1)*4], eax + sub ecx, 4096 + mov ebx, ecx + shr ecx, 12 + jz .released + .release: + xor eax, eax + xchg eax, [page_tabs + esi*4] + test al, 1 + jz @F + test eax, PG_SHARED + jnz @F + call free_page + mov eax, esi + shl eax, 12 + invlpg [eax] + @@: + inc esi + dec ecx + jnz .release + + .released: + push edi + + mov edx, [current_process] + lea ecx, [edx + PROC.heap_lock] + mov esi, dword [edx + PROC.heap_base] + mov edi, dword [edx + PROC.heap_top] + sub ebx, [edx + PROC.mem_used] + neg ebx + mov [edx + PROC.mem_used], ebx + call user_normalize + pop edi + .exit: + call mutex_unlock + + xor eax, eax + inc eax + pop ebx + pop esi + ret + + .cantfree: + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + jmp .exit + .fail: + xor eax, eax + pop esi + ret +endp + + +align 4 +proc user_unmap stdcall, base:dword, offset:dword, size:dword + + push ebx + + mov ebx, [base] ; must be valid pointer + test ebx, ebx + jz .error + + mov edx, [offset] ; check offset + add edx, ebx ; must be below 2Gb app limit + js .error + + shr ebx, 12 ; chek block attributes + lea ebx, [page_tabs + ebx*4] + mov eax, [ebx-4] ; block attributes + test al, MEM_BLOCK_USED + jz .error + test al, MEM_BLOCK_DONT_FREE + jnz .error + + shr edx, 12 + lea edx, [page_tabs + edx*4] ; unmap offset + + mov ecx, [size] + add ecx, 4095 + shr ecx, 12 ; unmap size in pages + + shr eax, 12 ; block size + 1 page + lea ebx, [ebx + eax*4-4] ; block end ptr + lea eax, [edx + ecx*4] ; unmap end ptr + + cmp eax, ebx ; check for overflow + ja .error + + mov ebx, [offset] + and ebx, not 4095 ; is it required ? + add ebx, [base] + + .unmap: + mov eax, [edx] ; get page addres + test al, 1 ; page mapped ? + jz @F + test eax, PG_SHARED ; page shared ? + jnz @F + mov dword[edx], MEM_BLOCK_RESERVED + ; mark page as reserved + invlpg [ebx] ; when we start using + call free_page ; empty c-o-w page instead this ? + @@: + add ebx, 4096 ; PAGESIZE? + add edx, 4 + dec ecx + jnz .unmap + + pop ebx + or al, 1 ; return non zero on success + ret +.error: + pop ebx + xor eax, eax ; something wrong + ret +endp + +align 4 +user_normalize: +; in: esi=heap_base, edi=heap_top +; out: eax=0 <=> OK +; destroys: ebx,edx,esi,edi + shr esi, 12 + shr edi, 12 +@@: + mov eax, [page_tabs + esi*4] + test al, MEM_BLOCK_USED + jz .test_free + shr eax, 12 + add esi, eax + jmp @B +.test_free: + test al, MEM_BLOCK_FREE + jz .err + mov edx, eax + shr edx, 12 + add edx, esi + cmp edx, edi + jae .exit + + mov ebx, [page_tabs + edx*4] + test bl, MEM_BLOCK_USED + jz .next_free + + shr ebx, 12 + add edx, ebx + mov esi, edx + jmp @B +.next_free: + test bl, MEM_BLOCK_FREE + jz .err + and dword[page_tabs + edx*4], 0 + add eax, ebx + and eax, not 4095 ; not (PAGESIZE - 1) ? + or eax, MEM_BLOCK_FREE + mov [page_tabs + esi*4], eax + jmp @B +.exit: + xor eax, eax + inc eax + ret +.err: + xor eax, eax + ret + +user_realloc: +; in: eax = pointer, ebx = new size +; out: eax = new pointer or NULL + test eax, eax + jnz @f +; realloc(NULL,sz) - same as malloc(sz) + push ebx + call user_alloc + ret +@@: + push ecx edx + + push eax + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_lock + pop eax + + lea ecx, [eax - 0x1000] + shr ecx, 12 + mov edx, [page_tabs + ecx*4] + test dl, MEM_BLOCK_USED + jnz @f +; attempt to realloc invalid pointer +.ret0: + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + + pop edx ecx + xor eax, eax + ret +@@: + test dl, MEM_BLOCK_DONT_FREE + jnz .ret0 + add ebx, 0x1FFF + shr edx, 12 + shr ebx, 12 +; edx = allocated size, ebx = new size + add edx, ecx + add ebx, ecx + cmp edx, ebx + jb .realloc_add +; release part of allocated memory +.loop: + cmp edx, ebx + jz .release_done + dec edx + xor eax, eax + xchg eax, [page_tabs + edx*4] + test al, 1 + jz .loop + call free_page + mov eax, edx + shl eax, 12 + invlpg [eax] + jmp .loop +.release_done: + sub ebx, ecx + cmp ebx, 1 + jnz .nofreeall + mov eax, [page_tabs + ecx*4] + and eax, not 0xFFF + mov edx, [current_process] + mov ebx, [edx + PROC.mem_used] + sub ebx, eax + add ebx, 0x1000 + or al, MEM_BLOCK_FREE + mov [page_tabs + ecx*4], eax + push esi edi + mov esi, [edx + PROC.heap_base] + mov edi, [edx + PROC.heap_top] + mov [edx + PROC.mem_used], ebx + call user_normalize + pop edi esi + jmp .ret0 ; all freed +.nofreeall: + sub edx, ecx + shl ebx, 12 + or ebx, MEM_BLOCK_USED + xchg [page_tabs + ecx*4], ebx + shr ebx, 12 + sub ebx, edx + push ebx ecx edx + mov edx, [current_process] + shl ebx, 12 + sub ebx, [edx + PROC.mem_used] + neg ebx + mov [edx + PROC.mem_used], ebx + pop edx ecx ebx + lea eax, [ecx + 1] + shl eax, 12 + push eax + add ecx, edx + lea edx, [ecx + ebx] + shl ebx, 12 + jz .ret + push esi + mov esi, [current_process] + mov esi, [esi + PROC.heap_top] + shr esi, 12 +@@: + cmp edx, esi + jae .merge_done + mov eax, [page_tabs + edx*4] + test al, MEM_BLOCK_USED + jnz .merge_done + and dword [page_tabs + edx*4], 0 + shr eax, 12 + add edx, eax + shl eax, 12 + add ebx, eax + jmp @b +.merge_done: + pop esi + or ebx, MEM_BLOCK_FREE + mov [page_tabs + ecx*4], ebx +.ret: + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + pop eax edx ecx + ret + +.realloc_add: +; get some additional memory + mov eax, [current_process] + mov eax, [eax + PROC.heap_top] + shr eax, 12 + cmp edx, eax + jae .cant_inplace + mov eax, [page_tabs + edx*4] + test al, MEM_BLOCK_FREE + jz .cant_inplace + shr eax, 12 + add eax, edx + sub eax, ebx + jb .cant_inplace + jz @f + shl eax, 12 + or al, MEM_BLOCK_FREE + mov [page_tabs + ebx*4], eax +@@: + mov eax, ebx + sub eax, ecx + shl eax, 12 + or al, MEM_BLOCK_USED + mov [page_tabs + ecx*4], eax + lea eax, [ecx + 1] + shl eax, 12 + push eax + push edi + lea edi, [page_tabs + edx*4] + mov eax, 2 + sub ebx, edx + mov ecx, ebx + cld + rep stosd + pop edi + mov edx, [current_process] + shl ebx, 12 + add [edx + PROC.mem_used], ebx + + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + pop eax edx ecx + ret + +.cant_inplace: + push esi edi + mov eax, [current_process] + mov esi, [eax + PROC.heap_base] + mov edi, [eax + PROC.heap_top] + shr esi, 12 + shr edi, 12 + sub ebx, ecx +.find_place: + cmp esi, edi + jae .place_not_found + mov eax, [page_tabs + esi*4] + test al, MEM_BLOCK_FREE + jz .next_place + shr eax, 12 + cmp eax, ebx + jae .place_found + add esi, eax + jmp .find_place +.next_place: + shr eax, 12 + add esi, eax + jmp .find_place +.place_not_found: + pop edi esi + jmp .ret0 +.place_found: + sub eax, ebx + jz @f + push esi + add esi, ebx + shl eax, 12 + or al, MEM_BLOCK_FREE + mov [page_tabs + esi*4], eax + pop esi +@@: + mov eax, ebx + shl eax, 12 + or al, MEM_BLOCK_USED + mov [page_tabs + esi*4], eax + inc esi + mov eax, esi + shl eax, 12 + push eax + mov eax, [page_tabs + ecx*4] + and eax, not 0xFFF + or al, MEM_BLOCK_FREE + sub edx, ecx + mov [page_tabs + ecx*4], eax + inc ecx + dec ebx + dec edx + jz .no +@@: + xor eax, eax + xchg eax, [page_tabs + ecx*4] + mov [page_tabs + esi*4], eax + mov eax, ecx + shl eax, 12 + invlpg [eax] + inc esi + inc ecx + dec ebx + dec edx + jnz @b +.no: + push ebx + mov edx, [current_process] + shl ebx, 12 + add [edx + PROC.mem_used], ebx + pop ebx +@@: + mov dword [page_tabs + esi*4], MEM_BLOCK_RESERVED + inc esi + dec ebx + jnz @b + + mov ecx, [current_process] + lea ecx, [ecx + PROC.heap_lock] + call mutex_unlock + pop eax edi esi edx ecx + ret + + + +;;;;;;;;;;;;;; SHARED MEMORY ;;;;;;;;;;;;;;;;; + + +; param +; eax= shm_map object + +align 4 +destroy_smap: + + pushfd + cli + + push esi + push edi + + mov edi, eax + mov esi, [eax + SMAP.parent] + test esi, esi + jz .done + + lock dec [esi + SMEM.refcount] + jnz .done + + mov ecx, [esi + SMEM.bk] + mov edx, [esi + SMEM.fd] + + mov [ecx + SMEM.fd], edx + mov [edx + SMEM.bk], ecx + + stdcall kernel_free, [esi + SMEM.base] + mov eax, esi + call free +.done: + mov eax, edi + call destroy_kernel_object + + pop edi + pop esi + popfd + + ret + +E_NOTFOUND = 5 +E_ACCESS = 10 +E_NOMEM = 30 +E_PARAM = 33 + +SHM_READ = 0 +SHM_WRITE = 1 + +SHM_ACCESS_MASK = 3 + +SHM_OPEN = 0 shl 2 +SHM_OPEN_ALWAYS = 1 shl 2 +SHM_CREATE = 2 shl 2 + +SHM_OPEN_MASK = 3 shl 2 + +align 4 +proc shmem_open stdcall name:dword, size:dword, access:dword + locals + action dd ? + owner_access dd ? + mapped dd ? + endl + + push ebx + push esi + push edi + + mov [mapped], 0 + mov [owner_access], 0 + + pushfd ;mutex required + cli + + mov eax, [access] + and eax, SHM_OPEN_MASK + mov [action], eax + + mov ebx, [name] + test ebx, ebx + mov edx, E_PARAM + jz .fail + + mov esi, [shmem_list.fd] +align 4 +@@: + cmp esi, shmem_list + je .not_found + + lea edx, [esi + SMEM.name]; link , base, size + stdcall strncmp, edx, ebx, 32 + test eax, eax + je .found + + mov esi, [esi + SMEM.fd] + jmp @B + +.not_found: + mov eax, [action] + + cmp eax, SHM_OPEN + mov edx, E_NOTFOUND + je .fail + + cmp eax, SHM_CREATE + mov edx, E_PARAM + je .create_shm + + cmp eax, SHM_OPEN_ALWAYS + jne .fail + +.create_shm: + + mov ecx, [size] + test ecx, ecx + jz .fail + + add ecx, 4095 + and ecx, -4096 + mov [size], ecx + + mov eax, sizeof.SMEM + call malloc + test eax, eax + mov esi, eax + mov edx, E_NOMEM + jz .fail + + stdcall kernel_alloc, [size] + test eax, eax + mov [mapped], eax + mov edx, E_NOMEM + jz .cleanup + + mov ecx, [size] + mov edx, [access] + and edx, SHM_ACCESS_MASK + + mov [esi + SMEM.base], eax + mov [esi + SMEM.size], ecx + mov [esi + SMEM.access], edx + mov [esi + SMEM.refcount], 0 + mov [esi + SMEM.name + 28], 0 + + lea eax, [esi + SMEM.name] + stdcall strncpy, eax, [name], 31 + + mov eax, [shmem_list.fd] + mov [esi + SMEM.bk], shmem_list + mov [esi + SMEM.fd], eax + + mov [eax + SMEM.bk], esi + mov [shmem_list.fd], esi + + mov [action], SHM_OPEN + mov [owner_access], SHM_WRITE + +.found: + mov eax, [action] + + cmp eax, SHM_CREATE + mov edx, E_ACCESS + je .exit + + cmp eax, SHM_OPEN + mov edx, E_PARAM + je .create_map + + cmp eax, SHM_OPEN_ALWAYS + jne .fail + +.create_map: + + mov eax, [access] + and eax, SHM_ACCESS_MASK + cmp eax, [esi + SMEM.access] + mov [access], eax + mov edx, E_ACCESS + ja .fail + + mov ebx, [current_slot_idx] + shl ebx, BSF sizeof.TASKDATA + mov ebx, [TASK_TABLE + ebx + TASKDATA.pid] + mov eax, sizeof.SMAP + + call create_kernel_object + test eax, eax + mov edi, eax + mov edx, E_NOMEM + jz .fail + + inc [esi + SMEM.refcount] + + mov [edi + SMAP.magic], 'SMAP' + mov [edi + SMAP.destroy], destroy_smap + mov [edi + SMAP.parent], esi + mov [edi + SMAP.base], 0 + + stdcall user_alloc, [esi + SMEM.size] + test eax, eax + mov [mapped], eax + mov edx, E_NOMEM + jz .cleanup2 + + mov [edi + SMAP.base], eax + + mov ecx, [esi + SMEM.size] + mov [size], ecx + + shr ecx, 12 + shr eax, 10 + + mov esi, [esi + SMEM.base] + shr esi, 10 + lea edi, [page_tabs + eax] + add esi, page_tabs + + mov edx, [access] + or edx, [owner_access] + shl edx, 1 + or edx, PG_SHARED + PG_UR +@@: + lodsd + and eax, 0xFFFFF000 + or eax, edx + stosd + loop @B + + xor edx, edx + + cmp [owner_access], 0 + jne .fail +.exit: + mov edx, [size] +.fail: + mov eax, [mapped] + + popfd + pop edi + pop esi + pop ebx + ret +.cleanup: + mov [size], edx + mov eax, esi + call free + jmp .exit + +.cleanup2: + mov [size], edx + mov eax, edi + call destroy_smap + jmp .exit +endp + +align 4 +proc shmem_close stdcall, name:dword + + mov eax, [name] + test eax, eax + jz .fail + + push esi + push edi + pushfd + cli + + mov esi, [current_slot] + add esi, APP_OBJ_OFFSET +.next: + mov eax, [esi + APPOBJ.fd] + test eax, eax + jz @F + + cmp eax, esi + mov esi, eax + je @F + + cmp [eax + SMAP.magic], 'SMAP' + jne .next + + mov edi, [eax + SMAP.parent] + test edi, edi + jz .next + + lea edi, [edi + SMEM.name] + stdcall strncmp, [name], edi, 32 + test eax, eax + jne .next + + stdcall user_free, [esi + SMAP.base] + + mov eax, esi + call [esi + APPOBJ.destroy] +@@: + popfd + pop edi + pop esi +.fail: + ret +endp + + + +proc user_ring stdcall, size:dword + +locals + virt_ptr dd ? + phys_ptr dd ? + num_pages dd ? +endl + +; Size must be an exact multiple of pagesize + mov eax, [size] + test eax, PAGE_SIZE-1 + jnz .exit + +; We must have at least one complete page + shr eax, 12 + jz .exit + mov [num_pages], eax + +; Allocate double the virtual memory + mov eax, [size] + shl eax, 1 + jz .exit + stdcall user_alloc, eax + test eax, eax + jz .exit + mov [virt_ptr], eax + +; Now allocate physical memory + stdcall alloc_pages, [num_pages] + test eax, eax + jz .exit_free_virt + mov [phys_ptr], eax + +; Map first half of virtual memory to physical memory + push ecx esi edi + mov ecx, [num_pages] + mov esi, [virt_ptr] + mov edi, [phys_ptr] + .loop1: + stdcall map_page, esi, edi, PG_UWR + add esi, PAGE_SIZE + add edi, PAGE_SIZE + dec ecx + jnz .loop1 + +; Map second half of virtual memory to same physical memory + mov ecx, [num_pages] + mov edi, [phys_ptr] + .loop2: + stdcall map_page, esi, edi, PG_UWR + add esi, PAGE_SIZE + add edi, PAGE_SIZE + dec ecx + jnz .loop2 + pop edi esi ecx + + mov eax, [virt_ptr] + ret + + .exit_free_virt: + stdcall user_free, [virt_ptr] + + .exit: + xor eax, eax + ret + +endp \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/core/hpet.inc b/kernel/branches/kolibri-lldw/core/hpet.inc new file mode 100644 index 000000000..08b504a23 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/hpet.inc @@ -0,0 +1,74 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +HPET_ID = 0x0000 +HPET_PERIOD = 0x0004 +HPET_CFG_ENABLE = 0x0001 +HPET_CFG = 0x0010 +HPET_COUNTER = 0x00f0 +HPET_T0_CFG = 0x0100 + +HPET_TN_LEVEL = 0x0002 +HPET_TN_ENABLE = 0x0004 +HPET_TN_FSB = 0x4000 + +uglobal +hpet_base rd 1 +hpet_period rd 1 +hpet_timers rd 1 +hpet_tsc_start rd 2 +endg + +align 4 +init_hpet: + mov ebx, [hpet_base] + test ebx, ebx + jz .done + + mov eax, [ebx] + and ah, 0x1F + inc ah + movzx eax, ah + mov [hpet_timers], eax + mov ecx, eax + + mov eax, [ebx+HPET_PERIOD] + xor edx, edx + shld edx, eax, 10 + shl eax, 10 + mov esi, 1000000 + div esi + mov [hpet_period], eax + + mov esi, [ebx+HPET_CFG] + and esi, not HPET_CFG_ENABLE + mov [ebx+HPET_CFG], esi ;stop main counter + + lea edx, [ebx+HPET_T0_CFG] +@@: + jcxz @F + mov eax, [edx] + and eax, not (HPET_TN_ENABLE+HPET_TN_LEVEL+HPET_TN_FSB) + mov [edx], eax + add edx, 0x20 + dec ecx + jmp @B +@@: + mov [ebx+HPET_COUNTER], ecx ;reset main counter + mov [ebx+HPET_COUNTER+4], ecx + + or esi, HPET_CFG_ENABLE + mov [ebx+HPET_CFG], esi ;and start again + +.done: + rdtsc + mov [hpet_tsc_start], eax + mov [hpet_tsc_start+4], edx + + ret diff --git a/kernel/branches/kolibri-lldw/core/irq.inc b/kernel/branches/kolibri-lldw/core/irq.inc new file mode 100644 index 000000000..deac174d5 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/irq.inc @@ -0,0 +1,294 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +IRQ_RESERVED = 56 + +IRQ_POOL_SIZE = 48 + +uglobal + +align 16 +irqh_tab rd sizeof.LHEAD * IRQ_RESERVED / 4 + +irqh_pool rd sizeof.IRQH * IRQ_POOL_SIZE /4 +next_irqh rd 1 + +irq_active_set rd (IRQ_RESERVED+31)/32 +irq_failed rd IRQ_RESERVED + +endg + +set_irq_active: + mov eax, ebp + mov ecx, ebp + shr ecx, 5 + and eax, 31 + bts [irq_active_set+ecx*4], eax + ret + +reset_irq_active: + mov eax, ebp + mov ecx, ebp + shr ecx, 5 + and eax, 31 + btr [irq_active_set+ecx*4], eax + ret + +align 4 +init_irqs: + + mov ecx, IRQ_RESERVED + mov edi, irqh_tab +@@: + mov eax, edi + stosd + stosd + loop @B + + mov ecx, IRQ_POOL_SIZE-1 + mov eax, irqh_pool+sizeof.IRQH + mov [next_irqh], irqh_pool +@@: + mov [eax-sizeof.IRQH], eax + add eax, sizeof.IRQH + loop @B + + mov [eax-sizeof.IRQH], dword 0 + ret + + +align 4 +proc attach_int_handler stdcall, irq:dword, handler:dword, user_data:dword + locals + .irqh dd ? + endl + + DEBUGF 1, "K : Attach Interrupt %d Handler %x\n", [irq], [handler] + + and [.irqh], 0 + + push ebx + + mov ebx, [irq] ;irq num + test ebx, ebx + jz .err + + cmp ebx, IRQ_RESERVED + jae .err + + mov edx, [handler] + test edx, edx + jz .err + + spin_lock_irqsave IrqsList + +;allocate handler + + mov ecx, [next_irqh] + test ecx, ecx + jz .fail + + mov eax, [ecx] + mov [next_irqh], eax + mov [.irqh], ecx + + mov [irq_failed+ebx*4], 0;clear counter + + mov eax, [user_data] + mov [ecx+IRQH.handler], edx + mov [ecx+IRQH.data], eax + and [ecx+IRQH.num_ints], 0 + + lea edx, [irqh_tab+ebx*8] + list_add_tail ecx, edx ;clobber eax + stdcall enable_irq, ebx + +.fail: + spin_unlock_irqrestore IrqsList +.err: + pop ebx + mov eax, [.irqh] + ret + +endp + +if 0 +align 4 +proc get_int_handler stdcall, irq:dword + + mov eax, [irq] + cmp eax, 15 + ja .fail + mov eax, [irq_tab + 4 * eax] + ret +.fail: + xor eax, eax + ret +endp +end if + + +align 4 +proc detach_int_handler + + ret +endp + + +macro irq_serv_h [num] { + forward +align 4 + .irq_#num : + push num + jmp .main +} + +align 16 +irq_serv: + +rept 12 irqn:1 {irq_serv_h irqn} ; 1--12 +rept 18 irqn:14 {irq_serv_h irqn} ; 14--31 (irq32 is vector 0x40) +rept 23 irqn:33 {irq_serv_h irqn} ; 33--55 + +purge irq_serv_h + +align 16 +.main: + save_ring3_context + mov ebp, [esp + 32] + mov bx, app_data;os_data + mov ds, bx + mov es, bx + + cmp [v86_irqhooks+ebp*8], 0 + jnz v86_irq + + call set_irq_active + + lea esi, [irqh_tab+ebp*8] ; esi= list head + mov ebx, esi +.next: + mov ebx, [ebx+IRQH.list.next]; ebx= irqh pointer + cmp ebx, esi + je .done + + push ebx ; FIX THIS + push edi + push esi + + push [ebx+IRQH.data] + call [ebx+IRQH.handler] + pop ecx + + pop esi + pop edi + pop ebx + + test eax, eax + jz .next + + inc [ebx+IRQH.num_ints] + call reset_irq_active + jmp .next + +.done: + call reset_irq_active + jnc .exit + +; There is at least one configuration with one device which generates IRQ +; that is not the same as it should be according to PCI config space. +; For that device, the handler is registered at wrong IRQ. +; As a workaround, when nobody acknowledges the generated IRQ, +; try to ask all other registered handlers; if some handler acknowledges +; the IRQ this time, relink it to the current IRQ list. +; To make this more reliable, for every handler keep number of times +; that it has acknowledged an IRQ, and assume that handlers with at least one +; acknowledged IRQ are registered properly. +; Note: this still isn't 100% correct, because two IRQs can fire simultaneously, +; the better way would be to find the correct IRQ, but I don't know how to do +; this in that case. + cmp ebp, 1 + jz .fail + push ebp + xor ebp, ebp +.try_other_irqs: + cmp ebp, [esp] + jz .try_next_irq + cmp ebp, 1 + jz .try_next_irq + cmp ebp, 6 + jz .try_next_irq + cmp ebp, 12 + jz .try_next_irq + cmp ebp, 14 + jz .try_next_irq + cmp ebp, 15 + jz .try_next_irq + lea esi, [irqh_tab+ebp*8] + mov ebx, esi +.try_next_handler: + mov ebx, [ebx+IRQH.list.next] + cmp ebx, esi + je .try_next_irq + cmp [ebx+IRQH.num_ints], 0 + jne .try_next_handler +; keyboard handler acknowledges everything + push [ebx+IRQH.data] + call [ebx+IRQH.handler] + pop ecx + test eax, eax + jz .try_next_handler + +.found_in_wrong_list: + DEBUGF 1,'K : warning: relinking handler from IRQ%d to IRQ%d\n',\ + ebp, [esp] + pop ebp + spin_lock_irqsave IrqsList + list_del ebx + lea edx, [irqh_tab+ebp*8] + list_add_tail ebx, edx + spin_unlock_irqrestore IrqsList + jmp .exit + +.try_next_irq: + inc ebp + cmp ebp, 16 + jb .try_other_irqs + pop ebp + +.fail: + inc [irq_failed+ebp*4] +.exit: + + mov ecx, ebp + call irq_eoi + +; IRQ handler could make some kernel thread ready; reschedule + mov bl, SCHEDULE_HIGHER_PRIORITY + call find_next_task + jz .return ; if there is only one running process + call do_change_task + .return: + restore_ring3_context + add esp, 4 + iret + +align 4 +irqD: + push eax + push ecx + xor eax, eax + out 0xf0, al + mov cl, 13 + call irq_eoi + pop ecx + pop eax + iret + diff --git a/kernel/branches/kolibri-lldw/core/malloc.inc b/kernel/branches/kolibri-lldw/core/malloc.inc new file mode 100644 index 000000000..b20eead7b --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/malloc.inc @@ -0,0 +1,1035 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Small heap based on malloc/free/realloc written by Doug Lea +; Version 2.8.3 Thu Sep 22 11:16:15 2005 Doug Lea (dl at gee) +; Source ftp://gee.cs.oswego.edu/pub/misc/malloc.c +; License http://creativecommons.org/licenses/publicdomain. + + +; eax= size + +; temp +; esi= nb +; ebx= idx +; +align 4 +malloc: + push ebx esi + +; nb = ((size+7)&~7)+8; + + mov esi, eax ;size + add esi, 7 + and esi, -8 + add esi, 8 + + mov ecx, mst.mutex + call mutex_lock + + cmp esi, 256 + jae .large + + mov ecx, esi + shr ecx, 3 + or eax, -1 + shl eax, cl + and eax, [mst.smallmap] + jz .small + + push ebp + push edi + + bsf eax, eax + mov ebx, eax + +; psize= idx<<3; +; B = &ms.smallbins[idx]; +; p = B->fd; +; F = p->fd; +; rsize= psize-nb; + + lea ebp, [eax*8] ;ebp= psize + shl eax, 4 + lea edi, [mst.smallbins+eax] ;edi= B + mov edx, [edi+8] ;edx= p + mov eax, [edx+8] ;eax= F + mov ecx, ebp + sub ecx, esi ;ecx= rsize + +; if (B == F) + cmp edi, eax + jne @F + + btr [mst.smallmap], ebx +@@: + +; B->fd = F; +; F->bk = B; +; if(rsize<16) + + cmp ecx, 16 + mov [edi+8], eax + mov [eax+12], edi + jae .split + +; p->head = psize|PINUSE_BIT|CINUSE_BIT; +; (p + psize)->head |= PINUSE_BIT; + + lea eax, [edx+8] + or dword [edx+ebp+4], 1 + + or ebp, 3 + mov [edx+4], ebp + + pop edi + pop ebp +.done: + mov esi, eax + mov ecx, mst.mutex + call mutex_unlock + mov eax, esi + pop esi ebx + ret + +.split: + lea ebx, [edx+8] ;ebx=mem + +; r = chunk_plus_offset(p, nb); +; p->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; + + lea eax, [edx+esi] ;eax= r + or esi, 3 + mov [edx+4], esi + + mov edx, ecx + or edx, 1 + mov [eax+4], edx + +; (r + rsize)->prev_foot = rsize; + + mov [eax+ecx], ecx + +; I = rsize>>3; + + shr ecx, 3 + +; ms.smallmap |= 1<< I; + bts [mst.smallmap], ecx + +; B = &ms.smallbins[I]; + + shl ecx, 4 + pop edi + pop ebp + add ecx, mst.smallbins ;ecx= B + + mov edx, [ecx+8] ; F = B->fd; + mov [ecx+8], eax ; B->fd = r; + mov [edx+12], eax ; F->bk = r; + mov [eax+8], edx ; r->fd = F; + mov [eax+12], ecx ; r->bk = B; + + mov eax, ebx + jmp .done + +.small: + +; if (ms.treemap != 0 && (mem = malloc_small(nb)) != 0) +;;;;;;;;;;; start a change + mov eax, [mst.treemap] + test eax, eax +;;;;;;;;;;; end the change +; cmp [mst.treemap], 0 + jz .from_top + mov eax, esi + call malloc_small + test eax, eax + jz .from_top + jmp .done + +.large: + +; if (ms.treemap != 0 && (mem = malloc_large(nb)) != 0) + + cmp [mst.treemap], 0 + je .from_top + + call malloc_large ;esi= nb + test eax, eax + jne .done +.from_top: + +; if (nb < ms.topsize) + + mov eax, [mst.topsize] + cmp esi, eax + jae .fail + +; rsize = ms.topsize -= nb; +; p = ms.top; + + mov ecx, [mst.top] + sub eax, esi + mov [mst.topsize], eax + +; r = ms.top = chunk_plus_offset(p, nb); +; r->head = rsize | PINUSE_BIT; +; p->head = nb |PINUSE_BIT|CINUSE_BIT; + + lea edx, [ecx+esi] + or eax, 1 + mov [mst.top], edx + or esi, 3 + mov [edx+4], eax + mov [ecx+4], esi + lea eax, [ecx+8] + jmp .done + +.fail: + xor eax, eax + jmp .done + +; param +; eax= mem +align 4 +free: + test eax, eax + jz .exit + + push ebx edi + mov edi, eax + add edi, -8 + +; if(p->head & CINUSE_BIT) + + test byte [edi+4], 2 + je .fail + + mov ecx, mst.mutex + call mutex_lock + +; psize = p->head & (~3); + + mov eax, [edi+4] + push esi + mov esi, eax + and esi, -4 + +; next = chunk_plus_offset(p, psize); +; if(!(p->head & PINUSE_BIT)) + + test al, 1 + lea ebx, [esi+edi] + jne .next + +; prevsize = p->prev_foot; +; prev=p - prevsize; +; psize += prevsize; +; p = prev; + + mov ecx, [edi] ;ecx= prevsize + add esi, ecx ;esi= psize + sub edi, ecx ;edi= p + +; if (prevsize < 256) + + cmp ecx, 256 + jae .unlink_large + + mov eax, [edi+8] ;F = p->fd; + mov edx, [edi+12] ;B = p->bk; + +; if (F == B) +; ms.smallmap &= ~(1<< I); + shr ecx, 3 + cmp eax, edx + jne @F + btr [mst.smallmap], ecx +@@: + mov [eax+12], edx ;F->bk = B; + mov [edx+8], eax ;B->fd = F + jmp .next +.unlink_large: + mov edx, edi + call unlink_large_chunk +.next: + +; if(next->head & PINUSE_BIT) + + mov eax, [ebx+4] + test al, 1 + jz .fail2 + +; if (! (next->head & CINUSE_BIT)) + + test al, 2 + jnz .fix_next + +; if (next == ms.top) + + cmp ebx, [mst.top] + jne @F + +; tsize = ms.topsize += psize; + + mov eax, [mst.topsize] + add eax, esi + mov [mst.topsize], eax + +; ms.top = p; +; p->head = tsize | PINUSE_BIT; + + or eax, 1 + mov [mst.top], edi + mov [edi+4], eax +.fail2: + mov esi, eax + mov ecx, mst.mutex + call mutex_unlock + mov eax, esi + pop esi +.fail: + pop edi ebx +.exit: + ret + +@@: + +; nsize = next->head & ~INUSE_BITS; + + and eax, -4 + add esi, eax ;psize += nsize; + +; if (nsize < 256) + + cmp eax, 256 + jae .unl_large + + mov edx, [ebx+8] ;F = next->fd + mov ebx, [ebx+12] ;B = next->bk + +; if (F == B) + + cmp edx, ebx + jne @F + mov ecx, eax + shr ecx, 3 + btr [mst.smallmap], ecx +@@: + mov [edx+12], ebx ;F->bk = B + +; p->head = psize|PINUSE_BIT; + + mov ecx, esi + mov [ebx+8], edx + or ecx, 1 + mov [edi+4], ecx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi + +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 +.unl_large: + +; unlink_large_chunk((tchunkptr)next); + + mov edx, ebx + call unlink_large_chunk +; p->head = psize|PINUSE_BIT; + + mov ecx, esi + or ecx, 1 + mov [edi+4], ecx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi + +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 +.fix_next: + +; (p+psize)->prev_foot = psize; +; next->head &= ~PINUSE_BIT; +; p->head = psize|PINUSE_BIT; + + and eax, -2 + mov edx, esi + mov [ebx+4], eax + or edx, 1 + mov [edi+4], edx + +; (p+psize)->prev_foot = psize; + + mov [esi+edi], esi +; insert_chunk(p,psize); + + mov eax, esi + mov ecx, edi + call insert_chunk + jmp .fail2 + +; param +; ecx = chunk +; eax = size + +insert_chunk: + + cmp eax, 256 + push esi + mov esi, ecx + jae .large + +; I = S>>3; +; ms.smallmap |= 1<< I; + + shr eax, 3 + bts [mst.smallmap], eax + +; B = &ms.smallbins[I]; + + shl eax, 4 + add eax, mst.smallbins + mov edx, [eax+8] ;F = B->fd + mov [eax+8], esi ;B->fd = P + mov [edx+12], esi ;F->bk = P + mov [esi+8], edx ;P->fd = F + mov [esi+12], eax ;P->bk = B + pop esi + ret +.large: + mov ebx, eax + call insert_large_chunk + pop esi + ret + + +; param +; esi= chunk +; ebx= size + +insert_large_chunk: + +; I = compute_tree_index(S); + + mov edx, ebx + shr edx, 8 + bsr eax, edx + lea ecx, [eax+7] + mov edx, ebx + shr edx, cl + and edx, 1 + lea ecx, [edx+eax*2] + +; X->index = I; + mov dword [esi+28], ecx + +; X->child[0] = X->child[1] = 0; + and dword [esi+20], 0 + and dword [esi+16], 0 + +; H = &ms.treebins[I]; + + mov eax, ecx + lea edx, [mst.treebins+eax*4] + +; if (!(ms.treemap & 1<child[(K >> 31) & 1]); + mov ecx, eax + shr ecx, 31 + lea ecx, [edx+ecx*4+16] + +; K <<= 1; +; if (*C != 0) + mov edi, [ecx] + add eax, eax + test edi, edi + jz .insert_child + +; T = *C; + mov edx, edi +.loop: + +; for (;;) +; if ((T->head & ~INUSE_BITS) != S) + + mov ecx, [edx+4] + and ecx, not 3 + cmp ecx, ebx + jne .not_eq_size + +; F = T->fd; + mov eax, [edx+8] + +; T->fd = F->bk = X; + mov [eax+12], esi + mov [edx+8], esi + +; X->fd = F; +; X->bk = T; +; X->parent = 0; + + and dword [esi+24], 0 + mov [esi+8], eax + mov [esi+12], edx + ret +.insert_child: + +; *C = X; + mov [ecx], esi +.done: + +; X->parent = T; + mov [esi+24], edx + +; X->fd = X->bk = X; + mov [esi+12], esi + mov [esi+8], esi + ret + + +; param +; edx= chunk + +unlink_large_chunk: + + mov eax, [edx+12] + cmp eax, edx + push edi + mov edi, [edx+24] + je @F + + mov ecx, [edx+8] ;F = X->fd + mov [ecx+12], eax ;F->bk = R; + mov [eax+8], ecx ;R->fd = F + jmp .parent +@@: + mov eax, [edx+20] + test eax, eax + push esi + lea esi, [edx+20] + jne .loop + + mov eax, [edx+16] + test eax, eax + lea esi, [edx+16] + je .l2 +.loop: + cmp dword [eax+20], 0 + lea ecx, [eax+20] + jne @F + + cmp dword [eax+16], 0 + lea ecx, [eax+16] + je .l1 +@@: + mov eax, [ecx] + mov esi, ecx + jmp .loop +.l1: + mov dword [esi], 0 +.l2: + pop esi +.parent: + test edi, edi + je .done + + mov ecx, [edx+28] + cmp edx, [mst.treebins+ecx*4] + lea ecx, [mst.treebins+ecx*4] + jne .l3 + + test eax, eax + mov [ecx], eax + jne .l5 + + mov ecx, [edx+28] + btr [mst.treemap], ecx + pop edi + ret + +.l3: + cmp [edi+16], edx + jne @F + + mov [edi+16], eax + jmp .l4 + +@@: + mov [edi+20], eax + +.l4: + test eax, eax + je .done + +.l5: + mov [eax+24], edi + mov ecx, [edx+16] + test ecx, ecx + je .l6 + + mov [eax+16], ecx + mov [ecx+24], eax + +.l6: + mov edx, [edx+20] + test edx, edx + je .done + + mov [eax+20], edx + mov [edx+24], eax + +.done: + pop edi + ret + +; param +; esi= nb + +malloc_small: + push ebp + mov ebp, esi + + push edi + + bsf eax, [mst.treemap] + mov ecx, [mst.treebins+eax*4] + +; rsize = (t->head & ~INUSE_BITS) - nb; + + mov edi, [ecx+4] + and edi, -4 + sub edi, esi + +.loop: + mov ebx, ecx + +.loop_1: + +; while ((t = leftmost_child(t)) != 0) + + mov eax, [ecx+16] + test eax, eax + jz @F + mov ecx, eax + jmp .l1 + +@@: + mov ecx, [ecx+20] + +.l1: + test ecx, ecx + jz .unlink + +; trem = (t->head & ~INUSE_BITS) - nb; + + mov eax, [ecx+4] + and eax, -4 + sub eax, ebp + +; if (trem < rsize) + + cmp eax, edi + jae .loop_1 + +; rsize = trem; + + mov edi, eax + jmp .loop +.unlink: + + +; r = chunk_plus_offset((mchunkptr)v, nb); +; unlink_large_chunk(v); + + mov edx, ebx + lea esi, [ebx+ebp] + call unlink_large_chunk + +; if (rsize < 16) + + cmp edi, 16 + jae .split + +; v->head = (rsize + nb)|PINUSE_BIT|CINUSE_BIT; + + lea ecx, [edi+ebp] + +; (v+rsize + nb)->head |= PINUSE_BIT; + + add edi, ebx + lea eax, [edi+ebp+4] + pop edi + or ecx, 3 + mov [ebx+4], ecx + or dword [eax], 1 + pop ebp + + lea eax, [ebx+8] + ret + +.split: + +; v->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; +; (r+rsize)->prev_foot = rsize; + + or ebp, 3 + mov edx, edi + or edx, 1 + + cmp edi, 256 + mov [ebx+4], ebp + mov [esi+4], edx + mov [esi+edi], edi + jae .large + + shr edi, 3 + bts [mst.smallmap], edi + + mov eax, edi + shl eax, 4 + add eax, mst.smallbins + + mov edx, [eax+8] + mov [eax+8], esi + mov [edx+12], esi + pop edi + mov [esi+12], eax + mov [esi+8], edx + pop ebp + lea eax, [ebx+8] + ret + +.large: + lea eax, [ebx+8] + push eax + mov ebx, edi + call insert_large_chunk + pop eax + pop edi + pop ebp + ret + + +; param +; esi= nb + +malloc_large: +.idx equ esp+4 +.rst equ esp + + push ebp + push esi + push edi + sub esp, 8 +; v = 0; +; rsize = -nb; + + mov edi, esi + mov ebx, esi + xor ebp, ebp + neg edi + +; idx = compute_tree_index(nb); + + mov edx, esi + shr edx, 8 + bsr eax, edx + lea ecx, [eax+7] + shr esi, cl + and esi, 1 + lea ecx, [esi+eax*2] + mov [.idx], ecx + +; if ((t = ms.treebins[idx]) != 0) + + mov eax, [mst.treebins+ecx*4] + test eax, eax + jz .l3 + +; sizebits = nb << leftshift_for_tree_index(idx); + + cmp ecx, 31 + jne @F + xor ecx, ecx + jmp .l1 + +@@: + mov edx, ecx + shr edx, 1 + mov ecx, 37 + sub ecx, edx + +.l1: + mov edx, ebx + shl edx, cl + +; rst = 0; + mov [.rst], ebp + +.loop: + +; trem = (t->head & ~INUSE_BITS) - nb; + + mov ecx, [eax+4] + and ecx, -4 + sub ecx, ebx + +; if (trem < rsize) + + cmp ecx, edi + jae @F +; v = t; +; if ((rsize = trem) == 0) + + test ecx, ecx + mov ebp, eax + mov edi, ecx + je .l2 + +@@: + +; rt = t->child[1]; + + mov ecx, [eax+20] + +; t = t->child[(sizebits >> 31) & 1]; + + mov esi, edx + shr esi, 31 + +; if (rt != 0 && rt != t) + + test ecx, ecx + mov eax, [eax+esi*4+16] + jz @F + cmp ecx, eax + jz @F + +; rst = rt; + mov [.rst], ecx + +@@: +; if (t == 0) + + test eax, eax + jz @F + +; sizebits <<= 1; + + add edx, edx + jmp .loop + +@@: +; t = rst; + mov eax, [.rst] + +.l2: +; if (t == 0 && v == 0) + + test eax, eax + jne .l4 + test ebp, ebp + jne .l7 + mov ecx, [.idx] + +.l3: + +; leftbits = (-1<head & ~INUSE_BITS) - nb; + + mov ecx, [eax+4] + and ecx, -4 + sub ecx, ebx + +; if (trem < rsize) + + cmp ecx, edi + jae @F +; rsize = trem; + + mov edi, ecx +; v = t; + mov ebp, eax + +@@: + +; t = leftmost_child(t); + + mov ecx, [eax+16] + test ecx, ecx + je @F + mov eax, ecx + jmp .l6 + +@@: + mov eax, [eax+20] + +.l6: + +; while (t != 0) + + test eax, eax + jne .l4 + +.l5: + +; if (v != 0) + + test ebp, ebp + jz .done + +.l7: + +; r = chunk_plus_offset((mchunkptr)v, nb); +; unlink_large_chunk(v); + + mov edx, ebp + lea esi, [ebx+ebp] + call unlink_large_chunk + +; if (rsize < 16) + + cmp edi, 16 + jae .large + +; v->head = (rsize + nb)|PINUSE_BIT|CINUSE_BIT; + + lea ecx, [edi+ebx] + +; (v+rsize + nb)->head |= PINUSE_BIT; + + add edi, ebp + lea eax, [edi+ebx+4] + or ecx, 3 + mov [ebp+4], ecx + or dword [eax], 1 + lea eax, [ebp+8] + add esp, 8 + pop edi + pop esi + pop ebp + ret + +.large: + +; v->head = nb|PINUSE_BIT|CINUSE_BIT; +; r->head = rsize|PINUSE_BIT; + + mov edx, edi + or ebx, 3 + mov [ebp+4], ebx + or edx, 1 + mov [esi+4], edx + +; (r+rsize)->prev_foot = rsize; +; insert_large_chunk((tchunkptr)r, rsize); + + mov [esi+edi], edi + mov eax, edi + mov ecx, esi + call insert_chunk + + lea eax, [ebp+8] + add esp, 8 + pop edi + pop esi + pop ebp + ret + +.done: + add esp, 8 + pop edi + pop esi + pop ebp + xor eax, eax + ret + +init_malloc: + + stdcall kernel_alloc, 0x40000 + + mov [mst.top], eax + mov [mst.topsize], 128*1024 + mov dword [eax+4], (128*1024) or 1 + mov eax, mst.smallbins + +@@: + mov [eax+8], eax + mov [eax+12], eax + add eax, 16 + cmp eax, mst.smallbins+512 + jb @B + + mov ecx, mst.mutex + call mutex_init + + ret + diff --git a/kernel/branches/kolibri-lldw/core/memory.inc b/kernel/branches/kolibri-lldw/core/memory.inc new file mode 100644 index 000000000..fa4275776 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/memory.inc @@ -0,0 +1,1364 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2020. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +align 4 +proc alloc_page + + pushfd + cli + push ebx + + cmp [pg_data.pages_free], 1 + jle .out_of_memory + + + mov ebx, [page_start] + mov ecx, [page_end] +.l1: + bsf eax, [ebx]; + jnz .found + add ebx, 4 + cmp ebx, ecx + jb .l1 + pop ebx + popfd + xor eax, eax + ret +.found: + + dec [pg_data.pages_free] + jz .out_of_memory + + btr [ebx], eax + mov [page_start], ebx + sub ebx, sys_pgmap + lea eax, [eax+ebx*8] + shl eax, 12 +; dec [pg_data.pages_free] + pop ebx + popfd + ret + +.out_of_memory: + mov [pg_data.pages_free], 1 + xor eax, eax + pop ebx + popfd + ret + +endp + +align 4 +proc alloc_pages stdcall, count:dword + pushfd + push ebx + push edi + cli + mov eax, [count] + add eax, 7 + shr eax, 3 + mov [count], eax + + mov ebx, [pg_data.pages_free] + sub ebx, 9 + js .out_of_memory + shr ebx, 3 + cmp eax, ebx + jg .out_of_memory + + mov ecx, [page_start] + mov ebx, [page_end] +.find: + mov edx, [count] + mov edi, ecx + .match: + cmp byte [ecx], 0xFF + jne .next + dec edx + jz .ok + inc ecx + cmp ecx, ebx + jb .match + .out_of_memory: + .fail: + xor eax, eax + pop edi + pop ebx + popfd + ret + .next: + inc ecx + cmp ecx, ebx + jb .find + pop edi + pop ebx + popfd + xor eax, eax + ret + .ok: + sub ecx, edi + inc ecx + push esi + mov esi, edi + xor eax, eax + rep stosb + sub esi, sys_pgmap + shl esi, 3+12 + mov eax, esi + mov ebx, [count] + shl ebx, 3 + sub [pg_data.pages_free], ebx + pop esi + pop edi + pop ebx + popfd + ret +endp + +align 4 +;proc map_page stdcall,lin_addr:dword,phis_addr:dword,flags:dword +map_page: + push ebx + mov eax, [esp+12] ; phis_addr + or eax, [esp+16] ; flags + and eax, [pte_valid_mask] + mov ebx, [esp+8] ; lin_addr + shr ebx, 12 + mov [page_tabs+ebx*4], eax + mov eax, [esp+8] ; lin_addr + pop ebx + invlpg [eax] + ret 12 + +align 4 +map_space: ;not implemented + + + ret + + +align 4 +proc free_page +;arg: eax page address + pushfd + cli + shr eax, 12 ;page index + bts dword [sys_pgmap], eax ;that's all! + cmc + adc [pg_data.pages_free], 0 + shr eax, 3 + and eax, not 3 ;dword offset from page_map + add eax, sys_pgmap + cmp [page_start], eax + ja @f + popfd + ret +@@: + mov [page_start], eax + popfd + ret +endp + +align 4 +proc map_io_mem stdcall, base:dword, size:dword, flags:dword + + push ebx + push edi + mov eax, [size] + add eax, [base] + add eax, 4095 + and eax, -4096 + mov ecx, [base] + and ecx, -4096 + sub eax, ecx + mov [size], eax + + stdcall alloc_kernel_space, eax + test eax, eax + jz .fail + push eax + + mov edi, 0x1000 + mov ebx, eax + mov ecx, [size] + mov edx, [base] + shr eax, 12 + shr ecx, 12 + or edx, [flags] + and edx, [pte_valid_mask] +@@: + mov [page_tabs+eax*4], edx + invlpg [ebx] + inc eax + add ebx, edi + add edx, edi + loop @B + + pop eax + mov edx, [base] + and edx, 4095 + add eax, edx +.fail: + pop edi + pop ebx + ret +endp + +; param +; eax= page base + page flags +; ebx= linear address +; ecx= count + +align 4 +commit_pages: + test ecx, ecx + jz .fail + + push edi + push eax + push ecx + mov ecx, pg_data.mutex + call mutex_lock + pop ecx + pop eax + + and eax, [pte_valid_mask ] + mov edi, ebx + shr edi, 12 + lea edi, [page_tabs+edi*4] +@@: + stosd + invlpg [ebx] + add eax, 0x1000 + add ebx, 0x1000 + loop @B + + pop edi + + mov ecx, pg_data.mutex + call mutex_unlock +.fail: + ret + + +; param +; eax= base +; ecx= count + +align 4 +release_pages: + + push ebp + push esi + push edi + push ebx + + mov esi, eax + mov edi, eax + + shr esi, 12 + lea esi, [page_tabs+esi*4] + + push ecx + mov ecx, pg_data.mutex + call mutex_lock + pop ecx + + mov ebp, [pg_data.pages_free] + mov ebx, [page_start] + mov edx, sys_pgmap +@@: + xor eax, eax + xchg eax, [esi] + invlpg [edi] + + test eax, 1 + jz .next + + shr eax, 12 + bts [edx], eax + cmc + adc ebp, 0 + shr eax, 3 + and eax, -4 + add eax, edx + cmp eax, ebx + jae .next + + mov ebx, eax +.next: + add edi, 0x1000 + add esi, 4 + loop @B + + mov [pg_data.pages_free], ebp + mov ecx, pg_data.mutex + call mutex_unlock + + pop ebx + pop edi + pop esi + pop ebp + ret + +; param +; eax= base +; ecx= count + +align 4 +unmap_pages: + + push edi + + mov edi, eax + mov edx, eax + + shr edi, 10 + add edi, page_tabs + + xor eax, eax +@@: + stosd + invlpg [edx] + add edx, 0x1000 + loop @b + + pop edi + ret + + +align 4 +proc map_page_table stdcall, lin_addr:dword, phis_addr:dword + push ebx + mov ebx, [lin_addr] + shr ebx, 22 + mov eax, [phis_addr] + and eax, not 0xFFF + or eax, PG_UWR + mov dword [master_tab+ebx*4], eax + mov eax, [lin_addr] + shr eax, 10 + add eax, page_tabs + invlpg [eax] + pop ebx + ret +endp + +uglobal +sb16_buffer_allocated db 0 +endg + +; Allocates [.size] bytes so that the target memory block +; is inside one 64K page for 24-bit DMA controller, +; that is, somewhere between 00xx0000h and 00xxFFFFh. +proc alloc_dma24 +; Implementation note. +; The only user of that function is SB16 driver, +; so just return a statically allocated buffer. +virtual at esp + dd ? ; return address +.size dd ? +end virtual + cmp [sb16_buffer_allocated], 0 + jnz .fail + inc [sb16_buffer_allocated] + mov eax, SB16Buffer + ret 4 +.fail: + xor eax, eax + ret 4 +endp + +; Allocates a physical page for master page table +; that duplicates first Mb of OS_BASE at address 0; +; used for starting APs and for shutting down, +; where it is important to execute code in trivial-mapped pages. +; Returns eax = allocated physical page. +proc create_trampoline_pgmap +; The only non-trivial moment: +; we need a linear address to fill information, +; but we don't need it outside of this function, +; so we're returning physical address. +; Therefore, allocate memory with kernel_alloc, +; this will allocate physical page and a linear address somewhere, +; and deallocate only linear address with free_kernel_space. + stdcall kernel_alloc, 0x1000 + mov edi, eax + mov esi, master_tab + mov ecx, 1024 + rep movsd + mov ecx, [master_tab+(OS_BASE shr 20)] + mov [eax], ecx + mov edi, eax + call get_pg_addr + push eax + stdcall free_kernel_space, edi + pop eax + ret +endp + +align 4 +proc new_mem_resize stdcall, new_size:dword + + push ebx + push esi + push edi + + mov edx, [current_slot] + mov ebx, [edx+APPDATA.process] + + cmp [ebx+PROC.heap_base], 0 + jne .exit + + mov edi, [new_size] + add edi, 4095 + and edi, not 4095 + mov [new_size], edi + + mov esi, [ebx+PROC.mem_used] + add esi, 4095 + and esi, not 4095 + + cmp edi, esi + ja .expand + je .exit + + mov ebx, edi + shr edi, 12 + shr esi, 12 + + mov ecx, pg_data.mutex + call mutex_lock +@@: + mov eax, [app_page_tabs+edi*4] + test eax, 1 + jz .next + + mov dword [app_page_tabs+edi*4], 0 + invlpg [ebx] + call free_page + +.next: + inc edi + add ebx, 0x1000 + cmp edi, esi + jb @B + + mov ecx, pg_data.mutex + call mutex_unlock + +.update_size: + mov edx, [current_slot] + mov ebx, [new_size] + mov edx, [edx+APPDATA.process] + mov [edx+PROC.mem_used], ebx +.exit: + pop edi + pop esi + pop ebx + xor eax, eax + ret + +.expand: + + mov ecx, pg_data.mutex + call mutex_lock + + xchg esi, edi + + push esi ;new size + push edi ;old size + + add edi, 0x3FFFFF + and edi, not(0x3FFFFF) + add esi, 0x3FFFFF + and esi, not(0x3FFFFF) + + cmp edi, esi + jae .grow + @@: + call alloc_page + test eax, eax + jz .exit_fail + + stdcall map_page_table, edi, eax + + push edi + shr edi, 10 + add edi, page_tabs + mov ecx, 1024 + xor eax, eax + cld + rep stosd + pop edi + + add edi, 0x00400000 + cmp edi, esi + jb @B +.grow: + pop edi ;old size + pop ecx ;new size + + shr edi, 10 + shr ecx, 10 + sub ecx, edi + shr ecx, 2 ;pages count + mov eax, 2 + + add edi, app_page_tabs + rep stosd + + mov ecx, pg_data.mutex + call mutex_unlock + + jmp .update_size + +.exit_fail: + mov ecx, pg_data.mutex + call mutex_unlock + + add esp, 8 + pop edi + pop esi + pop ebx + xor eax, eax + inc eax + ret +endp + + +; param +; eax= linear address +; +; retval +; eax= physical page address + +align 4 +get_pg_addr: + sub eax, OS_BASE + cmp eax, 0x400000 + jb @f + shr eax, 12 + mov eax, [page_tabs+(eax+(OS_BASE shr 12))*4] +@@: + and eax, 0xFFFFF000 + ret + + +align 4 +; Now it is called from core/sys32::exc_c (see stack frame there) +proc page_fault_handler + + .err_addr equ ebp-4 + + push ebx ;save exception number (#PF) + mov ebp, esp + mov ebx, cr2 + push ebx ;that is locals: .err_addr = cr2 + inc [pg_data.pages_faults] + + mov eax, [pf_err_code] + + cmp ebx, OS_BASE ;ebx == .err_addr + jb .user_space ;page in application memory + + cmp ebx, page_tabs + jb .kernel_space ;page in kernel memory + + cmp ebx, kernel_tabs + jb .alloc;.app_tabs ;page tables of application ; + ;simply create one +.core_tabs: +.fail: ;simply return to caller + mov esp, ebp + pop ebx ;restore exception number (#PF) + ret + +.user_space: + test eax, PG_READ + jnz .err_access ;Page presents + ;Access error ? + + shr ebx, 12 + mov ecx, ebx + shr ecx, 10 + mov edx, [master_tab+ecx*4] + test edx, PG_READ + jz .fail ;page table is not created + ;incorrect address in program + + mov eax, [page_tabs+ebx*4] + test eax, 2 + jz .fail ;address is not reserved for usage. Error + +.alloc: + call alloc_page + test eax, eax + jz .fail + + stdcall map_page, [.err_addr], eax, PG_UWR + + mov edi, [.err_addr] + and edi, 0xFFFFF000 + mov ecx, 1024 + xor eax, eax + ;cld ;caller is duty for this + rep stosd +.exit: ;iret with repeat fault instruction + add esp, 12;clear in stack: locals(.err_addr) + #PF + ret_to_caller + restore_ring3_context + iretd + +.err_access: +; access denied? this may be a result of copy-on-write protection for DLL +; check list of HDLLs + and ebx, not 0xFFF + mov eax, [current_process] + mov eax, [eax+PROC.dlls_list_ptr] + test eax, eax + jz .fail + mov esi, [eax+HDLL.fd] +.scan_hdll: + cmp esi, eax + jz .fail + mov edx, ebx + sub edx, [esi+HDLL.base] + cmp edx, [esi+HDLL.size] + jb .fault_in_hdll +.scan_hdll.next: + mov esi, [esi+HDLL.fd] + jmp .scan_hdll +.fault_in_hdll: +; allocate new page, map it as rw and copy data + call alloc_page + test eax, eax + jz .fail + stdcall map_page, ebx, eax, PG_UWR + mov edi, ebx + mov ecx, 1024 + sub ebx, [esi+HDLL.base] + mov esi, [esi+HDLL.parent] + mov esi, [esi+DLLDESCR.data] + add esi, ebx + rep movsd + jmp .exit + +.kernel_space: + test eax, PG_READ + jz .fail ;page does not present + + test eax, 12 ;U/S (+below) + jnz .fail ;application requested kernel memory + + ;test eax, 8 + ;jnz .fail ;the reserved bit is set in page tables. Added in P4/Xeon + + +;an attempt to write to a protected kernel page + + cmp ebx, tss._io_map_0 + jb .fail + + cmp ebx, tss._io_map_0+8192 + jae .fail + +; io permission map +; copy-on-write protection + + call alloc_page + test eax, eax + jz .fail + + push eax + stdcall map_page, [.err_addr], eax, dword PG_SWR + pop eax + mov edi, [.err_addr] + and edi, -4096 + lea esi, [edi+(not tss._io_map_0)+1]; -tss._io_map_0 + + mov ebx, esi + shr ebx, 12 + mov edx, [current_slot] + or eax, PG_SWR + mov [edx+APPDATA.io_map+ebx*4], eax + + add esi, [default_io_map] + mov ecx, 4096/4 + ;cld ;caller is duty for this + rep movsd + jmp .exit +endp + +; returns number of mapped bytes +proc map_mem_ipc stdcall, lin_addr:dword,slot:dword,\ + ofs:dword,buf_size:dword,req_access:dword + locals + count dd ? + process dd ? + endl + + mov [count], 0 + cmp [buf_size], 0 + jz .exit + + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.process] + test eax, eax + jz .exit + + mov [process], eax + mov ebx, [ofs] + shr ebx, 22 + mov eax, [eax+PROC.pdt_0+ebx*4] ;get page table + mov esi, [ipc_ptab] + and eax, 0xFFFFF000 + jz .exit + stdcall map_page, esi, eax, PG_SWR +@@: + mov edi, [lin_addr] + and edi, 0xFFFFF000 + mov ecx, [buf_size] + add ecx, 4095 + shr ecx, 12 + inc ecx ; ??????????? + + mov edx, [ofs] + shr edx, 12 + and edx, 0x3FF +.map: + stdcall safe_map_page, [slot], [req_access], [ofs] + jnc .exit + add [count], PAGE_SIZE + add [ofs], PAGE_SIZE + dec ecx + jz .exit + + add edi, PAGE_SIZE + inc edx + cmp edx, 1024 + jnz .map + + inc ebx + mov eax, [process] + mov eax, [eax+PROC.pdt_0+ebx*4] + and eax, 0xFFFFF000 + jz .exit + + stdcall map_page, esi, eax, PG_SWR + xor edx, edx + jmp .map +.exit: + mov eax, [count] + ret +endp + +proc map_memEx stdcall, lin_addr:dword,slot:dword,\ + ofs:dword,buf_size:dword,req_access:dword + locals + count dd ? + process dd ? + endl + + mov [count], 0 + cmp [buf_size], 0 + jz .exit + + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.process] + test eax, eax + jz .exit + + mov [process], eax + mov ebx, [ofs] + shr ebx, 22 + mov eax, [eax+PROC.pdt_0+ebx*4] ;get page table + mov esi, [proc_mem_tab] + and eax, 0xFFFFF000 + jz .exit + stdcall map_page, esi, eax, PG_SWR +@@: + mov edi, [lin_addr] + and edi, 0xFFFFF000 + mov ecx, [buf_size] + add ecx, 4095 + shr ecx, 12 + inc ecx ; ??????????? + + mov edx, [ofs] + shr edx, 12 + and edx, 0x3FF +.map: + stdcall safe_map_page, [slot], [req_access], [ofs] + jnc .exit + add [count], PAGE_SIZE + add [ofs], PAGE_SIZE + dec ecx + jz .exit + + add edi, PAGE_SIZE + inc edx + cmp edx, 1024 + jnz .map + + inc ebx + mov eax, [process] + mov eax, [eax+PROC.pdt_0+ebx*4] + and eax, 0xFFFFF000 + jz .exit + + stdcall map_page, esi, eax, PG_SWR + xor edx, edx + jmp .map +.exit: + mov eax, [count] + ret +endp + +; in: esi+edx*4 = pointer to page table entry +; in: [slot], [req_access], [ofs] on the stack +; in: edi = linear address to map +; out: CF cleared <=> failed +; destroys: only eax +proc safe_map_page stdcall, slot:dword, req_access:dword, ofs:dword + mov eax, [esi+edx*4] + test al, PG_READ + jz .not_present + test al, PG_WRITE + jz .resolve_readonly +; normal case: writable page, just map with requested access +.map: + stdcall map_page, edi, eax, [req_access] + stc +.fail: + ret +.not_present: +; check for alloc-on-demand page + test al, 2 + jz .fail +; allocate new page, save it to source page table + push ecx + call alloc_page + pop ecx + test eax, eax + jz .fail + or al, PG_UWR + mov [esi+edx*4], eax + jmp .map +.resolve_readonly: +; readonly page, probably copy-on-write +; check: readonly request of readonly page is ok + test [req_access], PG_WRITE + jz .map +; find control structure for this page + pushf + cli + cld + push ebx ecx + mov eax, [slot] + shl eax, 8 + mov eax, [SLOT_BASE+eax+APPDATA.process] + mov eax, [eax+PROC.dlls_list_ptr] + test eax, eax + jz .no_hdll + mov ecx, [eax+HDLL.fd] +.scan_hdll: + cmp ecx, eax + jz .no_hdll + mov ebx, [ofs] + and ebx, not 0xFFF + sub ebx, [ecx+HDLL.base] + cmp ebx, [ecx+HDLL.size] + jb .hdll_found + mov ecx, [ecx+HDLL.fd] + jmp .scan_hdll +.no_hdll: + pop ecx ebx + popf + clc + ret +.hdll_found: +; allocate page, save it in page table, map it, copy contents from base + mov eax, [ecx+HDLL.parent] + add ebx, [eax+DLLDESCR.data] + call alloc_page + test eax, eax + jz .no_hdll + or al, PG_UWR + mov [esi+edx*4], eax + stdcall map_page, edi, eax, [req_access] + push esi edi + mov esi, ebx + mov ecx, 4096/4 + rep movsd + pop edi esi + pop ecx ebx + popf + stc + ret +endp + +sys_IPC: +;input: +; ebx=1 - set ipc buffer area +; ecx=address of buffer +; edx=size of buffer +; eax=2 - send message +; ebx=PID +; ecx=address of message +; edx=size of message + + dec ebx + jnz @f + + mov eax, [current_slot] + pushf + cli + mov [eax+APPDATA.ipc_start], ecx ;set fields in extended information area + mov [eax+APPDATA.ipc_size], edx + + add edx, ecx + add edx, 4095 + and edx, not 4095 + +.touch: + mov eax, [ecx] + add ecx, 0x1000 + cmp ecx, edx + jb .touch + + popf + mov [esp+32], ebx ;ebx=0 + ret + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;2 +@@: + dec ebx + jnz @f + + stdcall sys_ipc_send, ecx, edx, esi + mov [esp+32], eax + ret +@@: + or eax, -1 + mov [esp+32], eax + ret + +proc sys_ipc_send stdcall, PID:dword, msg_addr:dword, msg_size:dword + locals + dst_slot dd ? + dst_offset dd ? + buf_size dd ? + used_buf dd ? + endl + + pushf + cli + + mov eax, [PID] + call pid_to_slot + test eax, eax + jz .no_pid + + mov [dst_slot], eax + shl eax, 8 + mov edi, [eax+SLOT_BASE+APPDATA.ipc_start] ;is ipc area defined? + test edi, edi + jz .no_ipc_area + + mov ebx, edi + and ebx, 0xFFF + mov [dst_offset], ebx + + mov esi, [eax+SLOT_BASE+APPDATA.ipc_size] + mov [buf_size], esi + + mov ecx, [ipc_tmp] + cmp esi, 0x40000-0x1000; size of [ipc_tmp] minus one page + jbe @f + push esi edi + add esi, 0x1000 + stdcall alloc_kernel_space, esi + mov ecx, eax + pop edi esi +@@: + mov [used_buf], ecx + stdcall map_mem_ipc, ecx, [dst_slot], \ + edi, esi, PG_SWR + + mov edi, [dst_offset] + add edi, [used_buf] + cmp dword [edi], 0 + jnz .ipc_blocked ;if dword [buffer]<>0 - ipc blocked now + + mov edx, dword [edi+4] + lea ebx, [edx+8] + add ebx, [msg_size] + cmp ebx, [buf_size] + ja .buffer_overflow ;esi<0 - not enough memory in buffer + + mov dword [edi+4], ebx + mov eax, [TASK_BASE] + mov eax, [eax+TASKDATA.pid] ;eax - our PID + add edi, edx + mov [edi], eax + mov ecx, [msg_size] + + mov [edi+4], ecx + add edi, 8 + mov esi, [msg_addr] + ; add esi, new_app_base + cld + rep movsb + + mov ebx, [ipc_tmp] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov ebx, [ipc_pdir] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov ebx, [ipc_ptab] + mov edx, ebx + shr ebx, 12 + xor eax, eax + mov [page_tabs+ebx*4], eax + invlpg [edx] + + mov eax, [dst_slot] + shl eax, BSF sizeof.APPDATA + or [eax+SLOT_BASE+APPDATA.occurred_events], EVENT_IPC + push 0 + jmp .ret +.no_pid: + popf + mov eax, 4 + ret +.no_ipc_area: + popf + xor eax, eax + inc eax + ret +.ipc_blocked: + push 2 + jmp .ret +.buffer_overflow: + push 3 +.ret: + mov eax, [used_buf] + cmp eax, [ipc_tmp] + je @f + stdcall free_kernel_space, eax +@@: + pop eax + popf + ret +endp + +align 4 +sysfn_meminfo: + cmp ecx, OS_BASE + jae .fail + + mov eax, [pg_data.pages_count] + mov [ecx], eax + shl eax, 12 + mov [esp+32], eax + mov eax, [pg_data.pages_free] + mov [ecx+4], eax + mov eax, [pg_data.pages_faults] + mov [ecx+8], eax + mov eax, [heap_size] + mov [ecx+12], eax + mov eax, [heap_free] + mov [ecx+16], eax + mov eax, [heap_blocks] + mov [ecx+20], eax + mov eax, [free_blocks] + mov [ecx+24], eax + ret +.fail: + or dword [esp+32], -1 + ret + +align 4 +f68: + cmp ebx, 4 + jbe sys_sheduler + cmp ebx, 11 + jb undefined_syscall + cmp ebx, 29 + ja undefined_syscall + xor eax, eax + jmp dword [f68call+ebx*4-11*4] +.11: + call init_heap + mov [esp+SYSCALL_STACK._eax], eax + ret +.12: + stdcall user_alloc, ecx + mov [esp+SYSCALL_STACK._eax], eax + ret +.13: + stdcall user_free, ecx + mov [esp+SYSCALL_STACK._eax], eax + ret +.14: + cmp ecx, OS_BASE + jae .fail + mov edi, ecx + call get_event_ex + mov [esp+SYSCALL_STACK._eax], eax + ret +.16: + test ecx, ecx + jz .fail + cmp ecx, OS_BASE + jae .fail + stdcall get_service, ecx + mov [esp+SYSCALL_STACK._eax], eax + ret +.17: + call srv_handlerEx ;ecx + mov [esp+SYSCALL_STACK._eax], eax + ret +.18: + mov eax, edx +.19: + cmp ecx, OS_BASE + jae .fail + stdcall load_library, ecx, eax + mov [esp+SYSCALL_STACK._eax], eax + ret +.20: + mov eax, edx + mov ebx, ecx + call user_realloc ;in: eax = pointer, ebx = new size + mov [esp+SYSCALL_STACK._eax], eax + ret +.21: + cmp ecx, OS_BASE + jae .fail + cmp edx, OS_BASE + jae .fail + stdcall load_pe_driver, ecx, edx + mov [esp+SYSCALL_STACK._eax], eax + ret +.22: + cmp ecx, OS_BASE + jae .fail + stdcall shmem_open, ecx, edx, esi + mov [esp+SYSCALL_STACK._edx], edx + mov [esp+SYSCALL_STACK._eax], eax + ret +.23: + cmp ecx, OS_BASE + jae .fail + stdcall shmem_close, ecx + mov [esp+SYSCALL_STACK._eax], eax + ret +.24: + mov eax, [current_slot] + xchg ecx, [eax+APPDATA.exc_handler] + xchg edx, [eax+APPDATA.except_mask] + mov [esp+SYSCALL_STACK._ebx], edx + mov [esp+SYSCALL_STACK._eax], ecx + ret +.25: + cmp ecx, 32 + jae .fail + mov eax, [current_slot] + btr [eax+APPDATA.except_mask], ecx + setc byte[esp+SYSCALL_STACK._eax] + jecxz @f + bts [eax+APPDATA.except_mask], ecx +@@: + ret +.26: + stdcall user_unmap, ecx, edx, esi + mov [esp+SYSCALL_STACK._eax], eax + ret +.27: + cmp ecx, OS_BASE + jae .fail + stdcall load_file_umode, ecx + mov [esp+SYSCALL_STACK._edx], edx + mov [esp+SYSCALL_STACK._eax], eax + ret +.28: + cmp ecx, OS_BASE + jae .fail + push ecx edx + stdcall kernel_alloc, maxPathLength + mov edi, eax + pop eax esi + push edi + call getFullPath + pop ebp + test eax, eax + jz @f + stdcall load_file_umode, ebp + mov [esp+SYSCALL_STACK._edx], edx +@@: + mov [esp+SYSCALL_STACK._eax], eax + stdcall kernel_free, ebp + ret + +.29: + stdcall user_ring, ecx + mov [esp+SYSCALL_STACK._eax], eax + ret + +.fail: + mov [esp+SYSCALL_STACK._eax], eax + ret + +align 4 +f68call: ; keep this table closer to main code + + dd f68.11 ; init_heap + dd f68.12 ; user_alloc + dd f68.13 ; user_free + dd f68.14 ; get_event_ex + dd f68.fail ; moved to f68.24 + dd f68.16 ; get_service + dd f68.17 ; call_service + dd f68.18 ; loadLibUnicode + dd f68.19 ; load_dll + dd f68.20 ; user_realloc + dd f68.21 ; load_driver + dd f68.22 ; shmem_open + dd f68.23 ; shmem_close + dd f68.24 ; set exception handler + dd f68.25 ; unmask exception + dd f68.26 ; user_unmap + dd f68.27 ; load_file_umode + dd f68.28 ; loadFileUnicode + dd f68.29 ; user_ring + +align 4 +proc load_pe_driver stdcall, file:dword, cmdline:dword + push esi + + stdcall load_PE, [file] + test eax, eax + jz .fail + + mov esi, eax + push [cmdline] + push DRV_ENTRY + call eax + pop ecx + pop ecx + test eax, eax + jz .fail + + mov [eax+SRV.entry], esi + pop esi + ret + +.fail: + xor eax, eax + pop esi + ret +endp + +align 4 +proc create_ring_buffer stdcall, size:dword, flags:dword + locals + buf_ptr dd ? + endl + + mov eax, [size] + test eax, eax + jz .fail + + add eax, eax + stdcall alloc_kernel_space, eax + test eax, eax + jz .fail + + push ebx + + mov [buf_ptr], eax + + mov ebx, [size] + shr ebx, 12 + push ebx + + stdcall alloc_pages, ebx + pop ecx + + test eax, eax + jz .mm_fail + + push edi + + or eax, [flags] + mov edi, [buf_ptr] + mov ebx, [buf_ptr] + mov edx, ecx + shl edx, 2 + shr edi, 10 +@@: + mov [page_tabs+edi], eax + mov [page_tabs+edi+edx], eax + invlpg [ebx] + invlpg [ebx+0x10000] + add eax, 0x1000 + add ebx, 0x1000 + add edi, 4 + dec ecx + jnz @B + + mov eax, [buf_ptr] + pop edi + pop ebx + ret +.mm_fail: + stdcall free_kernel_space, [buf_ptr] + xor eax, eax + pop ebx +.fail: + ret +endp + + +align 4 +proc print_mem + mov edi, BOOT.memmap_blocks + mov ecx, [edi-4] + test ecx, ecx + jz .done + +@@: + mov eax, [edi] + mov edx, [edi+4] + add eax, [edi+8] + adc edx, [edi+12] + + DEBUGF 1, "K : E820 %x%x - %x%x type %d\n", \ + [edi+4], [edi],\ + edx, eax, [edi+16] + add edi, 20 + dec ecx + jnz @b +.done: + ret +endp diff --git a/kernel/branches/kolibri-lldw/core/mtrr.inc b/kernel/branches/kolibri-lldw/core/mtrr.inc new file mode 100644 index 000000000..705550fe2 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/mtrr.inc @@ -0,0 +1,931 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Initializes MTRRs. +proc init_mtrr + + cmp [BOOT.mtrr], byte 2 + je .exit + + bt [cpu_caps], CAPS_MTRR + jnc .exit + + call mtrr_reconfigure + stdcall set_mtrr, [LFBAddress], 0x1000000, MEM_WC + +.exit: + ret +endp + +; Helper procedure for mtrr_reconfigure and set_mtrr, +; called before changes in MTRRs. +; 1. disable and flush caches +; 2. clear PGE bit in cr4 +; 3. flush TLB +; 4. disable mtrr + +proc mtrr_begin_change + mov eax, cr0 + or eax, 0x60000000 ;disable caching + mov cr0, eax + wbinvd ;invalidate cache + + bt [cpu_caps], CAPS_PGE + jnc .cr3_flush + + mov eax, cr4 + btr eax, 7 ;clear cr4.PGE + mov cr4, eax ;flush TLB + jmp @F ;skip extra serialization + +.cr3_flush: + mov eax, cr3 + mov cr3, eax ;flush TLB +@@: + mov ecx, MSR_MTRR_DEF_TYPE + rdmsr + btr eax, 11 ;clear enable flag + wrmsr ;disable mtrr + ret +endp + +; Helper procedure for mtrr_reconfigure and set_mtrr, +; called after changes in MTRRs. +; 1. enable mtrr +; 2. flush all caches +; 3. flush TLB +; 4. restore cr4.PGE flag, if required + +proc mtrr_end_change + mov ecx, MSR_MTRR_DEF_TYPE + rdmsr + or ah, 8 ; enable variable-ranges MTRR + and al, 0xF0 ; default memtype = UC + wrmsr + + wbinvd ;again invalidate + mov eax, cr0 + and eax, not 0x60000000 + mov cr0, eax ; enable caching + + mov eax, cr3 + mov cr3, eax ;flush tlb + + bt [cpu_caps], CAPS_PGE + jnc @F + + mov eax, cr4 + bts eax, 7 ;set cr4.PGE flag + mov cr4, eax +@@: + ret +endp + +; Some limits to number of structures located in the stack. +MAX_USEFUL_MTRRS = 16 +MAX_RANGES = 16 + +; mtrr_reconfigure keeps a list of MEM_WB ranges. +; This structure describes one item in the list. +struct mtrr_range +next dd ? ; next item +start dq ? ; first byte +length dq ? ; length in bytes +ends + +uglobal +align 4 +num_variable_mtrrs dd 0 ; number of variable-range MTRRs +endg + +; Helper procedure for MTRR initialization. +; Takes MTRR configured by BIOS and tries to recongifure them +; in order to allow non-UC data at top of 4G memory. +; Example: if low part of physical memory is 3.5G = 0xE0000000 bytes wide, +; BIOS can configure two MTRRs so that the first MTRR describes [0, 4G) as WB +; and the second MTRR describes [3.5G, 4G) as UC; +; WB+UC=UC, so the resulting memory map would be as needed, +; but in this configuration our attempts to map LFB at (say) 0xE8000000 as WC +; would be ignored, WB+UC+WC is still UC. +; So we must keep top of 4G memory not covered by MTRRs, +; using three WB MTRRs [0,2G) + [2G,3G) + [3G,3.5G), +; this gives the same memory map, but allows to add further entries. +; See mtrrtest.asm for detailed input/output from real hardware+BIOS. +proc mtrr_reconfigure + push ebp ; we're called from init_LFB, and it feels hurt when ebp is destroyed +; 1. Prepare local variables. +; 1a. Create list of MAX_RANGES free (aka not yet allocated) ranges. + xor eax, eax + lea ecx, [eax+MAX_RANGES] +.init_ranges: + sub esp, sizeof.mtrr_range - 4 + push eax + mov eax, esp + dec ecx + jnz .init_ranges + mov eax, esp +; 1b. Fill individual local variables. + xor edx, edx + sub esp, MAX_USEFUL_MTRRS * 16 ; .mtrrs + push edx ; .mtrrs_end + push edx ; .num_used_mtrrs + push eax ; .first_free_range + push edx ; .first_range: no ranges yet + mov cl, [cpu_phys_addr_width] + or eax, -1 + shl eax, cl ; note: this uses cl&31 = cl-32, not the entire cl + push eax ; .phys_reserved_mask +virtual at esp +.phys_reserved_mask dd ? +.first_range dd ? +.first_free_range dd ? +.num_used_mtrrs dd ? +.mtrrs_end dd ? +.mtrrs rq MAX_USEFUL_MTRRS * 2 +.local_vars_size = $ - esp +end virtual + +; 2. Get the number of variable-range MTRRs from MTRRCAP register. +; Abort if zero. + mov ecx, 0xFE + rdmsr + test al, al + jz .abort + mov byte [num_variable_mtrrs], al +; 3. Validate MTRR_DEF_TYPE register. + mov ecx, 0x2FF + rdmsr +; If BIOS has not initialized variable-range MTRRs, fallback to step 7. + test ah, 8 + jz .fill_ranges_from_memory_map +; If the default memory type (not covered by MTRRs) is not UC, +; then probably BIOS did something strange, so it is better to exit immediately +; hoping for the best. + cmp al, MEM_UC + jnz .abort +; 4. Validate all variable-range MTRRs +; and copy configured MTRRs to the local array [.mtrrs]. +; 4a. Prepare for the loop over existing variable-range MTRRs. + mov ecx, 0x200 + lea edi, [.mtrrs] +.get_used_mtrrs_loop: +; 4b. For every MTRR, read PHYSBASEn and PHYSMASKn. +; In PHYSBASEn, clear upper bits and copy to ebp:ebx. + rdmsr + or edx, [.phys_reserved_mask] + xor edx, [.phys_reserved_mask] + mov ebp, edx + mov ebx, eax + inc ecx +; If PHYSMASKn is not active, ignore this MTRR. + rdmsr + inc ecx + test ah, 8 + jz .get_used_mtrrs_next +; 4c. For every active MTRR, check that number of local entries is not too large. + inc [.num_used_mtrrs] + cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS + ja .abort +; 4d. For every active MTRR, store PHYSBASEn with upper bits cleared. +; This contains the MTRR base and the memory type in low byte. + mov [edi], ebx + mov [edi+4], ebp +; 4e. For every active MTRR, check that the range is continuous: +; PHYSMASKn with upper bits set must be negated power of two, and +; low bits of PHYSBASEn must be zeroes: +; PHYSMASKn = 1...10...0, +; PHYSBASEn = x...x0...0, +; this defines a continuous range from x...x0...0 to x...x1...1, +; length = 10...0 = negated PHYSMASKn. +; Store length in the local array. + and eax, not 0xFFF + or edx, [.phys_reserved_mask] + mov dword [edi+8], 0 + mov dword [edi+12], 0 + sub [edi+8], eax + sbb [edi+12], edx +; (x and -x) is the maximum power of two that divides x. +; Condition for powers of two: (x and -x) equals x. + and eax, [edi+8] + and edx, [edi+12] + cmp eax, [edi+8] + jnz .abort + cmp edx, [edi+12] + jnz .abort + sub eax, 1 + sbb edx, 0 + and eax, not 0xFFF + and eax, ebx + jnz .abort + and edx, ebp + jnz .abort +; 4f. For every active MTRR, validate memory type: it must be either WB or UC. + add edi, 16 + cmp bl, MEM_UC + jz .get_used_mtrrs_next + cmp bl, MEM_WB + jnz .abort +.get_used_mtrrs_next: +; 4g. Repeat the loop at 4b-4f for all [num_variable_mtrrs] entries. + mov eax, [num_variable_mtrrs] + lea eax, [0x200+eax*2] + cmp ecx, eax + jb .get_used_mtrrs_loop +; 4h. If no active MTRRs were detected, fallback to step 7. + cmp [.num_used_mtrrs], 0 + jz .fill_ranges_from_memory_map + mov [.mtrrs_end], edi +; 5. Generate sorted list of ranges marked as WB. +; 5a. Prepare for the loop over configured MTRRs filled at step 4. + lea ecx, [.mtrrs] +.fill_wb_ranges: +; 5b. Ignore non-WB MTRRs. + mov ebx, [ecx] + cmp bl, MEM_WB + jnz .next_wb_range + mov ebp, [ecx+4] + and ebx, not 0xFFF ; clear memory type and reserved bits +; ebp:ebx = start of the range described by the current MTRR. +; 5c. Find the first existing range containing a point greater than ebp:ebx. + lea esi, [.first_range] +.find_range_wb: +; If there is no next range or start of the next range is greater than ebp:ebx, +; exit the loop to 5d. + mov edi, [esi] + test edi, edi + jz .found_place_wb + mov eax, ebx + mov edx, ebp + sub eax, dword [edi+mtrr_range.start] + sbb edx, dword [edi+mtrr_range.start+4] + jb .found_place_wb +; Otherwise, if end of the next range is greater than or equal to ebp:ebx, +; exit the loop to 5e. + mov esi, edi + sub eax, dword [edi+mtrr_range.length] + sbb edx, dword [edi+mtrr_range.length+4] + jb .expand_wb + or eax, edx + jnz .find_range_wb + jmp .expand_wb +.found_place_wb: +; 5d. ebp:ebx is not within any existing range. +; Insert a new range between esi and edi. +; (Later, during 5e, it can be merged with the following ranges.) + mov eax, [.first_free_range] + test eax, eax + jz .abort + mov [esi], eax + mov edx, [eax+mtrr_range.next] + mov [.first_free_range], edx + mov dword [eax+mtrr_range.start], ebx + mov dword [eax+mtrr_range.start+4], ebp +; Don't fill [eax+mtrr_range.next] and [eax+mtrr_range.length] yet, +; they will be calculated including merges at step 5e. + mov esi, edi + mov edi, eax +.expand_wb: +; 5e. The range at edi contains ebp:ebx, and esi points to the first range +; to be checked for merge: esi=edi if ebp:ebx was found in an existing range, +; esi is next after edi if a new range with ebp:ebx was created. +; Merge it with following ranges while start of the next range is not greater +; than the end of the new range. + add ebx, [ecx+8] + adc ebp, [ecx+12] +; ebp:ebx = end of the range described by the current MTRR. +.expand_wb_loop: +; If there is no next range or start of the next range is greater than ebp:ebx, +; exit the loop to 5g. + test esi, esi + jz .expand_wb_done + mov eax, ebx + mov edx, ebp + sub eax, dword [esi+mtrr_range.start] + sbb edx, dword [esi+mtrr_range.start+4] + jb .expand_wb_done +; Otherwise, if end of the next range is greater than or equal to ebp:ebx, +; exit the loop to 5f. + sub eax, dword [esi+mtrr_range.length] + sbb edx, dword [esi+mtrr_range.length+4] + jb .expand_wb_last +; Otherwise, the current range is completely within the new range. +; Free it and continue the loop. + mov edx, [esi+mtrr_range.next] + cmp esi, edi + jz @f + mov eax, [.first_free_range] + mov [esi+mtrr_range.next], eax + mov [.first_free_range], esi +@@: + mov esi, edx + jmp .expand_wb_loop +.expand_wb_last: +; 5f. Start of the new range is inside range described by esi, +; end of the new range is inside range described by edi. +; If esi is equal to edi, the new range is completely within +; an existing range, so proceed to the next range. + cmp esi, edi + jz .next_wb_range +; Otherwise, set end of interval at esi to end of interval at edi +; and free range described by edi. + mov ebx, dword [esi+mtrr_range.start] + mov ebp, dword [esi+mtrr_range.start+4] + add ebx, dword [esi+mtrr_range.length] + adc ebp, dword [esi+mtrr_range.length+4] + mov edx, [esi+mtrr_range.next] + mov eax, [.first_free_range] + mov [esi+mtrr_range.next], eax + mov [.first_free_range], esi + mov esi, edx +.expand_wb_done: +; 5g. We have found the next range (maybe 0) after merging and +; the new end of range (maybe ebp:ebx from the new range +; or end of another existing interval calculated at step 5f). +; Write them to range at edi. + mov [edi+mtrr_range.next], esi + sub ebx, dword [edi+mtrr_range.start] + sbb ebp, dword [edi+mtrr_range.start+4] + mov dword [edi+mtrr_range.length], ebx + mov dword [edi+mtrr_range.length+4], ebp +.next_wb_range: +; 5h. Continue the loop 5b-5g over all configured MTRRs. + add ecx, 16 + cmp ecx, [.mtrrs_end] + jb .fill_wb_ranges +; 6. Exclude all ranges marked as UC. +; 6a. Prepare for the loop over configured MTRRs filled at step 4. + lea ecx, [.mtrrs] +.fill_uc_ranges: +; 6b. Ignore non-UC MTRRs. + mov ebx, [ecx] + cmp bl, MEM_UC + jnz .next_uc_range + mov ebp, [ecx+4] + and ebx, not 0xFFF ; clear memory type and reserved bits +; ebp:ebx = start of the range described by the current MTRR. + lea esi, [.first_range] +; 6c. Find the first existing range containing a point greater than ebp:ebx. +.find_range_uc: +; If there is no next range, ignore this MTRR, +; exit the loop and continue to next MTRR. + mov edi, [esi] + test edi, edi + jz .next_uc_range +; If start of the next range is greater than or equal to ebp:ebx, +; exit the loop to 6e. + mov eax, dword [edi+mtrr_range.start] + mov edx, dword [edi+mtrr_range.start+4] + sub eax, ebx + sbb edx, ebp + jnb .truncate_uc +; Otherwise, continue the loop if end of the next range is less than ebp:ebx, +; exit the loop to 6d otherwise. + mov esi, edi + add eax, dword [edi+mtrr_range.length] + adc edx, dword [edi+mtrr_range.length+4] + jnb .find_range_uc +; 6d. ebp:ebx is inside (or at end of) an existing range. +; Split the range. (The second range, maybe containing completely within UC-range, +; maybe of zero length, can be removed at step 6e, if needed.) + mov edi, [.first_free_range] + test edi, edi + jz .abort + mov dword [edi+mtrr_range.start], ebx + mov dword [edi+mtrr_range.start+4], ebp + mov dword [edi+mtrr_range.length], eax + mov dword [edi+mtrr_range.length+4], edx + mov eax, [edi+mtrr_range.next] + mov [.first_free_range], eax + mov eax, [esi+mtrr_range.next] + mov [edi+mtrr_range.next], eax +; don't change [esi+mtrr_range.next] yet, it will be filled at step 6e + mov eax, ebx + mov edx, ebp + sub eax, dword [esi+mtrr_range.start] + sbb edx, dword [esi+mtrr_range.start+4] + mov dword [esi+mtrr_range.length], eax + mov dword [esi+mtrr_range.length+4], edx +.truncate_uc: +; 6e. edi is the first range after ebp:ebx, check it and next ranges +; for intersection with the new range, truncate heads. + add ebx, [ecx+8] + adc ebp, [ecx+12] +; ebp:ebx = end of the range described by the current MTRR. +.truncate_uc_loop: +; If start of the next range is greater than ebp:ebx, +; exit the loop to 6g. + mov eax, ebx + mov edx, ebp + sub eax, dword [edi+mtrr_range.start] + sbb edx, dword [edi+mtrr_range.start+4] + jb .truncate_uc_done +; Otherwise, if end of the next range is greater than ebp:ebx, +; exit the loop to 6f. + sub eax, dword [edi+mtrr_range.length] + sbb edx, dword [edi+mtrr_range.length+4] + jb .truncate_uc_last +; Otherwise, the current range is completely within the new range. +; Free it and continue the loop if there is a next range. +; If that was a last range, exit the loop to 6g. + mov edx, [edi+mtrr_range.next] + mov eax, [.first_free_range] + mov [.first_free_range], edi + mov [edi+mtrr_range.next], eax + mov edi, edx + test edi, edi + jnz .truncate_uc_loop + jmp .truncate_uc_done +.truncate_uc_last: +; 6f. The range at edi partially intersects with the UC-range described by MTRR. +; Truncate it from the head. + mov dword [edi+mtrr_range.start], ebx + mov dword [edi+mtrr_range.start+4], ebp + neg eax + adc edx, 0 + neg edx + mov dword [edi+mtrr_range.length], eax + mov dword [edi+mtrr_range.length+4], edx +.truncate_uc_done: +; 6g. We have found the next range (maybe 0) after intersection. +; Write it to [esi+mtrr_range.next]. + mov [esi+mtrr_range.next], edi +.next_uc_range: +; 6h. Continue the loop 6b-6g over all configured MTRRs. + add ecx, 16 + cmp ecx, [.mtrrs_end] + jb .fill_uc_ranges +; Sanity check: if there are no ranges after steps 5-6, +; fallback to step 7. Otherwise, go to 8. + cmp [.first_range], 0 + jnz .ranges_ok +.fill_ranges_from_memory_map: +; 7. BIOS has not configured variable-range MTRRs. +; Create one range from 0 to [MEM_AMOUNT]. + mov eax, [.first_free_range] + mov edx, [eax+mtrr_range.next] + mov [.first_free_range], edx + mov [.first_range], eax + xor edx, edx + mov [eax+mtrr_range.next], edx + mov dword [eax+mtrr_range.start], edx + mov dword [eax+mtrr_range.start+4], edx + mov ecx, [MEM_AMOUNT] + mov dword [eax+mtrr_range.length], ecx + mov dword [eax+mtrr_range.length+4], edx +.ranges_ok: +; 8. We have calculated list of WB-ranges. +; Now we should calculate a list of MTRRs so that +; * every MTRR describes a range with length = power of 2 and start that is aligned, +; * every MTRR can be WB or UC +; * (sum of all WB ranges) minus (sum of all UC ranges) equals the calculated list +; * top of 4G memory must not be covered by any ranges +; Example: range [0,0xBC000000) can be converted to +; [0,0x80000000)+[0x80000000,0xC0000000)-[0xBC000000,0xC0000000) +; WB +WB -UC +; but not to [0,0x100000000)-[0xC0000000,0x100000000)-[0xBC000000,0xC0000000). +; 8a. Check that list of ranges is [0,something) plus, optionally, [4G,something). +; This holds in practice (see mtrrtest.asm for real-life examples) +; and significantly simplifies the code: ranges are independent, start of range +; is almost always aligned (the only exception >4G upper memory can be easily covered), +; there is no need to consider adding holes before start of range, only +; append them to end of range. + xor eax, eax + mov edi, [.first_range] + cmp dword [edi+mtrr_range.start], eax + jnz .abort + cmp dword [edi+mtrr_range.start+4], eax + jnz .abort + cmp dword [edi+mtrr_range.length+4], eax + jnz .abort + mov edx, [edi+mtrr_range.next] + test edx, edx + jz @f + cmp dword [edx+mtrr_range.start], eax + jnz .abort + cmp dword [edx+mtrr_range.start+4], 1 + jnz .abort + cmp [edx+mtrr_range.next], eax + jnz .abort +@@: +; 8b. Initialize: no MTRRs filled. + mov [.num_used_mtrrs], eax + lea esi, [.mtrrs] +.range2mtrr_loop: +; 8c. If we are dealing with upper-memory range (after 4G) +; with length > start, create one WB MTRR with [start,2*start), +; reset start to 2*start and return to this step. +; Example: [4G,24G) -> [4G,8G) {returning} + [8G,16G) {returning} +; + [16G,24G) {advancing to ?}. + mov eax, dword [edi+mtrr_range.length+4] + test eax, eax + jz .less4G + mov edx, dword [edi+mtrr_range.start+4] + cmp eax, edx + jb .start_aligned + inc [.num_used_mtrrs] + cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS + ja .abort + mov dword [esi], MEM_WB + mov dword [esi+4], edx + mov dword [esi+8], 0 + mov dword [esi+12], edx + add esi, 16 + add dword [edi+mtrr_range.start+4], edx + sub dword [edi+mtrr_range.length+4], edx + jnz .range2mtrr_loop + cmp dword [edi+mtrr_range.length], 0 + jz .range2mtrr_next +.less4G: +; 8d. If we are dealing with low-memory range (before 4G) +; and appending a maximal-size hole would create a range covering top of 4G, +; create a maximal-size WB range and return to this step. +; Example: for [0,0xBC000000) the following steps would consider +; variants [0,0x80000000)+(another range to be splitted) and +; [0,0x100000000)-(another range to be splitted); we forbid the last variant, +; so the first variant must be used. + bsr ecx, dword [edi+mtrr_range.length] + xor edx, edx + inc edx + shl edx, cl + lea eax, [edx*2] + add eax, dword [edi+mtrr_range.start] + jnz .start_aligned + inc [.num_used_mtrrs] + cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS + ja .abort + mov eax, dword [edi+mtrr_range.start] + mov dword [esi], eax + or dword [esi], MEM_WB + mov dword [esi+4], 0 + mov dword [esi+8], edx + mov dword [esi+12], 0 + add esi, 16 + add dword [edi+mtrr_range.start], edx + sub dword [edi+mtrr_range.length], edx + jnz .less4G + jmp .range2mtrr_next +.start_aligned: +; Start is aligned for any allowed length, maximum-size hole is allowed. +; Select the best MTRR configuration for one range. +; length=...101101 +; Without hole at the end, we need one WB MTRR for every 1-bit in length: +; length=...100000 + ...001000 + ...000100 + ...000001 +; We can also append one hole at the end so that one 0-bit (selected by us) +; becomes 1 and all lower bits become 0 for WB-range: +; length=...110000 - (...00010 + ...00001) +; In this way, we need one WB MTRR for every 1-bit higher than the selected bit, +; one WB MTRR for the selected bit, one UC MTRR for every 0-bit between +; the selected bit and lowest 1-bit (they become 1-bits after negation) +; and one UC MTRR for lowest 1-bit. +; So we need to select 0-bit with the maximal difference +; (number of 0-bits) - (number of 1-bits) between selected and lowest 1-bit, +; this equals the gain from using a hole. If the difference is negative for +; all 0-bits, don't append hole. +; Note that lowest 1-bit is not included when counting, but selected 0-bit is. +; 8e. Find the optimal bit position for hole. +; eax = current difference, ebx = best difference, +; ecx = hole bit position, edx = current bit position. + xor eax, eax + xor ebx, ebx + xor ecx, ecx + bsf edx, dword [edi+mtrr_range.length] + jnz @f + bsf edx, dword [edi+mtrr_range.length+4] + add edx, 32 +@@: + push edx ; save position of lowest 1-bit for step 8f +.calc_stat: + inc edx + cmp edx, 64 + jae .stat_done + inc eax ; increment difference in hope for 1-bit +; Note: bt conveniently works with both .length and .length+4, +; depending on whether edx>=32. + bt dword [edi+mtrr_range.length], edx + jc .calc_stat + dec eax ; hope was wrong, decrement difference to correct 'inc' + dec eax ; and again, now getting the real difference + cmp eax, ebx + jle .calc_stat + mov ebx, eax + mov ecx, edx + jmp .calc_stat +.stat_done: +; 8f. If we decided to create a hole, flip all bits between lowest and selected. + pop edx ; restore position of lowest 1-bit saved at step 8e + test ecx, ecx + jz .fill_hi_init +@@: + inc edx + cmp edx, ecx + ja .fill_hi_init + btc dword [edi+mtrr_range.length], edx + jmp @b +.fill_hi_init: +; 8g. Create MTRR ranges corresponding to upper 32 bits. + sub ecx, 32 +.fill_hi_loop: + bsr edx, dword [edi+mtrr_range.length+4] + jz .fill_hi_done + inc [.num_used_mtrrs] + cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS + ja .abort + mov eax, dword [edi+mtrr_range.start] + mov [esi], eax + mov eax, dword [edi+mtrr_range.start+4] + mov [esi+4], eax + xor eax, eax + mov [esi+8], eax + bts eax, edx + mov [esi+12], eax + cmp edx, ecx + jl .fill_hi_uc + or dword [esi], MEM_WB + add dword [edi+mtrr_range.start+4], eax + jmp @f +.fill_hi_uc: + sub dword [esi+4], eax + sub dword [edi+mtrr_range.start+4], eax +@@: + add esi, 16 + sub dword [edi+mtrr_range.length], eax + jmp .fill_hi_loop +.fill_hi_done: +; 8h. Create MTRR ranges corresponding to lower 32 bits. + add ecx, 32 +.fill_lo_loop: + bsr edx, dword [edi+mtrr_range.length] + jz .range2mtrr_next + inc [.num_used_mtrrs] + cmp [.num_used_mtrrs], MAX_USEFUL_MTRRS + ja .abort + mov eax, dword [edi+mtrr_range.start] + mov [esi], eax + mov eax, dword [edi+mtrr_range.start+4] + mov [esi+4], eax + xor eax, eax + mov [esi+12], eax + bts eax, edx + mov [esi+8], eax + cmp edx, ecx + jl .fill_lo_uc + or dword [esi], MEM_WB + add dword [edi+mtrr_range.start], eax + jmp @f +.fill_lo_uc: + sub dword [esi], eax + sub dword [edi+mtrr_range.start], eax +@@: + add esi, 16 + sub dword [edi+mtrr_range.length], eax + jmp .fill_lo_loop +.range2mtrr_next: +; 8i. Repeat the loop at 8c-8h for all ranges. + mov edi, [edi+mtrr_range.next] + test edi, edi + jnz .range2mtrr_loop +; 9. We have calculated needed MTRRs, now setup them in the CPU. +; 9a. Abort if number of MTRRs is too large. + mov eax, [num_variable_mtrrs] + cmp [.num_used_mtrrs], eax + ja .abort + +; 9b. Prepare for changes. + call mtrr_begin_change + +; 9c. Prepare for loop over MTRRs. + lea esi, [.mtrrs] + mov ecx, 0x200 +@@: +; 9d. For every MTRR, copy PHYSBASEn as is: step 8 has configured +; start value and type bits as needed. + mov eax, [esi] + mov edx, [esi+4] + wrmsr + inc ecx +; 9e. For every MTRR, calculate PHYSMASKn = -(length) or 0x800 +; with upper bits cleared, 0x800 = MTRR is valid. + xor eax, eax + xor edx, edx + sub eax, [esi+8] + sbb edx, [esi+12] + or eax, 0x800 + or edx, [.phys_reserved_mask] + xor edx, [.phys_reserved_mask] + wrmsr + inc ecx +; 9f. Continue steps 9d and 9e for all MTRRs calculated at step 8. + add esi, 16 + dec [.num_used_mtrrs] + jnz @b +; 9g. Zero other MTRRs. + xor eax, eax + xor edx, edx + mov ebx, [num_variable_mtrrs] + lea ebx, [0x200+ebx*2] +@@: + cmp ecx, ebx + jae @f + wrmsr + inc ecx + wrmsr + inc ecx + jmp @b +@@: + +; 9i. Check PAT support and reprogram PAT_MASR for write combining memory + bt [cpu_caps], CAPS_PAT + jnc @F + + mov ecx, MSR_CR_PAT + mov eax, PAT_VALUE ;UC UCM WC WB + mov edx, eax + wrmsr +@@: + +; 9j. Changes are done. + call mtrr_end_change + +.abort: + add esp, .local_vars_size + MAX_RANGES * sizeof.mtrr_range + pop ebp + ret +endp + +; Allocate&set one MTRR for given range. +; size must be power of 2 that divides base. +proc set_mtrr stdcall, base:dword,size:dword,mem_type:dword +; find unused register + mov ecx, 0x201 +.scan: + mov eax, [num_variable_mtrrs] + lea eax, [0x200+eax*2] + cmp ecx, eax + jae .ret + rdmsr + dec ecx + test ah, 8 + jz .found + rdmsr + test edx, edx + jnz @f + and eax, not 0xFFF ; clear reserved bits + cmp eax, [base] + jz .ret +@@: + add ecx, 3 + jmp .scan +; no free registers, ignore the call +.ret: + ret +.found: +; found, write values + push ecx + call mtrr_begin_change + pop ecx + xor edx, edx + mov eax, [base] + or eax, [mem_type] + wrmsr + + mov al, [cpu_phys_addr_width] + xor edx, edx + bts edx, eax + xor eax, eax + sub eax, [size] + sbb edx, 0 + or eax, 0x800 + inc ecx + wrmsr + call mtrr_end_change + ret +endp + +; Helper procedure for mtrr_validate. +; Calculates memory type for given address according to variable-range MTRRs. +; Assumes that MTRRs are enabled. +; in: ebx = 32-bit physical address +; out: eax = memory type for ebx +proc mtrr_get_real_type +; 1. Initialize: we have not yet found any MTRRs covering ebx. + push 0 + mov ecx, 0x201 +.mtrr_loop: +; 2. For every MTRR, check whether it is valid; if not, continue to the next MTRR. + rdmsr + dec ecx + test ah, 8 + jz .next +; 3. For every valid MTRR, check whether (ebx and PHYSMASKn) == PHYSBASEn, +; excluding low 12 bits. + and eax, ebx + push eax + rdmsr + test edx, edx + pop edx + jnz .next + xor edx, eax + and edx, not 0xFFF + jnz .next +; 4. If so, set the bit corresponding to memory type defined by this MTRR. + and eax, 7 + bts [esp], eax +.next: +; 5. Continue loop at 2-4 for all variable-range MTRRs. + add ecx, 3 + mov eax, [num_variable_mtrrs] + lea eax, [0x200+eax*2] + cmp ecx, eax + jb .mtrr_loop +; 6. If no MTRRs cover address in ebx, use default MTRR type from MTRR_DEF_CAP. + pop edx + test edx, edx + jz .default +; 7. Find&clear 1-bit in edx. + bsf eax, edx + btr edx, eax +; 8. If there was only one 1-bit, then all MTRRs are consistent, return that bit. + test edx, edx + jz .nothing +; Otherwise, return MEM_UC (e.g. WB+UC is UC). + xor eax, eax +.nothing: + ret +.default: + mov ecx, 0x2FF + rdmsr + movzx eax, al + ret +endp + +; If MTRRs are configured improperly, this is not obvious to the user; +; everything works, but the performance can be horrible. +; Try to detect this and let the user know that the low performance +; is caused by some problem and is not a global property of the system. +; Let's hope he would report it to developers... +proc mtrr_validate +; 1. If MTRRs are not supported, they cannot be configured improperly. +; Note: VirtualBox claims MTRR support in cpuid, but emulates MTRRCAP=0, +; which is efficiently equivalent to absent MTRRs. +; So check [num_variable_mtrrs] instead of CAPS_MTRR in [cpu_caps]. + cmp [num_variable_mtrrs], 0 + jz .exit +; 2. If variable-range MTRRs are not configured, this is a problem. + mov ecx, 0x2FF + rdmsr + test ah, 8 + jz .fail +; 3. Get the memory type for address somewhere inside working memory. +; It must be write-back. + mov ebx, 0x27FFFF + call mtrr_get_real_type + cmp al, MEM_WB + jnz .fail +; 4. If we're using a mode with LFB, +; get the memory type for last pixel of the framebuffer. +; It must be write-combined. + test word [SCR_MODE], 0x4000 + jz .exit + mov eax, [_display.lfb_pitch] + mul [_display.height] + dec eax +; LFB is mapped to virtual address LFB_BASE, +; it uses global pages if supported by CPU. + mov ebx, [sys_proc+PROC.pdt_0+(LFB_BASE shr 20)] + test ebx, PDE_LARGE + jnz @f + mov ebx, [page_tabs+(LFB_BASE shr 10)] +@@: + and ebx, not 0xFFF + add ebx, eax + call mtrr_get_real_type + cmp al, MEM_WC + jz .exit +; 5. The check at step 4 fails on Bochs: +; Bochs BIOS configures MTRRs in a strange way not respecting [cpu_phys_addr_width], +; so mtrr_reconfigure avoids to touch anything. +; However, Bochs core ignores MTRRs (keeping them only for rdmsr/wrmsr), +; so we don't care about proper setting for Bochs. +; Use northbridge PCI id to detect Bochs: it emulates either i440fx or i430fx +; depending on configuration file. + mov eax, [pcidev_list.fd] + cmp eax, pcidev_list ; sanity check: fail if no PCI devices + jz .fail + cmp [eax+PCIDEV.vendor_device_id], 0x12378086 + jz .exit + cmp [eax+PCIDEV.vendor_device_id], 0x01228086 + jnz .fail +.exit: + ret +.fail: + mov ebx, mtrr_user_message + mov ebp, notifyapp + call fs_execute_from_sysdir_param + ret +endp diff --git a/kernel/branches/kolibri-lldw/core/mtrrtest.asm b/kernel/branches/kolibri-lldw/core/mtrrtest.asm new file mode 100644 index 000000000..dede91186 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/mtrrtest.asm @@ -0,0 +1,213 @@ +; Simple test for ring-3 debugging of mtrr.inc. +; Contains some inputs taken from real-life MTRRs and expected outputs. +format PE console +;include 'win32a.inc' +macro $Revision [args] +{ +} +macro ignore_empty_revision_keyword { + macro $Revi#sion$ \{\} +} +ignore_empty_revision_keyword +include '../proc32.inc' +include '../struct.inc' +entry start + +; one test has 8, another test has 10 +; this is the maximal value for storing/copying, real value is in MTRRCAP +MAX_VARIABLE_MTRR = 10 + +start: +; Copy test inputs, run init_mtrr, compare with test outputs. Repeat. + mov esi, test1_in_data + mov edi, mtrrdata + mov ecx, mtrrdata_size / 4 + rep movsd + call init_mtrr + mov esi, test1_out_data + mov edi, mtrrdata + mov ecx, mtrrdata_size / 4 + repz cmpsd + jnz .fail + mov esi, test2_in_data + mov edi, mtrrdata + mov ecx, mtrrdata_size / 4 + rep movsd + call init_mtrr + mov esi, test2_out_data + mov edi, mtrrdata + mov ecx, mtrrdata_size / 4 + repz cmpsd + jnz .fail + ret + +.fail: + int3 + jmp $ + +; Helper procedure for _rdmsr/_wrmsr, replacements of rdmsr/wrmsr. +; Returns pointer to memory containing the given MSR. +; in: ecx = MSR +; out: esi -> MSR data +proc get_msr_ptr + mov esi, mtrrcap + cmp ecx, 0xFE + jz .ok + mov esi, mtrr_def_type + cmp ecx, 0x2FF + jz .ok + lea esi, [ecx-0x200] + cmp esi, MAX_VARIABLE_MTRR*2 + jae .fail + lea esi, [mtrr+esi*8] +.ok: + ret +.fail: + int3 + ret +endp + +; Emulates rdmsr. +proc _rdmsr + push esi + call get_msr_ptr + mov eax, [esi] + mov edx, [esi+4] + pop esi + ret +endp + +; Emulates wrmsr. +proc _wrmsr + push esi + call get_msr_ptr + mov [esi], eax + mov [esi+4], edx + pop esi + ret +endp + +; Macro to substitute rdmsr/wrmsr with emulating code. +macro rdmsr +{ + call _rdmsr +} +macro wrmsr +{ + call _wrmsr +} +; Our emulation of rdmsr/wrmsr has nothing to do with real cache +; and system-wide settings, +; remove all attempts to wbinvd and disable/enable cache in cr0. +macro wbinvd +{ +} +macro mov a,b +{ +if ~(a eq cr0) & ~(b eq cr0) + mov a, b +end if +} +macro movi r,i +{ + push i + pop r +} + +include '../kglobals.inc' +CAPS_MTRR = 12 +MSR_MTRR_DEF_TYPE = 0x2FF +CAPS_PGE = 13 +CAPS_PAT = 16 +MSR_CR_PAT = 0x277 +PAT_VALUE = 0x00070106 ; (UC<<24)|(UCM<<16)|(WC<<8)|WB +MEM_WB = 6 ;write-back memory +MEM_WC = 1 ;write combined memory +MEM_UC = 0 ;uncached memory +include 'mtrr.inc' + +BOOT_VARS = 0 +BOOT.mtrr db 1 +align 4 +cpu_caps dd 1 shl CAPS_MTRR +LFBAddress dd 0xE0000000 +LFBSize dd 0x10000000 +MEM_AMOUNT dd 0 ; not used, needed for compilation + +align 4 +; Test 1: input +test1_in_data: +test1_phys_addr_width db 36 + rb 3 +test1_in_mtrrcap dq 0xD08 +test1_in_mtrr_def_type dq 0xC00 +test1_in_mtrrs: + dq 0x000000006, 0xF00000800 + dq 0x100000006, 0xFC0000800 + dq 0x0BC000000, 0xFFC000800 + dq 0x0C0000000, 0xFC0000800 + dq 0x138000000, 0xFF8000800 + dq 0, 0 + dq 0, 0 + dq 0, 0 + dq -1, -1 ; not used + dq -1, -1 ; not used +; Test 1: output +test1_out_data: + dd 36 ; phys_addr_width, readonly + dq 0xD08 ; MTRRCAP, readonly + dq 0xC00 ; MTRR_DEF_TYPE, should be the same + dq 0x000000006, 0xF80000800 + dq 0x080000006, 0xFC0000800 + dq 0x0BC000000, 0xFFC000800 + dq 0x100000006, 0xFC0000800 + dq 0x138000000, 0xFF8000800 + dq 0x0E0000001, 0xFFF000800 ; added for [LFBAddress] + dq 0, 0 + dq 0, 0 + dq -1, -1 ; not used + dq -1, -1 ; not used + +; Test 2: input +test2_in_data: +test2_phys_addr_width db 39 + rb 3 +test2_in_mtrrcap dq 0xD0A +test2_in_mtrr_def_type dq 0xC00 +test2_in_mtrrs: + dq 0x0000000006, 0x7F00000800 + dq 0x0100000006, 0x7FE0000800 + dq 0x00E0000000, 0x7FE0000800 + dq 0x00DC000000, 0x7FFC000800 + dq 0x00DBC00000, 0x7FFFC00800 + dq 0x011F800000, 0x7FFF800800 + dq 0x011F400000, 0x7FFFC00800 + dq 0x011F200000, 0x7FFFE00800 + dq 0, 0 + dq 0, 0 + +; Test 2: output +test2_out_data: + dd 39 ; phys_addr_width, readonly + dq 0xD0A ; MTRRCAP, readonly + dq 0xC00 ; MTRR_DEF_TYPE, should be the same + dq 0x0000000006, 0x7F80000800 + dq 0x0080000006, 0x7FC0000800 + dq 0x00C0000006, 0x7FE0000800 + dq 0x00DC000000, 0x7FFC000800 + dq 0x00DBC00000, 0x7FFFC00800 + dq 0x0100000006, 0x7FE0000800 + dq 0x011F800000, 0x7FFF800800 + dq 0x011F400000, 0x7FFFC00800 + dq 0x011F200000, 0x7FFFE00800 + dq 0x00E0000001, 0x7FFF000800 ; added for [LFBAddress] +IncludeIGlobals +align 4 +mtrrdata: +cpu_phys_addr_width db ? + rb 3 +mtrrcap dq ? +mtrr_def_type dq ? +mtrr rq MAX_VARIABLE_MTRR*2 +mtrrdata_size = $ - mtrrdata +IncludeUGlobals diff --git a/kernel/branches/kolibri-lldw/core/peload.inc b/kernel/branches/kolibri-lldw/core/peload.inc new file mode 100644 index 000000000..2991a8b83 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/peload.inc @@ -0,0 +1,295 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +include 'export.inc' + +align 4 + +proc load_PE stdcall, file_name:dword + locals + image dd ? + entry dd ? + base dd ? + endl + + stdcall load_file, [file_name] + test eax, eax + jz .fail + + mov [image], eax + + mov edx, [eax+STRIPPED_PE_HEADER.SizeOfImage] +; mov cl, [eax+STRIPPED_PE_HEADER.Subsystem] + cmp word [eax], STRIPPED_PE_SIGNATURE + jz @f + + mov edx, [eax+60] +; mov cl, [eax+5Ch+edx] + mov edx, [eax+80+edx] + +@@: + mov [entry], 0 +; cmp cl, 1 +; jnz .cleanup + stdcall kernel_alloc, edx + test eax, eax + jz .cleanup + + mov [base], eax + DEBUGF 1,'K : driver %s mapped to %x\n',[file_name],[base] + + push ebx ebp + mov ebx, [image] + mov ebp, eax + call map_PE + pop ebp ebx + + mov [entry], eax + test eax, eax + jnz .cleanup + + stdcall kernel_free, [base] +.cleanup: + stdcall kernel_free, [image] + mov eax, [entry] + ret +.fail: + xor eax, eax + ret +endp + +map_PE: ;ebp=base:dword, ebx=image:dword + push edi + push esi + sub esp, .locals_size +virtual at esp +.numsections dd ? +.import_names dd ? +.import_targets dd ? +.peheader dd ? +.bad_import dd ? +.import_idx dd ? +.import_descr dd ? +.relocs_rva dd ? +.relocs_size dd ? +.section_header_size dd ? +.AddressOfEntryPoint dd ? +.ImageBase dd ? +.locals_size = $ - esp +end virtual + cmp word [ebx], STRIPPED_PE_SIGNATURE + jz .stripped + + mov edx, ebx + add edx, [ebx+60] + movzx eax, word [edx+6] + mov [.numsections], eax + mov eax, [edx+40] + mov [.AddressOfEntryPoint], eax + mov eax, [edx+52] + mov [.ImageBase], eax + mov ecx, [edx+84] + mov [.section_header_size], 40 + mov eax, [edx+128] + mov [.import_descr], eax + mov eax, [edx+160] + mov [.relocs_rva], eax + mov eax, [edx+164] + mov [.relocs_size], eax + add edx, 256 + + jmp .common +.stripped: + mov eax, [ebx+STRIPPED_PE_HEADER.AddressOfEntryPoint] + mov [.AddressOfEntryPoint], eax + mov eax, [ebx+STRIPPED_PE_HEADER.ImageBase] + mov [.ImageBase], eax + movzx eax, [ebx+STRIPPED_PE_HEADER.NumberOfSections] + mov [.numsections], eax + movzx ecx, [ebx+STRIPPED_PE_HEADER.NumberOfRvaAndSizes] + xor eax, eax + mov [.relocs_rva], eax + mov [.relocs_size], eax + test ecx, ecx + jz @f + mov eax, [ebx+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_IMPORT*8] +@@: + mov [.import_descr], eax + cmp ecx, SPE_DIRECTORY_BASERELOC + jbe @f + mov eax, [ebx+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BASERELOC*8] + mov [.relocs_rva], eax + mov eax, [ebx+sizeof.STRIPPED_PE_HEADER+SPE_DIRECTORY_BASERELOC*8+4] + mov [.relocs_size], eax +@@: + mov [.section_header_size], 28 + lea edx, [ebx+ecx*8+sizeof.STRIPPED_PE_HEADER+8] + mov ecx, [ebx+STRIPPED_PE_HEADER.SizeOfHeaders] + +.common: + mov esi, ebx + mov edi, ebp + shr ecx, 2 + rep movsd + + cmp [.numsections], 0 + jz .nosections +.copy_sections: + mov eax, [edx+8] + test eax, eax + je .no_section_data + mov esi, ebx + mov edi, ebp + add esi, [edx+12] + mov ecx, eax + add edi, [edx+4] + + add ecx, 3 + shr ecx, 2 + rep movsd + +.no_section_data: + mov ecx, [edx] + cmp ecx, eax + jbe .no_section_fill + sub ecx, eax + add eax, [edx+4] + lea edi, [eax+ebp] + + xor eax, eax + rep stosb + +.no_section_fill: + add edx, [.section_header_size] + dec [.numsections] + jnz .copy_sections +.nosections: + cmp [.relocs_size], 0 + je .no_relocations + mov esi, ebp + mov ecx, ebp + sub esi, [.ImageBase] + add ecx, [.relocs_rva] +.relocs_block: + mov edi, [ecx] + add edi, ebp + mov ebx, [ecx+4] + add ecx, 8 + sub [.relocs_size], ebx + sub ebx, 8 + shr ebx, 1 + jz .relocs_next_block +.one_reloc: + movzx eax, word [ecx] + add ecx, 2 + mov edx, eax + shr eax, 12 + and edx, 4095 + cmp eax, 3 + jne @f + add [edx+edi], esi +@@: + dec ebx + jnz .one_reloc +.relocs_next_block: + cmp [.relocs_size], 0 + jg .relocs_block +.no_relocations: + cmp [.import_descr], 0 + je .no_imports + add [.import_descr], ebp + mov [.bad_import], 0 +.import_block: + mov ecx, [.import_descr] + cmp dword [ecx+4], 0 + jne @f + cmp dword [ecx+12], 0 + je .done_imports +@@: + mov edx, dword [ecx] + mov ecx, dword [ecx+16] + test edx, edx + jnz @f + mov edx, ecx +@@: + mov [.import_idx], 0 + add ecx, ebp + add edx, ebp + mov [.import_names], edx + mov [.import_targets], ecx +.import_func: + mov esi, [.import_idx] + mov edi, [.import_names] + mov eax, [edi+esi*4] + test eax, eax + je .next_import_block + js .next_import_block + lea edi, [ebp+eax] + mov eax, [.import_targets] + mov dword [eax+esi*4], 0 + lea esi, [edi+2] + movzx ebx, word [edi] + push 32 + mov ecx, [__exports+32] + mov eax, [ecx+OS_BASE+ebx*4] + add eax, OS_BASE + push eax + push esi + call strncmp + test eax, eax + jz .import_func_found + xor ebx, ebx +.import_func_candidate: + push 32 + mov ecx, [__exports+32] + mov eax, [ecx+OS_BASE+ebx*4] + add eax, OS_BASE + push eax + push esi + call strncmp + test eax, eax + je .import_func_found + inc ebx + cmp ebx, [__exports+24] + jb .import_func_candidate + + mov esi, msg_unresolved + call sys_msg_board_str + lea esi, [edi+2] + call sys_msg_board_str + mov esi, msg_CR + call sys_msg_board_str + + mov [.bad_import], 1 + jmp .next_import_func +.import_func_found: + mov esi, [__exports+28] + mov edx, [.import_idx] + mov ecx, [.import_targets] + mov eax, [esi+OS_BASE+ebx*4] + add eax, OS_BASE + mov [ecx+edx*4], eax +.next_import_func: + inc [.import_idx] + jmp .import_func +.next_import_block: + add [.import_descr], 20 + jmp .import_block +.done_imports: + xor eax, eax + cmp [.bad_import], 0 + jne @f +.no_imports: + mov eax, ebp + add eax, [.AddressOfEntryPoint] +@@: + add esp, .locals_size + pop esi + pop edi + ret diff --git a/kernel/branches/kolibri-lldw/core/sched.inc b/kernel/branches/kolibri-lldw/core/sched.inc new file mode 100644 index 000000000..2b1d94b05 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/sched.inc @@ -0,0 +1,434 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Copyright (C) MenuetOS 2000-2004 Ville Mikael Turjanmaa ;; +;; Distributed under terms of the GNU General Public License ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; IRQ0 HANDLER (TIMER INTERRUPT) ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + + +align 32 +irq0: + pushad + mov ax, app_data + mov ds, ax + mov es, ax + inc [timer_ticks] + mov eax, [timer_ticks] + call playNote ; <<<--- Speaker driver + sub eax, [next_usage_update] + cmp eax, 100 + jb .nocounter + add [next_usage_update], 100 + call updatecputimes + .nocounter: + xor ecx, ecx ; send End Of Interrupt signal + call irq_eoi + + mov bl, SCHEDULE_ANY_PRIORITY + call find_next_task + jz .return ; if there is only one running process + call do_change_task + .return: + popad + iretd + +align 4 +change_task: + pushfd + cli + pushad + mov bl, SCHEDULE_ANY_PRIORITY + call find_next_task + jz .return ; the same task -> skip switch + + call do_change_task + .return: + popad + popfd + ret + +uglobal +align 4 +; far_jump: +; .offs dd ? +; .sel dw ? + context_counter dd 0 ;noname & halyavin + next_usage_update dd 0 + timer_ticks dd 0 +; prev_slot dd ? +; event_sched dd ? +endg + +align 4 +update_counters: + mov edi, [TASK_BASE] + rdtsc + sub eax, [edi+TASKDATA.counter_add] ; time stamp counter add + add [edi+TASKDATA.counter_sum], eax ; counter sum + ret +align 4 +updatecputimes: + mov ecx, [thread_count] + mov edi, TASK_DATA + .newupdate: + xor eax, eax + xchg eax, [edi+TASKDATA.counter_sum] + mov [edi+TASKDATA.cpu_usage], eax + add edi, 0x20 + loop .newupdate + ret + +;TODO: Надо бы убрать использование do_change_task из V86... +; и после этого перенести обработку TASKDATA.counter_add/sum в do_change_task + +align 4 +do_change_task: +;param: +; ebx = address of the APPDATA for incoming task (new) +;warning: +; [current_slot_idx] and [TASK_BASE] must be changed before (e.g. in find_next_task) +; [current_slot] is the outcoming (old), and set here to a new value (ebx) +;scratched: eax,ecx,esi + mov esi, ebx + xchg esi, [current_slot] +; set new stack after saving old + mov [esi+APPDATA.saved_esp], esp + mov esp, [ebx+APPDATA.saved_esp] +; set new thread io-map + mov eax, [ebx+APPDATA.io_map] + mov dword [page_tabs+((tss._io_map_0 and -4096) shr 10)], eax + mov eax, [ebx+APPDATA.io_map+4] + mov dword [page_tabs+((tss._io_map_1 and -4096) shr 10)], eax +; set new thread memory-map + mov eax, [ebx+APPDATA.process] + cmp eax, [current_process] + je @f + mov [current_process], eax + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax +@@: +; set tss.esp0 + + mov eax, [ebx+APPDATA.saved_esp0] + mov [tss._esp0], eax + + mov edx, [ebx+APPDATA.tls_base] + + mov [tls_data_l+2], dx + shr edx, 16 + mov [tls_data_l+4], dl + mov [tls_data_l+7], dh + + mov dx, app_tls + mov fs, dx + +; set gs selector unconditionally + Mov ax, graph_data + Mov gs, ax + ; TS flag is not triggered by AVX* instructions, therefore + ; we have to xsave/xrstor SIMD registers each task change + bt [cpu_caps+(CAPS_OSXSAVE/32)*4], CAPS_OSXSAVE mod 32 + jnc .no_xsave + mov ecx, [esi+APPDATA.fpu_state] + mov eax, [xsave_eax] + mov edx, [xsave_edx] + xsave [ecx] + mov ecx, [current_slot_idx] + mov [fpu_owner], ecx + mov ecx, [current_slot] + mov ecx, [ecx+APPDATA.fpu_state] + xrstor [ecx] + .no_xsave: + ; set CR0.TS + cmp bh, byte[fpu_owner] ;bh == incoming task (new) + clts ;clear a task switch flag + je @f + mov eax, cr0 ;and set it again if the owner + or eax, CR0_TS ;of a fpu has changed + mov cr0, eax + @@: ; set context_counter (only for user pleasure ???) + inc [context_counter] ;noname & halyavin + ; set debug-registers, if it's necessary + test byte[ebx+APPDATA.dbg_state], 1 + jz @f + xor eax, eax + mov dr6, eax + lea esi, [ebx+APPDATA.dbg_regs] + cld + macro lodsReg [reg] { + lodsd + mov reg, eax + } lodsReg dr0, dr1, dr2, dr3, dr7 + purge lodsReg + @@: + ret +;end. + + + + +MAX_PRIORITY = 0 ; highest, used for kernel tasks +USER_PRIORITY = 1 ; default +IDLE_PRIORITY = 2 ; lowest, only IDLE thread goes here +NR_SCHED_QUEUES = 3 ; MUST equal IDLE_PRIORYTY + 1 + +uglobal +; [scheduler_current + i*4] = zero if there are no threads with priority i, +; pointer to APPDATA of the current thread with priority i otherwise. +align 4 +scheduler_current rd NR_SCHED_QUEUES +endg + +; Add the given thread to the given priority list for the scheduler. +; in: edx -> APPDATA, ecx = priority +proc scheduler_add_thread +; 1. Acquire the lock. + spin_lock_irqsave SchedulerLock +; 2. Store the priority in APPDATA structure. + mov [edx+APPDATA.priority], ecx +; 3. There are two different cases: the given list is empty or not empty. +; In first case, go to 6. Otherwise, advance to 4. + mov eax, [scheduler_current+ecx*4] + test eax, eax + jz .new_list +; 4. Insert the new item immediately before the current item. + mov ecx, [eax+APPDATA.in_schedule.prev] + mov [edx+APPDATA.in_schedule.next], eax + mov [edx+APPDATA.in_schedule.prev], ecx + mov [eax+APPDATA.in_schedule.prev], edx + mov [ecx+APPDATA.in_schedule.next], edx +; 5. Release the lock and return. + spin_unlock_irqrestore SchedulerLock + ret +.new_list: +; 6. Initialize the list with one item and make it the current item. + mov [edx+APPDATA.in_schedule.next], edx + mov [edx+APPDATA.in_schedule.prev], edx + mov [scheduler_current+ecx*4], edx +; 7. Release the lock and return. + spin_unlock_irqrestore SchedulerLock + ret +endp + +; Remove the given thread from the corresponding priority list for the scheduler. +; in: edx -> APPDATA +proc scheduler_remove_thread +; 1. Acquire the lock. + spin_lock_irqsave SchedulerLock +; 2. Remove the item from the corresponding list. + mov eax, [edx+APPDATA.in_schedule.next] + mov ecx, [edx+APPDATA.in_schedule.prev] + mov [eax+APPDATA.in_schedule.prev], ecx + mov [ecx+APPDATA.in_schedule.next], eax +; 3. If the given thread is the current item in the list, +; advance the current item. +; 3a. Check whether the given thread is the current item; +; if no, skip the rest of this step. + mov ecx, [edx+APPDATA.priority] + cmp [scheduler_current+ecx*4], edx + jnz .return +; 3b. Set the current item to eax; step 2 has set eax = next item. + mov [scheduler_current+ecx*4], eax +; 3c. If there were only one item in the list, zero the current item. + cmp eax, edx + jnz .return + mov [scheduler_current+ecx*4], 0 +.return: +; 4. Release the lock and return. + spin_unlock_irqrestore SchedulerLock + ret +endp + +SCHEDULE_ANY_PRIORITY = 0 +SCHEDULE_HIGHER_PRIORITY = 1 +;info: +; Find next task to execute +;in: +; bl = SCHEDULE_ANY_PRIORITY: +; consider threads with any priority +; bl = SCHEDULE_HIGHER_PRIORITY: +; consider only threads with strictly higher priority than the current one, +; keep running the current thread if other ready threads have the same or lower priority +;retval: +; ebx = address of the APPDATA for the selected task (slot-base) +; edi = address of the TASKDATA for the selected task +; ZF = 1 if the task is the same +;warning: +; [current_slot_idx] = bh , [TASK_BASE] = edi -- as result +; [current_slot] is not set to new value (ebx)!!! +;scratched: eax,ecx +proc find_next_task + call update_counters + spin_lock_irqsave SchedulerLock + push NR_SCHED_QUEUES +; If bl == SCHEDULE_ANY_PRIORITY = 0, loop over all NR_SCHED lists. +; Otherwise, loop over first [APPDATA.priority] lists. + test bl, bl + jz .start + mov ebx, [current_slot] + mov edi, [TASK_BASE] + mov eax, [ebx+APPDATA.priority] + test eax, eax + jz .unlock_found + mov [esp], eax +.start: + xor ecx, ecx +.priority_loop: + mov ebx, [scheduler_current+ecx*4] + test ebx, ebx + jz .priority_next +.task_loop: + mov ebx, [ebx+APPDATA.in_schedule.next] + mov edi, ebx + shr edi, 3 + add edi, TASK_TABLE - (SLOT_BASE shr 3) + mov al, [edi+TASKDATA.state] + test al, al + jz .task_found ; state == 0 + cmp al, 5 + jne .task_next ; state == 1,2,3,4,9 + ; state == 5 + pushad ; more freedom for [APPDATA.wait_test] + call [ebx+APPDATA.wait_test] + mov [esp+28], eax + popad + or eax, eax + jnz @f + ; testing for timeout + mov eax, [timer_ticks] + sub eax, [ebx+APPDATA.wait_begin] + cmp eax, [ebx+APPDATA.wait_timeout] + jb .task_next + xor eax, eax +@@: + mov [ebx+APPDATA.wait_param], eax ; retval for wait + mov [edi+TASKDATA.state], TSTATE_RUNNING +.task_found: + mov [scheduler_current+ecx*4], ebx +; If we have selected a thread with higher priority +; AND rescheduling is due to IRQ, +; turn the current scheduler list one entry back, +; so the current thread will be next after high-priority thread is done. + mov ecx, [esp] + cmp ecx, NR_SCHED_QUEUES + jz .unlock_found + mov eax, [current_slot] + mov eax, [eax+APPDATA.in_schedule.prev] + mov [scheduler_current+ecx*4], eax +.unlock_found: + pop ecx + spin_unlock_irqrestore SchedulerLock +.found: + ; the line below assumes APPDATA is 256 bytes long and SLOT_BASE is + ; aligned on 0x10000 + mov byte [current_slot_idx], bh + mov [TASK_BASE], edi + rdtsc ;call _rdtsc + mov [edi+TASKDATA.counter_add], eax; for next using update_counters + cmp ebx, [current_slot] + ret +.task_next: + cmp ebx, [scheduler_current+ecx*4] + jnz .task_loop +.priority_next: + inc ecx + cmp ecx, [esp] + jb .priority_loop + mov ebx, [current_slot] + mov edi, [TASK_BASE] + jmp .unlock_found +endp + +if 0 + +struc TIMER +{ + .next dd ? + .exp_time dd ? + .func dd ? + .arg dd ? +} + + +uglobal +rdy_head rd 16 +endg + +align 4 +pick_task: + + xor eax, eax + .pick: + mov ebx, [rdy_head+eax*4] + test ebx, ebx + jz .next + + mov [next_task], ebx + test [ebx+flags.billable] + jz @F + mov [bill_task], ebx + @@: + ret + .next: + inc eax + jmp .pick + +; param +; eax= task +; +; retval +; eax= task +; ebx= queue +; ecx= front if 1 or back if 0 +align 4 +shed: + cmp [eax+.tics_left], 0;signed compare + mov ebx, [eax+.priority] + setg ecx + jg @F + + mov edx, [eax+.tics_quantum] + mov [eax+.ticks_left], edx + cmp ebx, (IDLE_PRIORITY-1) + je @F + inc ebx + @@: + ret + +; param +; eax= task +align 4 +enqueue: + call shed;eax + cmp [rdy_head+ebx*4], 0 + jnz @F + + mov [rdy_head+ebx*4], eax + mov [rdy_tail+ebx*4], eax + mov [eax+.next_ready], 0 + jmp .pick + @@: + test ecx, ecx + jz .back + + mov ecx, [rdy_head+ebx*4] + mov [eax+.next_ready], ecx + mov [rdy_head+ebx*4], eax + jmp .pick + .back: + mov ecx, [rdy_tail+ebx*4] + mov [ecx+.next_ready], eax + mov [rdy_tail+ebx*4], eax + mov [eax+.next_ready], 0 + .pick: + call pick_proc;select next task + ret + +end if diff --git a/kernel/branches/kolibri-lldw/core/slab.inc b/kernel/branches/kolibri-lldw/core/slab.inc new file mode 100644 index 000000000..2b70509af --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/slab.inc @@ -0,0 +1,125 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Memory management for slab structures. +; The allocator meets special requirements: +; * memory blocks are properly aligned +; * memory blocks do not cross page boundary +; The allocator manages fixed-size blocks. +; Thus, the specific allocator works as follows: +; allocate one page, split into blocks, maintain the single-linked +; list of all free blocks in each page. + +; Note: size must be a multiple of required alignment. + +; Data for one pool: dd pointer to the first page, MUTEX lock. + +; Allocator for fixed-size blocks: allocate a block. +; [ebx-4] = pointer to the first page, ebx = pointer to MUTEX structure. +proc slab_alloc + push edi ; save used register to be stdcall +virtual at esp + dd ? ; saved edi + dd ? ; return address +.size dd ? +end virtual +; 1. Take the lock. + mov ecx, ebx + call mutex_lock +; 2. Find the first allocated page with a free block, if any. +; 2a. Initialize for the loop. + mov edx, ebx +.pageloop: +; 2b. Get the next page, keeping the current in eax. + mov eax, edx + mov edx, [edx-4] +; 2c. If there is no next page, we're out of luck; go to 4. + test edx, edx + jz .newpage + add edx, 0x1000 +@@: +; 2d. Get the pointer to the first free block on this page. +; If there is no free block, continue to 2b. + mov eax, [edx-8] + test eax, eax + jz .pageloop +; 2e. Get the pointer to the next free block. + mov ecx, [eax] +; 2f. Update the pointer to the first free block from eax to ecx. +; Normally [edx-8] still contains eax, if so, atomically set it to ecx +; and proceed to 3. +; However, the price of simplicity of slab_free (in particular, it doesn't take +; the lock) is that [edx-8] could (rarely) be changed while we processed steps +; 2d+2e. If so, return to 2d and retry. + lock cmpxchg [edx-8], ecx + jnz @b +.return: +; 3. Release the lock taken in step 1 and return. + push eax + mov ecx, ebx + call mutex_unlock + pop eax + pop edi ; restore used register to be stdcall + ret 4 +.newpage: +; 4. Allocate a new page. + push eax + stdcall kernel_alloc, 0x1000 + pop edx +; If failed, say something to the debug board and return zero. + test eax, eax + jz .nomemory +; 5. Add the new page to the tail of list of allocated pages. + mov [edx-4], eax +; 6. Initialize two service dwords in the end of page: +; first free block is (start of page) + (block size) +; (we will return first block at (start of page), so consider it allocated), +; no next page. + mov edx, eax + lea edi, [eax+0x1000-8] + add edx, [.size] + mov [edi], edx + and dword [edi+4], 0 +; 7. All blocks starting from edx are free; join them in a single-linked list. +@@: + mov ecx, edx + add edx, [.size] + mov [ecx], edx + cmp edx, edi + jbe @b + sub ecx, [.size] + and dword [ecx], 0 +; 8. Return (start of page). + jmp .return +.nomemory: + dbgstr 'no memory for slab allocation' + xor eax, eax + jmp .return +endp + +; Allocator for fixed-size blocks: free a block. +proc slab_free + push ecx edx +virtual at esp + rd 2 ; saved registers + dd ? ; return address +.block dd ? +end virtual +; Insert the given block to the head of free blocks in this page. + mov ecx, [.block] + mov edx, ecx + or edx, 0xFFF +@@: + mov eax, [edx+1-8] + mov [ecx], eax + lock cmpxchg [edx+1-8], ecx + jnz @b + pop edx ecx + ret 4 +endp diff --git a/kernel/branches/kolibri-lldw/core/string.inc b/kernel/branches/kolibri-lldw/core/string.inc new file mode 100644 index 000000000..3c6694445 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/string.inc @@ -0,0 +1,189 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; Author: Kees J. Bot 1 Jan 1994 ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; size_t strncat(char *s1, const char *s2, size_t n) +; Append string s2 to s1. + +; char *strchr(const char *s, int c) + + +; int strncmp(const char *s1, const char *s2, size_t n) +; Compare two strings. + +; char *strncpy(char *s1, const char *s2, size_t n) +; Copy string s2 to s1. + +; size_t strnlen(const char *s, size_t n) +; Return the length of a string. + +; proc strrchr stdcall, s:dword, c:dword +; Look for the last occurrence a character in a string. + +proc strncat stdcall, s1:dword, s2:dword, n:dword + push esi + push edi + mov edi, [s1] ; String s1 + mov edx, [n] ; Maximum length + + mov ecx, -1 + xor al, al ; Null byte + cld + repne scasb ; Look for the zero byte in s1 + dec edi ; Back one up (and clear 'Z' flag) + push edi ; Save end of s1 + mov edi, [s2] ; edi = string s2 + mov ecx, edx ; Maximum count + repne scasb ; Look for the end of s2 + jne @F + inc ecx ; Exclude null byte +@@: + sub edx, ecx ; Number of bytes in s2 + mov ecx, edx + mov esi, [s2] ; esi = string s2 + pop edi ; edi = end of string s1 + rep movsb ; Copy bytes + stosb ; Add a terminating null + mov eax, [s1] ; Return s1 + pop edi + pop esi + ret +endp + +align 4 +proc strncmp stdcall, s1:dword, s2:dword, n:dword + + push esi + push edi + mov ecx, [n] + test ecx, ecx ; Max length is zero? + je .done + + mov esi, [s1] ; esi = string s1 + mov edi, [s2] ; edi = string s2 + cld +.compare: + cmpsb ; Compare two bytes + jne .done + cmp byte [esi-1], 0 ; End of string? + je .done + dec ecx ; Length limit reached? + jne .compare +.done: + seta al ; al = (s1 > s2) + setb ah ; ah = (s1 < s2) + sub al, ah + movsx eax, al ; eax = (s1 > s2) - (s1 < s2), i.e. -1, 0, 1 + pop edi + pop esi + ret +endp + +align 4 +proc strncpy stdcall, s1:dword, s2:dword, n:dword + + push esi + push edi + + mov ecx, [n] ; Maximum length + mov edi, [s2] ; edi = string s2 + xor al, al ; Look for a zero byte + mov edx, ecx ; Save maximum count + cld + repne scasb ; Look for end of s2 + sub edx, ecx ; Number of bytes in s2 including null + xchg ecx, edx + mov esi, [s2] ; esi = string s2 + mov edi, [s1] ; edi = string s1 + rep movsb ; Copy bytes + + mov ecx, edx ; Number of bytes not copied + rep stosb ; strncpy always copies n bytes by null padding + mov eax, [s1] ; Return s1 + pop edi + pop esi + ret +endp + +align 4 +proc strnlen stdcall, s:dword, n:dword + + push edi + mov ecx, [n] + mov edi, [s] ; edi = string + xor al, al ; Look for a zero byte + mov edx, ecx ; Save maximum count + cmp cl, 1 ; 'Z' bit must be clear if ecx = 0 + cld + repne scasb ; Look for zero + jne @F + inc ecx ; Don't count zero byte +@@: + mov eax, edx + sub eax, ecx ; Compute bytes scanned + pop edi + ret +endp + +align 4 +proc strchr stdcall, s:dword, c:dword + push edi + cld + mov edi, [s] ; edi = string + mov edx, 16 ; Look at small chunks of the string +.next: + shl edx, 1 ; Chunks become bigger each time + mov ecx, edx + xor al, al ; Look for the zero at the end + repne scasb + pushf ; Remember the flags + sub ecx, edx + neg ecx ; Some or all of the chunk + sub edi, ecx ; Step back + mov eax, [c] ; The character to look for + repne scasb + je .found + popf ; Did we find the end of string earlier? + jne .next ; No, try again + xor eax, eax ; Return NULL + pop edi + ret +.found: + pop eax ; Get rid of those flags + lea eax, [edi-1] ; Address of byte found + pop edi + ret + +endp + + +proc strrchr stdcall, s:dword, c:dword + push edi + mov edi, [s] ; edi = string + mov ecx, -1 + xor al, al + cld + repne scasb ; Look for the end of the string + not ecx ; -1 - ecx = Length of the string + null + dec edi ; Put edi back on the zero byte + mov eax, [c] ; The character to look for + std ; Downwards search + repne scasb + cld ; Direction bit back to default + jne .fail + lea eax, [edi+1] ; Found it + pop edi + ret +.fail: + xor eax, eax ; Not there + pop edi + ret +endp + + diff --git a/kernel/branches/kolibri-lldw/core/sync.inc b/kernel/branches/kolibri-lldw/core/sync.inc new file mode 100644 index 000000000..a6c41f039 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/sync.inc @@ -0,0 +1,355 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;; Synhronization for MenuetOS. ;; +;; Author: Halyavin Andrey, halyavin@land.ru ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + + +RWSEM_WAITING_FOR_WRITE = 0 +RWSEM_WAITING_FOR_READ = 1 + +;void __fastcall mutex_init(struct mutex *lock) + +align 4 +mutex_init: + mov [ecx+MUTEX.wait_list.next], ecx + mov [ecx+MUTEX.wait_list.prev], ecx + mov [ecx+MUTEX.count], 1 + ret + +;void __fastcall mutex_lock(struct mutex *lock) + +align 4 +mutex_lock: + + dec [ecx+MUTEX.count] + jns .done + + pushfd + cli + + sub esp, sizeof.MUTEX_WAITER + + list_add_tail esp, ecx ;esp= new waiter, ecx= list head + + mov edx, [TASK_BASE] + mov [esp+MUTEX_WAITER.task], edx + +.forever: + + mov eax, -1 + xchg eax, [ecx+MUTEX.count] + dec eax + jz @F + + mov [edx+TASKDATA.state], TSTATE_RUN_SUSPENDED + call change_task + jmp .forever +@@: + mov eax, ecx + list_del esp + + cmp [eax+MUTEX.wait_list.next], eax + jne @F + + mov [eax+MUTEX.count], 0 +@@: + add esp, sizeof.MUTEX_WAITER + + popfd +.done: + ret + +;void __fastcall mutex_unlock(struct mutex *lock) + +align 4 +mutex_unlock: + + pushfd + cli + + mov eax, [ecx+MUTEX.wait_list.next] + cmp eax, ecx + mov [ecx+MUTEX.count], 1 + je @F + + mov eax, [eax+MUTEX_WAITER.task] + mov [eax+TASKDATA.state], TSTATE_RUNNING +@@: + popfd + ret + + +;void __fastcall init_rwsem(struct rw_semaphore *sem) + +align 4 +init_rwsem: + mov [ecx+RWSEM.wait_list.next], ecx + mov [ecx+RWSEM.wait_list.prev], ecx + mov [ecx+RWSEM.count], 0 + ret + +;void __fastcall down_read(struct rw_semaphore *sem) + +align 4 +down_read: + pushfd + cli + + mov eax, [ecx+RWSEM.count] + test eax, eax + js @F + + cmp ecx, [ecx+RWSEM.wait_list.next] + je .ok +@@: + sub esp, sizeof.MUTEX_WAITER + + mov eax, [TASK_BASE] + mov [esp+MUTEX_WAITER.task], eax + mov [esp+MUTEX_WAITER.type], RWSEM_WAITING_FOR_READ + mov [eax+TASKDATA.state], TSTATE_RUN_SUSPENDED + + list_add_tail esp, ecx ;esp= new waiter, ecx= list head + + call change_task + + add esp, sizeof.MUTEX_WAITER + popfd + ret +.ok: + inc eax + mov [ecx+RWSEM.count], eax + + popfd + ret + +;void __fastcall down_write(struct rw_semaphore *sem) + +align 4 +down_write: + pushfd + cli + sub esp, sizeof.MUTEX_WAITER + + mov edx, [TASK_BASE] + mov [esp+MUTEX_WAITER.task], edx + mov [esp+MUTEX_WAITER.type], RWSEM_WAITING_FOR_WRITE + mov [edx+TASKDATA.state], TSTATE_RUN_SUSPENDED + + list_add_tail esp, ecx ;esp= new waiter, ecx= list head + + xor eax, eax + not eax + +.forever: + test eax, [ecx+RWSEM.count] + jz @F + + mov [edx+TASKDATA.state], TSTATE_RUN_SUSPENDED + call change_task + jmp .forever +@@: + mov [ecx+RWSEM.count], eax + list_del esp + + add esp, sizeof.MUTEX_WAITER + popfd + ret + +;void __fastcall up_read(struct rw_semaphore *sem) + +align 4 +up_read: + pushfd + cli + + dec [ecx+RWSEM.count] + jnz @F + + mov eax, [ecx+RWSEM.wait_list.next] + cmp eax, ecx + je @F + + mov eax, [eax+MUTEX_WAITER.task] + mov [eax+TASKDATA.state], TSTATE_RUNNING +@@: + popfd + ret + +;void __fastcall up_write(struct rw_semaphore *sem) + +align 4 +up_write: + + pushfd + cli + + mov eax, [ecx+RWSEM.wait_list.next] + mov [ecx+RWSEM.count], 0 + + cmp ecx, eax + je .done + + mov edx, [eax+MUTEX_WAITER.type] + test edx, edx + jnz .wake + + mov eax, [eax+MUTEX_WAITER.task] + mov [eax+TASKDATA.state], TSTATE_RUNNING +.done: + popfd + ret + +.wake: + push ebx + push esi + push edi + + xor esi, esi + mov edi, ecx + +.wake_list: + + mov ebx, [eax+MUTEX_WAITER.list.next] + list_del eax + mov edx, [eax+MUTEX_WAITER.task] + mov [edx+TASKDATA.state], TSTATE_RUNNING + inc esi + cmp edi, ebx + je .wake_done + + mov ecx, [ebx+MUTEX_WAITER.type] + test ecx, ecx + jz .wake_done + + mov eax, ebx + jmp .wake_list + +.wake_done: + add [edi+RWSEM.count], esi + + pop edi + pop esi + pop ebx + popfd + ret + + +purge RWSEM_WAITING_FOR_WRITE +purge RWSEM_WAITING_FOR_READ + + +if ~defined sync_inc +sync_inc_fix: +sync_inc fix sync_inc_fix + +;simplest mutex. +macro SimpleMutex name +{ +; iglobal + name dd 0 + name#.type = 1 +; endg +} +macro WaitSimpleMutex name +{ + local start_wait,ok +start_wait=$ + cli + cmp [name], dword 0 + jz ok + sti + call change_task + jmp start_wait +ok=$ + push eax + mov eax, dword [TASK_BASE+second_base_address] + mov eax, [eax+TASKDATA.pid] + mov [name], eax + pop eax + sti +} +macro ReleaseSimpleMutex name +{ + mov [name], dword 0 +} +macro TryWaitSimpleMutex name ;result in eax and in flags +{ + local ok,try_end + cmp [name], dword 0 + jz ok + xor eax, eax + jmp try_end +ok=$ + xor eax, eax + inc eax +try_end=$ +} +macro SimpleCriticalSection name +{ +; iglobal + name dd 0 + dd 0 + name#.type=2 +; endg +} +macro WaitSimpleCriticalSection name +{ + local start_wait,first_wait,inc_counter,end_wait + push eax + mov eax, [TASK_BASE+second_base_address] + mov eax, [eax+TASKDATA.pid] +start_wait=$ + cli + cmp [name], dword 0 + jz first_wait + cmp [name], eax + jz inc_counter + sti + call change_task + jmp start_wait +first_wait=$ + mov [name], eax + mov [name+4], dword 1 + jmp end_wait +inc_counter=$ + inc dword [name+4] +end_wait=$ + sti + pop eax +} +macro ReleaseSimpleCriticalSection name +{ + local release_end + dec dword [name+4] + jnz release_end + mov [name], dword 0 +release_end=$ +} +macro TryWaitSimpleCriticalSection name ;result in eax and in flags +{ + local ok,try_end + mov eax, [CURRENT_TASK+second_base_address] + mov eax, [eax+TASKDATA.pid] + cmp [name], eax + jz ok + cmp [name], 0 + jz ok + xor eax, eax + jmp try_end +ok=$ + xor eax, eax + inc eax +try_end=$ +} +_cli equ call MEM_HeapLock +_sti equ call MEM_HeapUnLock +end if + diff --git a/kernel/branches/kolibri-lldw/core/sys32-sp.inc b/kernel/branches/kolibri-lldw/core/sys32-sp.inc new file mode 100644 index 000000000..a7957cd96 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/sys32-sp.inc @@ -0,0 +1,14 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Éste archivo debe ser editado con codificación CP866 + + msg_sel_ker cp850 "núcleo", 0 + msg_sel_app cp850 "aplicación", 0 diff --git a/kernel/branches/kolibri-lldw/core/sys32.inc b/kernel/branches/kolibri-lldw/core/sys32.inc new file mode 100644 index 000000000..1309e9eca --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/sys32.inc @@ -0,0 +1,840 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +align 4 ;3A08 +build_interrupt_table: + mov edi, idts + mov esi, sys_int + mov ecx, 0x40 + mov eax, (10001110b shl 24) + os_code + @@: + movsw ; low word of code-entry + stosd ; interrupt gate type : os_code selector + movsw ; high word of code-entry + loop @b + movsd ; copy low dword of trap gate for int 0x40 + movsd ; copy high dword of trap gate for int 0x40 + mov ecx, 23 + mov eax, (10001110b shl 24) + os_code + @@: + movsw ; low word of code-entry + stosd ; interrupt gate type : os_code selector + movsw ; high word of code-entry + loop @b + lidt [esi] + ret + +iglobal + align 4 + sys_int: + ; exception handlers addresses (for interrupt gate construction) + dd e0,e1,e2,e3,e4,e5,e6,except_7 ; SEE: core/fpu.inc + dd e8,e9,e10,e11,e12,e13,page_fault_exc,e15 + dd e16, e17,e18, e19 + times 12 dd unknown_interrupt ;int_20..int_31 + + ; interrupt handlers addresses (for interrupt gate construction) + ; 0x20+ are IRQ handlers + dd irq0 + rept 12 irqn:1 \{dd irq_serv.irq_\#irqn\} + dd irqD + rept 18 irqn:14 \{dd irq_serv.irq_\#irqn\} + + ; int_0x40 gate trap (for directly copied) + dw i40 and 0xFFFF, os_code, 11101111b shl 8, i40 shr 16 + + rept 23 irqn:33 \{dd irq_serv.irq_\#irqn\} + + idtreg: ; data for LIDT instruction (!!! must be immediately below sys_int data) + dw 2*($-sys_int-4)-1 + dd idts ; 0x8000B100 + dw 0 ; alignment + + msg_fault_sel dd msg_exc_8,msg_exc_u,msg_exc_a,msg_exc_b + dd msg_exc_c,msg_exc_d,msg_exc_e,msg_exc_u + dd msg_exc_u,msg_exc_11 + + msg_exc_8 db "Double fault", 0 + msg_exc_u db "Undefined Exception", 0 + msg_exc_a db "Invalid TSS", 0 + msg_exc_b db "Segment not present", 0 + msg_exc_c db "Stack fault", 0 + msg_exc_d db "General protection fault", 0 + msg_exc_e db "Page fault", 0 + msg_exc_11 db "Alignment Check", 0 + + if lang eq sp + include 'core/sys32-sp.inc' + else + msg_sel_ker db "kernel", 0 + msg_sel_app db "application", 0 + end if + +endg + +macro save_ring3_context { + pushad +} +macro restore_ring3_context { + popad +} +macro exc_wo_code [num] { + e#num : + save_ring3_context + mov bl, num + jmp exc_c +} exc_wo_code 0,1,2,3,4,5,6,15,16,19 + +macro exc_w_code [num] { + e#num : + add esp, 4 + save_ring3_context + mov bl, num + jmp exc_c +} exc_w_code 8,9,10,11,12,13,17,18 + + +uglobal + pf_err_code dd ? +endg + +page_fault_exc: ; foolproof: selectors are clobbered ... + pop [ss:pf_err_code] ; actually, until the next #PF + save_ring3_context + mov bl, 14 + +exc_c: ; exceptions (all but 7th - #NM) + ; stack frame when exception/interrupt from ring3 + pushad (i.e right here) + reg_ss equ esp+0x30 + reg_esp3 equ esp+0x2C + reg_eflags equ esp+0x28 + reg_cs3 equ esp+0x24 + reg_eip equ esp+0x20 + ; this if frame from pushad + reg_eax equ esp+0x1C + reg_ecx equ esp+0x18 + reg_edx equ esp+0x14 + reg_ebx equ esp+0x10 + reg_esp0 equ esp+0x0C + reg_ebp equ esp+0x08 + reg_esi equ esp+0x04 + reg_edi equ esp+0x00 + + mov ax, app_data ; exception + mov ds, ax ; load proper values + mov es, ax ; to registers + cld ; clear the direction flag + movzx ebx, bl +; redirect to V86 manager? (EFLAGS & 0x20000) != 0? + test byte[reg_eflags+2], 2 + jnz v86_exc_c + cmp bl, 14 ; #PF + jne @f + call page_fault_handler ; SEE: core/memory.inc + @@: + mov esi, [current_slot] + btr [esi+APPDATA.except_mask], ebx + jnc @f + mov eax, [esi+APPDATA.exc_handler] + test eax, eax + jnz IRetToUserHook + @@: + cli + mov eax, [esi+APPDATA.debugger_slot] + test eax, eax + jnz .debug +; not debuggee => say error and terminate + call show_error_parameters + sti + mov [edx + TASKDATA.state], TSTATE_TERMINATING + call wakeup_osloop + call change_task +; If we're here, then the main OS thread has crashed before initializing IDLE thread. +; Or they both have crashed. Anyway, things are hopelessly broken. + hlt + jmp $-1 +.debug: +; we are debugged process, notify debugger and suspend ourself +; eax=debugger PID + mov ecx, 1 ; debug_message code=other_exception + cmp bl, 1 ; #DB + jne .notify ; notify debugger and suspend ourself + mov ebx, dr6 ; debug_message data=DR6_image + xor edx, edx + mov dr6, edx + mov edx, dr7 + mov cl, not 8 + .l1: + shl dl, 2 + jc @f + and bl, cl + @@: + sar cl, 1 + jc .l1 + mov cl, 3 ; debug_message code=debug_exception +.notify: + push ebx ; debug_message data + mov ebx, [TASK_BASE] + push [ebx+TASKDATA.pid] ; PID + push ecx ; debug_message code ((here: ecx==1/3)) + mov cl, 12 ; debug_message size + call debugger_notify ;; only ONE using, inline ??? SEE: core/debug.inc + add esp, 12 + mov edx, [TASK_BASE] + mov [edx+TASKDATA.state], TSTATE_RUN_SUSPENDED + call change_task ; SEE: core/shed.inc + restore_ring3_context + iretd + +IRetToUserHook: + xchg eax, [reg_eip] + sub dword[reg_esp3], 8 + mov edi, [reg_esp3] + stosd + mov [edi], ebx + restore_ring3_context +; simply return control to interrupted process +unknown_interrupt: + iretd + +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= +; bl - error vector +show_error_parameters: + cmp bl, 0x06 + jnz .no_ud + push ebx + mov ebx, ud_user_message + mov ebp, notifyapp + call fs_execute_from_sysdir_param + pop ebx +.no_ud: + mov edx, [TASK_BASE];not scratched below + if lang eq sp + DEBUGF 1, "K : Proceso - terminado forzado PID: %x [%s]\n", [edx+TASKDATA.pid], [current_slot] + else + DEBUGF 1, "K : Process - forced terminate PID: %x [%s]\n", [edx+TASKDATA.pid], [current_slot] + end if + cmp bl, 0x08 + jb .l0 + cmp bl, 0x11 + jbe .l1 + .l0: + mov bl, 0x09 + .l1: + mov eax, [msg_fault_sel+ebx*4 - 0x08*4] + DEBUGF 1, "K : %s\n", eax + mov eax, [reg_cs3+4] + mov edi, msg_sel_app + mov ebx, [reg_esp3+4] + cmp eax, app_code + je @f + mov edi, msg_sel_ker + mov ebx, [reg_esp0+4] + @@: + DEBUGF 1, "K : EAX : %x EBX : %x ECX : %x\n", [reg_eax+4], [reg_ebx+4], [reg_ecx+4] + DEBUGF 1, "K : EDX : %x ESI : %x EDI : %x\n", [reg_edx+4], [reg_esi+4], [reg_edi+4] + DEBUGF 1, "K : EBP : %x EIP : %x ESP : %x\n", [reg_ebp+4], [reg_eip+4], ebx + DEBUGF 1, "K : Flags : %x CS : %x (%s)\n", [reg_eflags+4], eax, edi + + DEBUGF 1, "K : Stack dump:\n" + push eax ebx ecx edx + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+00]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+04]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+08]: %x\n",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+12]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+16]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+20]: %x\n",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, "K : [ESP+24]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+28]: %x",[ebx] + add ebx, 4 + call .check_ESP + test eax, eax + jnz .error_ESP + DEBUGF 1, " [ESP+32]: %x\n",[ebx] + pop edx ecx ebx eax + ret +.error_ESP: + pop edx ecx ebx eax + DEBUGF 1, "\n" + DEBUGF 1, "K : Unexpected end of the stack\n" + ret +;-------------------------------------- +.check_ESP: + push ebx + shr ebx, 12 + mov ecx, ebx + shr ecx, 10 + mov edx, [master_tab+ecx*4] + test edx, PG_READ + jz .fail ; page table is not created + ; incorrect address in the program + + mov eax, [page_tabs+ebx*4] + test eax, 2 + jz .fail ; address not reserved for use. error + + pop ebx + xor eax, eax + ret + +.fail: + pop ebx + xor eax, eax + dec eax + ret +;-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-= + + restore reg_ss + restore reg_esp3 + restore reg_eflags + restore reg_cs + restore reg_eip + restore reg_eax + restore reg_ecx + restore reg_edx + restore reg_ebx + restore reg_esp0 + restore reg_ebp + restore reg_esi + restore reg_edi + + +align 4 +lock_application_table: + push eax ecx edx + mov ecx, application_table_mutex + call mutex_lock + + mov eax, [current_slot_idx] + shl eax, BSF sizeof.TASKDATA + add eax, TASK_TABLE+TASKDATA.pid + mov eax, [eax] + + mov [application_table_owner], eax + + pop edx ecx eax + + ret + +align 4 +unlock_application_table: + push eax ecx edx + + mov [application_table_owner], 0 + mov ecx, application_table_mutex + call mutex_unlock + + pop edx ecx eax + + ret + +; sysfn 64 implementation +align 4 +sys_resize_app_memory: +; in: eax = 64 - function number +; ebx = 1 - number of its only subfunction +; ecx = new amount of memory +; out: +; eax = 0 - success +; eax = 1 - out of memory + +; cmp eax,1 + dec ebx + jnz .no_application_mem_resize + + mov eax, [pg_data.pages_free] + shl eax, 12 + cmp eax, ecx + jae @f + + xor eax, eax + inc eax + jmp .store_result +@@: + stdcall new_mem_resize, ecx +.store_result: + mov [esp+32], eax +.no_application_mem_resize: + ret + +iglobal +; process_terminating db 'K : Process - terminating',13,10,0 +; process_terminated db 'K : Process - done',13,10,0 + msg_obj_destroy db 'K : destroy app object',13,10,0 +endg + +; param +; esi= slot + +align 4 +terminate: ; terminate application +destroy_thread: + + .slot equ esp+4 ;locals + .process equ esp ;ptr to parent process + + + push esi ;save .slot + + shl esi, 8 + mov edx, [SLOT_BASE+esi+APPDATA.process] + test edx, edx + jnz @F + pop esi + shl esi, BSF sizeof.TASKDATA + mov [TASK_TABLE+esi+TASKDATA.state], TSTATE_FREE + ret +@@: + push edx ;save .process + lea edx, [SLOT_BASE+esi] + call scheduler_remove_thread + call lock_application_table + +; if the process is in V86 mode... + mov eax, [.slot] + shl eax, 8 + mov esi, [eax+SLOT_BASE+APPDATA.pl0_stack] + add esi, RING0_STACK_SIZE + cmp [eax+SLOT_BASE+APPDATA.saved_esp0], esi + jz .nov86 +; ...it has page directory for V86 mode + mov esi, [eax+SLOT_BASE+APPDATA.saved_esp0] + mov ecx, [esi+4] + mov [eax+SLOT_BASE+APPDATA.process], ecx +; ...and I/O permission map for V86 mode + mov ecx, [esi+12] + mov [eax+SLOT_BASE+APPDATA.io_map], ecx + mov ecx, [esi+8] + mov [eax+SLOT_BASE+APPDATA.io_map+4], ecx +.nov86: +; destroy per-thread kernel objects + mov esi, [.slot] + shl esi, 8 + add esi, SLOT_BASE+APP_OBJ_OFFSET +@@: + mov eax, [esi+APPOBJ.fd] + test eax, eax + jz @F + + cmp eax, esi + je @F + + push esi + call [eax+APPOBJ.destroy] + DEBUGF 1,"%s",msg_obj_destroy + pop esi + jmp @B +@@: + mov esi, [.slot] + cmp [fpu_owner], esi ; if user fpu last -> fpu user = 2 + jne @F + + mov [fpu_owner], 2 + mov eax, [sizeof.APPDATA*2+SLOT_BASE+APPDATA.fpu_state] + clts + bt [cpu_caps], CAPS_SSE + jnc .no_SSE + fxrstor [eax] + jmp @F +.no_SSE: + fnclex + frstor [eax] +@@: + + mov [KEY_COUNT], byte 0 ; empty keyboard buffer + mov [BTN_COUNT], byte 0 ; empty button buffer + + +; remove defined hotkeys + mov eax, hotkey_list +.loop: + cmp [eax+8], esi + jnz .cont + mov ecx, [eax] + jecxz @f + push dword [eax+12] + pop dword [ecx+12] +@@: + mov ecx, [eax+12] + push dword [eax] + pop dword [ecx] + xor ecx, ecx + mov [eax], ecx + mov [eax+4], ecx + mov [eax+8], ecx + mov [eax+12], ecx +.cont: + add eax, 16 + cmp eax, hotkey_list+256*16 + jb .loop +; get process PID + mov eax, esi + shl eax, BSF sizeof.TASKDATA + mov eax, [eax+TASK_TABLE+TASKDATA.pid] +; compare current lock input with process PID + cmp eax, [PID_lock_input] + jne @f + + xor eax, eax + mov [PID_lock_input], eax +@@: +; remove hotkeys in buffer + mov eax, hotkey_buffer +.loop2: + cmp [eax], esi + jnz .cont2 + and dword [eax+4], 0 + and dword [eax], 0 +.cont2: + add eax, 8 + cmp eax, hotkey_buffer+120*8 + jb .loop2 + + mov ecx, esi ; remove buttons + bnewba2: + mov edi, [BTN_ADDR] + mov eax, edi + cld + movzx ebx, word [edi] + inc bx + bnewba: + dec bx + jz bnmba + add eax, 0x10 + cmp cx, [eax] + jnz bnewba + pusha + mov ecx, ebx + inc ecx + shl ecx, 4 + mov ebx, eax + add eax, 0x10 + call memmove + dec dword [edi] + popa + jmp bnewba2 + bnmba: + + pusha ; save window coordinates for window restoring + cld + shl esi, BSF sizeof.WDATA + add esi, window_data + mov eax, [esi+WDATA.box.left] + mov [draw_limits.left], eax + add eax, [esi+WDATA.box.width] + mov [draw_limits.right], eax + mov eax, [esi+WDATA.box.top] + mov [draw_limits.top], eax + add eax, [esi+WDATA.box.height] + mov [draw_limits.bottom], eax + + xor eax, eax + mov edi, esi + mov ecx, sizeof.WDATA/4 + rep stosd + + lea edi, [esi-window_data+draw_data] + mov ecx, sizeof.WDATA/4 + rep stosd + popa + +; debuggee test + pushad + mov edi, esi + shl edi, BSF sizeof.TASKDATA + mov eax, [SLOT_BASE+edi*8+APPDATA.debugger_slot] + test eax, eax + jz .nodebug + movi ecx, 8 + push dword [TASK_TABLE+edi+TASKDATA.pid]; PID + push 2 + call debugger_notify + pop ecx + pop ecx +.nodebug: + popad + + mov ebx, [.slot] + shl ebx, 8 + push ebx + mov ebx, [SLOT_BASE+ebx+APPDATA.pl0_stack] + + stdcall kernel_free, ebx + + pop ebx + mov ebx, [SLOT_BASE+ebx+APPDATA.cur_dir] + stdcall kernel_free, ebx + + mov edi, [.slot] + shl edi, 8 + add edi, SLOT_BASE + + mov eax, [edi+APPDATA.io_map] + cmp eax, [SLOT_BASE+sizeof.APPDATA+APPDATA.io_map] + je @F + call free_page +@@: + mov eax, [edi+APPDATA.io_map+4] + cmp eax, [SLOT_BASE+sizeof.APPDATA+APPDATA.io_map+4] + je @F + call free_page +@@: + lea ebx, [edi+APPDATA.list] + list_del ebx ;destroys edx, ecx + + mov eax, 0x20202020 + stosd + stosd + stosd + mov ecx, 244/4 + xor eax, eax + rep stosd + + ; activate window + movzx eax, word [WIN_STACK + esi*2] + cmp eax, [thread_count] + jne .dont_activate + pushad + .check_next_window: + dec eax + cmp eax, 1 + jbe .nothing_to_activate + lea esi, [WIN_POS+eax*2] + movzx edi, word [esi] ; edi = process + shl edi, BSF sizeof.TASKDATA + cmp [TASK_TABLE + edi + TASKDATA.state], TSTATE_FREE ; skip free slots + je .check_next_window + add edi, window_data +; \begin{diamond}[19.09.2006] +; skip minimized windows + test [edi + WDATA.fl_wstate], WSTATE_MINIMIZED + jnz .check_next_window +; \end{diamond} + call waredraw + .nothing_to_activate: + popad + .dont_activate: + + push esi ; remove hd1 & cd & flp reservation + shl esi, BSF sizeof.TASKDATA + mov esi, [esi+TASK_TABLE+TASKDATA.pid] + cmp [cd_status], esi + jnz @f + call free_cd_channel + and [cd_status], 0 +@@: + pop esi + cmp [bgrlockpid], esi + jnz @f + and [bgrlockpid], 0 + and [bgrlock], 0 +@@: + + pusha ; remove all port reservations + mov edx, esi + shl edx, BSF sizeof.TASKDATA + add edx, TASK_TABLE + mov edx, [edx+TASKDATA.pid] + + rmpr0: + + mov esi, [RESERVED_PORTS] + + test esi, esi + jz rmpr9 + + rmpr3: + + mov edi, esi + shl edi, 4 + add edi, RESERVED_PORTS + + cmp edx, [edi] + je rmpr4 + + dec esi + jnz rmpr3 + + jmp rmpr9 + + rmpr4: + + mov ecx, 256 + sub ecx, esi + shl ecx, 4 + + mov esi, edi + add esi, 16 + cld + rep movsb + + dec dword [RESERVED_PORTS] + + jmp rmpr0 + + rmpr9: + + popa + mov edi, esi ; do not run this process slot + shl edi, BSF sizeof.TASKDATA + mov [edi+TASK_TABLE + TASKDATA.state], TSTATE_FREE +; debugger test - terminate all debuggees + mov eax, 2 + mov ecx, SLOT_BASE+2*0x100+APPDATA.debugger_slot +.xd0: + cmp eax, [thread_count] + ja .xd1 + cmp dword [ecx], esi + jnz @f + and dword [ecx], 0 + pushad + xchg eax, ecx + mov ebx, 2 + call sys_system + popad +@@: + inc eax + add ecx, 0x100 + jmp .xd0 +.xd1: +;release slot + + bts [thr_slot_map], esi + + mov ecx, [.process] + lea eax, [ecx+PROC.thr_list] + cmp eax, [eax+LHEAD.next] + jne @F + + call destroy_process.internal +@@: + sti ; .. and life goes on + + mov eax, [draw_limits.left] + mov ebx, [draw_limits.top] + mov ecx, [draw_limits.right] + mov edx, [draw_limits.bottom] + call calculatescreen + xor eax, eax + xor esi, esi + call redrawscreen + + call unlock_application_table + ;mov esi,process_terminated + ;call sys_msg_board_str + add esp, 8 + ret +restore .slot +restore .process + +; Three following procedures are used to guarantee that +; some part of kernel code will not be terminated from outside +; while it is running. +; Note: they do not protect a thread from terminating due to errors inside +; the thread; accessing a nonexisting memory would still terminate it. + +; First two procedures must be used in pair by thread-to-be-protected +; to signal the beginning and the end of an important part. +; It is OK to have nested areas. + +; The last procedure must be used by outside wanna-be-terminators; +; if it is safe to terminate the given thread immediately, it returns eax=1; +; otherwise, it returns eax=0 and notifies the target thread that it should +; terminate itself when leaving a critical area (the last critical area if +; they are nested). + +; Implementation. Those procedures use one dword in APPDATA for the thread, +; APPDATA.terminate_protection. +; * The upper bit is 1 during normal operations and 0 when terminate is requested. +; * Other bits form a number = depth of critical regions, +; plus 1 if the upper bit is 1. +; * When this dword goes to zero, the thread should be destructed, +; and the procedure in which it happened becomes responsible for destruction. + +; Enter critical area. Called by thread which wants to be protected. +proc protect_from_terminate + mov edx, [current_slot] +; Atomically increment depth of critical areas and get the old value. + mov eax, 1 + lock xadd [edx+APPDATA.terminate_protection], eax +; If the old value was zero, somebody has started to terminate us, +; so we are destructing and cannot do anything protected. +; Otherwise, return to the caller. + test eax, eax + jz @f + ret +@@: +; Wait for somebody to finish us. + call change_task + jmp @b +endp + +; Leave critical area. Called by thread which wants to be protected. +proc unprotect_from_terminate + mov edx, [current_slot] +; Atomically decrement depth of critical areas. + lock dec [edx+APPDATA.terminate_protection] +; If the result of decrement is zero, somebody has requested termination, +; but at that moment we were inside a critical area; terminate now. + jz sys_end +; Otherwise, return to the caller. + ret +endp + +; Request termination of thread identified by edx = SLOT_BASE + slot*sizeof.APPDATA. +; Called by anyone. +proc request_terminate + xor eax, eax ; set return value +; Atomically clear the upper bit. If it was already zero, then +; somebody has requested termination before us, so just exit. + lock btr [edx+APPDATA.terminate_protection], 31 + jnc .unsafe +; Atomically decrement depth of critical areas. + lock dec [edx+APPDATA.terminate_protection] +; If the result of decrement is nonzero, the target thread is inside a +; critical area; leave termination to leaving that area. + jnz .unsafe +; Otherwise, it is safe to kill the target now and the caller is responsible +; for this. Return eax=1. + inc eax +.unsafe: + ret +endp + diff --git a/kernel/branches/kolibri-lldw/core/syscall.inc b/kernel/branches/kolibri-lldw/core/syscall.inc new file mode 100644 index 000000000..d78bd2516 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/syscall.inc @@ -0,0 +1,186 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSENTER ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +align 32 +sysenter_entry: + ; Setting up the stack + mov esp, [ss:tss._esp0] + sti + push ebp ; save app esp + 4 + mov ebp, [ebp] ; ebp - original ebp + ;------------------ + pushad + cld + + call protect_from_terminate + + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + + call unprotect_from_terminate + popad + ;------------------ + xchg ecx, [ss:esp] ; in the stack top - app ecx, ecx - app esp + 4 + sub ecx, 4 + xchg edx, [ecx] ; edx - return point, & save original edx + push edx + mov edx, [ss:esp + 4] + mov [ecx + 4], edx ; save original ecx + pop edx + sysexit + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSTEM CALL ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +align 16 +i40: + pushad + cld + call protect_from_terminate + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + call unprotect_from_terminate + popad + iretd + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; SYSCALL ENTRY ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +align 32 +syscall_entry: + ; cli syscall clear IF + xchg esp, [ss:tss._esp0] + push ecx + lea ecx, [esp+4] + xchg ecx, [ss:tss._esp0] + sti + push ecx + mov ecx, [ecx] + ;------------------ + pushad + cld + call protect_from_terminate + + movzx eax, byte [esp+28] + mov edx, dword [esp+20] + call dword [servetable2 + eax * 4] + + call unprotect_from_terminate + popad + ;------------------ + mov ecx, [ss:esp+4] + pop esp + sysret + +iglobal + ;;;;;;;;;;;;;;;;;;;;;;;;;;;; + ;; SYSTEM FUNCTIONS TABLE ;; + ;;;;;;;;;;;;;;;;;;;;;;;;;;;; + align 4 + servetable2: + + dd syscall_draw_window ; 0-DrawWindow + dd syscall_setpixel ; 1-SetPixel + dd sys_getkey ; 2-GetKey + dd sys_clock ; 3-GetTime + dd syscall_writetext ; 4-WriteText + dd delay_hs_unprotected ; 5-DelayHs + dd undefined_syscall ; 6-deprecated OpenRamdiskFile + dd syscall_putimage ; 7-PutImage + dd syscall_button ; 8-DefineButton + dd sys_cpuusage ; 9-GetProcessInfo + dd sys_waitforevent ; 10-WaitForEvent + dd sys_getevent ; 11-CheckForEvent + dd sys_redrawstat ; 12-BeginDraw and EndDraw + dd syscall_drawrect ; 13-DrawRect + dd syscall_getscreensize ; 14-GetScreenSize + dd sys_background ; 15-bgr + dd sys_cachetodiskette ; 16-FlushFloppyCache + dd sys_getbutton ; 17-GetButton + dd sys_system ; 18-System Services + dd paleholder ; 19-reserved + dd sys_midi ; 20-ResetMidi and OutputMidi + dd sys_setup ; 21-SetMidiBase,SetKeymap,SetShiftKeymap,. + dd sys_settime ; 22-setting date,time,clock and alarm-clock + dd sys_wait_event_timeout ; 23-TimeOutWaitForEvent + dd syscall_cdaudio ; 24-PlayCdTrack,StopCd and GetCdPlaylist + dd syscall_putarea_backgr ; 25-Put Area to background + dd sys_getsetup ; 26-GetMidiBase,GetKeymap,GetShiftKeymap,. + dd undefined_syscall ; 27-reserved + dd undefined_syscall ; 28-reserved + dd sys_date ; 29-GetDate + dd sys_current_directory ; 30-Get/SetCurrentDirectory + dd undefined_syscall ; 31-reserved + dd undefined_syscall ; 32-reserved + dd undefined_syscall ; 33-reserved + dd syscall_getpixel_WinMap ; 34-GetPixel WinMap + dd syscall_getpixel ; 35-GetPixel + dd syscall_getarea ; 36-GetArea + dd readmousepos ; 37-GetMousePosition_ScreenRelative,. + dd syscall_drawline ; 38-DrawLine + dd sys_getbackground ; 39-GetBackgroundSize,ReadBgrData,. + dd set_app_param ; 40-WantEvents + dd undefined_syscall ; 41- deprecated GetIrqOwner + dd undefined_syscall ; 42- deprecated ReadIrqData + dd sys_outport ; 43-SendDeviceData + dd undefined_syscall ; 44- deprecated ProgramIrqs + dd undefined_syscall ; 45- deprecated ReserveIrq and FreeIrq + dd syscall_reserveportarea ; 46-ReservePortArea and FreePortArea + dd display_number ; 47-WriteNum + dd syscall_display_settings ; 48-SetRedrawType and SetButtonType + dd sys_apm ; 49-Advanced Power Management (APM) + dd syscall_set_window_shape ; 50-Window shape & scale + dd syscall_threads ; 51-Threads + dd undefined_syscall ; 52- deprecated Stack driver status + dd undefined_syscall ; 53- deprecated Socket interface + dd sys_clipboard ; 54-Custom clipboard + dd sound_interface ; 55-Sound interface + dd undefined_syscall ; 56-reserved + dd sys_pcibios ; 57-PCI BIOS32 + dd undefined_syscall ; 58-deprecated Common file system interface + dd undefined_syscall ; 59-reserved + dd sys_IPC ; 60-Inter Process Communication + dd sys_gs ; 61-Direct graphics access + dd pci_api ; 62-PCI functions + dd sys_msg_board ; 63-System message board + dd sys_resize_app_memory ; 64-Resize application memory usage + dd sys_putimage_palette ; 65-PutImagePalette + dd sys_process_def ; 66-Process definitions - keyboard + dd syscall_move_window ; 67-Window move or resize + dd f68 ; 68-Some internal services + dd sys_debug_services ; 69-Debug + dd file_system_lfn ; 70-Common file system interface, version 2 + dd syscall_window_settings ; 71-Window settings + dd sys_sendwindowmsg ; 72-Send window message + dd blit_32 ; 73-blitter; + dd sys_network ; 74-reserved for new stack + dd sys_socket ; 75-reserved for new stack + dd sys_protocols ; 76-reserved for new stack + dd sys_posix ; posix support + dd undefined_syscall ; 78-free + dd undefined_syscall ; 79-free + dd fileSystemUnicode ; 80-File system interface for different encodings + + times 255 - ( ($-servetable2) /4 ) dd undefined_syscall + dd sys_end ; -1-end application + +endg diff --git a/kernel/branches/kolibri-lldw/core/taskman.inc b/kernel/branches/kolibri-lldw/core/taskman.inc new file mode 100644 index 000000000..dcd02d430 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/taskman.inc @@ -0,0 +1,1059 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +GREEDY_KERNEL = 0 + +struct APP_HEADER_00_ + banner dq ? + version dd ? ;+8 + start dd ? ;+12 + i_end dd ? ;+16 + mem_size dd ? ;+20 + i_param dd ? ;+24 +ends + +struct APP_HEADER_01_ + banner dq ? + version dd ? ;+8 + start dd ? ;+12 + i_end dd ? ;+16 + mem_size dd ? ;+20 + stack_top dd ? ;+24 + i_param dd ? ;+28 + i_icon dd ? ;+32 +ends + +struct APP_HDR + cmdline rd 1 ;0x00 + path rd 1 ;0x04 + eip rd 1 ;0x08 + esp rd 1 ;0x0C + _edata rd 1 ;0x10 + _emem rd 1 ;0x14 + img_base rd 1 ;0x18 + img_size rd 1 + filename_size rd 1 + cmdline_size rd 1 + path_string rd 1 +ends + +macro _clear_ op +{ mov ecx, op/4 + xor eax, eax + cld + rep stosd +} + +align 4 +_strnlen: + mov edx, ecx + xor eax, eax + repne scasb + jne @F + inc ecx +@@: + mov eax, edx + sub eax, ecx + retn + +fs_execute_from_sysdir: + xor ebx, ebx +fs_execute_from_sysdir_param: + stdcall kernel_alloc, maxPathLength + push eax ebx + mov esi, ebp + mov edi, eax + xor eax, eax + call getFullPath + pop ecx ebx + xor edx, edx +; @brief Executes a program +; @param edx Flags +; @param ecx Commandline +; @param ebx Absolute file path +; @param eax String length +; @returns Negated error code or new process number +proc fs_execute + locals + cmdline rd 1 + flags rd 1 + slot rd 1 ; index of new thread slot + slot_base rd 1 ; base address of it +; app header data + hdr_cmdline rd 1 + hdr_path rd 1 + hdr_eip rd 1 + hdr_esp rd 1 + hdr_edata rd 1 + hdr_emem rd 1 + file_base rd 1 + file_size rd 1 + filename_size rd 1 + cmdline_size rd 1 + path_string rd 1 + endl + + mov [flags], edx + mov [cmdline], ecx + mov [path_string], ebx + mov [filename_size], eax + mov esi, -ERROR_FILE_NOT_FOUND + test eax, eax + jz .err_file + stdcall load_file, ebx + test eax, eax + jz .err_file + + mov [file_base], eax + mov [file_size], ebx + lea ebx, [hdr_cmdline] + call test_app_header ; fill our app header data locals with values from header of given program (if its correct) + mov esi, -TASKMAN_ERROR_NOT_A_EXECUTABLE + test eax, eax + jz .err_hdr + + call lock_application_table + call alloc_thread_slot ; create a slot for new thread + mov esi, -TASKMAN_ERROR_TOO_MANY_PROCESSES + test eax, eax + jz .err_0 + + mov [slot], eax + shl eax, 8 + lea edi, [SLOT_BASE+eax] + mov [slot_base], edi +; clean extended information about process + mov ecx, sizeof.APPDATA/4 + xor eax, eax + cld + rep stosd +; write application name ( APPDATA.appname ) + stdcall strrchr, [path_string], '/' + lea esi, [eax+1] ; -> name without path + mov ecx, 11 + mov edi, [slot_base] +@@: + call utf8to16 + call uni2ansi_char + cmp al, '.' + jz @f + test al, al + jz @f + stosb + loop @b +@@: + mov edi, [cmdline] + xor eax, eax + test edi, edi + jz @f + mov ecx, 65535 + call _strnlen + cmp eax, 256 + jb @f +; if cmdline length >= 256 then increase needed memory size by this length + lea ebx, [eax+1] + add [hdr_emem], ebx +@@: + mov [cmdline_size], eax + stdcall create_process, [hdr_emem] ; create a new process + mov esi, -TASKMAN_ERROR_OUT_OF_MEMORY + test eax, eax + jz .err_hdr + +; add new process to the list + mov ebx, [sys_proc+LHEAD.prev] + __list_add eax, ebx, sys_proc +; fill the structure fields: + mov ebx, [hdr_emem] + mov [eax+PROC.mem_used], ebx + +; write that main thread of app belongs to new process + mov ebx, [slot_base] + mov [ebx+APPDATA.process], eax + +; initialize the thread list of process: at this moment it consists only of one main thread + lea edx, [ebx+APPDATA.list] + lea ecx, [eax+PROC.thr_list] + list_add_tail edx, ecx + +; allocate space and copy app header data locals and cmdline string there, put pointer to exec_params of new thread + mov eax, [cmdline_size] + add eax, sizeof.APP_HDR + stdcall kernel_alloc, eax + mov [ebx+APPDATA.exec_params], eax + mov edi, eax + lea esi, [hdr_cmdline] + mov ecx, sizeof.APP_HDR/4 + rep movsd + mov ecx, [cmdline_size] + mov esi, [cmdline] + rep movsb +; set other parameters of application + lea eax, [hdr_cmdline] + stdcall set_app_params , [slot], eax, [flags] + mov eax, [process_number] ; return process number + call unlock_application_table + ret + +.err_0: + call unlock_application_table +.err_hdr: + stdcall kernel_free, [file_base] +.err_file: + stdcall kernel_free, [path_string] + mov eax, esi + ret +endp + +align 4 +test_app_header: + virtual at eax + APP_HEADER_00 APP_HEADER_00_ + end virtual + virtual at eax + APP_HEADER_01 APP_HEADER_01_ + end virtual + + cmp dword [eax], 'MENU' + jne .fail + cmp word [eax+4], 'ET' + jne .fail + + cmp [eax+6], word '00' + jne .check_01_header + + mov ecx, [APP_HEADER_00.start] + mov [ebx+APP_HDR.eip], ecx + mov edx, [APP_HEADER_00.mem_size] + mov [ebx+APP_HDR._emem], edx + shr edx, 1 + sub edx, 0x10 + mov [ebx+APP_HDR.esp], edx + mov ecx, [APP_HEADER_00.i_param] + mov [ebx+APP_HDR.cmdline], ecx + mov [ebx+APP_HDR.path], 0 + mov edx, [APP_HEADER_00.i_end] + mov [ebx+APP_HDR._edata], edx + ret + + .check_01_header: + + cmp [eax+6], word '01' + je @f + cmp [eax+6], word '02' + jne .fail +@@: + mov ecx, [APP_HEADER_01.start] + mov [ebx+0x08], ecx + mov edx, [APP_HEADER_01.mem_size] + +; \begin{diamond}[20.08.2006] +; sanity check (functions 19,58 load app_i_end bytes and that must +; fit in allocated memory to prevent kernel faults) + cmp edx, [APP_HEADER_01.i_end] + jb .fail +; \end{diamond}[20.08.2006] + + mov [ebx+APP_HDR._emem], edx + mov ecx, [APP_HEADER_01.stack_top] + mov [ebx+APP_HDR.esp], ecx + mov edx, [APP_HEADER_01.i_param] + mov [ebx+APP_HDR.cmdline], edx + mov ecx, [APP_HEADER_01.i_icon] + mov [ebx+APP_HDR.path], ecx + mov edx, [APP_HEADER_01.i_end] + mov [ebx+APP_HDR._edata], edx + ret +.fail: + xor eax, eax + ret + +align 4 +alloc_thread_slot: +;input: +; none +;result: +; eax=[new_thread_slot]<>0 - ok +; 0 - failed. +;This function find least empty slot. +;It doesn't increase [thread_count]! + + + mov edx, thr_slot_map + pushfd + cli +.l1: + bsf eax, [edx] + jnz .found + add edx, 4 + cmp edx, thr_slot_map+32 + jb .l1 + + popfd + xor eax, eax + ret +.found: + btr [edx], eax + sub edx, thr_slot_map + lea eax, [eax+edx*8] + popfd + ret + +align 4 +proc create_process stdcall, app_size:dword + locals + process dd ? + app_tabs dd ? + endl + + push ebx + push esi + push edi + + xor eax, eax + mov [process], eax + + mov eax, [app_size] + add eax, 0x3FFFFF + shr eax, 22 + mov [app_tabs], eax + + stdcall kernel_alloc, 0x2000 + test eax, eax + jz .fail + mov [process], eax + + lea edi, [eax+PROC.heap_lock] + mov ecx, (PROC.ht_free-PROC.heap_lock)/4 + + list_init eax + add eax, PROC.thr_list + list_init eax + + xor eax, eax + cld + rep stosd + + mov [edi], dword (PROC.pdt_0 - PROC.htab)/4 - 3 + mov [edi+4], dword 3 ;reserve handles for stdin stdout and stderr + mov ecx, (PROC.pdt_0 - PROC.htab)/4 + add edi, 8 + inc eax +@@: + stosd + inc eax + cmp eax, ecx + jbe @B + + mov eax, edi + call get_pg_addr + mov [edi-4096+PROC.pdt_0_phys], eax + + mov ecx, (OS_BASE shr 20)/4 + xor eax, eax + rep stosd + + mov ecx, (OS_BASE shr 20)/4 + mov esi, sys_proc+PROC.pdt_0+(OS_BASE shr 20) + rep movsd + + mov eax, [edi-8192+PROC.pdt_0_phys] + or eax, PG_SWR + mov [edi-4096+(page_tabs shr 20)], eax + + lea edx, [edi-4096] + mov esi, [app_tabs] + +.alloc_page_dir: + call alloc_page + test eax, eax + jz .fail + or eax, PG_UWR + mov [edx], eax + + mov edi, [tmp_task_ptab] + stdcall map_page, edi, eax, PG_SWR + mov ecx, 1024 + xor eax, eax + rep stosd + + add edx, 4 + dec esi + jnz .alloc_page_dir + + stdcall map_page, [tmp_task_ptab], 0, PG_UNMAP + mov eax, [process] + + pop edi + pop esi + pop ebx + ret +.fail: + mov ecx, [process] + jcxz @F + + call destroy_process +@@: + xor eax, eax + pop edi + pop esi + pop ebx + ret +endp + +align 4 +proc destroy_page_table stdcall, pg_tab:dword + + push esi + + mov esi, [pg_tab] + mov ecx, 1024 +.free: + mov eax, [esi] + test eax, 1 + jz .next + test eax, 2 + jz .next + test eax, 1 shl 9 + jnz .next ;skip shared pages + call free_page +.next: + add esi, 4 + dec ecx + jnz .free + pop esi + ret +endp + +align 4 +destroy_process: ;fastcall ecx= ptr to process + + lea eax, [ecx+PROC.thr_list] + cmp eax, [eax+LHEAD.next] + jne .exit + +align 4 +.internal: + push ecx + + mov esi, ecx + list_del esi + + mov esi, [esi+PROC.dlls_list_ptr] + call destroy_all_hdlls + + mov esi, [esp] + add esi, PROC.pdt_0 + mov edi, (0x80000000 shr 20)/4 +.destroy: + mov eax, [esi] + test eax, 1 + jz .next + and eax, not 0xFFF + stdcall map_page, [tmp_task_ptab], eax, PG_SWR + stdcall destroy_page_table, [tmp_task_ptab] + mov eax, [esi] + call free_page +.next: + add esi, 4 + dec edi + jnz .destroy + + call kernel_free ;ecx still in stack + stdcall map_page, [tmp_task_ptab], 0, PG_UNMAP +.exit: + ret + +align 4 +get_pid: + mov eax, [TASK_BASE] + mov eax, [eax+TASKDATA.pid] + ret + +pid_to_slot: +;Input: +; eax - pid of process +;Output: +; eax - slot of process or 0 if process don't exists +;Search process by PID. + push ebx + push ecx + mov ebx, [thread_count] + shl ebx, BSF sizeof.TASKDATA ; multiply by size + ; add 2*32 cause: + ; [TASK_TABLE; TASK_TABLE + 32) isnt a task actually + ; skip first process in the task table + mov ecx, 2*32 + +.loop: +;ecx = offset of current process info entry +;ebx = maximum permitted offset + cmp [TASK_TABLE+ecx+TASKDATA.state], TSTATE_FREE + jz .endloop ;skip empty slots + cmp [TASK_TABLE+ecx+TASKDATA.pid], eax;check PID + jz .pid_found +.endloop: + add ecx, sizeof.TASKDATA + cmp ecx, ebx + jle .loop + + pop ecx + pop ebx + xor eax, eax + ret + +.pid_found: + shr ecx, BSF sizeof.TASKDATA ; divide by size + mov eax, ecx ;convert offset to index of slot + pop ecx + pop ebx + ret + + +align 4 +proc read_process_memory +;Input: +; eax - process slot +; ecx - buffer address +; edx - buffer size +; esi - start address in other process +;Output: +; eax - number of bytes read. + locals + slot dd ? + buff dd ? + r_count dd ? + offset dd ? + tmp_r_cnt dd ? + endl + + mov [slot], eax + mov [buff], ecx + and [r_count], 0 + mov [tmp_r_cnt], edx + mov [offset], esi + + pushad +.read_mem: + mov edx, [offset] + mov ebx, [tmp_r_cnt] + + mov ecx, 0x400000 + and edx, 0x3FFFFF + sub ecx, edx + cmp ecx, ebx + jbe @f + mov ecx, ebx +@@: + cmp ecx, 0x8000 + jna @F + mov ecx, 0x8000 +@@: + mov ebx, [offset] + + push ecx + stdcall map_memEx, [proc_mem_map], \ + [slot], ebx, ecx, PG_READ + pop ecx + + mov esi, [offset] + and esi, 0xfff + sub eax, esi + jbe .ret + cmp ecx, eax + jbe @f + mov ecx, eax + mov [tmp_r_cnt], eax +@@: + add esi, [proc_mem_map] + mov edi, [buff] + mov edx, ecx + rep movsb + add [r_count], edx + + add [offset], edx + sub [tmp_r_cnt], edx + jnz .read_mem +.ret: + popad + mov eax, [r_count] + ret +endp + +align 4 +proc write_process_memory +;Input: +; eax - process slot +; ecx - buffer address +; edx - buffer size +; esi - start address in other process +;Output: +; eax - number of bytes written + + locals + slot dd ? + buff dd ? + w_count dd ? + offset dd ? + tmp_w_cnt dd ? + endl + + mov [slot], eax + mov [buff], ecx + and [w_count], 0 + mov [tmp_w_cnt], edx + mov [offset], esi + + pushad +.read_mem: + mov edx, [offset] + mov ebx, [tmp_w_cnt] + + mov ecx, 0x400000 + and edx, 0x3FFFFF + sub ecx, edx + cmp ecx, ebx + jbe @f + mov ecx, ebx +@@: + cmp ecx, 0x8000 + jna @F + mov ecx, 0x8000 +@@: + mov ebx, [offset] + push ecx + stdcall map_memEx, [proc_mem_map], \ + [slot], ebx, ecx, PG_SWR + pop ecx + + mov edi, [offset] + and edi, 0xfff + sub eax, edi + jbe .ret + cmp ecx, eax + jbe @f + mov ecx, eax + mov [tmp_w_cnt], eax +@@: + add edi, [proc_mem_map] + mov esi, [buff] + mov edx, ecx + rep movsb + + add [w_count], edx + add [offset], edx + sub [tmp_w_cnt], edx + jnz .read_mem +.ret: + popad + mov eax, [w_count] + ret +endp + +;ebx = 1 - kernel thread +;ecx=thread entry point +;edx=thread stack pointer +;creation flags 0x01 - debugged +; 0x02 - kernel + +align 4 +proc new_sys_threads + locals + slot dd ? + flags dd ? + app_cmdline dd ? ;0x00 + app_path dd ? ;0x04 + app_eip dd ? ;0x08 + app_esp dd ? ;0x0C + app_mem dd ? ;0x10 + endl + + shl ebx, 1 + mov [flags], ebx + + xor eax, eax + mov [app_eip], ecx + mov [app_cmdline], eax + mov [app_esp], edx + mov [app_path], eax + + call lock_application_table + + call alloc_thread_slot + test eax, eax + jz .failed + + mov [slot], eax + + mov esi, [current_slot] + mov ebx, esi ;ebx=esi - pointer to extended information about current thread + + mov edi, eax + shl edi, 8 + add edi, SLOT_BASE + mov edx, edi ;edx=edi - pointer to extended infomation about new thread + mov ecx, sizeof.APPDATA/4 + xor eax, eax + cld + rep stosd ;clean extended information about new thread + mov esi, ebx + mov edi, edx + mov ecx, 11 + rep movsb ;copy process name + + + mov eax, [ebx+APPDATA.tls_base] + test eax, eax + jz @F + + push edx + stdcall user_alloc, 4096 + pop edx + test eax, eax + jz .failed1;eax=0 +@@: + mov [edx+APPDATA.tls_base], eax + + mov eax, [ebx+APPDATA.process] + mov [edx+APPDATA.process], eax + + lea ebx, [edx+APPDATA.list] + lea ecx, [eax+PROC.thr_list] + list_add_tail ebx, ecx ;add thread to process child's list + + lea eax, [app_cmdline] + stdcall set_app_params , [slot], eax, [flags] + + mov eax, [process_number] ;set result + call unlock_application_table + ret +.failed: + xor eax, eax +.failed1: + call unlock_application_table + dec eax ;-1 + ret +endp + +proc map_process_image stdcall, img_size:dword, file_base:dword, file_size:dword + + mov edx, [img_size] + mov esi, [file_base] + mov ecx, [file_size] + add edx, 4095 + add ecx, 4095 + shr edx, 12 ; total pages + shr ecx, 12 ; image pages + + mov edi, page_tabs + shr esi, 10 + add esi, edi + +.map_image: + lodsd + and eax, -4096 + or eax, PG_UWR + stosd + dec edx + loop .map_image + + test edx, edx + jz .done +.map_bss: + call alloc_page + test eax, eax + jz .fail + + or eax, PG_UWR + stosd + dec edx + jnz .map_bss + + mov edi, [file_size] + mov ecx, [img_size] + add edi, 4095 + and edi, -4096 + add ecx, 4095 + and ecx, -4096 + sub ecx, edi + shr ecx, 2 + xor eax, eax + rep stosd +.done: +.fail: + ret +endp + +align 4 +common_app_entry: + mov ebp, [current_slot] + mov ebp, [ebp+APPDATA.exec_params] + test ebp, ebp + jz .exit +; APPDATA.exec_params have first thread only, +; so second and next threads don't get here (they jump to .exit) + stdcall map_process_image, [ebp+APP_HDR._emem],\ + [ebp+APP_HDR.img_base], [ebp+APP_HDR.img_size] + mov esi, [ebp+APP_HDR.path_string] + mov edi, [ebp+APP_HDR.path] + mov ecx, [ebp+APP_HDR.filename_size] + cmp ecx, 1023 + jc @f + mov ecx, 1022 +@@: + push esi + test edi, edi + jz @f + stdcall is_region_userspace, edi, [ebp+APP_HDR.filename_size] + jnz @f + mov al, '/' + stosb + rep movsb + mov byte [edi], 0 +@@: + call kernel_free + mov edi, [ebp+APP_HDR.cmdline] + test edi, edi + jz .check_tls_header + lea esi, [ebp+sizeof.APP_HDR] + mov ecx, [ebp+APP_HDR.cmdline_size] + cmp ecx, 256 + jb .copy_cmdline + mov edi, [ebp+APP_HDR._emem] + add edi, 4095 + and edi, -4096 + sub edi, ecx + dec edi + cmp word [6], '00' + jne @f + mov [APP_HEADER_00_.i_param], edi + jmp .copy_cmdline +@@: + mov [APP_HEADER_01_.i_param], edi +.copy_cmdline: + inc ecx ; keep in mind about 0 in the end + stdcall is_region_userspace, edi, ecx + jnz .check_tls_header + dec ecx + rep movsb + mov byte [edi], 0 +.check_tls_header: + cmp word [6], '02' + jne .try_load_dll ;.cleanup + call init_heap + stdcall user_alloc, 4096 + mov edx, [current_slot] + mov [edx+APPDATA.tls_base], eax + mov [tls_data_l+2], ax + shr eax, 16 + mov [tls_data_l+4], al + mov [tls_data_l+7], ah + mov dx, app_tls + mov fs, dx +; { Patch by Coldy, For DLL autoload +.try_load_dll: +; Test app header version + mov ecx, dword[ebp+APP_HDR.img_base] + cmp dword[ecx+8], 2 + jne .cleanup +;if APP_HEADER.version = 2 => load lib/dll.obj & change eip to APP_STARTUP_THUNK + DEBUGF 1, 'K : App header version 2\n' + stdcall load_library, dll_lib_path, 0 + cmp eax, 0 + jne @f +; Something went wrong (TODO: Next 2 line is code copy after .cleanup) + stdcall free_kernel_space, [ebp+APP_HDR.img_base] + stdcall kernel_free, ebp + DEBUGF 1, 'K : DLL.OBJ not found! Terminate application!\n' + mov ebx, dll_error_msg + mov ebp, notifyapp + call fs_execute_from_sysdir_param +; Terminate process (TODO: Need jump to .cleanup after sys_end ?) + call sys_end + +@@: +; Find APP_STARTUP_THUNK in DLL.OBJ + sub eax, 4 + mov eax, [eax] + +;.change_eip: + mov ecx, [current_slot] + mov ecx, [ecx+APPDATA.pl0_stack] + mov [ecx+REG_EIP], eax + +; } End patch by Coldy, For DLL autoload +.cleanup: + stdcall free_kernel_space, [ebp+APP_HDR.img_base] + stdcall kernel_free, ebp + mov ebx, [current_slot] + cmp [ebx+APPDATA.debugger_slot], 0 + je .exit + mov eax, [TASK_BASE] + mov [eax+TASKDATA.state], TSTATE_RUN_SUSPENDED + call change_task +.exit: + popad + iretd + +EFL_IF = 0x0200 +EFL_IOPL1 = 0x1000 +EFL_IOPL2 = 0x2000 +EFL_IOPL3 = 0x3000 + +align 4 +proc set_app_params stdcall,slot:dword, params:dword, flags:dword + + locals + pl0_stack dd ? + endl + + mov eax, [xsave_area_size] + add eax, RING0_STACK_SIZE + stdcall kernel_alloc, eax + mov [pl0_stack], eax + + lea edi, [eax+RING0_STACK_SIZE] + + mov eax, [slot] + mov ebx, eax + + shl eax, 8 + mov [eax+SLOT_BASE+APPDATA.fpu_state], edi + mov [eax+SLOT_BASE+APPDATA.exc_handler], 0 + mov [eax+SLOT_BASE+APPDATA.except_mask], 0 + mov [eax+SLOT_BASE+APPDATA.terminate_protection], 80000001h + +;set default io permission map + mov ecx, [SLOT_BASE+sizeof.APPDATA+APPDATA.io_map] + mov [eax+SLOT_BASE+APPDATA.io_map], ecx + mov ecx, [SLOT_BASE+sizeof.APPDATA+APPDATA.io_map+4] + mov [eax+SLOT_BASE+APPDATA.io_map+4], ecx + + mov esi, fpu_data + mov ecx, [xsave_area_size] + add ecx, 3 + shr ecx, 2 + rep movsd + + cmp [thread_count], ebx + adc [thread_count], 0 ; update number of processes + shl ebx, 8 + lea edx, [ebx+SLOT_BASE+APP_EV_OFFSET] + mov [SLOT_BASE+APPDATA.fd_ev+ebx], edx + mov [SLOT_BASE+APPDATA.bk_ev+ebx], edx + + add edx, APP_OBJ_OFFSET-APP_EV_OFFSET + mov [SLOT_BASE+APPDATA.fd_obj+ebx], edx + mov [SLOT_BASE+APPDATA.bk_obj+ebx], edx + + mov ecx, [def_cursor] + mov [SLOT_BASE+APPDATA.cursor+ebx], ecx + mov eax, [pl0_stack] + mov [SLOT_BASE+APPDATA.pl0_stack+ebx], eax + add eax, RING0_STACK_SIZE + mov [SLOT_BASE+APPDATA.saved_esp0+ebx], eax + + push ebx + stdcall kernel_alloc, maxPathLength + pop ebx + mov esi, [current_slot] + mov esi, [esi+APPDATA.cur_dir] + mov ecx, maxPathLength/4 + mov edi, eax + mov [ebx+SLOT_BASE+APPDATA.cur_dir], eax + rep movsd + + shr ebx, 3 + mov dword [TASK_TABLE+ebx+TASKDATA.mem_start], 0 + + mov ebx, [slot] + mov eax, ebx + shl ebx, 5 + lea ecx, [draw_data+ebx];ecx - pointer to draw data + +; set window state to 'normal' (non-minimized/maximized/rolled-up) state + mov [ebx+window_data+WDATA.fl_wstate], WSTATE_NORMAL + mov [ebx+window_data+WDATA.fl_redraw], 1 + add ebx, TASK_TABLE ;ebx - pointer to information about process + mov [ebx+TASKDATA.wnd_number], al;set window number on screen = process slot + + mov [ebx+TASKDATA.event_mask], dword 1+2+4;set default event flags (see 40 function) + + inc dword [process_number] + mov eax, [process_number] + mov [ebx+TASKDATA.pid], eax ;set PID + +;set draw data to full screen + xor eax, eax + mov [ecx+0], dword eax + mov [ecx+4], dword eax + mov eax, [screen_workarea.right] + mov [ecx+8], eax + mov eax, [screen_workarea.bottom] + mov [ecx+12], eax + + mov ebx, [pl0_stack] + mov esi, [params] + lea ecx, [ebx+REG_EIP] + xor eax, eax + + mov [ebx+REG_RET], dword common_app_entry + mov [ebx+REG_EDI], eax + mov [ebx+REG_ESI], eax + mov [ebx+REG_EBP], eax + mov [ebx+REG_ESP], ecx;ebx+REG_EIP + mov [ebx+REG_EBX], eax + mov [ebx+REG_EDX], eax + mov [ebx+REG_ECX], eax + mov [ebx+REG_EAX], eax + + mov eax, [esi+APP_HDR.eip] + mov [ebx+REG_EIP], eax + mov [ebx+REG_CS], dword app_code + mov ecx, USER_PRIORITY + + test byte [flags], 2 + jz @F + + mov [ebx+REG_CS], dword os_code ; kernel thread + mov ecx, MAX_PRIORITY +@@: + mov [ebx+REG_EFLAGS], dword EFL_IOPL1+EFL_IF + + mov eax, [esi+APP_HDR.esp] + mov [ebx+REG_APP_ESP], eax + mov [ebx+REG_SS], dword app_data + + lea edx, [ebx+REG_RET] + mov ebx, [slot] + shl ebx, 5 + mov [ebx*8+SLOT_BASE+APPDATA.saved_esp], edx + + xor edx, edx; process state - running +; set if debuggee + test byte [flags], 1 + jz .no_debug + mov eax, [current_slot_idx] + mov [SLOT_BASE+ebx*8+APPDATA.debugger_slot], eax +.no_debug: + mov [TASK_TABLE+ebx+TASKDATA.state], dl + lea edx, [SLOT_BASE+ebx*8] + call scheduler_add_thread + ret +endp + +align 4 +get_stack_base: + mov eax, [current_slot] + mov eax, [eax+APPDATA.pl0_stack] + ret + + +include "debug.inc" diff --git a/kernel/branches/kolibri-lldw/core/test_malloc.asm b/kernel/branches/kolibri-lldw/core/test_malloc.asm new file mode 100644 index 000000000..ac42df8c7 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/test_malloc.asm @@ -0,0 +1,250 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2009-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; Tests of malloc()/free() from the kernel heap. +; This file is not included in the kernel, it is just test application. +use32 + db 'MENUET01' + dd 1, start, i_end, mem, mem, 0, 0 + +start: +; Zero-initialize uglobals (as in kernel at boot) + mov ecx, (zeroend - zerostart + 3) / 4 + xor eax, eax + mov edi, zerostart + rep stosd +; Initialize small heap (as in kernel at boot) + call init_malloc +; Run tests + call run_test1 + call run_test2 + call run_test3 +; All is OK, return + or eax, -1 + int 0x40 + +run_test1: +; basic test + mov eax, 1 + call malloc_with_test + mov byte [eax], 0xDD + mov esi, eax + mov eax, 1 + call malloc_with_test + cmp byte [esi], 0xDD + jnz memory_destroyed + mov byte [eax], 0xEE + xchg eax, esi + call free + cmp byte [esi], 0xEE + jnz memory_destroyed + xchg eax, esi + call free + ret + +run_test2: + ret + +run_test3: +; 1024 times run random operation. +; Randomly select malloc(random size from 1 to 1023) +; or free(random of previously allocated areas) + mov edi, 0x12345678 + xor esi, esi ; 0 areas allocated + mov ebx, 1024 +.loop: + imul edi, 1103515245 + add edi, 12345 + mov eax, edi + shr eax, 16 + test ebx, 64 + jz .prefer_free +.prefer_malloc: + test eax, 3 + jz .free + jmp @f +.prefer_free: + test eax, 3 + jnz .free +@@: + shr eax, 2 + and eax, 1023 + jz .loop + push ebx + push eax +; mov ecx, [saved_state_num] +; mov [saved_state+ecx*8], eax + push edi + call malloc_with_test + pop ecx + cmp ecx, edi + jnz edi_destroyed +; mov ecx, [saved_state_num] +; mov [saved_state+ecx*8+4], eax +; inc [saved_state_num] + pop ecx + pop ebx + inc esi + push ecx eax + push edi + mov edi, eax + mov eax, esi + rep stosb + pop edi + jmp .common +.free: + test esi, esi + jz .loop + xor edx, edx + div esi + sub edx, esi + neg edx + dec edx + mov eax, [esp+edx*8] +; mov ecx, [saved_state_num] +; mov [saved_state+ecx*8], -1 +; mov [saved_state+ecx*8+4], eax +; inc [saved_state_num] + mov ecx, [esp+edx*8+4] + push edi eax + mov edi, eax + mov al, [edi] + repz scasb + jnz memory_destroyed + pop eax edi + push ebx edx + push edi + call free + pop ecx + cmp ecx, edi + jnz edi_destroyed + pop edx ebx + dec esi + pop eax ecx + push edi + lea edi, [esp+4] +@@: + dec edx + js @f + xchg eax, [edi] + xchg ecx, [edi+4] + add edi, 8 + jmp @b +@@: + pop edi +.common: + dec ebx + jnz .loop +@@: + dec esi + js @f + pop eax ecx + call free + jmp @b +@@: + ret + +malloc_with_test: +; calls malloc() and checks returned value + call malloc + test eax, eax + jz generic_malloc_fail + call check_mutex + call check_range + ret + +; Stubs for kernel procedures used by heap code +mutex_init: + and dword [ecx], 0 + ret +mutex_lock: + inc dword [ecx] + ret +mutex_unlock: + dec dword [ecx] + ret + +kernel_alloc: + cmp dword [esp+4], bufsize + jnz error1 + mov eax, buffer + ret 4 + +macro $Revision [args] +{ +} + +; Error handlers +error1: + mov eax, 1 + jmp error_with_code + +generic_malloc_fail: + mov eax, 2 + jmp error_with_code + +check_mutex: + cmp dword [mst.mutex], 0 + jnz @f + ret +@@: + mov eax, 3 + jmp error_with_code + +check_range: + cmp eax, buffer + jb @f + cmp eax, buffer+bufsize + jae @f + ret +@@: + mov eax, 4 + jmp error_with_code + +memory_destroyed: + mov eax, 5 + jmp error_with_code + +edi_destroyed: + mov eax, 6 + jmp error_with_code + +error_with_code: + mov edx, saved_state_num +; eax = error code +; 1 signals error in testing code (wrong bufsize) +; 2 = malloc() returned NULL +; 3 = mutex not released +; 4 = weird returned value from malloc() +; 5 = memory destroyed by malloc() or free() + int3 ; simplest way to report error + jmp $-1 ; just in case + +; Include main heap code +include '../macros.inc' +include '../proc32.inc' +include '../struct.inc' +include '../const.inc' +include 'malloc.inc' + +i_end: + +align 4 +zerostart: +mst MEM_STATE + +align 16 +bufsize = 0x40000 ; change if malloc.inc changes +buffer rb bufsize +zeroend: + +saved_state_num dd ? +saved_state rd 0x10000 + +align 4 + rb 0x10000 ; for stack +mem: diff --git a/kernel/branches/kolibri-lldw/core/timers.inc b/kernel/branches/kolibri-lldw/core/timers.inc new file mode 100644 index 000000000..91e1e7ed4 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/timers.inc @@ -0,0 +1,229 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2012-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Simple implementation of timers. All timers are organized in a double-linked +; list, and the OS loop after every timer tick processes the list. + +; This structure describes a timer for the kernel. +struct TIMER + Next dd ? + Prev dd ? +; These fields organize a double-linked list of all timers. + TimerFunc dd ? +; Function to be called when the timer is activated. + UserData dd ? +; The value that is passed as is to .TimerFunc. + Time dd ? +; Time at which the timer should be activated. + Interval dd ? +; Interval between activations of the timer, in 0.01s. +ends + +iglobal +align 4 +; The head of timer list. +timer_list: + dd timer_list + dd timer_list +endg +uglobal +; These two variables are used to synchronize access to the global list. +; Logically, they form an recursive mutex. Physically, the first variable holds +; the slot number of the current owner or 0, the second variable holds the +; recursion count. +; The mutex should be recursive to allow a timer function to add/delete other +; timers or itself. +timer_list_owner dd 0 +timer_list_numlocks dd 0 +; A timer function can delete any timer, including itself and the next timer in +; the chain. To handle such situation correctly, we keep the next timer in a +; global variable, so the removing operation can update it. +timer_next dd 0 +endg + +; This internal function acquires the lock for the global list. +lock_timer_list: + mov edx, [current_slot_idx] +@@: + xor eax, eax + lock cmpxchg [timer_list_owner], edx + jz @f + cmp eax, edx + jz @f + call change_task + jmp @b +@@: + inc [timer_list_numlocks] + ret + +; This internal function releases the lock for the global list. +unlock_timer_list: + dec [timer_list_numlocks] + jnz .nothing + mov [timer_list_owner], 0 +.nothing: + ret + +; This function adds a timer. +; If deltaStart is nonzero, the timer is activated after deltaStart hundredths +; of seconds starting from the current time. If interval is nonzero, the timer +; is activated every deltaWork hundredths of seconds starting from the first +; activation. The activated timer calls timerFunc as stdcall function with one +; argument userData. +; Return value is NULL if something has failed or some value which is opaque +; for the caller. Later this value can be used for cancel_timer_hs. +proc timer_hs stdcall uses ebx, deltaStart:dword, interval:dword, \ + timerFunc:dword, userData:dword +; 1. Allocate memory for the TIMER structure. +; 1a. Call the allocator. + movi eax, sizeof.TIMER + call malloc +; 1b. If allocation failed, return (go to 5) with eax = 0. + test eax, eax + jz .nothing +; 2. Setup the TIMER structure. + xchg ebx, eax +; 2a. Copy values from the arguments. + mov ecx, [interval] + mov [ebx+TIMER.Interval], ecx + mov ecx, [timerFunc] + mov [ebx+TIMER.TimerFunc], ecx + mov ecx, [userData] + mov [ebx+TIMER.UserData], ecx +; 2b. Get time of the next activation. + mov ecx, [deltaStart] + test ecx, ecx + jnz @f + mov ecx, [interval] +@@: + add ecx, [timer_ticks] + mov [ebx+TIMER.Time], ecx +; 3. Insert the TIMER structure to the global list. +; 3a. Acquire the lock. + call lock_timer_list +; 3b. Insert an item at ebx to the tail of the timer_list. + mov eax, timer_list + mov ecx, [eax+TIMER.Prev] + mov [ebx+TIMER.Next], eax + mov [ebx+TIMER.Prev], ecx + mov [eax+TIMER.Prev], ebx + mov [ecx+TIMER.Next], ebx +; 3c. Release the lock. + call unlock_timer_list +; 4. Return with eax = pointer to TIMER structure. + xchg ebx, eax +.nothing: +; 5. Returning. + ret +endp + +; This function removes a timer. +; The only argument is [esp+4] = the value which was returned from timer_hs. +cancel_timer_hs: + push ebx ; save used register to be stdcall +; 1. Remove the TIMER structure from the global list. +; 1a. Acquire the lock. + call lock_timer_list + mov ebx, [esp+4+4] +; 1b. Delete an item at ebx from the double-linked list. + mov eax, [ebx+TIMER.Next] + mov ecx, [ebx+TIMER.Prev] + mov [eax+TIMER.Prev], ecx + mov [ecx+TIMER.Next], eax +; 1c. If we are removing the next timer in currently processing chain, +; the next timer for this timer becomes new next timer. + cmp ebx, [timer_next] + jnz @f + mov [timer_next], eax +@@: +; 1d. Release the lock. + call unlock_timer_list +; 2. Free the TIMER structure. + xchg eax, ebx + call free +; 3. Return. + pop ebx ; restore used register to be stdcall + ret 4 ; purge one dword argument to be stdcall + +; This function is regularly called from osloop. It processes the global list +; and activates the corresponding timers. +check_timers: +; 1. Acquire the lock. + call lock_timer_list +; 2. Loop over all registered timers, checking time. +; 2a. Get the first item. + mov eax, [timer_list+TIMER.Next] + mov [timer_next], eax +.loop: +; 2b. Check for end of list. + cmp eax, timer_list + jz .done +; 2c. Get and store the next timer. + mov edx, [eax+TIMER.Next] + mov [timer_next], edx +; 2d. Check time for timer activation. +; We can't just compare [timer_ticks] and [TIMER.Time], since overflows are +; possible: if the current time is 0FFFFFFFFh ticks and timer should be +; activated in 3 ticks, the simple comparison will produce incorrect result. +; So we calculate the difference [timer_ticks] - [TIMER.Time]; if it is +; non-negative, the time is over; if it is negative, then either the time is +; not over or we have not processed this timer for 2^31 ticks, what is very +; unlikely. + mov edx, [timer_ticks] + sub edx, [eax+TIMER.Time] + js .next +; The timer should be activated now. +; 2e. Store the timer data in the stack. This is required since 2f can delete +; the timer, invalidating the content. + push [eax+TIMER.UserData] ; parameter for TimerFunc + push [eax+TIMER.TimerFunc] ; to be restored in 2g +; 2f. Calculate time of next activation or delete the timer if it is one-shot. + mov ecx, [eax+TIMER.Interval] + add [eax+TIMER.Time], ecx + test ecx, ecx + jnz .nodelete + stdcall cancel_timer_hs, eax +.nodelete: +; 2g. Activate timer, using data from the stack. + pop eax + call eax +.next: +; 2h. Advance to the next timer and continue the loop. + mov eax, [timer_next] + jmp .loop +.done: +; 3. Release the lock. + call unlock_timer_list +; 4. Return. + ret + +; This is a simplified version of check_timers that does not call anything, +; just checks whether check_timers should do something. +proc check_timers_has_work? + pushf + cli + mov eax, [timer_list+TIMER.Next] +.loop: + cmp eax, timer_list + jz .done_nowork + mov edx, [timer_ticks] + sub edx, [eax+TIMER.Time] + jns .done_haswork + mov eax, [eax+TIMER.Next] + jmp .loop +.done_nowork: + popf + xor eax, eax + ret +.done_haswork: + popf + xor eax, eax + inc eax + ret +endp diff --git a/kernel/branches/kolibri-lldw/core/v86.inc b/kernel/branches/kolibri-lldw/core/v86.inc new file mode 100644 index 000000000..50da4cdd0 --- /dev/null +++ b/kernel/branches/kolibri-lldw/core/v86.inc @@ -0,0 +1,907 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2007-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; Virtual-8086 mode manager +; diamond, 2007, 2008 + +DEBUG_SHOW_IO = 0 + +struct V86_machine +; page directory + process dd ? +; mutex to protect all data from writing by multiple threads at one time + mutex dd ? +; i/o permission map + iopm dd ? +ends + +; Create V86 machine +; in: nothing +; out: eax = handle (pointer to struc V86_machine) +; eax = NULL => failure +; destroys: ebx, ecx, edx (due to malloc) +v86_create: +; allocate V86_machine structure + mov eax, sizeof.V86_machine + call malloc + test eax, eax + jz .fail +; initialize mutex + and dword [eax+V86_machine.mutex], 0 +; allocate tables + mov ebx, eax + + stdcall create_process, 4096 + test eax, eax + jz .fail2 + + mov [eax+PROC.mem_used], 4096 + mov [ebx+V86_machine.process], eax + + push 2000h + call kernel_alloc + test eax, eax + jz .fail2 + + mov [ebx+V86_machine.iopm], eax + +; initialize tables + push edi + mov edi, eax + mov eax, -1 + mov ecx, 2000h/4 + rep stosd + + mov eax, [ebx+V86_machine.process] + mov eax, [eax+PROC.pdt_0_phys] + + pushfd + cli + mov cr3, eax + +; now V86 specific: initialize known addresses in first Mb + +; first page - BIOS data (shared between all machines!) +; physical address = 0 +; linear address = OS_BASE +; page before 0xA0000 - Extended BIOS Data Area (shared between all machines!) +; physical address = 0x9C000 +; linear address = 0x8009C000 +; (I have seen one computer with EBDA segment = 0x9D80, +; all other computers use less memory) + + mov eax, PG_UWR + mov [page_tabs], eax + invlpg [eax] + + mov byte [0x500], 0xCD + mov byte [0x501], 0x13 + mov byte [0x502], 0xF4 + mov byte [0x503], 0xCD + mov byte [0x504], 0x10 + mov byte [0x505], 0xF4 + + mov eax, 0x98000+PG_UWR + mov edi, page_tabs+0x98*4 + mov edx, 0x1000 + mov ecx, 8 +@@: + stosd + add eax, edx + loop @b + +; addresses 0xC0000 - 0xFFFFF - BIOS code (shared between all machines!) +; physical address = 0xC0000 + + mov eax, 0xC0000+PG_UWR + mov edi, page_tabs+0xC0*4 + mov ecx, 64 +@@: + stosd + add eax, edx + loop @b + + mov eax, [sys_proc+PROC.pdt_0_phys] + mov cr3, eax + popfd + + pop edi + + mov eax, ebx + ret +.fail2: + mov eax, ebx + call free +.fail: + xor eax, eax + ret + +;not used +; Destroy V86 machine +; in: eax = handle +; out: nothing +; destroys: eax, ebx, ecx, edx (due to free) +;v86_destroy: +; push eax +; stdcall kernel_free, [eax+V86_machine.pagedir] +; pop eax +; jmp free + +; Translate V86-address to linear address +; in: eax=V86 address +; esi=handle +; out: eax=linear address +; destroys: nothing +v86_get_lin_addr: + push ecx edx + mov ecx, eax + shr ecx, 12 + mov edx, [page_tabs+ecx*4] + and eax, 0xFFF + and edx, 0xFFFFF000 + or eax, edx + pop edx ecx + ret + +;not used +; Sets linear address for V86-page +; in: eax=linear address (must be page-aligned) +; ecx=V86 page (NOT address!) +; esi=handle +; out: nothing +; destroys: nothing +;v86_set_page: +; push eax ebx +; mov ebx, [esi+V86_machine.pagedir] +; mov [ebx+ecx*4+0x1800], eax +; call get_pg_addr +; or al, 111b +; mov [ebx+ecx*4+0x1000], eax +; pop ebx eax +; ret + +; Allocate memory in V86 machine +; in: eax=size (in bytes) +; esi=handle +; out: eax=V86 address, para-aligned (0x10 multiple) +; destroys: nothing +; недописана!!! +;v86_alloc: +; push ebx ecx edx edi +; lea ebx, [esi+V86_machine.mutex] +; call wait_mutex +; add eax, 0x1F +; shr eax, 4 +; mov ebx, 0x1000 ; start with address 0x1000 (second page) +; mov edi, [esi+V86_machine.tables] +;.l: +; mov ecx, ebx +; shr ecx, 12 +; mov edx, [edi+0x1000+ecx*4] ; get linear address +; test edx, edx ; page allocated? +; jz .unalloc +; mov ecx, ebx +; and ecx, 0xFFF +; add edx, ecx +; cmp dword [edx], 0 ; free block? +; jnz .n +; cmp dword [edx+4], +; and [esi+V86_machine.mutex], 0 +; pop edi edx ecx ebx +; ret + +uglobal +sys_v86_machine dd ? +endg + +; Called from kernel.asm at first stages of loading +; Initialize system V86 machine (used to simulate BIOS int 13h) +init_sys_v86: + call v86_create + mov [sys_v86_machine], eax + test eax, eax + jz .ret + mov esi, eax +if ~DEBUG_SHOW_IO +; allow access to all ports + mov ecx, [esi+V86_machine.iopm] + xor eax, eax + mov edi, ecx + mov ecx, 10000h/8/4 + rep stosd +end if +.ret: + ret + +struct v86_regs +; don't change the order, it is important + edi dd ? + esi dd ? + ebp dd ? + dd ? ; ignored + ebx dd ? + edx dd ? + ecx dd ? + eax dd ? + eip dd ? + cs dd ? + eflags dd ? ; VM flag must be set! + esp dd ? + ss dd ? + es dd ? + ds dd ? + fs dd ? + gs dd ? +ends + +; Run V86 machine +; in: ebx -> registers for V86 (two structures: in and out) +; esi = handle +; ecx = expected end address (CS:IP) +; edx = IRQ to hook or -1 if not required +; out: structure pointed to by ebx is filled with new values +; eax = 1 - exception has occured, cl contains code +; eax = 2 - access to disabled i/o port, ecx contains port address +; eax = 3 - IRQ is already hooked by another VM +; destroys: nothing +v86_start: + + pushad + + cli + + mov ecx, [current_slot] + + push dword [ecx+APPDATA.io_map] + push dword [ecx+APPDATA.io_map+4] + push [ecx+APPDATA.process] + push [ecx+APPDATA.saved_esp0] + mov [ecx+APPDATA.saved_esp0], esp + mov [tss._esp0], esp + + mov eax, [esi+V86_machine.iopm] + call get_pg_addr + inc eax + mov dword [ecx+APPDATA.io_map], eax + mov dword [page_tabs + (tss._io_map_0 shr 10)], eax + + mov eax, [esi+V86_machine.iopm] + add eax, 0x1000 + call get_pg_addr + inc eax + mov dword [ecx+APPDATA.io_map+4], eax + mov dword [page_tabs + (tss._io_map_1 shr 10)], eax + + mov eax, [esi+V86_machine.process] + mov [ecx+APPDATA.process], eax + mov [current_process], eax + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax + +; We do not enable interrupts, because V86 IRQ redirector assumes that +; machine is running +; They will be enabled by IRET. +; sti + + mov eax, esi + sub esp, sizeof.v86_regs + mov esi, ebx + mov edi, esp + mov ecx, sizeof.v86_regs/4 + rep movsd + + cmp edx, -1 + jz .noirqhook +uglobal +v86_irqhooks rd IRQ_RESERVED * 2 +endg + cmp [v86_irqhooks+edx*8], 0 + jz @f + cmp [v86_irqhooks+edx*8], eax + jz @f + mov esi, v86_irqerr + call sys_msg_board_str + inc [v86_irqhooks+edx*8+4] + mov eax, 3 + jmp v86_exc_c.exit +@@: + mov [v86_irqhooks+edx*8], eax + inc [v86_irqhooks+edx*8+4] +.noirqhook: + + popad + iretd + +; It is only possible to leave virtual-8086 mode by faulting to +; a protected-mode interrupt handler (typically the general-protection +; exception handler, which in turn calls the virtual 8086-mode monitor). + +iglobal + v86_exc_str1 db 'V86 : unexpected exception ',0 + v86_exc_str2 db ' at ',0 + v86_exc_str3 db ':',0 + v86_exc_str4 db 13,10,'V86 : faulted code:',0 + v86_exc_str5 db ' (unavailable)',0 + v86_newline db 13,10,0 + v86_io_str1 db 'V86 : access to disabled i/o port ',0 + v86_io_byte db ' (byte)',13,10,0 + v86_io_word db ' (word)',13,10,0 + v86_io_dword db ' (dword)',13,10,0 + v86_irqerr db 'V86 : IRQ already hooked',13,10,0 +endg + +v86_exc_c: +; Did we all that we have wanted to do? + cmp bl, 1 + jne @f + xor eax, eax + mov dr6, eax + @@: + mov eax, [esp+sizeof.v86_regs+10h+18h] + cmp word [esp+v86_regs.eip], ax + jnz @f + shr eax, 16 + cmp word [esp+v86_regs.cs], ax + jz .done +@@: +; Various system events, which must be handled, result in #GP + cmp bl, 13 + jnz .nogp +; If faulted EIP exceeds 0xFFFF, we have #GP and it is an error + cmp word [esp+v86_regs.eip+2], 0 + jnz .nogp +; Otherwise we can safely access byte at CS:IP +; (because it is #GP, not #PF handler) +; If we could get an exception just because of reading code bytes, +; we would have got it already and it wouldn't be #GP + movzx esi, word [esp+v86_regs.cs] + shl esi, 4 + add esi, [esp+v86_regs.eip] + lodsb + cmp al, 0xCD ; int xx command = CD xx + jz .handle_int + cmp al, 0xCF + jz .handle_iret + cmp al, 0xF3 + jz .handle_rep + cmp al, 0xEC + jz .handle_in + cmp al, 0xED + jz .handle_in_word + cmp al, 0xEE + jz .handle_out + cmp al, 0xEF + jz .handle_out_word + cmp al, 0xE4 + jz .handle_in_imm + cmp al, 0xE6 + jz .handle_out_imm + cmp al, 0x9C + jz .handle_pushf + cmp al, 0x9D + jz .handle_popf + cmp al, 0xFA + jz .handle_cli + cmp al, 0xFB + jz .handle_sti + cmp al, 0x66 + jz .handle_66 + jmp .nogp +.handle_int: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + xor eax, eax + lodsb +; call sys_msg_board_byte +; simulate INT command +; N.B. It is possible that some checks need to be corrected, +; but at least in case of normal execution the code works. +.simulate_int: + cmp word [esp+v86_regs.esp], 6 + jae @f + mov bl, 12 ; #SS exception + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + push eax + movzx eax, word [esp+4+v86_regs.esp] + sub eax, 6 + add edx, eax + mov eax, edx + mov esi, [esp+4+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + sub word [esp+4+v86_regs.esp], 6 + mov eax, [esp+4+v86_regs.eip] + cmp byte [esp+1], 0 + jnz @f + inc eax + inc eax +@@: + mov word [edx], ax + mov eax, [esp+4+v86_regs.cs] + mov word [edx+2], ax + mov eax, [esp+4+v86_regs.eflags] + mov word [edx+4], ax + pop eax + mov ah, 0 + mov cx, [eax*4] + mov word [esp+v86_regs.eip], cx + mov cx, [eax*4+2] + mov word [esp+v86_regs.cs], cx +; note that interrupts will be disabled globally at IRET + and byte [esp+v86_regs.eflags+1], not 3 ; clear IF and TF flags +; continue V86 execution + popad + iretd +.handle_iret: + cmp word [esp+v86_regs.esp], 0x10000 - 6 + jbe @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov ax, [edx] + mov word [esp+v86_regs.eip], ax + mov ax, [edx+2] + mov word [esp+v86_regs.cs], ax + mov ax, [edx+4] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 6 + popad + iretd +.handle_pushf: + cmp word [esp+v86_regs.esp], 1 + jnz @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + mov eax, [esp+v86_regs.esp] + sub eax, 2 + movzx eax, ax + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+1] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + sub word [esp+v86_regs.esp], 2 + mov eax, [esp+v86_regs.eflags] + mov [edx], ax + inc word [esp+v86_regs.eip] + popad + iretd +.handle_pushfd: + cmp word [esp+v86_regs.esp], 4 + jae @f + mov bl, 12 ; #SS exception + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + sub eax, 4 + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+3] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + sub word [esp+v86_regs.esp], 4 + movzx eax, word [esp+v86_regs.eflags] + mov [edx], eax + add word [esp+v86_regs.eip], 2 + popad + iretd +.handle_popf: + cmp word [esp+v86_regs.esp], 0xFFFF + jnz @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 ; #PF exception + jmp .nogp +@@: + lea eax, [edx+1] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov ax, [edx] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_popfd: + cmp word [esp+v86_regs.esp], 0x10000 - 4 + jbe @f + mov bl, 12 + jmp .nogp +@@: + movzx edx, word [esp+v86_regs.ss] + shl edx, 4 + movzx eax, word [esp+v86_regs.esp] + add edx, eax + mov eax, edx + mov esi, [esp+sizeof.v86_regs+10h+4] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + lea eax, [edx+3] + call v86_get_lin_addr + cmp eax, 0x1000 + jae @f + mov bl, 14 + jmp .nogp +@@: + mov eax, [edx] + mov word [esp+v86_regs.eflags], ax + add word [esp+v86_regs.esp], 4 + add word [esp+v86_regs.eip], 2 + popad + iretd +.handle_cli: + and byte [esp+v86_regs.eflags+1], not 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_sti: + or byte [esp+v86_regs.eflags+1], 2 + inc word [esp+v86_regs.eip] + popad + iretd +.handle_rep: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + cmp al, 6Eh + jz .handle_rep_outsb + jmp .nogp +.handle_rep_outsb: +.handle_in: +.handle_out: +.invalid_io_byte: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 1 + jmp .invalid_io +.handle_in_imm: +.handle_out_imm: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + movzx ebx, al + mov ecx, 1 + jmp .invalid_io +.handle_66: + cmp word [esp+v86_regs.eip], 0xFFFF + jae .nogp + lodsb + cmp al, 0x9C + jz .handle_pushfd + cmp al, 0x9D + jz .handle_popfd + cmp al, 0xEF + jz .handle_out_dword + cmp al, 0xED + jz .handle_in_dword + jmp .nogp +.handle_in_word: +.handle_out_word: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 2 + jmp .invalid_io +.handle_in_dword: +.handle_out_dword: +.invalid_io_dword: + movzx ebx, word [esp+v86_regs.edx] + mov ecx, 4 +.invalid_io: + mov esi, v86_io_str1 + call sys_msg_board_str + mov eax, ebx + call sys_msg_board_dword + mov esi, v86_io_byte + cmp ecx, 1 + jz @f + mov esi, v86_io_word + cmp ecx, 2 + jz @f + mov esi, v86_io_dword +@@: + call sys_msg_board_str +if DEBUG_SHOW_IO + mov edx, ebx + mov ebx, 200 + call delay_hs + mov esi, [esp+v86_regs.size+10h+4] + mov eax, [esi+V86_machine.iopm] +@@: + btr [eax], edx + inc edx + loop @b + popad + iretd +else + mov eax, 2 + jmp .exit +end if +.nogp: + + mov esi, v86_exc_str1 + call sys_msg_board_str + mov al, bl + call sys_msg_board_byte + mov esi, v86_exc_str2 + call sys_msg_board_str + mov ax, [esp+32+4] + call sys_msg_board_word + mov esi, v86_exc_str3 + call sys_msg_board_str + mov ax, [esp+32] + call sys_msg_board_word + mov esi, v86_exc_str4 + call sys_msg_board_str + mov ecx, 8 + movzx edx, word [esp+32+4] + shl edx, 4 + add edx, [esp+32] +@@: + mov esi, [esp+sizeof.v86_regs+10h+4] + mov eax, edx + call v86_get_lin_addr + cmp eax, 0x1000 + jb .nopage + mov esi, v86_exc_str3-2 + call sys_msg_board_str + mov al, [edx] + call sys_msg_board_byte + inc edx + loop @b + jmp @f +.nopage: + mov esi, v86_exc_str5 + call sys_msg_board_str +@@: + mov esi, v86_newline + call sys_msg_board_str + mov eax, 1 + jmp .exit + +.done: + xor eax, eax + +.exit: + mov [esp+sizeof.v86_regs+10h+1Ch], eax + mov [esp+sizeof.v86_regs+10h+18h], ebx + + mov edx, [esp+sizeof.v86_regs+10h+14h] + cmp edx, -1 + jz @f + dec [v86_irqhooks+edx*8+4] + jnz @f + and [v86_irqhooks+edx*8], 0 +@@: + + mov esi, esp + mov edi, [esi+sizeof.v86_regs+10h+10h] + add edi, sizeof.v86_regs + mov ecx, sizeof.v86_regs/4 + rep movsd + mov esp, esi + + cli + mov ecx, [current_slot] + pop eax + + mov [ecx+APPDATA.saved_esp0], eax + mov [tss._esp0], eax + pop eax + mov [ecx+APPDATA.process], eax + mov [current_process], eax + pop ebx + mov dword [ecx+APPDATA.io_map+4], ebx + mov dword [page_tabs + (tss._io_map_1 shr 10)], ebx + pop ebx + mov dword [ecx+APPDATA.io_map], ebx + mov dword [page_tabs + (tss._io_map_0 shr 10)], ebx + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax + sti + + popad + ret + +;my05: +; mov dx, 30C2h +; mov cx, 4 +;.0: +; in al, dx +; cmp al, 0FFh +; jz @f +; test al, 4 +; jnz .1 +;@@: +; add dx, 8 +; in al, dx +; cmp al, 0FFh +; jz @f +; test al, 4 +; jnz .1 +;@@: +; loop .0 +; ret +;.1: +; or al, 84h +; out dx, al +;.2: +; mov dx, 30F7h +; in al, dx +; mov byte [BOOT_VAR + 48Eh], 0FFh +; ret + +align 4 +v86_irq: +; push irq/pushad/jmp v86_irq +; ebp = irq + lea esi, [esp+1Ch] + lea edi, [esi+4] + mov ecx, 8 + std + rep movsd + cld + mov edi, ebp + pop eax +v86_irq2: + mov esi, [v86_irqhooks+edi*8] ; get VM handle + mov eax, [esi+V86_machine.process] + mov ecx, [current_slot_idx] + shl ecx, 8 + cmp [SLOT_BASE+ecx+APPDATA.process], eax + jnz .notcurrent + lea eax, [edi+8] + cmp al, 10h + mov ah, 1 + jb @f + add al, 60h +@@: + jmp v86_exc_c.simulate_int +.notcurrent: + mov ebx, SLOT_BASE + 0x100 + mov ecx, [thread_count] +.scan: + cmp [ebx+APPDATA.process], eax + jnz .cont + push ecx + mov ecx, [ebx+APPDATA.saved_esp0] + cmp word [ecx-sizeof.v86_regs+v86_regs.esp], 6 + jb .cont2 + movzx edx, word [ecx-sizeof.v86_regs+v86_regs.ss] + shl edx, 4 + push eax + movzx eax, word [ecx-sizeof.v86_regs+v86_regs.esp] + sub eax, 6 + add edx, eax + mov eax, edx + call v86_get_lin_addr + cmp eax, 0x1000 + jb .cont3 + lea eax, [edx+5] + call v86_get_lin_addr + cmp eax, 0x1000 + jb .cont3 + pop eax + pop ecx + jmp .found +.cont3: + pop eax +.cont2: + pop ecx +.cont: + add ebx, 0x100 + loop .scan + mov ecx, edi + call irq_eoi + popad + iretd +.found: + mov eax, [eax+PROC.pdt_0_phys] + mov cr3, eax + mov esi, [ebx+APPDATA.saved_esp0] + sub word [esi-sizeof.v86_regs+v86_regs.esp], 6 + mov ecx, [esi-sizeof.v86_regs+v86_regs.eip] + mov word [edx], cx + mov ecx, [esi-sizeof.v86_regs+v86_regs.cs] + mov word [edx+2], cx + mov ecx, [esi-sizeof.v86_regs+v86_regs.eflags] + mov word [edx+4], cx + lea eax, [edi+8] + cmp al, 10h + jb @f + add al, 60h +@@: + mov cx, [eax*4] + mov word [esi-sizeof.v86_regs+v86_regs.eip], cx + mov cx, [eax*4+2] + mov word [esi-sizeof.v86_regs+v86_regs.cs], cx + and byte [esi-sizeof.v86_regs+v86_regs.eflags+1], not 3 + call update_counters + lea edi, [ebx + 0x100000000 - SLOT_BASE] + shr edi, 3 + add edi, TASK_TABLE + call find_next_task.found + call do_change_task + popad + iretd diff --git a/kernel/branches/kolibri-lldw/crc.inc b/kernel/branches/kolibri-lldw/crc.inc new file mode 100644 index 000000000..c84a1594e --- /dev/null +++ b/kernel/branches/kolibri-lldw/crc.inc @@ -0,0 +1,42 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2017. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License. ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; This crc32 routine doesn't use precomputed table to allow different +; polynomials, which is the first param. +; Partial hash in assumed to be eax (both in and out). +; Usage: +; 1. mov eax, -1 +; 2. stdcall crypto.crc32 zero or more times +; 3. xor eax, -1 +proc crc_32 _poly, _buffer, _length + push ebx ecx edx esi + + mov esi, [_buffer] +.next_byte: + dec [_length] + js .done + movzx ebx, byte[esi] + inc esi + mov ecx, 8 +.next_bit: + mov edx, eax + xor edx, ebx + shr eax, 1 + test edx, 1 + jz @f + xor eax, [_poly] +@@: + shr ebx, 1 + dec ecx + jnz .next_bit + jmp .next_byte +.done: + pop esi edx ecx ebx + ret +endp diff --git a/kernel/branches/kolibri-lldw/data16.inc b/kernel/branches/kolibri-lldw/data16.inc new file mode 100644 index 000000000..79fbb2698 --- /dev/null +++ b/kernel/branches/kolibri-lldw/data16.inc @@ -0,0 +1,87 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +flm db 0 +preboot_lfb db 0 +preboot_bootlog db 0 +boot_drive db 0 + +align 4 +old_ints_h: + dw 0x400 + dd 0 + dw 0 + +if ~ defined extended_primary_loader ; restart from memory is not supported in extended primary loader cfg +kernel_restart_bootblock: + db 1 ; version + dw 1 ; floppy image is in memory + dd 0 ; cannot save parameters +end if + +; table for move to extended memory (int 15h, ah=87h) +align 8 +movedesc: + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + + db 0xff,0xff,0x0,0xa0,0x00,0x93,0x0,0x0 + db 0xff,0xff,0x0,0x00,0x10,0x93,0x0,0x0 + + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + +fwmovedesc: + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + + db 0xff,0xff,0x0,0x00,0x10,0x93,0x0,0x0 + db 0xff,0xff,0x0,0xa0,0x00,0x93,0x0,0x0 + + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + db 0x00,0x00,0x0,0x00,0x00,0x00,0x0,0x0 + +if defined extended_primary_loader +; look in PrimaryLoader.txt for the description +bootdevice dw 0 ; ax from primary loader +bootfs dw 0 ; bx from primary loader +bootcallback dd 0 ; ds:si from primary loader +; data for configuration file loading, look in PrimaryLoader.txt +config_file_struct: + dw 0, 4000h ; load to 4000:0000 + dw 16 ; read no more than 16*4K = 64K + db 'config.ini',0 +; data for configuration file parsing +macro config_variable string,parser +{ +local len +len dw 0 + db string +store word $ - len - 2 at len + dw parser +} +config_file_variables: + config_variable 'timeout', parse_timeout + config_variable 'resolution', parse_resolution + config_variable 'vbemode', parse_vbemode + config_variable 'biosdisks', parse_biosdisks + config_variable 'imgfrom', parse_imgfrom + config_variable 'syspath', parse_syspath + dw 0 +; data for image file loading, look in PrimaryLoader.txt +image_file_struct: + dw 0, 4000h ; load to 4000:0000 + dw 16 ; read no more than 16*4K = 64K + db 'kolibri.img',0 +end if diff --git a/kernel/branches/kolibri-lldw/data32.inc b/kernel/branches/kolibri-lldw/data32.inc new file mode 100644 index 000000000..fa3c514e1 --- /dev/null +++ b/kernel/branches/kolibri-lldw/data32.inc @@ -0,0 +1,559 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2021. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +keymap: + + db '6',27 + db '1234567890-=',8,9 + db 'qwertyuiop[]',13 + db '~asdfghjkl;',39,96,0,'\zxcvbnm,./',0,'45 ' + db '@234567890123',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'AB?',0,'45 ' + db '@234567890123',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'AB>D',255,'FGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + +keymap_alt: + db ' ',27 + db ' @ $ {[]}\ ',8,9 + db ' ',13 + db ' ',0,' ',0,'4',0,' ' + db ' ',180,178,184,'6',176,'7' + db 179,'8',181,177,183,185,182 + db 'ABCD',255,'FGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + db 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' + + + +if lang eq ru + boot_initirq cp866 'Инициализация IRQ',0 + boot_picinit cp866 'Инициализация PIC',0 + boot_v86machine cp866 'Инициализация системной V86 машины',0 + boot_inittimer cp866 'Инициализация системного таймера (IRQ0)',0 + boot_initapic cp866 'Попытка инициализации APIC',0 + boot_enableirq cp866 'Включить прерывания 2, 13',0 + boot_disabling_ide cp866 'Запрещение прерываний в контроллере IDE',0 + boot_enabling_ide cp866 'Разрешение прерываний в контроллере IDE',0 + boot_set_int_IDE cp866 'Установка обработчиков прерываний IDE',0 + boot_detectfloppy cp866 'Поиск floppy дисководов',0 + boot_detecthdcd cp866 'Поиск жестких дисков и ATAPI приводов',0 + boot_getcache cp866 'Получение памяти для кэша',0 + boot_detectpart cp866 'Поиск разделов на дисковых устройствах',0 + boot_init_sys cp866 'Инициализация системного каталога /sys',0 + boot_loadlibs cp866 'Загрузка библиотек (.obj)',0 + boot_memdetect cp866 'Количество оперативной памяти',' ',' Мб',0 + boot_tss cp866 'Установка TSSs',0 + boot_cpuid cp866 'Чтение CPUIDs',0 +; boot_devices cp866 'Поиск устройств',0 + boot_timer cp866 'Установка таймера',0 + boot_initramdisk cp866 'Инициализация рамдиска',0 + boot_irqs cp866 'Переопределение IRQ',0 + boot_setmouse cp866 'Установка мыши',0 + boot_windefs cp866 'Установка настроек окон по умолчанию',0 + boot_bgr cp866 'Установка фона',0 + boot_resirqports cp866 'Резервирование IRQ и портов',0 + boot_setrports cp866 'Установка адресов IRQ',0 + boot_setostask cp866 'Создание процесса ядра',0 + boot_allirqs cp866 'Открытие всех IRQ',0 + boot_tsc cp866 'Чтение TSC',0 + boot_cpufreq cp866 'Частота процессора ',' ',' МГц',0 + boot_pal_ega cp866 'Установка EGA/CGA 320x200 палитры',0 + boot_pal_vga cp866 'Установка VGA 640x480 палитры',0 + boot_failed cp866 'Загрузка первого приложения не удалась',0 + boot_mtrr cp866 'Установка MTRR',0 + + boot_APIC_found cp866 'APIC включен', 0 + boot_APIC_nfound cp866 'APIC не найден', 0 +if preboot_blogesc + boot_tasking cp866 'Все готово для запуска, нажмитре ESC для старта',0 +end if +else if lang eq sp + include 'data32sp.inc' +else if lang eq et + include 'data32et.inc' +else + boot_initirq db 'Initialize IRQ',0 + boot_picinit db 'Initialize PIC',0 + boot_v86machine db 'Initialize system V86 machine',0 + boot_inittimer db 'Initialize system timer (IRQ0)',0 + boot_initramdisk db 'Initialize ramdisk',0 + boot_initapic db 'Try to initialize APIC',0 + boot_enableirq db 'Enable interrupts 2, 13',0 + boot_disabling_ide db 'Disable interrupts in IDE controller',0 + boot_enabling_ide db 'Enable interrupts in IDE controller',0 + boot_set_int_IDE db 'Set handler of interrupts for IDE',0 + boot_detectfloppy db 'Search floppy drives',0 + boot_detecthdcd db 'Search hard drives and ATAPI drives',0 + boot_getcache db 'Get memory for cache',0 + boot_detectpart db 'Search partitions on disk devices',0 + boot_init_sys db 'Initialize system directory /sys',0 + boot_loadlibs db 'Loading librares (.obj)',0 + boot_memdetect db 'Determining amount of memory',0 + boot_tss db 'Setting TSSs',0 + boot_cpuid db 'Reading CPUIDs',0 +; boot_devices db 'Detecting devices',0 + boot_setmouse db 'Setting mouse',0 + boot_windefs db 'Setting window defaults',0 + boot_bgr db 'Calculating background',0 + boot_resirqports db 'Reserving IRQs & ports',0 + boot_setostask db 'Setting OS task',0 + boot_allirqs db 'Unmasking IRQs',0 + boot_tsc db 'Reading TSC',0 + boot_cpufreq db 'CPU frequency is ',' ',' MHz',0 + boot_pal_ega db 'Setting EGA/CGA 320x200 palette',0 + boot_pal_vga db 'Setting VGA 640x480 palette',0 + boot_failed db 'Failed to start first app',0 + boot_mtrr db 'Setting MTRR',0 + + boot_APIC_found db 'APIC enabled', 0 + boot_APIC_nfound db 'APIC not found', 0 +if preboot_blogesc + boot_tasking db 'All set - press ESC to start',0 +end if +end if + +;new_process_loading db 'K : New Process - loading',13,10,0 +;new_process_running db 'K : New Process - done',13,10,0 +start_not_enough_memory db 'K : New Process - not enough memory',13,10,0 + +msg_unresolved db 'unresolved ',0 +;msg_module db 'in module ',0 +;if ~ lang eq sp +;msg_version db 'incompatible driver version',13,10,0 +;msg_www db 'please visit www.kolibrios.org',13,10,0 +;end if +msg_CR db 13,10,0 + +szPS2MDriver db '/sys/drivers/PS2MOUSE.SYS',0 +;szCOM_MDriver db 'COM_MOUSE',0 +szVidintel db '/sys/drivers/vidintel.sys',0 +szUSB db 'USB',0 + +szEXPORTS db 'EXPORTS',0 +sz_EXPORTS db '_EXPORTS',0 + +szIMPORTS db 'IMPORTS',0 + +read_firstapp db '/sys/' +firstapp db '/sys/LAUNCHER',0 +notifyapp db '/sys/@notify',0 +if lang eq ru +ud_user_message cp866 'Ошибка: неподдерживаемая инструкция процессора',0 +mtrr_user_message cp866 '"Обнаружена проблема с конфигурацией MTRR.\nПроизводительность может быть пониженной" -dW',0 +else if ~ lang eq sp +ud_user_message db 'Error: unsupported processor instruction',0 +mtrr_user_message db '"There is a problem with MTRR configuration.\nPerformance can be low" -dW',0 +end if + +kernel_file_load: +; load kernel.mnt to _CLEAN_ZONE + dd 0 ; subfunction + dq 0 ; offset in file + dd 0x31000 ; number of bytes to read + dd _CLEAN_ZONE ; buffer for data + db '/sys/KERNEL.MNT',0 + +dev_data_path db '/RD/1/DRIVERS/DEVICES.DAT',0 +; { Patch by Coldy, For DLL autoload +dll_lib_path db '/RD/1/LIB/DLL.OBJ',0 +dll_error_msg db '"DLL.OBJ not found!\nTerminate application!" -dE',0 +; } End patch by Coldy, For DLL autoload +align 4 + +shmem_list: + .bk dd shmem_list + .fd dd shmem_list + +dll_list: + .bk dd dll_list + .fd dd dll_list + +pcidev_list: + .bk dd pcidev_list + .fd dd pcidev_list + +MAX_DEFAULT_DLL_ADDR = 0x80000000 +MIN_DEFAULT_DLL_ADDR = 0x70000000 +dll_cur_addr dd MIN_DEFAULT_DLL_ADDR + +; supported videomodes + + +; mike.dld { +;db 0 +;dd servetable-0x10000 +;align 4 +;draw_line dd __sys_draw_line +;draw_pointer dd __sys_draw_pointer +;//mike.dld, 2006-08-02 [ +;;drawbar dd __sys_drawbar +;;drawbar dd __sys_drawbar.forced +;drawbar dd vesa20_drawbar +;//mike.dld, 2006-08-02 ] +;putpixel dd __sys_putpixel +; } mike.dld + + +align 4 +keyboard dd 1 + +if lang eq en + SYSLANG = 1 +else if lang eq fi + SYSLANG = 2 +else if lang eq de + SYSLANG = 3 +else if lang eq ru + SYSLANG = 4 +else if lang eq fr + SYSLANG = 5 +else if lang eq et + SYSLANG = 6 +else if lang eq sp + SYSLANG = 7 +else if lang eq it + SYSLANG = 8 +else if lang eq ca + SYSLANG = 9 +else + display 'unsupported language specified',13,10 +end if +syslang dd SYSLANG + +boot_y dd 10 + +pci_bios_entry dd 0 + dw pci_code_sel + +if __DEBUG__ eq 1 + include_debug_strings +end if + +IncludeIGlobals + +align 16 +gdts: + + dw gdte-$-1 + dd gdts + dw 0 + +; Attention! Do not change the order of the first four selectors. They are used in Fast System Call +; must be : os_code, os_data, app_code, app_data, .... + +int_code_l: +os_code_l: + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10011010b + db 0x00 + +int_data_l: +os_data_l: + dw 0xffff + dw 0x0000 + db 0x00 + dw 11011111b *256 +10010010b + db 0x00 + +app_code_l: + dw 0xFFFF + dw 0 + db 0 + db cpl3 + dw G32+D32+0xF; + +app_data_l: + dw 0xFFFF + dw 0 + db 0 + db drw3 + dw G32+D32+0xF; + +; ------------- PCI BIOS ------------------ + +pci_code_32: + dw 0 ;lim 0-15 + dw 0 ;base 0-15 + db 0 ;base 16-23 + db cpl0 ;type + db D32 ;lim 16-19+props + db 0 ;base 24-31 + +pci_data_32: + dw 0 ;lim 0-15 + dw 0 ;base 0-15 + db 0 ;base 16-23 + db dpl0 ;type + db D32 ;lim 16-19+props + db 0 ;base 24-31 + +; --------------- APM --------------------- +apm_code_32: + dw 0x0f ; limit 64kb + db 0, 0, 0 + dw 11010000b *256 +10011010b + db 0x00 +apm_code_16: + dw 0x0f + db 0, 0, 0 + dw 10010000b *256 +10011010b + db 0x00 +apm_data_16: + dw 0x0f + db 0, 0, 0 + dw 10010000b *256 +10010010b + db 0x00 +; ----------------------------------------- + +graph_data_l: + + dw 0x7ff + dw 0x0000 + db 0x00 + dw 11010000b *256 +11110010b + db 0x00 +tss0_l: + dw sizeof.TSS-1 + dw tss and 0xFFFF + db (tss shr 16) and 0xFF + db 10001001b + dw (tss shr 16) and 0xFF00 + +tls_data_l: + dw 0x0FFF + dw 0 + db 0 + db drw3 + dw D32 + +gdte: + +diff16 "end of .data segment",0,$ +endofcode: + +align 16 +cur_saved_data: + rb 4096 +align 64 +fpu_data: + rb 0xa80 ; bochs avx512 +fpu_data_size = $ - fpu_data +draw_data: + rb 32*256 +BPSLine_calc_area rd MAX_SCREEN_HEIGHT +d_width_calc_area rd MAX_SCREEN_HEIGHT +mouseunder rd 16*24 + +mem_block_list rd 64*2 +mem_used_list rd 64*2 +mem_hash_cnt rd 64 + +thr_slot_map rd 8 + +_display display_t +bios_fb FRB + +mst MEM_STATE + +cpu_freq dq ? + +heap_mutex MUTEX +heap_size dd ? +heap_free dd ? +heap_blocks dd ? +free_blocks dd ? +mem_block_mask rd 2 +next_memblock dd ? + +pte_valid_mask dd ? +page_start dd ? +page_end dd ? +sys_page_map dd ? +os_stack_seg dd ? + +srv.fd dd ? +srv.bk dd ? + +LFBAddress dd ? + +PUTPIXEL dd ? +GETPIXEL dd ? + +if VESA_1_2_VIDEO +BANK_SWITCH dd ? ; reserved for vesa 1.2 +BANK_RW dd ? +end if + +MOUSE_PICTURE dd ? + +def_cursor dd ? +def_cursor_clock dd ? +current_cursor dd ? +hw_cursor dd ? +cur_saved_base dd ? + +cur.lock dd ? ; 1 - lock update, 2- hide +cur.left dd ? ; cursor clip box +cur.top dd ? +cur.w dd ? +cur.h dd ? + +ipc_tmp dd ? +ipc_pdir dd ? +ipc_ptab dd ? + +proc_mem_map dd ? +proc_mem_pdir dd ? +proc_mem_tab dd ? + +tmp_task_ptab dd ? + +default_io_map dd ? + +LFBSize dd ? + +current_process dd ? +current_slot dd ? ; pointer to APPDATA of current thread +current_slot_idx dd ? ; index of current thread slot +thread_count dd ? + +; device addresses +mididp dd ? +midisp dd ? + +cdbase dd ? +cdid dd ? + +hdbase dd ? ; for boot 0x1f0 +hdid dd ? +hdpos dd ? ; for boot 0x1 +cdpos dd ? + +;CPUID information +cpu_vendor rd 3 +cpu_sign dd ? +cpu_info dd ? +cpu_caps rd 4 + +xsave_area_size dd ? +xsave_eax dd ? +xsave_edx dd ? + +pg_data PG_DATA +heap_test dd ? + +skin_data dd ? + +mouse_active dd ? +mouse_pause dd ? + +BgrDrawMode dd ? +BgrDataWidth dd ? +BgrDataHeight dd ? + +buttontype dd ? +windowtypechanged dd ? + +debug_step_pointer dd ? + +lba_read_enabled dd ? ; 0 = disabled , 1 = enabled +pci_access_enabled dd ? ; 0 = disabled , 1 = enabled + +NumBiosDisks dd ? +BiosDisksData rb 200h ; struct BiosDiskData +BiosDiskCaches rb 80h*(cache_ide1-cache_ide0) +BiosDiskPartitions rd 80h + +img_background dd ? +mem_BACKGROUND dd ? +static_background_data dd ? + +hd1_status dd ? ; 0 - free : other - pid +application_table_owner dd ? ; 0 - free : other - pid +application_table_mutex MUTEX + +redrawmouse_unconditional dd ? + +MOUSE_SCROLL_H rw 1 +MOUSE_X: rw 1 +MOUSE_Y: rw 1 +MOUSE_SCROLL_V rw 1 + +X_UNDER rw 1 +Y_UNDER rw 1 +COLOR_TEMP dd ? +MOUSE_COLOR_MEM dd ? + +SCR_MODE rw 2 + +BTN_DOWN: rb 4 + +cpu_phys_addr_width db ? ; also known as MAXPHYADDR in Intel manuals +hdd_appl_data db ? ; 0 = system cache, 1 - application cache +cd_appl_data db ? ; 0 = system cache, 1 - application cache + +timer_ticks_enable db ? ; for cd driver + +REDRAW_BACKGROUND db ? + +align 16 +DRIVE_DATA: rb DRIVE_DATA_SIZE + +IncludeUGlobals + +uglobals_size = $ - endofcode + +if ~ lang eq sp +diff16 "end of .bss",0,$ +end if + +; check if kernel fits memmap +assert $-OS_BASE+0x1000 < TMP_STACK_TOP + +org (OS_BASE+0x0100000) + +; Currently size of memory allocated for the ramdisk is fixed. +; This should be revisited when/if memory map would become more dynamic. +RAMDISK_CAPACITY = 2880 ; in sectors + +RAMDISK: + rb RAMDISK_CAPACITY*512 + +_CLEAN_ZONE: +CLEAN_ZONE = _CLEAN_ZONE - OS_BASE + +BgrAuxTable rb 32768 +align 65536 +SB16Buffer rb 65536 + +align 4096 +BUTTON_INFO rb 64*1024 +RESERVED_PORTS: + rb 64*1024 +sys_pgmap: + rb 1024*1024/8 diff --git a/kernel/branches/kolibri-lldw/data32et.inc b/kernel/branches/kolibri-lldw/data32et.inc new file mode 100644 index 000000000..4345c7862 --- /dev/null +++ b/kernel/branches/kolibri-lldw/data32et.inc @@ -0,0 +1,48 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + + boot_initirq latin1 'Algväärtustan IRQ',0 + boot_picinit latin1 'Algväärtustan PIC',0 + boot_v86machine latin1 'Algväärtustan süsteemi V86 masinat',0 + boot_inittimer latin1 'Algväärtustan süsteemi taimerit (IRQ0)',0 + boot_initramdisk latin1 'Initialize ramdisk',0 + boot_initapic latin1 'Proovin Algväärtustada APIC',0 + boot_enableirq latin1 'Luban katkestused 2, 13',0 + boot_disabling_ide latin1 'Keelan IDE kontrolleri katkestused',0 + boot_enabling_ide latin1 'Luban IDE kontrolleri katkestused',0 + boot_set_int_IDE latin1 'Määran IDE kontrolleri halduri',0 + boot_detectfloppy latin1 'Otsin floppi kettaid',0 + boot_detecthdcd latin1 'Otsin kõvakettaid ja ATAPI seadmeid',0 + boot_getcache latin1 'Küsin puhvri mälu',0 + boot_detectpart latin1 'Otsin kettaseadmete partitsioone',0 + boot_init_sys latin1 'Algväärtustan süsteemi kataloogi /sys',0 + boot_loadlibs latin1 'Laadin mooduleid (.obj)',0 + boot_memdetect latin1 'Avastan mälu mahtu',0 + boot_tss latin1 'Määran TSSe',0 + boot_cpuid latin1 'Loen CPUIDd',0 +; boot_devices db 'Detecting devices',0 + boot_setmouse latin1 'Seadistan hiirt',0 + boot_windefs latin1 'Seadistan akende vaikeväärtusi',0 + boot_bgr latin1 'Kalkuleerin tausta',0 + boot_resirqports latin1 'Reserveerin IRQsi ja porte',0 + boot_setostask latin1 'Seadistan OS protsessi',0 + boot_allirqs latin1 'Unmasking IRQs',0 + boot_tsc latin1 'Loen TSC',0 + boot_cpufreq latin1 'CPU sagedus on ',' ',' MHz',0 + boot_pal_ega latin1 'Seadistan EGA/CGA 320x200 paletti',0 + boot_pal_vga latin1 'Seadistan VGA 640x480 paletti',0 + boot_failed latin1 'Esimese programmi käivitamine ebaõnnestus',0 + boot_mtrr latin1 'Määran MTRR',0 + + boot_APIC_found latin1 'APIC aktiveeritud', 0 + boot_APIC_nfound latin1 'APIC ei leitud', 0 +if preboot_blogesc + boot_tasking latin1 'Kõik valmis - vajuta ESC alustamiseks',0 +end if \ No newline at end of file diff --git a/kernel/branches/kolibri-lldw/data32sp.inc b/kernel/branches/kolibri-lldw/data32sp.inc new file mode 100644 index 000000000..6f421a110 --- /dev/null +++ b/kernel/branches/kolibri-lldw/data32sp.inc @@ -0,0 +1,54 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2013-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + + boot_initirq: cp850 'Inicializar IRQ',0 + boot_picinit: cp850 'Inicializar PIC',0 + boot_v86machine: cp850 'Inicializar sistema V86',0 + boot_inittimer: cp850 'Inicializar reloj del sistema (IRQ0)',0 + boot_initramdisk cp850 'Initialize ramdisk',0 + boot_initapic: cp850 'Prueba inicializar APIC',0 + boot_enableirq: cp850 'Habilitar interrupciones 2, 13',0 + boot_disabling_ide:cp850 'Habiliar interrupciones en controladores IDE',0 + boot_enabling_ide:cp850 'Habilitar interrupciones en controladores IDE',0 + boot_set_int_IDE: cp850 'Configuración del controlador de interrupciones para el IDE',0 + boot_detectfloppy:cp850 'Buscar unidades de disquete',0 + boot_detecthdcd: cp850 'Buscar discos duros y unidades ATAPI',0 + boot_getcache: cp850 'Tomar memoria para caché',0 + boot_detectpart: cp850 'Buscar particiones en discos',0 + boot_init_sys: cp850 'Inicializar directorio del sistema /sys',0 + boot_loadlibs: cp850 'Cargando librerías (.obj)',0 + boot_memdetect: cp850 'Determinando cantidad de memoria',0 + boot_tss: cp850 'Configurando TSSs',0 + boot_cpuid: cp850 'Leyendo CPUIDs',0 +; boot_devices: cp850 'Detectando dispositivos',0 + boot_setmouse: cp850 'Configurando el ratón',0 + boot_windefs: cp850 'Setting window defaults',0 + boot_bgr: cp850 'Calculating background',0 + boot_resirqports: cp850 'Reservando IRQs y puertos',0 + boot_setostask: cp850 'Configurando tarea OS',0 + boot_allirqs: cp850 'Desenmascarando IRQs',0 + boot_tsc: cp850 'Leyendo TSC',0 + boot_cpufreq: cp850 'La frequencia del CPU es ',' ',' MHz',0 + boot_pal_ega: cp850 'Configurando paleta EGA/CGA 320x200',0 + boot_pal_vga: cp850 'Configurando paleta VGA 640x480',0 + boot_failed: cp850 'Fallo al iniciar la primer aplicación',0 + boot_mtrr: cp850 'Configurando MTRR',0 + + boot_APIC_found: cp850 'APIC habilitado', 0 + boot_APIC_nfound: cp850 'APIC no encontrado', 0 +if preboot_blogesc + boot_tasking: cp850 'Todo configurado - presiona ESC para iniciar',0 +end if + +;msg_version: cp850 'versión incompatible del controlador',13,10,0 +;msg_www: cp850 'por favor, visita www.kolibrios.org',13,10,0 + +ud_user_message:cp850 'Error: instrucción no soportada por el procesador',0 +mtrr_user_message cp850 '"There is a problem with MTRR configuration.\nPerformance can be low" -dW',0 diff --git a/kernel/branches/kolibri-lldw/detect/biosdisk.inc b/kernel/branches/kolibri-lldw/detect/biosdisk.inc new file mode 100644 index 000000000..81a9f463a --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/biosdisk.inc @@ -0,0 +1,122 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2008-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Detect all BIOS hard drives. +; diamond, 2008 +; Do not include USB mass storages. CleverMouse, 2013 +; Read the number of sectors, bytes per sector. dunkaist, 2017 + + xor cx, cx + mov es, cx + mov di, BOOT_LO.bios_hd + mov byte [es:di-1], cl + cmp [preboot_biosdisk], 1 + jnz bdde + mov dl, 80h +bdds: + mov ah, 15h + push cx dx di + int 13h + pop di dx cx + jc bddc + test ah, ah + jz bddc + inc cx +; We are going to call int 13h/func 48h, Extended get drive parameters. +; The latest version of the EDD specification is 3.0. +; There are two slightly incompatible variants for version 3.0; +; original one from Phoenix in 1998, see e.g. +; http://www.t10.org/t13/technical/d98120r0.pdf, and T13 draft, +; http://www.t13.org/documents/UploadedDocuments/docs2004/d1572r3-EDD3.pdf +; T13 draft addresses more possible buses, so it gives additional 8 bytes +; for device path. +; Most BIOSes follow Phoenix, but T13 version is also known to be used +; (e.g. systems based on AMD Geode). +; Fortunately, there is an in/out length field, so +; it is easy to tell what variant was selected by the BIOS: +; Phoenix-3.0 has 42h bytes, T13-3.0 has 4Ah bytes. +; Note that 2.0 has 1Eh bytes, 1.1 has 1Ah bytes; both variants of 3.0 have +; the same structure for first 1Eh bytes, compatible with previous versions. +; Note also that difference between Phoenix-3.0 and T13-3.0 starts near the +; end of the structure, so the current code doesn't even need to distinguish. +; It needs, however, give at least 4Ah bytes as input and expect that BIOS +; could return 42h bytes as output while still giving all the information. + mov ah, 48h + push ds + push es + pop ds + mov si, 0xA000 + mov word [si], 4Ah + mov ah, 48h + int 13h + pop ds + jc bddc2 + cmp word [es:si], 1Eh + jb .noide + cmp word [es:si+1Ah], 0xFFFF + jz .noide + inc byte [es:BOOT_LO.bios_hd_cnt] + mov al, dl + stosb + push ds + push si + lds si, [es:si+1Ah] + mov al, [si+6] + and al, 0xF + stosb + mov al, byte [si+4] + shr al, 4 + and ax, 1 + cmp word [si], 1F0h + jz @f + inc ax + inc ax + cmp word [si], 170h + jz @f + or ax, -1 +; mov ax, -1 +@@: + stosw + pop si + pop ds + jmp bddc3 +.noide: + cmp word [es:si], 42h + jb .nousb + cmp word [es:si+28h], 'US' + jnz .nousb + cmp byte [es:si+2Ah], 'B' + jz bddc2 +.nousb: + inc byte [es:BOOT_LO.bios_hd_cnt] + mov al, dl + stosb + xor ax, ax + stosb + dec ax + stosw +; mov al, 0 +; stosb +; mov ax, -1 +; stosw +bddc3: + movzx eax, word[es:si+24] + stosd + mov eax, [es:si+16] + stosd + mov eax, [es:si+20] + stosd +bddc2: + cmp cl, [es:0x475] + jae bdde +bddc: + inc dl + jnz bdds +bdde: diff --git a/kernel/branches/kolibri-lldw/detect/biosmem.inc b/kernel/branches/kolibri-lldw/detect/biosmem.inc new file mode 100644 index 000000000..bf032393d --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/biosmem.inc @@ -0,0 +1,46 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2009-2017. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +; Query physical memory map from BIOS. +; diamond, 2009 + + push ds +; first call to fn E820 + mov eax, 0xE820 + xor ebx, ebx + mov es, bx + mov ds, bx + mov di, BOOT_LO.memmap_blocks + mov [BOOT_LO.memmap_block_cnt], ebx ; no blocks yet + mov ecx, 20 + mov edx, 'PAMS' ; 'SMAP' + int 15h + jc no_E820 + cmp eax, 'PAMS' + jnz no_E820 +e820_mem_loop: +; cmp byte [di+16], 1 ; ignore non-free areas +; jnz e820_mem_next + inc byte [BOOT_LO.memmap_block_cnt] + add di, sizeof.e820entry +e820_mem_next: +; consequent calls to fn E820 + test ebx, ebx + jz e820_test_done + cmp byte [BOOT_LO.memmap_block_cnt], MAX_MEMMAP_BLOCKS + jz e820_test_done + mov eax, 0xE820 + int 15h + jc e820_test_done + jmp e820_mem_loop +no_E820: +; let's hope for mem_test from init.inc +e820_test_done: + pop ds diff --git a/kernel/branches/kolibri-lldw/detect/dev_fd.inc b/kernel/branches/kolibri-lldw/detect/dev_fd.inc new file mode 100644 index 000000000..6e3fded9d --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/dev_fd.inc @@ -0,0 +1,38 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;*************************************************** +; clear the DRIVE_DATA table, +; search for FDDs and add them into the table +; author - Mario79 +;*************************************************** + xor eax, eax + mov edi, DRIVE_DATA + mov ecx, DRIVE_DATA_SIZE/4 + cld + rep stosd + + mov al, 0x10 + out 0x70, al + mov cx, 0xff +wait_cmos: + dec cx + test cx, cx + jnz wait_cmos + in al, 0x71 + mov [DRIVE_DATA], al + test al, al + jz @f + + stdcall attach_int_handler, 6, FDCInterrupt, 0 + DEBUGF 1, "K : Set IDE IRQ6 return code %x\n", eax + call floppy_init +@@: + diff --git a/kernel/branches/kolibri-lldw/detect/dev_hdcd.inc b/kernel/branches/kolibri-lldw/detect/dev_hdcd.inc new file mode 100644 index 000000000..24e256c3f --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/dev_hdcd.inc @@ -0,0 +1,475 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +; HDD and CD search + + cmp [ecx+IDE_DATA.ProgrammingInterface], 0 + je EndFindHDD +FindHDD: + xor ebx, ebx + inc ebx + mov [DeviceNumber], 0 + cmp ecx, IDE_controller_1 + jz .find + add bl, 5 + add [DeviceNumber], sizeof.HD_DATA*4 + cmp ecx, IDE_controller_2 + jz .find + add bl, 5 + add [DeviceNumber], sizeof.HD_DATA*4 +.find: + mov [ChannelNumber], 1 + mov [DiskNumber], 0 + call FindHDD_1 + inc [DiskNumber] + call FindHDD_2 + inc [ChannelNumber] + dec [DiskNumber] + call FindHDD_2 + inc [DiskNumber] + call FindHDD_2 + jmp EndFindHDD +;----------------------------------------------------------------------------- +FindHDD_2: + add [DeviceNumber], sizeof.HD_DATA + shl byte [ebx+DRIVE_DATA], 2 +FindHDD_1: + DEBUGF 1, "K : Channel %d ",[ChannelNumber]:1 + DEBUGF 1, "Disk %d\n",[DiskNumber]:1 + push ecx ebx + call ReadHDD_ID + cmp [DevErrorCode], 7 + je .end + cmp [DevErrorCode], 0 + jne .FindCD + cmp [Sector512+6], word 16 + ja .FindCD + cmp [Sector512+12], word 255 + ja .FindCD + pop ebx + movzx eax, [DeviceNumber] + mov ecx, [Sector512+120] + mov dword[eax+hd0_data.sectors], ecx + and dword[eax+hd0_data.sectors+4], 0 + bt word [Sector512+166], 10 + jnc .Print_Device_Name + mov [eax+hd0_data.hd48], 1 + mov ecx, [Sector512+200] + mov dword[eax+hd0_data.sectors], ecx + mov ecx, [Sector512+204] + mov dword[eax+hd0_data.sectors+4], ecx + jmp .Print_Device_Name +;-------------------------------------- +.FindCD: + call DeviceReset + cmp [DevErrorCode], 0 + jne .end + call ReadCD_ID + cmp [DevErrorCode], 0 + jne .end + pop ebx + inc byte [ebx+DRIVE_DATA] +;-------------------------------------- +.Print_Device_Name: + inc byte [ebx+DRIVE_DATA] + pop ecx + pushad + movzx ebx, [ChannelNumber] + dec ebx + shl ebx, 1 + add bl, [DiskNumber] + shl ebx, 1 + call calculate_IDE_device_values_storage +;-------------------------------------- +.copy_dev_name: + mov esi, Sector512+27*2 + mov edi, dev_name + mov ecx, 20 + cld +;-------------------------------------- +@@: + lodsw + xchg ah, al + stosw + loop @b + DEBUGF 1, "K : Dev: %s \n", dev_name + xor eax, eax + mov ax, [Sector512+64*2] + DEBUGF 1, "K : PIO possible modes %x\n", al + mov ax, [Sector512+51*2] + mov al, ah + call convert_Sector512_value + DEBUGF 1, "K : PIO set mode %x\n", ah + mov ax, [Sector512+63*2] + DEBUGF 1, "K : Multiword DMA possible modes %x\n", al + mov al, ah + call convert_Sector512_value + DEBUGF 1, "K : Multiword DMA set mode %x\n", ah + mov ax, [Sector512+88*2] + DEBUGF 1, "K : Ultra DMA possible modes %x\n", al + mov [ebx+IDE_DEVICE.UDMA_possible_modes], al + mov al, ah + call convert_Sector512_value + DEBUGF 1, "K : Ultra DMA set mode %x\n", ah + mov [ebx+IDE_DEVICE.UDMA_set_mode], ah + popad + ret + +.end: + DEBUGF 1, "K : Device not found\n" + pop ebx ecx + ret +;----------------------------------------------------------------------------- +calculate_IDE_device_values_storage: + cmp ecx, IDE_controller_1 + jne @f + + add ebx, IDE_device_1 + jmp .exit +;-------------------------------------- +@@: + cmp ecx, IDE_controller_2 + jne @f + + add ebx, IDE_device_2 + jmp .exit +;-------------------------------------- +@@: + add ebx, IDE_device_3 +;-------------------------------------- +.exit: + ret +;----------------------------------------------------------------------------- +convert_Sector512_value: + mov ecx, 8 + xor ah, ah +;-------------------------------------- +@@: + test al, 1b + jnz .end + + shr al, 1 + inc ah + loop @b + + xor ah, ah +;-------------------------------------- +.end: + ret +;----------------------------------------------------------------------------- +; Address of reading sector in LBA mode +uglobal +SectorAddress dd ? +dev_name: + rb 41 +endg +;----------------------------------------------------------------------------- +;************************************************* +;* READING THE HARD DISK IDENTIFIER * +;* Input parameters are passed through the global* +;* variables: * +;* ChannelNumber - channel number (1 or 2); * +;* DiskNumber - disk number on channel (0 or 1) * +;* Block of identificational data is reading * +;* to Sector512 array. * +;************************************************* +ReadHDD_ID: +; set up CHS mode + mov [ATAAddressMode], 0 +; send device identification command + mov [ATAFeatures], 0 + mov [ATAHead], 0 + mov [ATACommand], 0xEC + call SendCommandToHDD + cmp [DevErrorCode], 0 ; check the error code + jne @@End ; finish, saving the error code + + mov dx, [ATABasePortAddr] + add dx, 7 ; address of state register + mov ecx, 0xffff +@@WaitCompleet: + ; Check command execution time + dec ecx + jz @@Error1 ; timeout error + ; Check if ready or not + in al, dx + test al, 80h ; BSY signal state + jnz @@WaitCompleet + + test al, 1 ; ERR signal state + jnz @@Error6 + + test al, 08h ; DRQ signal state + jz @@WaitCompleet +; Receive data block from controller + mov edi, Sector512 + mov dx, [ATABasePortAddr]; data register + mov cx, 256 ; number of word to receive + rep insw ; receive data block + ret +; write the error code +@@Error1: + mov [DevErrorCode], 1 + ret +@@Error6: + mov [DevErrorCode], 6 +@@End: + ret +;----------------------------------------------------------------------------- +uglobal +; Standart base addresses of channels 1 or 2 +StandardATABases dw ?, ? ; 1F0h, 170h +; Channel number +ChannelNumber db ? +; Disk number +DiskNumber db ? +DeviceNumber db ? +; Base address of ATA controller's port group +ATABasePortAddr dw ? +; ATA-command parameters +ATAFeatures db ? ; features +ATASectorCount db ? ; count of processing sectors +ATASectorNumber db ? ; initial sector number +ATACylinder dw ? ; initial cylinder number +ATAHead db ? ; initial head number +ATAAddressMode db ? ; addressing mode (0 - CHS, 1 - LBA) +ATACommand db ? ; executing command number +; Error code (0 - no errors, 1 - waiting time limit exceed +; 2 - incorrect code of addressing mode, +; 3 - incorrect channel number, 4 - incorrect disk number, +; 5 - incorrect head number, 6 - command execution error, +; 7 - time out when choosing channel) +DevErrorCode dd ? +endg +;----------------------------------------------------------------------------- +;**************************************************** +;* SEND COMMAND TO GIVEN DISK * +;* Input parameters are passed through the global * +;* variables: * +;* ChannelNumber - channel number (1 or 2); * +;* DiskNumber - disk number (0 or 1); * +;* ATAFeatures - "features"; * +;* ATASectorCount - sector count; * +;* ATASectorNumber - initial sector number; * +;* ATACylinder - initial cylinder number; * +;* ATAHead - initial head number; * +;* ATAAddressMode - addressing mode (0-CHS, 1-LBA); * +;* ATACommand - command code. * +;* If the function finished successfully: * +;* in ATABasePortAddr - base address of HDD; * +;* in DevErrorCode - zero. * +;* If error has occured then in DevErrorCode will * +;* be the error code. * +;**************************************************** +SendCommandToHDD: +; Check the addressing mode code + cmp [ATAAddressMode], 1 + ja @@Err2 +; Check the channel number correctness + movzx ebx, [ChannelNumber] + dec ebx + cmp ebx, 1 + ja @@Err3 +; Set the base address + shl ebx, 1 + mov ax, [ebx+StandardATABases] + mov [ATABasePortAddr], ax +; Waiting for HDD ready to receive a command + ; Choose desired disk + mov dx, [ATABasePortAddr] + add dx, 6 ; address of the heads register + mov al, [DiskNumber] + cmp al, 1 ; check the disk number + ja @@Err4 + + shl al, 4 + or al, 10100000b + out dx, al + ; Waiting for disk ready + inc dx + mov ecx, 0xfff +@@WaitHDReady: + ; Check waiting time + dec ecx + jz @@Err1 + ; Read the state register + in al, dx + ; Check the state of BSY signal + test al, 80h + jnz @@WaitHDReady + ; Check the state of DRQ signal + test al, 08h + jnz @@WaitHDReady +; load command to controller's registers + cli + mov dx, [ATABasePortAddr] + inc dx ; "features" register + mov al, [ATAFeatures] + out dx, AL + inc dx ; sector counter + mov al, [ATASectorCount] + out dx, AL + inc dx ; sector number register + mov al, [ATASectorNumber] + out dx, AL + inc dx ; cylinder number (low byte) + mov ax, [ATACylinder] + out dx, AL + inc dx ; cylinder number (high byte) + mov al, AH + out dx, AL + inc dx ; head number / disk number + mov al, [DiskNumber] + shl al, 4 + cmp [ATAHead], 0xF ; check head number + ja @@Err5 + + or al, [ATAHead] + or al, 10100000b + mov ah, [ATAAddressMode] + shl ah, 6 + or al, ah + out dx, al +; Send command + mov al, [ATACommand] + inc dx ; command register + out dx, al + sti +; reset the error sign + mov [DevErrorCode], 0 + ret +; write error code +@@Err1: + mov [DevErrorCode], 7 + ret +@@Err2: + mov [DevErrorCode], 2 + ret +@@Err3: + mov [DevErrorCode], 3 + ret +@@Err4: + mov [DevErrorCode], 4 + ret +@@Err5: + mov [DevErrorCode], 5 +; finish work + ret +;----------------------------------------------------------------------------- +;************************************************* +;* READ ATAPI DEVICE IDENTIFIER * +;* Input parameters are passed through the global* +;* variables: * +;* ChannelNumber - channel number; * +;* DiskNumber - disk number on channel. * +;* Block of identificational data is reading * +;* to Sector512 array. * * +;************************************************* +ReadCD_ID: +; Set CHS mode + mov [ATAAddressMode], 0 +; Send command for device identification + mov [ATAFeatures], 0 + mov [ATASectorCount], 0 + mov [ATASectorNumber], 0 + mov [ATACylinder], 0 + mov [ATAHead], 0 + mov [ATACommand], 0xA1 + call SendCommandToHDD + cmp [DevErrorCode], 0 ; check the error code + jne @@End_1 ; finish, saving the error code +; Wait for HDD data ready + mov dx, [ATABasePortAddr] + add dx, 7 ; port 1х7h + mov ecx, 0xffff +@@WaitCompleet_1: + ; Check time + dec ecx + jz @@Error1_1 ; time out error + ; Check readyness + in al, dx + test al, 80h ; BSY signal state + jnz @@WaitCompleet_1 + + test al, 1 ; ERR signal state + jnz @@Error6_1 + + test al, 08h ; DRQ signal state + jz @@WaitCompleet_1 +; Receive data block from controller + mov edi, Sector512 ; offset Sector512 + mov dx, [ATABasePortAddr] ; port 1x0h + mov cx, 256 ; words read count + rep insw + ret +; write the error code +@@Error1_1: + mov [DevErrorCode], 1 + ret +@@Error6_1: + mov [DevErrorCode], 6 +@@End_1: + ret +;----------------------------------------------------------------------------- +;************************************************* +;* DEVICE RESET * +;* Input parameters are passed through the global* +;* variables: * +;* ChannelNumber - channel number (1 or 2); * +;* DiskNumber - disk number (0 or 1). * +;************************************************* +DeviceReset: +; Check the channel number correctness + movzx ebx, [ChannelNumber] + dec ebx + cmp ebx, 1 + ja @@Err3_2 +; Set base address + shl ebx, 1 + mov dx, [ebx+StandardATABases] + mov [ATABasePortAddr], dx +; Choose desired disk + add dx, 6 ; address of heads register + mov al, [DiskNumber] + cmp al, 1 ; check disk number + ja @@Err4_2 + + shl al, 4 + or al, 10100000b + out dx, al +; Send the "Reset" command + mov al, 0x8 + inc dx ; command register + out dx, al + mov ecx, 0x80000 +@@WaitHDReady_1: + ; Check waiting time + dec ecx + je @@Err1_2 ; time out error + ; read the state register + in al, dx + ; Check the state of BSY signal + test al, 80h + jnz @@WaitHDReady_1 +; reset the error sign + mov [DevErrorCode], 0 + ret +; error processing +@@Err1_2: + mov [DevErrorCode], 1 + ret +@@Err3_2: + mov [DevErrorCode], 3 + ret +@@Err4_2: + mov [DevErrorCode], 4 +; write error code + ret +;----------------------------------------------------------------------------- +EndFindHDD: diff --git a/kernel/branches/kolibri-lldw/detect/disks.inc b/kernel/branches/kolibri-lldw/detect/disks.inc new file mode 100644 index 000000000..0001fb129 --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/disks.inc @@ -0,0 +1,15 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +include 'dev_fd.inc' +include 'dev_hdcd.inc' +include 'getcache.inc' +include 'sear_par.inc' + diff --git a/kernel/branches/kolibri-lldw/detect/getcache.inc b/kernel/branches/kolibri-lldw/detect/getcache.inc new file mode 100644 index 000000000..c7f7bde80 --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/getcache.inc @@ -0,0 +1,209 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +;----------------------------------------------------------------------------- + pusha + + mov eax, [pg_data.pages_free] +; 1/32 + shr eax, 5 +; round off up to 8 pages + shr eax, 3 + shl eax, 3 +; translate pages in butes *4096 + shl eax, 12 +; check a upper size of the cache, no more than 1 Mb on the physical device + cmp eax, 1024*1024 + jbe @f + + mov eax, 1024*1024 + jmp .continue +;-------------------------------------- +@@: +; check a lower size of the cache, not less than 128 Kb on the physical device + cmp eax, 128*1024 + jae .continue + + mov eax, 128*1024 +;-------------------------------------- +.continue: + push ecx + mov ecx, 12 + mov esi, cache_ide0+IDE_CACHE.size + cld +@@: + mov [esi], eax + add esi, sizeof.IDE_CACHE + loop @b + + pop ecx + + xor eax, eax + mov [hdd_appl_data], 1 ;al + mov [cd_appl_data], 1 +;-------------------------------------- + test byte [DRIVE_DATA+1], 0x80 + je @f + + mov esi, cache_ide0 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+1], 0x20 + je @f + + mov esi, cache_ide1 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+1], 8 + je @f + + mov esi, cache_ide2 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+1], 2 + je @f + + mov esi, cache_ide3 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+6], 0x80 + je @f + + mov esi, cache_ide4 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+6], 0x20 + je @f + + mov esi, cache_ide5 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+6], 8 + je @f + + mov esi, cache_ide6 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+6], 2 + je @f + + mov esi, cache_ide7 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+11], 0x80 + je @f + + mov esi, cache_ide8 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+11], 0x20 + je @f + + mov esi, cache_ide9 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+11], 8 + je @f + + mov esi, cache_ide10 + call get_cache_ide +;-------------------------------------- +@@: + test byte [DRIVE_DATA+11], 2 + je end_get_cache + + mov esi, cache_ide11 + call get_cache_ide + + jmp end_get_cache +;----------------------------------------------------------------------------- +get_cache_ide: + and [esi+IDE_CACHE.search_start], 0 + and [esi+IDE_CACHE.appl_search_start], 0 + + push ecx +; DEBUGF 1, "K : IDE_CACHE.size %x\n", [esi+IDE_CACHE.size] + stdcall kernel_alloc, [esi+IDE_CACHE.size] + mov [esi+IDE_CACHE.pointer], eax + pop ecx + + mov edx, eax + mov eax, [esi+IDE_CACHE.size] + shr eax, 3 +; DEBUGF 1, "K : IDE_CACHE.system_data_size %x\n", eax + mov [esi+IDE_CACHE.system_data_size], eax + mov ebx, eax + imul eax, 7 +; DEBUGF 1, "K : IDE_CACHE.appl_data_size %x\n", eax + mov [esi+IDE_CACHE.appl_data_size], eax + add ebx, edx + mov [esi+IDE_CACHE.data_pointer], ebx + +.cd: + push ecx + mov eax, [esi+IDE_CACHE.system_data_size] + call calculate_for_cd + add eax, [esi+IDE_CACHE.pointer] + mov [esi+IDE_CACHE.system_data], eax + mov [esi+IDE_CACHE.system_sad_size], ecx + + push edi + mov edi, [esi+IDE_CACHE.pointer] + call clear_ide_cache + pop edi + + mov eax, [esi+IDE_CACHE.appl_data_size] + call calculate_for_cd + add eax, [esi+IDE_CACHE.data_pointer] + mov [esi+IDE_CACHE.appl_data], eax + mov [esi+IDE_CACHE.appl_sad_size], ecx + + push edi + mov edi, [esi+IDE_CACHE.data_pointer] + call clear_ide_cache + pop edi + + pop ecx + ret +;----------------------------------------------------------------------------- +calculate_for_cd: + push eax + mov ebx, eax + shr eax, 11 + shl eax, 3 + sub ebx, eax + shr ebx, 11 + mov ecx, ebx + shl ebx, 11 + pop eax + sub eax, ebx + dec ecx + ret +;----------------------------------------------------------------------------- +clear_ide_cache: + push eax + shl ecx, 1 + xor eax, eax + cld + rep stosd + pop eax + ret +;----------------------------------------------------------------------------- +end_get_cache: + popa diff --git a/kernel/branches/kolibri-lldw/detect/init_ata.inc b/kernel/branches/kolibri-lldw/detect/init_ata.inc new file mode 100644 index 000000000..eccd10277 --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/init_ata.inc @@ -0,0 +1,492 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2014-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + + +;----------------------------------------------------------------------------- +; find the IDE controller in the device list +;----------------------------------------------------------------------------- + mov ecx, IDE_controller_1 + mov esi, pcidev_list +;-------------------------------------- +align 4 +.loop: + mov esi, [esi+PCIDEV.fd] + cmp esi, pcidev_list + jz find_IDE_controller_done + + mov eax, [esi+PCIDEV.class] +; shr eax, 4 +; cmp eax, 0x01018 + shr eax, 7 + cmp eax, 0x010180 shr 7 + jnz .loop +;-------------------------------------- +.found: + mov eax, [esi+PCIDEV.class] + DEBUGF 1, 'K : IDE controller programming interface %x\n', eax + mov [ecx+IDE_DATA.ProgrammingInterface], eax + mov [ecx+IDE_DATA.pcidev], esi + + mov ah, [esi+PCIDEV.bus] + mov al, 2 + mov bh, [esi+PCIDEV.devfn] +;-------------------------------------- + mov dx, 0x1F0 + test byte [esi+PCIDEV.class], 1 + jz @f + mov bl, 0x10 + push eax + call pci_read_reg + and eax, 0xFFFC + mov edx, eax + pop eax +@@: + DEBUGF 1, 'K : BAR0 IDE base addr %x\n', dx + mov [StandardATABases], dx + mov [ecx+IDE_DATA.BAR0_val], dx +;-------------------------------------- + mov dx, 0x3F4 + test byte [esi+PCIDEV.class], 1 + jz @f + mov bl, 0x14 + push eax + call pci_read_reg + and eax, 0xFFFC + mov edx, eax + pop eax +@@: + DEBUGF 1, 'K : BAR1 IDE base addr %x\n', dx + mov [ecx+IDE_DATA.BAR1_val], dx +;-------------------------------------- + mov dx, 0x170 + test byte [esi+PCIDEV.class], 4 + jz @f + mov bl, 0x18 + push eax + call pci_read_reg + and eax, 0xFFFC + mov edx, eax + pop eax +@@: + DEBUGF 1, 'K : BAR2 IDE base addr %x\n', dx + mov [StandardATABases+2], dx + mov [ecx+IDE_DATA.BAR2_val], dx +;-------------------------------------- + mov dx, 0x374 + test byte [esi+PCIDEV.class], 4 + jz @f + mov bl, 0x1C + push eax + call pci_read_reg + and eax, 0xFFFC + mov edx, eax + pop eax +@@: + DEBUGF 1, 'K : BAR3 IDE base addr %x\n', dx + mov [ecx+IDE_DATA.BAR3_val], dx +;-------------------------------------- + mov bl, 0x20 + push eax + call pci_read_reg + and eax, 0xFFFC + DEBUGF 1, 'K : BAR4 IDE controller register base addr %x\n', ax + mov [ecx+IDE_DATA.RegsBaseAddres], ax + pop eax +;-------------------------------------- + mov bl, 0x3C + push eax + call pci_read_reg + and eax, 0xFF + DEBUGF 1, 'K : IDE Interrupt %x\n', al + mov [ecx+IDE_DATA.Interrupt], ax + pop eax + + add ecx, sizeof.IDE_DATA +;-------------------------------------- + jmp .loop +;----------------------------------------------------------------------------- +uglobal +align 4 +;-------------------------------------- +IDE_controller_pointer dd ? +;-------------------------------------- +IDE_controller_1 IDE_DATA +IDE_controller_2 IDE_DATA +IDE_controller_3 IDE_DATA +;-------------------------------------- +cache_ide0 IDE_CACHE +cache_ide1 IDE_CACHE +cache_ide2 IDE_CACHE +cache_ide3 IDE_CACHE +cache_ide4 IDE_CACHE +cache_ide5 IDE_CACHE +cache_ide6 IDE_CACHE +cache_ide7 IDE_CACHE +cache_ide8 IDE_CACHE +cache_ide9 IDE_CACHE +cache_ide10 IDE_CACHE +cache_ide11 IDE_CACHE +;-------------------------------------- +IDE_device_1 rd 2 +IDE_device_2 rd 2 +IDE_device_3 rd 2 +;-------------------------------------- +endg + +;-------------------------------------- +; set Bus Master bit of Command PCI register +;-------------------------------------- +set_pci_command_bus_master: +PCI_COMMAND_BUS_MASTER = 0x0004 + push eax ecx + + mov ecx, [ecx+IDE_DATA.pcidev] + mov ah, [ecx+PCIDEV.bus] + mov al, 1 ; word + mov bh, [ecx+PCIDEV.devfn] + mov bl, 0x4 ; Command register + push eax + call pci_read_reg + mov ecx, eax + pop eax + test ecx, PCI_COMMAND_BUS_MASTER ; already set? + jnz @f + or ecx, PCI_COMMAND_BUS_MASTER + call pci_write_reg +@@: + pop ecx eax + ret + +;----------------------------------------------------------------------------- +; START of initialisation IDE ATA code +;----------------------------------------------------------------------------- +Init_IDE_ATA_controller: + cmp [ecx+IDE_DATA.ProgrammingInterface], 0 + jne @f + + ret +;-------------------------------------- +@@: + mov esi, boot_disabling_ide + call boot_log +;-------------------------------------- +; Disable IDE interrupts, because the search +; for IDE partitions is in the PIO mode. +;-------------------------------------- +.disable_IDE_interrupt: +; Disable interrupts in IDE controller for PIO + mov al, 2 + mov dx, [ecx+IDE_DATA.BAR1_val] ;0x3F4 + add dx, 2 ;0x3F6 + out dx, al + mov dx, [ecx+IDE_DATA.BAR3_val] ;0x374 + add dx, 2 ;0x376 + out dx, al +;----------------------------------------------------------------------------- +; set current ata bases +@@: + mov ax, [ecx+IDE_DATA.BAR0_val] + mov [StandardATABases], ax + mov ax, [ecx+IDE_DATA.BAR2_val] + mov [StandardATABases+2], ax + + mov esi, boot_detecthdcd + call boot_log +;-------------------------------------- +include 'dev_hdcd.inc' +;-------------------------------------- + ret +;----------------------------------------------------------------------------- +Init_IDE_ATA_controller_2: + cmp [ecx+IDE_DATA.ProgrammingInterface], 0 + jne @f + + ret +;-------------------------------------- +@@: + mov dx, [ecx+IDE_DATA.RegsBaseAddres] +; test whether it is our interrupt? + add dx, 2 + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +;-------------------------------------- +@@: + add dx, 8 +; test whether it is our interrupt? + in al, dx + test al, 100b + jz @f +; clear Bus Master IDE Status register +; clear Interrupt bit + out dx, al +;-------------------------------------- +@@: +; read status register and remove the interrupt request + mov dx, [ecx+IDE_DATA.BAR0_val] ;0x1F0 + add dx, 0x7 ;0x1F7 + in al, dx + mov dx, [ecx+IDE_DATA.BAR2_val] ;0x170 + add dx, 0x7 ;0x177 + in al, dx +;----------------------------------------------------------------------------- +; push eax edx +; mov dx, [ecx+IDE_DATA.RegsBaseAddres] +; xor eax, eax +; add dx, 2 +; in al, dx +; DEBUGF 1, "K : Primary Bus Master IDE Status Register %x\n", eax + +; add dx, 8 +; in al, dx +; DEBUGF 1, "K : Secondary Bus Master IDE Status Register %x\n", eax +; pop edx eax + +; cmp [ecx+IDE_DATA.RegsBaseAddres], 0 +; setnz [ecx+IDE_DATA.dma_hdd] +;----------------------------------------------------------------------------- +; set interrupts for IDE Controller +;----------------------------------------------------------------------------- + pushfd + cli +.enable_IDE_interrupt: + mov esi, boot_enabling_ide + call boot_log +; Enable interrupts in IDE controller for DMA + xor ebx, ebx + cmp ecx, IDE_controller_2 + jne @f + + add ebx, 5 + jmp .check_DRIVE_DATA +;-------------------------------------- +@@: + cmp ecx, IDE_controller_3 + jne .check_DRIVE_DATA + + add ebx, 10 +;-------------------------------------- +.check_DRIVE_DATA: + mov al, 0 + mov ah, [ebx+DRIVE_DATA+1] + test ah, 10100000b ; check for ATAPI devices + jz @f +;-------------------------------------- +.ch1_pio_set_ATAPI: + DEBUGF 1, "K : IDE CH1 PIO, because ATAPI drive present\n" + jmp .ch1_pio_set_for_all +;-------------------------------------- +.ch1_pio_set_no_devices: + DEBUGF 1, "K : IDE CH1 PIO because no devices\n" + jmp .ch1_pio_set_for_all +;------------------------------------- +.ch1_pio_set: + DEBUGF 1, "K : IDE CH1 PIO because device not support UDMA\n" +;------------------------------------- +.ch1_pio_set_for_all: + mov [ecx+IDE_DATA.dma_hdd_channel_1], al + jmp .ch2_check +;-------------------------------------- +@@: + xor ebx, ebx + call calculate_IDE_device_values_storage + + test ah, 1010000b + jz .ch1_pio_set_no_devices + + test ah, 1000000b + jz @f + + cmp [ebx+IDE_DEVICE.UDMA_possible_modes], al + je .ch1_pio_set + + cmp [ebx+IDE_DEVICE.UDMA_set_mode], al + je .ch1_pio_set +;-------------------------------------- +@@: + test ah, 10000b + jz @f + + add ebx, 2 + + cmp [ebx+IDE_DEVICE.UDMA_possible_modes], al + je .ch1_pio_set + + cmp [ebx+IDE_DEVICE.UDMA_set_mode], al + je .ch1_pio_set +;-------------------------------------- +@@: + mov dx, [ecx+IDE_DATA.BAR1_val] ;0x3F4 + add dx, 2 ;0x3F6 + out dx, al + call set_pci_command_bus_master + DEBUGF 1, "K : IDE CH1 DMA enabled\n" + mov [ecx+IDE_DATA.dma_hdd_channel_1], byte 1 +;-------------------------------------- +.ch2_check: + test ah, 1010b ; check for ATAPI devices + jz @f +;-------------------------------------- +.ch2_pio_set_ATAPI: + DEBUGF 1, "K : IDE CH2 PIO, because ATAPI drive present\n" + jmp .ch2_pio_set_for_all +;-------------------------------------- +.ch2_pio_set_no_devices: + DEBUGF 1, "K : IDE CH2 PIO because no devices\n" + jmp .ch2_pio_set_for_all +;-------------------------------------- +.ch2_pio_set: + DEBUGF 1, "K : IDE CH2 PIO because device not support UDMA\n" +;-------------------------------------- +.ch2_pio_set_for_all: + mov [ecx+IDE_DATA.dma_hdd_channel_2], al + jmp .set_interrupts_for_IDE_controllers +;-------------------------------------- +@@: + mov ebx, 4 + call calculate_IDE_device_values_storage + + test ah, 101b + jz .ch2_pio_set_no_devices + + test ah, 100b + jz @f + + cmp [ebx+IDE_DEVICE.UDMA_possible_modes], al + je .ch2_pio_set + + cmp [ebx+IDE_DEVICE.UDMA_set_mode], al + je .ch2_pio_set +;-------------------------------------- +@@: + test ah, 1b + jz @f + + add ebx, 2 + + cmp [ebx+IDE_DEVICE.UDMA_possible_modes], al + je .ch2_pio_set + + cmp [ebx+IDE_DEVICE.UDMA_set_mode], al + je .ch2_pio_set +;-------------------------------------- +@@: + mov dx, [ecx+IDE_DATA.BAR3_val] ;0x374 + add dx, 2 ;0x376 + out dx, al + call set_pci_command_bus_master + DEBUGF 1, "K : IDE CH2 DMA enabled\n" + mov [ecx+IDE_DATA.dma_hdd_channel_2], byte 1 +;-------------------------------------- +.set_interrupts_for_IDE_controllers: + mov esi, boot_set_int_IDE + call boot_log +;-------------------------------------- + mov eax, [ecx+IDE_DATA.ProgrammingInterface] +; cmp ax, 0x0180 +; je .pata_ide + +; cmp ax, 0x018a +; jne .sata_ide + + test al, 1 ; 0 - legacy PCI mode, 1 - native PCI mode + jnz .sata_ide +;-------------------------------------- +.pata_ide: + cmp [ecx+IDE_DATA.RegsBaseAddres], 0 + je .end_set_interrupts + + push ecx + stdcall attach_int_handler, 14, IDE_irq_14_handler, ecx + pop ecx + DEBUGF 1, "K : Set IDE IRQ14 return code %x\n", eax + push ecx + stdcall attach_int_handler, 15, IDE_irq_15_handler, ecx + DEBUGF 1, "K : Set IDE IRQ15 return code %x\n", eax + pop ecx + + jmp .end_set_interrupts +;-------------------------------------- +.sata_ide: +; cmp ax, 0x0185 +; je .sata_ide_1 + +; cmp ax, 0x018f +; jne .end_set_interrupts +;-------------------------------------- +;.sata_ide_1: +; Some weird controllers generate an interrupt even if IDE interrupts +; are disabled and no IDE devices. For example, notebook ASUS K72F - +; IDE controller 010185 generates false interrupt when we work with +; the IDE controller 01018f. For this reason, the interrupt handler +; does not need to be installed if both channel IDE controller +; running in PIO mode. + +; ...unfortunately, PCI interrupt can be shared with other devices +; which could enable it without consulting IDE code. +; So install the handler anyways and try to process +; even those interrupts which we are not expecting. + cmp [ecx+IDE_DATA.RegsBaseAddres], 0 + je .end_set_interrupts + + mov ax, [ecx+IDE_DATA.Interrupt] + movzx eax, al + push ecx + stdcall attach_int_handler, eax, IDE_common_irq_handler, ecx + pop ecx + DEBUGF 1, "K : Set IDE IRQ%d return code %x\n", [ecx+IDE_DATA.Interrupt]:1, eax +;-------------------------------------- +.end_set_interrupts: + popfd + ret +;----------------------------------------------------------------------------- +; END of initialisation IDE ATA code +;----------------------------------------------------------------------------- +find_IDE_controller_done: + mov ecx, IDE_controller_1 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller + mov ecx, IDE_controller_2 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller + mov ecx, IDE_controller_3 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller +;----------------------------------------------------------------------------- + mov esi, boot_getcache + call boot_log +include 'getcache.inc' +;----------------------------------------------------------------------------- + mov esi, boot_detectpart + call boot_log +include 'sear_par.inc' +;----------------------------------------------------------------------------- + mov esi, boot_init_sys + call boot_log + call Parser_params + +if ~ defined extended_primary_loader +; ramdisk image should be loaded by extended primary loader if it exists +; READ RAMDISK IMAGE FROM HD +include '../boot/rdload.inc' +end if +;----------------------------------------------------------------------------- + mov ecx, IDE_controller_1 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller_2 + mov ecx, IDE_controller_2 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller_2 + mov ecx, IDE_controller_3 + mov [IDE_controller_pointer], ecx + call Init_IDE_ATA_controller_2 +;----------------------------------------------------------------------------- diff --git a/kernel/branches/kolibri-lldw/detect/sear_par.inc b/kernel/branches/kolibri-lldw/detect/sear_par.inc new file mode 100644 index 000000000..1c896a27b --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/sear_par.inc @@ -0,0 +1,280 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +$Revision$ + +search_partitions: + push ecx +; 1. Fill missing parameters in HD_DATA structures. + xor eax, eax + mov edx, IDE_controller_1 + mov ax, [edx + IDE_DATA.BAR0_val] + mov [hd0_data.hdbase], ax + mov [hd1_data.hdbase], ax + mov ax, [edx + IDE_DATA.BAR2_val] + mov [hd2_data.hdbase], ax + mov [hd3_data.hdbase], ax + + mov edx, IDE_controller_2 + mov ax, [edx + IDE_DATA.BAR0_val] + mov [hd4_data.hdbase], ax + mov [hd5_data.hdbase], ax + mov ax, [edx + IDE_DATA.BAR2_val] + mov [hd6_data.hdbase], ax + mov [hd7_data.hdbase], ax + + mov edx, IDE_controller_3 + mov ax, [edx + IDE_DATA.BAR0_val] + mov [hd8_data.hdbase], ax + mov [hd9_data.hdbase], ax + mov ax, [edx + IDE_DATA.BAR2_val] + mov [hd10_data.hdbase], ax + mov [hd11_data.hdbase], ax +; 2. Notify the system about /hd* disks. +; For every existing disk, call ide_disk_add with correct parameters. +; Generate name "hdN" on the stack; this is 4 bytes including terminating zero. +;----------------------------------------------------------------------------- +; 2a. /hd0: exists if mask 0x40 in [DRIVE_DATA+1] is set, +; data: hd0_data, +; number of partitions: [DRIVE_DATA+2] + test [DRIVE_DATA+1], byte 0x40 + jz @f + + push 'hd0' + mov eax, esp ; name + mov edx, hd0_data + call ide_disk_add + mov [DRIVE_DATA+2], al + pop ecx ; restore the stack +;----------------------------------------------------------------------------- +@@: +; 2b. /hd1: exists if mask 0x10 in [DRIVE_DATA+1] is set, +; data: hd1_data, +; number of partitions: [DRIVE_DATA+3] + test [DRIVE_DATA+1], byte 0x10 + jz @f + + push 'hd1' + mov eax, esp + mov edx, hd1_data + call ide_disk_add + mov [DRIVE_DATA+3], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2c. /hd2: exists if mask 4 in [DRIVE_DATA+1] is set, +; data: hd2_data, +; number of partitions: [DRIVE_DATA+4] + test [DRIVE_DATA+1], byte 4 + jz @f + + push 'hd2' + mov eax, esp + mov edx, hd2_data + call ide_disk_add + mov [DRIVE_DATA+4], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2d. /hd3: exists if mask 1 in [DRIVE_DATA+1] is set, +; data: hd3_data, +; number of partitions: [DRIVE_DATA+5] + test [DRIVE_DATA+1], byte 1 + jz @f + + push 'hd3' + mov eax, esp + mov edx, hd3_data + call ide_disk_add + mov [DRIVE_DATA+5], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2e. /hd4: exists if mask 0x40 in [DRIVE_DATA+6] is set, +; data: hd4_data, +; number of partitions: [DRIVE_DATA+7] + test [DRIVE_DATA+6], byte 0x40 + jz @f + + push 'hd4' + mov eax, esp ; name + mov edx, hd4_data + call ide_disk_add + mov [DRIVE_DATA+7], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2f. /hd5: exists if mask 0x10 in [DRIVE_DATA+6] is set, +; data: hd5_data, +; number of partitions: [DRIVE_DATA+8] + test [DRIVE_DATA+6], byte 0x10 + jz @f + + push 'hd5' + mov eax, esp + mov edx, hd5_data + call ide_disk_add + mov [DRIVE_DATA+8], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2g. /hd6: exists if mask 4 in [DRIVE_DATA+6] is set, +; data: hd6_data, +; number of partitions: [DRIVE_DATA+9] + test [DRIVE_DATA+6], byte 4 + jz @f + + push 'hd6' + mov eax, esp + mov edx, hd6_data + call ide_disk_add + mov [DRIVE_DATA+9], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2h. /hd7: exists if mask 1 in [DRIVE_DATA+6] is set, +; data: hd7_data, +; number of partitions: [DRIVE_DATA+10] + test [DRIVE_DATA+6], byte 1 + jz @f + + push 'hd7' + mov eax, esp + mov edx, hd7_data + call ide_disk_add + mov [DRIVE_DATA+10], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2i. /hd8: exists if mask 0x40 in [DRIVE_DATA+11] is set, +; data: hd8_data, +; number of partitions: [DRIVE_DATA+12] + test [DRIVE_DATA+11], byte 0x40 + jz @f + + push 'hd8' + mov eax, esp ; name + mov edx, hd8_data + call ide_disk_add + mov [DRIVE_DATA+12], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2j. /hd9: exists if mask 0x10 in [DRIVE_DATA+11] is set, +; data: hd9_data, +; number of partitions: [DRIVE_DATA+13] + test [DRIVE_DATA+11], byte 0x10 + jz @f + + push 'hd9' + mov eax, esp + mov edx, hd9_data + call ide_disk_add + mov [DRIVE_DATA+13], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2k. /hd10: exists if mask 4 in [DRIVE_DATA+11] is set, +; data: hd10_data, +; number of partitions: [DRIVE_DATA+14] + test [DRIVE_DATA+14], byte 4 + jz @f + + push 'hd10' + mov eax, esp + mov edx, hd10_data + call ide_disk_add + mov [DRIVE_DATA+9], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 2l. /hd11: exists if mask 1 in [DRIVE_DATA+11] is set, +; data: hd11_data, +; number of partitions: [DRIVE_DATA+15] + test [DRIVE_DATA+11], byte 1 + jz @f + + push 'hd11' + mov eax, esp + mov edx, hd11_data + call ide_disk_add + mov [DRIVE_DATA+15], al + pop ecx +;----------------------------------------------------------------------------- +@@: +; 3. Notify the system about /bd* disks. +; 3a. Check whether there are BIOS disks. If no, skip step 3. + xor esi, esi + cmp esi, [NumBiosDisks] + jz .nobd +; Loop over all disks. + push 0 + push 'bd' +.bdloop: +; 3b. Get the drive number for using in /bd* name. + lea eax, [esi*4] + movzx eax, [BiosDisksData+eax*4+BiosDiskData.DriveNumber] + sub al, 80h +; 3c. Convert eax to decimal and store starting with [esp+3]. +; First 2 bytes in [esp] are "bd". + lea edi, [esp+2] +; store digits in the stack, ending with -'0' + push -'0' +@@: + xor edx, edx +iglobal +align 4 +_10 dd 10 +endg + div [_10] + push edx + test eax, eax + jnz @b +; restore digits from the stack, this reverses the order; +; add '0', stop, when zero is reached +@@: + pop eax + add al, '0' + stosb + jnz @b +; 3e. Call the API with userdata = 80h + ecx. + mov eax, esp + lea edx, [esi+80h] + stdcall disk_add, bd_callbacks, eax, edx, 0 + test eax, eax + jz @f + stdcall disk_media_changed, eax, 1 +@@: +; 3f. Continue the loop. + inc esi + cmp esi, [NumBiosDisks] + jnz .bdloop + pop ecx ecx ; restore stack after name +.nobd: + jmp end_search_partitions +;----------------------------------------------------------------------------- +; Helper procedure for search_partitions, adds one IDE disk. +; For compatibility, number of partitions for IDE disks is kept in a separate +; variable, so the procedure returns number of partitions. +; eax -> name, edx -> disk data +proc ide_disk_add + stdcall disk_add, ide_callbacks, eax, edx, 0 + test eax, eax + jz @f + push eax + stdcall disk_media_changed, eax, 1 + pop eax + mov eax, [eax+DISK.NumPartitions] + cmp eax, 255 + jbe @f + mov eax, 255 +@@: + ret +endp +;----------------------------------------------------------------------------- +end_search_partitions: + pop ecx diff --git a/kernel/branches/kolibri-lldw/detect/vortex86.inc b/kernel/branches/kolibri-lldw/detect/vortex86.inc new file mode 100644 index 000000000..5dd751c00 --- /dev/null +++ b/kernel/branches/kolibri-lldw/detect/vortex86.inc @@ -0,0 +1,158 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; ;; +;; Copyright (C) KolibriOS team 2004-2015. All rights reserved. ;; +;; Distributed under terms of the GNU General Public License ;; +;; ;; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; 20/11/2013 yogev_ezra: Initial version (Vortex86 SoC type detection) +; 26/11/2013 yogev_ezra: Added CPU speed modifier and MMX support flag detection +; Thanks for help to: dunkaist, eAndrew, hidnplayr, Mario + +$Revision$ + +VORTEX86DEBUG = 0 ; For testing in emulators and in non-Vortex86 CPU computers, set this to 1 +VORTEX86DEBUGVALUE = 'DMP5' ; FAKE port output = used for testing +NORTH_BRIDGE = 0x80000000 ; Base address of Vortex86 PCI North Bridge +SOUTH_BRIDGE = 0x80003800 ; Base address of Vortex86 PCI South Bridge + +; Detect Vortex86 CPU and generate CPU name in string format (PCI address at 93H~90H in Vortex86 North Bridge contains SoC type) +; Available Vortex86 CPU codes taken from Coreboot project. New codes should be added to "Vortex86SoClist" below +; #define DMP_CPUID_SX 0x31504d44 ("DMP1") +; #define DMP_CPUID_DX 0x32504d44 ("DMP2") +; #define DMP_CPUID_MX 0x33504d44 ("DMP3") +; #define DMP_CPUID_DX2 0x34504d44 ("DMP4") +; #define DMP_CPUID_MX_PLUS 0x35504d44 ("DMP5") +; #define DMP_CPUID_EX 0x37504d44 ("DMP7") + +iglobal +Vortex86CPUcode dd ? ; Vortex86 CPU code in HEX format (4 bytes), can be shown as string if converted to ASCII characters +Vortex86CPUid db 0 ; Vortex86 CPU id in integer format (1=Vortex86SX, 2=Vortex86DX, ...) +Vortex86SoCname db 'Vortex86 ',0 ; This variable will hold the full name of Vortex86 SoC +Vortex86SoClist: ; List of Vortex86 CPUs known today. Add new record to this list when new CPU becomes available + db 0x31, 'SX ' ; id=1 + db 0x32, 'DX ' ; id=2 + db 0x33, 'MX ' ; id=3 MMX is available starting from CPU code 'MX' (id=3) + db 0x34, 'DX2' ; id=4 + db 0x35, 'MX+' ; id=5 + db 0x37, 'EX ' ; id=7 +Vortex86SoCnum = ($ - Vortex86SoClist) / 4 ; Calculate the total number of known Vortex86 CPUs +endg + +; When in debug mode, perform SoC detection regardless of the actual CPU vendor (even for vendors other than DMP) +; When in normal (not debug) mode, check the CPU vendor first, and perform SoC detection only if vendor is 'Vortex86 SoC' +if ~ VORTEX86DEBUG + cmp [cpu_vendor], 'Vort' + jnz .Vortex86end ; If the CPU vendor is not 'Vortex86 SoC', skip the SoC detection +end if + + mov eax, NORTH_BRIDGE+0x90 ; 0x80000090 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get the CPU code from Vortex86 SoC North Bridge PCI register (Register Offset: 93H~90H) + +if VORTEX86DEBUG ; When in debug mode, pretend that we received port output equal to "VORTEX86DEBUGVALUE" + mov eax, VORTEX86DEBUGVALUE +end if + + DEBUGF 1, "K : Vortex86 SoC type register (93H~90H) returned 0x" + test eax, eax ; Check whether the port output was '\0' + jz .nullPCIoutput ; In case the result is '\0' (NULL), skip further testing and exit + mov [Vortex86CPUcode], eax ; Save HEX CPU code to Vortex86CPUcode (so it can be used later) + DEBUGF 1, "%x (%s): ", eax, Vortex86CPUcode ; Print the CPU code (as HEX and as string) to debug log + + mov ebx, 0x444d5000 ; Apply Vortex86 CPU code mask (all Vortex86 SoC have ID in form of "0xNN504d44") + bswap eax ; Assumed it is Vortex86 SoC, the highest byte identifies the exact CPU, so move it to the lowest byte + mov bl, al ; Copy SoC type to BL since EAX (that includes AL) is used implicitly in "LODSD" command below + cmp eax, ebx ; Now see whether the 3 higher bytes were "0x504d44" (which means it's Vortex86) + jnz .notVortex86 ; If it's not Vortex86 - go say so and exit + + sub al, 0x30 ; Current Vortex86 CPU codes are in the range of 31h-37h, so convert them to integer (1,2,...) + mov [Vortex86CPUid], al ; Save the CPUid (1=Vortex86SX, 2=Vortex86DX, ..., 7=Vortex86EX, ...) + + mov esi, Vortex86SoClist ; ESI points to the start of Vortex86SoClist (used implicitly in "LODSD" command below) + xor ecx, ecx ; Zero ECX (it is used as counter) + cld ; Clears the DF flag in the EFLAGS register (DF=0 --> String operations increment ESI) +@@: + inc ecx ; Increment our counter + cmp ecx, Vortex86SoCnum ; Check if we iterated Vortex86SoCnum times already (i.e. went over the entire Vortex86SoClist) + ja .unknownVortex86 ; If the entire list was tested and our CPU is not in that list, it is unknown Vortex86 SoC + lodsd ; Load DWORD at address DS:ESI into EAX (puts 1 line from Vortex86SoClist into EAX, then increments ESI) + cmp bl, al ; Check if our CPU matches the current record in the list + jne @b ; No match --> repeat with next record + + shr eax, 8 ; Match found --> drop the SoC type code from Vortex86SoClist name and replace it with \0 + mov dword [Vortex86SoCname+8], eax ; Concatenate it with prefix to receive complete SoC name (\0 is string termination) + + DEBUGF 1, "%s (id=%d)\n", Vortex86SoCname, [Vortex86CPUid]:1 ; Say what we have found (CPU name and id) + jmp .Vortex86 + +.notVortex86: ; In case this register is used by other CPUs for other purpose, it's interesting what it contains + DEBUGF 1, "not a Vortex86 CPU\n" + jmp .Vortex86end + +.unknownVortex86: ; It is Vortex86 CPU, but it's not in the list above + DEBUGF 1, "unknown Vortex86 CPU (id=%d)\n", [Vortex86CPUid]:1 ; Inform the user that the CPU is Vortex86 but name is unknown + +.Vortex86: + mov eax, NORTH_BRIDGE+0x60 ; 0x80000060 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge STRAP Register (Register Offset: 63h~60h) + DEBUGF 1, "K : Vortex86 STRAP Register (63h~60h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xC0 ; 0x800038C0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register (Register Offset: C3h~C0h) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register (C3h~C0h) returned 0x%x\n",eax + + mov eax, SOUTH_BRIDGE+0xCC ; 0x800038CC = PCI Configuration Address Register to read from (8-bit register - accessed as BYTE) + call .Vortex86PCIreg ; Flags of Vortex86 South Bridge Internal Peripheral Feature Control Register III (Register Offset: CCh) + DEBUGF 1, "K : Vortex86 Internal Peripheral Feature Control Register III (CCh) returned 0x%x\n",al + + mov eax, NORTH_BRIDGE+0xA0 ; 0x800000A0 = PCI Configuration Address Register to read from (32-bit register - accessed as DWORD) + call .Vortex86PCIreg ; Get current flags of Vortex86SoC North Bridge Host Control Register (Register Offset: A3h~A0h) + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) returned 0x%x: CPU speed is ",eax + mov bl, al ; The lower byte of Vortex86 Host Control Register contains CPU speed modifier and MMX support status + mov bh, al ; Backup the current AL value, so later we can test whether the value has changed + and bl, 00000111b ; CPU speed modifier is stored in bits 0-2. Value=0 means MAX speed, other values - speed reduction + jz .Vortex86CPUspeedMAX ; 0s in bits 0-2: CPU is at MAX speed (no need to modify) + inc ebx ; The actual value is 1 less than 'Divide by' setting (value '001' means 'Divide by 2', etc.) + DEBUGF 1, "reduced (divide by %d).\nK : Vortex86 changing CPU speed to ", bl ; Print the current CPU speed modifier to the log + and al, 11111000b ; At least one of the bits 0-2 contains 1: CPU is at reduced speed. Set bits 0-2 to 0s to change to MAX +.Vortex86CPUspeedMAX: + DEBUGF 1, "MAX\n" ; Now the CPU should be running at MAX speed (don't write the value to PCI port yet) + + cmp [Vortex86CPUid], 3 ; MMX is available starting from CPU code 'MX' (id=3) + jb .skipVortex86MMX ; No MMX support - skip MMX support status detection (for id=1,2) + DEBUGF 1, "K : Vortex86 MMX support status: MMX is " ; Bits 5-6 in Host Control Register contain MMX status + test al, 100000b ; On MMX-capable Vortex86 SoC, Bit5 = is MMX enabled? (1=Yes/0=No) + jnz .Vortex86MMXenabled ; MMX is already enabled (Bit5=1) + DEBUGF 1, "DISABLED - enabling it for this session\n" ; Print to the log that MMX is disabled + or al, 100000b ; Enable MMX support (don't write the value to PCI port yet) + jmp .AfterMMXenabled +.Vortex86MMXenabled: + DEBUGF 1, "ENABLED\n" ; Print to the log that MMX is enabled +.AfterMMXenabled: + DEBUGF 1, "K : Vortex86 MMX report to CPUID: " ; Print to the log what CPUID command knowns about MMX support + test al, 1000000b ; On MMX-capable Vortex86 SoC, Bit6 = report MMX support to CPUID? (1=Yes/0=No) + jnz .Vortex86MMXreported ; MMX is already reported to CPUID (Bit6=1) + DEBUGF 1, "OFF - turning it ON for this session\n" ; Print to the log that MMX will now be reported to CPUID + or al, 1000000b ; Turn on MMX reporting to CPUID (don't write the value to PCI port yet) + jmp .skipVortex86MMX +.Vortex86MMXreported: + DEBUGF 1, "ON\n" ; Print to the log that MMX reporting to CPUID is enabled + +.skipVortex86MMX: + cmp bh, al ; Check whether AL has changed before (if it did, we need to write it back to PCI port) + jz .Vortex86end ; No change - no need to write to the port + out dx, al ; Write the changed data to PCI port + DEBUGF 1, "K : Vortex86 Host Control Register (A3h~A0h) new value is 0x%x\n",eax + jmp .Vortex86end + +.Vortex86PCIreg: ; Procedure receives input register value in EAX, and returns the output value also in EAX + mov dx, 0xcf8 ; CF8h = Vortex86 PCI Configuration Address port + out dx, eax ; Send request to PCI address port to retrieve data from this address + mov dl, 0xfc ; CFCh = Vortex86 PCI Configuration Data port + in eax, dx ; Read data from PCI data port + ret + +.nullPCIoutput: ; Emulators and non-Vortex86 CPU computers will usually return \0 in this register + DEBUGF 1, "0 (NULL)\n" + +.Vortex86end: diff --git a/kernel/branches/kolibri-lldw/docs/apm.txt b/kernel/branches/kolibri-lldw/docs/apm.txt new file mode 100644 index 000000000..5b253195e --- /dev/null +++ b/kernel/branches/kolibri-lldw/docs/apm.txt @@ -0,0 +1,518 @@ +--------p-155300----------------------------- +INT 15 - Advanced Power Management v1.0+ - INSTALLATION CHECK + AX = 5300h + BX = device ID of system BIOS (0000h) +Return: CF clear if successful + AH = major version (BCD) + AL = minor version (BCD) + BX = 504Dh ("PM") + CX = flags (see #00472) + CF set on error + AH = error code (06h,09h,86h) (see #00473) +BUG: early versions of the Award Modular BIOS with built-in APM support + reportedly do not set BX on return + +Bitfields for APM flags: +Bit(s) Description (Table 00472) + 0 16-bit protected mode interface supported + 1 32-bit protected mode interface supported + 2 CPU idle call reduces processor speed + 3 BIOS power management disabled + 4 BIOS power management disengaged (APM v1.1) + 5-7 reserved + +(Table 00473) +Values for APM error code: + 01h power management functionality disabled + 02h interface connection already in effect + 03h interface not connected + 04h real-mode interface not connected + 05h 16-bit protected-mode interface already connected + 06h 16-bit protected-mode interface not supported + 07h 32-bit protected-mode interface already connected + 08h 32-bit protected-mode interface not supported + 09h unrecognized device ID + 0Ah invalid parameter value in CX + 0Bh (APM v1.1) interface not engaged + 0Ch (APM v1.2) function not supported + 0Dh (APM v1.2) Resume Timer disabled + 0Eh-1Fh reserved for other interface and general errors + 20h-3Fh reserved for CPU errors + 40h-5Fh reserved for device errors + 60h can't enter requested state + 61h-7Fh reserved for other system errors + 80h no power management events pending + 81h-85h reserved for other power management event errors + 86h APM not present + 87h-9Fh reserved for other power management event errors + A0h-FEh reserved + FFh undefined +--------p-155301----------------------------- +INT 15 - Advanced Power Management v1.0+ - CONNECT REAL-MODE INTERFACE + AX = 5301h + BX = device ID of system BIOS (0000h) +Return: CF clear if successful + CF set on error + AH = error code (02h,05h,07h,09h) (see #00473) +Note: on connection, an APM v1.1 or v1.2 BIOS switches to APM v1.0 + compatibility mode until it is informed that the user supports a + newer version of APM (see AX=530Eh) +SeeAlso: AX=5302h,AX=5303h,AX=5304h +--------p-155302----------------------------- +INT 15 R - Advanced Power Management v1.0+ - CONNECT 16-BIT PROTMODE INTERFACE + AX = 5302h + BX = device ID of system BIOS (0000h) +Return: CF clear if successful + AX = real-mode segment base address of protected-mode 16-bit code + segment + BX = offset of entry point + CX = real-mode segment base address of protected-mode 16-bit data + segment + ---APM v1.1--- + SI = APM BIOS code segment length + DI = APM BIOS data segment length + CF set on error + AH = error code (02h,05h,06h,07h,09h) (see #00473) +Notes: the caller must initialize two consecutive descriptors with the + returned segment base addresses; these descriptors must be valid + whenever the protected-mode interface is called, and will have + their limits arbitrarily set to 64K. + the protected mode interface is invoked by making a far call with the + same register values as for INT 15; it must be invoked while CPL=0, + the code segment descriptor must have a DPL of 0, the stack must be + in a 16-bit segment and have enough room for BIOS use and possible + interrupts, and the current I/O permission bit map must allow access + to the I/O ports used for power management. + functions 00h-03h are not available from protected mode + on connection, an APM v1.1 or v1.2 BIOS switches to APM v1.0 + compatibility mode until it is informed that the user supports a + newer version of APM (see AX=530Eh) +SeeAlso: AX=5301h,AX=5303h,AX=5304h +--------p-155303----------------------------- +INT 15 - Advanced Power Management v1.0+ - CONNECT 32-BIT PROTMODE INTERFACE + AX = 5303h + BX = device ID of system BIOS (0000h) +Return: CF clear if successful + AX = real-mode segment base address of protected-mode 32-bit code + segment + EBX = offset of entry point + CX = real-mode segment base address of protected-mode 16-bit code + segment + DX = real-mode segment base address of protected-mode 16-bit data + segment + ---APM v1.1--- + SI = APM BIOS code segment length + DI = APM BIOS data segment length + CF set on error + AH = error code (02h,05h,07h,08h,09h) (see #00473) +Notes: the caller must initialize three consecutive descriptors with the + returned segment base addresses for 32-bit code, 16-bit code, and + 16-bit data, respectively; these descriptors must be valid whenever + the protected-mode interface is called, and will have their limits + arbitrarily set to 64K. + the protected mode interface is invoked by making a far call to the + 32-bit code segment with the same register values as for INT 15; it + must be invoked while CPL=0, the code segment descriptor must have a + DPL of 0, the stack must be in a 32-bit segment and have enough room + for BIOS use and possible interrupts, and the current I/O permission + bit map must allow access to the I/O ports used for power management. + functions 00h-03h are not available from protected mode + on connection, an APM v1.1 or v1.2 BIOS switches to APM v1.0 + compatibility mode until it is informed that the user supports a + newer version of APM (see AX=530Eh) +SeeAlso: AX=5301h,AX=5302h,AX=5304h +--------p-155304----------------------------- +INT 15 - Advanced Power Management v1.0+ - DISCONNECT INTERFACE + AX = 5304h + BX = device ID of system BIOS (0000h) +Return: CF clear if successful + CF set on error + AH = error code (03h,09h) (see #00473) +SeeAlso: AX=5301h,AX=5302h,AX=5303h +--------p-155305----------------------------- +INT 15 - Advanced Power Management v1.0+ - CPU IDLE + AX = 5305h +Return: CF clear if successful (after system leaves idle state) + CF set on error + AH = error code (03h,0Bh) (see #00473) +Notes: call when the system is idle and should be suspended until the next + system event or interrupt + should not be called from within a hardware interrupt handler to avoid + reentrance problems + if an interrupt causes the system to resume normal processing, the + interrupt may or may not have been handled when the BIOS returns + from this call; thus, the caller should allow interrupts on return + interrupt handlers may not retain control if the BIOS allows + interrupts while in idle mode even if they are able to determine + that they were called from idle mode + the caller should issue this call continuously in a loop until it needs + to perform some processing of its own +SeeAlso: AX=1000h,AX=5306h,INT 2F/AX=1680h +--------p-155306----------------------------- +INT 15 - Advanced Power Management v1.0+ - CPU BUSY + AX = 5306h +Return: CF clear if successful + CF set on error + AH = error code (03h,0Bh) (see #00473) +Notes: called to ensure that the system runs at full speed even on systems + where the BIOS is unable to recognize increased activity (especially + if interrupts are hooked by other programs and not chained to the + BIOS) + this call may be made even when the system is already running at full + speed, but it will create unnecessary overhead + should not be called from within a hardware interrupt handler to avoid + reentrance problems +SeeAlso: AX=5305h +--------p-155307----------------------------- +INT 15 - Advanced Power Management v1.0+ - SET POWER STATE + AX = 5307h + BX = device ID (see #00474) + CX = system state ID (see #00475) +Return: CF clear if successful + CF set on error + AH = error code (01h,03h,09h,0Ah,0Bh,60h) (see #00473) +Note: should not be called from within a hardware interrupt handler to avoid + reentrance problems +SeeAlso: AX=530Ch + +(Table 00474) +Values for APM device IDs: + 0000h system BIOS + 0001h all devices for which the system BIOS manages power + 01xxh display (01FFh for all attached display devices) + 02xxh secondary storage (02FFh for all attached secondary storage devices) + 03xxh parallel ports (03FFh for all attached parallel ports) + 04xxh serial ports (04FFh for all attached serial ports) +---APM v1.1+ --- + 05xxh network adapters (05FFh for all attached network adapters) + 06xxh PCMCIA sockets (06FFh for all) + 0700h-7FFFh reserved + 80xxh system battery devices (APM v1.2) + 8100h-DFFFh reserved + Exxxh OEM-defined power device IDs + F000h-FFFFh reserved + +(Table 00475) +Values for system state ID: + 0000h ready (not supported for device ID 0001h) + 0001h stand-by + 0002h suspend + 0003h off (not supported for device ID 0001h in APM v1.0) +---APM v1.1--- + 0004h last request processing notification (only for device ID 0001h) + 0005h last request rejected (only for device ID 0001h) + 0006h-001Fh reserved system states + 0020h-003Fh OEM-defined system states + 0040h-007Fh OEM-defined device states + 0080h-FFFFh reserved device states +--------p-155307CX0001----------------------- +INT 15 - Advanced Power Management v1.0+ - SYSTEM STAND-BY + AX = 5307h + CX = 0001h + BX = 0001h (device ID for all power-managed devices) +Return: CF clear +Notes: puts the entire system into stand-by mode; normally called in response + to a System Stand-by Request notification after any necessary + processing, but may also be invoked at the caller's discretion + should not be called from within a hardware interrupt handler to avoid + reentrance problems + the stand-by state is typically exited on an interrupt +SeeAlso: AX=4280h,AX=5307h/CX=0002h"SUSPEND",AX=5307h/CX=0003h,AX=530Bh +--------p-155307CX0002----------------------- +INT 15 - Advanced Power Management v1.0+ - SUSPEND SYSTEM + AX = 5307h + CX = 0002h + BX = 0001h (device ID for all power-managed devices) +Return: after system is resumed + CF clear +Notes: puts the entire system into a low-power suspended state; normally + called in response to a Suspend System Request notification after + any necessary processing, but may also be invoked at the caller's + discretion + should not be called from within a hardware interrupt handler to avoid + reentrance problems + the caller may need to update its date and time values because the + system could have been suspended for a long period of time +SeeAlso: AX=5307h/CX=0001h"STAND-BY",AX=530Bh +--------p-155307CX0003----------------------- +INT 15 - Advanced Power Management v1.2 - TURN OFF SYSTEM + AX = 5307h + CX = 0003h + BX = 0001h (device ID for all power-managed devices) +Return: after system is resumed + CF clear +Notes: if supported by the system's power supply, turns off the system power +SeeAlso: AX=5307h/CX=0001h"STAND-BY",AX=530Bh +--------p-155308----------------------------- +INT 15 - Advanced Power Management v1.0+ - ENABLE/DISABLE POWER MANAGEMENT + AX = 5308h + BX = device ID for all devices power-managed by APM + 0001h (APM v1.1+) + FFFFh (APM v1.0) + CX = new state + 0000h disabled + 0001h enabled +Return: CF clear if successful + CF set on error + AH = error code (01h,03h,09h,0Ah,0Bh) (see #00473) +Notes: when power management is disabled, the system BIOS will not + automatically power down devices, enter stand-by or suspended mode, + or perform any power-saving actions in response to AX=5305h calls + should not be called from within a hardware interrupt handler to avoid + reentrance problems + the APM BIOS should never be both disabled and disengaged at the same + time +SeeAlso: AX=5309h,AX=530Dh,AX=530Fh +--------p-155309----------------------------- +INT 15 - Advanced Power Management v1.0+ - RESTORE POWER-ON DEFAULTS + AX = 5309h + BX = device ID for all devices power-managed by APM + 0001h (APM v1.1) + FFFFh (APM v1.0) +Return: CF clear if successful + CF set on error + AH = error code (03h,09h,0Bh) (see #00473) +Note: should not be called from within a hardware interrupt handler to avoid + reentrance problems +SeeAlso: AX=5308h +--------p-15530A----------------------------- +INT 15 - Advanced Power Management v1.0+ - GET POWER STATUS + AX = 530Ah + BX = device ID + 0001h all devices power-managed by APM + 80xxh specific battery unit number XXh (01h-FFh) (APM v1.2) +Return: CF clear if successful + BH = AC line status + 00h off-line + 01h on-line + 02h on backup power (APM v1.1) + FFh unknown + other reserved + BL = battery status (see #00476) + CH = battery flag (APM v1.1+) (see #00477) + CL = remaining battery life, percentage + 00h-64h (0-100) percentage of full charge + FFh unknown + DX = remaining battery life, time (APM v1.1) (see #00478) + ---if specific battery unit specified--- + SI = number of battery units currently installed + CF set on error + AH = error code (09h,0Ah) (see #00473) +Notes: should not be called from within a hardware interrupt handler to avoid + reentrance problems + supported in real mode (INT 15) and both 16-bit and 32-bit protected + mode + +(Table 00476) +Values for APM v1.0+ battery status: + 00h high + 01h low + 02h critical + 03h charging + FFh unknown + other reserved +SeeAlso: #00477,#00478 + +Bitfields for APM v1.1+ battery flag: +Bit(s) Description (Table 00477) + 0 high + 1 low + 2 critical + 3 charging + 4 selected battery not present (APM v1.2) + 5-6 reserved (0) + 7 no system battery +Note: all bits set (FFh) if unknown +SeeAlso: #00476,#00478 + +Bitfields for APM v1.1+ remaining battery life: +Bit(s) Description (Table 00478) + 15 time units: 0=seconds, 1=minutes + 14-0 battery life in minutes or seconds +Note: all bits set (FFFFh) if unknown +SeeAlso: #00476,#00477 +--------p-15530B----------------------------- +INT 15 - Advanced Power Management v1.0+ - GET POWER MANAGEMENT EVENT + AX = 530Bh +Return: CF clear if successful + BX = event code (see #00479) + CX = event information (APM v1.2) if BX=0003h or BX=0004h + bit 0: PCMCIA socket was powered down in suspend state + CF set on error + AH = error code (03h,0Bh,80h) (see #00473) +Notes: although power management events are often asynchronous, notification + will not be made until polled via this call to permit software to + only receive event notification when it is prepared to process + power management events; since these events are not very time- + critical, it should be sufficient to poll once or twice per second + the critical resume notification is made after the system resumes + from an emergency suspension; normally, the system BIOS only notifies + its partner that it wishes to suspend and relies on the partner to + actually request the suspension, but no notification is made on an + emergency suspension + should not be called from within a hardware interrupt handler to avoid + reentrance problems +SeeAlso: AX=5307h,AX=5307h/CX=0001h"STAND-BY",AX=5307h/CX=0002h"SUSPEND" + +(Table 00479) +Values for APM event code: + 0001h system stand-by request + 0002h system suspend request + 0003h normal resume system notification + 0004h critical resume system notification + 0005h battery low notification +---APM v1.1--- + 0006h power status change notification + 0007h update time notification + 0008h critical system suspend notification + 0009h user system standby request notification + 000Ah user system suspend request notification + 000Bh system standby resume notification +---APM v1.2--- + 000Ch capabilities change notification (see AX=5310h) +------ + 000Dh-00FFh reserved system events + 01xxh reserved device events + 02xxh OEM-defined APM events + 0300h-FFFFh reserved +--------p-15530C----------------------------- +INT 15 - Advanced Power Management v1.1+ - GET POWER STATE + AX = 530Ch + BX = device ID (see #00474) +Return: CF clear if successful + CX = system state ID (see #00475) + CF set on error + AH = error code (01h,09h) (see #00473) +SeeAlso: AX=5307h +--------p-15530D----------------------------- +INT 15 - Advanced Power Management v1.1+ - EN/DISABLE DEVICE POWER MANAGEMENT + AX = 530Dh + BX = device ID (see #00474) + CX = function + 0000h disable power management + 0001h enable power management +Return: CF clear if successful + CF set on error + AH = error code (01h,03h,09h,0Ah,0Bh) (see #00473) +Desc: specify whether automatic power management should be active for a + given device +SeeAlso: AX=5308h,AX=530Fh +--------p-15530E----------------------------- +INT 15 - Advanced Power Management v1.1+ - DRIVER VERSION + AX = 530Eh + BX = device ID of system BIOS (0000h) + CH = APM driver major version (BCD) + CL = APM driver minor version (BCD) (02h for APM v1.2) +Return: CF clear if successful + AH = APM connection major version (BCD) + AL = APM connection minor version (BCD) + CF set on error + AH = error code (03h,09h,0Bh) (see #00473) +SeeAlso: AX=5300h,AX=5303h +--------p-15530F----------------------------- +INT 15 - Advanced Power Management v1.1+ - ENGAGE/DISENGAGE POWER MANAGEMENT + AX = 530Fh + BX = device ID (see #00474) + CX = function + 0000h disengage power management + 0001h engage power management +Return: CF clear if successful + CF set on error + AH = error code (01h,09h) (see #00473) +Notes: unlike AX=5308h, this call does not affect the functioning of the APM + BIOS + when cooperative power management is disengaged, the APM BIOS performs + automatic power management of the system or device +SeeAlso: AX=5308h,AX=530Dh +--------p-155310----------------------------- +INT 15 - Advanced Power Management v1.2 - GET CAPABILITIES + AX = 5310h + BX = device ID (see #00474) + 0000h (APM BIOS) + other reserved +Return: CF clear if successful + BL = number of battery units supported (00h if no system batteries) + CX = capabilities flags (see #00480) + CF set on error + AH = error code (01h,09h,86h) (see #00473) +Notes: this function is supported via the INT 15, 16-bit protected mode, and + 32-bit protected mode interfaces; it does not require that a + connection be established prior to use + this function will return the capabilities currently in effect, not + any new settings which have been made but do not take effect until + a system restart +SeeAlso: AX=5300h,AX=530Fh,AX=5311h,AX=5312h,AX=5313h + +Bitfields for APM v1.2 capabilities flags: +Bit(s) Description (Table 00480) + 15-8 reserved + 7 PCMCIA Ring Indicator will wake up system from suspend mode + 6 PCMCIA Ring Indicator will wake up system from standby mode + 5 Resume on Ring Indicator will wake up system from suspend mode + 4 Resume on Ring Indicator will wake up system from standby mode + 3 resume timer will wake up system from suspend mode + 2 resume timer will wake up system from standby mode + 1 can enter global suspend state + 0 can enter global standby state +--------p-155311----------------------------- +INT 15 - Advanced Power Management v1.2 - GET/SET/DISABLE RESUME TIMER + AX = 5311h + BX = device ID (see #00474) + 0000h (APM BIOS) + other reserved + CL = function + 00h disable Resume Timer + 01h get Resume Timer + 02h set Resume Timer + CH = resume time, seconds (BCD) + DL = resume time, minutes (BCD) + DH = resume time, hours (BCD) + SI = resume date (BCD), high byte = month, low byte = day + DI = resume date, year (BCD) +Return: CF clear if successful + ---if getting timer--- + CH = resume time, seconds (BCD) + DL = resume time, minutes (BCD) + DH = resume time, hours (BCD) + SI = resume date (BCD), high byte = month, low byte = day + DI = resume date, year (BCD) + CF set on error + AH = error code (03h,09h,0Ah,0Bh,0Ch,0Dh,86h) (see #00473) +Notes: this function is supported via the INT 15, 16-bit protected mode, and + 32-bit protected mode interfaces +SeeAlso: AX=5300h,AX=5310h,AX=5312h,AX=5313h +--------p-155312----------------------------- +INT 15 - Advanced Power Management v1.2 - ENABLE/DISABLE RESUME ON RING + AX = 5312h + BX = device ID (see #00474) + 0000h (APM BIOS) + other reserved + CL = function + 00h disable Resume on Ring Indicator + 01h enable Resume on Ring Indicator + 02h get Resume on Ring Indicator status +Return: CF clear if successful + CX = resume status (0000h disabled, 0001h enabled) + CF set on error + AH = error code (03h,09h,0Ah,0Bh,0Ch,86h) (see #00473) +Notes: this function is supported via the INT 15, 16-bit protected mode, and + 32-bit protected mode interfaces +SeeAlso: AX=5300h,AX=5310h,AX=5311h,AX=5313h +--------p-155313----------------------------- +INT 15 - Advanced Power Management v1.2 - ENABLE/DISABLE TIMER-BASED REQUESTS + AX = 5313h + BX = device ID (see #00474) + 0000h (APM BIOS) + other reserved + CL = function + 00h disable timer-based requests + 01h enable timer-based requests + 02h get timer-based requests status +Return: CF clear if successful + CX = timer-based requests status (0000h disabled, 0001h enabled) + CF set on error + AH = error code (03h,09h,0Ah,0Bh,86h) (see #00473) +Notes: this function is supported via the INT 15, 16-bit protected mode, and + 32-bit protected mode interfaces + some BIOSes set AH on return even when successful +SeeAlso: AX=5300h,AX=5310h,AX=5311h,AX=5312h diff --git a/kernel/branches/kolibri-lldw/docs/doxygen/doxygen.cfg b/kernel/branches/kolibri-lldw/docs/doxygen/doxygen.cfg new file mode 100644 index 000000000..8ea1e6b2e --- /dev/null +++ b/kernel/branches/kolibri-lldw/docs/doxygen/doxygen.cfg @@ -0,0 +1,2612 @@ +# Doxyfile 1.9.1 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = "KolibriOS kernel" + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- +# directories (in 2 levels) under the output directory of each output format and +# will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, +# Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), +# Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, +# Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), +# Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, +# Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, +# Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, +# Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all generated output in the proper direction. +# Possible values are: None, LTR, RTL and Context. +# The default value is: None. + +OUTPUT_TEXT_DIRECTION = None + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = YES + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:\n" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". You can put \n's in the value part of an alias to insert +# newlines (in the resulting output). You can put ^^ in the value part of an +# alias to insert a newline as if a physical newline was in the original file. +# When you need a literal { or } or , in the value part of an alias you have to +# escape them by means of a backslash (\), this can lead to conflicts with the +# commands \{ and \} for these it is advised to use the version @{ and @} or use +# a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = NO + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, VHDL, +# Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = inc=C asm=C + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = NO + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which efficively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = YES + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# The default value is: system dependent. + +CASE_SENSE_NAMES = NO + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = NO + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as not documenting some parameters +# in a documented function, or documenting parameters that don't exist or using +# markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong or incomplete +# parameter documentation, but not about the absence of documentation. If +# EXTRACT_ALL is set to YES then this flag will automatically be disabled. +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# Possible values are: NO, YES and FAIL_ON_WARNINGS. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, +# *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, +# *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, +# *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), +# *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, *.vhdl, +# *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.inc *.asm + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = YES + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# AClass::ANamespace, ANamespace::*Test +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories use the pattern */test/* + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# classes and enums directly into the documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = YES + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If clang assisted parsing is enabled and the CLANG_ADD_INC_PATHS tag is set to +# YES then doxygen will add the directory of each input to the include path. +# The default value is: YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# In case all classes in a project start with a common prefix, all classes will +# be put under the same header in the alphabetical index. The IGNORE_PREFIX tag +# can be used to specify a prefix (or a list of prefixes) that should be ignored +# while generating the index headers. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). For an example see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a colorwheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use grayscales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML +# page will contain the date and time when the page was generated. Setting this +# to YES can help to show when doxygen was last run and thus if the +# documentation is up to date. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_TIMESTAMP = NO + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# (see: +# https://www.microsoft.com/en-us/download/details.aspx?id=21138) on Windows. +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = YES + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine-tune the look of the index. As an example, the default style +# sheet generated by doxygen has an example that shows how to put an image at +# the root of the tree instead of the PROJECT_NAME. Since the tree basically has +# the same information as the tab index, you could consider setting +# DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# Use the FORMULA_TRANSPARENT tag to determine whether or not the images +# generated for formulas are transparent PNGs. Transparent PNGs are not +# supported properly for IE 6.0, but are supported on all modern browsers. +# +# Note that when changing this option you need to delete any form_*.png files in +# the HTML output directory before the changes have effect. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_TRANSPARENT = YES + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. See the MathJax site (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility), NativeMML (i.e. MathML) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. +# The default value is: https://cdn.jsdelivr.net/npm/mathjax@2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = https://cdn.jsdelivr.net/npm/mathjax@2 + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /