7ade99b96d34d7ebb393b9454cf4ee60e93d6166
[people/cooldavid/gpxe.git] / src / arch / i386 / prefix / romprefix.S
1 /* At entry, the processor is in 16 bit real mode and the code is being
2  * executed from an address it was not linked to. Code must be pic and
3  * 32 bit sensitive until things are fixed up.
4  *
5  * Also be very careful as the stack is at the rear end of the interrupt
6  * table so using a noticeable amount of stack space is a no-no.
7  */
8
9 FILE_LICENCE ( GPL2_OR_LATER )
10
11 #include <config/general.h>
12
13 #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) )
14 #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) )
15 #define PCI_SIGNATURE ( 'P' + ( 'C' << 8 ) + ( 'I' << 16 ) + ( ' ' << 24 ) )
16 #define STACK_MAGIC ( 'L' + ( 'R' << 8 ) + ( 'E' << 16 ) + ( 'T' << 24 ) )
17 #define PNP_GET_BBS_VERSION 0x60
18 #define PMM_ALLOCATE 0x0000
19 #define PMM_DEALLOCATE 0x0002
20
21 /* ROM banner timeout.  Based on the configurable BANNER_TIMEOUT in
22  * config.h, but converted to a number of (18Hz) timer ticks, and
23  * doubled to allow for BIOSes that switch video modes immediately
24  * beforehand, so rendering the message almost invisible to the user.
25  */
26 #define ROM_BANNER_TIMEOUT ( 2 * ( 18 * BANNER_TIMEOUT ) / 10 )
27
28         .text
29         .code16
30         .arch i386
31         .section ".prefix", "ax", @progbits
32         
33         .org    0x00
34 romheader:
35         .word   0xAA55                  /* BIOS extension signature */
36 romheader_size: .byte 0                 /* Size in 512-byte blocks */
37         jmp     init                    /* Initialisation vector */
38 checksum:
39         .byte   0
40         .org    0x16
41         .word   undiheader
42         .org    0x18
43         .word   pciheader
44         .org    0x1a
45         .word   pnpheader
46         .size romheader, . - romheader
47
48         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
49         .ascii  "ADDB"
50         .long   romheader_size
51         .long   512
52         .long   0
53         .previous
54
55 build_id:
56         .long   _build_id               /* Randomly-generated build ID */
57
58 pciheader:
59         .ascii  "PCIR"                  /* Signature */
60         .word   pci_vendor_id           /* Vendor identification */ 
61         .word   pci_device_id           /* Device identification */
62         .word   0x0000                  /* Device list pointer */
63         .word   pciheader_len           /* PCI data structure length */
64         .byte   0x03                    /* PCI data structure revision */
65         .byte   0x02, 0x00, 0x00        /* Class code */
66 pciheader_image_length:
67         .word   0                       /* Image length */
68         .word   0x0001                  /* Revision level */
69         .byte   0x00                    /* Code type */
70         .byte   0x80                    /* Last image indicator */
71 pciheader_runtime_length:
72         .word   0                       /* Maximum run-time image length */
73         .word   0x0000                  /* Configuration utility code header */
74         .word   0x0000                  /* DMTF CLP entry point */
75         .equ pciheader_len, . - pciheader
76         .size pciheader, . - pciheader
77
78         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
79         .ascii  "ADDW"
80         .long   pciheader_image_length
81         .long   512
82         .long   0
83         .ascii  "ADDW"
84         .long   pciheader_runtime_length
85         .long   512
86         .long   0
87         .previous
88
89 pnpheader:
90         .ascii  "$PnP"                  /* Signature */
91         .byte   0x01                    /* Structure revision */
92         .byte   ( pnpheader_len / 16 )  /* Length (in 16 byte increments) */
93         .word   0x0000                  /* Offset of next header */
94         .byte   0x00                    /* Reserved */
95         .byte   0x00                    /* Checksum */
96         .long   0x00000000              /* Device identifier */
97         .word   mfgstr                  /* Manufacturer string */
98         .word   prodstr                 /* Product name */
99         .byte   0x02                    /* Device base type code */
100         .byte   0x00                    /* Device sub-type code */
101         .byte   0x00                    /* Device interface type code */
102         .byte   0xf4                    /* Device indicator */
103         .word   0x0000                  /* Boot connection vector */
104         .word   0x0000                  /* Disconnect vector */
105         .word   bev_entry               /* Boot execution vector */
106         .word   0x0000                  /* Reserved */
107         .word   0x0000                  /* Static resource information vector*/
108         .equ pnpheader_len, . - pnpheader
109         .size pnpheader, . - pnpheader
110
111 /* Manufacturer string */
112 mfgstr:
113         .asciz  "http://etherboot.org"
114         .size mfgstr, . - mfgstr
115
116 /* Product string
117  *
118  * Defaults to PRODUCT_SHORT_NAME.  If the ROM image is writable at
119  * initialisation time, it will be filled in to include the PCI
120  * bus:dev.fn number of the card as well.
121  */
122 prodstr:
123         .ascii  PRODUCT_SHORT_NAME
124 prodstr_separator:
125         .byte   0
126         .ascii  "(PCI "
127 prodstr_pci_id:
128         .asciz  "xx:xx.x)"              /* Filled in by init code */
129         .size prodstr, . - prodstr
130
131         .globl  undiheader      
132         .weak   undiloader
133 undiheader:
134         .ascii  "UNDI"                  /* Signature */
135         .byte   undiheader_len          /* Length of structure */
136         .byte   0                       /* Checksum */
137         .byte   0                       /* Structure revision */
138         .byte   0,1,2                   /* PXE version: 2.1.0 */
139         .word   undiloader              /* Offset to loader routine */
140         .word   _data16_memsz           /* Stack segment size */
141         .word   _data16_memsz           /* Data segment size */
142         .word   _text16_memsz           /* Code segment size */
143         .ascii  "PCIR"                  /* Bus type */
144         .equ undiheader_len, . - undiheader
145         .size undiheader, . - undiheader
146
147 /* Initialisation (called once during POST)
148  *
149  * Determine whether or not this is a PnP system via a signature
150  * check.  If it is PnP, return to the PnP BIOS indicating that we are
151  * a boot-capable device; the BIOS will call our boot execution vector
152  * if it wants to boot us.  If it is not PnP, hook INT 19.
153  */
154 init:
155         /* Preserve registers, clear direction flag, set %ds=%cs */
156         pushaw
157         pushw   %ds
158         pushw   %es
159         pushw   %fs
160         pushw   %gs
161         cld
162         pushw   %cs
163         popw    %ds
164
165         /* Shuffle some registers around.  We need %di available for
166          * the print_xxx functions, and in a register that's
167          * addressable from %es, so shuffle as follows:
168          *
169          *    %di (pointer to PnP structure) => %bx
170          *    %bx (runtime segment address, for PCI 3.0) => %gs
171          */
172         movw    %bx, %gs
173         movw    %di, %bx
174
175         /* Print message as early as possible */
176         movw    $init_message, %si
177         xorw    %di, %di
178         call    print_message
179         call    print_pci_busdevfn
180
181         /* Fill in product name string, if possible */
182         movw    $prodstr_pci_id, %di
183         call    print_pci_busdevfn
184         movb    $( ' ' ), prodstr_separator
185
186         /* Print segment address */
187         movb    $( ' ' ), %al
188         xorw    %di, %di
189         call    print_character
190         movw    %cs, %ax
191         call    print_hex_word
192
193         /* Check for PCI BIOS version */
194         pushl   %ebx
195         pushl   %edx
196         pushl   %edi
197         stc
198         movw    $0xb101, %ax
199         int     $0x1a
200         jc      no_pci3
201         cmpl    $PCI_SIGNATURE, %edx
202         jne     no_pci3
203         testb   %ah, %ah
204         jnz     no_pci3
205         movw    $init_message_pci, %si
206         xorw    %di, %di
207         call    print_message
208         movb    %bh, %al
209         call    print_hex_nibble
210         movb    $( '.' ), %al
211         call    print_character
212         movb    %bl, %al
213         call    print_hex_byte
214         cmpb    $3, %bh
215         jb      no_pci3
216         /* PCI >=3.0: leave %gs as-is if sane */
217         movw    %gs, %ax
218         cmpw    $0xa000, %ax    /* Insane if %gs < 0xa000 */
219         jb      pci3_insane
220         movw    %cs, %bx        /* Sane if %cs == %gs */
221         cmpw    %bx, %ax
222         je      1f
223         movzbw  romheader_size, %cx /* Sane if %cs+len <= %gs */
224         shlw    $5, %cx
225         addw    %cx, %bx
226         cmpw    %bx, %ax
227         jae     1f
228         movw    %cs, %bx        /* Sane if %gs+len <= %cs */
229         addw    %cx, %ax
230         cmpw    %bx, %ax
231         jbe     1f
232 pci3_insane: /* PCI 3.0 with insane %gs value: print error and ignore %gs */
233         movb    $( '!' ), %al
234         call    print_character
235         movw    %gs, %ax
236         call    print_hex_word
237 no_pci3:
238         /* PCI <3.0: set %gs (runtime segment) = %cs (init-time segment) */
239         pushw   %cs
240         popw    %gs
241 1:      popl    %edi
242         popl    %edx
243         popl    %ebx
244
245         /* Check for PnP BIOS.  Although %es:di should point to the
246          * PnP BIOS signature on entry, some BIOSes fail to do this.
247          */
248         movw    $( 0xf000 - 1 ), %bx
249 pnp_scan:
250         incw    %bx
251         jz      no_pnp
252         movw    %bx, %es
253         cmpl    $PNP_SIGNATURE, %es:0
254         jne     pnp_scan
255         xorw    %dx, %dx
256         xorw    %si, %si
257         movzbw  %es:5, %cx
258 1:      es lodsb
259         addb    %al, %dl
260         loop    1b
261         jnz     pnp_scan
262         /* Is PnP: print PnP message */
263         movw    $init_message_pnp, %si
264         xorw    %di, %di
265         call    print_message
266         /* Check for BBS */
267         pushw   %es:0x1b        /* Real-mode data segment */
268         pushw   %ds             /* &(bbs_version) */
269         pushw   $bbs_version
270         pushw   $PNP_GET_BBS_VERSION
271         lcall   *%es:0xd
272         addw    $8, %sp
273         testw   %ax, %ax
274         je      got_bbs
275 no_pnp: /* Not PnP-compliant - therefore cannot be BBS-compliant */
276 no_bbs: /* Not BBS-compliant - must hook INT 19 */
277         movw    $init_message_int19, %si
278         xorw    %di, %di
279         call    print_message
280         xorw    %ax, %ax
281         movw    %ax, %es
282         pushl   %es:( 0x19 * 4 )
283         popl    orig_int19
284         pushw   %gs /* %gs contains runtime %cs */
285         pushw   $int19_entry
286         popl    %es:( 0x19 * 4 )
287         jmp     bbs_done
288 got_bbs: /* BBS compliant - no need to hook INT 19 */
289         movw    $init_message_bbs, %si
290         xorw    %di, %di
291         call    print_message
292 bbs_done:
293
294         /* Check for PMM */
295         movw    $( 0xe000 - 1 ), %bx
296 pmm_scan:
297         incw    %bx
298         jz      no_pmm
299         movw    %bx, %es
300         cmpl    $PMM_SIGNATURE, %es:0
301         jne     pmm_scan
302         xorw    %dx, %dx
303         xorw    %si, %si
304         movzbw  %es:5, %cx
305 1:      es lodsb
306         addb    %al, %dl
307         loop    1b
308         jnz     pmm_scan
309         /* PMM found: print PMM message */
310         movw    $init_message_pmm, %si
311         xorw    %di, %di
312         call    print_message
313         /* We have PMM and so a 1kB stack: preserve upper register halves */
314         pushal
315         /* Calculate required allocation size in %esi */
316         movzbl  romheader_size, %eax
317         shll    $9, %eax
318         addl    $_textdata_memsz, %eax
319         orw     $0xffff, %ax    /* Ensure allocation size is at least 64kB */
320         bsrl    %eax, %ecx
321         subw    $15, %cx        /* Round up and convert to 64kB count */
322         movw    $1, %si
323         shlw    %cl, %si
324 pmm_loop:
325         /* Try to allocate block via PMM */
326         pushw   $0x0006         /* Aligned, extended memory */
327         pushl   $0xffffffff     /* No handle */
328         movzwl  %si, %eax
329         shll    $12, %eax
330         pushl   %eax            /* Allocation size in paragraphs */
331         pushw   $PMM_ALLOCATE
332         lcall   *%es:7
333         addw    $12, %sp
334         /* Abort if allocation fails */
335         testw   %dx, %dx        /* %ax==0 even on success, since align>=64kB */
336         jz      pmm_fail
337         /* If block has A20==1, free block and try again with twice
338          * the allocation size (and hence alignment).
339          */
340         testw   $0x0010, %dx
341         jz      got_pmm
342         pushw   %dx
343         pushw   $0
344         pushw   $PMM_DEALLOCATE
345         lcall   *%es:7
346         addw    $6, %sp
347         addw    %si, %si
348         jmp     pmm_loop
349 got_pmm: /* PMM allocation succeeded */
350         movw    %dx, ( image_source + 2 )
351         movw    %dx, %ax
352         xorw    %di, %di
353         call    print_hex_word
354         movb    $( '@' ), %al
355         call    print_character
356         movw    %si, %ax
357         call    print_hex_byte
358         /* Copy ROM to PMM block */
359         xorw    %ax, %ax
360         movw    %ax, %es
361         movl    image_source, %edi
362         xorl    %esi, %esi
363         movzbl  romheader_size, %ecx
364         shll    $9, %ecx
365         addr32 rep movsb        /* PMM presence implies flat real mode */
366         movl    %edi, decompress_to
367         /* Shrink ROM */
368         movb    shrunk_rom_size, %al
369         movb    %al, romheader_size
370 pmm_fail:
371         /* Restore upper register halves */
372         popal
373 no_pmm:
374
375         /* Update checksum */
376         xorw    %bx, %bx
377         xorw    %si, %si
378         movzbw  romheader_size, %cx
379         shlw    $9, %cx
380 1:      lodsb
381         addb    %al, %bl
382         loop    1b
383         subb    %bl, checksum
384
385         /* Copy self to option ROM space.  Required for PCI3.0, which
386          * loads us to a temporary location in low memory.  Will be a
387          * no-op for lower PCI versions.
388          */
389         movb    $( ' ' ), %al
390         xorw    %di, %di
391         call    print_character
392         movw    %gs, %ax
393         call    print_hex_word
394         movzbw  romheader_size, %cx
395         shlw    $9, %cx
396         movw    %ax, %es
397         xorw    %si, %si
398         xorw    %di, %di
399         cs rep  movsb
400
401         /* Prompt for POST-time shell */
402         movw    $init_message_prompt, %si
403         xorw    %di, %di
404         call    print_message
405         movw    $prodstr, %si
406         call    print_message
407         movw    $init_message_dots, %si
408         call    print_message
409         /* Wait for Ctrl-B */
410         movw    $0xff02, %bx
411         call    wait_for_key
412         /* Clear prompt */
413         pushf
414         xorw    %di, %di
415         call    print_kill_line
416         movw    $init_message_done, %si
417         call    print_message
418         popf
419         jnz     2f
420         /* Ctrl-B was pressed: invoke gPXE.  The keypress will be
421          * picked up by the initial shell prompt, and we will drop
422          * into a shell.
423          */
424         pushw   %cs
425         call    exec
426 2:
427         /* Restore registers */
428         popw    %gs
429         popw    %fs
430         popw    %es
431         popw    %ds
432         popaw
433
434         /* Indicate boot capability to PnP BIOS, if present */
435         movw    $0x20, %ax
436         lret
437         .size init, . - init
438
439 /*
440  * Note to hardware vendors:
441  *
442  * If you wish to brand this boot ROM, please do so by defining the
443  * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h.
444  *
445  * While nothing in the GPL prevents you from removing all references
446  * to gPXE or http://etherboot.org, we prefer you not to do so.
447  *
448  * If you have an OEM-mandated branding requirement that cannot be
449  * satisfied simply by defining PRODUCT_NAME and PRODUCT_SHORT_NAME,
450  * please contact us.
451  *
452  * [ Including an ASCII NUL in PRODUCT_NAME is considered to be
453  *   bypassing the spirit of this request! ]
454  */
455 init_message:
456         .ascii  "\n"
457         .ascii  PRODUCT_NAME
458         .ascii  "\n"
459         .asciz  "gPXE (http://etherboot.org) - "
460         .size   init_message, . - init_message
461 init_message_pci:
462         .asciz  " PCI"
463         .size   init_message_pci, . - init_message_pci
464 init_message_pnp:
465         .asciz  " PnP"
466         .size   init_message_pnp, . - init_message_pnp
467 init_message_bbs:
468         .asciz  " BBS"
469         .size   init_message_bbs, . - init_message_bbs
470 init_message_pmm:
471         .asciz  " PMM"
472         .size   init_message_pmm, . - init_message_pmm
473 init_message_int19:
474         .asciz  " INT19"
475         .size   init_message_int19, . - init_message_int19
476 init_message_prompt:
477         .asciz  "\nPress Ctrl-B to configure "
478         .size   init_message_prompt, . - init_message_prompt
479 init_message_dots:
480         .asciz  "..."
481         .size   init_message_dots, . - init_message_dots
482 init_message_done:
483         .asciz  "\n\n"
484         .size   init_message_done, . - init_message_done
485
486 /* Image source area
487  *
488  * May be either zero (indicating to use option ROM space as source),
489  * or within a PMM-allocated block.
490  */
491         .globl  image_source
492 image_source:
493         .long   0
494         .size   image_source, . - image_source
495
496 /* Shrunk ROM size (in 512-byte sectors)
497  *
498  */
499 shrunk_rom_size:
500         .byte   0
501         .size   shrunk_rom_size, . - shrunk_rom_size
502         .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */
503         .ascii  "ADHB"
504         .long   shrunk_rom_size
505         .long   512
506         .long   0
507         .previous
508
509 /* Temporary decompression area
510  *
511  * May be either zero (indicating to use default decompression area in
512  * high memory), or within a PMM-allocated block.
513  */
514         .globl  decompress_to
515 decompress_to:
516         .long   0
517         .size   decompress_to, . - decompress_to
518
519 /* BBS version
520  *
521  * Filled in by BBS BIOS.  We ignore the value.
522  */
523 bbs_version:
524         .word   0
525         .size   bbs_version, . - bbs_version
526
527 /* Boot Execution Vector entry point
528  *
529  * Called by the PnP BIOS when it wants to boot us.
530  */
531 bev_entry:
532         pushw   %cs
533         call    exec
534         lret
535         .size   bev_entry, . - bev_entry
536
537 /* INT19 entry point
538  *
539  * Called via the hooked INT 19 if we detected a non-PnP BIOS.  We
540  * attempt to return via the original INT 19 vector (if we were able
541  * to store it).
542  */
543 int19_entry:
544         pushw   %cs
545         popw    %ds
546         /* Prompt user to press B to boot */
547         movw    $int19_message_prompt, %si
548         xorw    %di, %di
549         call    print_message
550         movw    $prodstr, %si
551         call    print_message
552         movw    $int19_message_dots, %si
553         call    print_message
554         movw    $0xdf4e, %bx
555         call    wait_for_key
556         pushf
557         xorw    %di, %di
558         call    print_kill_line
559         movw    $int19_message_done, %si
560         call    print_message
561         popf
562         jz      1f
563         /* Leave keypress in buffer and start gPXE.  The keypress will
564          * cause the usual initial Ctrl-B prompt to be skipped.
565          */
566         pushw   %cs
567         call    exec
568 1:      /* Try to call original INT 19 vector */
569         movl    %cs:orig_int19, %eax
570         testl   %eax, %eax
571         je      2f
572         ljmp    *%cs:orig_int19
573 2:      /* No chained vector: issue INT 18 as a last resort */
574         int     $0x18
575         .size   int19_entry, . - int19_entry
576 orig_int19:
577         .long   0
578         .size   orig_int19, . - orig_int19
579
580 int19_message_prompt:
581         .asciz  "Press N to skip booting from "
582         .size   int19_message_prompt, . - int19_message_prompt
583 int19_message_dots:
584         .asciz  "..."
585         .size   int19_message_dots, . - int19_message_dots
586 int19_message_done:
587         .asciz  "\n\n"
588         .size   int19_message_done, . - int19_message_done
589         
590 /* Execute as a boot device
591  *
592  */
593 exec:   /* Set %ds = %cs */
594         pushw   %cs
595         popw    %ds
596
597         /* Print message as soon as possible */
598         movw    $prodstr, %si
599         xorw    %di, %di
600         call    print_message
601         movw    $exec_message_pre_install, %si
602         call    print_message
603
604         /* Store magic word on BIOS stack and remember BIOS %ss:sp */
605         pushl   $STACK_MAGIC
606         movw    %ss, %dx
607         movw    %sp, %bp
608
609         /* Obtain a reasonably-sized temporary stack */
610         xorw    %ax, %ax
611         movw    %ax, %ss
612         movw    $0x7c00, %sp
613
614         /* Install gPXE */
615         movl    image_source, %esi
616         movl    decompress_to, %edi
617         call    alloc_basemem
618         call    install_prealloc
619
620         /* Print message indicating successful installation */
621         movw    $exec_message_post_install, %si
622         xorw    %di, %di
623         call    print_message
624
625         /* Set up real-mode stack */
626         movw    %bx, %ss
627         movw    $_estack16, %sp
628
629         /* Jump to .text16 segment */
630         pushw   %ax
631         pushw   $1f
632         lret
633         .section ".text16", "awx", @progbits
634 1:      /* Call main() */
635         pushl   $main
636         pushw   %cs
637         call    prot_call
638         popl    %ecx /* discard */
639
640         /* Uninstall gPXE */
641         call    uninstall
642
643         /* Restore BIOS stack */
644         movw    %dx, %ss
645         movw    %bp, %sp
646
647         /* Check magic word on BIOS stack */
648         popl    %eax
649         cmpl    $STACK_MAGIC, %eax
650         jne     1f
651         /* BIOS stack OK: return to caller */
652         lret
653 1:      /* BIOS stack corrupt: use INT 18 */
654         int     $0x18
655         .previous
656
657 exec_message_pre_install:
658         .asciz  " starting execution..."
659         .size exec_message_pre_install, . - exec_message_pre_install
660 exec_message_post_install:
661         .asciz  "ok\n"
662         .size exec_message_post_install, . - exec_message_post_install
663
664 /* Wait for key press specified by %bl (masked by %bh)
665  *
666  * Used by init and INT19 code when prompting user.  If the specified
667  * key is pressed, it is left in the keyboard buffer.
668  *
669  * Returns with ZF set iff specified key is pressed.
670  */
671 wait_for_key:
672         /* Preserve registers */
673         pushw   %cx
674         pushw   %ax
675 1:      /* Empty the keyboard buffer before waiting for input */
676         movb    $0x01, %ah
677         int     $0x16
678         jz      2f
679         xorw    %ax, %ax
680         int     $0x16
681         jmp     1b
682 2:      /* Wait for a key press */
683         movw    $ROM_BANNER_TIMEOUT, %cx
684 3:      decw    %cx
685         js      99f             /* Exit with ZF clear */
686         /* Wait for timer tick to be updated */
687         call    wait_for_tick
688         /* Check to see if a key was pressed */
689         movb    $0x01, %ah
690         int     $0x16
691         jz      3b
692         /* Check to see if key was the specified key */
693         andb    %bh, %al
694         cmpb    %al, %bl
695         je      99f             /* Exit with ZF set */
696         /* Not the specified key: remove from buffer and stop waiting */
697         pushfw
698         xorw    %ax, %ax
699         int     $0x16
700         popfw                   /* Exit with ZF clear */
701 99:     /* Restore registers and return */
702         popw    %ax
703         popw    %cx
704         ret
705         .size wait_for_key, . - wait_for_key
706
707 /* Wait for timer tick
708  *
709  * Used by wait_for_key
710  */
711 wait_for_tick:
712         pushl   %eax
713         pushw   %fs
714         movw    $0x40, %ax
715         movw    %ax, %fs
716         movl    %fs:(0x6c), %eax
717 1:      pushf
718         sti
719         hlt
720         popf
721         cmpl    %fs:(0x6c), %eax
722         je      1b
723         popw    %fs
724         popl    %eax
725         ret
726         .size wait_for_tick, . - wait_for_tick