diff options
author | Yonghee Han <onstudy@samsung.com> | 2016-07-27 16:42:54 +0900 |
---|---|---|
committer | Yonghee Han <onstudy@samsung.com> | 2016-07-27 00:56:08 -0700 |
commit | a03c4728275d119af5f66c4a69e8d9d5a1730031 (patch) | |
tree | 2b4ed9542884bf8b947076c55c4ef1814217cb69 /roms | |
parent | 3158f4a51894e46ecb593bffbfd12824e1d6534a (diff) | |
download | qemu-a03c4728275d119af5f66c4a69e8d9d5a1730031.tar.gz qemu-a03c4728275d119af5f66c4a69e8d9d5a1730031.tar.bz2 qemu-a03c4728275d119af5f66c4a69e8d9d5a1730031.zip |
Imported Upstream version 2.5.1.1upstream/2.5.1.1
Change-Id: Ie290b0e68882590d8a64fab165a943940b7c98ed
Diffstat (limited to 'roms')
1056 files changed, 51190 insertions, 8909 deletions
diff --git a/roms/Makefile b/roms/Makefile index 7b3f15632..09e33b59e 100644 --- a/roms/Makefile +++ b/roms/Makefile @@ -120,20 +120,17 @@ efi-rom-%: build-pxe-roms build-efi-roms -ec ipxe/src/bin-x86_64-efi/$(VID)$(DID).efidrv \ -o ../pc-bios/efi-$*.rom -build-pxe-roms: ipxe/src/config/local/general.h - $(MAKE) -C ipxe/src GITVERSION="" \ +build-pxe-roms: + $(MAKE) -C ipxe/src CONFIG=qemu \ CROSS_COMPILE=$(x86_64_cross_prefix) \ $(patsubst %,bin/%.rom,$(pxerom_targets)) -build-efi-roms: build-pxe-roms ipxe/src/config/local/general.h - $(MAKE) -C ipxe/src GITVERSION="" \ +build-efi-roms: build-pxe-roms + $(MAKE) -C ipxe/src CONFIG=qemu \ CROSS_COMPILE=$(x86_64_cross_prefix) \ $(patsubst %,bin-i386-efi/%.efidrv,$(pxerom_targets)) \ $(patsubst %,bin-x86_64-efi/%.efidrv,$(pxerom_targets)) -ipxe/src/config/local/%: config.ipxe.% - cp $< $@ - slof: $(MAKE) -C SLOF CROSS=$(powerpc64_cross_prefix) qemu diff --git a/roms/SLOF/README b/roms/SLOF/README index 58e929427..789504568 100644 --- a/roms/SLOF/README +++ b/roms/SLOF/README @@ -11,6 +11,7 @@ Index 2.2 Overview of the source code 2.4 Extending the Forth engine 3.0 Limitations +4.0 Submitting patches 1.0 Introduction to Slimline Open Firmware @@ -236,6 +237,16 @@ To add primitives: On a JS21 all memory configurations should work. +4.0 Submitting patches +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + +Patches for SLOF should be made against https://github.com/aik/SLOF, +the master branch and posted to slof@lists.ozlabs.org. +The patches must be signed using "Signed-off-by" tag with a real name to +confirm that you certify the Developer Certificate of Origin Version 1.1, +see [3] for details. + + Documentation +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ @@ -244,3 +255,6 @@ Documentation [2] PAPR Standard, Power.org(TM) Standard for Power Architecture(R) Platform Requirements (Workstation, Server), Version 2.4, December 7, 2009 + +[3] Developer Certificate of Origin Version 1.1 + http://developercertificate.org/ diff --git a/roms/SLOF/VERSION b/roms/SLOF/VERSION index 20bdb2eb7..c99837873 100644 --- a/roms/SLOF/VERSION +++ b/roms/SLOF/VERSION @@ -1 +1 @@ -20150429 +20151103 diff --git a/roms/SLOF/board-js2x/llfw/stage2.lds b/roms/SLOF/board-js2x/llfw/stage2.lds index f91f0658a..e6315c3c8 100644 --- a/roms/SLOF/board-js2x/llfw/stage2.lds +++ b/roms/SLOF/board-js2x/llfw/stage2.lds @@ -45,7 +45,8 @@ SECTIONS { __bss_end = .; __bss_size = (__bss_end - __bss_start); - __toc_start = .; + . = ALIGN(256); + __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; .got : { *(.toc .got) diff --git a/roms/SLOF/board-js2x/llfw/stage2_head.S b/roms/SLOF/board-js2x/llfw/stage2_head.S index 5460bfebb..f3f5e0c8c 100644 --- a/roms/SLOF/board-js2x/llfw/stage2_head.S +++ b/roms/SLOF/board-js2x/llfw/stage2_head.S @@ -79,8 +79,6 @@ bsscdone: /* ------------------------------------ */ ASM_ENTRY(toc_init) LOAD64(r2, __toc_start) - addi r2,r2,0x4000 - addi r2,r2,0x4000 blr /* ------------------------------------ */ diff --git a/roms/SLOF/board-js2x/slof/Makefile b/roms/SLOF/board-js2x/slof/Makefile index ab3e683a4..fdc716fc6 100644 --- a/roms/SLOF/board-js2x/slof/Makefile +++ b/roms/SLOF/board-js2x/slof/Makefile @@ -82,6 +82,7 @@ OF_FFS_FILES = \ $(SLOFCMNDIR)/fs/scsi-host-helpers.fs \ $(SLOFCMNDIR)/fs/scsi-probe-helpers.fs \ $(SLOFCMNDIR)/fs/scsi-support.fs \ + $(SLOFCMNDIR)/fs/dma-function.fs \ $(SLOFCMNDIR)/fs/pci-device.fs \ $(SLOFCMNDIR)/fs/pci-bridge.fs \ $(SLOFCMNDIR)/fs/pci-properties.fs \ diff --git a/roms/SLOF/board-js2x/slof/helper.fs b/roms/SLOF/board-js2x/slof/helper.fs index 34d60da1f..1e2b03063 100644 --- a/roms/SLOF/board-js2x/slof/helper.fs +++ b/roms/SLOF/board-js2x/slof/helper.fs @@ -26,3 +26,14 @@ s" , " $cat bdate2human $cat encode-string THEN ; + +: invert-region ( addr len -- ) + 2dup or 7 and CASE + 0 OF 3 rshift 0 ?DO dup dup rx@ -1 xor swap rx! xa1+ LOOP ENDOF + 4 OF 2 rshift 0 ?DO dup dup rl@ -1 xor swap rl! la1+ LOOP ENDOF + 3 and + 2 OF 1 rshift 0 ?DO dup dup rw@ -1 xor swap rw! wa1+ LOOP ENDOF + dup OF 0 ?DO dup dup rb@ -1 xor swap rb! 1+ LOOP ENDOF + ENDCASE + drop +; diff --git a/roms/SLOF/board-qemu/llfw/stage2.lds b/roms/SLOF/board-qemu/llfw/stage2.lds index e060dd189..28c9dca93 100644 --- a/roms/SLOF/board-qemu/llfw/stage2.lds +++ b/roms/SLOF/board-qemu/llfw/stage2.lds @@ -49,7 +49,8 @@ SECTIONS { __bss_end = .; __bss_size = (__bss_end - __bss_start); - __toc_start = .; + . = ALIGN(256); + __toc_start = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; .got : { *(.toc .got) diff --git a/roms/SLOF/board-qemu/llfw/stage2_head.S b/roms/SLOF/board-qemu/llfw/stage2_head.S index c56b117ce..adf75547b 100644 --- a/roms/SLOF/board-qemu/llfw/stage2_head.S +++ b/roms/SLOF/board-qemu/llfw/stage2_head.S @@ -77,8 +77,6 @@ bsscdone: /* ------------------------------------ */ ASM_ENTRY(toc_init) LOAD64(r2, __toc_start) - addi r2,r2,0x4000 - addi r2,r2,0x4000 blr /* ------------------------------------ */ diff --git a/roms/SLOF/board-qemu/slof/Makefile b/roms/SLOF/board-qemu/slof/Makefile index 283f77d32..a3fe6abd0 100644 --- a/roms/SLOF/board-qemu/slof/Makefile +++ b/roms/SLOF/board-qemu/slof/Makefile @@ -69,6 +69,7 @@ VIO_FFS_FILES = \ $(SLOFBRDDIR)/pci-device_1af4_1001.fs \ $(SLOFBRDDIR)/pci-device_1af4_1004.fs \ $(SLOFBRDDIR)/pci-device_1af4_1009.fs \ + $(SLOFBRDDIR)/pci-device_1af4_1050.fs \ $(SLOFBRDDIR)/vio-hvterm.fs \ $(SLOFBRDDIR)/vio-vscsi.fs \ $(SLOFBRDDIR)/vio-veth.fs \ @@ -103,6 +104,7 @@ OF_FFS_FILES = \ $(SLOFBRDDIR)/pci-device_1013_00b8.fs \ $(SLOFBRDDIR)/pci-device_8086_100e.fs \ $(SLOFBRDDIR)/e1k.fs \ + $(SLOFBRDDIR)/qemu-vga.fs \ $(FCODE_FFS_FILES) # Uncomment the following line to enable the USB code: diff --git a/roms/SLOF/board-qemu/slof/helper.fs b/roms/SLOF/board-qemu/slof/helper.fs index 96da49894..40d4abc3a 100644 --- a/roms/SLOF/board-qemu/slof/helper.fs +++ b/roms/SLOF/board-qemu/slof/helper.fs @@ -33,3 +33,16 @@ swap - ; +: invert-region-cs ( addr len cellsize -- ) + >r over swap r@ rshift r> swap 1 hv-logical-memop drop +; + +: invert-region ( addr len -- ) + 2dup or 7 and CASE + 0 OF 3 invert-region-cs ENDOF + 4 OF 2 invert-region-cs ENDOF + 3 and + 2 OF 1 invert-region-cs ENDOF + dup OF 0 invert-region-cs ENDOF + ENDCASE +; diff --git a/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs b/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs index a5c3584f9..22ea45d5c 100644 --- a/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs +++ b/roms/SLOF/board-qemu/slof/pci-device_1234_1111.fs @@ -10,233 +10,6 @@ \ * IBM Corporation - initial implementation \ ****************************************************************************/ -my-space pci-device-generic-setup - -\ Defaults, overriden from qemu -d# 800 VALUE disp-width -d# 600 VALUE disp-height -d# 8 VALUE disp-depth - -\ Determine base address -10 config-l@ translate-my-address f not AND VALUE fb-base - -\ Fixed up later --1 VALUE io-base - -\ We support only one instance -false VALUE is-installed? - -: vga-io-xlate ( port -- addr ) - io-base -1 = IF - dup translate-my-address fff not and to io-base - THEN - io-base + -; - -: vga-w! ( value port -- ) - vga-io-xlate rw!-le -; - -: vga-w@ ( port -- value ) - vga-io-xlate rw@-le -; - -: vga-b! ( value port -- ) - vga-io-xlate rb! -; - -: vga-b@ ( port -- value ) - vga-io-xlate rb@ -; - -: vbe! ( value index -- ) - 1ce vga-w! 1d0 vga-w! -; - -: vbe@ ( index -- value ) - 1ce vga-w! 1d0 vga-w@ -; - -: color! ( r g b number -- ) - 3c8 vga-b! - rot 3c9 vga-b! - swap 3c9 vga-b! - 3c9 vga-b! -; - -: color@ ( number -- r g b ) - 3c8 vga-b! - 3c9 vga-b@ - 3c9 vga-b@ - 3c9 vga-b@ -; - -: set-colors ( adr number #numbers -- ) - over 3c8 vga-b! - swap DO - rb@ 3c9 vga-b! - rb@ 3c9 vga-b! - rb@ 3c9 vga-b! - LOOP - 3drop -; - -: get-colors ( adr number #numbers -- ) - 3drop -; - -include graphics.fs - -\ qemu fake VBE IO registers -0 CONSTANT VBE_DISPI_INDEX_ID -1 CONSTANT VBE_DISPI_INDEX_XRES -2 CONSTANT VBE_DISPI_INDEX_YRES -3 CONSTANT VBE_DISPI_INDEX_BPP -4 CONSTANT VBE_DISPI_INDEX_ENABLE -5 CONSTANT VBE_DISPI_INDEX_BANK -6 CONSTANT VBE_DISPI_INDEX_VIRT_WIDTH -7 CONSTANT VBE_DISPI_INDEX_VIRT_HEIGHT -8 CONSTANT VBE_DISPI_INDEX_X_OFFSET -9 CONSTANT VBE_DISPI_INDEX_Y_OFFSET -a CONSTANT VBE_DISPI_INDEX_NB - -\ ENABLE register -00 CONSTANT VBE_DISPI_DISABLED -01 CONSTANT VBE_DISPI_ENABLED -02 CONSTANT VBE_DISPI_GETCAPS -20 CONSTANT VBE_DISPI_8BIT_DAC -40 CONSTANT VBE_DISPI_LFB_ENABLED -80 CONSTANT VBE_DISPI_NOCLEARMEM - -: init-mode - 0 3c0 vga-b! - VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe! - 0 VBE_DISPI_INDEX_X_OFFSET vbe! - 0 VBE_DISPI_INDEX_Y_OFFSET vbe! - disp-width VBE_DISPI_INDEX_XRES vbe! - disp-height VBE_DISPI_INDEX_YRES vbe! - disp-depth VBE_DISPI_INDEX_BPP vbe! - VBE_DISPI_ENABLED VBE_DISPI_8BIT_DAC or VBE_DISPI_INDEX_ENABLE vbe! - 0 3c0 vga-b! - 20 3c0 vga-b! -; - -: clear-screen - fb-base disp-width disp-height disp-depth 7 + 8 / * * 0 rfill -; - -: read-settings - s" qemu,graphic-width" get-chosen IF - decode-int to disp-width 2drop - THEN - s" qemu,graphic-height" get-chosen IF - decode-int to disp-height 2drop - THEN - s" qemu,graphic-depth" get-chosen IF - decode-int nip nip - dup 8 = - over f = or - over 10 = or - over 20 = or IF - to disp-depth - ELSE - ." Unsupported bit depth, using 8bpp " drop cr - THEN - THEN -; - -: add-legacy-reg - \ add legacy I/O Ports / Memory regions to assigned-addresses - \ see PCI Bus Binding Revision 2.1 Section 7. - s" reg" get-node get-property IF - \ "reg" does not exist, create new - encode-start - ELSE - \ "reg" does exist, copy it - encode-bytes - THEN - \ I/O Range 0x1ce-0x1d2 - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 1ce encode-64+ 4 encode-64+ \ addr size - \ I/O Range 0x3B0-0x3BB - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 3b0 encode-64+ c encode-64+ \ addr size - \ I/O Range 0x3C0-0x3DF - my-space a1000000 or encode-int+ \ non-relocatable, aliased I/O space - 3c0 encode-64+ 20 encode-64+ \ addr size - \ Memory Range 0xA0000-0xBFFFF - my-space a2000000 or encode-int+ \ non-relocatable, <1MB Memory space - a0000 encode-64+ 20000 encode-64+ \ addr size - s" reg" property \ store "reg" property -; - -: setup-properties - \ Shouldn't this be done from open ? - disp-width encode-int s" width" property - disp-height encode-int s" height" property - disp-width disp-depth 7 + 8 / * encode-int s" linebytes" property - disp-depth encode-int s" depth" property - s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... - \ add "device_type" property - s" display" device-type - s" qemu,std-vga" encode-string s" compatible" property - \ XXX We don't create an "address" property because Linux doesn't know what - \ to do with it for >32-bit -; - -\ words for installation/removal, needed by is-install/is-remove, see display.fs -: display-remove ( -- ) -; - -: hcall-invert-screen ( -- ) - frame-buffer-adr frame-buffer-adr 3 - screen-height screen-width * screen-depth * /x / - 1 hv-logical-memop - drop -; - -: hcall-blink-screen ( -- ) - \ 32 msec delay for visually noticing the blink - hcall-invert-screen 20 ms hcall-invert-screen -; - -: display-install ( -- ) - is-installed? NOT IF - ." Installing QEMU fb" cr - fb-base to frame-buffer-adr - clear-screen - default-font - set-font - disp-width disp-height - disp-width char-width / disp-height char-height / - disp-depth 7 + 8 / ( width height #lines #cols depth ) - fb-install - ['] hcall-invert-screen to invert-screen - ['] hcall-blink-screen to blink-screen - true to is-installed? - THEN -; - -: set-alias - s" screen" find-alias 0= IF - \ no previous screen alias defined, define it... - s" screen" get-node node>path set-alias - ELSE - drop - THEN -; - - ." qemu vga" cr -pci-master-enable -pci-mem-enable -pci-io-enable -add-legacy-reg -read-settings -init-mode -init-default-palette -setup-properties -' display-install is-install -' display-remove is-remove -set-alias +s" qemu-vga.fs" included diff --git a/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs b/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs new file mode 100644 index 000000000..516056aad --- /dev/null +++ b/roms/SLOF/board-qemu/slof/pci-device_1af4_1050.fs @@ -0,0 +1,15 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +s" virtio [ vga ]" type cr + +s" qemu-vga.fs" included diff --git a/roms/SLOF/board-qemu/slof/qemu-vga.fs b/roms/SLOF/board-qemu/slof/qemu-vga.fs new file mode 100644 index 000000000..3f4c237fc --- /dev/null +++ b/roms/SLOF/board-qemu/slof/qemu-vga.fs @@ -0,0 +1,198 @@ +\ ***************************************************************************** +\ * Copyright (c) 2015 IBM Corporation +\ * All rights reserved. +\ * This program and the accompanying materials +\ * are made available under the terms of the BSD License +\ * which accompanies this distribution, and is available at +\ * http://www.opensource.org/licenses/bsd-license.php +\ * +\ * Contributors: +\ * IBM Corporation - initial implementation +\ ****************************************************************************/ + +my-space pci-device-generic-setup + +\ Defaults, overriden from qemu +d# 800 VALUE disp-width +d# 600 VALUE disp-height +d# 8 VALUE disp-depth + +: map-in " map-in" my-phandle parent $call-static ; +: map-out " map-out" my-phandle parent $call-static ; + +\ Determine base address +0 0 my-space h# 02000010 + 1 map-in VALUE fb-base +0 0 my-space h# 02000018 + 1 map-in VALUE reg-base + +\ We support only one instance +false VALUE is-installed? + +: vga-w! ( value port -- ) + 3c0 - reg-base 400 + + rw!-le +; + +: vga-w@ ( port -- value ) + 3c0 - reg-base 400 + + rw@-le +; + +: vga-b! ( value port -- ) + 3c0 - reg-base 400 + + rb! +; + +: vga-b@ ( port -- value ) + 3c0 - reg-base 400 + + rb@ +; + +: vbe! ( value index -- ) + 1 << reg-base 500 + + rw!-le +; + +: vbe@ ( index -- value ) + 1 << reg-base 500 + + rw@-le +; + +: color! ( r g b number -- ) + 3c8 vga-b! + rot 3c9 vga-b! + swap 3c9 vga-b! + 3c9 vga-b! +; + +: color@ ( number -- r g b ) + 3c8 vga-b! + 3c9 vga-b@ + 3c9 vga-b@ + 3c9 vga-b@ +; + +: set-colors ( adr number #numbers -- ) + over 3c8 vga-b! + swap DO + rb@ 3c9 vga-b! + rb@ 3c9 vga-b! + rb@ 3c9 vga-b! + LOOP + 3drop +; + +: get-colors ( adr number #numbers -- ) + 3drop +; + +include graphics.fs + +\ qemu fake VBE IO registers +0 CONSTANT VBE_DISPI_INDEX_ID +1 CONSTANT VBE_DISPI_INDEX_XRES +2 CONSTANT VBE_DISPI_INDEX_YRES +3 CONSTANT VBE_DISPI_INDEX_BPP +4 CONSTANT VBE_DISPI_INDEX_ENABLE +5 CONSTANT VBE_DISPI_INDEX_BANK +6 CONSTANT VBE_DISPI_INDEX_VIRT_WIDTH +7 CONSTANT VBE_DISPI_INDEX_VIRT_HEIGHT +8 CONSTANT VBE_DISPI_INDEX_X_OFFSET +9 CONSTANT VBE_DISPI_INDEX_Y_OFFSET +a CONSTANT VBE_DISPI_INDEX_NB + +\ ENABLE register +00 CONSTANT VBE_DISPI_DISABLED +01 CONSTANT VBE_DISPI_ENABLED +02 CONSTANT VBE_DISPI_GETCAPS +20 CONSTANT VBE_DISPI_8BIT_DAC +40 CONSTANT VBE_DISPI_LFB_ENABLED +80 CONSTANT VBE_DISPI_NOCLEARMEM + +: init-mode + 0 3c0 vga-b! + VBE_DISPI_DISABLED VBE_DISPI_INDEX_ENABLE vbe! + 0 VBE_DISPI_INDEX_X_OFFSET vbe! + 0 VBE_DISPI_INDEX_Y_OFFSET vbe! + disp-width VBE_DISPI_INDEX_XRES vbe! + disp-height VBE_DISPI_INDEX_YRES vbe! + disp-depth VBE_DISPI_INDEX_BPP vbe! + VBE_DISPI_ENABLED VBE_DISPI_8BIT_DAC or VBE_DISPI_INDEX_ENABLE vbe! + 0 3c0 vga-b! + 20 3c0 vga-b! +; + +: clear-screen + fb-base disp-width disp-height disp-depth 7 + 8 / * * 0 rfill +; + +: read-settings + s" qemu,graphic-width" get-chosen IF + decode-int to disp-width 2drop + THEN + s" qemu,graphic-height" get-chosen IF + decode-int to disp-height 2drop + THEN + s" qemu,graphic-depth" get-chosen IF + decode-int nip nip + dup 8 = + over f = or + over 10 = or + over 20 = or IF + to disp-depth + ELSE + ." Unsupported bit depth, using 8bpp " drop cr + THEN + THEN +; + +: setup-properties + \ Shouldn't this be done from open ? + disp-width encode-int s" width" property + disp-height encode-int s" height" property + disp-width disp-depth 7 + 8 / * encode-int s" linebytes" property + disp-depth encode-int s" depth" property + s" ISO8859-1" encode-string s" character-set" property \ i hope this is ok... + \ add "device_type" property + s" display" device-type + s" qemu,std-vga" encode-string s" compatible" property + \ XXX We don't create an "address" property because Linux doesn't know what + \ to do with it for >32-bit +; + +\ words for installation/removal, needed by is-install/is-remove, see display.fs +: display-remove ( -- ) +; + +: slow-blink-screen ( -- ) + \ 32 msec delay for visually noticing the blink + invert-screen 20 ms invert-screen +; + +: display-install ( -- ) + is-installed? NOT IF + ." Installing QEMU fb" cr + fb-base to frame-buffer-adr + clear-screen + default-font + set-font + disp-width disp-height + disp-width char-width / disp-height char-height / + disp-depth 7 + 8 / ( width height #lines #cols depth ) + fb-install + ['] slow-blink-screen to blink-screen + true to is-installed? + THEN +; + +: set-alias + s" screen" find-alias 0= IF + \ no previous screen alias defined, define it... + s" screen" get-node node>path set-alias + ELSE + drop + THEN +; + +pci-master-enable +pci-mem-enable +read-settings +init-mode +init-default-palette +setup-properties +' display-install is-install +' display-remove is-remove +set-alias diff --git a/roms/SLOF/clients/net-snk/client.lds b/roms/SLOF/clients/net-snk/client.lds index 39d04594e..c2086445b 100644 --- a/roms/SLOF/clients/net-snk/client.lds +++ b/roms/SLOF/clients/net-snk/client.lds @@ -44,10 +44,10 @@ SECTIONS { *(.opd) } - . = ALIGN(0x10); + . = ALIGN(256); .got : { - _got = .; + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got) *(.toc) _got_end = .; diff --git a/roms/SLOF/clients/net-snk/kernel/entry.S b/roms/SLOF/clients/net-snk/kernel/entry.S index 8849fb9d1..bf10542bd 100644 --- a/roms/SLOF/clients/net-snk/kernel/entry.S +++ b/roms/SLOF/clients/net-snk/kernel/entry.S @@ -44,7 +44,7 @@ C_ENTRY(_entry) bcl 20,31,over # branch after pointer table base: .align 3 -.LCgot: .quad _got-base+0x8000 +.LCgot: .quad _got-base .LCstack: .quad _stack+STACKSIZE-0x80-base over: mflr r8 # gpr 8 is the base diff --git a/roms/SLOF/clients/takeover/client.lds b/roms/SLOF/clients/takeover/client.lds index 2701d8e1e..0ab428a01 100644 --- a/roms/SLOF/clients/takeover/client.lds +++ b/roms/SLOF/clients/takeover/client.lds @@ -43,8 +43,8 @@ SECTIONS { .got : { - . = ALIGN(8); - _got = .; + . = ALIGN(256); + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got .toc) _got_end = .; } diff --git a/roms/SLOF/clients/takeover/entry.S b/roms/SLOF/clients/takeover/entry.S index a1030eb40..ff482732d 100644 --- a/roms/SLOF/clients/takeover/entry.S +++ b/roms/SLOF/clients/takeover/entry.S @@ -21,7 +21,7 @@ _wrapclient: bcl 20,31,over # branch after pointer table base: .align 3 -.LCgot: .quad _got-base+0x8000 +.LCgot: .quad _got-base over: mflr r8 # gpr 8 is the base ld r2, .LCgot-base(r8) # load got pointer diff --git a/roms/SLOF/clients/takeover/main.c b/roms/SLOF/clients/takeover/main.c index 360d8eaed..1e1b02614 100644 --- a/roms/SLOF/clients/takeover/main.c +++ b/roms/SLOF/clients/takeover/main.c @@ -16,7 +16,7 @@ #include <of.h> #include <pci.h> #include <cpu.h> -#include <ioctl.h> +#include <unistd.h> #include <takeover.h> extern void call_client_interface(of_arg_t *); diff --git a/roms/SLOF/include/ppc970/cache.h b/roms/SLOF/include/ppc970/cache.h index b74868986..500182ea6 100644 --- a/roms/SLOF/include/ppc970/cache.h +++ b/roms/SLOF/include/ppc970/cache.h @@ -55,8 +55,8 @@ cache_inhibited_access(uint64_t, 64) #define _FASTMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _MOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _MOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _MOVE(s, d, size, type_w); break; \ + case 4: _MOVE(s, d, size, type_l); break; \ + case 2: case 6: _MOVE(s, d, size, type_w); break; \ default: _MOVE(s, d, size, type_c); break; \ } @@ -78,9 +78,51 @@ cache_inhibited_access(uint64_t, 64) #define _FASTRMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _RMOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _RMOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _RMOVE(s, d, size, type_w); break; \ + case 4: _RMOVE(s, d, size, type_l); break; \ + case 2: case 6: _RMOVE(s, d, size, type_w); break; \ default: _RMOVE(s, d, size, type_c); break; \ } +/* main RAM to IO memory move */ +#define FAST_MRMOVE_TYPED(s, d, size, t) \ +{ \ + t *s1 = (s), *d1 = (d); \ + register t tmp; \ + while (size > 0) { \ + tmp = *s1++; SET_CI; *d1++ = tmp; CLR_CI; size -= sizeof(t); \ + } \ +} + +#define FAST_MRMOVE(s, d, size) \ + switch (((type_u)(s) | (type_u)(d) | (size)) & (sizeof(type_u)-1)) { \ + case 0: FAST_MRMOVE_TYPED(s, d, size, type_u); break; \ + case 4: FAST_MRMOVE_TYPED(s, d, size, type_l); break; \ + case 2: case 6: FAST_MRMOVE_TYPED(s, d, size, type_w); break; \ + default: FAST_MRMOVE_TYPED(s, d, size, type_c); break; \ + } + +/* fill IO memory with pattern */ +#define FAST_RFILL_TYPED(dst, size, pat, t) \ +{ \ + t *d1 = (dst); \ + register t tmp = 0; \ + int i = sizeof(t); \ + while (i-- > 0) { \ + tmp <<= 8; tmp |= pat & 0xff; \ + } \ + SET_CI; \ + while (size > 0) { \ + *d1++ = tmp; size -= sizeof(t); \ + } \ + CLR_CI; \ +} + +#define FAST_RFILL(dst, size, pat) \ + switch (((type_u)dst | size) & (sizeof(type_u)-1)) { \ + case 0: FAST_RFILL_TYPED(dst, size, pat, type_u); break; \ + case 4: FAST_RFILL_TYPED(dst, size, pat, type_l); break; \ + case 2: case 6: FAST_RFILL_TYPED(dst, size, pat, type_w); break; \ + default: FAST_RFILL_TYPED(dst, size, pat, type_c); break; \ + } + #endif diff --git a/roms/SLOF/include/ppcp7/cache.h b/roms/SLOF/include/ppcp7/cache.h index dc6837196..27975f09c 100644 --- a/roms/SLOF/include/ppcp7/cache.h +++ b/roms/SLOF/include/ppcp7/cache.h @@ -81,8 +81,8 @@ cache_inhibited_access(uint64_t, 64) #define _FASTMOVE(s, d, size) \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) { \ case 0: _MOVE(s, d, size, type_u); break; \ - case sizeof(type_l): _MOVE(s, d, size, type_l); break; \ - case sizeof(type_w): _MOVE(s, d, size, type_w); break; \ + case 4: _MOVE(s, d, size, type_l); break; \ + case 2: case 6: _MOVE(s, d, size, type_w); break; \ default: _MOVE(s, d, size, type_c); break; \ } @@ -116,12 +116,26 @@ static inline void ci_rmove(void *dst, void *src, unsigned long esize, #define _FASTRMOVE(s, d, size) do { \ switch (((type_u)s | (type_u)d | size) & (sizeof(type_u)-1)) {\ case 0: ci_rmove(d,s,3,size>>3); break; \ - case sizeof(type_l): ci_rmove(d,s,2,size>>2); break; \ - case sizeof(type_w): ci_rmove(d,s,1,size>>1); break; \ + case 4: ci_rmove(d,s,2,size>>2); break; \ + case 2: case 6: ci_rmove(d,s,1,size>>1); break; \ default: ci_rmove(d,s,0,size); break; \ } \ } while(0) +#define FAST_MRMOVE(s, d, size) _FASTRMOVE(s, d, size) + +#define FAST_RFILL(dst, size, pat) do { \ + type_u buf[64]; \ + char *d = (char *)(dst); \ + memset(buf, pat, size < sizeof(buf) ? size : sizeof(buf)); \ + while (size > sizeof(buf)) { \ + FAST_MRMOVE(buf, d, sizeof(buf)); \ + d += sizeof(buf); \ + size -= sizeof(buf); \ + } \ + FAST_MRMOVE(buf, d, size); \ + } while(0) + static inline uint16_t bswap16_load(uint64_t addr) { unsigned int val; diff --git a/roms/SLOF/lib/libusb/usb-hid.c b/roms/SLOF/lib/libusb/usb-hid.c index f0cab8a69..ac6616aba 100644 --- a/roms/SLOF/lib/libusb/usb-hid.c +++ b/roms/SLOF/lib/libusb/usb-hid.c @@ -28,6 +28,10 @@ #define HID_REQ_SET_IDLE 0x0A #define HID_REQ_SET_PROTOCOL 0x0B +//key position for latin letters +#define KEYP_LATIN_A 4 +#define KEYP_LATIN_Z 29 + //#define KEY_DEBUG /* HID SPEC - 7.2.6 Set_Protocol Request */ @@ -83,6 +87,8 @@ uint8_t set_leds; const uint8_t *key_std = NULL; const uint8_t *key_std_shift = NULL; +uint8_t ctrl; /* modifiers */ + /** * read character from Keyboard-Buffer * @@ -111,6 +117,16 @@ static void write_key(uint8_t key) } /** + * Checks if keypos is a latin key + * @param keypos + * @return - + */ +static bool is_latin(uint8_t keypos) +{ + return keypos >= KEYP_LATIN_A && keypos <= KEYP_LATIN_Z; +} + +/** * Convert keyboard usage-ID to ANSI-Code * * @param Ctrl=Modifier Byte @@ -120,22 +136,24 @@ static void write_key(uint8_t key) static void get_char(uint8_t ctrl, uint8_t keypos) { uint8_t ch; + bool caps = false; #ifdef KEY_DEBUG printf("pos %02X\n", keypos); #endif if (set_leds & LED_CAPS_LOCK) /* is CAPS Lock set ? */ - ctrl |= MODIFIER_SHIFT; /* simulate shift */ + caps = true; - if (ctrl == 0) { + /* caps is a shift only for latin chars */ + if ((!caps && ctrl == 0) || (caps && !is_latin(keypos))) { ch = key_std[keypos]; if (ch != 0) write_key(ch); return; } - if (ctrl & MODIFIER_SHIFT) { + if ((ctrl & MODIFIER_SHIFT) || caps) { ch = key_std_shift[keypos]; if (ch != 0) write_key(ch); @@ -187,36 +205,38 @@ static void check_key_code(uint8_t *buf) set_leds ^= LED_CAPS_LOCK; break; + case 0x36: /*Shift pressed*/ + ctrl |= MODIFIER_SHIFT; + break; + case 0xb6: /*Shift unpressed*/ + ctrl &= ~MODIFIER_SHIFT; + break; case 0x3a: /* F1 */ write_key(0x1b); write_key(0x5b); - write_key(0x31); - write_key(0x31); - write_key(0x7e); + write_key(0x4f); + write_key(0x50); break; case 0x3b: /* F2 */ write_key(0x1b); write_key(0x5b); - write_key(0x31); - write_key(0x32); - write_key(0x7e); + write_key(0x4f); + write_key(0x51); break; case 0x3c: write_key(0x1b); /* F3 */ write_key(0x5b); - write_key(0x31); - write_key(0x33); - write_key(0x7e); + write_key(0x4f); + write_key(0x52); break; case 0x3d: write_key(0x1b); /* F4 */ write_key(0x5b); - write_key(0x31); - write_key(0x34); - write_key(0x7e); + write_key(0x4f); + write_key(0x53); break; case 0x3e: @@ -254,7 +274,7 @@ static void check_key_code(uint8_t *buf) case 0x42: write_key(0x1b); /* F9 */ write_key(0x5b); - write_key(0x31); + write_key(0x32); write_key(0x30); write_key(0x7e); break; @@ -262,7 +282,7 @@ static void check_key_code(uint8_t *buf) case 0x43: write_key(0x1b); /* F10 */ write_key(0x5b); - write_key(0x31); + write_key(0x32); write_key(0x31); write_key(0x7e); break; @@ -270,7 +290,7 @@ static void check_key_code(uint8_t *buf) case 0x44: write_key(0x1b); /* F11 */ write_key(0x5b); - write_key(0x31); + write_key(0x32); write_key(0x33); write_key(0x7e); break; @@ -278,7 +298,7 @@ static void check_key_code(uint8_t *buf) case 0x45: write_key(0x1b); /* F12 */ write_key(0x5b); - write_key(0x31); + write_key(0x32); write_key(0x34); write_key(0x7e); break; @@ -290,36 +310,34 @@ static void check_key_code(uint8_t *buf) case 0x49: write_key(0x1b); /* INS */ write_key(0x5b); - write_key(0x31); + write_key(0x32); write_key(0x7e); break; case 0x4a: write_key(0x1b); /* HOME */ - write_key(0x5b); - write_key(0x32); - write_key(0x7e); + write_key(0x4f); + write_key(0x48); break; case 0x4b: write_key(0x1b); /* PgUp */ write_key(0x5b); - write_key(0x33); + write_key(0x35); write_key(0x7e); break; case 0x4c: write_key(0x1b); /* DEL */ write_key(0x5b); - write_key(0x34); + write_key(0x33); write_key(0x7e); break; case 0x4d: write_key(0x1b); /* END */ - write_key(0x5b); - write_key(0x35); - write_key(0x7e); + write_key(0x4f); + write_key(0x46); break; case 0x4e: @@ -443,11 +461,8 @@ unsigned char usb_key_available(void *dev) unsigned char usb_read_keyb(void *vdev) { - if (!vdev) - return false; - - while (usb_poll_key(vdev)) { - /* loop for all pending keys */ - } - return read_key(); + if (usb_key_available(vdev)) + return read_key(); + else + return 0; } diff --git a/roms/SLOF/lib/libusb/usb-xhci.c b/roms/SLOF/lib/libusb/usb-xhci.c index 0c3d6e47f..7683c51d6 100644 --- a/roms/SLOF/lib/libusb/usb-xhci.c +++ b/roms/SLOF/lib/libusb/usb-xhci.c @@ -225,11 +225,11 @@ static void xhci_handle_cmd_completion(struct xhci_hcd *xhcd, xhcd->slot_id = 0; } -static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, - uint32_t event_type) +static uint64_t xhci_poll_event(struct xhci_hcd *xhcd, + uint32_t event_type) { struct xhci_event_trb *event; - uint64_t val; + uint64_t val, retval = 0; uint32_t flags, time; int index; @@ -244,7 +244,7 @@ static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, mb(); flags = le32_to_cpu(event->flags); if (time < SLOF_GetTimer()) - return NULL; + return 0; } mb(); @@ -273,6 +273,7 @@ static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, break; } xhcd->ering.deq = (uint64_t) (event + 1); + retval = le64_to_cpu(event->addr); event->addr = 0; event->status = 0; @@ -289,7 +290,11 @@ static struct xhci_event_trb *xhci_poll_event(struct xhci_hcd *xhcd, dprintf("Update start %x deq %x index %d\n", xhcd->ering.trbs_dma, val, index/sizeof(*event)); write_reg64(&xhcd->run_regs->irs[0].erdp, val); - return event; + + if (retval == 0) + return (uint64_t)event; + else + return retval; } static void xhci_send_cmd(struct xhci_hcd *xhcd, uint32_t field1, @@ -388,10 +393,12 @@ static void xhci_init_seg(struct xhci_seg *seg, uint32_t size, uint32_t type) seg->deq = (uint64_t)seg->trbs; memset((void *)seg->trbs, 0, size); - link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1); - link->addr = cpu_to_le64(seg->trbs_dma); - link->field2 = 0; - link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + if (type != TYPE_EVENT) { + link =(struct xhci_link_trb *) (seg->trbs + seg->size - 1); + link->addr = cpu_to_le64(seg->trbs_dma); + link->field2 = 0; + link->field3 = cpu_to_le32(0x1 | TRB_CMD_TYPE(TRB_LINK)); + } return; } @@ -616,6 +623,7 @@ static void xhci_free_dev(struct xhci_dev *xdev) { xhci_free_seg(&xdev->bulk_in, XHCI_DATA_TRBS_SIZE); xhci_free_seg(&xdev->bulk_out, XHCI_DATA_TRBS_SIZE); + xhci_free_seg(&xdev->intr, XHCI_INTR_TRBS_SIZE); xhci_free_seg(&xdev->control, XHCI_CONTROL_TRBS_SIZE); xhci_free_ctx(&xdev->in_ctx, XHCI_CTX_BUF_SIZE); xhci_free_ctx(&xdev->out_ctx, XHCI_CTX_BUF_SIZE); @@ -637,7 +645,25 @@ static bool usb3_dev_init(struct xhci_hcd *xhcd, uint32_t port) return true; } -static int xhci_hub_check_ports(struct xhci_hcd *xhcd) +static int xhci_device_present(uint32_t portsc, uint32_t usb_ver) +{ + if (usb_ver == USB_XHCI) { + /* Device present and enabled state */ + if ((portsc & PORTSC_CCS) && + (portsc & PORTSC_PP) && + (portsc & PORTSC_PED)) { + return true; + } + } else if (usb_ver == USB_EHCI) { + /* Device present and in disabled state */ + if ((portsc & PORTSC_CCS) && (portsc & PORTSC_CSC)) + return true; + } + return false; +} + +static int xhci_port_scan(struct xhci_hcd *xhcd, + uint32_t usb_ver) { uint32_t num_ports, portsc, i; struct xhci_op_regs *op; @@ -645,7 +671,7 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) struct xhci_cap_regs *cap; uint32_t xecp_off; uint32_t *xecp_addr, *base; - uint32_t port_off = 1, port_cnt; + uint32_t port_off = 0, port_cnt; dprintf("enter\n"); @@ -658,14 +684,14 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) base = (uint32_t *)cap; while (xecp_off > 0) { xecp_addr = base + xecp_off; - dprintf(stderr, "xecp_off %d %p %p \n", xecp_off, base, xecp_addr); + dprintf("xecp_off %d %p %p \n", xecp_off, base, xecp_addr); if (XHCI_XECP_CAP_ID(read_reg32(xecp_addr)) == XHCI_XECP_CAP_SP && - XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == 3 && + XHCI_XECP_CAP_SP_MJ(read_reg32(xecp_addr)) == usb_ver && XHCI_XECP_CAP_SP_MN(read_reg32(xecp_addr)) == 0) { port_cnt = XHCI_XECP_CAP_SP_PC(read_reg32(xecp_addr + 2)); port_off = XHCI_XECP_CAP_SP_PO(read_reg32(xecp_addr + 2)); - dprintf(stderr, "PortCount %d Portoffset %d\n", port_cnt, port_off); + dprintf("PortCount %d Portoffset %d\n", port_cnt, port_off); } base = xecp_addr; xecp_off = XHCI_XECP_NEXT_PTR(read_reg32(xecp_addr)); @@ -675,10 +701,8 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) for (i = (port_off - 1); i < (port_off + port_cnt - 1); i++) { prs = &op->prs[i]; portsc = read_reg32(&prs->portsc); - if ((portsc & PORTSC_CCS) && - (portsc & PORTSC_PP) && - (portsc & PORTSC_PED)) { - /* Device present and enabled */ + if (xhci_device_present(portsc, usb_ver)) { + /* Device present */ dprintf("Device present on port %d\n", i); /* Reset the port */ portsc = read_reg32(&prs->portsc); @@ -701,6 +725,11 @@ static int xhci_hub_check_ports(struct xhci_hcd *xhcd) return true; } +static int xhci_hub_check_ports(struct xhci_hcd *xhcd) +{ + return xhci_port_scan(xhcd, USB_XHCI) | xhci_port_scan(xhcd, USB_EHCI); +} + static bool xhci_hcd_init(struct xhci_hcd *xhcd) { struct xhci_op_regs *op; @@ -868,6 +897,18 @@ static bool xhci_hcd_exit(struct xhci_hcd *xhcd) SLOF_dma_map_out(xhcd->dcbaap_dma, (void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); SLOF_dma_free((void *)xhcd->dcbaap, XHCI_DCBAAP_MAX_SIZE); } + + /* + * QEMU implementation of XHCI doesn't implement halt + * properly. It basically says that it's halted immediately + * but doesn't actually terminate ongoing activities and + * DMAs. This needs to be fixed in QEMU. + * + * For now, wait for 50ms grace time till qemu stops using + * this device. + */ + SLOF_msleep(50); + return true; } @@ -1079,18 +1120,17 @@ static inline struct xhci_seg *xhci_pipe_get_seg(struct usb_pipe *pipe) static inline void *xhci_get_trb(struct xhci_seg *seg) { uint64_t val, enq; - uint32_t size; + int index; struct xhci_link_trb *link; enq = val = seg->enq; val = val + XHCI_TRB_SIZE; - size = seg->size * XHCI_TRB_SIZE; - /* TRBs being a cyclic buffer, here we cycle back to beginning. */ - if ((val % size) == 0) { + index = (enq - (uint64_t)seg->trbs) / XHCI_TRB_SIZE + 1; + dprintf("%s: enq %llx, val %llx %x\n", __func__, enq, val, index); + /* TRBs being a cyclic buffer, here we cycle back to beginning. */ + if (index == (seg->size - 1)) { + dprintf("%s: rounding \n", __func__); seg->enq = (uint64_t)seg->trbs; - enq = seg->enq; - seg->enq = seg->enq + XHCI_TRB_SIZE; - val = 0; seg->cycle_state ^= seg->cycle_state; link = (struct xhci_link_trb *) (seg->trbs + seg->size - 1); link->addr = cpu_to_le64(seg->trbs_dma); @@ -1105,6 +1145,12 @@ static inline void *xhci_get_trb(struct xhci_seg *seg) return (void *)enq; } +static uint64_t xhci_get_trb_phys(struct xhci_seg *seg, uint64_t trb) +{ + return seg->trbs_dma + (trb - (uint64_t)seg->trbs); +} + +static int usb_kb = false; static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, void *data, int datalen) { @@ -1114,7 +1160,8 @@ static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, struct xhci_transfer_trb *trb; struct xhci_db_regs *dbr; int ret = true; - uint32_t slot_id, epno; + uint32_t slot_id, epno, time; + uint64_t trb_phys, event_phys; if (!pipe->dev || !pipe->dev->hcidev) { dprintf(" NULL pointer\n"); @@ -1139,13 +1186,26 @@ static int xhci_transfer_bulk(struct usb_pipe *pipe, void *td, void *td_phys, } trb = xhci_get_trb(seg); + trb_phys = xhci_get_trb_phys(seg, (uint64_t)trb); fill_normal_trb(trb, (void *)data, datalen); epno = xhci_get_epno(pipe); write_reg32(&dbr->db[slot_id], epno); - if (!xhci_poll_event(xhcd, 0)) { - dprintf("Bulk failed\n"); - ret = false; + + time = SLOF_GetTimer() + USB_TIMEOUT; + while (1) { + event_phys = xhci_poll_event(xhcd, 0); + if (event_phys == trb_phys) { + break; + } else if (event_phys == 0) { /* polling timed out */ + ret = false; + break; + } else + usb_kb = true; + + /* transfer timed out */ + if (time < SLOF_GetTimer()) + return false; } trb->addr = 0; trb->len = 0; @@ -1214,7 +1274,8 @@ static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) if (!seg->trbs) { if (!xhci_alloc_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK)) { - dprintf("Failed allocating seg\n"); + printf("usb-xhci: allocation failed for bulk endpoint\n"); + return; } } else { xhci_init_seg(seg, XHCI_DATA_TRBS_SIZE, TYPE_BULK); @@ -1235,6 +1296,61 @@ static void xhci_init_bulk_ep(struct usb_dev *dev, struct usb_pipe *pipe) xpipe->seg = seg; } +static int xhci_get_pipe_intr(struct usb_pipe *pipe, + struct xhci_hcd *xhcd, + char *buf, size_t len) +{ + struct xhci_dev *xdev; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_control_ctx *ctrl; + struct xhci_ep_ctx *ep; + uint32_t x_epno, val, type; + struct usb_dev *dev; + struct xhci_transfer_trb *trb; + + dev = pipe->dev; + if (dev->class != DEV_HID_KEYB) + return false; + + xdev = dev->priv; + pipe->mps = 8; + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + type = EP_INT_IN; + seg = &xdev->intr; + + if (!seg->trbs) { + if (!xhci_alloc_seg(seg, XHCI_INTR_TRBS_SIZE, TYPE_BULK)) { + printf("usb-xhci: allocation failed for interrupt endpoint\n"); + return false; + } + } else { + xhci_init_seg(seg, XHCI_EVENT_TRBS_SIZE, TYPE_BULK); + } + + xpipe->buf = buf; + xpipe->buf_phys = SLOF_dma_map_in(buf, len, false); + xpipe->buflen = len; + + ctrl = xhci_get_control_ctx(&xdev->in_ctx); + x_epno = xhci_get_epno(pipe); + ep = xhci_get_ep_ctx(&xdev->in_ctx, xdev->ctx_size, x_epno); + val = EP_TYPE(type) | MAX_BURST(0) | ERROR_COUNT(3) | + MAX_PACKET_SIZE(pipe->mps); + ep->field2 = cpu_to_le32(val); + ep->deq_addr = cpu_to_le64(seg->trbs_dma | seg->cycle_state); + ep->field4 = cpu_to_le32(8); + ctrl->a_flags = cpu_to_le32(BIT(x_epno) | 0x1); + ctrl->d_flags = 0; + xhci_configure_ep(xhcd, xdev->slot_id, xdev->in_ctx.dma_addr); + xpipe->seg = seg; + + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); + return true; +} + static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr *ep, char *buf, size_t len) { struct xhci_hcd *xhcd; @@ -1264,6 +1380,12 @@ static struct usb_pipe* xhci_get_pipe(struct usb_dev *dev, struct usb_ep_descr * new->dir = (ep->bEndpointAddress & 0x80) >> 7; new->epno = ep->bEndpointAddress & 0x0f; + if (new->type == USB_EP_TYPE_INTR) { + if (!xhci_get_pipe_intr(new, xhcd, buf, len)) { + printf("usb-xhci: %s alloc_intr failed %p\n", + __func__, new); + } + } if (new->type == USB_EP_TYPE_BULK) xhci_init_bulk_ep(dev, new); @@ -1284,6 +1406,10 @@ static void xhci_put_pipe(struct usb_pipe *pipe) if (pipe->type == USB_EP_TYPE_BULK) { xpipe = xhci_pipe_get_xpipe(pipe); xpipe->seg = NULL; + } else if (pipe->type == USB_EP_TYPE_INTR) { + xpipe = xhci_pipe_get_xpipe(pipe); + SLOF_dma_map_out(xpipe->buf_phys, xpipe->buf, xpipe->buflen); + xpipe->seg = NULL; } if (xhcd->end) xhcd->end->next = pipe; @@ -1298,6 +1424,51 @@ static void xhci_put_pipe(struct usb_pipe *pipe) dprintf("usb-xhci: %s exit\n", __func__); } +static int xhci_poll_intr(struct usb_pipe *pipe, uint8_t *data) +{ + struct xhci_transfer_trb *trb; + struct xhci_seg *seg; + struct xhci_pipe *xpipe; + struct xhci_dev *xdev; + struct xhci_hcd *xhcd; + struct xhci_db_regs *dbr; + uint32_t x_epno; + uint8_t *buf, ret = 1; + + if (!pipe || !pipe->dev || !pipe->dev->hcidev) + return 0; + xdev = pipe->dev->priv; + xhcd = (struct xhci_hcd *)pipe->dev->hcidev->priv; + x_epno = xhci_get_epno(pipe); + seg = xhci_pipe_get_seg(pipe); + xpipe = xhci_pipe_get_xpipe(pipe); + + if (usb_kb == true) { + /* This event was consumed by bulk transfer */ + usb_kb = false; + goto skip_poll; + } + buf = xpipe->buf; + memset(buf, 0, 8); + + mb(); + /* Ring the doorbell - x_epno */ + dbr = xhcd->db_regs; + write_reg32(&dbr->db[xdev->slot_id], x_epno); + if (!xhci_poll_event(xhcd, 0)) { + printf("poll intr failed\n"); + return 0; + } + mb(); + memcpy(data, buf, 8); + +skip_poll: + trb = xhci_get_trb(seg); + fill_normal_trb(trb, (void *)xpipe->buf_phys, pipe->mps); + mb(); + return ret; +} + struct usb_hcd_ops xhci_ops = { .name = "xhci-hcd", .init = xhci_init, @@ -1305,6 +1476,7 @@ struct usb_hcd_ops xhci_ops = { .usb_type = USB_XHCI, .get_pipe = xhci_get_pipe, .put_pipe = xhci_put_pipe, + .poll_intr = xhci_poll_intr, .send_ctrl = xhci_send_ctrl, .transfer_bulk = xhci_transfer_bulk, .next = NULL, diff --git a/roms/SLOF/lib/libusb/usb-xhci.h b/roms/SLOF/lib/libusb/usb-xhci.h index faeb07ead..3fc7e7889 100644 --- a/roms/SLOF/lib/libusb/usb-xhci.h +++ b/roms/SLOF/lib/libusb/usb-xhci.h @@ -266,6 +266,7 @@ struct xhci_seg { #define XHCI_EVENT_TRBS_SIZE 4096 #define XHCI_CONTROL_TRBS_SIZE 4096 #define XHCI_DATA_TRBS_SIZE 4096 +#define XHCI_INTR_TRBS_SIZE 4096 #define XHCI_ERST_NUM_SEGS 1 #define XHCI_MAX_BULK_SIZE 0xF000 @@ -349,6 +350,7 @@ struct xhci_dev { struct xhci_ctx in_ctx; struct xhci_ctx out_ctx; struct xhci_seg control; + struct xhci_seg intr; struct xhci_seg bulk_in; struct xhci_seg bulk_out; uint32_t ctx_size; @@ -381,6 +383,9 @@ struct xhci_hcd { struct xhci_pipe { struct usb_pipe pipe; struct xhci_seg *seg; + void *buf; + long buf_phys; + uint32_t buflen; }; #endif /* USB_XHCI_H */ diff --git a/roms/SLOF/make.rules b/roms/SLOF/make.rules index aebc4e360..cbc63530a 100644 --- a/roms/SLOF/make.rules +++ b/roms/SLOF/make.rules @@ -19,8 +19,12 @@ ARCH := $(shell uname -p) # Auto-detect ppc64 -ifeq ($(ARCH), ppc64) -CROSS = "" +ifeq (ppc64,$(findstring ppc64,$(ARCH))) + ifeq ($(ARCH), ppc64le) + EXTRA_CC = -mbig -mabi=elfv1 + EXTRA_LD = -mbig + endif +CROSS ?= else CROSS ?= powerpc64-linux- endif @@ -31,8 +35,8 @@ HOSTCC ?= gcc HOSTCFLAGS = -g -Wall -W -O2 -I. -I../include DD = dd -ONLY_CC = $(CROSS)gcc -m$(CELLSIZE) -ONLY_AS = $(CROSS)as -m$(CELLSIZE) +ONLY_CC = $(CROSS)gcc -m$(CELLSIZE) $(EXTRA_CC) +ONLY_AS = $(CROSS)as -m$(CELLSIZE) $(EXTRA_LD) ONLY_LD = $(CROSS)ld -melf$(CELLSIZE)ppc # Verbose level: diff --git a/roms/SLOF/rtas/reloc.S b/roms/SLOF/rtas/reloc.S index e24d293d4..1b5b59a68 100644 --- a/roms/SLOF/rtas/reloc.S +++ b/roms/SLOF/rtas/reloc.S @@ -61,7 +61,7 @@ _rtas_start: ._rtas_entry_offset: .quad rtas_entry-_rtas_start ._rtas_config_offset: .quad rtas_config-_rtas_start ._rtas_stack: .quad .stack-_rtas_start+RTAS_STACKSIZE-0x60 -._rtas_toc: .quad _got-_rtas_start+0x8000 +._rtas_toc: .quad _got-_rtas_start .over: mflr r8 # gpr 8 is the base diff --git a/roms/SLOF/rtas/rtas.lds b/roms/SLOF/rtas/rtas.lds index a5ba1daaf..30b18dd26 100644 --- a/roms/SLOF/rtas/rtas.lds +++ b/roms/SLOF/rtas/rtas.lds @@ -28,7 +28,8 @@ SECTIONS { } .got : { - _got = .; + . = ALIGN(256); + _got = DEFINED (.TOC.) ? .TOC. : ADDR (.got) + 0x8000; *(.got .toc) } .reloc : diff --git a/roms/SLOF/rtas/rtas_entry.S b/roms/SLOF/rtas/rtas_entry.S index 74693aa48..424137bf5 100644 --- a/roms/SLOF/rtas/rtas_entry.S +++ b/roms/SLOF/rtas/rtas_entry.S @@ -39,7 +39,7 @@ rtas_entry: bcl 20,31,.over # branch to over .base: .align 3 -..got: .quad _got-.base+0x8000 +..got: .quad _got-.base ..stack: .quad .stack+RTAS_STACKSIZE-0x60-.base .over: mflr r8 # gpr 8 is the base diff --git a/roms/SLOF/slof/entry.S b/roms/SLOF/slof/entry.S index dcff57ba0..d3d29f852 100644 --- a/roms/SLOF/slof/entry.S +++ b/roms/SLOF/slof/entry.S @@ -207,4 +207,13 @@ call_client: li 3, -1 # client app return blr + + # Call another function via pointer in r6 + # (arguments can be provided in r3 to r5) + # Destination function should jump back to lr +C_ENTRY(call_c) + mtctr r6 + bctr + + .lcomm the_system_stack, STACKSIZE, 16 diff --git a/roms/SLOF/slof/fs/archsupport.fs b/roms/SLOF/slof/fs/archsupport.fs index cc4668769..f564ab4e0 100644 --- a/roms/SLOF/slof/fs/archsupport.fs +++ b/roms/SLOF/slof/fs/archsupport.fs @@ -10,9 +10,9 @@ \ * IBM Corporation - initial implementation \ ****************************************************************************/ -\ Qemu supports max 256cpus, 32K will be able to accomodate the fdt changes if -\ needed. -8000 VALUE size +\ 128KB FDT buffer size is enough to accommodate 255 CPU cores and 1TB of +\ maxmem specification. +20000 VALUE size : ibm,client-architecture-support ( vec -- err? ) \ Store require parameters in nvram \ to come back to right boot device diff --git a/roms/SLOF/slof/fs/base.fs b/roms/SLOF/slof/fs/base.fs index e71e087eb..03e77e54f 100644 --- a/roms/SLOF/slof/fs/base.fs +++ b/roms/SLOF/slof/fs/base.fs @@ -579,8 +579,6 @@ defer cursor-off ( -- ) #include "debug.fs" \ provide 7.5.3.1 Dictionary search #include "dictionary.fs" -\ block data access for IO devices - ought to be implemented in engine -#include "rmove.fs" \ provide a simple run time preprocessor #include <preprocessor.fs> diff --git a/roms/SLOF/slof/fs/boot.fs b/roms/SLOF/slof/fs/boot.fs index 9a0ded0c2..a0fe29a1b 100644 --- a/roms/SLOF/slof/fs/boot.fs +++ b/roms/SLOF/slof/fs/boot.fs @@ -187,11 +187,6 @@ defer go ( -- ) dup to my-self dup ihandle>phandle set-node -rot ( ihandle devstr len ) - my-args nip 0= IF - 2dup 1- + c@ [char] : <> IF \ Add : to device path if missing - 1+ strdup 2dup 1- + [char] : swap c! - THEN - THEN encode-string s" bootpath" set-chosen $bootargs encode-string s" bootargs" set-chosen get-load-base s" load" 3 pick ['] $call-method CATCH IF @@ -211,7 +206,7 @@ defer go ( -- ) : parse-load ( "{devlist}" -- success ) \ Parse-execute boot-device list cr BEGIN parse-word dup WHILE - ( de-alias ) do-load dup 0< IF drop 0 THEN IF + de-alias do-load dup 0< IF drop 0 THEN IF state-valid @ IF ." Successfully loaded" cr THEN true 0d parse strdup load-list 2! EXIT THEN diff --git a/roms/SLOF/slof/fs/client.fs b/roms/SLOF/slof/fs/client.fs index 1b2bb0326..7d537a668 100644 --- a/roms/SLOF/slof/fs/client.fs +++ b/roms/SLOF/slof/fs/client.fs @@ -282,6 +282,18 @@ ALSO client-voc DEFINITIONS ; \ +\ Standard for Boot, defined in 6.3.2.5: +\ +: boot ( zstr -- ) + zcount + debug-client-interface? IF + ." ci: boot " 2dup type cr + THEN + " boot " 2swap $cat " boot-command" $setenv (nvupdate) + reset-all +; + +\ \ User Interface, defined in 6.3.2.6 \ : interpret ( ... zstr -- result ... ) diff --git a/roms/SLOF/slof/fs/fbuffer.fs b/roms/SLOF/slof/fs/fbuffer.fs index 756f05a95..47046087d 100644 --- a/roms/SLOF/slof/fs/fbuffer.fs +++ b/roms/SLOF/slof/fs/fbuffer.fs @@ -19,6 +19,7 @@ 0 VALUE screen-height 0 VALUE screen-width 0 VALUE screen-depth +0 VALUE screen-line-bytes 0 VALUE window-top 0 VALUE window-left @@ -54,10 +55,10 @@ : fb8-background inverse? ; : fb8-foreground inverse? invert ; -: fb8-lines2bytes ( #lines -- #bytes ) char-height * screen-width * screen-depth * ; +: fb8-lines2bytes ( #lines -- #bytes ) char-height * screen-line-bytes * ; : fb8-columns2bytes ( #columns -- #bytes ) char-width * screen-depth * ; : fb8-line2addr ( line# -- addr ) - char-height * window-top + screen-width * screen-depth * + char-height * window-top + screen-line-bytes * frame-buffer-adr + window-left screen-depth * + ; @@ -98,9 +99,10 @@ CREATE bitmap-buffer 400 4 * allot : fb8-toggle-cursor ( -- ) line# fb8-line2addr column# fb8-columns2bytes + - char-height 0 ?DO - char-width screen-depth * 0 ?DO dup dup rb@ -1 xor swap rb! 1+ LOOP - screen-width screen-depth * + char-width screen-depth * - + char-height 2 - screen-line-bytes * + + 2 0 ?DO + dup char-width screen-depth * invert-region + screen-line-bytes + LOOP drop ; @@ -110,7 +112,7 @@ CREATE bitmap-buffer 400 4 * allot line# fb8-line2addr column# fb8-columns2bytes + ( bitmap-buf fb-addr ) char-height 0 ?DO 2dup char-width screen-depth * mrmove - screen-width screen-depth * + >r char-width screen-depth * + r> + screen-line-bytes + >r char-width screen-depth * + r> LOOP 2drop ELSE 2drop r> 3drop THEN ; @@ -135,12 +137,12 @@ CREATE bitmap-buffer 400 4 * allot fb8-columns2bytes swap fb8-columns2bytes tuck - over r@ tuck + rot char-height 0 ?DO 3dup rmove - -rot screen-width screen-depth * tuck + -rot + swap rot + -rot screen-line-bytes tuck + -rot + swap rot LOOP 3drop r> THEN char-height 0 ?DO - dup 2 pick fb8-erase-block screen-width screen-depth * + + dup 2 pick fb8-erase-block screen-line-bytes + LOOP 2drop ; @@ -153,12 +155,12 @@ CREATE bitmap-buffer 400 4 * allot fb8-columns2bytes swap fb8-columns2bytes tuck - over r@ + 2dup + r> swap >r rot char-height 0 ?DO 3dup rmove - -rot screen-width screen-depth * tuck + -rot + swap rot + -rot screen-line-bytes tuck + -rot + swap rot LOOP 3drop r> over - THEN char-height 0 ?DO - dup 2 pick fb8-erase-block screen-width screen-depth * + + dup 2 pick fb8-erase-block screen-line-bytes + LOOP 2drop ; @@ -166,13 +168,11 @@ CREATE bitmap-buffer 400 4 * allot : fb8-reset-screen ( -- ) ( Left as no-op by design ) ; : fb8-erase-screen ( -- ) - frame-buffer-adr screen-height screen-width * screen-depth * fb8-erase-block + frame-buffer-adr screen-height screen-line-bytes * fb8-erase-block ; : fb8-invert-screen ( -- ) - frame-buffer-adr screen-height screen-width * screen-depth * 2dup /x / 0 ?DO - dup rx@ -1 xor over rx! xa1+ - LOOP 3drop + frame-buffer-adr screen-height screen-line-bytes * invert-region ; : fb8-blink-screen ( -- ) fb8-invert-screen fb8-invert-screen ; @@ -180,6 +180,7 @@ CREATE bitmap-buffer 400 4 * allot : fb8-install ( width height #columns #lines -- ) 1 to screen-depth 2swap to screen-height to screen-width + screen-width to screen-line-bytes screen-#rows min to #lines screen-#columns min to #columns screen-height char-height #lines * - 2/ to window-top @@ -201,6 +202,7 @@ CREATE bitmap-buffer 400 4 * allot >r fb8-install r> to screen-depth + screen-width screen-depth * to screen-line-bytes ; diff --git a/roms/SLOF/slof/fs/little-endian.fs b/roms/SLOF/slof/fs/little-endian.fs index f2e4e8d42..6b4779ee0 100644 --- a/roms/SLOF/slof/fs/little-endian.fs +++ b/roms/SLOF/slof/fs/little-endian.fs @@ -17,6 +17,9 @@ here c@ ef = CONSTANT ?littleendian ?bigendian [IF] +: x!-le >r xbflip r> x! ; +: x@-le x@ xbflip ; + : l!-le >r lbflip r> l! ; : l@-le l@ lbflip ; @@ -47,6 +50,9 @@ here c@ ef = CONSTANT ?littleendian [ELSE] +: x!-le x! ; +: x@-le x@ ; + : l!-le l! ; : l@-le l@ ; diff --git a/roms/SLOF/slof/fs/packages/disk-label.fs b/roms/SLOF/slof/fs/packages/disk-label.fs index fe1c25e7a..e034d6408 100644 --- a/roms/SLOF/slof/fs/packages/disk-label.fs +++ b/roms/SLOF/slof/fs/packages/disk-label.fs @@ -20,6 +20,7 @@ false VALUE debug-disk-label? \ If we ever want to put a large kernel with initramfs from a PREP partition \ we might need to increase this value. The default value is 65536 blocks (32MB) d# 65536 value max-prep-partition-blocks +d# 4096 CONSTANT block-array-size s" disk-label" device-name @@ -152,8 +153,8 @@ CONSTANT /gpt-part-entry : init-block ( -- ) s" block-size" ['] $call-parent CATCH IF ABORT" parent has no block-size." THEN to block-size - d# 4096 alloc-mem - dup d# 4096 erase + block-array-size alloc-mem + dup block-array-size erase to block debug-disk-label? IF ." init-block: block-size=" block-size .d ." block=0x" block u. cr @@ -178,7 +179,8 @@ CONSTANT /gpt-part-entry \ This word returns true if the currently loaded block has _NO_ GPT partition id : no-gpt? ( -- true|false ) 0 read-sector - 1 partition>part-entry part-entry>id c@ ee <> + 1 partition>part-entry part-entry>id c@ ee <> IF true EXIT THEN + block mbr>magic w@-le aa55 <> ; : pc-extended-partition? ( part-entry-addr -- true|false ) @@ -266,7 +268,10 @@ CONSTANT /gpt-part-entry : try-dos-partition ( -- okay? ) \ Read partition table and check magic. - no-mbr? IF cr ." No DOS disk-label found." cr false EXIT THEN + no-mbr? IF + debug-disk-label? IF cr ." No DOS disk-label found." cr THEN + false EXIT + THEN count-dos-logical-partitions TO dos-logical-partitions @@ -320,6 +325,14 @@ CONSTANT /gpt-part-entry \ Load from first active DOS boot partition. +: fat-bootblock? ( addr -- flag ) + \ byte 0-2 of the bootblock is a jump instruction in + \ all FAT filesystems. + \ e9 and eb are jump instructions in x86 assembler. + dup c@ e9 = IF drop true EXIT THEN + dup c@ eb = swap 2+ c@ 90 = and +; + \ NOTE: block-size is always 512 bytes for DOS partition tables. : load-from-dos-boot-partition ( addr -- size ) @@ -352,60 +365,103 @@ CONSTANT /gpt-part-entry drop 0 ; -\ Check for GPT PReP partition GUID -9E1A2D38 CONSTANT GPT-PREP-PARTITION-1 -C612 CONSTANT GPT-PREP-PARTITION-2 -4316 CONSTANT GPT-PREP-PARTITION-3 -AA26 CONSTANT GPT-PREP-PARTITION-4 -8B49521E5A8B CONSTANT GPT-PREP-PARTITION-5 +\ Check for GPT PReP partition GUID. Only first 3 blocks are +\ byte-swapped treating last two blocks as contigous for simplifying +\ comparison +9E1A2D38 CONSTANT GPT-PREP-PARTITION-1 +C612 CONSTANT GPT-PREP-PARTITION-2 +4316 CONSTANT GPT-PREP-PARTITION-3 +AA268B49521E5A8B CONSTANT GPT-PREP-PARTITION-4 : gpt-prep-partition? ( -- true|false ) - block gpt-part-entry>part-type-guid l@-le GPT-PREP-PARTITION-1 = IF - block gpt-part-entry>part-type-guid 4 + w@-le - GPT-PREP-PARTITION-2 = IF - block gpt-part-entry>part-type-guid 6 + w@-le - GPT-PREP-PARTITION-3 = IF - block gpt-part-entry>part-type-guid 8 + w@ - GPT-PREP-PARTITION-4 = IF - block gpt-part-entry>part-type-guid a + w@ - block gpt-part-entry>part-type-guid c + l@ swap lxjoin - GPT-PREP-PARTITION-5 = IF - TRUE EXIT - THEN - THEN - THEN - THEN + block gpt-part-entry>part-type-guid + dup l@-le GPT-PREP-PARTITION-1 <> IF drop false EXIT THEN + dup 4 + w@-le GPT-PREP-PARTITION-2 <> IF drop false EXIT THEN + dup 6 + w@-le GPT-PREP-PARTITION-3 <> IF drop false EXIT THEN + 8 + x@ GPT-PREP-PARTITION-4 = +; + +\ Check for GPT MSFT BASIC DATA GUID - fat based +EBD0A0A2 CONSTANT GPT-BASIC-DATA-PARTITION-1 +B9E5 CONSTANT GPT-BASIC-DATA-PARTITION-2 +4433 CONSTANT GPT-BASIC-DATA-PARTITION-3 +87C068B6B72699C7 CONSTANT GPT-BASIC-DATA-PARTITION-4 + +: gpt-basic-data-partition? ( -- true|false ) + block gpt-part-entry>part-type-guid + dup l@-le GPT-BASIC-DATA-PARTITION-1 <> IF drop false EXIT THEN + dup 4 + w@-le GPT-BASIC-DATA-PARTITION-2 <> IF drop false EXIT THEN + dup 6 + w@-le GPT-BASIC-DATA-PARTITION-3 <> IF drop false EXIT THEN + 8 + x@ GPT-BASIC-DATA-PARTITION-4 = +; + +\ +\ GPT Signature +\ ("EFI PART", 45h 46h 49h 20h 50h 41h 52h 54h) +\ +4546492050415254 CONSTANT GPT-SIGNATURE + +\ The routine checks whether the protective MBR has GPT ID and then +\ reads the gpt data from the sector. Also set the seek position and +\ the partition size used in caller routines. + +: get-gpt-partition ( -- true|false ) + no-gpt? IF false EXIT THEN + debug-disk-label? IF cr ." GPT partition found " cr THEN + 1 read-sector + block gpt>part-entry-lba x@-le + block-size * to seek-pos + block gpt>part-entry-size l@-le to gpt-part-size + gpt-part-size block-array-size > IF + cr ." GPT part size exceeds buffer allocated " cr + false exit THEN - FALSE + block gpt>signature x@ GPT-SIGNATURE = ; : load-from-gpt-prep-partition ( addr -- size ) - no-gpt? IF drop FALSE EXIT THEN - debug-disk-label? IF - cr ." GPT partition found " cr - THEN - 1 read-sector block gpt>part-entry-lba l@-le - block-size * to seek-pos - block gpt>part-entry-size l@-le to gpt-part-size - block gpt>num-part-entry l@-le dup 0= IF FALSE EXIT THEN + get-gpt-partition 0= IF false EXIT THEN + block gpt>num-part-entry l@-le dup 0= IF false exit THEN 1+ 1 ?DO seek-pos 0 seek drop block gpt-part-size read drop gpt-prep-partition? IF - debug-disk-label? IF - ." GPT PReP partition found " cr - THEN - block gpt-part-entry>first-lba x@ xbflip - block gpt-part-entry>last-lba x@ xbflip - over - 1+ ( addr offset len ) - swap ( addr len offset ) - block-size * to part-offset - 0 0 seek drop ( addr len ) - block-size * read ( size ) + debug-disk-label? IF ." GPT PReP partition found " cr THEN + block gpt-part-entry>first-lba x@-le ( addr first-lba ) + block gpt-part-entry>last-lba x@-le ( addr first-lba last-lba) + over - 1+ ( addr first-lba blocks ) + swap ( addr blocks first-lba ) + block-size * to part-offset ( addr blocks ) + 0 0 seek drop ( addr blocks ) + block-size * read ( size ) + UNLOOP EXIT + THEN + seek-pos gpt-part-size + to seek-pos + LOOP + false +; + +: try-gpt-dos-partition ( -- true|false ) + get-gpt-partition 0= IF false EXIT THEN + block gpt>num-part-entry l@-le dup 0= IF false EXIT THEN + 1+ 1 ?DO + seek-pos 0 seek drop + block gpt-part-size read drop + gpt-basic-data-partition? IF + debug-disk-label? IF ." GPT BASIC DATA partition found " cr THEN + block gpt-part-entry>first-lba x@-le ( first-lba ) + dup to part-start ( first-lba ) + block gpt-part-entry>last-lba x@-le ( first-lba last-lba ) + over - 1+ ( first-lba s1 ) + block-size * to part-size ( first-lba ) + block-size * to part-offset ( ) + 0 0 seek drop + block block-size read drop + block fat-bootblock? ( true|false ) UNLOOP EXIT THEN - seek-pos gpt-part-size i * + to seek-pos + seek-pos gpt-part-size + to seek-pos LOOP - FALSE + false ; \ Extract the boot loader path from a bootinfo.txt file @@ -493,7 +549,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 debug-disk-label? IF ." Trying CHRP boot " .s cr THEN 1 disk-chrp-boot ! - dup load-chrp-boot-file ?dup 0 <> IF .s cr nip EXIT THEN + dup load-chrp-boot-file ?dup 0 <> IF nip EXIT THEN 0 disk-chrp-boot ! debug-disk-label? IF ." Trying GPT boot " .s cr THEN @@ -558,14 +614,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : try-dos-files ( -- found? ) no-mbr? IF false EXIT THEN - \ block 0 byte 0-2 is a jump instruction in all FAT - \ filesystems. - \ e9 and eb are jump instructions in x86 assembler. - block c@ e9 <> IF - block c@ eb <> - block 2+ c@ 90 <> or - IF false EXIT THEN - THEN + block fat-bootblock? 0= IF false EXIT THEN s" fat-files" (interpose-filesystem) true ; @@ -600,6 +649,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : try-partitions ( -- found? ) try-dos-partition IF try-files EXIT THEN + try-gpt-dos-partition IF try-files EXIT THEN \ try-iso9660-partition IF try-files EXIT THEN \ ... more partition types here... false @@ -610,7 +660,7 @@ AA26 CONSTANT GPT-PREP-PARTITION-4 : close ( -- ) debug-disk-label? IF ." Closing disk-label: block=0x" block u. ." block-size=" block-size .d cr THEN - block d# 4096 free-mem + block block-array-size free-mem ; diff --git a/roms/SLOF/slof/fs/pci-scan.fs b/roms/SLOF/slof/fs/pci-scan.fs index b8b9fe61f..2fdf0e8f5 100644 --- a/roms/SLOF/slof/fs/pci-scan.fs +++ b/roms/SLOF/slof/fs/pci-scan.fs @@ -110,10 +110,13 @@ here 100 allot CONSTANT pci-device-vec dup 100000 + pci-next-mem ! \ and write back with 1MB for bridge over 24 + rtas-config-w@ \ check if 64bit support 1 and IF \ IF 64 bit support - 2dup 20 rshift \ | keep upper 32 bits - swap 28 + rtas-config-l! \ | and write it into the Base-Upper32-bits - pci-max-mem @ 20 rshift \ | fetch max Limit address and keep upper 32 bits - 2 pick 2C + rtas-config-l! \ | and set the Limit + pci-next-mem64 @ 100000000 #aligned \ | read the current Value of 64-bit and align to 4GB boundary + dup 100000000 + pci-next-mem64 x! \ | and write back with 1GB for bridge + 2 pick swap \ | + 20 rshift \ | keep upper 32 bits + swap 28 + rtas-config-l! \ | and write it into the Base-Upper32-bits + pci-max-mem64 @ 20 rshift \ | fetch max Limit address and keep upper 32 bits + 2 pick 2C + rtas-config-l! \ | and set the Limit THEN \ FI 10 rshift \ keep upper 16 bits pci-max-mem @ 1- FFFF0000 and or \ and Insert mmem Limit (set it to max) @@ -129,8 +132,12 @@ here 100 allot CONSTANT pci-device-vec 1- \ make limit one less than boundary over 24 + rtas-config-w@ \ check if 64bit support 1 and IF \ IF 64 bit support - 2dup 20 rshift \ | keep upper 32 bits - swap 2C + rtas-config-l! \ | and write it into the Limit-Upper32-bits + pci-next-mem64 @ 100000000 #aligned \ | Reat current value of 64-bar and align at 4GB + dup pci-next-mem64 x! \ | and write it back + 1- \ | make limite one less than boundary + 2 pick swap \ | + 20 rshift \ | keep upper 32 bits + swap 2C + rtas-config-l! \ | and write it into the Limit-Upper32-bits THEN \ FI FFFF0000 and \ keep upper 16 bits over 24 + rtas-config-l@ 0000FFFF and \ fetch original Value diff --git a/roms/SLOF/slof/fs/rmove.fs b/roms/SLOF/slof/fs/rmove.fs deleted file mode 100644 index c28dba9c4..000000000 --- a/roms/SLOF/slof/fs/rmove.fs +++ /dev/null @@ -1,53 +0,0 @@ -\ ***************************************************************************** -\ * Copyright (c) 2004, 2008 IBM Corporation -\ * All rights reserved. -\ * This program and the accompanying materials -\ * are made available under the terms of the BSD License -\ * which accompanies this distribution, and is available at -\ * http://www.opensource.org/licenses/bsd-license.php -\ * -\ * Contributors: -\ * IBM Corporation - initial implementation -\ ****************************************************************************/ - -defer '(r@) -defer '(r!) -1 VALUE /(r) - - -\ The rest of the code already implemented in prim.in -\ In the end all of this should be moved over there and this file terminated - -: (rfill) ( addr size pattern 'r! /r -- ) - to /(r) to '(r!) ff and - dup 8 lshift or dup 10 lshift or dup 20 lshift or - -rot bounds ?do dup i '(r!) /(r) +loop drop -; - -: (fwrmove) ( src dest size -- ) - >r 0 -rot r> bounds ?do + dup '(r@) i '(r!) /(r) dup +loop 2drop -; - -\ Move from main to device memory -: mrmove ( src dest size -- ) - 3dup or or 7 AND CASE - 0 OF ['] x@ ['] rx! /x ENDOF - 4 OF ['] l@ ['] rl! /l ENDOF - 2 OF ['] w@ ['] rw! /w ENDOF - dup OF ['] c@ ['] rb! /c ENDOF - ENDCASE - ( We already know that source and destination do not overlap ) - to /(r) to '(r!) to '(r@) (fwrmove) -; - -: rfill ( addr size pattern -- ) - 3dup drop or 7 AND CASE - 0 OF ['] rx! /x ENDOF - 4 OF ['] rl! /l ENDOF - 2 OF ['] rw! /w ENDOF - dup OF ['] rb! /c ENDOF - ENDCASE (rfill) -; - - - diff --git a/roms/SLOF/slof/fs/terminal.fs b/roms/SLOF/slof/fs/terminal.fs index 582bedeb3..dc82e7bf4 100644 --- a/roms/SLOF/slof/fs/terminal.fs +++ b/roms/SLOF/slof/fs/terminal.fs @@ -167,6 +167,7 @@ false VALUE stopcsi CREATE twtracebuf 4000 allot twtracebuf 4000 erase twtracebuf VALUE twbp 0 VALUE twbc +0 VALUE twtrace-enabled? : twtrace twbc 4000 = IF 0 to twbc twtracebuf to twbp THEN @@ -176,7 +177,7 @@ twtracebuf VALUE twbp : terminal-write ( addr len -- actual-len ) cursor-off tuck bounds ?DO i c@ - twtrace + twtrace-enabled? IF twtrace THEN esc-on IF esc-process ELSE CASE 1B OF true to esc-on ENDOF diff --git a/roms/SLOF/slof/ppc64.c b/roms/SLOF/slof/ppc64.c index 20d927069..619d95ec7 100644 --- a/roms/SLOF/slof/ppc64.c +++ b/roms/SLOF/slof/ppc64.c @@ -42,24 +42,7 @@ cell *the_heap_start = &the_heap[0]; cell *the_heap_end = &the_heap[HEAP_SIZE / CELLSIZE]; extern void io_putchar(unsigned char); - - -static unsigned long __attribute__((noinline)) -call_c(cell arg0, cell arg1, cell arg2, cell entry) -{ - register unsigned long r3 asm("r3") = arg0.u; - register unsigned long r4 asm("r4") = arg1.u; - register unsigned long r5 asm("r5") = arg2.u; - register unsigned long r6 = entry.u ; - - asm volatile("mflr 31 ; mtctr %4 ; bctrl ; mtlr 31" - : "=r" (r3) - : "r" (r3), "r" (r4), "r" (r5), "r" (r6) - : "ctr", "r6", "r7", "r8", "r9", "r10", "r11", - "r12", "r13", "r31", "lr", "cc"); - - return r3; -} +extern unsigned long call_c(cell arg0, cell arg1, cell arg2, cell entry); long diff --git a/roms/SLOF/slof/prim.code b/roms/SLOF/slof/prim.code index 9fbed7168..bb9e036a9 100644 --- a/roms/SLOF/slof/prim.code +++ b/roms/SLOF/slof/prim.code @@ -520,6 +520,19 @@ PRIM(RMOVE) MIRP +PRIM(MRMOVE) + type_u size = TOS.u; POP; + void *d = TOS.a; POP; + void *s = TOS.a; POP; + FAST_MRMOVE(s, d, size); + MIRP + +PRIM(RFILL) + type_u pat = TOS.u; POP; + type_u size = TOS.u; POP; + void *dst = TOS.a; POP; + FAST_RFILL(dst, size, pat); + MIRP // String compare, case insensitive: // : string=ci ( str1 len1 str2 len2 -- equal? ) diff --git a/roms/SLOF/slof/prim.in b/roms/SLOF/slof/prim.in index 7a0d6a2ed..855f59262 100644 --- a/roms/SLOF/slof/prim.in +++ b/roms/SLOF/slof/prim.in @@ -104,8 +104,9 @@ cod(SEMICOLON) cod(EXECUTE) cod(MOVE) -// cod(RMOVE64) cod(RMOVE) +cod(MRMOVE) +cod(RFILL) cod(ZCOUNT) con(HASH-SIZE HASHSIZE) cod(HASH) diff --git a/roms/config.ipxe.general.h b/roms/config.ipxe.general.h deleted file mode 100644 index 619ee4c15..000000000 --- a/roms/config.ipxe.general.h +++ /dev/null @@ -1,4 +0,0 @@ -#undef BANNER_TIMEOUT -#define BANNER_TIMEOUT 30 -#undef ROM_BANNER_TIMEOUT -#define ROM_BANNER_TIMEOUT 0 diff --git a/roms/ipxe/COPYING b/roms/ipxe/COPYING index a43ea2126..342330bb9 100644 --- a/roms/ipxe/COPYING +++ b/roms/ipxe/COPYING @@ -1,339 +1,12 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 +In general iPXE files are licensed under the GPL. For historical +reasons, individual files may contain their own licence declarations. +Most builds of iPXE do not contain all iPXE code (in particular, most +builds will include only one driver), and so the overall licence can +vary depending on what target you are building. - 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. +The resultant applicable licence(s) for any particular build can be +determined by using "make bin/xxxxxxx.yyy.licence"; for example: - Preamble + make bin/rtl8139.rom.licence - 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. - - <one line to give the program's name and a brief idea of what it does.> - Copyright (C) 19yy <name of author> - - 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. - - <signature of Ty Coon>, 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. +to determine the resultant licence(s) for the build bin/rtl8139.rom diff --git a/roms/ipxe/COPYING.GPLv2 b/roms/ipxe/COPYING.GPLv2 new file mode 100644 index 000000000..d159169d1 --- /dev/null +++ b/roms/ipxe/COPYING.GPLv2 @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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 Lesser 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 + + 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. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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., + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 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) year 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. + + <signature of Ty Coon>, 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 Lesser General +Public License instead of this License. diff --git a/roms/ipxe/COPYING.UBDL b/roms/ipxe/COPYING.UBDL new file mode 100644 index 000000000..780ddcd77 --- /dev/null +++ b/roms/ipxe/COPYING.UBDL @@ -0,0 +1,59 @@ +UNMODIFIED BINARY DISTRIBUTION LICENCE + + +PREAMBLE + +The GNU General Public License provides a legal guarantee that +software covered by it remains free (in the sense of freedom, not +price). It achieves this guarantee by imposing obligations on anyone +who chooses to distribute the software. + +Some of these obligations may be seen as unnecessarily burdensome. In +particular, when the source code for the software is already publicly +and freely available, there is minimal value in imposing upon each +distributor the obligation to provide the complete source code (or an +equivalent written offer to provide the complete source code). + +This Licence allows for the distribution of unmodified binaries built +from publicly available source code, without imposing the obligations +of the GNU General Public License upon anyone who chooses to +distribute only the unmodified binaries built from that source code. + +The extra permissions granted by this Licence apply only to unmodified +binaries built from source code which has already been made available +to the public in accordance with the terms of the GNU General Public +Licence. Nothing in this Licence allows for the creation of +closed-source modified versions of the Program. Any modified versions +of the Program are subject to the usual terms and conditions of the +GNU General Public License. + + +TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + +This Licence 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 Unmodified Binary Distribution Licence. All +terms used in the text of this Licence are to be interpreted as they +are used in version 2 of the GNU General Public License as published +by the Free Software Foundation. + +If you have made this Program available to the public in both source +code and executable form in accordance with 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, then you are hereby granted an additional permission to use, +copy, and distribute the unmodified executable form of this Program +(the "Unmodified Binary") without restriction, including the right to +permit persons to whom the Unmodified Binary is furnished to do +likewise, subject to the following conditions: + +- when started running, the Program must display an announcement which + includes the details of your existing publication of the Program + made in accordance with the terms of the GNU General Public License. + For example, the Program could display the URL of the publicly + available source code from which the Unmodified Binary was built. + +- when exercising your right to grant permissions under this Licence, + you do not need to refer directly to the text of this Licence, but + you may not grant permissions beyond those granted to you by this + Licence. diff --git a/roms/ipxe/COPYRIGHTS b/roms/ipxe/COPYRIGHTS deleted file mode 100644 index 342330bb9..000000000 --- a/roms/ipxe/COPYRIGHTS +++ /dev/null @@ -1,12 +0,0 @@ -In general iPXE files are licensed under the GPL. For historical -reasons, individual files may contain their own licence declarations. -Most builds of iPXE do not contain all iPXE code (in particular, most -builds will include only one driver), and so the overall licence can -vary depending on what target you are building. - -The resultant applicable licence(s) for any particular build can be -determined by using "make bin/xxxxxxx.yyy.licence"; for example: - - make bin/rtl8139.rom.licence - -to determine the resultant licence(s) for the build bin/rtl8139.rom diff --git a/roms/ipxe/src/Makefile b/roms/ipxe/src/Makefile index b742d1283..2a9cc9e8f 100644 --- a/roms/ipxe/src/Makefile +++ b/roms/ipxe/src/Makefile @@ -83,11 +83,13 @@ SRCDIRS += drivers/block SRCDIRS += drivers/nvs SRCDIRS += drivers/bitbash SRCDIRS += drivers/infiniband +SRCDIRS += drivers/usb SRCDIRS += interface/pxe interface/efi interface/smbios SRCDIRS += interface/bofm SRCDIRS += interface/xen +SRCDIRS += interface/hyperv SRCDIRS += tests -SRCDIRS += crypto crypto/axtls crypto/matrixssl +SRCDIRS += crypto crypto/mishmash SRCDIRS += hci hci/commands hci/tui SRCDIRS += hci/mucurses hci/mucurses/widgets SRCDIRS += hci/keymap diff --git a/roms/ipxe/src/Makefile.housekeeping b/roms/ipxe/src/Makefile.housekeeping index 1a75d3939..03800c8ef 100644 --- a/roms/ipxe/src/Makefile.housekeeping +++ b/roms/ipxe/src/Makefile.housekeeping @@ -157,17 +157,6 @@ SP_FLAGS := $(shell $(SP_TEST) && $(ECHO) '-fno-stack-protector') WORKAROUND_CFLAGS += $(SP_FLAGS) endif -# Some widespread patched versions of gcc include -fPIE -Wl,-pie by -# default. Note that gcc will exit *successfully* if it fails to -# recognise an option that starts with "no", so we have to test for -# output on stderr instead of checking the exit status. -# -ifeq ($(CCTYPE),gcc) -PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] -PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') -WORKAROUND_CFLAGS += $(PIE_FLAGS) -endif - # gcc 4.4 generates .eh_frame sections by default, which distort the # output of "size". Inhibit this. # @@ -533,6 +522,7 @@ endif # COMPILE_c = $(CC) $(CFLAGS) $(CFLAGS_c) $(OBJ_CFLAGS) RULE_c = $(Q)$(COMPILE_c) -c $< -o $@ $(POST_O) +RULE_c_to_ids.o = $(Q)$(ECHO_E) '$(OBJ_IDS_ASM_NL)' | $(ASSEMBLE_S) -o $@ RULE_c_to_dbg%.o = $(Q)$(COMPILE_c) -DDBGLVL_MAX=$* -c $< -o $@ $(POST_O) RULE_c_to_c = $(Q)$(COMPILE_c) -E -c $< > $@ RULE_c_to_s = $(Q)$(COMPILE_c) -S -g0 -c $< -o $@ @@ -543,7 +533,7 @@ RULE_S = $(Q)$(PREPROCESS_S) $< | $(ASSEMBLE_S) -o $@ RULE_S_to_dbg%.o = $(Q)$(PREPROCESS_S) -DDBGLVL_MAX=$* $< | $(ASSEMBLE_S) -o $@ RULE_S_to_s = $(Q)$(PREPROCESS_S) $< > $@ -DEBUG_TARGETS += dbg%.o c s +GENERIC_TARGETS += ids.o dbg%.o c s # List of embedded images included in the last build of embedded.o. # This is needed in order to correctly rebuild embedded.o whenever the @@ -775,8 +765,6 @@ define deps_template_parts $(Q)$(CPP) $(CFLAGS) $(CFLAGS_$(2)) $(CFLAGS_$(3)) -DOBJECT=$(3) \ -Wno-error -M $(1) -MG -MP | \ sed 's/\.o\s*:/_DEPS +=/' > $(BIN)/deps/$(1).d - $(Q)$(if $(findstring drivers/,$(1)),\ - $(PERL) $(PARSEROM) $(1) >> $(BIN)/deps/$(1).d) endef # rules_template : generate rules for a given source file @@ -796,7 +784,7 @@ $$(BIN)/$(3).o : $(1) $$(MAKEDEPS) $$(POST_O_DEPS) $$($(3)_DEPS) $$(QM)$(ECHO) " [BUILD] $$@" $$(RULE_$(2)) BOBJS += $$(BIN)/$(3).o -$(foreach TGT,$(DEBUG_TARGETS),$(if $(RULE_$(2)_to_$(TGT)),$(NEWLINE)$(call rules_template_target,$(1),$(2),$(3),$(TGT)))) +$(foreach TGT,$(GENERIC_TARGETS),$(if $(RULE_$(2)_to_$(TGT)),$(NEWLINE)$(call rules_template_target,$(1),$(2),$(3),$(TGT)))) $$(BIN)/deps/$(1).d : $$($(3)_DEPS) TAGS : $$($(3)_DEPS) endef @@ -824,7 +812,7 @@ endef # Generate the dependency files # -$(BIN)/deps/%.d : % $(MAKEDEPS) $(PARSEROM) +$(BIN)/deps/%.d : % $(MAKEDEPS) $(call deps_template_file,$<) # Calculate list of dependency files @@ -866,10 +854,69 @@ endif endif endif -# The following variables are created by the rules files +# Files to be parsed using parserom.pl +# +ROM_SRCS = $(foreach SRC,$(AUTO_SRCS),\ + $(if $(findstring drivers/,$(SRC)),$(SRC))) +romsrcs : + @$(ECHO) $(ROM_SRCS) + +# List of files to be parsed using parserom.pl +# +ROM_SRCS_LIST := $(BIN)/.rom.list +ifeq ($(wildcard $(ROM_SRCS_LIST)),) +ROM_SRCS_OLD := <invalid> +else +ROM_SRCS_OLD := $(shell cat $(ROM_SRCS_LIST)) +endif +ifneq ($(ROM_SRCS_OLD),$(ROM_SRCS)) +$(shell $(ECHO) "$(ROM_SRCS)" > $(ROM_SRCS_LIST)) +endif + +$(ROM_SRCS_LIST) : $(MAKEDEPS) + +VERYCLEANUP += $(ROM_SRCS_LIST) + +# ROM definition file +# +ROMDEFS = $(BIN)/.rom.defs +$(ROMDEFS) : $(ROM_SRCS) $(ROM_SRCS_LIST) $(PARSEROM) $(MAKEDEPS) + $(QM)$(ECHO) " [PARSEROM]" + $(Q)$(PERL) $(PARSEROM) $(ROM_SRCS) > $@ + +VERYCLEANUP += $(ROMDEFS) + +# Evaluate ROM definition file +ifdef NEED_DEPS +ifneq ($(ROM_SRCS),) +-include $(ROMDEFS) +endif +endif + +# Device ID tables (using IDs from ROM definition file) +# +define obj_pci_id_asm + .section ".pci_devlist.$(1)", "a", @progbits + .globl pci_devlist_$(1) +pci_devlist_$(1): + .short ( 0x$(1) & 0xffff ) + +endef +define obj_isa_id_asm +endef +OBJ_IDS_ASM = $(foreach ROM,$(ROMS_$(OBJECT)),$(call obj_$(ROM_TYPE_$(ROM))_id_asm,$(ROM))) +OBJ_IDS_ASM_NL = $(subst $(NEWLINE),\n,$(OBJ_IDS_ASM)) +$(BIN)/%.ids : + @$(ECHO_E) '$(OBJ_IDS_ASM_NL)' + +BOBJS += $(patsubst %,$(BIN)/%.ids.o,$(DRIVERS)) + +# The following variables are created by the autogenerated rules # bobjs : @$(ECHO) $(BOBJS) +drivers_% : + @$(ECHO) $(DRIVERS_$*) drivers : @$(ECHO) $(DRIVERS) .PHONY : drivers @@ -900,6 +947,11 @@ $(BIN)/NIC : $(AUTO_DEPS) @perl -ne 'chomp; print "$$1\n" if /\# NIC\t(.*)$$/' $^ >> $@ CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern +# Select drivers to be included in the all-drivers build +# +DRIVERS_ipxe = $(DRIVERS_net) $(DRIVERS_infiniband) \ + $(DRIVERS_xen) $(DRIVERS_hyperv) + # Analyse a target name (e.g. "bin/dfe538--prism2_pci.rom.tmp") and # derive the variables: # @@ -908,7 +960,6 @@ CLEANUP += $(BIN)/NIC # Doesn't match the $(BIN)/*.* pattern # TGT_DRIVERS : the driver for each element (e.g. "rtl8139 prism2_pci") # TGT_ROM_NAME : the ROM name (e.g. "dfe538") # -DRIVERS_ipxe = $(DRIVERS) CARD_DRIVER = $(firstword $(DRIVER_$(1)) $(1)) TGT_ELEMENTS = $(subst --, ,$(firstword $(subst ., ,$(notdir $@)))) TGT_ROM_NAME = $(firstword $(TGT_ELEMENTS)) @@ -941,6 +992,8 @@ TGT_PCI_DEVICE = $(PCI_DEVICE_$(TGT_ROM_NAME)) TGT_LD_DRIVERS = $(subst -,_,$(patsubst %,obj_%,$(TGT_DRIVERS))) TGT_LD_IDS = pci_vendor_id=$(firstword $(TGT_PCI_VENDOR) 0) \ pci_device_id=$(firstword $(TGT_PCI_DEVICE) 0) +TGT_LD_DEVLIST = $(foreach ELEM,$(TGT_ELEMENTS),$(if $(PCI_VENDOR_$(ELEM)),\ + pci_devlist_$(patsubst 0x%,%,$(PCI_VENDOR_$(ELEM)))$(patsubst 0x%,%,$(PCI_DEVICE_$(ELEM))))) TGT_LD_ENTRY = _$(TGT_PREFIX)_start # Calculate linker flags based on link-time options for the current @@ -951,7 +1004,8 @@ TGT_LD_ENTRY = _$(TGT_PREFIX)_start # "-u obj_zpciprefix -u obj_rtl8139 -u obj_prism2_pci # --defsym pci_vendor=0x1186 --defsym pci_device=0x1300") # -TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) obj_config,\ +TGT_LD_FLAGS = $(foreach SYM,$(TGT_LD_ENTRY) $(TGT_LD_DRIVERS) \ + $(TGT_LD_DEVLIST) obj_config,\ -u $(SYM) --defsym check_$(SYM)=$(SYM) ) \ $(patsubst %,--defsym %,$(TGT_LD_IDS)) \ -e $(TGT_LD_ENTRY) @@ -981,6 +1035,7 @@ $(BIN)/%.info : @$(ECHO) @$(ECHO) 'LD driver symbols : $(TGT_LD_DRIVERS)' @$(ECHO) 'LD ID symbols : $(TGT_LD_IDS)' + @$(ECHO) 'LD devlist symbols : $(TGT_LD_DEVLIST)' @$(ECHO) 'LD entry point : $(TGT_LD_ENTRY)' @$(ECHO) @$(ECHO) 'LD target flags : $(TGT_LD_FLAGS)' @@ -1012,7 +1067,7 @@ BLIB = $(BIN)/blib.a $(BLIB) : $(BLIB_OBJS) $(BLIB_LIST) $(MAKEDEPS) $(Q)$(RM) $(BLIB) $(QM)$(ECHO) " [AR] $@" - $(Q)$(AR) r $@ $(BLIB_OBJS) + $(Q)$(AR) r $@ $(sort $(BLIB_OBJS)) $(Q)$(RANLIB) $@ blib : $(BLIB) @@ -1231,15 +1286,12 @@ endif # defined(BIN) # # The compression utilities # -$(NRV2B) : util/nrv2b.c $(MAKEDEPS) - $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -DENCODE -DDECODE -DMAIN -DVERBOSE \ - -DNDEBUG -DBITSIZE=32 -DENDIAN=0 -o $@ $< -CLEANUP += $(NRV2B) -$(ZBIN) : util/zbin.c util/nrv2b.c $(MAKEDEPS) +ZBIN_LDFLAGS := -llzma + +$(ZBIN) : util/zbin.c $(MAKEDEPS) $(QM)$(ECHO) " [HOSTCC] $@" - $(Q)$(HOST_CC) $(HOST_CFLAGS) -o $@ $< + $(Q)$(HOST_CC) $(HOST_CFLAGS) $< $(ZBIN_LDFLAGS) -o $@ CLEANUP += $(ZBIN) ############################################################################### @@ -1319,31 +1371,6 @@ endif ############################################################################### # -# Auto-incrementing build serial number. Append "bs" to your list of -# build targets to get a serial number printed at the end of the -# build. Enable -DBUILD_SERIAL in order to see it when the code runs. -# -BUILDSERIAL_H = config/.buildserial.h -BUILDSERIAL_NOW = config/.buildserial.now -BUILDSERIAL_NEXT = config/.buildserial.next - -$(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) : - $(ECHO) 1 > $@ - -$(BUILDSERIAL_H) : $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) - $(ECHO) '#define BUILD_SERIAL_NUM $(shell cat $<)' > $@ - -ifeq ($(filter bs,$(MAKECMDGOALS)),bs) -$(shell diff -q $(BUILDSERIAL_NOW) $(BUILDSERIAL_NEXT) > /dev/null || \ - cp -f $(BUILDSERIAL_NEXT) $(BUILDSERIAL_NOW)) -endif - -bs : $(BUILDSERIAL_NOW) - @$(ECHO) $$(( $(shell cat $<) + 1 )) > $(BUILDSERIAL_NEXT) - @$(ECHO) "Build serial number is $(shell cat $<)" - -############################################################################### -# # Build the TAGS file(s) for emacs # TAGS : diff --git a/roms/ipxe/src/arch/i386/Makefile b/roms/ipxe/src/arch/i386/Makefile index 4925cc4e6..99f875314 100644 --- a/roms/ipxe/src/arch/i386/Makefile +++ b/roms/ipxe/src/arch/i386/Makefile @@ -69,6 +69,17 @@ CFLAGS += -fshort-wchar # CFLAGS += -Ui386 +# Some widespread patched versions of gcc include -fPIE -Wl,-pie by +# default. Note that gcc will exit *successfully* if it fails to +# recognise an option that starts with "no", so we have to test for +# output on stderr instead of checking the exit status. +# +ifeq ($(CCTYPE),gcc) +PIE_TEST = [ -z "`$(CC) -fno-PIE -nopie -x c -c /dev/null -o /dev/null 2>&1`" ] +PIE_FLAGS := $(shell $(PIE_TEST) && $(ECHO) '-fno-PIE -nopie') +WORKAROUND_CFLAGS += $(PIE_FLAGS) +endif + # Define version string for lkrnprefix.S # CFLAGS_lkrnprefix += -DVERSION="\"$(VERSION)\"" diff --git a/roms/ipxe/src/arch/i386/core/basemem_packet.c b/roms/ipxe/src/arch/i386/core/basemem_packet.c index 06ffa3bbd..9f5fbf330 100644 --- a/roms/ipxe/src/arch/i386/core/basemem_packet.c +++ b/roms/ipxe/src/arch/i386/core/basemem_packet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/core/cachedhcp.c b/roms/ipxe/src/arch/i386/core/cachedhcp.c index 3cac28e7d..a5c624035 100644 --- a/roms/ipxe/src/arch/i386/core/cachedhcp.c +++ b/roms/ipxe/src/arch/i386/core/cachedhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/arch/i386/core/gdbmach.c b/roms/ipxe/src/arch/i386/core/gdbmach.c index 4d6897f7d..d92a4ac08 100644 --- a/roms/ipxe/src/arch/i386/core/gdbmach.c +++ b/roms/ipxe/src/arch/i386/core/gdbmach.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdio.h> diff --git a/roms/ipxe/src/arch/i386/core/patch_cf.S b/roms/ipxe/src/arch/i386/core/patch_cf.S index 97a62f494..4365563fe 100644 --- a/roms/ipxe/src/arch/i386/core/patch_cf.S +++ b/roms/ipxe/src/arch/i386/core/patch_cf.S @@ -14,9 +14,13 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/roms/ipxe/src/arch/i386/core/pci_autoboot.c b/roms/ipxe/src/arch/i386/core/pci_autoboot.c index a3eb1f97d..337598091 100644 --- a/roms/ipxe/src/arch/i386/core/pci_autoboot.c +++ b/roms/ipxe/src/arch/i386/core/pci_autoboot.c @@ -16,9 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/device.h> diff --git a/roms/ipxe/src/arch/i386/core/rdtsc_timer.c b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c index 2f31afc66..e720a239c 100644 --- a/roms/ipxe/src/arch/i386/core/rdtsc_timer.c +++ b/roms/ipxe/src/arch/i386/core/rdtsc_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -27,7 +31,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <assert.h> #include <ipxe/timer.h> -#include <ipxe/timer2.h> +#include <ipxe/pit8254.h> /** * Number of TSC ticks per microsecond @@ -56,10 +60,10 @@ static void rdtsc_udelay ( unsigned long usecs ) { elapsed = ( currticks() - start ); } while ( elapsed < ( usecs * rdtsc_ticks_per_usec ) ); } else { - /* Not yet calibrated; use timer2 and calibrate + /* Not yet calibrated; use 8254 PIT and calibrate * based on result. */ - timer2_udelay ( usecs ); + pit8254_udelay ( usecs ); elapsed = ( currticks() - start ); rdtsc_ticks_per_usec = ( elapsed / usecs ); DBG ( "RDTSC timer calibrated: %ld ticks in %ld usecs " diff --git a/roms/ipxe/src/arch/i386/core/relocate.c b/roms/ipxe/src/arch/i386/core/relocate.c index 5fbf2d2c2..54ad387e4 100644 --- a/roms/ipxe/src/arch/i386/core/relocate.c +++ b/roms/ipxe/src/arch/i386/core/relocate.c @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * The linker passes in the symbol _max_align, which is the alignment diff --git a/roms/ipxe/src/arch/i386/core/runtime.c b/roms/ipxe/src/arch/i386/core/runtime.c index 18ca7936e..d160fee04 100644 --- a/roms/ipxe/src/arch/i386/core/runtime.c +++ b/roms/ipxe/src/arch/i386/core/runtime.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/core/setjmp.S b/roms/ipxe/src/arch/i386/core/setjmp.S index 03727148c..81d3b4911 100644 --- a/roms/ipxe/src/arch/i386/core/setjmp.S +++ b/roms/ipxe/src/arch/i386/core/setjmp.S @@ -1,42 +1,64 @@ -/* setjmp and longjmp. Use of these functions is deprecated. */ - -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 .code32 - -/************************************************************************** -SETJMP - Save stack context for non-local goto -**************************************************************************/ + + /* Must match jmp_buf structure layout */ + .struct 0 +env_retaddr: .long 0 +env_stack: .long 0 +env_ebx: .long 0 +env_esi: .long 0 +env_edi: .long 0 +env_ebp: .long 0 + .previous + +/* + * Save stack context for non-local goto + */ .globl setjmp setjmp: - movl 4(%esp),%ecx /* jmpbuf */ - movl 0(%esp),%edx /* return address */ - movl %edx,0(%ecx) - movl %ebx,4(%ecx) - movl %esp,8(%ecx) - movl %ebp,12(%ecx) - movl %esi,16(%ecx) - movl %edi,20(%ecx) - movl $0,%eax + /* Get jmp_buf pointer in %edx */ + movl 4(%esp),%edx + /* Save return address */ + movl 0(%esp),%eax + movl %eax, env_retaddr(%edx) + /* Save stack pointer */ + movl %esp, env_stack(%edx) + /* Save other registers */ + movl %ebx, env_ebx(%edx) + movl %esi, env_esi(%edx) + movl %edi, env_edi(%edx) + movl %ebp, env_ebp(%edx) + /* Return 0 when returning as setjmp() */ + xorl %eax, %eax ret + .size setjmp, . - setjmp -/************************************************************************** -LONGJMP - Non-local jump to a saved stack context -**************************************************************************/ +/* + * Non-local jump to a saved stack context + */ .globl longjmp longjmp: - movl 4(%esp),%edx /* jumpbuf */ - movl 8(%esp),%eax /* result */ - movl 0(%edx),%ecx - movl 4(%edx),%ebx - movl 8(%edx),%esp - movl 12(%edx),%ebp - movl 16(%edx),%esi - movl 20(%edx),%edi - cmpl $0,%eax - jne 1f - movl $1,%eax -1: movl %ecx,0(%esp) + /* Get jmp_buf pointer in %edx */ + movl 4(%esp),%edx + /* Get result in %eax */ + movl 8(%esp),%eax + /* Force result to non-zero */ + testl %eax, %eax + jnz 1f + incl %eax +1: /* Restore stack pointer */ + movl env_stack(%edx), %esp + /* Restore other registers */ + movl env_ebx(%edx), %ebx + movl env_esi(%edx), %esi + movl env_edi(%edx), %edi + movl env_ebp(%edx), %ebp + /* Replace return address on the new stack */ + popl %ecx /* discard */ + pushl env_retaddr(%edx) + /* Return to setjmp() caller */ ret + .size longjmp, . - longjmp diff --git a/roms/ipxe/src/arch/i386/core/stack.S b/roms/ipxe/src/arch/i386/core/stack.S index 737ec0eed..98f1cd9b9 100644 --- a/roms/ipxe/src/arch/i386/core/stack.S +++ b/roms/ipxe/src/arch/i386/core/stack.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/roms/ipxe/src/arch/i386/core/stack16.S b/roms/ipxe/src/arch/i386/core/stack16.S index 523f0288b..4bc6f081a 100644 --- a/roms/ipxe/src/arch/i386/core/stack16.S +++ b/roms/ipxe/src/arch/i386/core/stack16.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/roms/ipxe/src/arch/i386/core/timer2.c b/roms/ipxe/src/arch/i386/core/timer2.c deleted file mode 100644 index 077866562..000000000 --- a/roms/ipxe/src/arch/i386/core/timer2.c +++ /dev/null @@ -1,87 +0,0 @@ -/* - * arch/i386/core/i386_timer.c - * - * Use the "System Timer 2" to implement the udelay callback in - * the BIOS timer driver. Also used to calibrate the clock rate - * in the RTDSC timer driver. - * - * 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, or (at - * your option) any later version. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stddef.h> -#include <ipxe/timer2.h> -#include <ipxe/io.h> - -/* Timers tick over at this rate */ -#define TIMER2_TICKS_PER_SEC 1193180U - -/* Parallel Peripheral Controller Port B */ -#define PPC_PORTB 0x61 - -/* Meaning of the port bits */ -#define PPCB_T2OUT 0x20 /* Bit 5 */ -#define PPCB_SPKR 0x02 /* Bit 1 */ -#define PPCB_T2GATE 0x01 /* Bit 0 */ - -/* Ports for the 8254 timer chip */ -#define TIMER2_PORT 0x42 -#define TIMER_MODE_PORT 0x43 - -/* Meaning of the mode bits */ -#define TIMER0_SEL 0x00 -#define TIMER1_SEL 0x40 -#define TIMER2_SEL 0x80 -#define READBACK_SEL 0xC0 - -#define LATCH_COUNT 0x00 -#define LOBYTE_ACCESS 0x10 -#define HIBYTE_ACCESS 0x20 -#define WORD_ACCESS 0x30 - -#define MODE0 0x00 -#define MODE1 0x02 -#define MODE2 0x04 -#define MODE3 0x06 -#define MODE4 0x08 -#define MODE5 0x0A - -#define BINARY_COUNT 0x00 -#define BCD_COUNT 0x01 - -static void load_timer2 ( unsigned int ticks ) { - /* - * Now let's take care of PPC channel 2 - * - * Set the Gate high, program PPC channel 2 for mode 0, - * (interrupt on terminal count mode), binary count, - * load 5 * LATCH count, (LSB and MSB) to begin countdown. - * - * Note some implementations have a bug where the high bits byte - * of channel 2 is ignored. - */ - /* Set up the timer gate, turn off the speaker */ - /* Set the Gate high, disable speaker */ - outb((inb(PPC_PORTB) & ~PPCB_SPKR) | PPCB_T2GATE, PPC_PORTB); - /* binary, mode 0, LSB/MSB, Ch 2 */ - outb(TIMER2_SEL|WORD_ACCESS|MODE0|BINARY_COUNT, TIMER_MODE_PORT); - /* LSB of ticks */ - outb(ticks & 0xFF, TIMER2_PORT); - /* MSB of ticks */ - outb(ticks >> 8, TIMER2_PORT); -} - -static int timer2_running ( void ) { - return ((inb(PPC_PORTB) & PPCB_T2OUT) == 0); -} - -void timer2_udelay ( unsigned long usecs ) { - load_timer2 ( ( usecs * TIMER2_TICKS_PER_SEC ) / ( 1000 * 1000 ) ); - while (timer2_running()) { - /* Do nothing */ - } -} diff --git a/roms/ipxe/src/arch/i386/core/virtaddr.S b/roms/ipxe/src/arch/i386/core/virtaddr.S index 5e5d77352..425591570 100644 --- a/roms/ipxe/src/arch/i386/core/virtaddr.S +++ b/roms/ipxe/src/arch/i386/core/virtaddr.S @@ -4,7 +4,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #include "librm.h" diff --git a/roms/ipxe/src/arch/i386/drivers/net/undi.c b/roms/ipxe/src/arch/i386/drivers/net/undi.c index 2bc54824c..9820cf629 100644 --- a/roms/ipxe/src/arch/i386/drivers/net/undi.c +++ b/roms/ipxe/src/arch/i386/drivers/net/undi.c @@ -68,10 +68,6 @@ static int undipci_probe ( struct pci_device *pci ) { struct undi_rom *undirom; int rc; - /* Ignore non-network devices */ - if ( PCI_BASE_CLASS ( pci->class ) != PCI_BASE_CLASS_NETWORK ) - return -ENOTTY; - /* Allocate UNDI device structure */ undi = zalloc ( sizeof ( *undi ) ); if ( ! undi ) @@ -138,12 +134,13 @@ static void undipci_remove ( struct pci_device *pci ) { } static struct pci_device_id undipci_nics[] = { -PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ), + PCI_ROM ( 0xffff, 0xffff, "undipci", "UNDI (PCI)", 0 ), }; struct pci_driver undipci_driver __pci_driver_fallback = { .ids = undipci_nics, .id_count = ( sizeof ( undipci_nics ) / sizeof ( undipci_nics[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_NETWORK, PCI_ANY_ID, PCI_ANY_ID ), .probe = undipci_probe, .remove = undipci_remove, }; diff --git a/roms/ipxe/src/arch/i386/drivers/net/undiload.c b/roms/ipxe/src/arch/i386/drivers/net/undiload.c index 77134dcb8..7160ee384 100644 --- a/roms/ipxe/src/arch/i386/drivers/net/undiload.c +++ b/roms/ipxe/src/arch/i386/drivers/net/undiload.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/arch/i386/drivers/net/undionly.c b/roms/ipxe/src/arch/i386/drivers/net/undionly.c index 028fac5d9..70dbe4bfd 100644 --- a/roms/ipxe/src/arch/i386/drivers/net/undionly.c +++ b/roms/ipxe/src/arch/i386/drivers/net/undionly.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/arch/i386/drivers/net/undipreload.c b/roms/ipxe/src/arch/i386/drivers/net/undipreload.c index 81d7a80eb..fca771843 100644 --- a/roms/ipxe/src/arch/i386/drivers/net/undipreload.c +++ b/roms/ipxe/src/arch/i386/drivers/net/undipreload.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <realmode.h> #include <undipreload.h> diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c b/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c index b23f2c356..6a46081aa 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/basemem.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c index bd73838b5..63413cdc1 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/bios_console.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <assert.h> #include <realmode.h> @@ -39,6 +43,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ATTR_FCOL_YELLOW 0x06 #define ATTR_FCOL_WHITE 0x07 +#define ATTR_BLINK 0x80 + #define ATTR_BCOL_MASK 0x70 #define ATTR_BCOL_BLACK 0x00 #define ATTR_BCOL_BLUE 0x10 @@ -137,8 +143,12 @@ static void bios_handle_sgr ( struct ansiesc_context *ctx __unused, bios_attr = ATTR_DEFAULT; } else if ( aspect == 1 ) { bios_attr |= ATTR_BOLD; + } else if ( aspect == 5 ) { + bios_attr |= ATTR_BLINK; } else if ( aspect == 22 ) { bios_attr &= ~ATTR_BOLD; + } else if ( aspect == 25 ) { + bios_attr &= ~ATTR_BLINK; } else if ( ( aspect >= 30 ) && ( aspect <= 39 ) ) { bios_attr &= ~ATTR_FCOL_MASK; bios_attr |= bios_attr_fcols[ aspect - 30 ]; diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S b/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S index cea17ef8e..d5d97b482 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/e820mangler.S @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c b/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c index e5f713728..15f4d772f 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/fakee820.c @@ -14,9 +14,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <realmode.h> #include <biosint.h> diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c b/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c index 8f3069e18..253c601ff 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/hidemem.c @@ -14,9 +14,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <assert.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c b/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c index 0937a7ce2..bcacecd6a 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/memmap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> diff --git a/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c b/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c index 5c74b0431..20ec35d75 100644 --- a/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c +++ b/roms/ipxe/src/arch/i386/firmware/pcbios/pnpbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c b/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c index 523724ab0..473b97f97 100644 --- a/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c +++ b/roms/ipxe/src/arch/i386/hci/commands/pxe_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <ipxe/netdevice.h> @@ -23,7 +27,7 @@ #include <hci/ifmgmt_cmd.h> #include <pxe_call.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/image/bootsector.c b/roms/ipxe/src/arch/i386/image/bootsector.c index 9a089e6bb..dba87613c 100644 --- a/roms/ipxe/src/arch/i386/image/bootsector.c +++ b/roms/ipxe/src/arch/i386/image/bootsector.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/image/bzimage.c b/roms/ipxe/src/arch/i386/image/bzimage.c index 4865c394c..a64206cd3 100644 --- a/roms/ipxe/src/arch/i386/image/bzimage.c +++ b/roms/ipxe/src/arch/i386/image/bzimage.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/image/elfboot.c b/roms/ipxe/src/arch/i386/image/elfboot.c index 0f6957f02..dc3568929 100644 --- a/roms/ipxe/src/arch/i386/image/elfboot.c +++ b/roms/ipxe/src/arch/i386/image/elfboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <elf.h> @@ -75,6 +79,27 @@ static int elfboot_exec ( struct image *image ) { } /** + * Check that ELF segment uses flat physical addressing + * + * @v image ELF file + * @v phdr ELF program header + * @v dest Destination address + * @ret rc Return status code + */ +static int elfboot_check_segment ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ) { + + /* Check that ELF segment uses flat physical addressing */ + if ( phdr->p_vaddr != dest ) { + DBGC ( image, "ELF %p uses virtual addressing (phys %x, " + "virt %x)\n", image, phdr->p_paddr, phdr->p_vaddr ); + return -ENOEXEC; + } + + return 0; +} + +/** * Probe ELF image * * @v image ELF file @@ -91,14 +116,24 @@ static int elfboot_probe ( struct image *image ) { [EI_DATA] = ELFDATA2LSB, [EI_VERSION] = EV_CURRENT, }; + physaddr_t entry; + physaddr_t max; + int rc; /* Read ELF header */ copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); if ( memcmp ( ehdr.e_ident, e_ident, sizeof ( e_ident ) ) != 0 ) { - DBG ( "Invalid ELF identifier\n" ); + DBGC ( image, "Invalid ELF identifier\n" ); return -ENOEXEC; } + /* Check that this image uses flat physical addressing */ + if ( ( rc = elf_segments ( image, &ehdr, elfboot_check_segment, + &entry, &max ) ) != 0 ) { + DBGC ( image, "Unloadable ELF image\n" ); + return rc; + } + return 0; } diff --git a/roms/ipxe/src/arch/i386/image/initrd.c b/roms/ipxe/src/arch/i386/image/initrd.c index eaba3a645..80c197417 100644 --- a/roms/ipxe/src/arch/i386/image/initrd.c +++ b/roms/ipxe/src/arch/i386/image/initrd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <initrd.h> diff --git a/roms/ipxe/src/arch/i386/image/multiboot.c b/roms/ipxe/src/arch/i386/image/multiboot.c index 86b0bc12d..0c85df708 100644 --- a/roms/ipxe/src/arch/i386/image/multiboot.c +++ b/roms/ipxe/src/arch/i386/image/multiboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/image/pxe_image.c b/roms/ipxe/src/arch/i386/image/pxe_image.c index dc28f6082..5b0f6eb89 100644 --- a/roms/ipxe/src/arch/i386/image/pxe_image.c +++ b/roms/ipxe/src/arch/i386/image/pxe_image.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -34,6 +38,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/netdevice.h> #include <ipxe/features.h> #include <ipxe/console.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/IndustryStandard/PeImage.h> FEATURE ( FEATURE_IMAGE, "PXE", DHCP_EB_FEATURE_PXE, 1 ); @@ -121,9 +127,45 @@ int pxe_probe ( struct image *image ) { return 0; } +/** + * Probe PXE image (with rejection of potential EFI images) + * + * @v image PXE file + * @ret rc Return status code + */ +int pxe_probe_no_mz ( struct image *image ) { + uint16_t magic; + int rc; + + /* Probe PXE image */ + if ( ( rc = pxe_probe ( image ) ) != 0 ) + return rc; + + /* Reject image with an "MZ" signature which may indicate an + * EFI image incorrectly handed out to a BIOS system. + */ + if ( image->len >= sizeof ( magic ) ) { + copy_from_user ( &magic, image->data, 0, sizeof ( magic ) ); + if ( magic == cpu_to_le16 ( EFI_IMAGE_DOS_SIGNATURE ) ) { + DBGC ( image, "IMAGE %p may be an EFI image\n", + image ); + return -ENOTTY; + } + } + + return 0; +} + /** PXE image type */ -struct image_type pxe_image_type __image_type ( PROBE_PXE ) = { - .name = "PXE", - .probe = pxe_probe, - .exec = pxe_exec, +struct image_type pxe_image_type[] __image_type ( PROBE_PXE ) = { + { + .name = "PXE-NBP", + .probe = pxe_probe_no_mz, + .exec = pxe_exec, + }, + { + .name = "PXE-NBP (may be EFI?)", + .probe = pxe_probe, + .exec = pxe_exec, + }, }; diff --git a/roms/ipxe/src/arch/i386/image/sdi.c b/roms/ipxe/src/arch/i386/image/sdi.c index df1c3a868..fa2d0b73f 100644 --- a/roms/ipxe/src/arch/i386/image/sdi.c +++ b/roms/ipxe/src/arch/i386/image/sdi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/arch/i386/include/basemem.h b/roms/ipxe/src/arch/i386/include/basemem.h index c477c7fe2..01c2ea917 100644 --- a/roms/ipxe/src/arch/i386/include/basemem.h +++ b/roms/ipxe/src/arch/i386/include/basemem.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/include/basemem_packet.h b/roms/ipxe/src/arch/i386/include/basemem_packet.h index 3cb477671..def6dee31 100644 --- a/roms/ipxe/src/arch/i386/include/basemem_packet.h +++ b/roms/ipxe/src/arch/i386/include/basemem_packet.h @@ -1,7 +1,7 @@ #ifndef BASEMEM_PACKET_H #define BASEMEM_PACKET_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/include/bios.h b/roms/ipxe/src/arch/i386/include/bios.h index 0754b1168..988bbc62b 100644 --- a/roms/ipxe/src/arch/i386/include/bios.h +++ b/roms/ipxe/src/arch/i386/include/bios.h @@ -1,7 +1,7 @@ #ifndef BIOS_H #define BIOS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define BDA_SEG 0x0040 #define BDA_EQUIPMENT_WORD 0x0010 diff --git a/roms/ipxe/src/arch/i386/include/biosint.h b/roms/ipxe/src/arch/i386/include/biosint.h index ab466af3c..67d6a3811 100644 --- a/roms/ipxe/src/arch/i386/include/biosint.h +++ b/roms/ipxe/src/arch/i386/include/biosint.h @@ -6,7 +6,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/byteswap.h b/roms/ipxe/src/arch/i386/include/bits/byteswap.h index 0d9cb967c..53b6a454d 100644 --- a/roms/ipxe/src/arch/i386/include/bits/byteswap.h +++ b/roms/ipxe/src/arch/i386/include/bits/byteswap.h @@ -9,7 +9,7 @@ #include <stdint.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static inline __attribute__ (( always_inline, const )) uint16_t __bswap_variable_16 ( uint16_t x ) { diff --git a/roms/ipxe/src/arch/i386/include/bits/compiler.h b/roms/ipxe/src/arch/i386/include/bits/compiler.h index d48b4b385..87201135f 100644 --- a/roms/ipxe/src/arch/i386/include/bits/compiler.h +++ b/roms/ipxe/src/arch/i386/include/bits/compiler.h @@ -1,7 +1,10 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_386_NONE #ifndef ASSEMBLY diff --git a/roms/ipxe/src/arch/i386/include/bits/endian.h b/roms/ipxe/src/arch/i386/include/bits/endian.h deleted file mode 100644 index 841885424..000000000 --- a/roms/ipxe/src/arch/i386/include/bits/endian.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef ETHERBOOT_BITS_ENDIAN_H -#define ETHERBOOT_BITS_ENDIAN_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#define __BYTE_ORDER __LITTLE_ENDIAN - -#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/roms/ipxe/src/arch/i386/include/bits/entropy.h b/roms/ipxe/src/arch/i386/include/bits/entropy.h index 6dcceec6d..bfeb5e3b5 100644 --- a/roms/ipxe/src/arch/i386/include/bits/entropy.h +++ b/roms/ipxe/src/arch/i386/include/bits/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/rtc_entropy.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/hyperv.h b/roms/ipxe/src/arch/i386/include/bits/hyperv.h new file mode 100644 index 000000000..3565c8a83 --- /dev/null +++ b/roms/ipxe/src/arch/i386/include/bits/hyperv.h @@ -0,0 +1,72 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> +#include <stdint.h> +#include <ipxe/io.h> + +/** + * Issue hypercall + * + * @v hv Hyper-V hypervisor + * @v code Call code + * @v in Input parameters + * @v out Output parameters + * @ret status Status code + */ +static inline __attribute__ (( always_inline )) int +hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, + void *out ) { + void *hypercall = hv->hypercall; + uint32_t in_phys; + uint32_t out_phys; + uint32_t discard_ecx; + uint32_t discard_edx; + uint16_t result; + + in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) ) + ? 0 : virt_to_phys ( in ) ); + out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) ) + ? 0 : virt_to_phys ( out ) ); + __asm__ __volatile__ ( "call *%9" + : "=a" ( result ), "=c" ( discard_ecx ), + "=d" ( discard_edx ) + : "d" ( 0 ), "a" ( code ), + "b" ( 0 ), "c" ( in_phys ), + "D" ( 0 ), "S" ( out_phys ), + "m" ( hypercall ) ); + return result; +} + +/** + * Set bit atomically + * + * @v bits Bit field + * @v bit Bit to set + */ +static inline __attribute__ (( always_inline )) void +hv_set_bit ( void *bits, unsigned int bit ) { + struct { + uint32_t dword[ ( bit / 32 ) + 1 ]; + } *dwords = bits; + + /* Set bit using "lock bts". Inform compiler that any memory + * from the start of the bit field up to and including the + * dword containing this bit may be modified. (This is + * overkill but shouldn't matter in practice since we're + * unlikely to subsequently read other bits from the same bit + * field.) + */ + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *dwords ) : "Ir" ( bit ) ); +} + +#endif /* _BITS_HYPERV_H */ diff --git a/roms/ipxe/src/arch/i386/include/bits/nap.h b/roms/ipxe/src/arch/i386/include/bits/nap.h index 64066e6ab..e8bcfd13b 100644 --- a/roms/ipxe/src/arch/i386/include/bits/nap.h +++ b/roms/ipxe/src/arch/i386/include/bits/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bios_nap.h> #include <ipxe/efi/efix86_nap.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/profile.h b/roms/ipxe/src/arch/i386/include/bits/profile.h index f3ee54ae9..e184d7b51 100644 --- a/roms/ipxe/src/arch/i386/include/bits/profile.h +++ b/roms/ipxe/src/arch/i386/include/bits/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/reboot.h b/roms/ipxe/src/arch/i386/include/bits/reboot.h index 5b09e95f7..803dacfe4 100644 --- a/roms/ipxe/src/arch/i386/include/bits/reboot.h +++ b/roms/ipxe/src/arch/i386/include/bits/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bios_reboot.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/sanboot.h b/roms/ipxe/src/arch/i386/include/bits/sanboot.h index 9c77a4d42..f02d2e649 100644 --- a/roms/ipxe/src/arch/i386/include/bits/sanboot.h +++ b/roms/ipxe/src/arch/i386/include/bits/sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bios_sanboot.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/smbios.h b/roms/ipxe/src/arch/i386/include/bits/smbios.h index cc79eec51..2ab31e74b 100644 --- a/roms/ipxe/src/arch/i386/include/bits/smbios.h +++ b/roms/ipxe/src/arch/i386/include/bits/smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bios_smbios.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/stdint.h b/roms/ipxe/src/arch/i386/include/bits/stdint.h index 8edf13192..fe1f9946a 100644 --- a/roms/ipxe/src/arch/i386/include/bits/stdint.h +++ b/roms/ipxe/src/arch/i386/include/bits/stdint.h @@ -1,7 +1,7 @@ #ifndef _BITS_STDINT_H #define _BITS_STDINT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); typedef __SIZE_TYPE__ size_t; typedef signed long ssize_t; diff --git a/roms/ipxe/src/arch/i386/include/bits/strings.h b/roms/ipxe/src/arch/i386/include/bits/strings.h index 092bcb593..453545f00 100644 --- a/roms/ipxe/src/arch/i386/include/bits/strings.h +++ b/roms/ipxe/src/arch/i386/include/bits/strings.h @@ -1,7 +1,51 @@ #ifndef _BITS_STRINGS_H #define _BITS_STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + long lsb_minus_one; + + /* If the input value is zero, the BSF instruction returns + * ZF=0 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsfl %1, %0" + : "=r" ( lsb_minus_one ) + : "rm" ( value ) ); + return ( lsb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + unsigned long high = ( value >> 32 ); + unsigned long low = ( value >> 0 ); + + if ( low ) { + return ( __ffsl ( low ) ); + } else if ( high ) { + return ( 32 + __ffsl ( high ) ); + } else { + return 0; + } +} /** * Find last (i.e. most significant) set bit @@ -13,7 +57,7 @@ static inline __attribute__ (( always_inline )) int __flsl ( long value ) { long msb_minus_one; /* If the input value is zero, the BSR instruction returns - * ZF=1 and leaves an undefined value in the output register. + * ZF=0 and leaves an undefined value in the output register. * Perform this check in C rather than asm so that it can be * omitted in cases where the compiler is able to prove that * the input is non-zero. diff --git a/roms/ipxe/src/arch/i386/include/bits/time.h b/roms/ipxe/src/arch/i386/include/bits/time.h index 24dd020e9..6a5d63d32 100644 --- a/roms/ipxe/src/arch/i386/include/bits/time.h +++ b/roms/ipxe/src/arch/i386/include/bits/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/rtc_time.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/timer.h b/roms/ipxe/src/arch/i386/include/bits/timer.h index 50b676b77..f7d86d78c 100644 --- a/roms/ipxe/src/arch/i386/include/bits/timer.h +++ b/roms/ipxe/src/arch/i386/include/bits/timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bios_timer.h> #include <ipxe/rdtsc_timer.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/uaccess.h b/roms/ipxe/src/arch/i386/include/bits/uaccess.h index 2bb52e021..aac09ba95 100644 --- a/roms/ipxe/src/arch/i386/include/bits/uaccess.h +++ b/roms/ipxe/src/arch/i386/include/bits/uaccess.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <librm.h> diff --git a/roms/ipxe/src/arch/i386/include/bits/umalloc.h b/roms/ipxe/src/arch/i386/include/bits/umalloc.h index 54fb006f0..113f16fd1 100644 --- a/roms/ipxe/src/arch/i386/include/bits/umalloc.h +++ b/roms/ipxe/src/arch/i386/include/bits/umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/memtop_umalloc.h> diff --git a/roms/ipxe/src/arch/i386/include/bootsector.h b/roms/ipxe/src/arch/i386/include/bootsector.h index 8730fbfcc..c5d35aae3 100644 --- a/roms/ipxe/src/arch/i386/include/bootsector.h +++ b/roms/ipxe/src/arch/i386/include/bootsector.h @@ -6,7 +6,7 @@ * x86 bootsector image format */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int call_bootsector ( unsigned int segment, unsigned int offset, unsigned int drive ); diff --git a/roms/ipxe/src/arch/i386/include/bzimage.h b/roms/ipxe/src/arch/i386/include/bzimage.h index 7e42e3188..4933ce5b1 100644 --- a/roms/ipxe/src/arch/i386/include/bzimage.h +++ b/roms/ipxe/src/arch/i386/include/bzimage.h @@ -1,7 +1,7 @@ #ifndef _BZIMAGE_H #define _BZIMAGE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h b/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h index 184177219..c17c1ea5e 100644 --- a/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h +++ b/roms/ipxe/src/arch/i386/include/efi/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/dhcp.h> diff --git a/roms/ipxe/src/arch/i386/include/fakee820.h b/roms/ipxe/src/arch/i386/include/fakee820.h index 9d00fb670..552b1e48d 100644 --- a/roms/ipxe/src/arch/i386/include/fakee820.h +++ b/roms/ipxe/src/arch/i386/include/fakee820.h @@ -1,7 +1,7 @@ #ifndef _FAKEE820_H #define _FAKEE820_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void fake_e820 ( void ); extern void unfake_e820 ( void ); diff --git a/roms/ipxe/src/arch/i386/include/initrd.h b/roms/ipxe/src/arch/i386/include/initrd.h index a5659f43c..ddb3e5a45 100644 --- a/roms/ipxe/src/arch/i386/include/initrd.h +++ b/roms/ipxe/src/arch/i386/include/initrd.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/arch/i386/include/int13.h b/roms/ipxe/src/arch/i386/include/int13.h index e337ca1d1..f82a583c6 100644 --- a/roms/ipxe/src/arch/i386/include/int13.h +++ b/roms/ipxe/src/arch/i386/include/int13.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/list.h> diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h index 5b684c041..c9b82c1e5 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_PCBIOS #define NAP_PREFIX_pcbios diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h index a0845328d..3f6df9073 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_PCBIOS #define REBOOT_PREFIX_pcbios diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h index 689227b70..1a86b7d57 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SANBOOT_PCBIOS #define SANBOOT_PREFIX_pcbios diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h index d8c7f648a..9f7f9c8ff 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_PCBIOS #define SMBIOS_PREFIX_pcbios diff --git a/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h b/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h index f9fc80412..6b88a623c 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/bios_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_PCBIOS #define TIMER_PREFIX_pcbios @@ -15,7 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define TIMER_PREFIX_pcbios __pcbios_ #endif -#include <ipxe/timer2.h> +#include <ipxe/pit8254.h> /** * Delay for a fixed number of microseconds @@ -25,9 +25,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); static inline __always_inline void TIMER_INLINE ( pcbios, udelay ) ( unsigned long usecs ) { /* BIOS timer is not high-resolution enough for udelay(), so - * we use timer2 + * we use the 8254 Programmable Interval Timer. */ - timer2_udelay ( usecs ); + pit8254_udelay ( usecs ); } /** diff --git a/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h b/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h index 3a9eb2495..6312adaa4 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/errno/pcbios.h @@ -10,7 +10,7 @@ * for the PC-BIOS platform. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <pxe_error.h> diff --git a/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h b/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h index 72a0f714f..bc3d85506 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/guestrpc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/vmware.h> diff --git a/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h b/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h index 001648fe5..dee055d16 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/memtop_umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UMALLOC_MEMTOP #define UMALLOC_PREFIX_memtop diff --git a/roms/ipxe/src/arch/i386/include/ipxe/msr.h b/roms/ipxe/src/arch/i386/include/ipxe/msr.h index c88e26a39..5705318fd 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/msr.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/msr.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Read model-specific register diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h b/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h index 472e14007..598f4bb08 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/rdtsc_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_RDTSC #define TIMER_PREFIX_rdtsc diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h b/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h index 6c3cf2104..e214745d0 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/rtc_entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h b/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h index c0dfe3f88..cb8c7f49e 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/rtc_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_RTC #define TIME_PREFIX_rtc diff --git a/roms/ipxe/src/arch/i386/include/ipxe/timer2.h b/roms/ipxe/src/arch/i386/include/ipxe/timer2.h deleted file mode 100644 index 322a3ed59..000000000 --- a/roms/ipxe/src/arch/i386/include/ipxe/timer2.h +++ /dev/null @@ -1,14 +0,0 @@ -#ifndef _IPXE_TIMER2_H -#define _IPXE_TIMER2_H - -/** @file - * - * Timer chip control - * - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -extern void timer2_udelay ( unsigned long usecs ); - -#endif /* _IPXE_TIMER2_H */ diff --git a/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h b/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h index 48cd6a7b7..efc8f2cb8 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/vesafb.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/include/ipxe/vmware.h b/roms/ipxe/src/arch/i386/include/ipxe/vmware.h index 2ac65f436..24f60a03a 100644 --- a/roms/ipxe/src/arch/i386/include/ipxe/vmware.h +++ b/roms/ipxe/src/arch/i386/include/ipxe/vmware.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/librm.h b/roms/ipxe/src/arch/i386/include/librm.h index c8ba72b53..a8a578a39 100644 --- a/roms/ipxe/src/arch/i386/include/librm.h +++ b/roms/ipxe/src/arch/i386/include/librm.h @@ -1,7 +1,7 @@ #ifndef LIBRM_H #define LIBRM_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Segment selectors as used in our protected-mode GDTs. * @@ -170,18 +170,6 @@ extern uint16_t __text16 ( rm_cs ); extern uint16_t __text16 ( rm_ds ); #define rm_ds __use_text16 ( rm_ds ) -/** - * Convert segment:offset address to user buffer - * - * @v segment Real-mode segment - * @v offset Real-mode offset - * @ret buffer User buffer - */ -static inline __always_inline userptr_t -real_to_user ( unsigned int segment, unsigned int offset ) { - return ( phys_to_user ( ( segment << 4 ) + offset ) ); -} - extern uint16_t copy_user_to_rm_stack ( userptr_t data, size_t size ); extern void remove_user_from_rm_stack ( userptr_t data, size_t size ); diff --git a/roms/ipxe/src/arch/i386/include/limits.h b/roms/ipxe/src/arch/i386/include/limits.h index 031b6c57a..bb48b75ab 100644 --- a/roms/ipxe/src/arch/i386/include/limits.h +++ b/roms/ipxe/src/arch/i386/include/limits.h @@ -1,7 +1,7 @@ #ifndef LIMITS_H #define LIMITS_H 1 -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Number of bits in a `char' */ #define CHAR_BIT 8 diff --git a/roms/ipxe/src/arch/i386/include/memsizes.h b/roms/ipxe/src/arch/i386/include/memsizes.h index 7b217494a..f115f7574 100644 --- a/roms/ipxe/src/arch/i386/include/memsizes.h +++ b/roms/ipxe/src/arch/i386/include/memsizes.h @@ -1,7 +1,7 @@ #ifndef _MEMSIZES_H #define _MEMSIZES_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <basemem.h> diff --git a/roms/ipxe/src/arch/i386/include/multiboot.h b/roms/ipxe/src/arch/i386/include/multiboot.h index 44614c73a..ae09df6c7 100644 --- a/roms/ipxe/src/arch/i386/include/multiboot.h +++ b/roms/ipxe/src/arch/i386/include/multiboot.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h b/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h index a36d9cfa1..e07e4c192 100644 --- a/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h +++ b/roms/ipxe/src/arch/i386/include/pcbios/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/dhcp.h> diff --git a/roms/ipxe/src/arch/i386/include/pnpbios.h b/roms/ipxe/src/arch/i386/include/pnpbios.h index 4c20e73ed..d14873700 100644 --- a/roms/ipxe/src/arch/i386/include/pnpbios.h +++ b/roms/ipxe/src/arch/i386/include/pnpbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* BIOS segment address */ #define BIOS_SEG 0xf000 diff --git a/roms/ipxe/src/arch/i386/include/pxe.h b/roms/ipxe/src/arch/i386/include/pxe.h index b95b0cce5..66d752683 100644 --- a/roms/ipxe/src/arch/i386/include/pxe.h +++ b/roms/ipxe/src/arch/i386/include/pxe.h @@ -1,7 +1,7 @@ #ifndef PXE_H #define PXE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include "pxe_types.h" #include "pxe_error.h" diff --git a/roms/ipxe/src/arch/i386/include/pxe_api.h b/roms/ipxe/src/arch/i386/include/pxe_api.h index e4396efb2..3110d26da 100644 --- a/roms/ipxe/src/arch/i386/include/pxe_api.h +++ b/roms/ipxe/src/arch/i386/include/pxe_api.h @@ -17,6 +17,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * * As an alternative, at your option, you may use this file under the * following terms, known as the "MIT license": * @@ -49,7 +53,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include "pxe_types.h" diff --git a/roms/ipxe/src/arch/i386/include/pxe_call.h b/roms/ipxe/src/arch/i386/include/pxe_call.h index 45af46549..cbd548318 100644 --- a/roms/ipxe/src/arch/i386/include/pxe_call.h +++ b/roms/ipxe/src/arch/i386/include/pxe_call.h @@ -6,7 +6,7 @@ * PXE API entry point */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <pxe_api.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/include/pxe_error.h b/roms/ipxe/src/arch/i386/include/pxe_error.h index a1398cbd4..51298e665 100644 --- a/roms/ipxe/src/arch/i386/include/pxe_error.h +++ b/roms/ipxe/src/arch/i386/include/pxe_error.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @defgroup pxeerrors PXE error codes diff --git a/roms/ipxe/src/arch/i386/include/pxe_types.h b/roms/ipxe/src/arch/i386/include/pxe_types.h index db8214591..483666e33 100644 --- a/roms/ipxe/src/arch/i386/include/pxe_types.h +++ b/roms/ipxe/src/arch/i386/include/pxe_types.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> /* PXE status codes */ diff --git a/roms/ipxe/src/arch/i386/include/realmode.h b/roms/ipxe/src/arch/i386/include/realmode.h index dafc5a32a..4defd3b97 100644 --- a/roms/ipxe/src/arch/i386/include/realmode.h +++ b/roms/ipxe/src/arch/i386/include/realmode.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Declaration of variables in .data16 @@ -65,6 +65,18 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** + * Convert segment:offset address to user buffer + * + * @v segment Real-mode segment + * @v offset Real-mode offset + * @ret buffer User buffer + */ +static inline __always_inline userptr_t +real_to_user ( unsigned int segment, unsigned int offset ) { + return ( phys_to_user ( ( segment << 4 ) + offset ) ); +} + +/** * Copy data to base memory * * @v dest_seg Destination segment diff --git a/roms/ipxe/src/arch/i386/include/registers.h b/roms/ipxe/src/arch/i386/include/registers.h index 06d236524..d9aa3c376 100644 --- a/roms/ipxe/src/arch/i386/include/registers.h +++ b/roms/ipxe/src/arch/i386/include/registers.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/i386/include/rtc.h b/roms/ipxe/src/arch/i386/include/rtc.h index 2a6abbae5..6294b63e3 100644 --- a/roms/ipxe/src/arch/i386/include/rtc.h +++ b/roms/ipxe/src/arch/i386/include/rtc.h @@ -13,7 +13,7 @@ * http://wiki.osdev.org/CMOS */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <pic8259.h> diff --git a/roms/ipxe/src/arch/i386/include/sdi.h b/roms/ipxe/src/arch/i386/include/sdi.h index fc486402d..806c3f194 100644 --- a/roms/ipxe/src/arch/i386/include/sdi.h +++ b/roms/ipxe/src/arch/i386/include/sdi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** SDI image header */ struct sdi_header { diff --git a/roms/ipxe/src/arch/i386/include/setjmp.h b/roms/ipxe/src/arch/i386/include/setjmp.h index 5d3c11b69..fe1a9ef4d 100644 --- a/roms/ipxe/src/arch/i386/include/setjmp.h +++ b/roms/ipxe/src/arch/i386/include/setjmp.h @@ -1,40 +1,50 @@ -#ifndef ETHERBOOT_SETJMP_H -#define ETHERBOOT_SETJMP_H +#ifndef _SETJMP_H +#define _SETJMP_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <realmode.h> /** A jump buffer */ typedef struct { + /** Saved return address */ uint32_t retaddr; + /** Saved stack pointer */ + uint32_t stack; + /** Saved %ebx */ uint32_t ebx; - uint32_t esp; - uint32_t ebp; + /** Saved %esi */ uint32_t esi; + /** Saved %edi */ uint32_t edi; + /** Saved %ebp */ + uint32_t ebp; } jmp_buf[1]; /** A real-mode-extended jump buffer */ typedef struct { + /** Jump buffer */ jmp_buf env; - uint16_t rm_ss; - uint16_t rm_sp; + /** Real-mode stack pointer */ + segoff_t rm_stack; } rmjmp_buf[1]; -extern int __asmcall setjmp ( jmp_buf env ); -extern void __asmcall longjmp ( jmp_buf env, int val ); +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); -#define rmsetjmp( _env ) ( { \ - (_env)->rm_ss = rm_ss; \ - (_env)->rm_sp = rm_sp; \ - setjmp ( (_env)->env ); } ) \ +#define rmsetjmp( _env ) ( { \ + (_env)->rm_stack.segment = rm_ss; \ + (_env)->rm_stack.offset = rm_sp; \ + setjmp ( (_env)->env ); } ) \ -#define rmlongjmp( _env, _val ) do { \ - rm_ss = (_env)->rm_ss; \ - rm_sp = (_env)->rm_sp; \ - longjmp ( (_env)->env, (_val) ); \ +#define rmlongjmp( _env, _val ) do { \ + rm_ss = (_env)->rm_stack.segment; \ + rm_sp = (_env)->rm_stack.offset; \ + longjmp ( (_env)->env, (_val) ); \ } while ( 0 ) -#endif /* ETHERBOOT_SETJMP_H */ +#endif /* _SETJMP_H */ diff --git a/roms/ipxe/src/arch/i386/include/undi.h b/roms/ipxe/src/arch/i386/include/undi.h index 325fcbbf9..7a5624f93 100644 --- a/roms/ipxe/src/arch/i386/include/undi.h +++ b/roms/ipxe/src/arch/i386/include/undi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ASSEMBLY diff --git a/roms/ipxe/src/arch/i386/include/undiload.h b/roms/ipxe/src/arch/i386/include/undiload.h index 426830e8d..235e7a79e 100644 --- a/roms/ipxe/src/arch/i386/include/undiload.h +++ b/roms/ipxe/src/arch/i386/include/undiload.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; struct undi_rom; diff --git a/roms/ipxe/src/arch/i386/include/undinet.h b/roms/ipxe/src/arch/i386/include/undinet.h index c3c17c11a..2798c4466 100644 --- a/roms/ipxe/src/arch/i386/include/undinet.h +++ b/roms/ipxe/src/arch/i386/include/undinet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct undi_device; diff --git a/roms/ipxe/src/arch/i386/include/undipreload.h b/roms/ipxe/src/arch/i386/include/undipreload.h index de9b8fb52..57f493cec 100644 --- a/roms/ipxe/src/arch/i386/include/undipreload.h +++ b/roms/ipxe/src/arch/i386/include/undipreload.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <realmode.h> #include <undi.h> diff --git a/roms/ipxe/src/arch/i386/include/undirom.h b/roms/ipxe/src/arch/i386/include/undirom.h index 86d7077b5..1c530118d 100644 --- a/roms/ipxe/src/arch/i386/include/undirom.h +++ b/roms/ipxe/src/arch/i386/include/undirom.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <pxe_types.h> diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/apm.c b/roms/ipxe/src/arch/i386/interface/pcbios/apm.c index 3b13e1cd0..50b19cb81 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/apm.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/apm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c index 1e7de756b..f1ba8297b 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_nap.c @@ -1,7 +1,7 @@ #include <ipxe/nap.h> #include <realmode.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Save power by halting the CPU until the next interrupt diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c index 68546b2e5..10a1ecb89 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c index dd7897e29..a8c0fc325 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_smbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c b/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c index 65bbf9e01..3299c9aae 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/bios_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c b/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c index a193defa3..3b8e80438 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/biosint.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Hook INT vector diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/int13.c b/roms/ipxe/src/arch/i386/interface/pcbios/int13.c index 1c7a8128f..f0450da90 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/int13.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/int13.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c b/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c new file mode 100644 index 000000000..2414c6909 --- /dev/null +++ b/roms/ipxe/src/arch/i386/interface/pcbios/int13con.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <ipxe/console.h> +#include <ipxe/init.h> +#include <realmode.h> +#include <int13.h> +#include <config/console.h> + +/** @file + * + * INT13 disk log console + * + */ + +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_INT13 ) && CONSOLE_EXPLICIT ( CONSOLE_INT13 ) ) +#undef CONSOLE_INT13 +#define CONSOLE_INT13 ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) +#endif + +/** Disk drive number */ +#define INT13CON_DRIVE 0x80 + +/** Log partition type */ +#define INT13CON_PARTITION_TYPE 0xe0 + +/** Maximum number of outstanding unwritten characters */ +#define INT13CON_MAX_UNWRITTEN 64 + +/** Log partition header */ +struct int13con_header { + /** Magic signature */ + char magic[10]; +} __attribute__ (( packed )); + +/** Log partition magic signature */ +#define INT13CON_MAGIC "iPXE LOG\n\n" + +/** Sector buffer */ +static uint8_t __bss16_array ( int13con_buffer, [INT13_BLKSIZE] ); +#define int13con_buffer __use_data16 ( int13con_buffer ) + +/** Disk address packet */ +static struct int13_disk_address __bss16 ( int13con_address ); +#define int13con_address __use_data16 ( int13con_address ) + +/** Current LBA */ +static uint64_t int13con_lba; + +/** Maximum LBA */ +static uint64_t int13con_max_lba; + +/** Current offset within sector */ +static size_t int13con_offset; + +/** Number of unwritten characters */ +static size_t int13con_unwritten; + +struct console_driver int13con __console_driver; + +/** + * Read/write disk sector + * + * @v op Operation + * @v lba Logical block address + * @ret rc Return status code + */ +static int int13con_rw ( unsigned int op, uint64_t lba ) { + uint8_t error; + + /* Construct disk address packet */ + int13con_address.bufsize = sizeof ( int13con_address ); + int13con_address.count = 1; + int13con_address.buffer.segment = rm_ds; + int13con_address.buffer.offset = __from_data16 ( int13con_buffer ); + int13con_address.lba = lba; + + /* Issue INT13 */ + __asm__ ( REAL_CODE ( "int $0x13\n\t" ) + : "=a" ( error ) + : "0" ( op << 8 ), "d" ( INT13CON_DRIVE ), + "S" ( __from_data16 ( &int13con_address ) ) ); + if ( error ) { + DBG ( "INT13CON operation %04x failed: %02x\n", + op, error ); + return -EIO; + } + + return 0; +} + +/** + * Write character to console + * + * @v character Character + */ +static void int13con_putchar ( int character ) { + static int busy; + int rc; + + /* Ignore if we are already mid-logging */ + if ( busy ) + return; + busy = 1; + + /* Write character to buffer */ + int13con_buffer[int13con_offset++] = character; + int13con_unwritten++; + + /* Write sector to disk, if applicable */ + if ( ( int13con_offset == INT13_BLKSIZE ) || + ( int13con_unwritten == INT13CON_MAX_UNWRITTEN ) || + ( character == '\n' ) ) { + + /* Write sector to disk */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_WRITE, + int13con_lba ) ) != 0 ) { + DBG ( "INT13CON could not write log\n" ); + /* Ignore and continue; there's nothing we can do */ + } + + /* Reset count of unwritten characters */ + int13con_unwritten = 0; + } + + /* Move to next sector, if applicable */ + if ( int13con_offset == INT13_BLKSIZE ) { + + /* Disable console if we have run out of space */ + if ( int13con_lba >= int13con_max_lba ) + int13con.disabled = 1; + + /* Clear log buffer */ + memset ( int13con_buffer, 0, sizeof ( int13con_buffer ) ); + int13con_offset = 0; + + /* Move to next sector */ + int13con_lba++; + } + + /* Clear busy flag */ + busy = 0; +} + +/** + * Find log partition + * + * @ret rc Return status code + */ +static int int13con_find ( void ) { + struct master_boot_record *mbr = + ( ( struct master_boot_record * ) int13con_buffer ); + struct int13con_header *hdr = + ( ( struct int13con_header * ) int13con_buffer ); + struct partition_table_entry part[4]; + unsigned int i; + int rc; + + /* Read MBR */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, 0 ) ) != 0 ) { + DBG ( "INT13CON could not read MBR: %s\n", strerror ( rc ) ); + return rc; + } + + /* Check MBR magic */ + if ( mbr->magic != INT13_MBR_MAGIC ) { + DBG ( "INT13CON incorrect MBR magic\n" ); + DBG2_HDA ( 0, mbr, sizeof ( *mbr ) ); + return -EINVAL; + } + + /* Look for magic partition */ + memcpy ( part, mbr->partitions, sizeof ( part ) ); + for ( i = 0 ; i < ( sizeof ( part ) / sizeof ( part[0] ) ) ; i++ ) { + + /* Skip partitions of the wrong type */ + if ( part[i].type != INT13CON_PARTITION_TYPE ) + continue; + + /* Read partition header */ + if ( ( rc = int13con_rw ( INT13_EXTENDED_READ, + part[i].start ) ) != 0 ) { + DBG ( "INT13CON partition %d could not read header: " + "%s\n", ( i + 1 ), strerror ( rc ) ); + continue; + } + + /* Check partition header */ + if ( memcmp ( hdr->magic, INT13CON_MAGIC, + sizeof ( hdr->magic ) ) != 0 ) { + DBG ( "INT13CON partition %d bad magic\n", ( i + 1 ) ); + DBG2_HDA ( 0, hdr, sizeof ( *hdr ) ); + continue; + } + + /* Found log partition */ + DBG ( "INT13CON partition %d at [%08x,%08x)\n", ( i + 1 ), + part[i].start, ( part[i].start + part[i].length ) ); + int13con_lba = part[i].start; + int13con_max_lba = ( part[i].start + part[i].length - 1 ); + + /* Initialise log buffer */ + memset ( &int13con_buffer[ sizeof ( *hdr ) ], 0, + ( sizeof ( int13con_buffer ) - sizeof ( *hdr ) ) ); + int13con_offset = sizeof ( hdr->magic ); + + return 0; + } + + DBG ( "INT13CON found no log partition\n" ); + return -ENOENT; +} + +/** + * Initialise INT13 console + * + */ +static void int13con_init ( void ) { + uint8_t error; + uint16_t check; + unsigned int discard_c; + unsigned int discard_d; + int rc; + + /* Check for INT13 extensions */ + __asm__ __volatile__ ( REAL_CODE ( "int $0x13\n\t" + "setc %%al\n\t" ) + : "=a" ( error ), "=b" ( check ), + "=c" ( discard_c ), "=d" ( discard_d ) + : "0" ( INT13_EXTENSION_CHECK << 8 ), + "1" ( 0x55aa ), "3" ( INT13CON_DRIVE ) ); + if ( error || ( check != 0xaa55 ) ) { + DBG ( "INT13CON missing extensions (%02x,%04x)\n", + error, check ); + return; + } + + /* Locate log partition */ + if ( ( rc = int13con_find() ) != 0) + return; + + /* Enable console */ + int13con.disabled = 0; +} + +/** + * INT13 console initialisation function + */ +struct init_fn int13con_init_fn __init_fn ( INIT_CONSOLE ) = { + .initialise = int13con_init, +}; + +/** INT13 console driver */ +struct console_driver int13con __console_driver = { + .putchar = int13con_putchar, + .disabled = CONSOLE_DISABLED, + .usage = CONSOLE_INT13, +}; diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c b/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c index c382e3c36..957f8e324 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/memtop_umalloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c b/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c index 61873039f..34efa0b39 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/pcibios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/pci.h> @@ -38,7 +42,11 @@ static int pcibios_num_bus ( void ) { int discard_a, discard_D; uint8_t max_bus; - __asm__ __volatile__ ( REAL_CODE ( "stc\n\t" + /* We issue this call using flat real mode, to work around a + * bug in some HP BIOSes. + */ + __asm__ __volatile__ ( REAL_CODE ( "call flatten_real_mode\n\t" + "stc\n\t" "int $0x1a\n\t" "jnc 1f\n\t" "xorw %%cx, %%cx\n\t" diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c index fad421c2a..9aab03c03 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c index 67041d4ca..cdbeac8d5 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/rtc_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c b/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c index 2adc7b040..9cf2bf29e 100644 --- a/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c +++ b/roms/ipxe/src/arch/i386/interface/pcbios/vesafb.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c index 657d47b6c..104313666 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_call.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> #include <ipxe/init.h> @@ -342,6 +346,7 @@ int pxe_start_nbp ( void ) { return 0; } +REQUIRING_SYMBOL ( pxe_api_call ); REQUIRE_OBJECT ( pxe_preboot ); REQUIRE_OBJECT ( pxe_undi ); REQUIRE_OBJECT ( pxe_udp ); diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S b/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S index 6274264ff..07852cd50 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_entry.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c index 9d1896507..f92dae0d1 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_exit_hook.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <realmode.h> diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c index 6e9610294..456ffb5fd 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_file.c @@ -31,9 +31,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); FEATURE ( FEATURE_MISC, "PXEXT", DHCP_EB_FEATURE_PXE_EXT, 2 ); diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c index 695af3b93..e6a2e072a 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_loader.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/init.h> #include "pxe.h" diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c index 534352b2b..6e09080bc 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_preboot.c @@ -22,9 +22,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -174,18 +178,16 @@ pxenv_get_cached_info ( struct s_PXENV_GET_CACHED_INFO *get_cached_info ) { } info = &cached_info[idx]; - /* Construct cached version of packet, if not already constructed. */ - if ( ! info->dhcphdr.op ) { - /* Construct DHCP packet */ - creator = &pxe_dhcp_packet_creators[idx]; - if ( ( rc = creator->create ( pxe_netdev, info, - sizeof ( *info ) ) ) != 0 ) { - DBGC ( &pxe_netdev, " failed to build packet: %s\n", - strerror ( rc ) ); - goto err; - } + /* Construct DHCP packet */ + creator = &pxe_dhcp_packet_creators[idx]; + if ( ( rc = creator->create ( pxe_netdev, info, + sizeof ( *info ) ) ) != 0 ) { + DBGC ( &pxe_netdev, " failed to build packet: %s\n", + strerror ( rc ) ); + goto err; } + /* Copy packet (if applicable) */ len = get_cached_info->BufferSize; if ( len == 0 ) { /* Point client at our cached buffer. diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c index f4801bad0..068d8a7b2 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_tftp.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stdio.h> @@ -36,6 +40,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/xfer.h> #include <ipxe/open.h> #include <ipxe/process.h> +#include <ipxe/uri.h> +#include <realmode.h> #include <pxe.h> /** A PXE TFTP connection */ @@ -170,11 +176,10 @@ static struct pxe_tftp_connection pxe_tftp = { * @v blksize Requested block size * @ret rc Return status code */ -static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port, - const unsigned char *filename, size_t blksize, - int sizeonly ) { - char uri_string[PXE_TFTP_URI_LEN]; +static int pxe_tftp_open ( IP4_t ipaddress, UDP_PORT_t port, + UINT8_t *filename, UINT16_t blksize ) { struct in_addr address; + struct uri *uri; int rc; /* Reset PXE TFTP connection structure */ @@ -185,19 +190,20 @@ static int pxe_tftp_open ( uint32_t ipaddress, unsigned int port, pxe_tftp.blksize = blksize; pxe_tftp.rc = -EINPROGRESS; - /* Construct URI string */ + /* Construct URI */ address.s_addr = ipaddress; - if ( ! port ) - port = htons ( TFTP_PORT ); - snprintf ( uri_string, sizeof ( uri_string ), "tftp%s://%s:%d%s%s", - sizeonly ? "size" : "", inet_ntoa ( address ), - ntohs ( port ), ( ( filename[0] == '/' ) ? "" : "/" ), - filename ); - DBG ( " %s", uri_string ); + DBG ( " %s", inet_ntoa ( address ) ); + if ( port ) + DBG ( ":%d", ntohs ( port ) ); + DBG ( ":%s", filename ); + uri = tftp_uri ( address, ntohs ( port ), ( ( char * ) filename ) ); + if ( ! uri ) { + DBG ( " could not create URI\n" ); + return -ENOMEM; + } /* Open PXE TFTP connection */ - if ( ( rc = xfer_open_uri_string ( &pxe_tftp.xfer, - uri_string ) ) != 0 ) { + if ( ( rc = xfer_open_uri ( &pxe_tftp.xfer, uri ) ) != 0 ) { DBG ( " could not open (%s)\n", strerror ( rc ) ); return rc; } @@ -259,8 +265,7 @@ static PXENV_EXIT_t pxenv_tftp_open ( struct s_PXENV_TFTP_OPEN *tftp_open ) { if ( ( rc = pxe_tftp_open ( tftp_open->ServerIPAddress, tftp_open->TFTPPort, tftp_open->FileName, - tftp_open->PacketSize, - 0) ) != 0 ) { + tftp_open->PacketSize ) ) != 0 ) { tftp_open->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } @@ -483,7 +488,7 @@ PXENV_EXIT_t pxenv_tftp_read_file ( struct s_PXENV_TFTP_READ_FILE /* Open TFTP file */ if ( ( rc = pxe_tftp_open ( tftp_read_file->ServerIPAddress, 0, - tftp_read_file->FileName, 0, 0 ) ) != 0 ) { + tftp_read_file->FileName, 0 ) ) != 0 ) { tftp_read_file->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } @@ -553,7 +558,7 @@ static PXENV_EXIT_t pxenv_tftp_get_fsize ( struct s_PXENV_TFTP_GET_FSIZE /* Open TFTP file */ if ( ( rc = pxe_tftp_open ( tftp_get_fsize->ServerIPAddress, 0, - tftp_get_fsize->FileName, 0, 1 ) ) != 0 ) { + tftp_get_fsize->FileName, 0 ) ) != 0 ) { tftp_get_fsize->Status = PXENV_STATUS ( rc ); return PXENV_EXIT_FAILURE; } diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c index 32bc39c8e..071cb59db 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_udp.c @@ -11,6 +11,7 @@ #include <ipxe/udp.h> #include <ipxe/uaccess.h> #include <ipxe/process.h> +#include <realmode.h> #include <pxe.h> /* @@ -30,9 +31,25 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** A PXE UDP pseudo-header */ +struct pxe_udp_pseudo_header { + /** Source IP address */ + IP4_t src_ip; + /** Source port */ + UDP_PORT_t s_port; + /** Destination IP address */ + IP4_t dest_ip; + /** Destination port */ + UDP_PORT_t d_port; +} __attribute__ (( packed )); /** A PXE UDP connection */ struct pxe_udp_connection { @@ -40,8 +57,8 @@ struct pxe_udp_connection { struct interface xfer; /** Local address */ struct sockaddr_in local; - /** Current PXENV_UDP_READ parameter block */ - struct s_PXENV_UDP_READ *pxenv_udp_read; + /** List of received packets */ + struct list_head list; }; /** @@ -58,45 +75,38 @@ struct pxe_udp_connection { static int pxe_udp_deliver ( struct pxe_udp_connection *pxe_udp, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - struct s_PXENV_UDP_READ *pxenv_udp_read = pxe_udp->pxenv_udp_read; + struct pxe_udp_pseudo_header *pshdr; struct sockaddr_in *sin_src; struct sockaddr_in *sin_dest; - userptr_t buffer; - size_t len; - int rc = 0; - - if ( ! pxenv_udp_read ) { - DBG ( "PXE discarded UDP packet\n" ); - rc = -ENOBUFS; - goto done; - } - - /* Copy packet to buffer and record length */ - buffer = real_to_user ( pxenv_udp_read->buffer.segment, - pxenv_udp_read->buffer.offset ); - len = iob_len ( iobuf ); - if ( len > pxenv_udp_read->buffer_size ) - len = pxenv_udp_read->buffer_size; - copy_to_user ( buffer, 0, iobuf->data, len ); - pxenv_udp_read->buffer_size = len; + int rc; - /* Fill in source/dest information */ + /* Extract metadata */ assert ( meta ); sin_src = ( struct sockaddr_in * ) meta->src; assert ( sin_src ); assert ( sin_src->sin_family == AF_INET ); - pxenv_udp_read->src_ip = sin_src->sin_addr.s_addr; - pxenv_udp_read->s_port = sin_src->sin_port; sin_dest = ( struct sockaddr_in * ) meta->dest; assert ( sin_dest ); assert ( sin_dest->sin_family == AF_INET ); - pxenv_udp_read->dest_ip = sin_dest->sin_addr.s_addr; - pxenv_udp_read->d_port = sin_dest->sin_port; - /* Mark as received */ - pxe_udp->pxenv_udp_read = NULL; + /* Construct pseudo-header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *pshdr ) ) ) != 0 ) { + DBG ( "PXE could not prepend pseudo-header\n" ); + rc = -ENOMEM; + goto drop; + } + pshdr = iob_push ( iobuf, sizeof ( *pshdr ) ); + pshdr->src_ip = sin_src->sin_addr.s_addr; + pshdr->s_port = sin_src->sin_port; + pshdr->dest_ip = sin_dest->sin_addr.s_addr; + pshdr->d_port = sin_dest->sin_port; - done: + /* Add to queue */ + list_add_tail ( &iobuf->list, &pxe_udp->list ); + + return 0; + + drop: free_iob ( iobuf ); return rc; } @@ -116,6 +126,7 @@ static struct pxe_udp_connection pxe_udp = { .local = { .sin_family = AF_INET, }, + .list = LIST_HEAD_INIT ( pxe_udp.list ), }; /** @@ -205,11 +216,20 @@ static PXENV_EXIT_t pxenv_udp_open ( struct s_PXENV_UDP_OPEN *pxenv_udp_open ) { */ static PXENV_EXIT_t pxenv_udp_close ( struct s_PXENV_UDP_CLOSE *pxenv_udp_close ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + DBG ( "PXENV_UDP_CLOSE\n" ); /* Close UDP connection */ intf_restart ( &pxe_udp.xfer, 0 ); + /* Discard any received packets */ + list_for_each_entry_safe ( iobuf, tmp, &pxe_udp.list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + pxenv_udp_close->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; } @@ -365,20 +385,32 @@ pxenv_udp_write ( struct s_PXENV_UDP_WRITE *pxenv_udp_write ) { static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { struct in_addr dest_ip_wanted = { .s_addr = pxenv_udp_read->dest_ip }; struct in_addr dest_ip; + struct io_buffer *iobuf; + struct pxe_udp_pseudo_header *pshdr; uint16_t d_port_wanted = pxenv_udp_read->d_port; uint16_t d_port; + userptr_t buffer; + size_t len; + + /* Try receiving a packet, if the queue is empty */ + if ( list_empty ( &pxe_udp.list ) ) + step(); - /* Try receiving a packet */ - pxe_udp.pxenv_udp_read = pxenv_udp_read; - step(); - if ( pxe_udp.pxenv_udp_read ) { + /* Remove first packet from the queue */ + iobuf = list_first_entry ( &pxe_udp.list, struct io_buffer, list ); + if ( ! iobuf ) { /* No packet received */ DBG2 ( "PXENV_UDP_READ\n" ); - pxe_udp.pxenv_udp_read = NULL; goto no_packet; } - dest_ip.s_addr = pxenv_udp_read->dest_ip; - d_port = pxenv_udp_read->d_port; + list_del ( &iobuf->list ); + + /* Strip pseudo-header */ + assert ( iob_len ( iobuf ) >= sizeof ( *pshdr ) ); + pshdr = iobuf->data; + iob_pull ( iobuf, sizeof ( *pshdr ) ); + dest_ip.s_addr = pshdr->dest_ip; + d_port = pshdr->d_port; DBG ( "PXENV_UDP_READ" ); /* Filter on destination address and/or port */ @@ -386,14 +418,29 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { ( dest_ip_wanted.s_addr != dest_ip.s_addr ) ) { DBG ( " wrong IP %s", inet_ntoa ( dest_ip ) ); DBG ( " (wanted %s)\n", inet_ntoa ( dest_ip_wanted ) ); - goto no_packet; + goto drop; } if ( d_port_wanted && ( d_port_wanted != d_port ) ) { DBG ( " wrong port %d", htons ( d_port ) ); DBG ( " (wanted %d)\n", htons ( d_port_wanted ) ); - goto no_packet; + goto drop; } + /* Copy packet to buffer and record length */ + buffer = real_to_user ( pxenv_udp_read->buffer.segment, + pxenv_udp_read->buffer.offset ); + len = iob_len ( iobuf ); + if ( len > pxenv_udp_read->buffer_size ) + len = pxenv_udp_read->buffer_size; + copy_to_user ( buffer, 0, iobuf->data, len ); + pxenv_udp_read->buffer_size = len; + + /* Fill in source/dest information */ + pxenv_udp_read->src_ip = pshdr->src_ip; + pxenv_udp_read->s_port = pshdr->s_port; + pxenv_udp_read->dest_ip = pshdr->dest_ip; + pxenv_udp_read->d_port = pshdr->d_port; + DBG ( " %04x:%04x+%x %s:", pxenv_udp_read->buffer.segment, pxenv_udp_read->buffer.offset, pxenv_udp_read->buffer_size, inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->src_ip ) )); @@ -401,9 +448,14 @@ static PXENV_EXIT_t pxenv_udp_read ( struct s_PXENV_UDP_READ *pxenv_udp_read ) { inet_ntoa ( *( ( struct in_addr * ) &pxenv_udp_read->dest_ip ) ), ntohs ( pxenv_udp_read->d_port ) ); + /* Free I/O buffer */ + free_iob ( iobuf ); + pxenv_udp_read->Status = PXENV_STATUS_SUCCESS; return PXENV_EXIT_SUCCESS; + drop: + free_iob ( iobuf ); no_packet: pxenv_udp_read->Status = PXENV_STATUS_FAILURE; return PXENV_EXIT_FAILURE; diff --git a/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c b/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c index 29e586ed2..2eb68178a 100644 --- a/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c +++ b/roms/ipxe/src/arch/i386/interface/pxe/pxe_undi.c @@ -21,9 +21,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c index 1854501de..69d94c407 100644 --- a/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c +++ b/roms/ipxe/src/arch/i386/interface/syslinux/comboot_call.c @@ -41,8 +41,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/image.h> #include <ipxe/version.h> #include <usr/imgmgmt.h> -#include "config/console.h" -#include "config/serial.h" /** The "SYSLINUX" version string */ static char __bss16_array ( syslinux_version, [32] ); @@ -86,7 +84,6 @@ rmjmp_buf comboot_return; /* Mode flags set by INT 22h AX=0017h */ static uint16_t comboot_graphics_mode = 0; - /** * Print a string with a particular terminator */ @@ -261,8 +258,10 @@ static __asmcall void int21 ( struct i386_all_regs *ix86 ) { break; case 0x04: /* Write Character to Serial Port */ - serial_putc ( ix86->regs.dl ); - ix86->flags &= ~CF; + if ( serial_console.base ) { + uart_transmit ( &serial_console, ix86->regs.dl ); + ix86->flags &= ~CF; + } break; case 0x09: /* Write DOS String to Console */ @@ -455,15 +454,16 @@ static __asmcall void int22 ( struct i386_all_regs *ix86 ) { break; case 0x000B: /* Get Serial Console Configuration */ -#if defined(CONSOLE_SERIAL) && !defined(COMPRESERVE) - ix86->regs.dx = COMCONSOLE; - ix86->regs.cx = 115200 / COMSPEED; - ix86->regs.bx = 0; -#else - ix86->regs.dx = 0; -#endif + if ( serial_console.base ) { + ix86->regs.dx = ( ( intptr_t ) serial_console.base ); + ix86->regs.cx = serial_console.divisor; + ix86->regs.bx = 0; + ix86->flags &= ~CF; + } + break; - ix86->flags &= ~CF; + case 0x000C: /* Perform final cleanup */ + shutdown_boot(); break; case 0x000E: /* Get configuration file name */ @@ -712,3 +712,6 @@ void unhook_comboot_interrupts ( ) { unhook_bios_interrupt ( 0x22, ( unsigned int ) int22_wrapper, &int22_vector ); } + +/* Avoid dragging in serial console support unconditionally */ +struct uart serial_console __attribute__ (( weak )); diff --git a/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c b/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c index 390fc5545..ef7ee8151 100644 --- a/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c +++ b/roms/ipxe/src/arch/i386/interface/vmware/guestrpc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c b/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c index c6b9fff12..f7df4f75b 100644 --- a/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c +++ b/roms/ipxe/src/arch/i386/interface/vmware/vmconsole.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/interface/vmware/vmware.c b/roms/ipxe/src/arch/i386/interface/vmware/vmware.c index 8074e6118..a415465fb 100644 --- a/roms/ipxe/src/arch/i386/interface/vmware/vmware.c +++ b/roms/ipxe/src/arch/i386/interface/vmware/vmware.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/i386/prefix/bootpart.S b/roms/ipxe/src/arch/i386/prefix/bootpart.S index 968da1a38..6d0c6034a 100644 --- a/roms/ipxe/src/arch/i386/prefix/bootpart.S +++ b/roms/ipxe/src/arch/i386/prefix/bootpart.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BOOT_SEG 0x07c0 #define EXEC_SEG 0x0100 diff --git a/roms/ipxe/src/arch/i386/prefix/exeprefix.S b/roms/ipxe/src/arch/i386/prefix/exeprefix.S index cb61287d3..5c648d51d 100644 --- a/roms/ipxe/src/arch/i386/prefix/exeprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/exeprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Initial temporary stack size */ #define EXE_STACK_SIZE 0x400 diff --git a/roms/ipxe/src/arch/i386/prefix/hdprefix.S b/roms/ipxe/src/arch/i386/prefix/hdprefix.S index 876bfe1be..1d012d80b 100644 --- a/roms/ipxe/src/arch/i386/prefix/hdprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/hdprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .arch i386 diff --git a/roms/ipxe/src/arch/i386/prefix/isaromprefix.S b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S index e28208089..fb49819ee 100644 --- a/roms/ipxe/src/arch/i386/prefix/isaromprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/isaromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BUSTYPE "ISAR" #define _rom_start _isarom_start diff --git a/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S index 27ed231e7..6e43cd26a 100644 --- a/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/kkkpxeprefix.S @@ -5,12 +5,10 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) - -/* Since we have the whole stack, we can use cached DHCP information */ -REQUIRE_OBJECT ( pxeparent_dhcp ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Provide the PXENV_FILE_EXIT_HOOK API call */ +REQUIRING_SYMBOL ( _kkkpxe_start ) REQUIRE_OBJECT ( pxe_exit_hook ) #define PXELOADER_KEEP_UNDI diff --git a/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S index d177d7d62..3c17dbdb1 100644 --- a/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/kkpxeprefix.S @@ -3,10 +3,7 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) - -/* Since we have the whole stack, we can use cached DHCP information */ -REQUEST_OBJECT ( pxeparent_dhcp ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXELOADER_KEEP_UNDI #define PXELOADER_KEEP_PXE diff --git a/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S b/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S index c75608172..200006d83 100644 --- a/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/kpxeprefix.S @@ -3,7 +3,7 @@ ***************************************************************************** */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXELOADER_KEEP_UNDI #define _pxe_start _kpxe_start diff --git a/roms/ipxe/src/arch/i386/prefix/libprefix.S b/roms/ipxe/src/arch/i386/prefix/libprefix.S index 7c1ece791..7d5c1ed53 100644 --- a/roms/ipxe/src/arch/i386/prefix/libprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/libprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 @@ -296,11 +300,9 @@ copy_bytes: * Zero bytes * * Parameters: - * %ds:esi : source address * %es:edi : destination address * %ecx : length * Returns: - * %ds:esi : next source address * %es:edi : next destination address * Corrupts: * None @@ -396,8 +398,10 @@ process_bytes: movw %ax, %fs movw %ax, %gs +#ifdef NDEBUG /* Call memcpy()-like function */ call *%bx +#endif /* Return to (flat) real mode */ movl %cr0, %eax @@ -411,6 +415,20 @@ process_bytes: popw %fs popw %gs +#ifndef NDEBUG + /* Call memcpy()-like function in flat real mode (to allow for + * debug output via INT 10). + */ + pushw %ds + pushw %es + xorw %ax, %ax + movw %ax, %ds + movw %ax, %es + call *%bx + popw %es + popw %ds +#endif + /* Restore GDT */ data32 lgdt -8(%bp) addw $( 8 /* saved GDT */ + ( PM_DS + 8 ) /* GDT on stack */ ), %sp @@ -442,11 +460,11 @@ process_bytes: /* Convert %ds:esi and %es:edi back to physical addresses */ xorl %eax, %eax - movw %ds, %cx + movw %ds, %ax shll $4, %eax addl %eax, %esi xorl %eax, %eax - movw %es, %cx + movw %es, %ax shll $4, %eax addl %eax, %edi @@ -678,12 +696,21 @@ install: .globl install_prealloc install_prealloc: progress "install_prealloc:\n" - /* Save registers */ + /* Save registers on external stack */ pushal pushw %ds pushw %es cld /* Sanity: clear the direction flag asap */ + /* Switch to temporary stack in .bss16 */ + pushw %ss + popw %ds + movl %esp, %ecx + movw %bx, %ss + movl $_data16_memsz, %esp + pushw %ds + pushl %ecx + /* Set up %ds for (read-only) access to .prefix */ pushw %cs popw %ds @@ -710,6 +737,7 @@ install_prealloc: popl %esi #ifndef KEEP_IT_REAL + /* Access high memory by enabling the A20 gate. (We will * already have 4GB segment limits as a result of calling * install_block.) @@ -778,7 +806,7 @@ payload_death_message: movzwl %bx, %edi shll $4, %edi movl $_data16_filesz, %ecx - movl $_data16_memsz, %edx + movl $_data16_filesz, %edx /* do not zero our temporary stack */ call install_block /* .data16 */ /* Set up %ds for access to .data16 */ @@ -787,11 +815,8 @@ payload_death_message: /* Restore decompression temporary area physical address */ popl %edi -#ifdef KEEP_IT_REAL - /* Initialise libkir */ - movw %ax, (init_libkir_vector+2) - lcall *init_libkir_vector -#else +#ifndef KEEP_IT_REAL + /* Find a suitable decompression temporary area, if none specified */ pushl %eax testl %edi, %edi @@ -823,6 +848,22 @@ payload_death_message: call install_block popl %edi +#endif /* KEEP_IT_REAL */ + + /* Switch back to original stack and zero .bss16 */ + addr32 lss %ss:(%esp), %esp + pushl %edi + pushw %es + movw %bx, %es + movl $_data16_filesz, %edi + movl $_data16_memsz, %ecx + subl %edi, %ecx + call zero_bytes + popw %es + popl %edi + +#ifndef KEEP_IT_REAL + /* Initialise librm at current location */ progress " init_librm\n" movw %ax, (init_librm_vector+2) @@ -834,7 +875,6 @@ payload_death_message: incb memmap_post decl %ebp 1: - /* Call relocate() to determine target address for relocation. * relocate() will return with %esi, %edi and %ecx set up * ready for the copy to the new location. @@ -857,7 +897,14 @@ payload_death_message: /* Initialise librm at new location */ progress " init_librm\n" lcall *init_librm_vector -#endif + +#else /* KEEP_IT_REAL */ + + /* Initialise libkir */ + movw %ax, (init_libkir_vector+2) + lcall *init_libkir_vector + +#endif /* KEEP_IT_REAL */ /* Close access to payload */ progress " close_payload\n" diff --git a/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S index 259bc6ba5..64135e14b 100644 --- a/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/lkrnprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL_ANY ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BZI_LOAD_HIGH_ADDR 0x100000 diff --git a/roms/ipxe/src/arch/i386/prefix/mbr.S b/roms/ipxe/src/arch/i386/prefix/mbr.S index adfe20410..a1e237de8 100644 --- a/roms/ipxe/src/arch/i386/prefix/mbr.S +++ b/roms/ipxe/src/arch/i386/prefix/mbr.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .section ".prefix", "awx", @progbits diff --git a/roms/ipxe/src/arch/i386/prefix/mromprefix.S b/roms/ipxe/src/arch/i386/prefix/mromprefix.S index 4c94457c2..b636b92af 100644 --- a/roms/ipxe/src/arch/i386/prefix/mromprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/mromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PCIBIOS_READ_CONFIG_WORD 0xb109 #define PCIBIOS_READ_CONFIG_DWORD 0xb10a @@ -463,6 +467,7 @@ pci_set_mem_access: .org 0x00 mromheader: .word 0xaa55 /* BIOS extension signature */ + .byte 0x01 /* Dummy size (BIOS bug workaround) */ .org 0x18 .word mpciheader .org 0x1a diff --git a/roms/ipxe/src/arch/i386/prefix/nbiprefix.S b/roms/ipxe/src/arch/i386/prefix/nbiprefix.S index 06e7df5b7..16c79566c 100644 --- a/roms/ipxe/src/arch/i386/prefix/nbiprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/nbiprefix.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .code16 diff --git a/roms/ipxe/src/arch/i386/prefix/nullprefix.S b/roms/ipxe/src/arch/i386/prefix/nullprefix.S index 032d41e0f..bd0ff339e 100644 --- a/roms/ipxe/src/arch/i386/prefix/nullprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/nullprefix.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .org 0 .text .arch i386 diff --git a/roms/ipxe/src/arch/i386/prefix/pciromprefix.S b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S index 45ba31f50..5a5a49647 100644 --- a/roms/ipxe/src/arch/i386/prefix/pciromprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/pciromprefix.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define BUSTYPE "PCIR" #define _rom_start _pcirom_start diff --git a/roms/ipxe/src/arch/i386/prefix/pxeprefix.S b/roms/ipxe/src/arch/i386/prefix/pxeprefix.S index 6e29c7949..465ce4345 100644 --- a/roms/ipxe/src/arch/i386/prefix/pxeprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/pxeprefix.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define PXENV_UNDI_SHUTDOWN 0x0005 #define PXENV_UNDI_GET_NIC_TYPE 0x0012 diff --git a/roms/ipxe/src/arch/i386/prefix/romprefix.S b/roms/ipxe/src/arch/i386/prefix/romprefix.S index 7bc4fe8cd..18dda2b37 100644 --- a/roms/ipxe/src/arch/i386/prefix/romprefix.S +++ b/roms/ipxe/src/arch/i386/prefix/romprefix.S @@ -6,9 +6,10 @@ * table so using a noticeable amount of stack space is a no-no. */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #include <config/general.h> +#include <config/branding.h> #define PNP_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'n' << 16 ) + ( 'P' << 24 ) ) #define PMM_SIGNATURE ( '$' + ( 'P' << 8 ) + ( 'M' << 16 ) + ( 'M' << 24 ) ) @@ -90,7 +91,7 @@ pciheader: .ascii "PCIR" /* Signature */ .word pci_vendor_id /* Vendor identification */ .word pci_device_id /* Device identification */ - .word 0x0000 /* Device list pointer */ + .word ( pci_devlist - pciheader ) /* Device list pointer */ .word pciheader_len /* PCI data structure length */ .byte 0x03 /* PCI data structure revision */ .byte 0x02, 0x00, 0x00 /* Class code */ @@ -106,6 +107,17 @@ pciheader_runtime_length: .equ pciheader_len, . - pciheader .size pciheader, . - pciheader + /* PCI additional device list (filled in by linker) */ + .section ".pci_devlist.00000000", "a", @progbits +pci_devlist: + .previous + .section ".pci_devlist.ffffffff", "a", @progbits +pci_devlist_end: + .short 0x0000 /* List terminator */ + .previous + /* Ensure that terminator is always present */ + .reloc pciheader, RELOC_TYPE_NONE, pci_devlist_end + .section ".zinfo.fixup", "a", @progbits /* Compressor fixups */ .ascii ZINFO_TYPE_ADxW .long pciheader_image_length @@ -573,7 +585,7 @@ get_pmm_decompress_to: * Note to hardware vendors: * * If you wish to brand this boot ROM, please do so by defining the - * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/general.h. + * strings PRODUCT_NAME and PRODUCT_SHORT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all references * to iPXE or http://ipxe.org, we prefer you not to do so. @@ -589,7 +601,10 @@ init_message: .ascii "\n" .ascii PRODUCT_NAME .ascii "\n" - .asciz "iPXE (http://ipxe.org)" + .ascii PRODUCT_SHORT_NAME + .ascii " (" + .ascii PRODUCT_URI + .asciz ")" .size init_message, . - init_message .ifeqs BUSTYPE, "PCIR" init_message_pci: @@ -771,7 +786,9 @@ exec: /* Set %ds = %cs */ /* Store PCI bus:dev.fn, if applicable */ .ifeqs BUSTYPE, "PCIR" +#ifdef AUTOBOOT_ROM_FILTER movw %ax, autoboot_busdevfn +#endif /* AUTOBOOT_ROM_FILTER */ .endif /* Call main() */ @@ -870,3 +887,9 @@ wait_for_tick: popl %eax ret .size wait_for_tick, . - wait_for_tick + +/* Drag in objects via _rom_start */ +REQUIRING_SYMBOL ( _rom_start ) + +/* Drag in ROM configuration */ +REQUIRE_OBJECT ( config_romprefix ) diff --git a/roms/ipxe/src/arch/i386/prefix/undiloader.S b/roms/ipxe/src/arch/i386/prefix/undiloader.S index 74bb59041..5cace44b7 100644 --- a/roms/ipxe/src/arch/i386/prefix/undiloader.S +++ b/roms/ipxe/src/arch/i386/prefix/undiloader.S @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .text .code16 diff --git a/roms/ipxe/src/arch/i386/prefix/unlzma.S b/roms/ipxe/src/arch/i386/prefix/unlzma.S new file mode 100644 index 000000000..8d4b3c1a8 --- /dev/null +++ b/roms/ipxe/src/arch/i386/prefix/unlzma.S @@ -0,0 +1,942 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/**************************************************************************** + * + * This file provides the decompress() and decompress16() functions + * which can be called in order to decompress an LZMA-compressed + * image. The code is modelled on the public-domain "XZ Embedded" + * implementation as used by the Linux kernel. Symbol names are + * chosen to match the XZ Embedded implementation where possible, for + * ease of reference. + * + * This code is optimised for size rather than speed, since the amount + * of data to be decompressed is trivially small by modern standards. + * + * The same basic assembly code is used to compile both decompress() + * and decompress16(). + * + * Note that these functions require large amounts of stack space. + * + **************************************************************************** + */ + + .text + .arch i586 + .section ".prefix.lib", "ax", @progbits + +#ifdef CODE16 +#define ADDR16 +#define ADDR32 addr32 +#define decompress decompress16 + .code16 +#else /* CODE16 */ +#define ADDR16 addr16 +#define ADDR32 + .code32 +#endif /* CODE16 */ + +/**************************************************************************** + * Debugging + **************************************************************************** + * + * This code will usually run in 16-bit protected mode, in which case + * only the 0xe9 debug port (present on some virtual machines) can be + * used. + * + * To debug on real hardware, build with DEBUG=libprefix. This will + * cause this code to be called in flat real mode, and so DEBUG_INT10 + * may be used. + */ + +/* Enable debugging via 0xe9 debug port */ +#define DEBUG_E9 0 + +/* Enable debugging via BIOS INT 10 (works only when in flat real mode) */ +#define DEBUG_INT10 0 + +#if ( DEBUG_E9 || DEBUG_INT10 ) + .macro print_character, reg + pushfl + pushw %ax + pushw %bx + pushw %bp + movb \reg, %al + movw $0x0007, %bx + movb $0x0e, %ah +#if DEBUG_E9 + outb %al, $0xe9 +#endif +#if DEBUG_INT10 + cmpb $('\n'), %al + jne L\@ + int $0x10 + movb $('\r'), %al +L\@: int $0x10 +#endif + popw %bp + popw %bx + popw %ax + popfl + .endm + + .macro print_hex_nibble + pushfl + pushw %ax + cmpb $10, %al + sbb $0x69, %al + das + print_character %al + popw %ax + popfl + .endm + + .macro print_hex_byte, reg + pushfl + pushw %ax + movb \reg, %al + pushw %ax + shrb $4, %al + print_hex_nibble + popw %ax + andb $0x0f, %al + print_hex_nibble + popw %ax + popfl + .endm + + .macro print_hex_word, reg + pushw %ax + movw \reg, %ax + print_hex_byte %ah + print_hex_byte %al + popw %ax + .endm + + .macro print_hex_dword, reg + pushl %eax + movl \reg, %eax + rorl $16, %eax + print_hex_word %ax + rorl $16, %eax + print_hex_word %ax + popl %eax + .endm +#else + .macro print_character, char + .endm + .macro print_hex_byte, reg + .endm + .macro print_hex_word, reg + .endm + .macro print_hex_dword, reg + .endm +#endif + +/**************************************************************************** + * LZMA parameters and data structures + **************************************************************************** + */ + +/* LZMA decompressor states (as used in XZ Embedded) */ +#define STATE_LIT_LIT 0x00 +#define STATE_MATCH_LIT_LIT 0x01 +#define STATE_REP_LIT_LIT 0x02 +#define STATE_SHORTREP_LIT_LIT 0x03 +#define STATE_MATCH_LIT 0x04 +#define STATE_REP_LIT 0x05 +#define STATE_SHORTREP_LIT 0x06 +#define STATE_LIT_MATCH 0x07 +#define STATE_LIT_LONGREP 0x08 +#define STATE_LIT_SHORTREP 0x09 +#define STATE_NONLIT_MATCH 0x0a +#define STATE_NONLIT_REP 0x0b + +/* LZMA maximum decompressor state in which most recent symbol was a literal */ +#define STATE_LIT_MAX 0x06 + +/* LZMA number of literal context bits ("lc=" parameter) */ +#define LZMA_LC 2 + + .struct 0 +lzma_len_dec: +choice: .word 0 +choice2: .word 0 +low: .rept ( 1 << 3 ) + .word 0 + .endr +mid: .rept ( 1 << 3 ) + .word 0 + .endr +high: .rept ( 1 << 8 ) + .word 0 + .endr + .equ sizeof__lzma_len_dec, . - lzma_len_dec + .previous + + .struct 0 +lzma_dec: +out_start: .long 0 +rc_code: .long 0 +rc_range: .long 0 +len: .word 0 +reps: +rep0: .long 0 +rep1: .long 0 +rep2: .long 0 +rep3: .long 0 +probs: +is_match: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep1: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep2: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +is_rep0_long: .word 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +dist_slot: .rept ( 4 * ( 1 << 6 ) ) + .word 0 + .endr +dist_special: .rept ( ( 1 << ( 14 / 2 ) ) - 14 ) + .word 0 + .endr +dist_align: .rept ( 1 << 4 ) + .word 0 + .endr +match_len_dec: .space sizeof__lzma_len_dec +rep_len_dec: .space sizeof__lzma_len_dec +literal: .rept ( ( 1 << LZMA_LC ) * 0x300 ) + .word 0 + .endr + .align 4 + .equ sizeof__lzma_dec, . - lzma_dec + .previous + + /* Some binutils versions seem not to handle .struct/.previous */ + .section ".prefix.lib", "ax", @progbits + +/***************************************************************************** + * Normalise range encoder + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : current range + ***************************************************************************** + */ +rc_normalise: + /* Check if rc_range is less than 1<<24 */ + testb $0xff, (rc_range+3)(%ebp) + jnz 1f + /* If it is, shift in a new byte from the compressed input data */ + shll $8, rc_range(%ebp) + shll $8, rc_code(%ebp) + ADDR32 lodsb + movb %al, (rc_code+0)(%ebp) +1: /* Return current range */ + movl rc_range(%ebp), %eax + ret + .size rc_normalise, . - rc_normalise + +/***************************************************************************** + * Decode single range-encoded bit using a probability estimate + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * CF : decoded bit + * ZF : inverse of decoded bit + * Corrupts: + * none + ***************************************************************************** + */ +rc_bit: + /* Preserve registers */ + pushl %eax + pushl %edx + /* Perform normalisation */ + call rc_normalise + /* Calculate bound in %eax and probability estimate in %dx */ + shrl $11, %eax + movzwl (%ebp,%ebx), %edx + mul %edx /* will zero %edx */ + movw (%ebp,%ebx), %dx + /* Compare code against bound */ + cmpl %eax, rc_code(%ebp) + jae 2f +1: /* Code is less than bound */ + movl %eax, rc_range(%ebp) + negw %dx + addw $(1<<11), %dx + shrw $5, %dx + addw %dx, (%ebp,%ebx) + xorw %ax, %ax /* Clear CF, set ZF */ + jmp 99f +2: /* Code is greater than or equal to bound */ + subl %eax, rc_range(%ebp) + subl %eax, rc_code(%ebp) + shrw $5, %dx + subw %dx, (%ebp,%ebx) + incw %dx /* Clear ZF (%dx is 11-bit; can never wrap) */ + stc /* Set CF */ +99: /* Restore registers and return */ + popl %edx + popl %eax + ret + .size rc_bit, . - rc_bit + +/***************************************************************************** + * Decode MSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree: + /* Preserve registers */ + pushl %edi + pushw %cx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + leaw (%edi,%eax,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + ADDR16 loop 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %cx + popl %edi + btrw %cx, %ax + ret + .size rc_bittree, . - rc_bittree + +/***************************************************************************** + * Decode LSB-first bittree + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_reverse: + /* Preserve registers */ + pushw %cx + /* Decode bittree */ + call rc_bittree +1: /* Reverse result */ + rcrb %al + rclb %ah + ADDR16 loop 1b + shrw $8, %ax + /* Restore registers and return */ + popw %cx + ret + .size rc_bittree_reverse, . - rc_bittree_reverse + +/***************************************************************************** + * Decode MSB-first bittree with optional match byte + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : probability estimate set pointer (offset from %ebp) + * %cl : match byte + * %ch : 1 to use match byte, 0 to ignore match byte + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bittree + * Corrupts: + * none + ***************************************************************************** + */ +rc_bittree_match: + /* Preserve registers */ + pushl %edi + pushw %cx + pushw %dx + movl %ebx, %edi + /* Initialise registers */ + movl $1, %eax +1: /* Decode bit */ + rolb $1, %cl + movw %cx, %dx + andb %dh, %dl /* match_bit in %dl */ + movw %dx, %bx + addb %bl, %bh + xorb %bl, %bl + addw %ax, %bx /* offset + match_bit + symbol */ + leaw (%edi,%ebx,2), %bx /* high word always zero anyway */ + call rc_bit + rclw %ax + movb %al, %dh + notb %dh + xorb %dh, %dl + andb %dl, %ch /* offset &= ( match_bit ^ bit ) */ + testb %ah, %ah + jz 1b + /* Restore registers, clear unwanted high bit of result, and return */ + movl %edi, %ebx + popw %dx + popw %cx + popl %edi + xorb %ah, %ah + ret + .size rc_bittree_match, . - rc_bittree_match + +/***************************************************************************** + * Decode direct bits (no probability estimates) + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %cx : number of bits to decode + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %eax : decoded bits + * Corrupts: + * none + ***************************************************************************** + */ +rc_direct: + /* Preserve registers */ + pushl %ebx + pushw %cx + pushl %edx + /* Initialise registers */ + xorl %edx, %edx +1: /* Perform normalisation */ + call rc_normalise + /* Decode bit */ + shrl $1, %eax + movl %eax, rc_range(%ebp) + movl rc_code(%ebp), %ebx + subl %eax, %ebx + js 2f + movl %ebx, rc_code(%ebp) +2: rcll %ebx + rcll %edx + xorb $1, %dl + ADDR16 loop 1b + /* Restore registers and return */ + movl %edx, %eax + popl %edx + popw %cx + popl %ebx + ret + .size rc_direct, . - rc_direct + +/***************************************************************************** + * Decode an LZMA literal + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found (always zero) + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Literals are coded as an eight-bit tree, using a match byte if the + * previous symbol was not a literal. + * + */ +lzma_literal: + /* Get most recent output byte, if available */ + xorl %ebx, %ebx + cmpl %edi, out_start(%ebp) + je 1f + movb %es:-1(%edi), %bh +1: /* Locate probability estimate set */ + shrb $( 8 - LZMA_LC ), %bh + shlb $1, %bh + leaw literal(%ebx,%ebx,2), %bx + /* Get match byte, if applicable */ + xorw %cx, %cx + cmpb $STATE_LIT_MAX, %dl + jbe 1f + movl rep0(%ebp), %eax + notl %eax + movb %es:(%edi,%eax), %cl + movb $1, %ch +1: /* Decode bittree */ + call rc_bittree_match + /* Store output byte */ + ADDR32 stosb + print_hex_byte %al + print_character $(' ') + /* Update LZMA state */ + subb $3, %dl + jns 1f + xorb %dl, %dl +1: cmpb $7, %dl + jb 1f + subb $3, %dl +1: /* Clear CF and return */ + clc + ret + .size lzma_literal, . - lzma_literal + +/***************************************************************************** + * Decode an LZMA length + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %ebx : length parameter pointer (offset from %ebp) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * Corrupts: + * %ebx + ***************************************************************************** + * + * Lengths are encoded as: + * + * "0" + 3 bits : lengths 2-9 ("low") + * "10" + 3 bits : lengths 10-17 ("mid") + * "11" + 8 bits : lengths 18-273 ("high") + */ +lzma_len: + /* Preserve registers */ + pushl %eax + pushl %ecx + pushl %edi + movl %ebx, %edi + /* Start by assuming three bits and a base length of 2 */ + movw $3, %cx + movw $2, len(%ebp) + /* Check low-length choice bit */ + leal choice(%edi), %ebx + call rc_bit + leal low(%edi), %ebx + jz 1f + /* Check high-length choice bit */ + leal choice2(%edi), %ebx + call rc_bit + leal mid(%edi), %ebx + movb $10, len(%ebp) + jz 1f + leal high(%edi), %ebx + movb $8, %cl + movb $18, len(%ebp) +1: /* Get encoded length */ + call rc_bittree + addw %ax, len(%ebp) + /* Restore registers and return */ + movl %edi, %ebx + popl %edi + popl %ecx + popl %eax + ret + .size lzma_len, . - lzma_len + +/***************************************************************************** + * Copy (possibly repeated) matched data + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %cl : repeated match distance index (for repeated matches) + * %eax : match distance (for non-repeated matches) + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * CF : match distance is out of range + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +match: /* Update repeated match list */ + print_character $('[') + movl $3, %ecx + jmp 1f +match_rep: + print_character $('[') + print_character $('R') + print_hex_byte %cl + print_character $('=') + movzbl %cl, %ecx + movl reps(%ebp,%ecx,4), %eax + jcxz 2f +1: movl (reps-4)(%ebp,%ecx,4), %ebx + movl %ebx, reps(%ebp,%ecx,4) + loop 1b + movl %eax, rep0(%ebp) +2: /* Preserve registers */ + pushl %esi + /* Get stored match length */ + movzwl len(%ebp), %ecx + print_hex_dword %eax + print_character $('+') + print_hex_word %cx + print_character $(']') + print_character $(' ') + /* Abort with CF set if match distance is out of range */ + movl out_start(%ebp), %esi + negl %esi + leal -1(%edi,%esi), %esi + cmpl %eax, %esi + jc 99f + /* Perform copy */ + notl %eax + leal (%edi,%eax), %esi + ADDR32 es rep movsb +99: /* Restore registers and return */ + popl %esi + ret + .size match, . - match + +/***************************************************************************** + * Decode an LZMA match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Matches are encoded as an LZMA length followed by a 6-bit "distance + * slot" code, 0-26 fixed-probability bits, and 0-5 context encoded + * bits. + */ +lzma_match: + /* Preserve registers */ + pushl %edi + /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb $STATE_LIT_MATCH, %dl + jbe 1f + movb $STATE_NONLIT_MATCH, %dl +1: /* Decode length */ + movl $match_len_dec, %ebx + call lzma_len + /* Decode distance slot */ + movw len(%ebp), %bx + subw $2, %bx + cmpw $4, %bx + jb 1f + movw $3, %bx +1: shlw $7, %bx + addw $dist_slot, %bx + movw $6, %cx + call rc_bittree + /* Distance slots 0-3 are literal distances */ + cmpb $4, %al + jb 99f + /* Determine initial bits: 10/11 for even/odd distance codes */ + movl %eax, %edi + andw $1, %di + orw $2, %di + /* Determine number of context-encoded bits */ + movw %ax, %cx + shrb $1, %cl + decb %cl + /* Select context to be used in absence of fixed-probability bits */ + movl %edi, %ebx + shlw %cl, %bx + subw %ax, %bx + leaw (dist_special-2)(%ebx,%ebx), %bx + /* Decode fixed-probability bits, if any */ + cmpb $6, %cl + jb 1f + subb $4, %cl + shll %cl, %edi + call rc_direct + orl %eax, %edi + /* Select context to be used in presence of fixed-probability bits */ + movb $4, %cl + movl $dist_align, %ebx +1: /* Decode context-encoded bits */ + shll %cl, %edi + call rc_bittree_reverse + orl %edi, %eax +99: /* Restore registers and tail-call */ + popl %edi + jmp match + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode an LZMA repeated match + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + * + * Repeated matches are encoded as: + * + * "00" : shortrep0 (implicit length 1) + * "01" + len : longrep0 + * "10" + len : longrep1 + * "110" + len : longrep2 + * "111" + len : longrep3 + */ +lzma_rep_match: + /* Initially assume longrep0 */ + movw $(STATE_LIT_LONGREP << 8), %cx + /* Get is_rep0 bit */ + leal is_rep0(,%edx,2), %ebx + call rc_bit + jnz 1f + /* Get is_rep0_long bit */ + leal is_rep0_long(,%edx,2), %ebx + call rc_bit + jnz 98f + movw $1, len(%ebp) + movb $STATE_LIT_SHORTREP, %ch + jmp 99f +1: /* Get is_rep1 bit */ + incb %cl + leal is_rep1(,%edx,2), %ebx + call rc_bit + jz 98f + /* Get is_rep2 bit */ + incb %cl + leal is_rep2(,%edx,2), %ebx + call rc_bit + adcb $0, %cl +98: /* Decode length */ + movl $rep_len_dec, %ebx + call lzma_len +99: /* Update LZMA state */ + cmpb $STATE_LIT_MAX, %dl + movb %ch, %dl + jbe 1f + movb $STATE_NONLIT_REP, %dl +1: /* Tail call */ + jmp match_rep + .size lzma_match, . - lzma_match + +/***************************************************************************** + * Decode one LZMA symbol + * + * Parameters: + * %ss:%ebp : LZMA parameter block + * %ds:%esi : compressed input data pointer + * %es:%edi : uncompressed output data pointer + * %edx : LZMA state + * Returns: + * %ds:%esi : compressed input data pointer (possibly updated) + * %es:%edi : uncompressed output data pointer (updated) + * %edx : LZMA state + * CF : end of payload marker found + * Corrupts: + * %eax + * %ebx + * %ecx + ***************************************************************************** + */ +lzma_decode: + /* Get is_match bit */ + leal is_match(,%edx,2), %ebx + call rc_bit + jz lzma_literal + /* Get is_rep bit */ + leal is_rep(,%edx,2), %ebx + call rc_bit + jz lzma_match + jmp lzma_rep_match + .size lzma_decode, . - lzma_decode + +/**************************************************************************** + * Undo effect of branch-call-jump (BCJ) filter + * + * Parameters: + * %es:%esi : start of uncompressed output data (note %es) + * %es:%edi : end of uncompressed output data + * Returns: + * Corrupts: + * %eax + * %ebx + * %ecx + * %edx + * %esi + ***************************************************************************** + */ +bcj_filter: + /* Store (negative) start of data in %edx */ + movl %esi, %edx + negl %edx + /* Calculate limit in %ecx */ + leal -5(%edi,%edx), %ecx +1: /* Calculate offset in %ebx */ + leal (%esi,%edx), %ebx + /* Check for end of data */ + cmpl %ecx, %ebx + ja 99f + /* Check for an opcode which would be followed by a rel32 address */ + ADDR32 es lodsb + andb $0xfe, %al + cmpb $0xe8, %al + jne 1b + /* Get current jump target value in %eax */ + ADDR32 es lodsl + /* Convert absolute addresses in the range [0,limit) back to + * relative addresses in the range [-offset,limit-offset). + */ + cmpl %ecx, %eax + jae 2f + subl %ebx,%es:-4(%esi) +2: /* Convert negative numbers in the range [-offset,0) back to + * positive numbers in the range [limit-offset,limit). + */ + notl %eax /* Range is now [0,offset) */ + cmpl %ebx, %eax + jae 1b + addl %ecx,%es:-4(%esi) + jmp 1b +99: /* Return */ + ret + .size bcj_filter, . - bcj_filter + +/**************************************************************************** + * decompress (real-mode or 16/32-bit protected-mode near call) + * + * Decompress data + * + * Parameters (passed via registers): + * %ds:%esi : Start of compressed input data + * %es:%edi : Start of output buffer + * Returns: + * %ds:%esi - End of compressed input data + * %es:%edi - End of decompressed output data + * All other registers are preserved + * + * NOTE: It would be possible to build a smaller version of the + * decompression code for -DKEEP_IT_REAL by using 16-bit registers + * where possible. + **************************************************************************** + */ + .globl decompress +decompress: + /* Preserve registers */ + pushl %eax + pushl %ebx + pushl %ecx + pushl %edx + pushl %ebp + /* Allocate parameter block */ + subl $sizeof__lzma_dec, %esp + movl %esp, %ebp + /* Zero parameter block and set all probabilities to 0.5 */ + pushl %edi + pushw %es + pushw %ss + popw %es + movl %ebp, %edi + xorl %eax, %eax + movl $( sizeof__lzma_dec / 4 ), %ecx + ADDR32 rep stosl + leal probs(%ebp), %edi + movw $( ( 1 << 11 ) / 2 ), %ax + movl $( ( sizeof__lzma_dec - probs ) / 2 ), %ecx + ADDR32 rep stosw + popw %es + popl %edi + /* Initialise remaining parameters */ + movl %edi, out_start(%ebp) + print_character $('\n') + ADDR32 lodsb /* discard initial byte */ + print_hex_byte %al + ADDR32 lodsl + bswapl %eax + print_hex_dword %eax + print_character $('\n') + movl %eax, rc_code(%ebp) + decl rc_range(%ebp) + movl $STATE_LIT_LIT, %edx +1: /* Decompress until we reach end of buffer */ + call lzma_decode + jnc 1b + call rc_normalise + print_character $('\n') + /* Undo BCJ filter */ + pushl %esi + movl out_start(%ebp), %esi + call bcj_filter + popl %esi + /* Restore registers and return */ + addl $sizeof__lzma_dec, %esp + popl %ebp + popl %edx + popl %ecx + popl %ebx + popl %eax + ret + + /* Specify minimum amount of stack space required */ + .globl _min_decompress_stack + .equ _min_decompress_stack, ( sizeof__lzma_dec + 512 /* margin */ ) diff --git a/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S b/roms/ipxe/src/arch/i386/prefix/unlzma16.S index b24c2846f..32b43f0dc 100644 --- a/roms/ipxe/src/arch/i386/prefix/unnrv2b16.S +++ b/roms/ipxe/src/arch/i386/prefix/unlzma16.S @@ -3,7 +3,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) #define CODE16 -#include "unnrv2b.S" +#include "unlzma.S" diff --git a/roms/ipxe/src/arch/i386/prefix/unnrv2b.S b/roms/ipxe/src/arch/i386/prefix/unnrv2b.S deleted file mode 100644 index f5724c134..000000000 --- a/roms/ipxe/src/arch/i386/prefix/unnrv2b.S +++ /dev/null @@ -1,184 +0,0 @@ -/* - * Copyright (C) 1996-2002 Markus Franz Xaver Johannes Oberhumer - * - * This file 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. - * - * Originally this code was part of ucl the data compression library - * for upx the ``Ultimate Packer of eXecutables''. - * - * - Converted to gas assembly, and refitted to work with etherboot. - * Eric Biederman 20 Aug 2002 - * - * - Structure modified to be a subroutine call rather than an - * executable prefix. - * Michael Brown 30 Mar 2004 - * - * - Modified to be compilable as either 16-bit or 32-bit code. - * Michael Brown 9 Mar 2005 - */ - -FILE_LICENCE ( GPL2_OR_LATER ) - -/**************************************************************************** - * This file provides the decompress() and decompress16() functions - * which can be called in order to decompress an image compressed with - * the nrv2b utility in src/util. - * - * These functions are designed to be called by the prefix. They are - * position-independent code. - * - * The same basic assembly code is used to compile both - * decompress() and decompress16(). - **************************************************************************** - */ - - .text - .arch i386 - .section ".prefix.lib", "ax", @progbits - -#ifdef CODE16 -/**************************************************************************** - * decompress16 (real-mode near call, position independent) - * - * Decompress data in 16-bit mode - * - * Parameters (passed via registers): - * %ds:%esi - Start of compressed input data - * %es:%edi - Start of output buffer - * Returns: - * %ds:%esi - End of compressed input data - * %es:%edi - End of decompressed output data - * All other registers are preserved - * - * NOTE: It would be possible to build a smaller version of the - * decompression code for -DKEEP_IT_REAL by using - * #define REG(x) x - * to use 16-bit registers where possible. This would impose limits - * that the compressed data size must be in the range [1,65533-%si] - * and the uncompressed data size must be in the range [1,65536-%di] - * (where %si and %di are the input values for those registers). Note - * particularly that the lower limit is 1, not 0, and that the upper - * limit on the input (compressed) data really is 65533, since the - * algorithm may read up to three bytes beyond the end of the input - * data, since it reads dwords. - **************************************************************************** - */ - -#define REG(x) e ## x -#define ADDR32 addr32 - - .code16 - .globl decompress16 -decompress16: - -#else /* CODE16 */ - -/**************************************************************************** - * decompress (32-bit protected-mode near call, position independent) - * - * Parameters (passed via registers): - * %ds:%esi - Start of compressed input data - * %es:%edi - Start of output buffer - * Returns: - * %ds:%esi - End of compressed input data - * %es:%edi - End of decompressed output data - * All other registers are preserved - **************************************************************************** - */ - -#define REG(x) e ## x -#define ADDR32 - - .code32 - .globl decompress -decompress: - -#endif /* CODE16 */ - -#define xAX REG(ax) -#define xCX REG(cx) -#define xBP REG(bp) -#define xSI REG(si) -#define xDI REG(di) - - /* Save registers */ - push %xAX - pushl %ebx - push %xCX - push %xBP - /* Do the decompression */ - cld - xor %xBP, %xBP - dec %xBP /* last_m_off = -1 */ - jmp dcl1_n2b - -decompr_literals_n2b: - ADDR32 movsb -decompr_loop_n2b: - addl %ebx, %ebx - jnz dcl2_n2b -dcl1_n2b: - call getbit32 -dcl2_n2b: - jc decompr_literals_n2b - xor %xAX, %xAX - inc %xAX /* m_off = 1 */ -loop1_n2b: - call getbit1 - adc %xAX, %xAX /* m_off = m_off*2 + getbit() */ - call getbit1 - jnc loop1_n2b /* while(!getbit()) */ - sub $3, %xAX - jb decompr_ebpeax_n2b /* if (m_off == 2) goto decompr_ebpeax_n2b ? */ - shl $8, %xAX - ADDR32 movb (%xSI), %al /* m_off = (m_off - 3)*256 + src[ilen++] */ - inc %xSI - xor $-1, %xAX - jz decompr_end_n2b /* if (m_off == 0xffffffff) goto decomp_end_n2b */ - mov %xAX, %xBP /* last_m_off = m_off ?*/ -decompr_ebpeax_n2b: - xor %xCX, %xCX - call getbit1 - adc %xCX, %xCX /* m_len = getbit() */ - call getbit1 - adc %xCX, %xCX /* m_len = m_len*2 + getbit()) */ - jnz decompr_got_mlen_n2b /* if (m_len == 0) goto decompr_got_mlen_n2b */ - inc %xCX /* m_len++ */ -loop2_n2b: - call getbit1 - adc %xCX, %xCX /* m_len = m_len*2 + getbit() */ - call getbit1 - jnc loop2_n2b /* while(!getbit()) */ - inc %xCX - inc %xCX /* m_len += 2 */ -decompr_got_mlen_n2b: - cmp $-0xd00, %xBP - adc $1, %xCX /* m_len = m_len + 1 + (last_m_off > 0xd00) */ - push %xSI - ADDR32 lea (%xBP,%xDI), %xSI /* m_pos = dst + olen + -m_off */ - rep - es ADDR32 movsb /* dst[olen++] = *m_pos++ while(m_len > 0) */ - pop %xSI - jmp decompr_loop_n2b - - -getbit1: - addl %ebx, %ebx - jnz 1f -getbit32: - ADDR32 movl (%xSI), %ebx - sub $-4, %xSI /* sets carry flag */ - adcl %ebx, %ebx -1: - ret - -decompr_end_n2b: - /* Restore registers and return */ - pop %xBP - pop %xCX - popl %ebx - pop %xAX - ret diff --git a/roms/ipxe/src/arch/i386/prefix/usbdisk.S b/roms/ipxe/src/arch/i386/prefix/usbdisk.S index fa7d1956e..9676406e2 100644 --- a/roms/ipxe/src/arch/i386/prefix/usbdisk.S +++ b/roms/ipxe/src/arch/i386/prefix/usbdisk.S @@ -1,3 +1,5 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + .text .arch i386 .section ".prefix", "awx", @progbits @@ -6,18 +8,27 @@ #include "mbr.S" -/* Partition table: ZIP-compatible partition 4, 64 heads, 32 sectors/track */ +/* Partition table: 64 heads, 32 sectors/track (ZIP-drive compatible) */ .org 446 .space 16 .space 16 - .space 16 - .byte 0x80, 0x01, 0x01, 0x00 - .byte 0xeb, 0x3f, 0x20, 0x01 + /* Partition 3: log partition (for CONSOLE_INT13) */ + .byte 0x00, 0x01, 0x01, 0x00 + .byte 0xe0, 0x3f, 0x20, 0x00 .long 0x00000020 - .long 0x00000fe0 + .long 0x000007e0 + /* Partition 4: boot partition */ + .byte 0x80, 0x00, 0x01, 0x01 + .byte 0xeb, 0x3f, 0x20, 0x02 + .long 0x00000800 + .long 0x00001000 .org 510 .byte 0x55, 0xaa -/* Skip to start of partition */ +/* Skip to start of log partition */ .org 32 * 512 + .ascii "iPXE LOG\n\n" + +/* Skip to start of boot partition */ + .org 2048 * 512 diff --git a/roms/ipxe/src/arch/i386/scripts/i386.lds b/roms/ipxe/src/arch/i386/scripts/i386.lds index 98f95cb23..38c89e14b 100644 --- a/roms/ipxe/src/arch/i386/scripts/i386.lds +++ b/roms/ipxe/src/arch/i386/scripts/i386.lds @@ -27,6 +27,13 @@ SECTIONS { PROVIDE ( _max_align = 16 ); /* + * Allow decompressor to require a minimum amount of temporary stack + * space. + * + */ + PROVIDE ( _min_decompress_stack = 0 ); + + /* * The prefix * */ @@ -34,6 +41,7 @@ SECTIONS { .prefix 0x0 : AT ( _prefix_lma ) { _prefix = .; *(.prefix) + *(SORT(.pci_devlist.*)) *(.prefix.*) _mprefix = .; } .bss.prefix (NOLOAD) : AT ( _end_lma ) { @@ -87,6 +95,7 @@ SECTIONS { *(.bss16.*) *(.stack16) *(.stack16.*) + . = MAX ( ., _mdata16 + _min_decompress_stack ); _edata16 = .; } _data16_filesz = ABSOLUTE ( _mdata16 ) - ABSOLUTE ( _data16 ); diff --git a/roms/ipxe/src/arch/i386/transitions/liba20.S b/roms/ipxe/src/arch/i386/transitions/liba20.S index 684697525..6c1e1f62f 100644 --- a/roms/ipxe/src/arch/i386/transitions/liba20.S +++ b/roms/ipxe/src/arch/i386/transitions/liba20.S @@ -16,9 +16,13 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) .arch i386 diff --git a/roms/ipxe/src/arch/i386/transitions/libkir.S b/roms/ipxe/src/arch/i386/transitions/libkir.S index 1176fcced..fa9459d52 100644 --- a/roms/ipxe/src/arch/i386/transitions/libkir.S +++ b/roms/ipxe/src/arch/i386/transitions/libkir.S @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /**************************************************************************** * This file defines libkir: an interface between external and diff --git a/roms/ipxe/src/arch/i386/transitions/librm.S b/roms/ipxe/src/arch/i386/transitions/librm.S index 2e447b030..863e22415 100644 --- a/roms/ipxe/src/arch/i386/transitions/librm.S +++ b/roms/ipxe/src/arch/i386/transitions/librm.S @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ) +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) /* Drag in local definitions */ #include "librm.h" diff --git a/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c index cc4765de2..becb02677 100644 --- a/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c +++ b/roms/ipxe/src/arch/i386/transitions/librm_mgmt.c @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/profile.h> @@ -103,7 +103,7 @@ void init_idt ( void ) { ( uint32_t ) vec->next ); set_interrupt_vector ( intr, vec ); } - DBGC ( &intr_vec[0], "INTn vector at %p+%xn (phys %#lx+%xn)\n", + DBGC ( &intr_vec[0], "INTn vector at %p+%zxn (phys %#lx+%zxn)\n", intr_vec, sizeof ( intr_vec[0] ), virt_to_phys ( intr_vec ), sizeof ( intr_vec[0] ) ); diff --git a/roms/ipxe/src/arch/i386/transitions/librm_test.c b/roms/ipxe/src/arch/i386/transitions/librm_test.c index e07cfccdd..f1a517eda 100644 --- a/roms/ipxe/src/arch/i386/transitions/librm_test.c +++ b/roms/ipxe/src/arch/i386/transitions/librm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -114,4 +118,5 @@ struct self_test librm_test __self_test = { .exec = librm_test_exec, }; +REQUIRING_SYMBOL ( librm_test ); REQUIRE_OBJECT ( test ); diff --git a/roms/ipxe/src/arch/x86/Makefile b/roms/ipxe/src/arch/x86/Makefile index e555587df..98c49b98d 100644 --- a/roms/ipxe/src/arch/x86/Makefile +++ b/roms/ipxe/src/arch/x86/Makefile @@ -9,9 +9,14 @@ SRCDIRS += arch/x86/interface/efi SRCDIRS += arch/x86/prefix SRCDIRS += arch/x86/hci/commands SRCDIRS += arch/x86/drivers/xen +SRCDIRS += arch/x86/drivers/hyperv # breaks building some of the linux-related objects CFLAGS += -Ulinux # disable valgrind CFLAGS += -DNVALGRIND + +# Include Hyper-V driver in the all-drivers build +# +DRIVERS_hyperv += hyperv diff --git a/roms/ipxe/src/arch/x86/Makefile.efi b/roms/ipxe/src/arch/x86/Makefile.efi index 13a69d9f7..f73bc7d5d 100644 --- a/roms/ipxe/src/arch/x86/Makefile.efi +++ b/roms/ipxe/src/arch/x86/Makefile.efi @@ -17,7 +17,7 @@ NON_AUTO_MEDIA += efirom # Include SNP driver in the all-drivers build # -DRIVERS += snp +DRIVERS_net += snp # Rules for building EFI files # diff --git a/roms/ipxe/src/arch/x86/core/cpuid.c b/roms/ipxe/src/arch/x86/core/cpuid.c index 5908f4419..bc5a6c68c 100644 --- a/roms/ipxe/src/arch/x86/core/cpuid.c +++ b/roms/ipxe/src/arch/x86/core/cpuid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <ipxe/cpuid.h> diff --git a/roms/ipxe/src/arch/x86/core/cpuid_settings.c b/roms/ipxe/src/arch/x86/core/cpuid_settings.c index 42dea9336..08bd3918a 100644 --- a/roms/ipxe/src/arch/x86/core/cpuid_settings.c +++ b/roms/ipxe/src/arch/x86/core/cpuid_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/arch/x86/core/debugcon.c b/roms/ipxe/src/arch/x86/core/debugcon.c index 263cb4af1..60de61f55 100644 --- a/roms/ipxe/src/arch/x86/core/debugcon.c +++ b/roms/ipxe/src/arch/x86/core/debugcon.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/x86/core/pcidirect.c b/roms/ipxe/src/arch/x86/core/pcidirect.c index dbc8317b8..9b8e6b1d9 100644 --- a/roms/ipxe/src/arch/x86/core/pcidirect.c +++ b/roms/ipxe/src/arch/x86/core/pcidirect.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/io.h> #include <ipxe/pci.h> diff --git a/roms/ipxe/src/arch/i386/core/pic8259.c b/roms/ipxe/src/arch/x86/core/pic8259.c index 0a9ea2e03..0a9ea2e03 100644 --- a/roms/ipxe/src/arch/i386/core/pic8259.c +++ b/roms/ipxe/src/arch/x86/core/pic8259.c diff --git a/roms/ipxe/src/arch/x86/core/pit8254.c b/roms/ipxe/src/arch/x86/core/pit8254.c new file mode 100644 index 000000000..da2099263 --- /dev/null +++ b/roms/ipxe/src/arch/x86/core/pit8254.c @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <assert.h> +#include <ipxe/io.h> +#include <ipxe/pit8254.h> + +/** @file + * + * 8254 Programmable Interval Timer + * + */ + +/** + * Delay for a fixed number of timer ticks using the speaker channel + * + * @v ticks Number of timer ticks for which to delay + */ +void pit8254_speaker_delay ( unsigned int ticks ) { + uint8_t spkr; + uint8_t cmd; + uint8_t low; + uint8_t high; + + /* Sanity check */ + assert ( ticks <= 0xffff ); + + /* Disable speaker, set speaker channel gate input high */ + spkr = inb ( PIT8254_SPKR ); + spkr &= ~PIT8254_SPKR_ENABLE; + spkr |= PIT8254_SPKR_GATE; + outb ( spkr, PIT8254_SPKR ); + + /* Program speaker channel to "interrupt" on terminal count */ + cmd = ( PIT8254_CMD_CHANNEL ( PIT8254_CH_SPKR ) | + PIT8254_CMD_ACCESS_LOHI | PIT8254_CMD_OP_TERMINAL | + PIT8254_CMD_BINARY ); + low = ( ( ticks >> 0 ) & 0xff ); + high = ( ( ticks >> 8 ) & 0xff ); + outb ( cmd, PIT8254_CMD ); + outb ( low, PIT8254_DATA ( PIT8254_CH_SPKR ) ); + outb ( high, PIT8254_DATA ( PIT8254_CH_SPKR ) ); + + /* Wait for channel to "interrupt" */ + do { + spkr = inb ( PIT8254_SPKR ); + } while ( ! ( spkr & PIT8254_SPKR_OUT ) ); +} diff --git a/roms/ipxe/src/arch/x86/core/vram_settings.c b/roms/ipxe/src/arch/x86/core/vram_settings.c new file mode 100644 index 000000000..9c169b40c --- /dev/null +++ b/roms/ipxe/src/arch/x86/core/vram_settings.c @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/uaccess.h> +#include <ipxe/settings.h> + +/** @file + * + * Video RAM dump + * + */ + +/** Video RAM base address */ +#define VRAM_BASE 0xb8000 + +/** Video RAM length */ +#define VRAM_LEN \ + ( 80 /* columns */ * 25 /* rows */ * 2 /* bytes per character */ ) + +/** + * Fetch video RAM setting + * + * @v data Buffer to fill with setting data + * @v len Length of buffer + * @ret len Length of setting data, or negative error + */ +static int vram_fetch ( void *data, size_t len ) { + userptr_t vram = phys_to_user ( VRAM_BASE ); + + /* Copy video RAM */ + if ( len > VRAM_LEN ) + len = VRAM_LEN; + copy_from_user ( data, vram, 0, len ); + + return VRAM_LEN; +} + +/** Video RAM setting */ +const struct setting vram_setting __setting ( SETTING_MISC, vram ) = { + .name = "vram", + .description = "Video RAM", + .type = &setting_type_base64, + .scope = &builtin_scope, +}; + +/** Video RAM built-in setting */ +struct builtin_setting vram_builtin_setting __builtin_setting = { + .setting = &vram_setting, + .fetch = vram_fetch, +}; diff --git a/roms/ipxe/src/arch/x86/core/x86_bigint.c b/roms/ipxe/src/arch/x86/core/x86_bigint.c index 418ac2309..6413b2fa8 100644 --- a/roms/ipxe/src/arch/x86/core/x86_bigint.c +++ b/roms/ipxe/src/arch/x86/core/x86_bigint.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/arch/x86/core/x86_io.c b/roms/ipxe/src/arch/x86/core/x86_io.c index 9b2d2d935..3081fa8b9 100644 --- a/roms/ipxe/src/arch/x86/core/x86_io.c +++ b/roms/ipxe/src/arch/x86/core/x86_io.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/io.h> #include <ipxe/x86_io.h> diff --git a/roms/ipxe/src/arch/x86/core/x86_string.c b/roms/ipxe/src/arch/x86/core/x86_string.c index d48347c96..7d5e4a5f1 100644 --- a/roms/ipxe/src/arch/x86/core/x86_string.c +++ b/roms/ipxe/src/arch/x86/core/x86_string.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ /** @file @@ -23,7 +27,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> @@ -104,87 +108,3 @@ void * __memmove ( void *dest, const void *src, size_t len ) { return __memcpy_reverse ( dest, src, len ); } } - -/** - * Swap memory areas - * - * @v dest Destination address - * @v src Source address - * @v len Length - * @ret dest Destination address - */ -void * memswap ( void *dest, void *src, size_t len ) { - size_t discard_c; - int discard; - - __asm__ __volatile__ ( "\n1:\n\t" - "dec %2\n\t" - "js 2f\n\t" - "movb (%0,%2), %b3\n\t" - "xchgb (%1,%2), %b3\n\t" - "movb %b3, (%0,%2)\n\t" - "jmp 1b\n\t" - "2:\n\t" - : "=r" ( src ), "=r" ( dest ), - "=&c" ( discard_c ), "=&q" ( discard ) - : "0" ( src ), "1" ( dest ), "2" ( len ) - : "memory" ); - - return dest; -} - -/** - * Calculate length of string - * - * @v string String - * @ret len Length (excluding NUL) - */ -size_t strlen ( const char *string ) { - const char *discard_D; - size_t len_plus_one; - - __asm__ __volatile__ ( "repne scasb\n\t" - "not %1\n\t" - : "=&D" ( discard_D ), "=&c" ( len_plus_one ) - : "0" ( string ), "1" ( -1UL ), "a" ( 0 ) ); - - return ( len_plus_one - 1 ); -} - -/** - * Compare strings (up to a specified length) - * - * @v str1 First string - * @v str2 Second string - * @v len Maximum length - * @ret diff Difference - */ -int strncmp ( const char *str1, const char *str2, size_t len ) { - const void *discard_S; - const void *discard_D; - size_t discard_c; - int diff; - - __asm__ __volatile__ ( "\n1:\n\t" - "dec %2\n\t" - "js 2f\n\t" - "lodsb\n\t" - "scasb\n\t" - "jne 3f\n\t" - "testb %b3, %b3\n\t" - "jnz 1b\n\t" - /* Equal */ - "\n2:\n\t" - "xor %3, %3\n\t" - "jmp 4f\n\t" - /* Not equal; CF indicates difference */ - "\n3:\n\t" - "sbb %3, %3\n\t" - "orb $1, %b3\n\t" - "\n4:\n\t" - : "=&S" ( discard_S ), "=&D" ( discard_D ), - "=&c" ( discard_c ), "=&a" ( diff ) - : "0" ( str1 ), "1" ( str2 ), "2" ( len ) ); - - return diff; -} diff --git a/roms/ipxe/src/arch/x86/core/x86_tcpip.c b/roms/ipxe/src/arch/x86/core/x86_tcpip.c index 8a4ce5152..88042f5f7 100644 --- a/roms/ipxe/src/arch/x86/core/x86_tcpip.c +++ b/roms/ipxe/src/arch/x86/core/x86_tcpip.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/arch/x86/core/x86_uart.c b/roms/ipxe/src/arch/x86/core/x86_uart.c new file mode 100644 index 000000000..e455775bf --- /dev/null +++ b/roms/ipxe/src/arch/x86/core/x86_uart.c @@ -0,0 +1,69 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * 16550-compatible UART + * + */ + +#include <errno.h> +#include <ipxe/uart.h> + +/** UART port bases */ +static uint16_t uart_base[] = { + [COM1] = 0x3f8, + [COM2] = 0x2f8, + [COM3] = 0x3e8, + [COM4] = 0x2e8, +}; + +/** + * Select UART port + * + * @v uart UART + * @v port Port number, or 0 to disable + * @ret rc Return status code + */ +int uart_select ( struct uart *uart, unsigned int port ) { + int rc; + + /* Set new UART base */ + if ( port >= ( sizeof ( uart_base ) / sizeof ( uart_base[0] ) ) ) { + rc = -ENODEV; + goto err; + } + uart->base = ( ( void * ) ( intptr_t ) uart_base[port] ); + + /* Check that UART exists */ + if ( ( rc = uart_exists ( uart ) ) != 0 ) + goto err; + + return 0; + + err: + uart->base = NULL; + return rc; +} diff --git a/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c b/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c new file mode 100644 index 000000000..f73829bd5 --- /dev/null +++ b/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.c @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V driver + * + */ + +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <unistd.h> +#include <assert.h> +#include <errno.h> +#include <byteswap.h> +#include <pic8259.h> +#include <ipxe/malloc.h> +#include <ipxe/device.h> +#include <ipxe/cpuid.h> +#include <ipxe/msr.h> +#include <ipxe/hyperv.h> +#include <ipxe/vmbus.h> +#include "hyperv.h" + +/** Maximum time to wait for a message response + * + * This is a policy decision. + */ +#define HV_MESSAGE_MAX_WAIT_MS 1000 + +/** + * Convert a Hyper-V status code to an iPXE status code + * + * @v status Hyper-V status code + * @ret rc iPXE status code (before negation) + */ +#define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) ) + +/** + * Allocate zeroed pages + * + * @v hv Hyper-V hypervisor + * @v ... Page addresses to fill in, terminated by NULL + * @ret rc Return status code + */ +__attribute__ (( sentinel )) int +hv_alloc_pages ( struct hv_hypervisor *hv, ... ) { + va_list args; + void **page; + int i; + + /* Allocate and zero pages */ + va_start ( args, hv ); + for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) { + *page = malloc_dma ( PAGE_SIZE, PAGE_SIZE ); + if ( ! *page ) + goto err_alloc; + memset ( *page, 0, PAGE_SIZE ); + } + va_end ( args ); + + return 0; + + err_alloc: + va_end ( args ); + va_start ( args, hv ); + for ( ; i >= 0 ; i-- ) { + page = va_arg ( args, void ** ); + free_dma ( *page, PAGE_SIZE ); + } + va_end ( args ); + return -ENOMEM; +} + +/** + * Free pages + * + * @v hv Hyper-V hypervisor + * @v ... Page addresses, terminated by NULL + */ +__attribute__ (( sentinel )) void +hv_free_pages ( struct hv_hypervisor *hv, ... ) { + va_list args; + void *page; + + va_start ( args, hv ); + while ( ( page = va_arg ( args, void * ) ) != NULL ) + free_dma ( page, PAGE_SIZE ); + va_end ( args ); +} + +/** + * Allocate message buffer + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_alloc_message ( struct hv_hypervisor *hv ) { + + /* Allocate buffer. Must be aligned to at least 8 bytes and + * must not cross a page boundary, so align on its own size. + */ + hv->message = malloc_dma ( sizeof ( *hv->message ), + sizeof ( *hv->message ) ); + if ( ! hv->message ) + return -ENOMEM; + + return 0; +} + +/** + * Free message buffer + * + * @v hv Hyper-V hypervisor + */ +static void hv_free_message ( struct hv_hypervisor *hv ) { + + /* Free buffer */ + free_dma ( hv->message, sizeof ( *hv->message ) ); +} + +/** + * Check whether or not we are running in Hyper-V + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_check_hv ( struct hv_hypervisor *hv ) { + struct x86_features features; + uint32_t interface_id; + uint32_t discard_ebx; + uint32_t discard_ecx; + uint32_t discard_edx; + uint32_t available; + uint32_t permissions; + + /* Check for presence of a hypervisor (not necessarily Hyper-V) */ + x86_features ( &features ); + if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) { + DBGC ( hv, "HV %p not running in a hypervisor\n", hv ); + return -ENODEV; + } + + /* Check that hypervisor is Hyper-V */ + cpuid ( HV_CPUID_INTERFACE_ID, &interface_id, &discard_ebx, + &discard_ecx, &discard_edx ); + if ( interface_id != HV_INTERFACE_ID ) { + DBGC ( hv, "HV %p not running in Hyper-V (interface ID " + "%#08x)\n", hv, interface_id ); + return -ENODEV; + } + + /* Check that required features and privileges are available */ + cpuid ( HV_CPUID_FEATURES, &available, &permissions, &discard_ecx, + &discard_edx ); + if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) { + DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n", + hv, available, permissions ); + return -ENODEV; + } + if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) { + DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n", + hv, available, permissions ); + return -ENODEV; + } + if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) { + DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n", + hv, available, permissions ); + return -EACCES; + } + if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) { + DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)", + hv, available, permissions ); + return -EACCES; + } + + return 0; +} + +/** + * Map hypercall page + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_map_hypercall ( struct hv_hypervisor *hv ) { + union { + struct { + uint32_t ebx; + uint32_t ecx; + uint32_t edx; + } __attribute__ (( packed )); + char text[ 13 /* "bbbbccccdddd" + NUL */ ]; + } vendor_id; + uint32_t build; + uint32_t version; + uint32_t discard_eax; + uint32_t discard_ecx; + uint32_t discard_edx; + uint64_t guest_os_id; + uint64_t hypercall; + + /* Report guest OS identity */ + guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID ); + if ( guest_os_id != 0 ) { + DBGC ( hv, "HV %p guest OS ID MSR already set to %#08llx\n", + hv, guest_os_id ); + return -EBUSY; + } + guest_os_id = HV_GUEST_OS_ID_IPXE; + DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id ); + wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id ); + + /* Get hypervisor system identity (for debugging) */ + cpuid ( HV_CPUID_VENDOR_ID, &discard_eax, &vendor_id.ebx, + &vendor_id.ecx, &vendor_id.edx ); + vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0'; + cpuid ( HV_CPUID_HYPERVISOR_ID, &build, &version, &discard_ecx, + &discard_edx ); + DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv, + vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build ); + + /* Map hypercall page */ + hypercall = rdmsr ( HV_X64_MSR_HYPERCALL ); + hypercall &= ( PAGE_SIZE - 1 ); + hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE ); + DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall ); + wrmsr ( HV_X64_MSR_HYPERCALL, hypercall ); + + return 0; +} + +/** + * Unmap hypercall page + * + * @v hv Hyper-V hypervisor + */ +static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) { + uint64_t hypercall; + uint64_t guest_os_id; + + /* Unmap the hypercall page */ + hypercall = rdmsr ( HV_X64_MSR_HYPERCALL ); + hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE ); + DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall ); + wrmsr ( HV_X64_MSR_HYPERCALL, hypercall ); + + /* Reset the guest OS identity */ + guest_os_id = 0; + DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id ); + wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id ); +} + +/** + * Map synthetic interrupt controller + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int hv_map_synic ( struct hv_hypervisor *hv ) { + uint64_t simp; + uint64_t siefp; + uint64_t scontrol; + + /* Map SynIC message page */ + simp = rdmsr ( HV_X64_MSR_SIMP ); + simp &= ( PAGE_SIZE - 1 ); + simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE ); + DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp ); + wrmsr ( HV_X64_MSR_SIMP, simp ); + + /* Map SynIC event page */ + siefp = rdmsr ( HV_X64_MSR_SIEFP ); + siefp &= ( PAGE_SIZE - 1 ); + siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE ); + DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp ); + wrmsr ( HV_X64_MSR_SIEFP, siefp ); + + /* Enable SynIC */ + scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); + scontrol |= HV_SCONTROL_ENABLE; + DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); + wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); + + return 0; +} + +/** + * Unmap synthetic interrupt controller + * + * @v hv Hyper-V hypervisor + */ +static void hv_unmap_synic ( struct hv_hypervisor *hv ) { + uint64_t scontrol; + uint64_t siefp; + uint64_t simp; + + /* Disable SynIC */ + scontrol = rdmsr ( HV_X64_MSR_SCONTROL ); + scontrol &= ~HV_SCONTROL_ENABLE; + DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol ); + wrmsr ( HV_X64_MSR_SCONTROL, scontrol ); + + /* Unmap SynIC event page */ + siefp = rdmsr ( HV_X64_MSR_SIEFP ); + siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE ); + DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp ); + wrmsr ( HV_X64_MSR_SIEFP, siefp ); + + /* Unmap SynIC message page */ + simp = rdmsr ( HV_X64_MSR_SIMP ); + simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE ); + DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp ); + wrmsr ( HV_X64_MSR_SIMP, simp ); +} + +/** + * Enable synthetic interrupt + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + */ +void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) { + unsigned long msr = HV_X64_MSR_SINT ( sintx ); + uint64_t sint; + + /* Enable synthetic interrupt + * + * We have to enable the interrupt, otherwise messages will + * not be delivered (even though the documentation implies + * that polling for messages is possible). We enable AutoEOI + * and hook the interrupt to the obsolete IRQ13 (FPU + * exception) vector, which will be implemented as a no-op. + */ + sint = rdmsr ( msr ); + sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK ); + sint |= ( HV_SINT_AUTO_EOI | + HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) ); + DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint ); + wrmsr ( msr, sint ); +} + +/** + * Disable synthetic interrupt + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + */ +void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) { + unsigned long msr = HV_X64_MSR_SINT ( sintx ); + uint64_t sint; + + /* Disable synthetic interrupt */ + sint = rdmsr ( msr ); + sint &= ~HV_SINT_AUTO_EOI; + sint |= HV_SINT_MASKED; + DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint ); + wrmsr ( msr, sint ); +} + +/** + * Post message + * + * @v hv Hyper-V hypervisor + * @v id Connection ID + * @v type Message type + * @v data Message + * @v len Length of message + * @ret rc Return status code + */ +int hv_post_message ( struct hv_hypervisor *hv, unsigned int id, + unsigned int type, const void *data, size_t len ) { + struct hv_post_message *msg = &hv->message->posted; + int status; + int rc; + + /* Sanity check */ + assert ( len <= sizeof ( msg->data ) ); + + /* Construct message */ + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = cpu_to_le32 ( id ); + msg->type = cpu_to_le32 ( type ); + msg->len = cpu_to_le32 ( len ); + memcpy ( msg->data, data, len ); + DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n", + hv, id, type ); + DBGC2_HDA ( hv, 0, msg->data, len ); + + /* Post message */ + if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) { + rc = -EHV ( status ); + DBGC ( hv, "HV %p could not post message to %#08x: %s\n", + hv, id, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Wait for received message + * + * @v hv Hyper-V hypervisor + * @v sintx Synthetic interrupt number + * @ret rc Return status code + */ +int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) { + struct hv_message *msg = &hv->message->received; + struct hv_message *src = &hv->synic.message[sintx]; + unsigned int retries; + size_t len; + + /* Wait for message to arrive */ + for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) { + + /* Check for message */ + if ( src->type ) { + + /* Copy message */ + memset ( msg, 0, sizeof ( *msg ) ); + len = src->len; + assert ( len <= sizeof ( *msg ) ); + memcpy ( msg, src, + ( offsetof ( typeof ( *msg ), data ) + len ) ); + DBGC2 ( hv, "HV %p SINT%d received message type " + "%#08x:\n", hv, sintx, + le32_to_cpu ( msg->type ) ); + DBGC2_HDA ( hv, 0, msg->data, len ); + + /* Consume message */ + src->type = 0; + + return 0; + } + + /* Trigger message delivery */ + wrmsr ( HV_X64_MSR_EOM, 0 ); + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( hv, "HV %p SINT%d timed out waiting for message\n", + hv, sintx ); + return -ETIMEDOUT; +} + +/** + * Signal event + * + * @v hv Hyper-V hypervisor + * @v id Connection ID + * @v flag Flag number + * @ret rc Return status code + */ +int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id, + unsigned int flag ) { + struct hv_signal_event *event = &hv->message->signalled; + int status; + int rc; + + /* Construct event */ + memset ( event, 0, sizeof ( *event ) ); + event->id = cpu_to_le32 ( id ); + event->flag = cpu_to_le16 ( flag ); + + /* Signal event */ + if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) { + rc = -EHV ( status ); + DBGC ( hv, "HV %p could not signal event to %#08x: %s\n", + hv, id, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Probe root device + * + * @v rootdev Root device + * @ret rc Return status code + */ +static int hv_probe ( struct root_device *rootdev ) { + struct hv_hypervisor *hv; + int rc; + + /* Allocate and initialise structure */ + hv = zalloc ( sizeof ( *hv ) ); + if ( ! hv ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Check we are running in Hyper-V */ + if ( ( rc = hv_check_hv ( hv ) ) != 0 ) + goto err_check_hv; + + /* Allocate pages */ + if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message, + &hv->synic.event, NULL ) ) != 0 ) + goto err_alloc_pages; + + /* Allocate message buffer */ + if ( ( rc = hv_alloc_message ( hv ) ) != 0 ) + goto err_alloc_message; + + /* Map hypercall page */ + if ( ( rc = hv_map_hypercall ( hv ) ) != 0 ) + goto err_map_hypercall; + + /* Map synthetic interrupt controller */ + if ( ( rc = hv_map_synic ( hv ) ) != 0 ) + goto err_map_synic; + + /* Probe Hyper-V devices */ + if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 ) + goto err_vmbus_probe; + + rootdev_set_drvdata ( rootdev, hv ); + return 0; + + vmbus_remove ( hv, &rootdev->dev ); + err_vmbus_probe: + hv_unmap_synic ( hv ); + err_map_synic: + hv_unmap_hypercall ( hv ); + err_map_hypercall: + hv_free_message ( hv ); + err_alloc_message: + hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, + NULL ); + err_alloc_pages: + err_check_hv: + free ( hv ); + err_alloc: + return rc; +} + +/** + * Remove root device + * + * @v rootdev Root device + */ +static void hv_remove ( struct root_device *rootdev ) { + struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev ); + + vmbus_remove ( hv, &rootdev->dev ); + hv_unmap_synic ( hv ); + hv_unmap_hypercall ( hv ); + hv_free_message ( hv ); + hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event, + NULL ); + free ( hv ); +} + +/** Hyper-V root device driver */ +static struct root_driver hv_root_driver = { + .probe = hv_probe, + .remove = hv_remove, +}; + +/** Hyper-V root device */ +struct root_device hv_root_device __root_device = { + .dev = { .name = "Hyper-V" }, + .driver = &hv_root_driver, +}; + +/* Drag in objects via hv_root_device */ +REQUIRING_SYMBOL ( hv_root_device ); + +/* Drag in netvsc driver */ +REQUIRE_OBJECT ( netvsc ); diff --git a/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h b/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h new file mode 100644 index 000000000..0d09beb44 --- /dev/null +++ b/roms/ipxe/src/arch/x86/drivers/hyperv/hyperv.h @@ -0,0 +1,57 @@ +#ifndef _HYPERV_H +#define _HYPERV_H + +/** @file + * + * Hyper-V driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Get vendor identification */ +#define HV_CPUID_VENDOR_ID 0x40000000UL + +/** Get interface identification */ +#define HV_CPUID_INTERFACE_ID 0x40000001UL + +/** Get hypervisor identification */ +#define HV_CPUID_HYPERVISOR_ID 0x40000002UL + +/** Get hypervisor features */ +#define HV_CPUID_FEATURES 0x40000003UL + +/** SynIC MSRs are available */ +#define HV_FEATURES_AVAIL_SYNIC_MSR 0x00000004UL + +/** Hypercall MSRs are available */ +#define HV_FEATURES_AVAIL_HYPERCALL_MSR 0x00000020UL + +/** Guest may post messages */ +#define HV_FEATURES_PERM_POST_MESSAGES 0x00000010UL + +/** Guest may signal events */ +#define HV_FEATURES_PERM_SIGNAL_EVENTS 0x00000020UL + +/** Guest OS identity MSR */ +#define HV_X64_MSR_GUEST_OS_ID 0x40000000UL + +/** Hypercall page MSR */ +#define HV_X64_MSR_HYPERCALL 0x40000001UL + +/** SynIC control MSR */ +#define HV_X64_MSR_SCONTROL 0x40000080UL + +/** SynIC event flags page MSR */ +#define HV_X64_MSR_SIEFP 0x40000082UL + +/** SynIC message page MSR */ +#define HV_X64_MSR_SIMP 0x40000083UL + +/** SynIC end of message MSR */ +#define HV_X64_MSR_EOM 0x40000084UL + +/** SynIC interrupt source MSRs */ +#define HV_X64_MSR_SINT(x) ( 0x40000090UL + (x) ) + +#endif /* _HYPERV_H */ diff --git a/roms/ipxe/src/arch/x86/drivers/xen/hvm.c b/roms/ipxe/src/arch/x86/drivers/xen/hvm.c index 7406ca68d..7ac32d54c 100644 --- a/roms/ipxe/src/arch/x86/drivers/xen/hvm.c +++ b/roms/ipxe/src/arch/x86/drivers/xen/hvm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> @@ -492,5 +496,8 @@ struct pci_driver hvm_driver __pci_driver = { .remove = hvm_remove, }; +/* Drag in objects via hvm_driver */ +REQUIRING_SYMBOL ( hvm_driver ); + /* Drag in netfront driver */ REQUIRE_OBJECT ( netfront ); diff --git a/roms/ipxe/src/arch/x86/drivers/xen/hvm.h b/roms/ipxe/src/arch/x86/drivers/xen/hvm.h index 325d20d66..72ed94f6d 100644 --- a/roms/ipxe/src/arch/x86/drivers/xen/hvm.h +++ b/roms/ipxe/src/arch/x86/drivers/xen/hvm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/xen.h> diff --git a/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c b/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c index c4e35d179..d73ce2a3e 100644 --- a/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c +++ b/roms/ipxe/src/arch/x86/hci/commands/cpuid_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/arch/x86/include/bits/bigint.h b/roms/ipxe/src/arch/x86/include/bits/bigint.h index d3449af5a..c9bb6ea45 100644 --- a/roms/ipxe/src/arch/x86/include/bits/bigint.h +++ b/roms/ipxe/src/arch/x86/include/bits/bigint.h @@ -6,7 +6,7 @@ * Big integer support */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/arch/x86/include/bits/endian.h b/roms/ipxe/src/arch/x86/include/bits/endian.h new file mode 100644 index 000000000..85718cfdd --- /dev/null +++ b/roms/ipxe/src/arch/x86/include/bits/endian.h @@ -0,0 +1,8 @@ +#ifndef _BITS_ENDIAN_H +#define _BITS_ENDIAN_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#define __BYTE_ORDER __LITTLE_ENDIAN + +#endif /* _BITS_ENDIAN_H */ diff --git a/roms/ipxe/src/arch/x86/include/bits/errfile.h b/roms/ipxe/src/arch/x86/include/bits/errfile.h index 624575621..0d1617d20 100644 --- a/roms/ipxe/src/arch/x86/include/bits/errfile.h +++ b/roms/ipxe/src/arch/x86/include/bits/errfile.h @@ -1,7 +1,7 @@ #ifndef _BITS_ERRFILE_H #define _BITS_ERRFILE_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @addtogroup errfile Error file identifiers @@ -21,6 +21,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_guestinfo ( ERRFILE_ARCH | ERRFILE_CORE | 0x000a0000 ) #define ERRFILE_apm ( ERRFILE_ARCH | ERRFILE_CORE | 0x000b0000 ) #define ERRFILE_vesafb ( ERRFILE_ARCH | ERRFILE_CORE | 0x000c0000 ) +#define ERRFILE_int13con ( ERRFILE_ARCH | ERRFILE_CORE | 0x000d0000 ) #define ERRFILE_bootsector ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_bzimage ( ERRFILE_ARCH | ERRFILE_IMAGE | 0x00010000 ) @@ -46,9 +47,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_timer_rdtsc ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_timer_bios ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00010000 ) #define ERRFILE_hvm ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00020000 ) +#define ERRFILE_hyperv ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00030000 ) +#define ERRFILE_x86_uart ( ERRFILE_ARCH | ERRFILE_DRIVER | 0x00040000 ) #define ERRFILE_cpuid_cmd ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00000000 ) #define ERRFILE_cpuid_settings ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00010000 ) +#define ERRFILE_efi_entropy ( ERRFILE_ARCH | ERRFILE_OTHER | 0x00020000 ) /** @} */ diff --git a/roms/ipxe/src/arch/x86/include/bits/io.h b/roms/ipxe/src/arch/x86/include/bits/io.h index cb1b67a6f..60c2e3edf 100644 --- a/roms/ipxe/src/arch/x86/include/bits/io.h +++ b/roms/ipxe/src/arch/x86/include/bits/io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/x86_io.h> diff --git a/roms/ipxe/src/arch/x86/include/bits/pci_io.h b/roms/ipxe/src/arch/x86/include/bits/pci_io.h index 01b12326e..b41e562ee 100644 --- a/roms/ipxe/src/arch/x86/include/bits/pci_io.h +++ b/roms/ipxe/src/arch/x86/include/bits/pci_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/pcibios.h> #include <ipxe/pcidirect.h> diff --git a/roms/ipxe/src/arch/x86/include/bits/string.h b/roms/ipxe/src/arch/x86/include/bits/string.h index dce994983..c26fe30d5 100644 --- a/roms/ipxe/src/arch/x86/include/bits/string.h +++ b/roms/ipxe/src/arch/x86/include/bits/string.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -28,8 +32,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#define __HAVE_ARCH_MEMCPY - extern void * __memcpy ( void *dest, const void *src, size_t len ); extern void * __memcpy_reverse ( void *dest, const void *src, size_t len ); @@ -169,8 +171,6 @@ memcpy ( void *dest, const void *src, size_t len ) { } } -#define __HAVE_ARCH_MEMMOVE - extern void * __memmove ( void *dest, const void *src, size_t len ); /** @@ -196,8 +196,6 @@ memmove ( void *dest, const void *src, size_t len ) { } } -#define __HAVE_ARCH_MEMSET - /** * Fill memory region * @@ -206,7 +204,8 @@ memmove ( void *dest, const void *src, size_t len ) { * @v len Length * @ret dest Destination address */ -static inline void * memset ( void *dest, int fill, size_t len ) { +static inline __attribute__ (( always_inline )) void * +__memset ( void *dest, int fill, size_t len ) { void *discard_D; size_t discard_c; @@ -217,16 +216,129 @@ static inline void * memset ( void *dest, int fill, size_t len ) { return dest; } -#define __HAVE_ARCH_MEMSWAP +/** + * Fill memory region with zero (where length is a compile-time constant) + * + * @v dest Destination address + * @v len Length + * @ret dest Destination address + */ +static inline __attribute__ (( always_inline )) void * +__constant_memset_zero ( void *dest, size_t len ) { + union { + uint32_t u32[2]; + uint16_t u16[4]; + uint8_t u8[8]; + } __attribute__ (( __may_alias__ )) *dest_u = dest; + void *edi; + uint32_t eax; + + switch ( len ) { + case 0 : /* 0 bytes */ + return dest; + + /* Single-register moves. Almost certainly better than a + * string operation. We can avoid clobbering any registers, + * we can reuse a zero that happens to already be in a + * register, and we can optimise away the code entirely if the + * memset() is used to clear a region which then gets + * immediately overwritten. + */ + case 1 : /* 3 bytes */ + dest_u->u8[0] = 0; + return dest; + case 2: /* 5 bytes */ + dest_u->u16[0] = 0; + return dest; + case 4: /* 6 bytes */ + dest_u->u32[0] = 0; + return dest; + + /* Double-register moves. Very probably better than a string + * operation. + */ + case 3 : /* 9 bytes */ + dest_u->u16[0] = 0; + dest_u->u8[2] = 0; + return dest; + case 5 : /* 10 bytes */ + dest_u->u32[0] = 0; + dest_u->u8[4] = 0; + return dest; + case 6 : /* 12 bytes */ + dest_u->u32[0] = 0; + dest_u->u16[2] = 0; + return dest; + case 8 : /* 13 bytes */ + dest_u->u32[0] = 0; + dest_u->u32[1] = 0; + return dest; + } + + /* As with memcpy(), we can potentially save space by using + * multiple single-byte "stos" instructions instead of loading + * up ecx and using "rep stosb". + * + * "load ecx, rep movsb" is 7 bytes, plus an average of 1 byte + * to allow for saving/restoring ecx 50% of the time. + * + * "stosl" and "stosb" are 1 byte each, "stosw" is two bytes. + * + * The calculations are therefore the same as for memcpy(), + * giving a cutoff point of around 26 bytes. + */ -extern void * memswap ( void *dest, void *src, size_t len ); + edi = dest; + eax = 0; + + if ( len >= 26 ) + return __memset ( dest, 0, len ); -#define __HAVE_ARCH_STRNCMP + if ( len >= 6*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 5*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 4*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 3*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 2*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( len >= 1*4 ) + __asm__ __volatile__ ( "stosl" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( ( len % 4 ) >= 2 ) + __asm__ __volatile__ ( "stosw" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); + if ( ( len % 2 ) >= 1 ) + __asm__ __volatile__ ( "stosb" : "=&D" ( edi ), "=&a" ( eax ) + : "0" ( edi ), "1" ( eax ) : "memory" ); -extern int strncmp ( const char *str1, const char *str2, size_t len ); + return dest; +} -#define __HAVE_ARCH_STRLEN +/** + * Fill memory region + * + * @v dest Destination address + * @v fill Fill pattern + * @v len Length + * @ret dest Destination address + */ +static inline __attribute__ (( always_inline )) void * +memset ( void *dest, int fill, size_t len ) { -extern size_t strlen ( const char *string ); + if ( __builtin_constant_p ( fill ) && ( fill == 0 ) && + __builtin_constant_p ( len ) ) { + return __constant_memset_zero ( dest, len ); + } else { + return __memset ( dest, fill, len ); + } +} #endif /* X86_BITS_STRING_H */ diff --git a/roms/ipxe/src/arch/x86/include/bits/tcpip.h b/roms/ipxe/src/arch/x86/include/bits/tcpip.h index a4b335eb1..5c2baffcf 100644 --- a/roms/ipxe/src/arch/x86/include/bits/tcpip.h +++ b/roms/ipxe/src/arch/x86/include/bits/tcpip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern uint16_t x86_tcpip_continue_chksum ( uint16_t partial, const void *data, size_t len ); diff --git a/roms/ipxe/src/arch/x86/include/bits/uart.h b/roms/ipxe/src/arch/x86/include/bits/uart.h new file mode 100644 index 000000000..e09cd3f4c --- /dev/null +++ b/roms/ipxe/src/arch/x86/include/bits/uart.h @@ -0,0 +1,41 @@ +#ifndef _BITS_UART_H +#define _BITS_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/io.h> + +/** + * Write to UART register + * + * @v uart UART + * @v addr Register address + * @v data Data + */ +static inline __attribute__ (( always_inline )) void +uart_write ( struct uart *uart, unsigned int addr, uint8_t data ) { + outb ( data, ( uart->base + addr ) ); +} + +/** + * Read from UART register + * + * @v uart UART + * @v addr Register address + * @ret data Data + */ +static inline __attribute__ (( always_inline )) uint8_t +uart_read ( struct uart *uart, unsigned int addr ) { + return inb ( uart->base + addr ); +} + +extern int uart_select ( struct uart *uart, unsigned int port ); + +#endif /* _BITS_UART_H */ diff --git a/roms/ipxe/src/arch/x86/include/bits/xen.h b/roms/ipxe/src/arch/x86/include/bits/xen.h index dbccf1b77..fc065ea38 100644 --- a/roms/ipxe/src/arch/x86/include/bits/xen.h +++ b/roms/ipxe/src/arch/x86/include/bits/xen.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Hypercall registers */ #ifdef __x86_64__ @@ -161,4 +161,23 @@ xen_hypercall_5 ( struct xen_hypervisor *xen, unsigned int hypercall, return retval; } +/** + * Test and clear pending event + * + * @v xen Xen hypervisor + * @v port Event channel port + * @ret pending Event was pending + */ +static inline __attribute__ (( always_inline )) uint8_t +xenevent_pending ( struct xen_hypervisor *xen, evtchn_port_t port ) { + uint8_t pending; + + __asm__ __volatile__ ( "lock btr %2, %0\n\t" + "setc %1\n\t" + : "+m" ( xen->shared->evtchn_pending ), + "=a" ( pending ) + : "Ir" ( port ) ); + return pending; +} + #endif /* _BITS_XEN_H */ diff --git a/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h b/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h index 2f78dfca1..da85d0b88 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/cpuid.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> @@ -39,6 +39,9 @@ struct x86_features { /** Get standard features */ #define CPUID_FEATURES 0x00000001UL +/** Hypervisor is present */ +#define CPUID_FEATURES_INTEL_ECX_HYPERVISOR 0x80000000UL + /** Get largest extended function */ #define CPUID_AMD_MAX_FN 0x80000000UL diff --git a/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h b/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h index e85a272b3..1a391c9b6 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/efi/efix86_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_EFIX86 #define NAP_PREFIX_efix86 diff --git a/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h b/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h index 36af7fcde..7e1bcd814 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/pcibios.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_PCBIOS #define PCIAPI_PREFIX_pcbios diff --git a/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h b/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h index 7fa7c4fa7..d924f2f20 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/pcidirect.h @@ -1,7 +1,7 @@ #ifndef _PCIDIRECT_H #define _PCIDIRECT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/io.h> diff --git a/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h b/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h new file mode 100644 index 000000000..00b0ab164 --- /dev/null +++ b/roms/ipxe/src/arch/x86/include/ipxe/pit8254.h @@ -0,0 +1,81 @@ +#ifndef _IPXE_PIT8254_H +#define _IPXE_PIT8254_H + +/** @file + * + * 8254 Programmable Interval Timer + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** IRQ0 channel */ +#define PIT8254_CH_IRQ0 0 + +/** PC speaker channel */ +#define PIT8254_CH_SPKR 2 + +/** Timer frequency (1.193182MHz) */ +#define PIT8254_HZ 1193182UL + +/** Data port */ +#define PIT8254_DATA(channel) ( 0x40 + (channel) ) + +/** Mode/command register */ +#define PIT8254_CMD 0x43 + +/** Select channel */ +#define PIT8254_CMD_CHANNEL(channel) ( (channel) << 6 ) + +/** Access modes */ +#define PIT8254_CMD_ACCESS_LATCH 0x00 /**< Latch count value command */ +#define PIT8254_CMD_ACCESS_LO 0x10 /**< Low byte only */ +#define PIT8254_CMD_ACCESS_HI 0x20 /**< High byte only */ +#define PIT8254_CMD_ACCESS_LOHI 0x30 /**< Low-byte, high-byte pair */ + +/* Operating modes */ +#define PIT8254_CMD_OP_TERMINAL 0x00 /**< Interrupt on terminal count */ +#define PIT8254_CMD_OP_ONESHOT 0x02 /**< Hardware re-triggerable one-shot */ +#define PIT8254_CMD_OP_RATE 0x04 /**< Rate generator */ +#define PIT8254_CMD_OP_SQUARE 0x06 /**< Square wave generator */ +#define PIT8254_CMD_OP_SWSTROBE 0x08 /**< Software triggered strobe */ +#define PIT8254_CMD_OP_HWSTROBE 0x0a /**< Hardware triggered strobe */ +#define PIT8254_CMD_OP_RATE2 0x0c /**< Rate generator (duplicate) */ +#define PIT8254_CMD_OP_SQUARE2 0x0e /**< Square wave generator (duplicate)*/ + +/** Binary mode */ +#define PIT8254_CMD_BINARY 0x00 + +/** BCD mode */ +#define PIT8254_CMD_BCD 0x01 + +/** PC speaker control register */ +#define PIT8254_SPKR 0x61 + +/** PC speaker channel gate */ +#define PIT8254_SPKR_GATE 0x01 + +/** PC speaker enabled */ +#define PIT8254_SPKR_ENABLE 0x02 + +/** PC speaker channel output */ +#define PIT8254_SPKR_OUT 0x20 + +extern void pit8254_speaker_delay ( unsigned int ticks ); + +/** + * Delay for a fixed number of microseconds + * + * @v usecs Number of microseconds for which to delay + */ +static inline __attribute__ (( always_inline )) void +pit8254_udelay ( unsigned long usecs ) { + + /* Delays are invariably compile-time constants; force the + * multiplication and division to take place at compilation + * time rather than runtime. + */ + pit8254_speaker_delay ( ( usecs * PIT8254_HZ ) / 1000000 ); +} + +#endif /* _IPXE_PIT8254_H */ diff --git a/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h b/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h index 9e68f4e78..5214e9fbb 100644 --- a/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h +++ b/roms/ipxe/src/arch/x86/include/ipxe/x86_io.h @@ -15,7 +15,7 @@ * physically fit into a machine with such an old CPU anyway. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef IOAPI_X86 #define IOAPI_PREFIX_x86 diff --git a/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h b/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h index e83fd9d87..d60905f22 100644 --- a/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h +++ b/roms/ipxe/src/arch/x86/include/linux/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _LINUX_DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE(GPL2_OR_LATER_OR_UBDL); #include <ipxe/dhcp.h> diff --git a/roms/ipxe/src/arch/i386/include/pic8259.h b/roms/ipxe/src/arch/x86/include/pic8259.h index a07e97d30..f02e62909 100644 --- a/roms/ipxe/src/arch/i386/include/pic8259.h +++ b/roms/ipxe/src/arch/x86/include/pic8259.h @@ -4,16 +4,13 @@ * Initially written by Michael Brown (mcb30). */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef PIC8259_H #define PIC8259_H #include <ipxe/io.h> -/* For segoff_t */ -#include "realmode.h" - #define IRQ_PIC_CUTOFF 8 /* 8259 register locations */ diff --git a/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c b/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c new file mode 100644 index 000000000..a54bd12e6 --- /dev/null +++ b/roms/ipxe/src/arch/x86/interface/efi/efi_entropy.c @@ -0,0 +1,223 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <errno.h> +#include <ipxe/entropy.h> +#include <ipxe/crc32.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/Protocol/Rng.h> + +/** @file + * + * EFI entropy source + * + */ + +/** Random number generator protocol */ +static EFI_RNG_PROTOCOL *efirng; +EFI_REQUEST_PROTOCOL ( EFI_RNG_PROTOCOL, &efirng ); + +/** Minimum number of bytes to request from RNG + * + * The UEFI spec states (for no apparently good reason) that "When a + * Deterministic Random Bit Generator (DRBG) is used on the output of + * a (raw) entropy source, its security level must be at least 256 + * bits." The EDK2 codebase (mis)interprets this to mean that the + * call to GetRNG() should fail if given a buffer less than 32 bytes. + * + * Incidentally, nothing in the EFI RNG protocol provides any way to + * report the actual amount of entropy returned by GetRNG(). + */ +#define EFI_ENTROPY_RNG_LEN 32 + +/** Time (in 100ns units) to delay waiting for timer tick + * + * In theory, UEFI allows us to specify a trigger time of zero to + * simply wait for the next timer tick. In practice, specifying zero + * seems to often return immediately, which produces almost no + * entropy. Specify a delay of 1000ns to try to force an existent + * delay. + */ +#define EFI_ENTROPY_TRIGGER_TIME 10 + +/** Event used to wait for timer tick */ +static EFI_EVENT tick; + +/** + * Enable entropy gathering + * + * @ret rc Return status code + */ +static int efi_entropy_enable ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + EFI_STATUS efirc; + int rc; + + DBGC ( &tick, "ENTROPY %s RNG protocol\n", + ( efirng ? "has" : "has no" ) ); + + /* Create timer tick event */ + if ( ( efirc = bs->CreateEvent ( EVT_TIMER, TPL_NOTIFY, NULL, NULL, + &tick ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not create event: %s\n", + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Disable entropy gathering + * + */ +static void efi_entropy_disable ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + + /* Close timer tick event */ + bs->CloseEvent ( tick ); +} + +/** + * Wait for a timer tick + * + * @ret low TSC low-order bits, or negative error + */ +static int efi_entropy_tick ( void ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + UINTN index; + uint16_t low; + uint32_t discard_d; + EFI_STATUS efirc; + int rc; + + /* Wait for next timer tick */ + if ( ( efirc = bs->SetTimer ( tick, TimerRelative, + EFI_ENTROPY_TRIGGER_TIME ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not set timer: %s\n", + strerror ( rc ) ); + return rc; + } + if ( ( efirc = bs->WaitForEvent ( 1, &tick, &index ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not wait for timer tick: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Get current TSC low-order bits */ + __asm__ __volatile__ ( "rdtsc" : "=a" ( low ), "=d" ( discard_d ) ); + + return low; +} + +/** + * Get noise sample from timer ticks + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise_ticks ( noise_sample_t *noise ) { + int before; + int after; + int rc; + + /* Wait for a timer tick */ + before = efi_entropy_tick(); + if ( before < 0 ) { + rc = before; + return rc; + } + + /* Wait for another timer tick */ + after = efi_entropy_tick(); + if ( after < 0 ) { + rc = after; + return rc; + } + + /* Use TSC delta as noise sample */ + *noise = ( after - before ); + + return 0; +} + +/** + * Get noise sample from RNG protocol + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise_rng ( noise_sample_t *noise ) { + uint8_t buf[EFI_ENTROPY_RNG_LEN]; + EFI_STATUS efirc; + int rc; + + /* Fail if we have no EFI RNG protocol */ + if ( ! efirng ) + return -ENOTSUP; + + /* Get the minimum allowed number of random bytes */ + if ( ( efirc = efirng->GetRNG ( efirng, NULL, EFI_ENTROPY_RNG_LEN, + buf ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( &tick, "ENTROPY could not read from RNG: %s\n", + strerror ( rc ) ); + return rc; + } + + /* Reduce random bytes to a single noise sample. This seems + * like overkill, but we have no way of knowing how much + * entropy is actually present in the bytes returned by the + * RNG protocol. + */ + *noise = crc32_le ( 0, buf, sizeof ( buf ) ); + + return 0; +} + +/** + * Get noise sample + * + * @ret noise Noise sample + * @ret rc Return status code + */ +static int efi_get_noise ( noise_sample_t *noise ) { + int rc; + + /* Try RNG first, falling back to timer ticks */ + if ( ( ( rc = efi_get_noise_rng ( noise ) ) != 0 ) && + ( ( rc = efi_get_noise_ticks ( noise ) ) != 0 ) ) + return rc; + + return 0; +} + +PROVIDE_ENTROPY_INLINE ( efi, min_entropy_per_sample ); +PROVIDE_ENTROPY ( efi, entropy_enable, efi_entropy_enable ); +PROVIDE_ENTROPY ( efi, entropy_disable, efi_entropy_disable ); +PROVIDE_ENTROPY ( efi, get_noise, efi_get_noise ); diff --git a/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c b/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c index b05421fab..3ebf0bd68 100644 --- a/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c +++ b/roms/ipxe/src/arch/x86/interface/efi/efix86_nap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/nap.h> #include <ipxe/efi/efi.h> diff --git a/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c b/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c index 3daefd00a..4fbb19ff7 100644 --- a/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c +++ b/roms/ipxe/src/arch/x86/prefix/efidrvprefix.c @@ -21,7 +21,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdlib.h> #include <ipxe/init.h> +#include <ipxe/device.h> #include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_driver.h> /** * EFI entry point @@ -44,3 +46,36 @@ EFI_STATUS EFIAPI _efidrv_start ( EFI_HANDLE image_handle, return 0; } + +/** + * Probe EFI root bus + * + * @v rootdev EFI root device + */ +static int efi_probe ( struct root_device *rootdev __unused ) { + + /* Do nothing */ + return 0; +} + +/** + * Remove EFI root bus + * + * @v rootdev EFI root device + */ +static void efi_remove ( struct root_device *rootdev __unused ) { + + efi_driver_disconnect_all(); +} + +/** EFI root device driver */ +static struct root_driver efi_root_driver = { + .probe = efi_probe, + .remove = efi_remove, +}; + +/** EFI root device */ +struct root_device efi_root_device __root_device = { + .dev = { .name = "EFI" }, + .driver = &efi_root_driver, +}; diff --git a/roms/ipxe/src/arch/x86/prefix/efiprefix.c b/roms/ipxe/src/arch/x86/prefix/efiprefix.c index b0bf99c65..18b931e68 100644 --- a/roms/ipxe/src/arch/x86/prefix/efiprefix.c +++ b/roms/ipxe/src/arch/x86/prefix/efiprefix.c @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_autoboot.h> +#include <ipxe/efi/efi_watchdog.h> /** * EFI entry point @@ -49,6 +50,9 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, /* Claim SNP devices for use by iPXE */ efi_snp_claim(); + /* Start watchdog holdoff timer */ + efi_watchdog_start(); + /* Call to main() */ if ( ( rc = main() ) != 0 ) { efirc = EFIRC ( rc ); @@ -56,6 +60,7 @@ EFI_STATUS EFIAPI _efi_start ( EFI_HANDLE image_handle, } err_main: + efi_watchdog_stop(); efi_snp_release(); efi_loaded_image->Unload ( image_handle ); efi_driver_reconnect_all(); diff --git a/roms/ipxe/src/arch/x86_64/Makefile b/roms/ipxe/src/arch/x86_64/Makefile index b687f3407..48c0aa1af 100644 --- a/roms/ipxe/src/arch/x86_64/Makefile +++ b/roms/ipxe/src/arch/x86_64/Makefile @@ -40,6 +40,7 @@ endif # x86_64-specific directories containing source files # +SRCDIRS += arch/x86_64/core SRCDIRS += arch/x86_64/prefix # Include common x86 Makefile diff --git a/roms/ipxe/src/arch/x86_64/core/setjmp.S b/roms/ipxe/src/arch/x86_64/core/setjmp.S new file mode 100644 index 000000000..e43200d7b --- /dev/null +++ b/roms/ipxe/src/arch/x86_64/core/setjmp.S @@ -0,0 +1,65 @@ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ) + + .text + .code64 + + /* Must match jmp_buf structure layout */ + .struct 0 +env_retaddr: .quad 0 +env_stack: .quad 0 +env_rbx: .quad 0 +env_rbp: .quad 0 +env_r12: .quad 0 +env_r13: .quad 0 +env_r14: .quad 0 +env_r15: .quad 0 + .previous + +/* + * Save stack context for non-local goto + */ + .globl setjmp +setjmp: + /* Save return address */ + movq 0(%rsp), %rax + movq %rax, env_retaddr(%rdi) + /* Save stack pointer */ + movq %rsp, env_stack(%rdi) + /* Save other registers */ + movq %rbx, env_rbx(%rdi) + movq %rbp, env_rbp(%rdi) + movq %r12, env_r12(%rdi) + movq %r13, env_r13(%rdi) + movq %r14, env_r14(%rdi) + movq %r15, env_r15(%rdi) + /* Return 0 when returning as setjmp() */ + xorq %rax, %rax + ret + .size setjmp, . - setjmp + +/* + * Non-local jump to a saved stack context + */ + .globl longjmp +longjmp: + /* Get result in %rax */ + movq %rsi, %rax + /* Force result to non-zero */ + testq %rax, %rax + jnz 1f + incq %rax +1: /* Restore stack pointer */ + movq env_stack(%rdi), %rsp + /* Restore other registers */ + movq env_rbx(%rdi), %rbx + movq env_rbp(%rdi), %rbp + movq env_r12(%rdi), %r12 + movq env_r13(%rdi), %r13 + movq env_r14(%rdi), %r14 + movq env_r15(%rdi), %r15 + /* Replace return address on the new stack */ + popq %rcx /* discard */ + pushq env_retaddr(%rdi) + /* Return to setjmp() caller */ + ret + .size longjmp, . - longjmp diff --git a/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h b/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h index 2e472d98a..d8c5098ef 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/byteswap.h @@ -9,7 +9,7 @@ #include <stdint.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static inline __attribute__ (( always_inline, const )) uint16_t __bswap_variable_16 ( uint16_t x ) { diff --git a/roms/ipxe/src/arch/x86_64/include/bits/compiler.h b/roms/ipxe/src/arch/x86_64/include/bits/compiler.h index 51a7eaae2..f70b2e517 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/compiler.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/compiler.h @@ -1,6 +1,9 @@ #ifndef _BITS_COMPILER_H #define _BITS_COMPILER_H +/** Dummy relocation type */ +#define RELOC_TYPE_NONE R_X86_64_NONE + #ifndef ASSEMBLY /** Declare a function with standard calling conventions */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/endian.h b/roms/ipxe/src/arch/x86_64/include/bits/endian.h deleted file mode 100644 index 413e702db..000000000 --- a/roms/ipxe/src/arch/x86_64/include/bits/endian.h +++ /dev/null @@ -1,6 +0,0 @@ -#ifndef ETHERBOOT_BITS_ENDIAN_H -#define ETHERBOOT_BITS_ENDIAN_H - -#define __BYTE_ORDER __LITTLE_ENDIAN - -#endif /* ETHERBOOT_BITS_ENDIAN_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/entropy.h b/roms/ipxe/src/arch/x86_64/include/bits/entropy.h index 9c64c833b..a9b3bc10e 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/entropy.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/entropy.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_ENTROPY_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h b/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h new file mode 100644 index 000000000..845c182f7 --- /dev/null +++ b/roms/ipxe/src/arch/x86_64/include/bits/hyperv.h @@ -0,0 +1,75 @@ +#ifndef _BITS_HYPERV_H +#define _BITS_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> +#include <stdint.h> +#include <ipxe/io.h> + +/** + * Issue hypercall + * + * @v hv Hyper-V hypervisor + * @v code Call code + * @v in Input parameters + * @v out Output parameters + * @ret status Status code + */ +static inline __attribute__ (( always_inline )) int +hv_call ( struct hv_hypervisor *hv, unsigned int code, const void *in, + void *out ) { + void *hypercall = hv->hypercall; + register uint64_t rcx asm ( "rcx" ); + register uint64_t rdx asm ( "rdx" ); + register uint64_t r8 asm ( "r8" ); + uint64_t in_phys; + uint64_t out_phys; + uint16_t result; + + in_phys = ( ( __builtin_constant_p ( in ) && ( in == NULL ) ) + ? 0 : virt_to_phys ( in ) ); + out_phys = ( ( __builtin_constant_p ( out ) && ( out == NULL ) ) + ? 0 : virt_to_phys ( out ) ); + rcx = code; + rdx = in_phys; + r8 = out_phys; + __asm__ __volatile__ ( "call *%4" + : "=a" ( result ), "+r" ( rcx ), "+r" ( rdx ), + "+r" ( r8 ) + : "m" ( hypercall ) + : "r9", "r10", "r11", "xmm0", "xmm1", "xmm2", + "xmm3", "xmm4", "xmm5" ); + return result; +} + +/** + * Set bit atomically + * + * @v bits Bit field + * @v bit Bit to set + */ +static inline __attribute__ (( always_inline )) void +hv_set_bit ( void *bits, unsigned int bit ) { + struct { + uint64_t qword[ ( bit / 64 ) + 1 ]; + } *qwords = bits; + + /* Set bit using "lock bts". Inform compiler that any memory + * from the start of the bit field up to and including the + * qword containing this bit may be modified. (This is + * overkill but shouldn't matter in practice since we're + * unlikely to subsequently read other bits from the same bit + * field.) + */ + __asm__ __volatile__ ( "lock bts %1, %0" + : "+m" ( *qwords ) : "Ir" ( bit ) ); +} + +#endif /* _BITS_HYPERV_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/profile.h b/roms/ipxe/src/arch/x86_64/include/bits/profile.h index 6fc16d84b..b7c74fbe7 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/profile.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/arch/x86_64/include/bits/reboot.h b/roms/ipxe/src/arch/x86_64/include/bits/reboot.h index f1bce0540..f9bcd6a7b 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/reboot.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/reboot.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_REBOOT_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h b/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h index d33d03cbe..dcab830f6 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/sanboot.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_SANBOOT_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/bits/strings.h b/roms/ipxe/src/arch/x86_64/include/bits/strings.h index 6ee99a500..3b7911f3b 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/strings.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/strings.h @@ -1,7 +1,43 @@ #ifndef _BITS_STRINGS_H #define _BITS_STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsll ( long long value ){ + long long lsb_minus_one; + + /* If the input value is zero, the BSF instruction returns + * ZF=0 and leaves an undefined value in the output register. + * Perform this check in C rather than asm so that it can be + * omitted in cases where the compiler is able to prove that + * the input is non-zero. + */ + if ( value ) { + __asm__ ( "bsfq %1, %0" + : "=r" ( lsb_minus_one ) + : "rm" ( value ) ); + return ( lsb_minus_one + 1 ); + } else { + return 0; + } +} + +/** + * Find first (i.e. least significant) set bit + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int __ffsl ( long value ) { + + return __ffsll ( value ); +} /** * Find last (i.e. most significant) set bit @@ -13,7 +49,7 @@ static inline __attribute__ (( always_inline )) int __flsll ( long long value ){ long long msb_minus_one; /* If the input value is zero, the BSR instruction returns - * ZF=1 and leaves an undefined value in the output register. + * ZF=0 and leaves an undefined value in the output register. * Perform this check in C rather than asm so that it can be * omitted in cases where the compiler is able to prove that * the input is non-zero. diff --git a/roms/ipxe/src/arch/x86_64/include/bits/time.h b/roms/ipxe/src/arch/x86_64/include/bits/time.h index 59b355359..aa74fac8c 100644 --- a/roms/ipxe/src/arch/x86_64/include/bits/time.h +++ b/roms/ipxe/src/arch/x86_64/include/bits/time.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _BITS_TIME_H */ diff --git a/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h b/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h index 9a4790fdc..6511c1ad3 100644 --- a/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h +++ b/roms/ipxe/src/arch/x86_64/include/efi/ipxe/dhcp_arch.h @@ -4,7 +4,7 @@ * 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 any later version. + * 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 @@ -13,7 +13,12 @@ * * 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _DHCP_ARCH_H @@ -24,7 +29,7 @@ * Architecture-specific DHCP options */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/dhcp.h> diff --git a/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h b/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h index a5816ac35..316243b69 100644 --- a/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h +++ b/roms/ipxe/src/arch/x86_64/include/ipxe/msr.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Read model-specific register diff --git a/roms/ipxe/src/arch/x86_64/include/setjmp.h b/roms/ipxe/src/arch/x86_64/include/setjmp.h new file mode 100644 index 000000000..69835d9fa --- /dev/null +++ b/roms/ipxe/src/arch/x86_64/include/setjmp.h @@ -0,0 +1,34 @@ +#ifndef _SETJMP_H +#define _SETJMP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +/** A jump buffer */ +typedef struct { + /** Saved return address */ + uint64_t retaddr; + /** Saved stack pointer */ + uint64_t stack; + /** Saved %rbx */ + uint64_t rbx; + /** Saved %rbp */ + uint64_t rbp; + /** Saved %r12 */ + uint64_t r12; + /** Saved %r13 */ + uint64_t r13; + /** Saved %r14 */ + uint64_t r14; + /** Saved %r15 */ + uint64_t r15; +} jmp_buf[1]; + +extern int __asmcall __attribute__ (( returns_twice )) +setjmp ( jmp_buf env ); + +extern void __asmcall __attribute__ (( noreturn )) +longjmp ( jmp_buf env, int val ); + +#endif /* _SETJMP_H */ diff --git a/roms/ipxe/src/config/.gitignore b/roms/ipxe/src/config/.gitignore deleted file mode 100644 index 8e94f32fe..000000000 --- a/roms/ipxe/src/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.buildserial.* diff --git a/roms/ipxe/src/config/branding.h b/roms/ipxe/src/config/branding.h new file mode 100644 index 000000000..73f00af95 --- /dev/null +++ b/roms/ipxe/src/config/branding.h @@ -0,0 +1,174 @@ +#ifndef CONFIG_BRANDING_H +#define CONFIG_BRANDING_H + +/** @file + * + * Branding configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/defaults.h> + +/* + * Branding + * + * Vendors may use these strings to add their own branding to iPXE. + * PRODUCT_NAME is displayed prior to any iPXE branding in startup + * messages, and PRODUCT_SHORT_NAME is used where a brief product + * label is required (e.g. in BIOS boot selection menus). + * + * To minimise end-user confusion, it's probably a good idea to either + * make PRODUCT_SHORT_NAME a substring of PRODUCT_NAME or leave it as + * "iPXE". + * + */ +#define PRODUCT_NAME "" +#define PRODUCT_SHORT_NAME "iPXE" +#define PRODUCT_URI "http://ipxe.org" + +/* + * Tag line + * + * If your PRODUCT_SHORT_NAME is longer than the four characters used + * by "iPXE", then the standard tag line "Open Source Network Boot + * Firmware" is unlikely to fit neatly onto the screen. + */ +#define PRODUCT_TAG_LINE "Open Source Network Boot Firmware" + +/* + * Error messages + * + * iPXE error messages comprise a summary error message + * (e.g. "Permission denied") and a 32-bit error number. This number + * is incorporated into an error URI such as + * + * "No such file or directory (http://ipxe.org/2d0c613b)" + * + * or + * + * "Operation not supported (http://ipxe.org/3c092003)" + * + * Users may browse to the URI within the error message, which is + * provided by a database running on the iPXE web site + * (http://ipxe.org). This database provides details for all possible + * errors generated by iPXE, including: + * + * - the detailed error message (e.g. "Not an OCSP signing + * certificate") to complement the summary message (e.g. "Permission + * denied") which is compiled into the iPXE binary. + * + * - an instruction to the user to upgrade, if the error cannot be + * generated by the latest version of iPXE. + * + * - hints on how to fix the error (e.g. "This error indicates that + * the file was not found on the TFTP server. Check that you can + * retrieve the file using an alternative TFTP client, such as + * tftp-hpa on Linux.") + * + * - details of which source file within the iPXE codebase generated + * the error. + * + * - a direct link to the line(s) of code which generated the error. + * + * If you have a customer support team and would like your customers + * to contact your support team for all problems, instead of using the + * existing support infrastructure provided by http://ipxe.org, then + * you may define a custom URI to be included within error messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the 32-bit error number. + */ +#define PRODUCT_ERROR_URI "http://ipxe.org/%08x" + +/* + * Command help messages + * + * iPXE command help messages include a URI constructed from the + * command name, such as + * + * "See http://ipxe.org/cmd/vcreate for further information" + * + * The iPXE web site includes documentation for the commands provided + * by the iPXE shell, including: + * + * - details of the command syntax (e.g. "vcreate --tag <tag> + * [--priority <priority>] <trunk interface>"). + * + * - example usages of the command (e.g. "vcreate --tag 123 net0") + * + * - a formal description of the command (e.g. "Create a VLAN network + * interface on an existing trunk network interface. The new network + * interface will be named by appending a hyphen and the VLAN tag + * value to the trunk network interface name.") + * + * - details of the possible exit statuses from the command. + * + * - links to documentation for related commands (e.g. "vdestroy") + * + * - links to documentation for relevant build options (e.g. "VLAN_CMD"). + * + * - general hints and tips on using the command. + * + * If you want to provide your own documentation for all of the + * commands provided by the iPXE shell, rather than using the existing + * support infrastructure provided by http://ipxe.org, then you may + * define a custom URI to be included within command help messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the command name. + * + * [ Please also note that the existing documentation is licensed + * under Creative Commons terms which require attribution to the + * iPXE project and prohibit the alteration or removal of any + * references to "iPXE". ] + */ +#define PRODUCT_COMMAND_URI "http://ipxe.org/cmd/%s" + +/* + * Setting help messages + * + * iPXE setting help messages include a URI constructed from the + * setting name, such as + * + * "http://ipxe.org/cfg/initiator-iqn" + * + * The iPXE web site includes documentation for the settings used by + * iPXE, including: + * + * - details of the corresponding DHCP option number. + * + * - details of the corresponding ISC dhcpd option name. + * + * - examples of using the setting from the iPXE command line, or in + * iPXE scripts. + * + * - examples of configuring the setting via a DHCP server. + * + * - a formal description of the setting. + * + * - links to documentation for related settings. + * + * - links to documentation for relevant build options. + * + * - general notes about the setting. + * + * If you want to provide your own documentation for all of the + * settings used by iPXE, rather than using the existing support + * infrastructure provided by http://ipxe.org, then you may define a + * custom URI to be included within setting help messages. + * + * Note that the custom URI is a printf() format string which must + * include a format specifier for the setting name. + * + * [ Please also note that the existing documentation is licensed + * under Creative Commons terms which require attribution to the + * iPXE project and prohibit the alteration or removal of any + * references to "iPXE". ] + */ +#define PRODUCT_SETTING_URI "http://ipxe.org/cfg/%s" + +#include <config/local/branding.h> + +#endif /* CONFIG_BRANDING_H */ diff --git a/roms/ipxe/src/config/colour.h b/roms/ipxe/src/config/colour.h index 57d20c1db..98198f12f 100644 --- a/roms/ipxe/src/config/colour.h +++ b/roms/ipxe/src/config/colour.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define COLOR_NORMAL_FG COLOR_WHITE #define COLOR_NORMAL_BG COLOR_BLUE diff --git a/roms/ipxe/src/config/config.c b/roms/ipxe/src/config/config.c index 6c8b9551a..1dd912c1d 100644 --- a/roms/ipxe/src/config/config.c +++ b/roms/ipxe/src/config/config.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> #include <config/console.h> @@ -30,33 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * in the final iPXE executable built. */ -/* - * Build ID string calculations - * - */ -#undef XSTR -#undef STR -#define XSTR(s) STR(s) -#define STR(s) #s - -#ifdef BUILD_SERIAL -#include "config/.buildserial.h" -#define BUILD_SERIAL_STR " #" XSTR(BUILD_SERIAL_NUM) -#else -#define BUILD_SERIAL_STR "" -#endif - -#ifdef BUILD_ID -#define BUILD_ID_STR " " BUILD_ID -#else -#define BUILD_ID_STR "" -#endif - -#if defined(BUILD_ID) || defined(BUILD_SERIAL) -#define BUILD_STRING " [build" BUILD_ID_STR BUILD_SERIAL_STR "]" -#else -#define BUILD_STRING "" -#endif +PROVIDE_REQUIRING_SYMBOL(); /* * Drag in all requested console types @@ -67,7 +55,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); REQUIRE_OBJECT ( bios_console ); #endif #ifdef CONSOLE_SERIAL -REQUIRE_OBJECT ( serial_console ); +REQUIRE_OBJECT ( serial ); #endif #ifdef CONSOLE_DIRECT_VGA REQUIRE_OBJECT ( video_subr ); @@ -96,6 +84,9 @@ REQUIRE_OBJECT ( debugcon ); #ifdef CONSOLE_VESAFB REQUIRE_OBJECT ( vesafb ); #endif +#ifdef CONSOLE_INT13 +REQUIRE_OBJECT ( int13con ); +#endif /* * Drag in all requested network protocols @@ -149,6 +140,9 @@ REQUIRE_OBJECT ( slam ); #ifdef SANBOOT_PROTO_ISCSI REQUIRE_OBJECT ( iscsi ); #endif +#ifdef SANBOOT_PROTO_HTTP +REQUIRE_OBJECT ( httpblock ); +#endif /* * Drag in all requested resolvers @@ -349,6 +343,9 @@ REQUIRE_OBJECT ( cpuid_settings ); #ifdef MEMMAP_SETTINGS REQUIRE_OBJECT ( memmap_settings ); #endif +#ifdef VRAM_SETTINGS +REQUIRE_OBJECT ( vram_settings ); +#endif /* * Drag in selected keyboard map diff --git a/roms/ipxe/src/config/config_crypto.c b/roms/ipxe/src/config/config_crypto.c new file mode 100644 index 000000000..1e125d8ab --- /dev/null +++ b/roms/ipxe/src/config/config_crypto.c @@ -0,0 +1,76 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/crypto.h> + +/** @file + * + * Cryptographic configuration + * + * Cryptographic configuration is slightly messy since we need to drag + * in objects based on combinations of build options. + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* RSA and MD5 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_MD5 ) +REQUIRE_OBJECT ( rsa_md5 ); +#endif + +/* RSA and SHA-1 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA1 ) +REQUIRE_OBJECT ( rsa_sha1 ); +#endif + +/* RSA and SHA-224 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA224 ) +REQUIRE_OBJECT ( rsa_sha224 ); +#endif + +/* RSA and SHA-256 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA256 ) +REQUIRE_OBJECT ( rsa_sha256 ); +#endif + +/* RSA and SHA-384 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA384 ) +REQUIRE_OBJECT ( rsa_sha384 ); +#endif + +/* RSA and SHA-512 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_DIGEST_SHA512 ) +REQUIRE_OBJECT ( rsa_sha512 ); +#endif + +/* RSA, AES-CBC, and SHA-1 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \ + defined ( CRYPTO_DIGEST_SHA1 ) +REQUIRE_OBJECT ( rsa_aes_cbc_sha1 ); +#endif + +/* RSA, AES-CBC, and SHA-256 */ +#if defined ( CRYPTO_PUBKEY_RSA ) && defined ( CRYPTO_CIPHER_AES_CBC ) && \ + defined ( CRYPTO_DIGEST_SHA256 ) +REQUIRE_OBJECT ( rsa_aes_cbc_sha256 ); +#endif diff --git a/roms/ipxe/src/config/config_ethernet.c b/roms/ipxe/src/config/config_ethernet.c index d13bd6144..de7a07c57 100644 --- a/roms/ipxe/src/config/config_ethernet.c +++ b/roms/ipxe/src/config/config_ethernet.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Ethernet-specific protocols */ @@ -24,3 +40,6 @@ REQUIRE_OBJECT ( aoe ); #ifdef NET_PROTO_FCOE REQUIRE_OBJECT ( fcoe ); #endif +#ifdef NET_PROTO_STP +REQUIRE_OBJECT ( stp ); +#endif diff --git a/roms/ipxe/src/config/config_fc.c b/roms/ipxe/src/config/config_fc.c index 414646994..33fc9462a 100644 --- a/roms/ipxe/src/config/config_fc.c +++ b/roms/ipxe/src/config/config_fc.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Fibre Channel-specific commands * diff --git a/roms/ipxe/src/config/config_http.c b/roms/ipxe/src/config/config_http.c new file mode 100644 index 000000000..3f198d228 --- /dev/null +++ b/roms/ipxe/src/config/config_http.c @@ -0,0 +1,45 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/general.h> + +/** @file + * + * HTTP extensions + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in HTTP extensions + */ +#ifdef HTTP_AUTH_BASIC +REQUIRE_OBJECT ( httpbasic ); +#endif +#ifdef HTTP_AUTH_DIGEST +REQUIRE_OBJECT ( httpdigest ); +#endif +#ifdef HTTP_ENC_PEERDIST +REQUIRE_OBJECT ( peerdist ); +#endif diff --git a/roms/ipxe/src/config/config_infiniband.c b/roms/ipxe/src/config/config_infiniband.c index 432e621d0..a742e7559 100644 --- a/roms/ipxe/src/config/config_infiniband.c +++ b/roms/ipxe/src/config/config_infiniband.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in Infiniband-specific protocols */ diff --git a/roms/ipxe/src/config/config_net80211.c b/roms/ipxe/src/config/config_net80211.c index b33c363b1..343617548 100644 --- a/roms/ipxe/src/config/config_net80211.c +++ b/roms/ipxe/src/config/config_net80211.c @@ -1,8 +1,18 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -15,6 +25,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in 802.11-specific commands * diff --git a/roms/ipxe/src/config/config_romprefix.c b/roms/ipxe/src/config/config_romprefix.c index 85f1e78ab..21921b867 100644 --- a/roms/ipxe/src/config/config_romprefix.c +++ b/roms/ipxe/src/config/config_romprefix.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Provide UNDI loader if PXE stack is requested * diff --git a/roms/ipxe/src/config/config_route.c b/roms/ipxe/src/config/config_route.c index 33e18cdd3..c0b4ee91d 100644 --- a/roms/ipxe/src/config/config_route.c +++ b/roms/ipxe/src/config/config_route.c @@ -1,11 +1,25 @@ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/general.h> @@ -15,6 +29,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +PROVIDE_REQUIRING_SYMBOL(); + /* * Drag in routing management for relevant protocols * diff --git a/roms/ipxe/src/config/config_usb.c b/roms/ipxe/src/config/config_usb.c new file mode 100644 index 000000000..dc0e6e6af --- /dev/null +++ b/roms/ipxe/src/config/config_usb.c @@ -0,0 +1,52 @@ +/* + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/usb.h> + +/** @file + * + * USB configuration options + * + */ + +PROVIDE_REQUIRING_SYMBOL(); + +/* + * Drag in USB controllers + */ +#ifdef USB_HCD_XHCI +REQUIRE_OBJECT ( xhci ); +#endif +#ifdef USB_HCD_EHCI +REQUIRE_OBJECT ( ehci ); +#endif +#ifdef USB_HCD_UHCI +REQUIRE_OBJECT ( uhci ); +#endif + +/* + * Drag in USB peripherals + */ +#ifdef USB_KEYBOARD +REQUIRE_OBJECT ( usbkbd ); +#endif diff --git a/roms/ipxe/src/config/console.h b/roms/ipxe/src/config/console.h index 908ec5a0b..ffa5cf50d 100644 --- a/roms/ipxe/src/config/console.h +++ b/roms/ipxe/src/config/console.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> @@ -23,6 +23,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#define CONSOLE_VMWARE /* VMware logfile console */ //#define CONSOLE_DEBUGCON /* Debug port console */ //#define CONSOLE_VESAFB /* VESA framebuffer console */ +//#define CONSOLE_INT13 /* INT13 disk log console */ #define KEYBOARD_MAP us diff --git a/roms/ipxe/src/config/crypto.h b/roms/ipxe/src/config/crypto.h index 1e021b0fb..bccfc04b8 100644 --- a/roms/ipxe/src/config/crypto.h +++ b/roms/ipxe/src/config/crypto.h @@ -7,7 +7,40 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** RSA public-key algorithm */ +#define CRYPTO_PUBKEY_RSA + +/** AES-CBC block cipher */ +#define CRYPTO_CIPHER_AES_CBC + +/** MD5 digest algorithm + * + * Note that use of MD5 is implicit when using TLSv1.1 or earlier. + */ +#define CRYPTO_DIGEST_MD5 + +/** SHA-1 digest algorithm + * + * Note that use of SHA-1 is implicit when using TLSv1.1 or earlier. + */ +#define CRYPTO_DIGEST_SHA1 + +/** SHA-224 digest algorithm */ +#define CRYPTO_DIGEST_SHA224 + +/** SHA-256 digest algorithm + * + * Note that use of SHA-256 is implicit when using TLSv1.2. + */ +#define CRYPTO_DIGEST_SHA256 + +/** SHA-384 digest algorithm */ +#define CRYPTO_DIGEST_SHA384 + +/** SHA-512 digest algorithm */ +#define CRYPTO_DIGEST_SHA512 /** Margin of error (in seconds) allowed in signed timestamps * diff --git a/roms/ipxe/src/config/defaults.h b/roms/ipxe/src/config/defaults.h index 389c0b07b..32d6dbcce 100644 --- a/roms/ipxe/src/config/defaults.h +++ b/roms/ipxe/src/config/defaults.h @@ -1,7 +1,7 @@ #ifndef CONFIG_DEFAULTS_H #define CONFIG_DEFAULTS_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define CONFIG_DEFAULTS(_platform) <config/defaults/_platform.h> diff --git a/roms/ipxe/src/config/defaults/efi.h b/roms/ipxe/src/config/defaults/efi.h index 4276d9366..cdf41c54d 100644 --- a/roms/ipxe/src/config/defaults/efi.h +++ b/roms/ipxe/src/config/defaults/efi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_EFI #define IOAPI_X86 @@ -19,8 +19,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SMBIOS_EFI #define SANBOOT_NULL #define BOFM_EFI -#define ENTROPY_NULL -#define TIME_NULL +#define ENTROPY_EFI +#define TIME_EFI #define REBOOT_EFI #define IMAGE_EFI /* EFI image support */ diff --git a/roms/ipxe/src/config/defaults/pcbios.h b/roms/ipxe/src/config/defaults/pcbios.h index 7debc8d2f..3ed8343ce 100644 --- a/roms/ipxe/src/config/defaults/pcbios.h +++ b/roms/ipxe/src/config/defaults/pcbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define UACCESS_LIBRM #define IOAPI_X86 @@ -35,6 +35,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define SANBOOT_PROTO_AOE /* AoE protocol */ #define SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ #define SANBOOT_PROTO_FCP /* Fibre Channel protocol */ +#define SANBOOT_PROTO_HTTP /* HTTP SAN protocol */ + +#define USB_HCD_XHCI /* xHCI USB host controller */ +#define USB_HCD_EHCI /* EHCI USB host controller */ +#define USB_HCD_UHCI /* UHCI USB host controller */ +#define USB_KEYBOARD /* USB keyboards */ #define REBOOT_CMD /* Reboot command */ #define CPUID_CMD /* x86 CPU feature detection command */ diff --git a/roms/ipxe/src/config/dhcp.h b/roms/ipxe/src/config/dhcp.h new file mode 100644 index 000000000..49fe16b92 --- /dev/null +++ b/roms/ipxe/src/config/dhcp.h @@ -0,0 +1,87 @@ +#ifndef CONFIG_DHCP_H +#define CONFIG_DHCP_H + +/** @file + * + * DHCP configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/defaults.h> + +/* + * DHCP and PXE Boot Server timeout parameters + * + * Initial and final timeout for DHCP discovery + * + * The PXE spec indicates discover request are sent 4 times, with + * timeouts of 4, 8, 16, 32 seconds. iPXE by default uses 1, 2, 4, 8. + */ +#define DHCP_DISC_START_TIMEOUT_SEC 1 +#define DHCP_DISC_END_TIMEOUT_SEC 10 +//#define DHCP_DISC_START_TIMEOUT_SEC 4 /* as per PXE spec */ +//#define DHCP_DISC_END_TIMEOUT_SEC 32 /* as per PXE spec */ + +/* + * ProxyDHCP offers are given precedence by continue to wait for them + * after a valid DHCPOFFER is received. We'll wait through this + * timeout for it. The PXE spec indicates waiting through the 4 & 8 + * second timeouts, iPXE by default stops after 2. + */ +#define DHCP_DISC_PROXY_TIMEOUT_SEC 2 +//#define DHCP_DISC_PROXY_TIMEOUT_SEC 11 /* as per PXE spec */ + +/* + * Per the PXE spec, requests are also tried 4 times, but at timeout + * intervals of 1, 2, 3, 4 seconds. To adapt this to an exponential + * backoff timer, we can either do 1, 2, 4, 8, ie. 4 retires with a + * longer interval or start at 0 (0.25s) for 0.25, 0.5, 1, 2, 4, + * ie. one extra try and shorter initial timeouts. iPXE by default + * does a combination of both, starting at 0 and going through the 8 + * second timeout. + */ +#define DHCP_REQ_START_TIMEOUT_SEC 0 +#define DHCP_REQ_END_TIMEOUT_SEC 10 +//#define DHCP_REQ_END_TIMEOUT_SEC 4 /* as per PXE spec */ + +/* + * A ProxyDHCP offer without PXE options also goes through a request + * phase using these same parameters, but note the early break below. + */ +#define DHCP_PROXY_START_TIMEOUT_SEC 0 +#define DHCP_PROXY_END_TIMEOUT_SEC 10 +//#define DHCP_PROXY_END_TIMEOUT_SEC 8 /* as per PXE spec */ + +/* + * A ProxyDHCP request timeout should not induce a failure condition, + * so we always want to break before the above set of timers expire. + * The iPXE default value of 2 breaks at the first timeout after 2 + * seconds, which will be after the 2 second timeout. + */ +#define DHCP_REQ_PROXY_TIMEOUT_SEC 2 +//#define DHCP_REQ_PROXY_TIMEOUT_SEC 7 /* as per PXE spec */ + +/* + * Per the PXE spec, a PXE boot server request is also be retried 4 + * times at timeouts of 1, 2, 3, 4. iPXE uses the same timeouts as + * discovery, 1, 2, 4, 8, but will move on to the next server if + * available after an elapsed time greater than 3 seconds, therefore + * effectively only sending 3 tries at timeouts of 1, 2, 4. + */ +#define PXEBS_START_TIMEOUT_SEC 1 +#define PXEBS_END_TIMEOUT_SEC 10 +//#define PXEBS_START_TIMEOUT_SEC 0 /* as per PXE spec */ +//#define PXEBS_END_TIMEOUT_SEC 8 /* as per PXE spec */ + +/* + * Increment to the next PXE Boot server, if available, after this + * this much time has elapsed. + */ +#define PXEBS_MAX_TIMEOUT_SEC 3 +//#define PXEBS_MAX_TIMEOUT_SEC 7 /* as per PXE spec */ + +#include <config/local/dhcp.h> + +#endif /* CONFIG_DHCP_H */ diff --git a/roms/ipxe/src/config/entropy.h b/roms/ipxe/src/config/entropy.h index 7de2f6737..c79060fd5 100644 --- a/roms/ipxe/src/config/entropy.h +++ b/roms/ipxe/src/config/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/fault.h b/roms/ipxe/src/config/fault.h new file mode 100644 index 000000000..5024a8ff3 --- /dev/null +++ b/roms/ipxe/src/config/fault.h @@ -0,0 +1,34 @@ +#ifndef CONFIG_FAULT_H +#define CONFIG_FAULT_H + +/** @file + * + * Fault injection + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/defaults.h> + +/* Drop every N transmitted or received network packets */ +#define NETDEV_DISCARD_RATE 0 + +/* Drop every N transmitted or received PeerDist discovery packets */ +#define PEERDISC_DISCARD_RATE 0 + +/* Annul every N PeerDist download attempts */ +#define PEERBLK_ANNUL_RATE 0 + +/* Stall every N PeerDist download attempts */ +#define PEERBLK_STALL_RATE 0 + +/* Abort every N PeerDist download attempts */ +#define PEERBLK_ABORT_RATE 0 + +/* Corrupt every N received PeerDist packets */ +#define PEERBLK_CORRUPT_RATE 0 + +#include <config/local/fault.h> + +#endif /* CONFIG_FAULT_H */ diff --git a/roms/ipxe/src/config/general.h b/roms/ipxe/src/config/general.h index 539203457..ee15f6bf1 100644 --- a/roms/ipxe/src/config/general.h +++ b/roms/ipxe/src/config/general.h @@ -7,27 +7,11 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> /* - * Branding - * - * Vendors may use these strings to add their own branding to iPXE. - * PRODUCT_NAME is displayed prior to any iPXE branding in startup - * messages, and PRODUCT_SHORT_NAME is used where a brief product - * label is required (e.g. in BIOS boot selection menus). - * - * To minimise end-user confusion, it's probably a good idea to either - * make PRODUCT_SHORT_NAME a substring of PRODUCT_NAME or leave it as - * "iPXE". - * - */ -#define PRODUCT_NAME "" -#define PRODUCT_SHORT_NAME "iPXE" - -/* * Banner timeout configuration * * This controls the timeout for the "Press Ctrl-B for the iPXE @@ -53,6 +37,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define NET_PROTO_IPV4 /* IPv4 protocol */ #undef NET_PROTO_IPV6 /* IPv6 protocol */ #undef NET_PROTO_FCOE /* Fibre Channel over Ethernet protocol */ +#define NET_PROTO_STP /* Spanning Tree protocol */ /* * PXE support @@ -82,6 +67,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); //#undef SANBOOT_PROTO_AOE /* AoE protocol */ //#undef SANBOOT_PROTO_IB_SRP /* Infiniband SCSI RDMA protocol */ //#undef SANBOOT_PROTO_FCP /* Fibre Channel protocol */ +//#undef SANBOOT_PROTO_HTTP /* HTTP SAN protocol */ + +/* + * HTTP extensions + * + */ +#define HTTP_AUTH_BASIC /* Basic authentication */ +#define HTTP_AUTH_DIGEST /* Digest authentication */ +//#define HTTP_ENC_PEERDIST /* PeerDist content encoding */ /* * 802.11 cryptosystems and handshaking protocols @@ -156,6 +150,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ #undef NONPNP_HOOK_INT19 /* Hook INT19 on non-PnP BIOSes */ +#define AUTOBOOT_ROM_FILTER /* Autoboot only devices matching our ROM */ /* * Error message tables to include @@ -170,7 +165,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#define NETDEV_DISCARD_RATE 0 /* Drop every N packets (0=>no drop) */ #undef BUILD_SERIAL /* Include an automatic build serial * number. Add "bs" to the list of * make targets. For example: @@ -181,6 +175,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #undef GDBSERIAL /* Remote GDB debugging over serial */ #undef GDBUDP /* Remote GDB debugging over UDP * (both may be set) */ +//#define EFI_DOWNGRADE_UX /* Downgrade UEFI user experience */ #include <config/named.h> #include NAMED_CONFIG(general.h) diff --git a/roms/ipxe/src/config/ioapi.h b/roms/ipxe/src/config/ioapi.h index ce19c6d71..abe5a50ce 100644 --- a/roms/ipxe/src/config/ioapi.h +++ b/roms/ipxe/src/config/ioapi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/named.h b/roms/ipxe/src/config/named.h index 36efdabdd..ddde6f0a6 100644 --- a/roms/ipxe/src/config/named.h +++ b/roms/ipxe/src/config/named.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* config/<name>/<header>.h */ #ifdef CONFIG diff --git a/roms/ipxe/src/config/nap.h b/roms/ipxe/src/config/nap.h index 187af4289..e4fe97964 100644 --- a/roms/ipxe/src/config/nap.h +++ b/roms/ipxe/src/config/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/qemu/colour.h b/roms/ipxe/src/config/qemu/colour.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/colour.h diff --git a/roms/ipxe/src/config/qemu/console.h b/roms/ipxe/src/config/qemu/console.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/console.h diff --git a/roms/ipxe/src/config/qemu/crypto.h b/roms/ipxe/src/config/qemu/crypto.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/crypto.h diff --git a/roms/ipxe/src/config/qemu/general.h b/roms/ipxe/src/config/qemu/general.h new file mode 100644 index 000000000..30f60d3f7 --- /dev/null +++ b/roms/ipxe/src/config/qemu/general.h @@ -0,0 +1,10 @@ +/* Disable entry during POST */ +#undef ROM_BANNER_TIMEOUT +#define ROM_BANNER_TIMEOUT 0 + +/* Extend banner timeout */ +#undef BANNER_TIMEOUT +#define BANNER_TIMEOUT 30 + +/* Work around missing EFI_PXE_BASE_CODE_PROTOCOL */ +#define EFI_DOWNGRADE_UX diff --git a/roms/ipxe/src/config/qemu/serial.h b/roms/ipxe/src/config/qemu/serial.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/serial.h diff --git a/roms/ipxe/src/config/qemu/settings.h b/roms/ipxe/src/config/qemu/settings.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/settings.h diff --git a/roms/ipxe/src/config/qemu/sideband.h b/roms/ipxe/src/config/qemu/sideband.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/sideband.h diff --git a/roms/ipxe/src/config/qemu/usb.h b/roms/ipxe/src/config/qemu/usb.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/qemu/usb.h diff --git a/roms/ipxe/src/config/reboot.h b/roms/ipxe/src/config/reboot.h index 240ef87be..2d1648e7b 100644 --- a/roms/ipxe/src/config/reboot.h +++ b/roms/ipxe/src/config/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/sanboot.h b/roms/ipxe/src/config/sanboot.h index 1d7f5f177..ccc4bda1f 100644 --- a/roms/ipxe/src/config/sanboot.h +++ b/roms/ipxe/src/config/sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/serial.h b/roms/ipxe/src/config/serial.h index 08368efdb..27040dc54 100644 --- a/roms/ipxe/src/config/serial.h +++ b/roms/ipxe/src/config/serial.h @@ -13,11 +13,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); -#define COM1 0x3f8 -#define COM2 0x2f8 -#define COM3 0x3e8 -#define COM4 0x2e8 - #define COMCONSOLE COM1 /* I/O port address */ /* Keep settings from a previous user of the serial port (e.g. lilo or diff --git a/roms/ipxe/src/config/settings.h b/roms/ipxe/src/config/settings.h index 42fe9cc81..01feaaa87 100644 --- a/roms/ipxe/src/config/settings.h +++ b/roms/ipxe/src/config/settings.h @@ -7,12 +7,13 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define PCI_SETTINGS /* PCI device settings */ //#define CPUID_SETTINGS /* CPUID settings */ //#define MEMMAP_SETTINGS /* Memory map settings */ //#define VMWARE_SETTINGS /* VMware GuestInfo settings */ +//#define VRAM_SETTINGS /* Video RAM dump settings */ #include <config/named.h> #include NAMED_CONFIG(settings.h) diff --git a/roms/ipxe/src/config/sideband.h b/roms/ipxe/src/config/sideband.h index 039bb5d09..dd704f9bb 100644 --- a/roms/ipxe/src/config/sideband.h +++ b/roms/ipxe/src/config/sideband.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); //#define CONFIG_BOFM /* IBM's BladeCenter Open Fabric Manager */ diff --git a/roms/ipxe/src/config/time.h b/roms/ipxe/src/config/time.h index 0576211fd..678f6f864 100644 --- a/roms/ipxe/src/config/time.h +++ b/roms/ipxe/src/config/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/timer.h b/roms/ipxe/src/config/timer.h index abd669851..5a54d398c 100644 --- a/roms/ipxe/src/config/timer.h +++ b/roms/ipxe/src/config/timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/umalloc.h b/roms/ipxe/src/config/umalloc.h index 245c6b4aa..832dd21d1 100644 --- a/roms/ipxe/src/config/umalloc.h +++ b/roms/ipxe/src/config/umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <config/defaults.h> diff --git a/roms/ipxe/src/config/usb.h b/roms/ipxe/src/config/usb.h new file mode 100644 index 000000000..52e82eaad --- /dev/null +++ b/roms/ipxe/src/config/usb.h @@ -0,0 +1,33 @@ +#ifndef CONFIG_USB_H +#define CONFIG_USB_H + +/** @file + * + * USB configuration + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <config/defaults.h> + +/* + * USB host controllers (all enabled by default) + * + */ +//#undef USB_HCD_XHCI /* xHCI USB host controller */ +//#undef USB_HCD_EHCI /* EHCI USB host controller */ +//#undef USB_HCD_UHCI /* UHCI USB host controller */ + +/* + * USB peripherals + * + */ +//#undef USB_KEYBOARD /* USB keyboards */ + +#include <config/named.h> +#include NAMED_CONFIG(usb.h) +#include <config/local/usb.h> +#include LOCAL_NAMED_CONFIG(usb.h) + +#endif /* CONFIG_USB_H */ diff --git a/roms/ipxe/src/config/vbox/general.h b/roms/ipxe/src/config/vbox/general.h index 27d15daf2..06b45f1a8 100644 --- a/roms/ipxe/src/config/vbox/general.h +++ b/roms/ipxe/src/config/vbox/general.h @@ -1,25 +1,17 @@ /* Disabled from config/defaults/pcbios.h */ -#undef IMAGE_ELF #undef SANBOOT_PROTO_ISCSI #undef SANBOOT_PROTO_AOE #undef SANBOOT_PROTO_IB_SRP #undef SANBOOT_PROTO_FCP -#undef REBOOT_CMD -#undef CPUID_CMD /* Disabled from config/general.h */ -#undef DOWNLOAD_PROTO_HTTP #undef CRYPTO_80211_WEP #undef CRYPTO_80211_WPA #undef CRYPTO_80211_WPA2 #undef IWMGMT_CMD -#undef FCMGMT_CMD -#undef SANBOOT_CMD #undef MENU_CMD -#undef LOGIN_CMD -#undef SYNC_CMD /* Ensure ROM banner is not displayed */ diff --git a/roms/ipxe/src/config/vbox/usb.h b/roms/ipxe/src/config/vbox/usb.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/roms/ipxe/src/config/vbox/usb.h diff --git a/roms/ipxe/src/core/acpi.c b/roms/ipxe/src/core/acpi.c index 330f50631..b0ccfa78d 100644 --- a/roms/ipxe/src/core/acpi.c +++ b/roms/ipxe/src/core/acpi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/acpi.h> diff --git a/roms/ipxe/src/core/ansicol.c b/roms/ipxe/src/core/ansicol.c index 142a00f8d..ddf9ba77c 100644 --- a/roms/ipxe/src/core/ansicol.c +++ b/roms/ipxe/src/core/ansicol.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/core/ansicoldef.c b/roms/ipxe/src/core/ansicoldef.c index dd89f3b70..6d8598e11 100644 --- a/roms/ipxe/src/core/ansicoldef.c +++ b/roms/ipxe/src/core/ansicoldef.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/core/ansiesc.c b/roms/ipxe/src/core/ansiesc.c index ca9a73ce0..7f545db0e 100644 --- a/roms/ipxe/src/core/ansiesc.c +++ b/roms/ipxe/src/core/ansiesc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <assert.h> diff --git a/roms/ipxe/src/core/asprintf.c b/roms/ipxe/src/core/asprintf.c index 03cf45cfc..00edf8e11 100644 --- a/roms/ipxe/src/core/asprintf.c +++ b/roms/ipxe/src/core/asprintf.c @@ -4,7 +4,7 @@ #include <stdio.h> #include <errno.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Write a formatted string to newly allocated memory. diff --git a/roms/ipxe/src/core/assert.c b/roms/ipxe/src/core/assert.c index 0791ea7b9..294e766be 100644 --- a/roms/ipxe/src/core/assert.c +++ b/roms/ipxe/src/core/assert.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/base16.c b/roms/ipxe/src/core/base16.c index bf9cc21bb..f9e0f3364 100644 --- a/roms/ipxe/src/core/base16.c +++ b/roms/ipxe/src/core/base16.c @@ -15,14 +15,20 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> -#include <stdlib.h> #include <stdio.h> #include <errno.h> +#include <assert.h> +#include <ipxe/string.h> +#include <ipxe/vsprintf.h> #include <ipxe/base16.h> /** @file @@ -32,48 +38,42 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** - * Base16-encode data + * Encode hexadecimal string (with optional byte separator character) * + * @v separator Byte separator character, or 0 for no separator * @v raw Raw data - * @v len Length of raw data - * @v encoded Buffer for encoded string - * - * The buffer must be the correct length for the encoded string. Use - * something like - * - * char buf[ base16_encoded_len ( len ) + 1 ]; - * - * (the +1 is for the terminating NUL) to provide a buffer of the - * correct size. + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length */ -void base16_encode ( const uint8_t *raw, size_t len, char *encoded ) { - const uint8_t *raw_bytes = raw; - char *encoded_bytes = encoded; - size_t remaining = len; - - /* Encode each byte */ - for ( ; remaining-- ; encoded_bytes += 2 ) { - sprintf ( encoded_bytes, "%02x", *(raw_bytes++) ); +size_t hex_encode ( char separator, const void *raw, size_t raw_len, + char *data, size_t len ) { + const uint8_t *bytes = raw; + const char delimiter[2] = { separator, '\0' }; + size_t used = 0; + unsigned int i; + + if ( len ) + data[0] = 0; /* Ensure that a terminating NUL exists */ + for ( i = 0 ; i < raw_len ; i++ ) { + used += ssnprintf ( ( data + used ), ( len - used ), + "%s%02x", ( used ? delimiter : "" ), + bytes[i] ); } - - /* Ensure terminating NUL exists even if length was zero */ - *encoded_bytes = '\0'; - - DBG ( "Base16-encoded to \"%s\":\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( strlen ( encoded ) == base16_encoded_len ( len ) ); + return used; } /** - * Decode hexadecimal string + * Decode hexadecimal string (with optional byte separator character) * - * @v encoded Encoded string * @v separator Byte separator character, or 0 for no separator + * @v encoded Encoded string * @v data Buffer * @v len Length of buffer * @ret len Length of data, or negative error */ -int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { +int hex_decode ( char separator, const char *encoded, void *data, size_t len ) { uint8_t *out = data; unsigned int count = 0; unsigned int sixteens; @@ -87,13 +87,13 @@ int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { /* Extract digits. Note that either digit may be NUL, * which would be interpreted as an invalid value by - * strtoul_charval(); there is therefore no need for an + * digit_value(); there is therefore no need for an * explicit end-of-string check. */ - sixteens = strtoul_charval ( *(encoded++) ); + sixteens = digit_value ( *(encoded++) ); if ( sixteens >= 16 ) return -EINVAL; - units = strtoul_charval ( *(encoded++) ); + units = digit_value ( *(encoded++) ); if ( units >= 16 ) return -EINVAL; @@ -105,31 +105,3 @@ int hex_decode ( const char *encoded, char separator, void *data, size_t len ) { } return count; } - -/** - * Base16-decode data - * - * @v encoded Encoded string - * @v raw Raw data - * @ret len Length of raw data, or negative error - * - * The buffer must be large enough to contain the decoded data. Use - * something like - * - * char buf[ base16_decoded_max_len ( encoded ) ]; - * - * to provide a buffer of the correct size. - */ -int base16_decode ( const char *encoded, uint8_t *raw ) { - int len; - - len = hex_decode ( encoded, 0, raw, -1UL ); - if ( len < 0 ) - return len; - - DBG ( "Base16-decoded \"%s\" to:\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( len <= ( int ) base16_decoded_max_len ( encoded ) ); - - return len; -} diff --git a/roms/ipxe/src/core/base64.c b/roms/ipxe/src/core/base64.c index bdaf70957..e452f7d41 100644 --- a/roms/ipxe/src/core/base64.c +++ b/roms/ipxe/src/core/base64.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -39,80 +43,73 @@ static const char base64[64] = * Base64-encode data * * @v raw Raw data - * @v len Length of raw data - * @v encoded Buffer for encoded string - * - * The buffer must be the correct length for the encoded string. Use - * something like - * - * char buf[ base64_encoded_len ( len ) + 1 ]; - * - * (the +1 is for the terminating NUL) to provide a buffer of the - * correct size. + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length */ -void base64_encode ( const uint8_t *raw, size_t len, char *encoded ) { +size_t base64_encode ( const void *raw, size_t raw_len, char *data, + size_t len ) { const uint8_t *raw_bytes = ( ( const uint8_t * ) raw ); - uint8_t *encoded_bytes = ( ( uint8_t * ) encoded ); - size_t raw_bit_len = ( 8 * len ); + size_t raw_bit_len = ( 8 * raw_len ); + size_t used = 0; unsigned int bit; unsigned int byte; unsigned int shift; unsigned int tmp; - for ( bit = 0 ; bit < raw_bit_len ; bit += 6 ) { + for ( bit = 0 ; bit < raw_bit_len ; bit += 6, used++ ) { byte = ( bit / 8 ); shift = ( bit % 8 ); tmp = ( raw_bytes[byte] << shift ); - if ( ( byte + 1 ) < len ) + if ( ( byte + 1 ) < raw_len ) tmp |= ( raw_bytes[ byte + 1 ] >> ( 8 - shift ) ); tmp = ( ( tmp >> 2 ) & 0x3f ); - *(encoded_bytes++) = base64[tmp]; + if ( used < len ) + data[used] = base64[tmp]; + } + for ( ; ( bit % 8 ) != 0 ; bit += 6, used++ ) { + if ( used < len ) + data[used] = '='; } - for ( ; ( bit % 8 ) != 0 ; bit += 6 ) - *(encoded_bytes++) = '='; - *(encoded_bytes++) = '\0'; + if ( used < len ) + data[used] = '\0'; + if ( len ) + data[ len - 1 ] = '\0'; /* Ensure terminator exists */ - DBG ( "Base64-encoded to \"%s\":\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( strlen ( encoded ) == base64_encoded_len ( len ) ); + return used; } /** * Base64-decode string * * @v encoded Encoded string - * @v raw Raw data - * @ret len Length of raw data, or negative error - * - * The buffer must be large enough to contain the decoded data. Use - * something like - * - * char buf[ base64_decoded_max_len ( encoded ) ]; - * - * to provide a buffer of the correct size. + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error */ -int base64_decode ( const char *encoded, uint8_t *raw ) { - const uint8_t *encoded_bytes = ( ( const uint8_t * ) encoded ); - uint8_t *raw_bytes = ( ( uint8_t * ) raw ); - uint8_t encoded_byte; +int base64_decode ( const char *encoded, void *data, size_t len ) { + const char *in = encoded; + uint8_t *out = data; + uint8_t in_char; char *match; - int decoded; + int in_bits; unsigned int bit = 0; unsigned int pad_count = 0; - size_t len; + size_t offset; - /* Zero the raw data */ - memset ( raw, 0, base64_decoded_max_len ( encoded ) ); + /* Zero the output buffer */ + memset ( data, 0, len ); /* Decode string */ - while ( ( encoded_byte = *(encoded_bytes++) ) ) { + while ( ( in_char = *(in++) ) ) { /* Ignore whitespace characters */ - if ( isspace ( encoded_byte ) ) + if ( isspace ( in_char ) ) continue; /* Process pad characters */ - if ( encoded_byte == '=' ) { + if ( in_char == '=' ) { if ( pad_count >= 2 ) { DBG ( "Base64-encoded string \"%s\" has too " "many pad characters\n", encoded ); @@ -129,18 +126,22 @@ int base64_decode ( const char *encoded, uint8_t *raw ) { } /* Process normal characters */ - match = strchr ( base64, encoded_byte ); + match = strchr ( base64, in_char ); if ( ! match ) { DBG ( "Base64-encoded string \"%s\" contains invalid " - "character '%c'\n", encoded, encoded_byte ); + "character '%c'\n", encoded, in_char ); return -EINVAL; } - decoded = ( match - base64 ); + in_bits = ( match - base64 ); /* Add to raw data */ - decoded <<= 2; - raw_bytes[ bit / 8 ] |= ( decoded >> ( bit % 8 ) ); - raw_bytes[ bit / 8 + 1 ] |= ( decoded << ( 8 - ( bit % 8 ) ) ); + in_bits <<= 2; + offset = ( bit / 8 ); + if ( offset < len ) + out[offset] |= ( in_bits >> ( bit % 8 ) ); + offset++; + if ( offset < len ) + out[offset] |= ( in_bits << ( 8 - ( bit % 8 ) ) ); bit += 6; } @@ -150,12 +151,7 @@ int base64_decode ( const char *encoded, uint8_t *raw ) { "%d\n", encoded, bit ); return -EINVAL; } - len = ( bit / 8 ); - - DBG ( "Base64-decoded \"%s\" to:\n", encoded ); - DBG_HDA ( 0, raw, len ); - assert ( len <= base64_decoded_max_len ( encoded ) ); /* Return length in bytes */ - return ( len ); + return ( bit / 8 ); } diff --git a/roms/ipxe/src/core/basename.c b/roms/ipxe/src/core/basename.c index b534a7886..f4f929517 100644 --- a/roms/ipxe/src/core/basename.c +++ b/roms/ipxe/src/core/basename.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/core/bitmap.c b/roms/ipxe/src/core/bitmap.c index 0d1152327..2aac33870 100644 --- a/roms/ipxe/src/core/bitmap.c +++ b/roms/ipxe/src/core/bitmap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/bitmap.h> diff --git a/roms/ipxe/src/core/blockdev.c b/roms/ipxe/src/core/blockdev.c index 9d118cb2f..c219d9673 100644 --- a/roms/ipxe/src/core/blockdev.c +++ b/roms/ipxe/src/core/blockdev.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/interface.h> diff --git a/roms/ipxe/src/core/blocktrans.c b/roms/ipxe/src/core/blocktrans.c new file mode 100644 index 000000000..3f32f9cf8 --- /dev/null +++ b/roms/ipxe/src/core/blocktrans.c @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Block device translator + * + */ + +#include <stdlib.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/blockdev.h> +#include <ipxe/blocktrans.h> + +/** + * Reallocate block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int blktrans_xferbuf_realloc ( struct xfer_buffer *xferbuf, + size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Record length, if applicable */ + if ( blktrans->buffer ) { + + /* We have a (non-reallocatable) data buffer */ + return -ENOTSUP; + + } else { + + /* Record length (for block device capacity) */ + xferbuf->len = len; + return 0; + } +} + +/** + * Write data to block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void blktrans_xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Write data to buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Write data to buffer */ + copy_to_user ( blktrans->buffer, offset, data, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** + * Read data from block device translator data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void blktrans_xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + struct block_translator *blktrans = + container_of ( xferbuf, struct block_translator, xferbuf ); + + /* Read data from buffer, if applicable */ + if ( blktrans->buffer ) { + + /* Read data from buffer */ + copy_from_user ( data, blktrans->buffer, offset, len ); + + } else { + + /* Sanity check */ + assert ( len == 0 ); + } +} + +/** Block device translator data transfer buffer operations */ +static struct xfer_buffer_operations blktrans_xferbuf_operations = { + .realloc = blktrans_xferbuf_realloc, + .write = blktrans_xferbuf_write, + .read = blktrans_xferbuf_read, +}; + +/** + * Close block device translator + * + * @v blktrans Block device translator + * @v rc Reason for close + */ +static void blktrans_close ( struct block_translator *blktrans, int rc ) { + struct block_device_capacity capacity; + + /* Report block device capacity, if applicable */ + if ( ( rc == 0 ) && ( blktrans->blksize ) ) { + + /* Construct block device capacity */ + capacity.blocks = + ( blktrans->xferbuf.len / blktrans->blksize ); + capacity.blksize = blktrans->blksize; + capacity.max_count = -1U; + + /* Report block device capacity */ + block_capacity ( &blktrans->block, &capacity ); + } + + /* Shut down interfaces */ + intf_shutdown ( &blktrans->xfer, rc ); + intf_shutdown ( &blktrans->block, rc ); +} + +/** + * Deliver data + * + * @v blktrans Block device translator + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int blktrans_deliver ( struct block_translator *blktrans, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Deliver to buffer */ + if ( ( rc = xferbuf_deliver ( &blktrans->xferbuf, iob_disown ( iobuf ), + meta ) ) != 0 ) { + DBGC ( blktrans, "BLKTRANS %p could not deliver: %s\n", + blktrans, strerror ( rc ) ); + goto err; + } + + return 0; + + err: + blktrans_close ( blktrans, rc ); + return rc; +} + +/** + * Get underlying data transfer buffer + * + * @v blktrans Block device translator + * @ret xferbuf Data transfer buffer + */ +static struct xfer_buffer * +blktrans_buffer ( struct block_translator *blktrans ) { + + return &blktrans->xferbuf; +} + +/** Block device translator block device interface operations */ +static struct interface_operation blktrans_block_operations[] = { + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator block device interface descriptor */ +static struct interface_descriptor blktrans_block_desc = + INTF_DESC_PASSTHRU ( struct block_translator, block, + blktrans_block_operations, xfer ); + +/** Block device translator data transfer interface operations */ +static struct interface_operation blktrans_xfer_operations[] = { + INTF_OP ( xfer_deliver, struct block_translator *, blktrans_deliver ), + INTF_OP ( xfer_buffer, struct block_translator *, blktrans_buffer ), + INTF_OP ( intf_close, struct block_translator *, blktrans_close ), +}; + +/** Block device translator data transfer interface descriptor */ +static struct interface_descriptor blktrans_xfer_desc = + INTF_DESC_PASSTHRU ( struct block_translator, xfer, + blktrans_xfer_operations, block ); + +/** + * Insert block device translator + * + * @v block Block device interface + * @v buffer Data buffer (or UNULL) + * @v size Length of data buffer, or block size + * @ret rc Return status code + */ +int block_translate ( struct interface *block, userptr_t buffer, size_t size ) { + struct block_translator *blktrans; + int rc; + + /* Allocate and initialise structure */ + blktrans = zalloc ( sizeof ( *blktrans ) ); + if ( ! blktrans ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &blktrans->refcnt, NULL ); + intf_init ( &blktrans->block, &blktrans_block_desc, &blktrans->refcnt ); + intf_init ( &blktrans->xfer, &blktrans_xfer_desc, &blktrans->refcnt ); + blktrans->xferbuf.op = &blktrans_xferbuf_operations; + blktrans->buffer = buffer; + if ( buffer ) { + blktrans->xferbuf.len = size; + } else { + blktrans->blksize = size; + } + + /* Attach to interfaces, mortalise self, and return */ + assert ( block->dest != &null_intf ); + intf_plug_plug ( &blktrans->xfer, block->dest ); + intf_plug_plug ( &blktrans->block, block ); + ref_put ( &blktrans->refcnt ); + + DBGC2 ( blktrans, "BLKTRANS %p created", blktrans ); + if ( buffer ) { + DBGC2 ( blktrans, " for %#lx+%#zx", + user_to_phys ( buffer, 0 ), size ); + } + DBGC2 ( blktrans, "\n" ); + return 0; + + ref_put ( &blktrans->refcnt ); + err_alloc: + return rc; +} diff --git a/roms/ipxe/src/core/console.c b/roms/ipxe/src/core/console.c index 141d8f0f0..7fd00036f 100644 --- a/roms/ipxe/src/core/console.c +++ b/roms/ipxe/src/core/console.c @@ -5,7 +5,7 @@ /** @file */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Current console usage */ int console_usage = CONSOLE_USAGE_STDOUT; diff --git a/roms/ipxe/src/core/cpio.c b/roms/ipxe/src/core/cpio.c index 3a5f4d2b6..080c72daf 100644 --- a/roms/ipxe/src/core/cpio.c +++ b/roms/ipxe/src/core/cpio.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/ctype.c b/roms/ipxe/src/core/ctype.c index c812346a0..891af71ea 100644 --- a/roms/ipxe/src/core/ctype.c +++ b/roms/ipxe/src/core/ctype.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -31,11 +35,12 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** * Check to see if character is a space * - * @v c Character + * @v character Character * @ret isspace Character is a space */ -int isspace ( int c ) { - switch ( c ) { +int isspace ( int character ) { + + switch ( character ) { case ' ' : case '\f' : case '\n' : diff --git a/roms/ipxe/src/core/cwuri.c b/roms/ipxe/src/core/cwuri.c index 5865552a0..612f0b179 100644 --- a/roms/ipxe/src/core/cwuri.c +++ b/roms/ipxe/src/core/cwuri.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/uri.h> diff --git a/roms/ipxe/src/core/debug.c b/roms/ipxe/src/core/debug.c index 7ded47089..def5d8b09 100644 --- a/roms/ipxe/src/core/debug.c +++ b/roms/ipxe/src/core/debug.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdint.h> diff --git a/roms/ipxe/src/core/debug_md5.c b/roms/ipxe/src/core/debug_md5.c index f049ac757..d0dbad9ed 100644 --- a/roms/ipxe/src/core/debug_md5.c +++ b/roms/ipxe/src/core/debug_md5.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdint.h> diff --git a/roms/ipxe/src/core/device.c b/roms/ipxe/src/core/device.c index 330f95c5a..77d7b719b 100644 --- a/roms/ipxe/src/core/device.c +++ b/roms/ipxe/src/core/device.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <ipxe/list.h> diff --git a/roms/ipxe/src/core/downloader.c b/roms/ipxe/src/core/downloader.c index ec69db6b1..d745f3617 100644 --- a/roms/ipxe/src/core/downloader.c +++ b/roms/ipxe/src/core/downloader.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <errno.h> @@ -29,7 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/uaccess.h> #include <ipxe/umalloc.h> #include <ipxe/image.h> -#include <ipxe/profile.h> +#include <ipxe/xferbuf.h> #include <ipxe/downloader.h> /** @file @@ -38,14 +42,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -/** Receive profiler */ -static struct profiler downloader_rx_profiler __profiler = - { .name = "downloader.rx" }; - -/** Data copy profiler */ -static struct profiler downloader_copy_profiler __profiler = - { .name = "downloader.copy" }; - /** A downloader */ struct downloader { /** Reference count for this object */ @@ -58,8 +54,8 @@ struct downloader { /** Image to contain downloaded file */ struct image *image; - /** Current position within image buffer */ - size_t pos; + /** Data transfer buffer */ + struct xfer_buffer buffer; }; /** @@ -92,42 +88,14 @@ static void downloader_finished ( struct downloader *downloader, int rc ) { downloader->image->name, strerror ( rc ) ); } + /* Update image length */ + downloader->image->len = downloader->buffer.len; + /* Shut down interfaces */ intf_shutdown ( &downloader->xfer, rc ); intf_shutdown ( &downloader->job, rc ); } -/** - * Ensure that download buffer is large enough for the specified size - * - * @v downloader Downloader - * @v len Required minimum size - * @ret rc Return status code - */ -static int downloader_ensure_size ( struct downloader *downloader, - size_t len ) { - userptr_t new_buffer; - - /* If buffer is already large enough, do nothing */ - if ( len <= downloader->image->len ) - return 0; - - DBGC ( downloader, "Downloader %p extending to %zd bytes\n", - downloader, len ); - - /* Extend buffer */ - new_buffer = urealloc ( downloader->image->data, len ); - if ( ! new_buffer ) { - DBGC ( downloader, "Downloader %p could not extend buffer to " - "%zd bytes\n", downloader, len ); - return -ENOSPC; - } - downloader->image->data = new_buffer; - downloader->image->len = len; - - return 0; -} - /**************************************************************************** * * Job control interface @@ -148,8 +116,8 @@ static int downloader_progress ( struct downloader *downloader, * arrive out of order (e.g. with multicast protocols), but * it's a reasonable first approximation. */ - progress->completed = downloader->pos; - progress->total = downloader->image->len; + progress->completed = downloader->buffer.pos; + progress->total = downloader->buffer.len; return 0; } @@ -171,44 +139,37 @@ static int downloader_progress ( struct downloader *downloader, static int downloader_xfer_deliver ( struct downloader *downloader, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - size_t len; - size_t max; int rc; - /* Start profiling */ - profile_start ( &downloader_rx_profiler ); - - /* Calculate new buffer position */ - if ( meta->flags & XFER_FL_ABS_OFFSET ) - downloader->pos = 0; - downloader->pos += meta->offset; - - /* Ensure that we have enough buffer space for this data */ - len = iob_len ( iobuf ); - max = ( downloader->pos + len ); - if ( ( rc = downloader_ensure_size ( downloader, max ) ) != 0 ) - goto done; - - /* Copy data to buffer */ - profile_start ( &downloader_copy_profiler ); - copy_to_user ( downloader->image->data, downloader->pos, - iobuf->data, len ); - profile_stop ( &downloader_copy_profiler ); - - /* Update current buffer position */ - downloader->pos += len; - - done: - free_iob ( iobuf ); - if ( rc != 0 ) - downloader_finished ( downloader, rc ); - profile_stop ( &downloader_rx_profiler ); + /* Add data to buffer */ + if ( ( rc = xferbuf_deliver ( &downloader->buffer, iob_disown ( iobuf ), + meta ) ) != 0 ) + goto err_deliver; + + return 0; + + err_deliver: + downloader_finished ( downloader, rc ); return rc; } +/** + * Get underlying data transfer buffer + * + * @v downloader Downloader + * @ret xferbuf Data transfer buffer, or NULL on error + */ +static struct xfer_buffer * +downloader_xfer_buffer ( struct downloader *downloader ) { + + /* Provide direct access to underlying data transfer buffer */ + return &downloader->buffer; +} + /** Downloader data transfer interface operations */ static struct interface_operation downloader_xfer_operations[] = { INTF_OP ( xfer_deliver, struct downloader *, downloader_xfer_deliver ), + INTF_OP ( xfer_buffer, struct downloader *, downloader_xfer_buffer ), INTF_OP ( intf_close, struct downloader *, downloader_finished ), }; @@ -262,6 +223,7 @@ int create_downloader ( struct interface *job, struct image *image ) { intf_init ( &downloader->xfer, &downloader_xfer_desc, &downloader->refcnt ); downloader->image = image_get ( image ); + xferbuf_umalloc_init ( &downloader->buffer, &image->data ); /* Instantiate child objects and attach to our interfaces */ if ( ( rc = xfer_open_uri ( &downloader->xfer, image->uri ) ) != 0 ) diff --git a/roms/ipxe/src/core/edd.c b/roms/ipxe/src/core/edd.c index d574ea6c0..a50b74ab1 100644 --- a/roms/ipxe/src/core/edd.c +++ b/roms/ipxe/src/core/edd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/interface.h> diff --git a/roms/ipxe/src/core/errno.c b/roms/ipxe/src/core/errno.c index 06905561f..5de15bb92 100644 --- a/roms/ipxe/src/core/errno.c +++ b/roms/ipxe/src/core/errno.c @@ -1,6 +1,6 @@ #include <errno.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/exec.c b/roms/ipxe/src/core/exec.c index 1c85705ae..2c2ade0a5 100644 --- a/roms/ipxe/src/core/exec.c +++ b/roms/ipxe/src/core/exec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/core/fault.c b/roms/ipxe/src/core/fault.c new file mode 100644 index 000000000..63d3ccacf --- /dev/null +++ b/roms/ipxe/src/core/fault.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <errno.h> +#include <ipxe/fault.h> + +/** @file + * + * Fault injection + * + */ + +/** + * Inject fault with a specified probability + * + * @v rate Reciprocal of fault probability (must be non-zero) + * @ret rc Return status code + */ +int inject_fault_nonzero ( unsigned int rate ) { + + /* Do nothing unless we want to inject a fault now */ + if ( ( random() % rate ) != 0 ) + return 0; + + /* Generate error number here so that faults can be injected + * into files that don't themselves have error file + * identifiers (via errfile.h). + */ + return -EFAULT; +} + +/** + * Corrupt data with a specified probability + * + * @v rate Reciprocal of fault probability (must be non-zero) + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +void inject_corruption_nonzero ( unsigned int rate, const void *data, + size_t len ) { + uint8_t *writable; + size_t offset; + + /* Do nothing if we have no data to corrupt */ + if ( ! len ) + return; + + /* Do nothing unless we want to inject a fault now */ + if ( ! inject_fault_nonzero ( rate ) ) + return; + + /* Get a writable pointer to the nominally read-only data */ + writable = ( ( uint8_t * ) data ); + + /* Pick a random victim byte and zap it */ + offset = ( random() % len ); + writable[offset] ^= random(); +} diff --git a/roms/ipxe/src/core/fbcon.c b/roms/ipxe/src/core/fbcon.c index 72d6a6789..6d8b0086d 100644 --- a/roms/ipxe/src/core/fbcon.c +++ b/roms/ipxe/src/core/fbcon.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/fnrec.c b/roms/ipxe/src/core/fnrec.c index 3453c8b6a..0430817f8 100644 --- a/roms/ipxe/src/core/fnrec.c +++ b/roms/ipxe/src/core/fnrec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stdio.h> diff --git a/roms/ipxe/src/core/gdbserial.c b/roms/ipxe/src/core/gdbserial.c index 6f78c88bf..0983f2557 100644 --- a/roms/ipxe/src/core/gdbserial.c +++ b/roms/ipxe/src/core/gdbserial.c @@ -15,35 +15,105 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> #include <assert.h> -#include <ipxe/serial.h> +#include <ipxe/uart.h> #include <ipxe/gdbstub.h> #include <ipxe/gdbserial.h> +#include <config/serial.h> + +/* UART port number */ +#ifdef COMCONSOLE +#define GDBSERIAL_PORT COMCONSOLE +#else +#define GDBSERIAL_PORT 0 +#endif + +/* UART baud rate */ +#ifdef COMPRESERVE +#define GDBSERIAL_BAUD 0 +#else +#define GDBSERIAL_BAUD COMSPEED +#endif + +/* UART line control register value */ +#ifdef COMPRESERVE +#define GDBSERIAL_LCR 0 +#else +#define GDBSERIAL_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP ) +#endif + +/** GDB serial UART */ +static struct uart gdbserial_uart; struct gdb_transport serial_gdb_transport __gdb_transport; static size_t gdbserial_recv ( char *buf, size_t len ) { + assert ( len > 0 ); - buf [ 0 ] = serial_getc(); + while ( ! uart_data_ready ( &gdbserial_uart ) ) {} + buf[0] = uart_receive ( &gdbserial_uart ); return 1; } static void gdbserial_send ( const char *buf, size_t len ) { + while ( len-- > 0 ) { - serial_putc ( *buf++ ); + uart_transmit ( &gdbserial_uart, *buf++ ); } } +static int gdbserial_init ( int argc, char **argv ) { + unsigned int port; + char *endp; + + if ( argc == 0 ) { + port = GDBSERIAL_PORT; + } else if ( argc == 1 ) { + port = strtoul ( argv[0], &endp, 10 ); + if ( *endp ) { + printf ( "serial: invalid port\n" ); + return 1; + } + } else { + printf ( "serial: syntax <port>\n" ); + return 1; + } + + if ( ! gdbserial_configure ( port, GDBSERIAL_BAUD, GDBSERIAL_LCR ) ) { + printf ( "serial: unable to configure\n" ); + return 1; + } + + return 0; +} + struct gdb_transport serial_gdb_transport __gdb_transport = { .name = "serial", + .init = gdbserial_init, .recv = gdbserial_recv, .send = gdbserial_send, }; -struct gdb_transport *gdbserial_configure ( void ) { +struct gdb_transport * gdbserial_configure ( unsigned int port, + unsigned int baud, uint8_t lcr ) { + int rc; + + if ( ( rc = uart_select ( &gdbserial_uart, port ) ) != 0 ) + return NULL; + + if ( ( rc = uart_init ( &gdbserial_uart, baud, lcr ) ) != 0 ) + return NULL; + return &serial_gdb_transport; } diff --git a/roms/ipxe/src/core/gdbstub.c b/roms/ipxe/src/core/gdbstub.c index af06118b2..6ad52d1a6 100644 --- a/roms/ipxe/src/core/gdbstub.c +++ b/roms/ipxe/src/core/gdbstub.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/core/gdbudp.c b/roms/ipxe/src/core/gdbudp.c index 5977547c8..e4613d137 100644 --- a/roms/ipxe/src/core/gdbudp.c +++ b/roms/ipxe/src/core/gdbudp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <string.h> diff --git a/roms/ipxe/src/core/getkey.c b/roms/ipxe/src/core/getkey.c index d69cfb44b..0f0f8b7c3 100644 --- a/roms/ipxe/src/core/getkey.c +++ b/roms/ipxe/src/core/getkey.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ctype.h> #include <ipxe/console.h> diff --git a/roms/ipxe/src/core/getopt.c b/roms/ipxe/src/core/getopt.c index abc1edd6c..e6c3948d1 100644 --- a/roms/ipxe/src/core/getopt.c +++ b/roms/ipxe/src/core/getopt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/core/image.c b/roms/ipxe/src/core/image.c index ec4480238..529e3d72c 100644 --- a/roms/ipxe/src/core/image.c +++ b/roms/ipxe/src/core/image.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <string.h> @@ -154,6 +158,32 @@ int image_set_cmdline ( struct image *image, const char *cmdline ) { } /** + * Determine image type + * + * @v image Executable image + * @ret rc Return status code + */ +static int image_probe ( struct image *image ) { + struct image_type *type; + int rc; + + /* Try each type in turn */ + for_each_table_entry ( type, IMAGE_TYPES ) { + if ( ( rc = type->probe ( image ) ) == 0 ) { + image->type = type; + DBGC ( image, "IMAGE %s is %s\n", + image->name, type->name ); + break; + } + DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, + type->name, strerror ( rc ) ); + } + + DBGC ( image, "IMAGE %s format not recognised\n", image->name ); + return -ENOTSUP; +} + +/** * Register executable image * * @v image Executable image @@ -185,6 +215,14 @@ int register_image ( struct image *image ) { image->name, user_to_phys ( image->data, 0 ), user_to_phys ( image->data, image->len ) ); + /* Try to detect image type, if applicable. Ignore failures, + * since we expect to handle some unrecognised images + * (e.g. kernel initrds, multiboot modules, random files + * provided via our EFI virtual filesystem, etc). + */ + if ( ! image->type ) + image_probe ( image ); + return 0; } @@ -223,36 +261,6 @@ struct image * find_image ( const char *name ) { } /** - * Determine image type - * - * @v image Executable image - * @ret rc Return status code - */ -int image_probe ( struct image *image ) { - struct image_type *type; - int rc; - - /* Succeed if we already have a type */ - if ( image->type ) - return 0; - - /* Try each type in turn */ - for_each_table_entry ( type, IMAGE_TYPES ) { - if ( ( rc = type->probe ( image ) ) == 0 ) { - image->type = type; - DBGC ( image, "IMAGE %s is %s\n", - image->name, type->name ); - return 0; - } - DBGC ( image, "IMAGE %s is not %s: %s\n", image->name, - type->name, strerror ( rc ) ); - } - - DBGC ( image, "IMAGE %s format not recognised\n", image->name ); - return -ENOEXEC; -} - -/** * Execute image * * @v image Executable image @@ -284,9 +292,11 @@ int image_exec ( struct image *image ) { */ current_image = image_get ( image ); - /* Check that this image can be selected for execution */ - if ( ( rc = image_select ( image ) ) != 0 ) + /* Check that this image can be executed */ + if ( ! ( image->type && image->type->exec ) ) { + rc = -ENOEXEC; goto err; + } /* Check that image is trusted (if applicable) */ if ( require_trusted_images && ! ( image->flags & IMAGE_TRUSTED ) ) { @@ -378,8 +388,8 @@ int image_replace ( struct image *replacement ) { } /* Check that the replacement image can be executed */ - if ( ( rc = image_probe ( replacement ) ) != 0 ) - return rc; + if ( ! ( replacement->type && replacement->type->exec ) ) + return -ENOEXEC; /* Clear any existing replacement */ image_put ( image->replacement ); @@ -400,16 +410,13 @@ int image_replace ( struct image *replacement ) { */ int image_select ( struct image *image ) { struct image *tmp; - int rc; /* Unselect all other images */ for_each_image ( tmp ) tmp->flags &= ~IMAGE_SELECTED; /* Check that this image can be executed */ - if ( ( rc = image_probe ( image ) ) != 0 ) - return rc; - if ( ! image->type->exec ) + if ( ! ( image->type && image->type->exec ) ) return -ENOEXEC; /* Mark image as selected */ @@ -468,9 +475,7 @@ int image_pixbuf ( struct image *image, struct pixel_buffer **pixbuf ) { int rc; /* Check that this image can be used to create a pixel buffer */ - if ( ( rc = image_probe ( image ) ) != 0 ) - return rc; - if ( ! image->type->pixbuf ) + if ( ! ( image->type && image->type->pixbuf ) ) return -ENOTSUP; /* Try creating pixel buffer */ diff --git a/roms/ipxe/src/core/init.c b/roms/ipxe/src/core/init.c index 7ea0730fa..d91e44669 100644 --- a/roms/ipxe/src/core/init.c +++ b/roms/ipxe/src/core/init.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/device.h> #include <ipxe/console.h> diff --git a/roms/ipxe/src/core/interface.c b/roms/ipxe/src/core/interface.c index 62f4621db..ba148c13d 100644 --- a/roms/ipxe/src/core/interface.c +++ b/roms/ipxe/src/core/interface.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <ipxe/interface.h> @@ -307,3 +311,28 @@ void intf_restart ( struct interface *intf, int rc ) { */ intf->desc = desc; } + +/** + * Poke an object interface + * + * @v intf Object interface + * @v type Operation type + * + * This is a helper function to implement methods which take no + * parameters and return nothing. + */ +void intf_poke ( struct interface *intf, + void ( type ) ( struct interface *intf ) ) { + struct interface *dest; + intf_poke_TYPE ( void * ) *op = + intf_get_dest_op_untyped ( intf, type, &dest ); + void *object = intf_object ( dest ); + + if ( op ) { + op ( object ); + } else { + /* Default is to do nothing */ + } + + intf_put ( dest ); +} diff --git a/roms/ipxe/src/core/iobuf.c b/roms/ipxe/src/core/iobuf.c index afc91d150..3e52ada4f 100644 --- a/roms/ipxe/src/core/iobuf.c +++ b/roms/ipxe/src/core/iobuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <strings.h> @@ -200,3 +204,33 @@ struct io_buffer * iob_concatenate ( struct list_head *list ) { return concatenated; } + +/** + * Split I/O buffer + * + * @v iobuf I/O buffer + * @v len Length to split into a new I/O buffer + * @ret split New I/O buffer, or NULL on allocation failure + * + * Split the first @c len bytes of the existing I/O buffer into a + * separate I/O buffer. The resulting buffers are likely to have no + * headroom or tailroom. + * + * If this call fails, then the original buffer will be unmodified. + */ +struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ) { + struct io_buffer *split; + + /* Sanity checks */ + assert ( len <= iob_len ( iobuf ) ); + + /* Allocate new I/O buffer */ + split = alloc_iob ( len ); + if ( ! split ) + return NULL; + + /* Copy in data */ + memcpy ( iob_put ( split, len ), iobuf->data, len ); + iob_pull ( iobuf, len ); + return split; +} diff --git a/roms/ipxe/src/core/isqrt.c b/roms/ipxe/src/core/isqrt.c index 35c918d19..c4d0571e7 100644 --- a/roms/ipxe/src/core/isqrt.c +++ b/roms/ipxe/src/core/isqrt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/job.c b/roms/ipxe/src/core/job.c index 674bec8b5..65df80056 100644 --- a/roms/ipxe/src/core/job.c +++ b/roms/ipxe/src/core/job.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/core/linebuf.c b/roms/ipxe/src/core/linebuf.c index 8fb2f86a7..c197e383c 100644 --- a/roms/ipxe/src/core/linebuf.c +++ b/roms/ipxe/src/core/linebuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -39,7 +43,18 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret line Buffered line, or NULL if no line ready to read */ char * buffered_line ( struct line_buffer *linebuf ) { - return ( linebuf->ready ? linebuf->data : NULL ); + char *line = &linebuf->data[ linebuf->len ]; + + /* Fail unless we have a newly completed line to retrieve */ + if ( ( linebuf->len == 0 ) || ( linebuf->consumed == 0 ) || + ( *(--line) != '\0' ) ) + return NULL; + + /* Identify start of line */ + while ( ( line > linebuf->data ) && ( line[-1] != '\0' ) ) + line--; + + return line; } /** @@ -48,10 +63,11 @@ char * buffered_line ( struct line_buffer *linebuf ) { * @v linebuf Line buffer */ void empty_line_buffer ( struct line_buffer *linebuf ) { + free ( linebuf->data ); linebuf->data = NULL; linebuf->len = 0; - linebuf->ready = 0; + linebuf->consumed = 0; } /** @@ -72,16 +88,13 @@ void empty_line_buffer ( struct line_buffer *linebuf ) { * should call empty_line_buffer() before freeing a @c struct @c * line_buffer. */ -ssize_t line_buffer ( struct line_buffer *linebuf, - const char *data, size_t len ) { +int line_buffer ( struct line_buffer *linebuf, const char *data, size_t len ) { const char *eol; size_t consume; size_t new_len; char *new_data; - - /* Free any completed line from previous iteration */ - if ( linebuf->ready ) - empty_line_buffer ( linebuf ); + char *lf; + char *cr; /* Search for line terminator */ if ( ( eol = memchr ( data, '\n', len ) ) ) { @@ -90,6 +103,10 @@ ssize_t line_buffer ( struct line_buffer *linebuf, consume = len; } + /* Reject any embedded NULs within the data to be consumed */ + if ( memchr ( data, '\0', consume ) ) + return -EINVAL; + /* Reallocate data buffer and copy in new data */ new_len = ( linebuf->len + consume ); new_data = realloc ( linebuf->data, ( new_len + 1 ) ); @@ -100,13 +117,27 @@ ssize_t line_buffer ( struct line_buffer *linebuf, linebuf->data = new_data; linebuf->len = new_len; - /* If we have reached end of line, trim the line and mark as ready */ + /* If we have reached end of line, terminate the line */ if ( eol ) { - linebuf->data[--linebuf->len] = '\0'; /* trim NL */ - if ( linebuf->data[linebuf->len - 1] == '\r' ) - linebuf->data[--linebuf->len] = '\0'; /* trim CR */ - linebuf->ready = 1; + + /* Overwrite trailing LF (which must exist at this point) */ + assert ( linebuf->len > 0 ); + lf = &linebuf->data[ linebuf->len - 1 ]; + assert ( *lf == '\n' ); + *lf = '\0'; + + /* Trim (and overwrite) trailing CR, if present */ + if ( linebuf->len > 1 ) { + cr = ( lf - 1 ); + if ( *cr == '\r' ) { + linebuf->len--; + *cr = '\0'; + } + } } + /* Record consumed length */ + linebuf->consumed = consume; + return consume; } diff --git a/roms/ipxe/src/core/lineconsole.c b/roms/ipxe/src/core/lineconsole.c index 1b6791cf3..bb3bfafc9 100644 --- a/roms/ipxe/src/core/lineconsole.c +++ b/roms/ipxe/src/core/lineconsole.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/list.c b/roms/ipxe/src/core/list.c index 77579d69a..5175c84ec 100644 --- a/roms/ipxe/src/core/list.c +++ b/roms/ipxe/src/core/list.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/log.c b/roms/ipxe/src/core/log.c index f160b4fc8..c08e4bb9b 100644 --- a/roms/ipxe/src/core/log.c +++ b/roms/ipxe/src/core/log.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/main.c b/roms/ipxe/src/core/main.c index db09e4c39..638dea9cf 100644 --- a/roms/ipxe/src/core/main.c +++ b/roms/ipxe/src/core/main.c @@ -12,7 +12,7 @@ Literature dealing with the network protocols: **************************************************************************/ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdio.h> @@ -26,6 +26,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); * @ret rc Return status code */ __asmcall int main ( void ) { + int rc; /* Perform one-time-only initialisation (e.g. heap) */ initialise(); @@ -35,9 +36,11 @@ __asmcall int main ( void ) { startup(); printf ( "ok\n" ); - ipxe ( NULL ); + /* Attempt to boot */ + if ( ( rc = ipxe ( NULL ) ) != 0 ) + goto err_ipxe; + err_ipxe: shutdown_exit(); - - return 0; + return rc; } diff --git a/roms/ipxe/src/core/malloc.c b/roms/ipxe/src/core/malloc.c index d9c07495d..b120c0325 100644 --- a/roms/ipxe/src/core/malloc.c +++ b/roms/ipxe/src/core/malloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> @@ -106,6 +110,7 @@ static char heap[HEAP_SIZE] __attribute__ (( aligned ( __alignof__(void *) ))); static inline void valgrind_make_blocks_defined ( void ) { struct memory_block *block; + /* Do nothing unless running under Valgrind */ if ( RUNNING_ON_VALGRIND <= 0 ) return; @@ -147,6 +152,7 @@ static inline void valgrind_make_blocks_noaccess ( void ) { struct memory_block *block; struct memory_block *prev = NULL; + /* Do nothing unless running under Valgrind */ if ( RUNNING_ON_VALGRIND <= 0 ) return; @@ -267,24 +273,25 @@ static void discard_all_cache ( void ) { void * alloc_memblock ( size_t size, size_t align, size_t offset ) { struct memory_block *block; size_t align_mask; + size_t actual_size; size_t pre_size; ssize_t post_size; struct memory_block *pre; struct memory_block *post; - struct memory_block *ptr; + void *ptr; /* Sanity checks */ assert ( size != 0 ); assert ( ( align == 0 ) || ( ( align & ( align - 1 ) ) == 0 ) ); - valgrind_make_blocks_defined(); check_blocks(); /* Round up size to multiple of MIN_MEMBLOCK_SIZE and * calculate alignment mask. */ - size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); - align_mask = ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ); + actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & + ~( MIN_MEMBLOCK_SIZE - 1 ) ); + align_mask = ( ( align - 1 ) | ( MIN_MEMBLOCK_SIZE - 1 ) ); DBGC2 ( &heap, "Allocating %#zx (aligned %#zx+%zx)\n", size, align, offset ); @@ -293,7 +300,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { list_for_each_entry ( block, &free_blocks, list ) { pre_size = ( ( offset - virt_to_phys ( block ) ) & align_mask ); - post_size = ( block->size - pre_size - size ); + post_size = ( block->size - pre_size - actual_size ); if ( post_size >= 0 ) { /* Split block into pre-block, block, and * post-block. After this split, the "pre" @@ -302,7 +309,7 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { */ pre = block; block = ( ( ( void * ) pre ) + pre_size ); - post = ( ( ( void * ) block ) + size ); + post = ( ( ( void * ) block ) + actual_size ); DBGC2 ( &heap, "[%p,%p) -> [%p,%p) + [%p,%p)\n", pre, ( ( ( void * ) pre ) + pre->size ), pre, block, post, @@ -313,8 +320,8 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { * the heap). */ if ( (size_t) post_size >= MIN_MEMBLOCK_SIZE ) { - VALGRIND_MAKE_MEM_DEFINED ( post, - sizeof ( *post ) ); + VALGRIND_MAKE_MEM_UNDEFINED + ( post, sizeof ( *post ) ); post->size = post_size; list_add ( &post->list, &pre->list ); } @@ -328,14 +335,18 @@ void * alloc_memblock ( size_t size, size_t align, size_t offset ) { * it is too small, which can happen only at * the very start of the heap. */ - if ( pre_size < MIN_MEMBLOCK_SIZE ) + if ( pre_size < MIN_MEMBLOCK_SIZE ) { list_del ( &pre->list ); + VALGRIND_MAKE_MEM_NOACCESS + ( pre, sizeof ( *pre ) ); + } /* Update total free memory */ - freemem -= size; + freemem -= actual_size; /* Return allocated block */ DBGC2 ( &heap, "Allocated [%p,%p)\n", block, ( ( ( void * ) block ) + size ) ); ptr = block; + VALGRIND_MAKE_MEM_UNDEFINED ( ptr, size ); goto done; } } @@ -368,13 +379,16 @@ void free_memblock ( void *ptr, size_t size ) { struct memory_block *freeing; struct memory_block *block; struct memory_block *tmp; + size_t actual_size; ssize_t gap_before; ssize_t gap_after = -1; /* Allow for ptr==NULL */ if ( ! ptr ) return; + VALGRIND_MAKE_MEM_NOACCESS ( ptr, size ); + /* Sanity checks */ valgrind_make_blocks_defined(); check_blocks(); @@ -382,9 +396,10 @@ void free_memblock ( void *ptr, size_t size ) { * would have used. */ assert ( size != 0 ); - size = ( size + MIN_MEMBLOCK_SIZE - 1 ) & ~( MIN_MEMBLOCK_SIZE - 1 ); + actual_size = ( ( size + MIN_MEMBLOCK_SIZE - 1 ) & + ~( MIN_MEMBLOCK_SIZE - 1 ) ); freeing = ptr; - VALGRIND_MAKE_MEM_DEFINED ( freeing, sizeof ( *freeing ) ); + VALGRIND_MAKE_MEM_UNDEFINED ( freeing, sizeof ( *freeing ) ); DBGC2 ( &heap, "Freeing [%p,%p)\n", freeing, ( ( ( void * ) freeing ) + size ) ); @@ -392,7 +407,7 @@ void free_memblock ( void *ptr, size_t size ) { if ( ASSERTING ) { list_for_each_entry ( block, &free_blocks, list ) { if ( ( ( ( void * ) block ) < - ( ( void * ) freeing + size ) ) && + ( ( void * ) freeing + actual_size ) ) && ( ( void * ) freeing < ( ( void * ) block + block->size ) ) ) { assert ( 0 ); @@ -407,7 +422,7 @@ void free_memblock ( void *ptr, size_t size ) { } /* Insert/merge into free list */ - freeing->size = size; + freeing->size = actual_size; list_for_each_entry_safe ( block, tmp, &free_blocks, list ) { /* Calculate gaps before and after the "freeing" block */ gap_before = ( ( ( void * ) freeing ) - @@ -421,8 +436,10 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) freeing ) + freeing->size ), block, ( ( ( void * ) freeing ) + freeing->size ) ); - block->size += size; + block->size += actual_size; list_del ( &block->list ); + VALGRIND_MAKE_MEM_NOACCESS ( freeing, + sizeof ( *freeing ) ); freeing = block; } /* Stop processing as soon as we reach a following block */ @@ -444,10 +461,11 @@ void free_memblock ( void *ptr, size_t size ) { ( ( ( void * ) block ) + block->size ) ); freeing->size += block->size; list_del ( &block->list ); + VALGRIND_MAKE_MEM_NOACCESS ( block, sizeof ( *block ) ); } /* Update free memory counter */ - freemem += size; + freemem += actual_size; check_blocks(); valgrind_make_blocks_noaccess(); @@ -490,9 +508,9 @@ void * realloc ( void *old_ptr, size_t new_size ) { new_block = alloc_memblock ( new_total_size, 1, 0 ); if ( ! new_block ) return NULL; - VALGRIND_MAKE_MEM_UNDEFINED ( new_block, offsetof ( struct autosized_block, data ) ); new_block->size = new_total_size; - VALGRIND_MAKE_MEM_NOACCESS ( new_block, offsetof ( struct autosized_block, data ) ); + VALGRIND_MAKE_MEM_NOACCESS ( &new_block->size, + sizeof ( new_block->size ) ); new_ptr = &new_block->data; VALGRIND_MALLOCLIKE_BLOCK ( new_ptr, new_size, 0, 0 ); } @@ -505,16 +523,16 @@ void * realloc ( void *old_ptr, size_t new_size ) { if ( old_ptr && ( old_ptr != NOWHERE ) ) { old_block = container_of ( old_ptr, struct autosized_block, data ); - VALGRIND_MAKE_MEM_DEFINED ( old_block, offsetof ( struct autosized_block, data ) ); + VALGRIND_MAKE_MEM_DEFINED ( &old_block->size, + sizeof ( old_block->size ) ); old_total_size = old_block->size; assert ( old_total_size != 0 ); old_size = ( old_total_size - offsetof ( struct autosized_block, data ) ); memcpy ( new_ptr, old_ptr, ( ( old_size < new_size ) ? old_size : new_size ) ); - free_memblock ( old_block, old_total_size ); - VALGRIND_MAKE_MEM_NOACCESS ( old_block, offsetof ( struct autosized_block, data ) ); VALGRIND_FREELIKE_BLOCK ( old_ptr, 0 ); + free_memblock ( old_block, old_total_size ); } if ( ASSERTED ) { @@ -611,6 +629,7 @@ void mpopulate ( void *start, size_t len ) { */ static void init_heap ( void ) { VALGRIND_MAKE_MEM_NOACCESS ( heap, sizeof ( heap ) ); + VALGRIND_MAKE_MEM_NOACCESS ( &free_blocks, sizeof ( free_blocks ) ); mpopulate ( heap, sizeof ( heap ) ); } diff --git a/roms/ipxe/src/core/memblock.c b/roms/ipxe/src/core/memblock.c index 1fd89b871..aecddc22c 100644 --- a/roms/ipxe/src/core/memblock.c +++ b/roms/ipxe/src/core/memblock.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/memmap_settings.c b/roms/ipxe/src/core/memmap_settings.c index 0f6d0abf5..fab3e5f3a 100644 --- a/roms/ipxe/src/core/memmap_settings.c +++ b/roms/ipxe/src/core/memmap_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/core/menu.c b/roms/ipxe/src/core/menu.c index 8d42e1f83..ab5b0c7f5 100644 --- a/roms/ipxe/src/core/menu.c +++ b/roms/ipxe/src/core/menu.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/misc.c b/roms/ipxe/src/core/misc.c deleted file mode 100644 index eaceddfea..000000000 --- a/roms/ipxe/src/core/misc.c +++ /dev/null @@ -1,85 +0,0 @@ -/************************************************************************** -MISC Support Routines -**************************************************************************/ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <ctype.h> -#include <byteswap.h> -#include <ipxe/in.h> -#include <ipxe/timer.h> - -/************************************************************************** -INET_ATON - Convert an ascii x.x.x.x to binary form -**************************************************************************/ -int inet_aton ( const char *cp, struct in_addr *inp ) { - const char *p = cp; - const char *digits_start; - unsigned long ip = 0; - unsigned long val; - int j; - for(j = 0; j <= 3; j++) { - digits_start = p; - val = strtoul(p, ( char ** ) &p, 10); - if ((p == digits_start) || (val > 255)) return 0; - if ( ( j < 3 ) && ( *(p++) != '.' ) ) return 0; - ip = (ip << 8) | val; - } - if ( *p == '\0' ) { - inp->s_addr = htonl(ip); - return 1; - } - return 0; -} - -unsigned int strtoul_charval ( unsigned int charval ) { - - if ( charval >= 'a' ) { - charval = ( charval - 'a' + 10 ); - } else if ( charval >= 'A' ) { - charval = ( charval - 'A' + 10 ); - } else if ( charval <= '9' ) { - charval = ( charval - '0' ); - } - - return charval; -} - -unsigned long strtoul ( const char *p, char **endp, int base ) { - unsigned long ret = 0; - int negative = 0; - unsigned int charval; - - while ( isspace ( *p ) ) - p++; - - if ( *p == '-' ) { - negative = 1; - p++; - } - - base = strtoul_base ( &p, base ); - - while ( 1 ) { - charval = strtoul_charval ( *p ); - if ( charval >= ( unsigned int ) base ) - break; - ret = ( ( ret * base ) + charval ); - p++; - } - - if ( negative ) - ret = -ret; - - if ( endp ) - *endp = ( char * ) p; - - return ( ret ); -} - -/* - * Local variables: - * c-basic-offset: 8 - * End: - */ diff --git a/roms/ipxe/src/core/monojob.c b/roms/ipxe/src/core/monojob.c index 820fa31dc..817f21b2c 100644 --- a/roms/ipxe/src/core/monojob.c +++ b/roms/ipxe/src/core/monojob.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> diff --git a/roms/ipxe/src/core/null_reboot.c b/roms/ipxe/src/core/null_reboot.c index a3d5b2ef8..7be5612a3 100644 --- a/roms/ipxe/src/core/null_reboot.c +++ b/roms/ipxe/src/core/null_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/core/null_sanboot.c b/roms/ipxe/src/core/null_sanboot.c index 18c0dea84..2f7522c6c 100644 --- a/roms/ipxe/src/core/null_sanboot.c +++ b/roms/ipxe/src/core/null_sanboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/sanboot.h> diff --git a/roms/ipxe/src/core/null_time.c b/roms/ipxe/src/core/null_time.c index 506c70b52..90041a456 100644 --- a/roms/ipxe/src/core/null_time.c +++ b/roms/ipxe/src/core/null_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/nvo.c b/roms/ipxe/src/core/nvo.c index e135d2b41..d2c9b5e73 100644 --- a/roms/ipxe/src/core/nvo.c +++ b/roms/ipxe/src/core/nvo.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/core/open.c b/roms/ipxe/src/core/open.c index b479c2975..9d665ffda 100644 --- a/roms/ipxe/src/core/open.c +++ b/roms/ipxe/src/core/open.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdarg.h> #include <string.h> diff --git a/roms/ipxe/src/core/params.c b/roms/ipxe/src/core/params.c index 93b834419..e1f66acca 100644 --- a/roms/ipxe/src/core/params.c +++ b/roms/ipxe/src/core/params.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/parseopt.c b/roms/ipxe/src/core/parseopt.c index d268c0594..66f60158c 100644 --- a/roms/ipxe/src/core/parseopt.c +++ b/roms/ipxe/src/core/parseopt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> @@ -32,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/params.h> #include <ipxe/timer.h> #include <ipxe/parseopt.h> +#include <config/branding.h> /** @file * @@ -343,7 +348,7 @@ void print_usage ( struct command_descriptor *cmd, char **argv ) { } if ( cmd->usage ) printf ( " %s", cmd->usage ); - printf ( "\n\nSee http://ipxe.org/cmd/%s for further information\n", + printf ( "\n\nSee " PRODUCT_COMMAND_URI " for further information\n", argv[0] ); } diff --git a/roms/ipxe/src/core/pending.c b/roms/ipxe/src/core/pending.c index 7bb0c2e00..96d0cf197 100644 --- a/roms/ipxe/src/core/pending.c +++ b/roms/ipxe/src/core/pending.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/process.h> diff --git a/roms/ipxe/src/core/pinger.c b/roms/ipxe/src/core/pinger.c index 31ea2ce1c..0ff7bb9f2 100644 --- a/roms/ipxe/src/core/pinger.c +++ b/roms/ipxe/src/core/pinger.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> diff --git a/roms/ipxe/src/core/pixbuf.c b/roms/ipxe/src/core/pixbuf.c index 48f8e9f9a..41e18f8dc 100644 --- a/roms/ipxe/src/core/pixbuf.c +++ b/roms/ipxe/src/core/pixbuf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/core/pool.c b/roms/ipxe/src/core/pool.c new file mode 100644 index 000000000..0163405f7 --- /dev/null +++ b/roms/ipxe/src/core/pool.c @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Pooled connections + * + */ + +#include <assert.h> +#include <ipxe/pool.h> + +/** + * Recycle this connection after closing + * + * @v intf Data transfer interface + */ +void pool_recycle ( struct interface *intf ) { + + intf_poke ( intf, pool_recycle ); +} + +/** + * Reopen a defunct connection + * + * @v intf Data transfer interface + */ +void pool_reopen ( struct interface *intf ) { + + intf_poke ( intf, pool_reopen ); +} + +/** + * Add connection to pool + * + * @v pool Pooled connection + * @v list List of pooled connections + * @v expiry Expiry time + */ +void pool_add ( struct pooled_connection *pool, struct list_head *list, + unsigned long expiry ) { + + /* Sanity check */ + assert ( list_empty ( &pool->list ) ); + assert ( ! timer_running ( &pool->timer ) ); + + /* Add to list of pooled connections */ + list_add_tail ( &pool->list, list ); + + /* Start expiry timer */ + start_timer_fixed ( &pool->timer, expiry ); +} + +/** + * Remove connection from pool + * + * @v pool Pooled connection + */ +void pool_del ( struct pooled_connection *pool ) { + + /* Remove from list of pooled connections */ + list_del ( &pool->list ); + INIT_LIST_HEAD ( &pool->list ); + + /* Stop expiry timer */ + stop_timer ( &pool->timer ); + + /* Mark as a freshly recycled connection */ + pool->flags = POOL_RECYCLED; +} + +/** + * Close expired pooled connection + * + * @v timer Expiry timer + * @v over Failure indicator + */ +void pool_expired ( struct retry_timer *timer, int over __unused ) { + struct pooled_connection *pool = + container_of ( timer, struct pooled_connection, timer ); + + /* Sanity check */ + assert ( ! list_empty ( &pool->list ) ); + + /* Remove from connection pool */ + list_del ( &pool->list ); + INIT_LIST_HEAD ( &pool->list ); + + /* Close expired connection */ + pool->expired ( pool ); +} diff --git a/roms/ipxe/src/core/posix_io.c b/roms/ipxe/src/core/posix_io.c index 8460d0f51..35b52beeb 100644 --- a/roms/ipxe/src/core/posix_io.c +++ b/roms/ipxe/src/core/posix_io.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> diff --git a/roms/ipxe/src/core/process.c b/roms/ipxe/src/core/process.c index d341a2c37..69852c416 100644 --- a/roms/ipxe/src/core/process.c +++ b/roms/ipxe/src/core/process.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> #include <ipxe/init.h> diff --git a/roms/ipxe/src/core/profile.c b/roms/ipxe/src/core/profile.c index 150e6b273..1075047b9 100644 --- a/roms/ipxe/src/core/profile.c +++ b/roms/ipxe/src/core/profile.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/core/random.c b/roms/ipxe/src/core/random.c index 8824dca3a..a74175a79 100644 --- a/roms/ipxe/src/core/random.c +++ b/roms/ipxe/src/core/random.c @@ -4,7 +4,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <ipxe/timer.h> diff --git a/roms/ipxe/src/core/refcnt.c b/roms/ipxe/src/core/refcnt.c index 68a86120e..47c975a0b 100644 --- a/roms/ipxe/src/core/refcnt.c +++ b/roms/ipxe/src/core/refcnt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/core/resolv.c b/roms/ipxe/src/core/resolv.c index d59a8c0ad..1e3182b0b 100644 --- a/roms/ipxe/src/core/resolv.c +++ b/roms/ipxe/src/core/resolv.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/core/serial.c b/roms/ipxe/src/core/serial.c index 7e4460ab9..4ce025519 100644 --- a/roms/ipxe/src/core/serial.c +++ b/roms/ipxe/src/core/serial.c @@ -1,259 +1,184 @@ /* - * The serial port interface routines implement a simple polled i/o - * interface to a standard serial port. Due to the space restrictions - * for the boot blocks, no BIOS support is used (since BIOS requires - * expensive real/protected mode switches), instead the rudimentary - * BIOS support is duplicated here. + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. * - * The base address and speed for the i/o port are passed from the - * Makefile in the COMCONSOLE and CONSPEED preprocessor macros. The - * line control parameters are currently hard-coded to 8 bits, no - * parity, 1 stop bit (8N1). This can be changed in init_serial(). + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Serial console + * + */ -#include "stddef.h" +#include <stddef.h> #include <ipxe/init.h> -#include <ipxe/io.h> -#include <unistd.h> +#include <ipxe/uart.h> +#include <ipxe/console.h> #include <ipxe/serial.h> -#include "config/serial.h" - -/* Set default values if none specified */ +#include <config/console.h> +#include <config/serial.h> -#ifndef COMCONSOLE -#define COMCONSOLE 0x3f8 +/* Set default console usage if applicable */ +#if ! ( defined ( CONSOLE_SERIAL ) && CONSOLE_EXPLICIT ( CONSOLE_SERIAL ) ) +#undef CONSOLE_SERIAL +#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) #endif -#ifndef COMSPEED -#define COMSPEED 9600 -#endif - -#ifndef COMDATA -#define COMDATA 8 +/* UART port number */ +#ifdef COMCONSOLE +#define CONSOLE_PORT COMCONSOLE +#else +#define CONSOLE_PORT 0 #endif -#ifndef COMPARITY -#define COMPARITY 0 +/* UART baud rate */ +#ifdef COMPRESERVE +#define CONSOLE_BAUD 0 +#else +#define CONSOLE_BAUD COMSPEED #endif -#ifndef COMSTOP -#define COMSTOP 1 +/* UART line control register value */ +#ifdef COMPRESERVE +#define CONSOLE_LCR 0 +#else +#define CONSOLE_LCR UART_LCR_WPS ( COMDATA, COMPARITY, COMSTOP ) #endif -#undef UART_BASE -#define UART_BASE ( COMCONSOLE ) - -#undef UART_BAUD -#define UART_BAUD ( COMSPEED ) +/** Serial console UART */ +struct uart serial_console; -#if ((115200%UART_BAUD) != 0) -#error Bad ttys0 baud rate -#endif - -#define COMBRD (115200/UART_BAUD) +/** + * Print a character to serial console + * + * @v character Character to be printed + */ +static void serial_putchar ( int character ) { -/* Line Control Settings */ -#define UART_LCS ( ( ( (COMDATA) - 5 ) << 0 ) | \ - ( ( (COMPARITY) ) << 3 ) | \ - ( ( (COMSTOP) - 1 ) << 2 ) ) + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return; -/* Data */ -#define UART_RBR 0x00 -#define UART_TBR 0x00 + /* Transmit character */ + uart_transmit ( &serial_console, character ); +} -/* Control */ -#define UART_IER 0x01 -#define UART_IIR 0x02 -#define UART_FCR 0x02 -#define UART_LCR 0x03 -#define UART_MCR 0x04 -#define UART_DLL 0x00 -#define UART_DLM 0x01 +/** + * Get character from serial console + * + * @ret character Character read from console + */ +static int serial_getchar ( void ) { + uint8_t data; -/* Status */ -#define UART_LSR 0x05 -#define UART_LSR_TEMPT 0x40 /* Transmitter empty */ -#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */ -#define UART_LSR_BI 0x10 /* Break interrupt indicator */ -#define UART_LSR_FE 0x08 /* Frame error indicator */ -#define UART_LSR_PE 0x04 /* Parity error indicator */ -#define UART_LSR_OE 0x02 /* Overrun error indicator */ -#define UART_LSR_DR 0x01 /* Receiver data ready */ + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return 0; -#define UART_MSR 0x06 -#define UART_SCR 0x07 + /* Wait for data to be ready */ + while ( ! uart_data_ready ( &serial_console ) ) {} -#if defined(UART_MEM) -#define uart_readb(addr) readb((addr)) -#define uart_writeb(val,addr) writeb((val),(addr)) -#else -#define uart_readb(addr) inb((addr)) -#define uart_writeb(val,addr) outb((val),(addr)) -#endif + /* Receive data */ + data = uart_receive ( &serial_console ); -/* Boolean for the state of serial driver initialization */ -int serial_initialized = 0; + /* Strip any high bit and convert DEL to backspace */ + data &= 0x7f; + if ( data == 0x7f ) + data = 0x08; -/* - * void serial_putc(int ch); - * Write character `ch' to port UART_BASE. - */ -void serial_putc ( int ch ) { - int i; - int status; - i = 1000; /* timeout */ - while(--i > 0) { - status = uart_readb(UART_BASE + UART_LSR); - if (status & UART_LSR_THRE) { - /* TX buffer emtpy */ - uart_writeb(ch, UART_BASE + UART_TBR); - break; - } - mdelay(2); - } + return data; } -/* - * int serial_getc(void); - * Read a character from port UART_BASE. - */ -int serial_getc ( void ) { - int status; - int ch; - do { - status = uart_readb(UART_BASE + UART_LSR); - } while((status & 1) == 0); - ch = uart_readb(UART_BASE + UART_RBR); /* fetch (first) character */ - ch &= 0x7f; /* remove any parity bits we get */ - if (ch == 0x7f) { /* Make DEL... look like BS */ - ch = 0x08; - } - return ch; -} - -/* - * int serial_ischar(void); - * If there is a character in the input buffer of port UART_BASE, - * return nonzero; otherwise return 0. +/** + * Check for character ready to read from serial console + * + * @ret True Character available to read + * @ret False No character available to read */ -int serial_ischar ( void ) { - int status; - status = uart_readb(UART_BASE + UART_LSR); /* line status reg; */ - return status & 1; /* rx char available */ -} +static int serial_iskey ( void ) { -/* - * int serial_init(void); - * Initialize port UART_BASE to speed COMSPEED, line settings 8N1. - */ -static void serial_init ( void ) { - int status; - int divisor, lcs; + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return 0; - DBG ( "Serial port %#x initialising\n", UART_BASE ); + /* Check UART */ + return uart_data_ready ( &serial_console ); +} - divisor = COMBRD; - lcs = UART_LCS; +/** Serial console */ +struct console_driver serial_console_driver __console_driver = { + .putchar = serial_putchar, + .getchar = serial_getchar, + .iskey = serial_iskey, + .usage = CONSOLE_SERIAL, +}; +/** Initialise serial console */ +static void serial_init ( void ) { + int rc; -#ifdef COMPRESERVE - lcs = uart_readb(UART_BASE + UART_LCR) & 0x7f; - uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); - divisor = (uart_readb(UART_BASE + UART_DLM) << 8) | uart_readb(UART_BASE + UART_DLL); - uart_writeb(lcs, UART_BASE + UART_LCR); -#endif + /* Do nothing if we have no default port */ + if ( ! CONSOLE_PORT ) + return; - /* Set Baud Rate Divisor to COMSPEED, and test to see if the - * serial port appears to be present. - */ - uart_writeb(0x80 | lcs, UART_BASE + UART_LCR); - uart_writeb(0xaa, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != 0xaa) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; - } - uart_writeb(0x55, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != 0x55) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; + /* Select UART */ + if ( ( rc = uart_select ( &serial_console, CONSOLE_PORT ) ) != 0 ) { + DBG ( "Could not select UART %d: %s\n", + CONSOLE_PORT, strerror ( rc ) ); + return; } - uart_writeb(divisor & 0xff, UART_BASE + UART_DLL); - if (uart_readb(UART_BASE + UART_DLL) != (divisor & 0xff)) { - DBG ( "Serial port %#x UART_DLL failed\n", UART_BASE ); - goto out; - } - uart_writeb(0xaa, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != 0xaa) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb(0x55, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != 0x55) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb((divisor >> 8) & 0xff, UART_BASE + UART_DLM); - if (uart_readb(UART_BASE + UART_DLM) != ((divisor >> 8) & 0xff)) { - DBG ( "Serial port %#x UART_DLM failed\n", UART_BASE ); - goto out; - } - uart_writeb(lcs, UART_BASE + UART_LCR); - - /* disable interrupts */ - uart_writeb(0x0, UART_BASE + UART_IER); - /* enable fifos */ - uart_writeb(0x01, UART_BASE + UART_FCR); + /* Initialise UART */ + if ( ( rc = uart_init ( &serial_console, CONSOLE_BAUD, + CONSOLE_LCR ) ) != 0 ) { + DBG ( "Could not initialise UART %d baud %d LCR %#02x: %s\n", + CONSOLE_PORT, CONSOLE_BAUD, CONSOLE_LCR, strerror ( rc )); + return; + } +} - /* Set clear to send, so flow control works... */ - uart_writeb((1<<1), UART_BASE + UART_MCR); +/** + * Shut down serial console + * + * @v flags Shutdown flags + */ +static void serial_shutdown ( int flags __unused ) { - /* Flush the input buffer. */ - do { - /* rx buffer reg - * throw away (unconditionally the first time) - */ - (void) uart_readb(UART_BASE + UART_RBR); - /* line status reg */ - status = uart_readb(UART_BASE + UART_LSR); - } while(status & UART_LSR_DR); + /* Do nothing if we have no UART */ + if ( ! serial_console.base ) + return; - /* Note that serial support has been initialized */ - serial_initialized = 1; - out: - return; -} + /* Flush any pending output */ + uart_flush ( &serial_console ); -/* - * void serial_fini(void); - * Cleanup our use of the serial port, in particular flush the - * output buffer so we don't accidentially lose characters. - */ -static void serial_fini ( int flags __unused ) { - int i, status; - /* Flush the output buffer to avoid dropping characters, - * if we are reinitializing the serial port. - */ - i = 10000; /* timeout */ - do { - status = uart_readb(UART_BASE + UART_LSR); - } while((--i > 0) && !(status & UART_LSR_TEMPT)); - /* Don't mark it as disabled; it's still usable */ + /* Leave console enabled; it's still usable */ } -/** - * Serial driver initialisation function - * - * Initialise serial port early on so that it is available to capture - * early debug messages. - */ -struct init_fn serial_init_fn __init_fn ( INIT_SERIAL ) = { +/** Serial console initialisation function */ +struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { .initialise = serial_init, }; -/** Serial driver startup function */ +/** Serial console startup function */ struct startup_fn serial_startup_fn __startup_fn ( STARTUP_EARLY ) = { - .shutdown = serial_fini, + .shutdown = serial_shutdown, }; diff --git a/roms/ipxe/src/core/serial_console.c b/roms/ipxe/src/core/serial_console.c deleted file mode 100644 index de9b84ca7..000000000 --- a/roms/ipxe/src/core/serial_console.c +++ /dev/null @@ -1,42 +0,0 @@ -#include <ipxe/init.h> -#include <ipxe/serial.h> -#include <ipxe/console.h> -#include <config/console.h> - -/** @file - * - * Serial console - * - */ - -/* Set default console usage if applicable */ -#if ! ( defined ( CONSOLE_SERIAL ) && CONSOLE_EXPLICIT ( CONSOLE_SERIAL ) ) -#undef CONSOLE_SERIAL -#define CONSOLE_SERIAL ( CONSOLE_USAGE_ALL & ~CONSOLE_USAGE_LOG ) -#endif - -struct console_driver serial_console __console_driver; - -static void serial_console_init ( void ) { - /* - * Check if serial driver initialization is done. - * If so, it's time to enable the serial console. - */ - if ( serial_initialized ) - serial_console.disabled = 0; -} - -struct console_driver serial_console __console_driver = { - .putchar = serial_putc, - .getchar = serial_getc, - .iskey = serial_ischar, - .disabled = CONSOLE_DISABLED, - .usage = CONSOLE_SERIAL, -}; - -/** - * Serial console initialisation function - */ -struct init_fn serial_console_init_fn __init_fn ( INIT_CONSOLE ) = { - .initialise = serial_console_init, -}; diff --git a/roms/ipxe/src/core/settings.c b/roms/ipxe/src/core/settings.c index 5e16b27d0..12e6c7d61 100644 --- a/roms/ipxe/src/core/settings.c +++ b/roms/ipxe/src/core/settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -35,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/uuid.h> #include <ipxe/uri.h> #include <ipxe/base16.h> +#include <ipxe/base64.h> #include <ipxe/pci.h> #include <ipxe/init.h> #include <ipxe/version.h> @@ -337,17 +342,20 @@ struct settings * autovivify_child_settings ( struct settings *parent, */ const char * settings_name ( struct settings *settings ) { static char buf[16]; - char tmp[ sizeof ( buf ) ]; + char tmp[ 1 /* '.' */ + sizeof ( buf ) ]; /* Find target settings block */ settings = settings_target ( settings ); /* Construct name */ - for ( buf[2] = buf[0] = 0 ; settings ; settings = settings->parent ) { - memcpy ( tmp, buf, sizeof ( tmp ) ); - snprintf ( buf, sizeof ( buf ), ".%s%s", settings->name, tmp ); + buf[0] = '\0'; + tmp[0] = '\0'; + for ( ; settings->parent ; settings = settings->parent ) { + memcpy ( ( tmp + 1 ), buf, ( sizeof ( tmp ) - 1 ) ); + snprintf ( buf, sizeof ( buf ), "%s%s", settings->name, tmp ); + tmp[0] = '.'; } - return ( buf + 2 ); + return buf; } /** @@ -499,10 +507,10 @@ int register_settings ( struct settings *settings, struct settings *parent, */ void unregister_settings ( struct settings *settings ) { struct settings *child; - struct settings *tmp; /* Unregister child settings */ - list_for_each_entry_safe ( child, tmp, &settings->children, siblings ) { + while ( ( child = list_first_entry ( &settings->children, + struct settings, siblings ) ) ) { unregister_settings ( child ); } @@ -1999,32 +2007,6 @@ const struct setting_type setting_type_uint32 __setting_type = SETTING_TYPE_UINT ( SETTING_TYPE_INT32 ); /** - * Format hex string setting value - * - * @v delimiter Byte delimiter - * @v raw Raw setting value - * @v raw_len Length of raw setting value - * @v buf Buffer to contain formatted value - * @v len Length of buffer - * @ret len Length of formatted value, or negative error - */ -static int format_hex_setting ( const char *delimiter, const void *raw, - size_t raw_len, char *buf, size_t len ) { - const uint8_t *bytes = raw; - int used = 0; - unsigned int i; - - if ( len ) - buf[0] = 0; /* Ensure that a terminating NUL exists */ - for ( i = 0 ; i < raw_len ; i++ ) { - used += ssnprintf ( ( buf + used ), ( len - used ), - "%s%02x", ( used ? delimiter : "" ), - bytes[i] ); - } - return used; -} - -/** * Parse hex string setting value (using colon delimiter) * * @v type Setting type @@ -2036,7 +2018,7 @@ static int format_hex_setting ( const char *delimiter, const void *raw, */ static int parse_hex_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, ':', buf, len ); + return hex_decode ( ':', value, buf, len ); } /** @@ -2052,7 +2034,7 @@ static int parse_hex_setting ( const struct setting_type *type __unused, static int format_hex_colon_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( ":", raw, raw_len, buf, len ); + return hex_encode ( ':', raw, raw_len, buf, len ); } /** @@ -2068,7 +2050,7 @@ static int format_hex_colon_setting ( const struct setting_type *type __unused, static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, '-', buf, len ); + return hex_decode ( '-', value, buf, len ); } /** @@ -2084,7 +2066,7 @@ static int parse_hex_hyphen_setting ( const struct setting_type *type __unused, static int format_hex_hyphen_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( "-", raw, raw_len, buf, len ); + return hex_encode ( '-', raw, raw_len, buf, len ); } /** @@ -2099,7 +2081,7 @@ static int format_hex_hyphen_setting ( const struct setting_type *type __unused, */ static int parse_hex_raw_setting ( const struct setting_type *type __unused, const char *value, void *buf, size_t len ) { - return hex_decode ( value, 0, buf, len ); + return hex_decode ( 0, value, buf, len ); } /** @@ -2115,7 +2097,7 @@ static int parse_hex_raw_setting ( const struct setting_type *type __unused, static int format_hex_raw_setting ( const struct setting_type *type __unused, const void *raw, size_t raw_len, char *buf, size_t len ) { - return format_hex_setting ( "", raw, raw_len, buf, len ); + return hex_encode ( 0, raw, raw_len, buf, len ); } /** A hex-string setting (colon-delimited) */ @@ -2140,6 +2122,46 @@ const struct setting_type setting_type_hexraw __setting_type = { }; /** + * Parse Base64-encoded setting value + * + * @v type Setting type + * @v value Formatted setting value + * @v buf Buffer to contain raw value + * @v len Length of buffer + * @v size Integer size, in bytes + * @ret len Length of raw value, or negative error + */ +static int parse_base64_setting ( const struct setting_type *type __unused, + const char *value, void *buf, size_t len ) { + + return base64_decode ( value, buf, len ); +} + +/** + * Format Base64-encoded setting value + * + * @v type Setting type + * @v raw Raw setting value + * @v raw_len Length of raw setting value + * @v buf Buffer to contain formatted value + * @v len Length of buffer + * @ret len Length of formatted value, or negative error + */ +static int format_base64_setting ( const struct setting_type *type __unused, + const void *raw, size_t raw_len, + char *buf, size_t len ) { + + return base64_encode ( raw, raw_len, buf, len ); +} + +/** A Base64-encoded setting */ +const struct setting_type setting_type_base64 __setting_type = { + .name = "base64", + .parse = parse_base64_setting, + .format = format_base64_setting, +}; + +/** * Format UUID setting value * * @v type Setting type diff --git a/roms/ipxe/src/core/string.c b/roms/ipxe/src/core/string.c index e53c283c2..3e658e54e 100644 --- a/roms/ipxe/src/core/string.c +++ b/roms/ipxe/src/core/string.c @@ -1,353 +1,501 @@ /* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2004 Tobias Lorenz + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. * - * string handling functions - * based on linux/lib/string.c + * 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 free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -FILE_LICENCE ( GPL2_ONLY ); - -/* - * stupid library routines.. The optimized versions should generally be found - * as inline code in <asm-xx/string.h> + * 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. * - * These are buggy as well.. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. * - * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de> - * - Added strsep() which will replace strtok() soon (because strsep() is - * reentrant and should be faster). Use only strsep() in new code, please. + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ - + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> #include <stdint.h> #include <stdlib.h> #include <string.h> #include <ctype.h> -/* *** FROM string.c *** */ +/** @file + * + * String functions + * + */ -#ifndef __HAVE_ARCH_STRCPY /** - * strcpy - Copy a %NUL terminated string - * @dest: Where to copy the string to - * @src: Where to copy the string from + * Fill memory region + * + * @v dest Destination region + * @v character Fill character + * @v len Length + * @ret dest Destination region */ -char * strcpy(char * dest,const char *src) -{ - char *tmp = dest; +void * generic_memset ( void *dest, int character, size_t len ) { + uint8_t *dest_bytes = dest; - while ((*dest++ = *src++) != '\0') - /* nothing */; - return tmp; + while ( len-- ) + *(dest_bytes++) = character; + return dest; } -#endif -#ifndef __HAVE_ARCH_STRNCPY /** - * strncpy - Copy a length-limited, %NUL-terminated string - * @dest: Where to copy the string to - * @src: Where to copy the string from - * @count: The maximum number of bytes to copy + * Copy memory region * - * Note that unlike userspace strncpy, this does not %NUL-pad the buffer. - * However, the result is not %NUL-terminated if the source exceeds - * @count bytes. + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region */ -char * strncpy(char * dest,const char *src,size_t count) -{ - char *tmp = dest; - - while (count-- && (*dest++ = *src++) != '\0') - /* nothing */; +void * generic_memcpy ( void *dest, const void *src, size_t len ) { + const uint8_t *src_bytes = src; + uint8_t *dest_bytes = dest; - return tmp; + while ( len-- ) + *(dest_bytes++) = *(src_bytes++); + return dest; } -#endif -#ifndef __HAVE_ARCH_STRCAT /** - * strcat - Append one %NUL-terminated string to another - * @dest: The string to be appended to - * @src: The string to append to it + * Copy (possibly overlapping) memory region + * + * @v dest Destination region + * @v src Source region + * @v len Length + * @ret dest Destination region */ -char * strcat(char * dest, const char * src) -{ - char *tmp = dest; - - while (*dest) - dest++; - while ((*dest++ = *src++) != '\0') - ; +void * generic_memmove ( void *dest, const void *src, size_t len ) { + const uint8_t *src_bytes = ( src + len ); + uint8_t *dest_bytes = ( dest + len ); + + if ( dest < src ) + return memcpy ( dest, src, len ); + while ( len-- ) + *(--dest_bytes) = *(--src_bytes); + return dest; +} - return tmp; +/** + * Compare memory regions + * + * @v first First region + * @v second Second region + * @v len Length + * @ret diff Difference + */ +int memcmp ( const void *first, const void *second, size_t len ) { + const uint8_t *first_bytes = first; + const uint8_t *second_bytes = second; + int diff; + + while ( len-- ) { + diff = ( *(second_bytes++) - *(first_bytes++) ); + if ( diff ) + return diff; + } + return 0; } -#endif -#ifndef __HAVE_ARCH_STRCMP /** - * strcmp - Compare two strings - * @cs: One string - * @ct: Another string + * Find character within a memory region + * + * @v src Source region + * @v character Character to find + * @v len Length + * @ret found Found character, or NULL if not found */ -int strcmp(const char * cs,const char * ct) -{ - register signed char __res; +void * memchr ( const void *src, int character, size_t len ) { + const uint8_t *src_bytes = src; - while (1) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; + for ( ; len-- ; src_bytes++ ) { + if ( *src_bytes == character ) + return ( ( void * ) src_bytes ); } - - return __res; + return NULL; } -#endif -#ifndef __HAVE_ARCH_STRNCMP /** - * strncmp - Compare two length-limited strings - * @cs: One string - * @ct: Another string - * @count: The maximum number of bytes to compare + * Swap memory regions + * + * @v first First region + * @v second Second region + * @v len Length + * @ret first First region */ -int strncmp(const char * cs,const char * ct,size_t count) -{ - register signed char __res = 0; - - while (count) { - if ((__res = *cs - *ct++) != 0 || !*cs++) - break; - count--; +void * memswap ( void *first, void *second, size_t len ) { + uint8_t *first_bytes = first; + uint8_t *second_bytes = second; + uint8_t temp; + + for ( ; len-- ; first_bytes++, second_bytes++ ) { + temp = *first_bytes; + *first_bytes = *second_bytes; + *second_bytes = temp; } + return first; +} + +/** + * Compare strings + * + * @v first First string + * @v second Second string + * @ret diff Difference + */ +int strcmp ( const char *first, const char *second ) { - return __res; + return strncmp ( first, second, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRCASECMP -int strcasecmp(const char *a, const char *b) -{ - while (*a && *b && (*a & ~0x20) == (*b & ~0x20)) {a++; b++; } - return((*a & ~0x20) - (*b & ~0x20)); +/** + * Compare strings + * + * @v first First string + * @v second Second string + * @v max Maximum length to compare + * @ret diff Difference + */ +int strncmp ( const char *first, const char *second, size_t max ) { + const uint8_t *first_bytes = ( ( const uint8_t * ) first ); + const uint8_t *second_bytes = ( ( const uint8_t * ) second ); + int diff; + + for ( ; max-- ; first_bytes++, second_bytes++ ) { + diff = ( *second_bytes - *first_bytes ); + if ( diff ) + return diff; + if ( ! *first_bytes ) + return 0; + } + return 0; } -#endif -#ifndef __HAVE_ARCH_STRCHR /** - * strchr - Find the first occurrence of a character in a string - * @s: The string to be searched - * @c: The character to search for + * Compare case-insensitive strings + * + * @v first First string + * @v second Second string + * @ret diff Difference */ -char * strchr(const char * s, int c) -{ - for(; *s != (char) c; ++s) - if (*s == '\0') - return NULL; - return (char *) s; +int strcasecmp ( const char *first, const char *second ) { + const uint8_t *first_bytes = ( ( const uint8_t * ) first ); + const uint8_t *second_bytes = ( ( const uint8_t * ) second ); + int diff; + + for ( ; ; first_bytes++, second_bytes++ ) { + diff = ( toupper ( *second_bytes ) - + toupper ( *first_bytes ) ); + if ( diff ) + return diff; + if ( ! *first_bytes ) + return 0; + } } -#endif -#ifndef __HAVE_ARCH_STRRCHR /** - * strrchr - Find the last occurrence of a character in a string - * @s: The string to be searched - * @c: The character to search for + * Get length of string + * + * @v src String + * @ret len Length */ -char * strrchr(const char * s, int c) -{ - const char *p = s + strlen(s); - do { - if (*p == (char)c) - return (char *)p; - } while (--p >= s); - return NULL; +size_t strlen ( const char *src ) { + + return strnlen ( src, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRLEN /** - * strlen - Find the length of a string - * @s: The string to be sized + * Get length of string + * + * @v src String + * @v max Maximum length + * @ret len Length */ -size_t strlen(const char * s) -{ - const char *sc; +size_t strnlen ( const char *src, size_t max ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + size_t len = 0; - for (sc = s; *sc != '\0'; ++sc) - /* nothing */; - return sc - s; + while ( max-- && *(src_bytes++) ) + len++; + return len; } -#endif -#ifndef __HAVE_ARCH_STRNLEN /** - * strnlen - Find the length of a length-limited string - * @s: The string to be sized - * @count: The maximum number of bytes to search + * Find character within a string + * + * @v src String + * @v character Character to find + * @ret found Found character, or NULL if not found */ -size_t strnlen(const char * s, size_t count) -{ - const char *sc; +char * strchr ( const char *src, int character ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); - for (sc = s; count-- && *sc != '\0'; ++sc) - /* nothing */; - return sc - s; + for ( ; ; src_bytes++ ) { + if ( *src_bytes == character ) + return ( ( char * ) src_bytes ); + if ( ! *src_bytes ) + return NULL; + } } -#endif -#ifndef __HAVE_ARCH_MEMSET /** - * memset - Fill a region of memory with the given value - * @s: Pointer to the start of the area. - * @c: The byte to fill the area with - * @count: The size of the area. + * Find rightmost character within a string * - * Do not use memset() to access IO space, use memset_io() instead. + * @v src String + * @v character Character to find + * @ret found Found character, or NULL if not found */ -void * memset(void * s,int c,size_t count) -{ - char *xs = (char *) s; +char * strrchr ( const char *src, int character ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + const uint8_t *start = src_bytes; + + while ( *src_bytes ) + src_bytes++; + for ( src_bytes-- ; src_bytes >= start ; src_bytes-- ) { + if ( *src_bytes == character ) + return ( ( char * ) src_bytes ); + } + return NULL; +} - while (count--) - *xs++ = c; +/** + * Find substring + * + * @v haystack String + * @v needle Substring + * @ret found Found substring, or NULL if not found + */ +char * strstr ( const char *haystack, const char *needle ) { + size_t len = strlen ( needle ); - return s; + for ( ; *haystack ; haystack++ ) { + if ( memcmp ( haystack, needle, len ) == 0 ) + return ( ( char * ) haystack ); + } + return NULL; } -#endif -#ifndef __HAVE_ARCH_MEMCPY /** - * memcpy - Copy one area of memory to another - * @dest: Where to copy to - * @src: Where to copy from - * @count: The size of the area. + * Copy string * - * You should not use this function to access IO space, use memcpy_toio() - * or memcpy_fromio() instead. + * @v dest Destination string + * @v src Source string + * @ret dest Destination string */ -void * memcpy(void * dest,const void *src,size_t count) -{ - char *tmp = (char *) dest, *s = (char *) src; +char * strcpy ( char *dest, const char *src ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + uint8_t *dest_bytes = ( ( uint8_t * ) dest ); + + /* We cannot use strncpy(), since that would pad the destination */ + for ( ; ; src_bytes++, dest_bytes++ ) { + *dest_bytes = *src_bytes; + if ( ! *dest_bytes ) + break; + } + return dest; +} - while (count--) - *tmp++ = *s++; +/** + * Copy string + * + * @v dest Destination string + * @v src Source string + * @v max Maximum length + * @ret dest Destination string + */ +char * strncpy ( char *dest, const char *src, size_t max ) { + const uint8_t *src_bytes = ( ( const uint8_t * ) src ); + uint8_t *dest_bytes = ( ( uint8_t * ) dest ); + for ( ; max ; max--, src_bytes++, dest_bytes++ ) { + *dest_bytes = *src_bytes; + if ( ! *dest_bytes ) + break; + } + while ( max-- ) + *(dest_bytes++) = '\0'; return dest; } -#endif -#ifndef __HAVE_ARCH_MEMMOVE /** - * memmove - Copy one area of memory to another - * @dest: Where to copy to - * @src: Where to copy from - * @count: The size of the area. + * Concatenate string * - * Unlike memcpy(), memmove() copes with overlapping areas. + * @v dest Destination string + * @v src Source string + * @ret dest Destination string */ -void * memmove(void * dest,const void *src,size_t count) -{ - char *tmp, *s; - - if (dest <= src) { - tmp = (char *) dest; - s = (char *) src; - while (count--) - *tmp++ = *s++; - } - else { - tmp = (char *) dest + count; - s = (char *) src + count; - while (count--) - *--tmp = *--s; - } +char * strcat ( char *dest, const char *src ) { + strcpy ( ( dest + strlen ( dest ) ), src ); return dest; } -#endif -#ifndef __HAVE_ARCH_MEMCMP /** - * memcmp - Compare two areas of memory - * @cs: One area of memory - * @ct: Another area of memory - * @count: The size of the area. + * Duplicate string + * + * @v src Source string + * @ret dup Duplicated string, or NULL if allocation failed */ -int memcmp(const void * cs,const void * ct,size_t count) -{ - const unsigned char *su1, *su2; - int res = 0; +char * strdup ( const char *src ) { - for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--) - if ((res = *su1 - *su2) != 0) - break; - return res; + return strndup ( src, ~( ( size_t ) 0 ) ); } -#endif -#ifndef __HAVE_ARCH_STRSTR /** - * strstr - Find the first substring in a %NUL terminated string - * @s1: The string to be searched - * @s2: The string to search for + * Duplicate string + * + * @v src Source string + * @v max Maximum length + * @ret dup Duplicated string, or NULL if allocation failed */ -char * strstr(const char * s1,const char * s2) -{ - int l1, l2; - - l2 = strlen(s2); - if (!l2) - return (char *) s1; - l1 = strlen(s1); - while (l1 >= l2) { - l1--; - if (!memcmp(s1,s2,l2)) - return (char *) s1; - s1++; - } - return NULL; +char * strndup ( const char *src, size_t max ) { + size_t len = strnlen ( src, max ); + char *dup; + + dup = malloc ( len + 1 /* NUL */ ); + if ( dup ) { + memcpy ( dup, src, len ); + dup[len] = '\0'; + } + return dup; +} + +/** + * Calculate digit value + * + * @v character Digit character + * @ret digit Digit value + * + * Invalid digits will be returned as a value greater than or equal to + * the numeric base. + */ +unsigned int digit_value ( unsigned int character ) { + + if ( character >= 'a' ) + return ( character - ( 'a' - 10 ) ); + if ( character >= 'A' ) + return ( character - ( 'A' - 10 ) ); + if ( character <= '9' ) + return ( character - '0' ); + return character; } -#endif -#ifndef __HAVE_ARCH_MEMCHR /** - * memchr - Find a character in an area of memory. - * @s: The memory area - * @c: The byte to search for - * @n: The size of the area. + * Preprocess string for strtoul() or strtoull() * - * returns the address of the first occurrence of @c, or %NULL - * if @c is not found + * @v string String + * @v negate Final value should be negated + * @v base Numeric base + * @ret string Remaining string */ -void * memchr(const void *s, int c, size_t n) -{ - const unsigned char *p = s; - while (n-- != 0) { - if ((unsigned char)c == *p++) { - return (void *)(p-1); +static const char * strtoul_pre ( const char *string, int *negate, int *base ) { + + /* Skip any leading whitespace */ + while ( isspace ( *string ) ) + string++; + + /* Process arithmetic sign, if present */ + *negate = 0; + if ( *string == '-' ) { + string++; + *negate = 1; + } else if ( *string == '+' ) { + string++; + } + + /* Process base, if present */ + if ( *base == 0 ) { + *base = 10; + if ( *string == '0' ) { + string++; + *base = 8; + if ( ( *string & ~0x20 ) == 'X' ) { + string++; + *base = 16; + } } } - return NULL; + + return string; } -#endif +/** + * Convert string to numeric value + * + * @v string String + * @v endp End pointer (or NULL) + * @v base Numeric base (or zero to autodetect) + * @ret value Numeric value + */ +unsigned long strtoul ( const char *string, char **endp, int base ) { + unsigned long value = 0; + unsigned int digit; + int negate; + + /* Preprocess string */ + string = strtoul_pre ( string, &negate, &base ); + + /* Process digits */ + for ( ; ; string++ ) { + digit = digit_value ( *string ); + if ( digit >= ( unsigned int ) base ) + break; + value = ( ( value * base ) + digit ); + } + + /* Negate value if, applicable */ + if ( negate ) + value = -value; -char * strndup(const char *s, size_t n) -{ - size_t len = strnlen(s,n); - char *new; + /* Fill in end pointer, if applicable */ + if ( endp ) + *endp = ( ( char * ) string ); - new = malloc(len+1); - if (new) { - new[len] = '\0'; - memcpy(new,s,len); - } - return new; + return value; } -char * strdup(const char *s) { - return strndup(s, ~((size_t)0)); +/** + * Convert string to numeric value + * + * @v string String + * @v endp End pointer (or NULL) + * @v base Numeric base (or zero to autodetect) + * @ret value Numeric value + */ +unsigned long long strtoull ( const char *string, char **endp, int base ) { + unsigned long long value = 0; + unsigned int digit; + int negate; + + /* Preprocess string */ + string = strtoul_pre ( string, &negate, &base ); + + /* Process digits */ + for ( ; ; string++ ) { + digit = digit_value ( *string ); + if ( digit >= ( unsigned int ) base ) + break; + value = ( ( value * base ) + digit ); + } + + /* Negate value if, applicable */ + if ( negate ) + value = -value; + + /* Fill in end pointer, if applicable */ + if ( endp ) + *endp = ( ( char * ) string ); + + return value; } diff --git a/roms/ipxe/src/core/stringextra.c b/roms/ipxe/src/core/stringextra.c index 0a509852e..18ffc6301 100644 --- a/roms/ipxe/src/core/stringextra.c +++ b/roms/ipxe/src/core/stringextra.c @@ -38,122 +38,6 @@ FILE_LICENCE ( GPL2_ONLY ); /* *** FROM string.c *** */ -#ifndef __HAVE_ARCH_STRNICMP -/** - * strnicmp - Case insensitive, length-limited string comparison - * @s1: One string - * @s2: The other string - * @len: the maximum number of characters to compare - */ -int strnicmp(const char *s1, const char *s2, size_t len) -{ - /* Yes, Virginia, it had better be unsigned */ - unsigned char c1, c2; - - c1 = 0; c2 = 0; - if (len) { - do { - c1 = *s1; c2 = *s2; - s1++; s2++; - if (!c1) - break; - if (!c2) - break; - if (c1 == c2) - continue; - c1 = tolower(c1); - c2 = tolower(c2); - if (c1 != c2) - break; - } while (--len); - } - return (int)c1 - (int)c2; -} -#endif - -char * ___strtok; - -#ifndef __HAVE_ARCH_STRNCAT -/** - * strncat - Append a length-limited, %NUL-terminated string to another - * @dest: The string to be appended to - * @src: The string to append to it - * @count: The maximum numbers of bytes to copy - * - * Note that in contrast to strncpy, strncat ensures the result is - * terminated. - */ -char * strncat(char *dest, const char *src, size_t count) -{ - char *tmp = dest; - - if (count) { - while (*dest) - dest++; - while ((*dest++ = *src++)) { - if (--count == 0) { - *dest = '\0'; - break; - } - } - } - - return tmp; -} -#endif - -#ifndef __HAVE_ARCH_STRSPN -/** - * strspn - Calculate the length of the initial substring of @s which only - * contain letters in @accept - * @s: The string to be searched - * @accept: The string to search for - */ -size_t strspn(const char *s, const char *accept) -{ - const char *p; - const char *a; - size_t count = 0; - - for (p = s; *p != '\0'; ++p) { - for (a = accept; *a != '\0'; ++a) { - if (*p == *a) - break; - } - if (*a == '\0') - return count; - ++count; - } - - return count; -} -#endif - -#ifndef __HAVE_ARCH_STRCSPN -/** - * strcspn - Calculate the length of the initial substring of @s which only - * contain letters not in @reject - * @s: The string to be searched - * @accept: The string to search for - */ -size_t strcspn(const char *s, const char *reject) -{ - const char *p; - const char *r; - size_t count = 0; - - for (p = s; *p != '\0'; ++p) { - for (r = reject; *r != '\0'; ++r) { - if (*p == *r) - return count; - } - ++count; - } - - return count; -} -#endif - #ifndef __HAVE_ARCH_STRPBRK /** * strpbrk - Find the first occurrence of a set of characters @@ -174,35 +58,6 @@ char * strpbrk(const char * cs,const char * ct) } #endif -#ifndef __HAVE_ARCH_STRTOK -/** - * strtok - Split a string into tokens - * @s: The string to be searched - * @ct: The characters to search for - * - * WARNING: strtok is deprecated, use strsep instead. - */ -char * strtok(char * s,const char * ct) -{ - char *sbegin, *send; - - sbegin = s ? s : ___strtok; - if (!sbegin) { - return NULL; - } - sbegin += strspn(sbegin,ct); - if (*sbegin == '\0') { - ___strtok = NULL; - return( NULL ); - } - send = strpbrk( sbegin, ct); - if (send && *send != '\0') - *send++ = '\0'; - ___strtok = send; - return (sbegin); -} -#endif - #ifndef __HAVE_ARCH_STRSEP /** * strsep - Split a string into tokens @@ -230,46 +85,3 @@ char * strsep(char **s, const char *ct) return sbegin; } #endif - -#ifndef __HAVE_ARCH_BCOPY -/** - * bcopy - Copy one area of memory to another - * @src: Where to copy from - * @dest: Where to copy to - * @count: The size of the area. - * - * Note that this is the same as memcpy(), with the arguments reversed. - * memcpy() is the standard, bcopy() is a legacy BSD function. - * - * You should not use this function to access IO space, use memcpy_toio() - * or memcpy_fromio() instead. - */ -char * bcopy(const char * src, char * dest, int count) -{ - return memmove(dest,src,count); -} -#endif - -#ifndef __HAVE_ARCH_MEMSCAN -/** - * memscan - Find a character in an area of memory. - * @addr: The memory area - * @c: The byte to search for - * @size: The size of the area. - * - * returns the address of the first occurrence of @c, or 1 byte past - * the area if @c is not found - */ -void * memscan(const void * addr, int c, size_t size) -{ - unsigned char * p = (unsigned char *) addr; - - while (size) { - if (*p == c) - return (void *) p; - p++; - size--; - } - return (void *) p; -} -#endif diff --git a/roms/ipxe/src/core/strtoull.c b/roms/ipxe/src/core/strtoull.c deleted file mode 100644 index 00986eef0..000000000 --- a/roms/ipxe/src/core/strtoull.c +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk> - * Copyright (C) 2010 Piotr JaroszyÅ„ski <p.jaroszynski@gmail.com> - * - * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdlib.h> -#include <ctype.h> - -/* - * Despite being exactly the same as strtoul() except the long long instead of - * long it ends up being much bigger so provide a separate implementation in a - * separate object so that it won't be linked in if not used. - */ -unsigned long long strtoull ( const char *p, char **endp, int base ) { - unsigned long long ret = 0; - int negative = 0; - unsigned int charval; - - while ( isspace ( *p ) ) - p++; - - if ( *p == '-' ) { - negative = 1; - p++; - } - - base = strtoul_base ( &p, base ); - - while ( 1 ) { - charval = strtoul_charval ( *p ); - if ( charval >= ( unsigned int ) base ) - break; - ret = ( ( ret * base ) + charval ); - p++; - } - - if ( negative ) - ret = -ret; - - if ( endp ) - *endp = ( char * ) p; - - return ( ret ); -} diff --git a/roms/ipxe/src/core/time.c b/roms/ipxe/src/core/time.c index f70e1981d..29a924ebe 100644 --- a/roms/ipxe/src/core/time.c +++ b/roms/ipxe/src/core/time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <time.h> diff --git a/roms/ipxe/src/core/timer.c b/roms/ipxe/src/core/timer.c index 18c2b2849..dbd89f12b 100644 --- a/roms/ipxe/src/core/timer.c +++ b/roms/ipxe/src/core/timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <unistd.h> diff --git a/roms/ipxe/src/core/uart.c b/roms/ipxe/src/core/uart.c new file mode 100644 index 000000000..b85fe0767 --- /dev/null +++ b/roms/ipxe/src/core/uart.c @@ -0,0 +1,153 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * 16550-compatible UART + * + */ + +#include <unistd.h> +#include <errno.h> +#include <ipxe/uart.h> + +/** Timeout for transmit holding register to become empty */ +#define UART_THRE_TIMEOUT_MS 100 + +/** Timeout for transmitter to become empty */ +#define UART_TEMT_TIMEOUT_MS 1000 + +/** + * Transmit data + * + * @v uart UART + * @v data Data + */ +void uart_transmit ( struct uart *uart, uint8_t data ) { + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter holding register to become empty */ + for ( i = 0 ; i < UART_THRE_TIMEOUT_MS ; i++ ) { + lsr = uart_read ( uart, UART_LSR ); + if ( lsr & UART_LSR_THRE ) + break; + mdelay ( 1 ); + } + + /* Transmit data (even if we timed out) */ + uart_write ( uart, UART_THR, data ); +} + +/** + * Flush data + * + * @v uart UART + */ +void uart_flush ( struct uart *uart ) { + unsigned int i; + uint8_t lsr; + + /* Wait for transmitter and receiver to become empty */ + for ( i = 0 ; i < UART_TEMT_TIMEOUT_MS ; i++ ) { + uart_read ( uart, UART_RBR ); + lsr = uart_read ( uart, UART_LSR ); + if ( ( lsr & UART_LSR_TEMT ) && ! ( lsr & UART_LSR_DR ) ) + break; + } +} + +/** + * Check for existence of UART + * + * @v uart UART + * @ret rc Return status code + */ +int uart_exists ( struct uart *uart ) { + + /* Fail if no UART port is defined */ + if ( ! uart->base ) + return -ENODEV; + + /* Fail if UART scratch register seems not to be present */ + uart_write ( uart, UART_SCR, 0x18 ); + if ( uart_read ( uart, UART_SCR ) != 0x18 ) + return -ENODEV; + uart_write ( uart, UART_SCR, 0xae ); + if ( uart_read ( uart, UART_SCR ) != 0xae ) + return -ENODEV; + + return 0; +} + +/** + * Initialise UART + * + * @v uart UART + * @v baud Baud rate, or zero to leave unchanged + * @v lcr Line control register value, or zero to leave unchanged + * @ret rc Return status code + */ +int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ) { + uint8_t dlm; + uint8_t dll; + int rc; + + /* Check for existence of UART */ + if ( ( rc = uart_exists ( uart ) ) != 0 ) + return rc; + + /* Configure divisor and line control register, if applicable */ + if ( ! lcr ) + lcr = uart_read ( uart, UART_LCR ); + uart->lcr = lcr; + uart_write ( uart, UART_LCR, ( lcr | UART_LCR_DLAB ) ); + if ( baud ) { + uart->divisor = ( UART_MAX_BAUD / baud ); + dlm = ( ( uart->divisor >> 8 ) & 0xff ); + dll = ( ( uart->divisor >> 0 ) & 0xff ); + uart_write ( uart, UART_DLM, dlm ); + uart_write ( uart, UART_DLL, dll ); + } else { + dlm = uart_read ( uart, UART_DLM ); + dll = uart_read ( uart, UART_DLL ); + uart->divisor = ( ( dlm << 8 ) | dll ); + } + uart_write ( uart, UART_LCR, ( lcr & ~UART_LCR_DLAB ) ); + + /* Disable interrupts */ + uart_write ( uart, UART_IER, 0 ); + + /* Enable FIFOs */ + uart_write ( uart, UART_FCR, UART_FCR_FE ); + + /* Assert DTR and RTS */ + uart_write ( uart, UART_MCR, ( UART_MCR_DTR | UART_MCR_RTS ) ); + + /* Flush any stale data */ + uart_flush ( uart ); + + return 0; +} diff --git a/roms/ipxe/src/core/uri.c b/roms/ipxe/src/core/uri.c index 9ec21cee4..3b5f270fe 100644 --- a/roms/ipxe/src/core/uri.c +++ b/roms/ipxe/src/core/uri.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -661,6 +665,7 @@ struct uri * resolve_uri ( const struct uri *base_uri, * Construct TFTP URI from next-server and filename * * @v next_server Next-server address + * @v port Port number, or zero to use the default port * @v filename Filename * @ret uri URI, or NULL on failure * @@ -669,12 +674,18 @@ struct uri * resolve_uri ( const struct uri *base_uri, * generic URI parser. We provide a mechanism for directly * constructing a TFTP URI from the next-server and filename. */ -struct uri * tftp_uri ( struct in_addr next_server, const char *filename ) { +struct uri * tftp_uri ( struct in_addr next_server, unsigned int port, + const char *filename ) { + char buf[ 6 /* "65535" + NUL */ ]; struct uri uri; memset ( &uri, 0, sizeof ( uri ) ); uri.scheme = "tftp"; uri.host = inet_ntoa ( next_server ); + if ( port ) { + snprintf ( buf, sizeof ( buf ), "%d", port ); + uri.port = buf; + } uri.path = filename; return uri_dup ( &uri ); } diff --git a/roms/ipxe/src/core/uuid.c b/roms/ipxe/src/core/uuid.c index 27a249da8..b8d21de17 100644 --- a/roms/ipxe/src/core/uuid.c +++ b/roms/ipxe/src/core/uuid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/core/version.c b/roms/ipxe/src/core/version.c index 1e1e9daca..c984335c2 100644 --- a/roms/ipxe/src/core/version.c +++ b/roms/ipxe/src/core/version.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -29,6 +33,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/features.h> #include <ipxe/version.h> #include <config/general.h> +#include <config/branding.h> /** * Create wide-character version of string diff --git a/roms/ipxe/src/core/vsprintf.c b/roms/ipxe/src/core/vsprintf.c index 54811b11b..cb3bec5dd 100644 --- a/roms/ipxe/src/core/vsprintf.c +++ b/roms/ipxe/src/core/vsprintf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdarg.h> diff --git a/roms/ipxe/src/core/wchar.c b/roms/ipxe/src/core/wchar.c index 7fabca470..860322820 100644 --- a/roms/ipxe/src/core/wchar.c +++ b/roms/ipxe/src/core/wchar.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/core/xfer.c b/roms/ipxe/src/core/xfer.c index 8d4bc9f53..112fee1bf 100644 --- a/roms/ipxe/src/core/xfer.c +++ b/roms/ipxe/src/core/xfer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdlib.h> @@ -134,18 +138,8 @@ size_t xfer_window ( struct interface *intf ) { * generating an xfer_window_changed() message. */ void xfer_window_changed ( struct interface *intf ) { - struct interface *dest; - xfer_window_changed_TYPE ( void * ) *op = - intf_get_dest_op ( intf, xfer_window_changed, &dest ); - void *object = intf_object ( dest ); - if ( op ) { - op ( object ); - } else { - /* Default is to do nothing */ - } - - intf_put ( dest ); + intf_poke ( intf, xfer_window_changed ); } /** @@ -365,3 +359,34 @@ int xfer_seek ( struct interface *intf, off_t offset ) { return xfer_deliver ( intf, iobuf, &meta ); } + +/** + * Check that data is delivered strictly in order + * + * @v meta Data transfer metadata + * @v pos Current position + * @v len Length of data + * @ret rc Return status code + */ +int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, size_t len ) { + size_t new_pos; + + /* Allow out-of-order zero-length packets (as used by xfer_seek()) */ + if ( len == 0 ) + return 0; + + /* Calculate position of this delivery */ + new_pos = *pos; + if ( meta->flags & XFER_FL_ABS_OFFSET ) + new_pos = 0; + new_pos += meta->offset; + + /* Fail if delivery position is not equal to current position */ + if ( new_pos != *pos ) + return -EPROTO; + + /* Update current position */ + *pos += len; + + return 0; +} diff --git a/roms/ipxe/src/core/xferbuf.c b/roms/ipxe/src/core/xferbuf.c index a0457feee..240118557 100644 --- a/roms/ipxe/src/core/xferbuf.c +++ b/roms/ipxe/src/core/xferbuf.c @@ -15,15 +15,21 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> #include <errno.h> #include <ipxe/xfer.h> #include <ipxe/iobuf.h> +#include <ipxe/umalloc.h> +#include <ipxe/profile.h> #include <ipxe/xferbuf.h> /** @file @@ -32,14 +38,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/** Data delivery profiler */ +static struct profiler xferbuf_deliver_profiler __profiler = + { .name = "xferbuf.deliver" }; + +/** Data write profiler */ +static struct profiler xferbuf_write_profiler __profiler = + { .name = "xferbuf.write" }; + +/** Data read profiler */ +static struct profiler xferbuf_read_profiler __profiler = + { .name = "xferbuf.read" }; + /** - * Finish using data transfer buffer + * Free data transfer buffer * * @v xferbuf Data transfer buffer */ -void xferbuf_done ( struct xfer_buffer *xferbuf ) { - free ( xferbuf->data ); - xferbuf->data = NULL; +void xferbuf_free ( struct xfer_buffer *xferbuf ) { + + xferbuf->op->realloc ( xferbuf, 0 ); xferbuf->len = 0; xferbuf->pos = 0; } @@ -52,26 +70,78 @@ void xferbuf_done ( struct xfer_buffer *xferbuf ) { * @ret rc Return status code */ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { - void *new_data; + int rc; /* If buffer is already large enough, do nothing */ if ( len <= xferbuf->len ) return 0; /* Extend buffer */ - new_data = realloc ( xferbuf->data, len ); - if ( ! new_data ) { + if ( ( rc = xferbuf->op->realloc ( xferbuf, len ) ) != 0 ) { DBGC ( xferbuf, "XFERBUF %p could not extend buffer to " - "%zd bytes\n", xferbuf, len ); - return -ENOSPC; + "%zd bytes: %s\n", xferbuf, len, strerror ( rc ) ); + return rc; } - xferbuf->data = new_data; xferbuf->len = len; return 0; } /** + * Write to data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + size_t max_len; + int rc; + + /* Check for overflow */ + max_len = ( offset + len ); + if ( max_len < offset ) + return -EOVERFLOW; + + /* Ensure buffer is large enough to contain this write */ + if ( ( rc = xferbuf_ensure_size ( xferbuf, max_len ) ) != 0 ) + return rc; + + /* Copy data to buffer */ + profile_start ( &xferbuf_write_profiler ); + xferbuf->op->write ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_write_profiler ); + + return 0; +} + +/** + * Read from data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + */ +int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + /* Check that read is within buffer range */ + if ( ( offset > xferbuf->len ) || + ( len > ( xferbuf->len - offset ) ) ) + return -ENOENT; + + /* Copy data from buffer */ + profile_start ( &xferbuf_read_profiler ); + xferbuf->op->read ( xferbuf, offset, data, len ); + profile_stop ( &xferbuf_read_profiler ); + + return 0; +} + +/** * Add received data to data transfer buffer * * @v xferbuf Data transfer buffer @@ -81,28 +151,174 @@ static int xferbuf_ensure_size ( struct xfer_buffer *xferbuf, size_t len ) { */ int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ) { - size_t len; - size_t max; + size_t len = iob_len ( iobuf ); + size_t pos; int rc; + /* Start profiling */ + profile_start ( &xferbuf_deliver_profiler ); + /* Calculate new buffer position */ + pos = xferbuf->pos; if ( meta->flags & XFER_FL_ABS_OFFSET ) - xferbuf->pos = 0; - xferbuf->pos += meta->offset; + pos = 0; + pos += meta->offset; - /* Ensure that we have enough buffer space for this data */ - len = iob_len ( iobuf ); - max = ( xferbuf->pos + len ); - if ( ( rc = xferbuf_ensure_size ( xferbuf, max ) ) != 0 ) + /* Write data to buffer */ + if ( ( rc = xferbuf_write ( xferbuf, pos, iobuf->data, len ) ) != 0 ) goto done; - /* Copy data to buffer */ - memcpy ( ( xferbuf->data + xferbuf->pos ), iobuf->data, len ); - /* Update current buffer position */ - xferbuf->pos += len; + xferbuf->pos = ( pos + len ); done: free_iob ( iobuf ); + profile_stop ( &xferbuf_deliver_profiler ); return rc; } + +/** + * Reallocate malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_malloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + void *new_data; + + new_data = realloc ( xferbuf->data, len ); + if ( ! new_data ) + return -ENOSPC; + xferbuf->data = new_data; + return 0; +} + +/** + * Write data to malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_malloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + + memcpy ( ( xferbuf->data + offset ), data, len ); +} + +/** + * Read data from malloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_malloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + + memcpy ( data, ( xferbuf->data + offset ), len ); +} + +/** malloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_malloc_operations = { + .realloc = xferbuf_malloc_realloc, + .write = xferbuf_malloc_write, + .read = xferbuf_malloc_read, +}; + +/** + * Reallocate umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ +static int xferbuf_umalloc_realloc ( struct xfer_buffer *xferbuf, size_t len ) { + userptr_t *udata = xferbuf->data; + userptr_t new_udata; + + new_udata = urealloc ( *udata, len ); + if ( ! new_udata ) + return -ENOSPC; + *udata = new_udata; + return 0; +} + +/** + * Write data to umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to copy + * @v len Length of data + */ +static void xferbuf_umalloc_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_to_user ( *udata, offset, data, len ); +} + +/** + * Read data from umalloc()-based data buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + */ +static void xferbuf_umalloc_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ) { + userptr_t *udata = xferbuf->data; + + copy_from_user ( data, *udata, offset, len ); +} + +/** umalloc()-based data buffer operations */ +struct xfer_buffer_operations xferbuf_umalloc_operations = { + .realloc = xferbuf_umalloc_realloc, + .write = xferbuf_umalloc_write, + .read = xferbuf_umalloc_read, +}; + +/** + * Get underlying data transfer buffer + * + * @v interface Data transfer interface + * @ret xferbuf Data transfer buffer, or NULL on error + * + * This call will check that the xfer_buffer() handler belongs to the + * destination interface which also provides xfer_deliver() for this + * interface. + * + * This is done to prevent accidental accesses to a data transfer + * buffer which may be located behind a non-transparent datapath via a + * series of pass-through interfaces. + */ +struct xfer_buffer * xfer_buffer ( struct interface *intf ) { + struct interface *dest; + xfer_buffer_TYPE ( void * ) *op = + intf_get_dest_op ( intf, xfer_buffer, &dest ); + void *object = intf_object ( dest ); + struct interface *xfer_deliver_dest; + struct xfer_buffer *xferbuf; + + /* Check that this operation is provided by the same interface + * which handles xfer_deliver(). + */ + ( void ) intf_get_dest_op ( intf, xfer_deliver, &xfer_deliver_dest ); + + if ( op && ( dest == xfer_deliver_dest ) ) { + xferbuf = op ( object ); + } else { + /* Default is to not have a data transfer buffer */ + xferbuf = NULL; + } + + intf_put ( xfer_deliver_dest ); + intf_put ( dest ); + return xferbuf; +} diff --git a/roms/ipxe/src/crypto/aes.c b/roms/ipxe/src/crypto/aes.c new file mode 100644 index 000000000..b9e206bfb --- /dev/null +++ b/roms/ipxe/src/crypto/aes.c @@ -0,0 +1,808 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * AES algorithm + * + */ + +#include <stdint.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/rotate.h> +#include <ipxe/crypto.h> +#include <ipxe/ecb.h> +#include <ipxe/cbc.h> +#include <ipxe/aes.h> + +/** AES strides + * + * These are the strides (modulo 16) used to walk through the AES + * input state bytes in order of byte position after [Inv]ShiftRows. + */ +enum aes_stride { + /** Input stride for ShiftRows + * + * 0 4 8 c + * \ \ \ + * 1 5 9 d + * \ \ \ + * 2 6 a e + * \ \ \ + * 3 7 b f + */ + AES_STRIDE_SHIFTROWS = +5, + /** Input stride for InvShiftRows + * + * 0 4 8 c + * / / / + * 1 5 9 d + * / / / + * 2 6 a e + * / / / + * 3 7 b f + */ + AES_STRIDE_INVSHIFTROWS = -3, +}; + +/** A single AES lookup table entry + * + * This represents the product (in the Galois field GF(2^8)) of an + * eight-byte vector multiplier with a single scalar multiplicand. + * + * The vector multipliers used for AES will be {1,1,1,3,2,1,1,3} for + * MixColumns and {1,9,13,11,14,9,13,11} for InvMixColumns. This + * allows for the result of multiplying any single column of the + * [Inv]MixColumns matrix by a scalar value to be obtained simply by + * extracting the relevant four-byte subset from the lookup table + * entry. + * + * For example, to find the result of multiplying the second column of + * the MixColumns matrix by the scalar value 0x80: + * + * MixColumns column[0]: { 2, 1, 1, 3 } + * MixColumns column[1]: { 3, 2, 1, 1 } + * MixColumns column[2]: { 1, 3, 2, 1 } + * MixColumns column[3]: { 1, 1, 3, 2 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Scalar multiplicand: 0x80 + * Lookup table entry: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * + * The second column of the MixColumns matrix is {3,2,1,1}. The + * product of this column with the scalar value 0x80 can be obtained + * by extracting the relevant four-byte subset of the lookup table + * entry: + * + * MixColumns column[1]: { 3, 2, 1, 1 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Lookup table entry: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * Product: { 0x9b, 0x1b, 0x80, 0x80 } + * + * The column lookups require only seven bytes of the eight-byte + * entry: the remaining (first) byte is used to hold the scalar + * multiplicand itself (i.e. the first byte of the vector multiplier + * is always chosen to be 1). + */ +union aes_table_entry { + /** Viewed as an array of bytes */ + uint8_t byte[8]; +} __attribute__ (( packed )); + +/** An AES lookup table + * + * This represents the products (in the Galois field GF(2^8)) of a + * constant eight-byte vector multiplier with all possible 256 scalar + * multiplicands. + * + * The entries are indexed by the AES [Inv]SubBytes S-box output + * values (denoted S(N)). This allows for the result of multiplying + * any single column of the [Inv]MixColumns matrix by S(N) to be + * obtained simply by extracting the relevant four-byte subset from + * the Nth table entry. For example: + * + * Input byte (N): 0x3a + * SubBytes output S(N): 0x80 + * MixColumns column[1]: { 3, 2, 1, 1 } + * Vector multiplier: { 1, 1, 1, 3, 2, 1, 1, 3 } + * Table entry[0x3a]: { 0x80, 0x80, 0x80, 0x9b, 0x1b, 0x80, 0x80, 0x9b } + * Product: { 0x9b, 0x1b, 0x80, 0x80 } + * + * Since the first byte of the eight-byte vector multiplier is always + * chosen to be 1, the value of S(N) may be lookup up by extracting + * the first byte of the Nth table entry. + */ +struct aes_table { + /** Table entries, indexed by S(N) */ + union aes_table_entry entry[256]; +} __attribute__ (( aligned ( 8 ) )); + +/** AES MixColumns lookup table */ +static struct aes_table aes_mixcolumns; + +/** AES InvMixColumns lookup table */ +static struct aes_table aes_invmixcolumns; + +/** + * Multiply [Inv]MixColumns matrix column by scalar multiplicand + * + * @v entry AES lookup table entry for scalar multiplicand + * @v column [Inv]MixColumns matrix column index + * @ret product Product of matrix column with scalar multiplicand + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_entry_column ( const union aes_table_entry *entry, unsigned int column ) { + const union { + uint8_t byte; + uint32_t column; + } __attribute__ (( may_alias )) *product; + + /* Locate relevant four-byte subset */ + product = container_of ( &entry->byte[ 4 - column ], + typeof ( *product ), byte ); + + /* Extract this four-byte subset */ + return product->column; +} + +/** + * Multiply [Inv]MixColumns matrix column by S-boxed input byte + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v offset Output byte offset (after [Inv]ShiftRows) + * @ret product Product of matrix column with S(input byte) + * + * Note that the specified offset is not the offset of the input byte; + * it is the offset of the output byte which corresponds to the input + * byte. This output byte offset is used to calculate both the input + * byte offset and to select the appropriate matric column. + * + * With a compile-time constant offset, this function will optimise + * down to a single "movzbl" (to extract the input byte) and will + * generate a single x86 memory reference expression which can then be + * used directly within a single "xorl" instruction. + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_column ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, size_t offset ) { + const union aes_table_entry *entry; + unsigned int byte; + + /* Extract input byte corresponding to this output byte offset + * (i.e. perform [Inv]ShiftRows). + */ + byte = in->byte[ ( stride * offset ) & 0xf ]; + + /* Locate lookup table entry for this input byte (i.e. perform + * [Inv]SubBytes). + */ + entry = &table->entry[byte]; + + /* Multiply appropriate matrix column by this input byte + * (i.e. perform [Inv]MixColumns). + */ + return aes_entry_column ( entry, ( offset & 0x3 ) ); +} + +/** + * Calculate intermediate round output column + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v key AES round key + * @v column Column index + * @ret output Output column value + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_output ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, const union aes_matrix *key, + unsigned int column ) { + size_t offset = ( column * 4 ); + + /* Perform [Inv]ShiftRows, [Inv]SubBytes, [Inv]MixColumns, and + * AddRoundKey for this column. The loop is unrolled to allow + * for the required compile-time constant optimisations. + */ + return ( aes_column ( table, stride, in, ( offset + 0 ) ) ^ + aes_column ( table, stride, in, ( offset + 1 ) ) ^ + aes_column ( table, stride, in, ( offset + 2 ) ) ^ + aes_column ( table, stride, in, ( offset + 3 ) ) ^ + key->column[column] ); +} + +/** + * Perform a single intermediate round + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v out AES output state + * @v key AES round key + */ +static inline __attribute__ (( always_inline )) void +aes_round ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key ) { + + /* Perform [Inv]ShiftRows, [Inv]SubBytes, [Inv]MixColumns, and + * AddRoundKey for all columns. The loop is unrolled to allow + * for the required compile-time constant optimisations. + */ + out->column[0] = aes_output ( table, stride, in, key, 0 ); + out->column[1] = aes_output ( table, stride, in, key, 1 ); + out->column[2] = aes_output ( table, stride, in, key, 2 ); + out->column[3] = aes_output ( table, stride, in, key, 3 ); +} + +/** + * Perform encryption intermediate rounds + * + * @v in AES input state + * @v out AES output state + * @v key Round keys + * @v rounds Number of rounds (must be odd) + * + * This function is deliberately marked as non-inlinable to ensure + * maximal availability of registers for GCC's register allocator, + * which has a tendency to otherwise spill performance-critical + * registers to the stack. + */ +static __attribute__ (( noinline )) void +aes_encrypt_rounds ( union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key, unsigned int rounds ) { + union aes_matrix *tmp; + + /* Perform intermediate rounds */ + do { + /* Perform one intermediate round */ + aes_round ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, + in, out, key++ ); + + /* Swap input and output states for next round */ + tmp = in; + in = out; + out = tmp; + + } while ( --rounds ); +} + +/** + * Perform decryption intermediate rounds + * + * @v in AES input state + * @v out AES output state + * @v key Round keys + * @v rounds Number of rounds (must be odd) + * + * As with aes_encrypt_rounds(), this function is deliberately marked + * as non-inlinable. + * + * This function could potentially use the same binary code as is used + * for encryption. To compensate for the difference between ShiftRows + * and InvShiftRows, half of the input byte offsets would have to be + * modifiable at runtime (half by an offset of +4/-4, half by an + * offset of -4/+4 for ShiftRows/InvShiftRows). This can be + * accomplished in x86 assembly within the number of available + * registers, but GCC's register allocator struggles to do so, + * resulting in a significant performance decrease due to registers + * being spilled to the stack. We therefore use two separate but very + * similar binary functions based on the same C source. + */ +static __attribute__ (( noinline )) void +aes_decrypt_rounds ( union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key, unsigned int rounds ) { + union aes_matrix *tmp; + + /* Perform intermediate rounds */ + do { + /* Perform one intermediate round */ + aes_round ( &aes_invmixcolumns, AES_STRIDE_INVSHIFTROWS, + in, out, key++ ); + + /* Swap input and output states for next round */ + tmp = in; + in = out; + out = tmp; + + } while ( --rounds ); +} + +/** + * Perform standalone AddRoundKey + * + * @v state AES state + * @v key AES round key + */ +static inline __attribute__ (( always_inline )) void +aes_addroundkey ( union aes_matrix *state, const union aes_matrix *key ) { + + state->column[0] ^= key->column[0]; + state->column[1] ^= key->column[1]; + state->column[2] ^= key->column[2]; + state->column[3] ^= key->column[3]; +} + +/** + * Perform final round + * + * @v table AES lookup table + * @v stride AES row shift stride + * @v in AES input state + * @v out AES output state + * @v key AES round key + */ +static void aes_final ( const struct aes_table *table, size_t stride, + const union aes_matrix *in, union aes_matrix *out, + const union aes_matrix *key ) { + const union aes_table_entry *entry; + unsigned int byte; + size_t out_offset; + size_t in_offset; + + /* Perform [Inv]ShiftRows and [Inv]SubBytes */ + for ( out_offset = 0, in_offset = 0 ; out_offset < 16 ; + out_offset++, in_offset = ( ( in_offset + stride ) & 0xf ) ) { + + /* Extract input byte (i.e. perform [Inv]ShiftRows) */ + byte = in->byte[in_offset]; + + /* Locate lookup table entry for this input byte + * (i.e. perform [Inv]SubBytes). + */ + entry = &table->entry[byte]; + + /* Store output byte */ + out->byte[out_offset] = entry->byte[0]; + } + + /* Perform AddRoundKey */ + aes_addroundkey ( out, key ); +} + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + */ +static void aes_encrypt ( void *ctx, const void *src, void *dst, size_t len ) { + struct aes_context *aes = ctx; + union aes_matrix buffer[2]; + union aes_matrix *in = &buffer[0]; + union aes_matrix *out = &buffer[1]; + unsigned int rounds = aes->rounds; + + /* Sanity check */ + assert ( len == sizeof ( *in ) ); + + /* Initialise input state */ + memcpy ( in, src, sizeof ( *in ) ); + + /* Perform initial round (AddRoundKey) */ + aes_addroundkey ( in, &aes->encrypt.key[0] ); + + /* Perform intermediate rounds (ShiftRows, SubBytes, + * MixColumns, AddRoundKey). + */ + aes_encrypt_rounds ( in, out, &aes->encrypt.key[1], ( rounds - 2 ) ); + in = out; + + /* Perform final round (ShiftRows, SubBytes, AddRoundKey) */ + out = dst; + aes_final ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, in, out, + &aes->encrypt.key[ rounds - 1 ] ); +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + */ +static void aes_decrypt ( void *ctx, const void *src, void *dst, size_t len ) { + struct aes_context *aes = ctx; + union aes_matrix buffer[2]; + union aes_matrix *in = &buffer[0]; + union aes_matrix *out = &buffer[1]; + unsigned int rounds = aes->rounds; + + /* Sanity check */ + assert ( len == sizeof ( *in ) ); + + /* Initialise input state */ + memcpy ( in, src, sizeof ( *in ) ); + + /* Perform initial round (AddRoundKey) */ + aes_addroundkey ( in, &aes->decrypt.key[0] ); + + /* Perform intermediate rounds (InvShiftRows, InvSubBytes, + * InvMixColumns, AddRoundKey). + */ + aes_decrypt_rounds ( in, out, &aes->decrypt.key[1], ( rounds - 2 ) ); + in = out; + + /* Perform final round (InvShiftRows, InvSubBytes, AddRoundKey) */ + out = dst; + aes_final ( &aes_invmixcolumns, AES_STRIDE_INVSHIFTROWS, in, out, + &aes->decrypt.key[ rounds - 1 ] ); +} + +/** + * Multiply a polynomial by (x) modulo (x^8 + x^4 + x^3 + x^2 + 1) in GF(2^8) + * + * @v poly Polynomial to be multiplied + * @ret result Result + */ +static __attribute__ (( const )) unsigned int aes_double ( unsigned int poly ) { + + /* Multiply polynomial by (x), placing the resulting x^8 + * coefficient in the LSB (i.e. rotate byte left by one). + */ + poly = rol8 ( poly, 1 ); + + /* If coefficient of x^8 (in LSB) is non-zero, then reduce by + * subtracting (x^8 + x^4 + x^3 + x^2 + 1) in GF(2^8). + */ + if ( poly & 0x01 ) { + poly ^= 0x01; /* Subtract x^8 (currently in LSB) */ + poly ^= 0x1b; /* Subtract (x^4 + x^3 + x^2 + 1) */ + } + + return poly; +} + +/** + * Fill in MixColumns lookup table entry + * + * @v entry AES lookup table entry for scalar multiplicand + * + * The MixColumns lookup table vector multiplier is {1,1,1,3,2,1,1,3}. + */ +static void aes_mixcolumns_entry ( union aes_table_entry *entry ) { + unsigned int scalar_x_1; + unsigned int scalar_x; + unsigned int scalar; + + /* Retrieve scalar multiplicand */ + scalar = entry->byte[0]; + entry->byte[1] = scalar; + entry->byte[2] = scalar; + entry->byte[5] = scalar; + entry->byte[6] = scalar; + + /* Calculate scalar multiplied by (x) */ + scalar_x = aes_double ( scalar ); + entry->byte[4] = scalar_x; + + /* Calculate scalar multiplied by (x + 1) */ + scalar_x_1 = ( scalar_x ^ scalar ); + entry->byte[3] = scalar_x_1; + entry->byte[7] = scalar_x_1; +} + +/** + * Fill in InvMixColumns lookup table entry + * + * @v entry AES lookup table entry for scalar multiplicand + * + * The InvMixColumns lookup table vector multiplier is {1,9,13,11,14,9,13,11}. + */ +static void aes_invmixcolumns_entry ( union aes_table_entry *entry ) { + unsigned int scalar_x3_x2_x; + unsigned int scalar_x3_x2_1; + unsigned int scalar_x3_x2; + unsigned int scalar_x3_x_1; + unsigned int scalar_x3_1; + unsigned int scalar_x3; + unsigned int scalar_x2; + unsigned int scalar_x; + unsigned int scalar; + + /* Retrieve scalar multiplicand */ + scalar = entry->byte[0]; + + /* Calculate scalar multiplied by (x) */ + scalar_x = aes_double ( scalar ); + + /* Calculate scalar multiplied by (x^2) */ + scalar_x2 = aes_double ( scalar_x ); + + /* Calculate scalar multiplied by (x^3) */ + scalar_x3 = aes_double ( scalar_x2 ); + + /* Calculate scalar multiplied by (x^3 + 1) */ + scalar_x3_1 = ( scalar_x3 ^ scalar ); + entry->byte[1] = scalar_x3_1; + entry->byte[5] = scalar_x3_1; + + /* Calculate scalar multiplied by (x^3 + x + 1) */ + scalar_x3_x_1 = ( scalar_x3_1 ^ scalar_x ); + entry->byte[3] = scalar_x3_x_1; + entry->byte[7] = scalar_x3_x_1; + + /* Calculate scalar multiplied by (x^3 + x^2) */ + scalar_x3_x2 = ( scalar_x3 ^ scalar_x2 ); + + /* Calculate scalar multiplied by (x^3 + x^2 + 1) */ + scalar_x3_x2_1 = ( scalar_x3_x2 ^ scalar ); + entry->byte[2] = scalar_x3_x2_1; + entry->byte[6] = scalar_x3_x2_1; + + /* Calculate scalar multiplied by (x^3 + x^2 + x) */ + scalar_x3_x2_x = ( scalar_x3_x2 ^ scalar_x ); + entry->byte[4] = scalar_x3_x2_x; +} + +/** + * Generate AES lookup tables + * + */ +static void aes_generate ( void ) { + union aes_table_entry *entry; + union aes_table_entry *inventry; + unsigned int poly = 0x01; + unsigned int invpoly = 0x01; + unsigned int transformed; + unsigned int i; + + /* Iterate over non-zero values of GF(2^8) using generator (x + 1) */ + do { + + /* Multiply polynomial by (x + 1) */ + poly ^= aes_double ( poly ); + + /* Divide inverse polynomial by (x + 1). This code + * fragment is taken directly from the Wikipedia page + * on the Rijndael S-box. An explanation of why it + * works would be greatly appreciated. + */ + invpoly ^= ( invpoly << 1 ); + invpoly ^= ( invpoly << 2 ); + invpoly ^= ( invpoly << 4 ); + if ( invpoly & 0x80 ) + invpoly ^= 0x09; + invpoly &= 0xff; + + /* Apply affine transformation */ + transformed = ( 0x63 ^ invpoly ^ rol8 ( invpoly, 1 ) ^ + rol8 ( invpoly, 2 ) ^ rol8 ( invpoly, 3 ) ^ + rol8 ( invpoly, 4 ) ); + + /* Populate S-box (within MixColumns lookup table) */ + aes_mixcolumns.entry[poly].byte[0] = transformed; + + } while ( poly != 0x01 ); + + /* Populate zeroth S-box entry (which has no inverse) */ + aes_mixcolumns.entry[0].byte[0] = 0x63; + + /* Fill in MixColumns and InvMixColumns lookup tables */ + for ( i = 0 ; i < 256 ; i++ ) { + + /* Fill in MixColumns lookup table entry */ + entry = &aes_mixcolumns.entry[i]; + aes_mixcolumns_entry ( entry ); + + /* Populate inverse S-box (within InvMixColumns lookup table) */ + inventry = &aes_invmixcolumns.entry[ entry->byte[0] ]; + inventry->byte[0] = i; + + /* Fill in InvMixColumns lookup table entry */ + aes_invmixcolumns_entry ( inventry ); + } +} + +/** + * Rotate key column + * + * @v column Key column + * @ret column Updated key column + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_key_rotate ( uint32_t column ) { + + return ( ( __BYTE_ORDER == __LITTLE_ENDIAN ) ? + ror32 ( column, 8 ) : rol32 ( column, 8 ) ); +} + +/** + * Apply S-box to key column + * + * @v column Key column + * @ret column Updated key column + */ +static uint32_t aes_key_sbox ( uint32_t column ) { + unsigned int i; + uint8_t byte; + + for ( i = 0 ; i < 4 ; i++ ) { + byte = ( column & 0xff ); + byte = aes_mixcolumns.entry[byte].byte[0]; + column = ( ( column & ~0xff ) | byte ); + column = rol32 ( column, 8 ); + } + return column; +} + +/** + * Apply schedule round constant to key column + * + * @v column Key column + * @v rcon Round constant + * @ret column Updated key column + */ +static inline __attribute__ (( always_inline )) uint32_t +aes_key_rcon ( uint32_t column, unsigned int rcon ) { + + return ( ( __BYTE_ORDER == __LITTLE_ENDIAN ) ? + ( column ^ rcon ) : ( column ^ ( rcon << 24 ) ) ); +} + +/** + * Set key + * + * @v ctx Context + * @v key Key + * @v keylen Key length + * @ret rc Return status code + */ +static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { + struct aes_context *aes = ctx; + union aes_matrix *enc; + union aes_matrix *dec; + union aes_matrix temp; + union aes_matrix zero; + unsigned int rcon = 0x01; + unsigned int rounds; + size_t offset = 0; + uint32_t *prev; + uint32_t *next; + uint32_t *end; + uint32_t tmp; + + /* Generate lookup tables, if not already done */ + if ( ! aes_mixcolumns.entry[0].byte[0] ) + aes_generate(); + + /* Validate key length and calculate number of intermediate rounds */ + switch ( keylen ) { + case ( 128 / 8 ) : + rounds = 11; + break; + case ( 192 / 8 ) : + rounds = 13; + break; + case ( 256 / 8 ) : + rounds = 15; + break; + default: + DBGC ( aes, "AES %p unsupported key length (%zd bits)\n", + aes, ( keylen * 8 ) ); + return -EINVAL; + } + aes->rounds = rounds; + enc = aes->encrypt.key; + end = enc[rounds].column; + + /* Copy raw key */ + memcpy ( enc, key, keylen ); + prev = enc->column; + next = ( ( ( void * ) prev ) + keylen ); + tmp = next[-1]; + + /* Construct expanded key */ + while ( next < end ) { + + /* If this is the first column of an expanded key + * block, or the middle column of an AES-256 key + * block, then apply the S-box. + */ + if ( ( offset == 0 ) || ( ( offset | keylen ) == 48 ) ) + tmp = aes_key_sbox ( tmp ); + + /* If this is the first column of an expanded key + * block then rotate and apply the round constant. + */ + if ( offset == 0 ) { + tmp = aes_key_rotate ( tmp ); + tmp = aes_key_rcon ( tmp, rcon ); + rcon = aes_double ( rcon ); + } + + /* XOR with previous key column */ + tmp ^= *prev; + + /* Store column */ + *next = tmp; + + /* Move to next column */ + offset += sizeof ( *next ); + if ( offset == keylen ) + offset = 0; + next++; + prev++; + } + DBGC2 ( aes, "AES %p expanded %zd-bit key:\n", aes, ( keylen * 8 ) ); + DBGC2_HDA ( aes, 0, &aes->encrypt, ( rounds * sizeof ( *enc ) ) ); + + /* Convert to decryption key */ + memset ( &zero, 0, sizeof ( zero ) ); + dec = &aes->decrypt.key[ rounds - 1 ]; + memcpy ( dec--, enc++, sizeof ( *dec ) ); + while ( dec > aes->decrypt.key ) { + /* Perform InvMixColumns (by reusing the encryption + * final-round code to perform ShiftRows+SubBytes and + * reusing the decryption intermediate-round code to + * perform InvShiftRows+InvSubBytes+InvMixColumns, all + * with a zero encryption key). + */ + aes_final ( &aes_mixcolumns, AES_STRIDE_SHIFTROWS, + enc++, &temp, &zero ); + aes_decrypt_rounds ( &temp, dec--, &zero, 1 ); + } + memcpy ( dec--, enc++, sizeof ( *dec ) ); + DBGC2 ( aes, "AES %p inverted %zd-bit key:\n", aes, ( keylen * 8 ) ); + DBGC2_HDA ( aes, 0, &aes->decrypt, ( rounds * sizeof ( *dec ) ) ); + + return 0; +} + +/** + * Set initialisation vector + * + * @v ctx Context + * @v iv Initialisation vector + */ +static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { + /* Nothing to do */ +} + +/** Basic AES algorithm */ +struct cipher_algorithm aes_algorithm = { + .name = "aes", + .ctxsize = sizeof ( struct aes_context ), + .blocksize = AES_BLOCKSIZE, + .setkey = aes_setkey, + .setiv = aes_setiv, + .encrypt = aes_encrypt, + .decrypt = aes_decrypt, +}; + +/* AES in Electronic Codebook mode */ +ECB_CIPHER ( aes_ecb, aes_ecb_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); + +/* AES in Cipher Block Chaining mode */ +CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, + aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/roms/ipxe/src/crypto/asn1.c b/roms/ipxe/src/crypto/asn1.c index 6d880704f..aca12bf30 100644 --- a/roms/ipxe/src/crypto/asn1.c +++ b/roms/ipxe/src/crypto/asn1.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> diff --git a/roms/ipxe/src/crypto/axtls/aes.c b/roms/ipxe/src/crypto/axtls/aes.c deleted file mode 100644 index bd99a7097..000000000 --- a/roms/ipxe/src/crypto/axtls/aes.c +++ /dev/null @@ -1,457 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * 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 axTLS project 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT OWNER OR - * CONTRIBUTORS 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. - */ - -/** - * AES implementation - this is a small code version. There are much faster - * versions around but they are much larger in size (i.e. they use large - * submix tables). - */ - -#include <string.h> -#include "os_port.h" -#include "crypto.h" - -/* all commented out in skeleton mode */ -#ifndef CONFIG_SSL_SKELETON_MODE - -#define rot1(x) (((x) << 24) | ((x) >> 8)) -#define rot2(x) (((x) << 16) | ((x) >> 16)) -#define rot3(x) (((x) << 8) | ((x) >> 24)) - -/* - * This cute trick does 4 'mul by two' at once. Stolen from - * Dr B. R. Gladman <brg@gladman.uk.net> but I'm sure the u-(u>>7) is - * a standard graphics trick - * The key to this is that we need to xor with 0x1b if the top bit is set. - * a 1xxx xxxx 0xxx 0xxx First we mask the 7bit, - * b 1000 0000 0000 0000 then we shift right by 7 putting the 7bit in 0bit, - * c 0000 0001 0000 0000 we then subtract (c) from (b) - * d 0111 1111 0000 0000 and now we and with our mask - * e 0001 1011 0000 0000 - */ -#define mt 0x80808080 -#define ml 0x7f7f7f7f -#define mh 0xfefefefe -#define mm 0x1b1b1b1b -#define mul2(x,t) ((t)=((x)&mt), \ - ((((x)+(x))&mh)^(((t)-((t)>>7))&mm))) - -#define inv_mix_col(x,f2,f4,f8,f9) (\ - (f2)=mul2(x,f2), \ - (f4)=mul2(f2,f4), \ - (f8)=mul2(f4,f8), \ - (f9)=(x)^(f8), \ - (f8)=((f2)^(f4)^(f8)), \ - (f2)^=(f9), \ - (f4)^=(f9), \ - (f8)^=rot3(f2), \ - (f8)^=rot2(f4), \ - (f8)^rot1(f9)) - -/* - * AES S-box - */ -static const uint8_t aes_sbox[256] = -{ - 0x63,0x7C,0x77,0x7B,0xF2,0x6B,0x6F,0xC5, - 0x30,0x01,0x67,0x2B,0xFE,0xD7,0xAB,0x76, - 0xCA,0x82,0xC9,0x7D,0xFA,0x59,0x47,0xF0, - 0xAD,0xD4,0xA2,0xAF,0x9C,0xA4,0x72,0xC0, - 0xB7,0xFD,0x93,0x26,0x36,0x3F,0xF7,0xCC, - 0x34,0xA5,0xE5,0xF1,0x71,0xD8,0x31,0x15, - 0x04,0xC7,0x23,0xC3,0x18,0x96,0x05,0x9A, - 0x07,0x12,0x80,0xE2,0xEB,0x27,0xB2,0x75, - 0x09,0x83,0x2C,0x1A,0x1B,0x6E,0x5A,0xA0, - 0x52,0x3B,0xD6,0xB3,0x29,0xE3,0x2F,0x84, - 0x53,0xD1,0x00,0xED,0x20,0xFC,0xB1,0x5B, - 0x6A,0xCB,0xBE,0x39,0x4A,0x4C,0x58,0xCF, - 0xD0,0xEF,0xAA,0xFB,0x43,0x4D,0x33,0x85, - 0x45,0xF9,0x02,0x7F,0x50,0x3C,0x9F,0xA8, - 0x51,0xA3,0x40,0x8F,0x92,0x9D,0x38,0xF5, - 0xBC,0xB6,0xDA,0x21,0x10,0xFF,0xF3,0xD2, - 0xCD,0x0C,0x13,0xEC,0x5F,0x97,0x44,0x17, - 0xC4,0xA7,0x7E,0x3D,0x64,0x5D,0x19,0x73, - 0x60,0x81,0x4F,0xDC,0x22,0x2A,0x90,0x88, - 0x46,0xEE,0xB8,0x14,0xDE,0x5E,0x0B,0xDB, - 0xE0,0x32,0x3A,0x0A,0x49,0x06,0x24,0x5C, - 0xC2,0xD3,0xAC,0x62,0x91,0x95,0xE4,0x79, - 0xE7,0xC8,0x37,0x6D,0x8D,0xD5,0x4E,0xA9, - 0x6C,0x56,0xF4,0xEA,0x65,0x7A,0xAE,0x08, - 0xBA,0x78,0x25,0x2E,0x1C,0xA6,0xB4,0xC6, - 0xE8,0xDD,0x74,0x1F,0x4B,0xBD,0x8B,0x8A, - 0x70,0x3E,0xB5,0x66,0x48,0x03,0xF6,0x0E, - 0x61,0x35,0x57,0xB9,0x86,0xC1,0x1D,0x9E, - 0xE1,0xF8,0x98,0x11,0x69,0xD9,0x8E,0x94, - 0x9B,0x1E,0x87,0xE9,0xCE,0x55,0x28,0xDF, - 0x8C,0xA1,0x89,0x0D,0xBF,0xE6,0x42,0x68, - 0x41,0x99,0x2D,0x0F,0xB0,0x54,0xBB,0x16, -}; - -/* - * AES is-box - */ -static const uint8_t aes_isbox[256] = -{ - 0x52,0x09,0x6a,0xd5,0x30,0x36,0xa5,0x38, - 0xbf,0x40,0xa3,0x9e,0x81,0xf3,0xd7,0xfb, - 0x7c,0xe3,0x39,0x82,0x9b,0x2f,0xff,0x87, - 0x34,0x8e,0x43,0x44,0xc4,0xde,0xe9,0xcb, - 0x54,0x7b,0x94,0x32,0xa6,0xc2,0x23,0x3d, - 0xee,0x4c,0x95,0x0b,0x42,0xfa,0xc3,0x4e, - 0x08,0x2e,0xa1,0x66,0x28,0xd9,0x24,0xb2, - 0x76,0x5b,0xa2,0x49,0x6d,0x8b,0xd1,0x25, - 0x72,0xf8,0xf6,0x64,0x86,0x68,0x98,0x16, - 0xd4,0xa4,0x5c,0xcc,0x5d,0x65,0xb6,0x92, - 0x6c,0x70,0x48,0x50,0xfd,0xed,0xb9,0xda, - 0x5e,0x15,0x46,0x57,0xa7,0x8d,0x9d,0x84, - 0x90,0xd8,0xab,0x00,0x8c,0xbc,0xd3,0x0a, - 0xf7,0xe4,0x58,0x05,0xb8,0xb3,0x45,0x06, - 0xd0,0x2c,0x1e,0x8f,0xca,0x3f,0x0f,0x02, - 0xc1,0xaf,0xbd,0x03,0x01,0x13,0x8a,0x6b, - 0x3a,0x91,0x11,0x41,0x4f,0x67,0xdc,0xea, - 0x97,0xf2,0xcf,0xce,0xf0,0xb4,0xe6,0x73, - 0x96,0xac,0x74,0x22,0xe7,0xad,0x35,0x85, - 0xe2,0xf9,0x37,0xe8,0x1c,0x75,0xdf,0x6e, - 0x47,0xf1,0x1a,0x71,0x1d,0x29,0xc5,0x89, - 0x6f,0xb7,0x62,0x0e,0xaa,0x18,0xbe,0x1b, - 0xfc,0x56,0x3e,0x4b,0xc6,0xd2,0x79,0x20, - 0x9a,0xdb,0xc0,0xfe,0x78,0xcd,0x5a,0xf4, - 0x1f,0xdd,0xa8,0x33,0x88,0x07,0xc7,0x31, - 0xb1,0x12,0x10,0x59,0x27,0x80,0xec,0x5f, - 0x60,0x51,0x7f,0xa9,0x19,0xb5,0x4a,0x0d, - 0x2d,0xe5,0x7a,0x9f,0x93,0xc9,0x9c,0xef, - 0xa0,0xe0,0x3b,0x4d,0xae,0x2a,0xf5,0xb0, - 0xc8,0xeb,0xbb,0x3c,0x83,0x53,0x99,0x61, - 0x17,0x2b,0x04,0x7e,0xba,0x77,0xd6,0x26, - 0xe1,0x69,0x14,0x63,0x55,0x21,0x0c,0x7d -}; - -static const unsigned char Rcon[30]= -{ - 0x01,0x02,0x04,0x08,0x10,0x20,0x40,0x80, - 0x1b,0x36,0x6c,0xd8,0xab,0x4d,0x9a,0x2f, - 0x5e,0xbc,0x63,0xc6,0x97,0x35,0x6a,0xd4, - 0xb3,0x7d,0xfa,0xef,0xc5,0x91, -}; - -/* ----- static functions ----- */ -static void AES_encrypt(const AES_CTX *ctx, uint32_t *data); -static void AES_decrypt(const AES_CTX *ctx, uint32_t *data); - -/* Perform doubling in Galois Field GF(2^8) using the irreducible polynomial - x^8+x^4+x^3+x+1 */ -static unsigned char AES_xtime(uint32_t x) -{ - return (x&0x80) ? (x<<1)^0x1b : x<<1; -} - -/** - * Set up AES with the key/iv and cipher size. - */ -void AES_set_key(AES_CTX *ctx, const uint8_t *key, - const uint8_t *iv, AES_MODE mode) -{ - int i, ii; - uint32_t *W, tmp, tmp2; - const unsigned char *ip; - int words; - - switch (mode) - { - case AES_MODE_128: - i = 10; - words = 4; - break; - - case AES_MODE_256: - i = 14; - words = 8; - break; - - default: /* fail silently */ - return; - } - - ctx->rounds = i; - ctx->key_size = words; - W = ctx->ks; - for (i = 0; i < words; i+=2) - { - W[i+0]= ((uint32_t)key[ 0]<<24)| - ((uint32_t)key[ 1]<<16)| - ((uint32_t)key[ 2]<< 8)| - ((uint32_t)key[ 3] ); - W[i+1]= ((uint32_t)key[ 4]<<24)| - ((uint32_t)key[ 5]<<16)| - ((uint32_t)key[ 6]<< 8)| - ((uint32_t)key[ 7] ); - key += 8; - } - - ip = Rcon; - ii = 4 * (ctx->rounds+1); - for (i = words; i<ii; i++) - { - tmp = W[i-1]; - - if ((i % words) == 0) - { - tmp2 =(uint32_t)aes_sbox[(tmp )&0xff]<< 8; - tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<<16; - tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<24; - tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]; - tmp=tmp2^(((unsigned int)*ip)<<24); - ip++; - } - - if ((words == 8) && ((i % words) == 4)) - { - tmp2 =(uint32_t)aes_sbox[(tmp )&0xff] ; - tmp2|=(uint32_t)aes_sbox[(tmp>> 8)&0xff]<< 8; - tmp2|=(uint32_t)aes_sbox[(tmp>>16)&0xff]<<16; - tmp2|=(uint32_t)aes_sbox[(tmp>>24) ]<<24; - tmp=tmp2; - } - - W[i]=W[i-words]^tmp; - } - - /* copy the iv across */ - memcpy(ctx->iv, iv, 16); -} - -/** - * Change a key for decryption. - */ -void AES_convert_key(AES_CTX *ctx) -{ - int i; - uint32_t *k,w,t1,t2,t3,t4; - - k = ctx->ks; - k += 4; - - for (i= ctx->rounds*4; i > 4; i--) - { - w= *k; - w = inv_mix_col(w,t1,t2,t3,t4); - *k++ =w; - } -} - -/** - * Encrypt a byte sequence (with a block size 16) using the AES cipher. - */ -void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) -{ - int i; - uint32_t tin[4], tout[4], iv[4]; - - memcpy(iv, ctx->iv, AES_IV_SIZE); - for (i = 0; i < 4; i++) - tout[i] = ntohl(iv[i]); - - for (length -= AES_BLOCKSIZE; length >= 0; length -= AES_BLOCKSIZE) - { - uint32_t msg_32[4]; - uint32_t out_32[4]; - memcpy(msg_32, msg, AES_BLOCKSIZE); - msg += AES_BLOCKSIZE; - - for (i = 0; i < 4; i++) - tin[i] = ntohl(msg_32[i])^tout[i]; - - AES_encrypt(ctx, tin); - - for (i = 0; i < 4; i++) - { - tout[i] = tin[i]; - out_32[i] = htonl(tout[i]); - } - - memcpy(out, out_32, AES_BLOCKSIZE); - out += AES_BLOCKSIZE; - } - - for (i = 0; i < 4; i++) - iv[i] = htonl(tout[i]); - memcpy(ctx->iv, iv, AES_IV_SIZE); -} - -/** - * Decrypt a byte sequence (with a block size 16) using the AES cipher. - */ -void AES_cbc_decrypt(AES_CTX *ctx, const uint8_t *msg, uint8_t *out, int length) -{ - int i; - uint32_t tin[4], xor[4], tout[4], data[4], iv[4]; - - memcpy(iv, ctx->iv, AES_IV_SIZE); - for (i = 0; i < 4; i++) - xor[i] = ntohl(iv[i]); - - for (length -= 16; length >= 0; length -= 16) - { - uint32_t msg_32[4]; - uint32_t out_32[4]; - memcpy(msg_32, msg, AES_BLOCKSIZE); - msg += AES_BLOCKSIZE; - - for (i = 0; i < 4; i++) - { - tin[i] = ntohl(msg_32[i]); - data[i] = tin[i]; - } - - AES_decrypt(ctx, data); - - for (i = 0; i < 4; i++) - { - tout[i] = data[i]^xor[i]; - xor[i] = tin[i]; - out_32[i] = htonl(tout[i]); - } - - memcpy(out, out_32, AES_BLOCKSIZE); - out += AES_BLOCKSIZE; - } - - for (i = 0; i < 4; i++) - iv[i] = htonl(xor[i]); - memcpy(ctx->iv, iv, AES_IV_SIZE); -} - -/** - * Encrypt a single block (16 bytes) of data - */ -static void AES_encrypt(const AES_CTX *ctx, uint32_t *data) -{ - /* To make this code smaller, generate the sbox entries on the fly. - * This will have a really heavy effect upon performance. - */ - uint32_t tmp[4]; - uint32_t tmp1, old_a0, a0, a1, a2, a3, row; - int curr_rnd; - int rounds = ctx->rounds; - const uint32_t *k = ctx->ks; - - /* Pre-round key addition */ - for (row = 0; row < 4; row++) - data[row] ^= *(k++); - - /* Encrypt one block. */ - for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) - { - /* Perform ByteSub and ShiftRow operations together */ - for (row = 0; row < 4; row++) - { - a0 = (uint32_t)aes_sbox[(data[row%4]>>24)&0xFF]; - a1 = (uint32_t)aes_sbox[(data[(row+1)%4]>>16)&0xFF]; - a2 = (uint32_t)aes_sbox[(data[(row+2)%4]>>8)&0xFF]; - a3 = (uint32_t)aes_sbox[(data[(row+3)%4])&0xFF]; - - /* Perform MixColumn iff not last round */ - if (curr_rnd < (rounds - 1)) - { - tmp1 = a0 ^ a1 ^ a2 ^ a3; - old_a0 = a0; - a0 ^= tmp1 ^ AES_xtime(a0 ^ a1); - a1 ^= tmp1 ^ AES_xtime(a1 ^ a2); - a2 ^= tmp1 ^ AES_xtime(a2 ^ a3); - a3 ^= tmp1 ^ AES_xtime(a3 ^ old_a0); - } - - tmp[row] = ((a0 << 24) | (a1 << 16) | (a2 << 8) | a3); - } - - /* KeyAddition - note that it is vital that this loop is separate from - the MixColumn operation, which must be atomic...*/ - for (row = 0; row < 4; row++) - data[row] = tmp[row] ^ *(k++); - } -} - -/** - * Decrypt a single block (16 bytes) of data - */ -static void AES_decrypt(const AES_CTX *ctx, uint32_t *data) -{ - uint32_t tmp[4]; - uint32_t xt0,xt1,xt2,xt3,xt4,xt5,xt6; - uint32_t a0, a1, a2, a3, row; - int curr_rnd; - int rounds = ctx->rounds; - const uint32_t *k = ctx->ks + ((rounds+1)*4); - - /* pre-round key addition */ - for (row=4; row > 0;row--) - data[row-1] ^= *(--k); - - /* Decrypt one block */ - for (curr_rnd = 0; curr_rnd < rounds; curr_rnd++) - { - /* Perform ByteSub and ShiftRow operations together */ - for (row = 4; row > 0; row--) - { - a0 = aes_isbox[(data[(row+3)%4]>>24)&0xFF]; - a1 = aes_isbox[(data[(row+2)%4]>>16)&0xFF]; - a2 = aes_isbox[(data[(row+1)%4]>>8)&0xFF]; - a3 = aes_isbox[(data[row%4])&0xFF]; - - /* Perform MixColumn iff not last round */ - if (curr_rnd<(rounds-1)) - { - /* The MDS cofefficients (0x09, 0x0B, 0x0D, 0x0E) - are quite large compared to encryption; this - operation slows decryption down noticeably. */ - xt0 = AES_xtime(a0^a1); - xt1 = AES_xtime(a1^a2); - xt2 = AES_xtime(a2^a3); - xt3 = AES_xtime(a3^a0); - xt4 = AES_xtime(xt0^xt1); - xt5 = AES_xtime(xt1^xt2); - xt6 = AES_xtime(xt4^xt5); - - xt0 ^= a1^a2^a3^xt4^xt6; - xt1 ^= a0^a2^a3^xt5^xt6; - xt2 ^= a0^a1^a3^xt4^xt6; - xt3 ^= a0^a1^a2^xt5^xt6; - tmp[row-1] = ((xt0<<24)|(xt1<<16)|(xt2<<8)|xt3); - } - else - tmp[row-1] = ((a0<<24)|(a1<<16)|(a2<<8)|a3); - } - - for (row = 4; row > 0; row--) - data[row-1] = tmp[row-1] ^ *(--k); - } -} - -#endif diff --git a/roms/ipxe/src/crypto/axtls/bigint.h b/roms/ipxe/src/crypto/axtls/bigint.h deleted file mode 100644 index 1f38c53d6..000000000 --- a/roms/ipxe/src/crypto/axtls/bigint.h +++ /dev/null @@ -1,99 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * 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 axTLS project 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT OWNER OR - * CONTRIBUTORS 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. - */ - -#ifndef BIGINT_HEADER -#define BIGINT_HEADER - -#include "crypto.h" - -BI_CTX *bi_initialize(void); -void bi_terminate(BI_CTX *ctx); -void bi_permanent(bigint *bi); -void bi_depermanent(bigint *bi); -void bi_clear_cache(BI_CTX *ctx); -void bi_free(BI_CTX *ctx, bigint *bi); -bigint *bi_copy(bigint *bi); -bigint *bi_clone(BI_CTX *ctx, const bigint *bi); -void bi_export(BI_CTX *ctx, bigint *bi, uint8_t *data, int size); -bigint *bi_import(BI_CTX *ctx, const uint8_t *data, int len); -bigint *int_to_bi(BI_CTX *ctx, comp i); - -/* the functions that actually do something interesting */ -bigint *bi_add(BI_CTX *ctx, bigint *bia, bigint *bib); -bigint *bi_subtract(BI_CTX *ctx, bigint *bia, - bigint *bib, int *is_negative); -bigint *bi_divide(BI_CTX *ctx, bigint *bia, bigint *bim, int is_mod); -bigint *bi_multiply(BI_CTX *ctx, bigint *bia, bigint *bib); -bigint *bi_mod_power(BI_CTX *ctx, bigint *bi, bigint *biexp); -bigint *bi_mod_power2(BI_CTX *ctx, bigint *bi, bigint *bim, bigint *biexp); -int bi_compare(bigint *bia, bigint *bib); -void bi_set_mod(BI_CTX *ctx, bigint *bim, int mod_offset); -void bi_free_mod(BI_CTX *ctx, int mod_offset); - -#ifdef CONFIG_SSL_FULL_MODE -void bi_print(const char *label, bigint *bi); -bigint *bi_str_import(BI_CTX *ctx, const char *data); -#endif - -/** - * @def bi_mod - * Find the residue of B. bi_set_mod() must be called before hand. - */ -#define bi_mod(A, B) bi_divide(A, B, ctx->bi_mod[ctx->mod_offset], 1) - -/** - * bi_residue() is technically the same as bi_mod(), but it uses the - * appropriate reduction technique (which is bi_mod() when doing classical - * reduction). - */ -#if defined(CONFIG_BIGINT_MONTGOMERY) -#define bi_residue(A, B) bi_mont(A, B) -bigint *bi_mont(BI_CTX *ctx, bigint *bixy); -#elif defined(CONFIG_BIGINT_BARRETT) -#define bi_residue(A, B) bi_barrett(A, B) -bigint *bi_barrett(BI_CTX *ctx, bigint *bi); -#else /* if defined(CONFIG_BIGINT_CLASSICAL) */ -#define bi_residue(A, B) bi_mod(A, B) -#endif - -#ifdef CONFIG_BIGINT_SQUARE -bigint *bi_square(BI_CTX *ctx, bigint *bi); -#else -#define bi_square(A, B) bi_multiply(A, bi_copy(B), B) -#endif - -#ifdef CONFIG_BIGINT_CRT -bigint *bi_crt(BI_CTX *ctx, bigint *bi, - bigint *dP, bigint *dQ, - bigint *p, bigint *q, - bigint *qInv); -#endif - -#endif diff --git a/roms/ipxe/src/crypto/axtls/bigint_impl.h b/roms/ipxe/src/crypto/axtls/bigint_impl.h deleted file mode 100644 index 09d8550ea..000000000 --- a/roms/ipxe/src/crypto/axtls/bigint_impl.h +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * 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 axTLS project 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT OWNER OR - * CONTRIBUTORS 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. - */ - -#ifndef BIGINT_IMPL_HEADER -#define BIGINT_IMPL_HEADER - -/* Maintain a number of precomputed variables when doing reduction */ -#define BIGINT_M_OFFSET 0 /**< Normal modulo offset. */ -#ifdef CONFIG_BIGINT_CRT -#define BIGINT_P_OFFSET 1 /**< p modulo offset. */ -#define BIGINT_Q_OFFSET 2 /**< q module offset. */ -#define BIGINT_NUM_MODS 3 /**< The number of modulus constants used. */ -#else -#define BIGINT_NUM_MODS 1 -#endif - -/* Architecture specific functions for big ints */ -#if defined(CONFIG_INTEGER_8BIT) -#define COMP_RADIX 256U /**< Max component + 1 */ -#define COMP_MAX 0xFFFFU/**< (Max dbl comp -1) */ -#define COMP_BIT_SIZE 8 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 1 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 2 /**< Used For diagnostics only. */ -typedef uint8_t comp; /**< A single precision component. */ -typedef uint16_t long_comp; /**< A double precision component. */ -typedef int16_t slong_comp; /**< A signed double precision component. */ -#elif defined(CONFIG_INTEGER_16BIT) -#define COMP_RADIX 65536U /**< Max component + 1 */ -#define COMP_MAX 0xFFFFFFFFU/**< (Max dbl comp -1) */ -#define COMP_BIT_SIZE 16 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 2 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 4 /**< Used For diagnostics only. */ -typedef uint16_t comp; /**< A single precision component. */ -typedef uint32_t long_comp; /**< A double precision component. */ -typedef int32_t slong_comp; /**< A signed double precision component. */ -#else /* regular 32 bit */ -#ifdef WIN32 -#define COMP_RADIX 4294967296i64 -#define COMP_MAX 0xFFFFFFFFFFFFFFFFui64 -#else -#define COMP_RADIX 4294967296ULL /**< Max component + 1 */ -#define COMP_MAX 0xFFFFFFFFFFFFFFFFULL/**< (Max dbl comp -1) */ -#endif -#define COMP_BIT_SIZE 32 /**< Number of bits in a component. */ -#define COMP_BYTE_SIZE 4 /**< Number of bytes in a component. */ -#define COMP_NUM_NIBBLES 8 /**< Used For diagnostics only. */ -typedef uint32_t comp; /**< A single precision component. */ -typedef uint64_t long_comp; /**< A double precision component. */ -typedef int64_t slong_comp; /**< A signed double precision component. */ -#endif - -/** - * @struct _bigint - * @brief A big integer basic object - */ -struct _bigint -{ - struct _bigint* next; /**< The next bigint in the cache. */ - short size; /**< The number of components in this bigint. */ - short max_comps; /**< The heapsize allocated for this bigint */ - int refs; /**< An internal reference count. */ - comp* comps; /**< A ptr to the actual component data */ -}; - -typedef struct _bigint bigint; /**< An alias for _bigint */ - -/** - * Maintains the state of the cache, and a number of variables used in - * reduction. - */ -typedef struct /**< A big integer "session" context. */ -{ - bigint *active_list; /**< Bigints currently used. */ - bigint *free_list; /**< Bigints not used. */ - bigint *bi_radix; /**< The radix used. */ - bigint *bi_mod[BIGINT_NUM_MODS]; /**< modulus */ - -#if defined(CONFIG_BIGINT_MONTGOMERY) - bigint *bi_RR_mod_m[BIGINT_NUM_MODS]; /**< R^2 mod m */ - bigint *bi_R_mod_m[BIGINT_NUM_MODS]; /**< R mod m */ - comp N0_dash[BIGINT_NUM_MODS]; -#elif defined(CONFIG_BIGINT_BARRETT) - bigint *bi_mu[BIGINT_NUM_MODS]; /**< Storage for mu */ -#endif - bigint *bi_normalised_mod[BIGINT_NUM_MODS]; /**< Normalised mod storage. */ - bigint **g; /**< Used by sliding-window. */ - int window; /**< The size of the sliding window */ - int active_count; /**< Number of active bigints. */ - int free_count; /**< Number of free bigints. */ - -#ifdef CONFIG_BIGINT_MONTGOMERY - uint8_t use_classical; /**< Use classical reduction. */ -#endif - uint8_t mod_offset; /**< The mod offset we are using */ -} BI_CTX; - -#ifndef WIN32 -#define max(a,b) ((a)>(b)?(a):(b)) /**< Find the maximum of 2 numbers. */ -#define min(a,b) ((a)<(b)?(a):(b)) /**< Find the minimum of 2 numbers. */ -#endif - -#define PERMANENT 0x7FFF55AA /**< A magic number for permanents. */ - -#endif diff --git a/roms/ipxe/src/crypto/axtls/config.h b/roms/ipxe/src/crypto/axtls/config.h deleted file mode 100644 index 32fa3bf03..000000000 --- a/roms/ipxe/src/crypto/axtls/config.h +++ /dev/null @@ -1,13 +0,0 @@ -#ifndef AXTLS_CONFIG_H -#define AXTLS_CONFIG_H - -/** - * @file config.h - * - * Trick the axtls code into building within our build environment. - */ - -#define CONFIG_SSL_ENABLE_CLIENT 1 -#define CONFIG_BIGINT_CLASSICAL 1 - -#endif diff --git a/roms/ipxe/src/crypto/axtls/crypto.h b/roms/ipxe/src/crypto/axtls/crypto.h deleted file mode 100644 index 2c4cda4de..000000000 --- a/roms/ipxe/src/crypto/axtls/crypto.h +++ /dev/null @@ -1,229 +0,0 @@ -/* - * Copyright (c) 2007, Cameron Rich - * - * 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 axTLS project 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 THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "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 THE COPYRIGHT OWNER OR - * CONTRIBUTORS 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. - */ - -/** - * @file crypto.h - */ - -#ifndef HEADER_CRYPTO_H -#define HEADER_CRYPTO_H - -#ifdef __cplusplus -extern "C" { -#endif - -#include "config.h" -#include "bigint_impl.h" -#include "bigint.h" - -#ifndef STDCALL -#define STDCALL -#endif -#ifndef EXP_FUNC -#define EXP_FUNC -#endif - - -/* enable features based on a 'super-set' capbaility. */ -#if defined(CONFIG_SSL_FULL_MODE) -#define CONFIG_SSL_ENABLE_CLIENT -#define CONFIG_SSL_CERT_VERIFICATION -#elif defined(CONFIG_SSL_ENABLE_CLIENT) -#define CONFIG_SSL_CERT_VERIFICATION -#endif - -/************************************************************************** - * AES declarations - **************************************************************************/ - -#define AES_MAXROUNDS 14 -#define AES_BLOCKSIZE 16 -#define AES_IV_SIZE 16 - -typedef struct aes_key_st -{ - uint16_t rounds; - uint16_t key_size; - uint32_t ks[(AES_MAXROUNDS+1)*8]; - uint8_t iv[AES_IV_SIZE]; -} AES_CTX; - -typedef enum -{ - AES_MODE_128, - AES_MODE_256 -} AES_MODE; - -void AES_set_key(AES_CTX *ctx, const uint8_t *key, - const uint8_t *iv, AES_MODE mode); -void AES_cbc_encrypt(AES_CTX *ctx, const uint8_t *msg, - uint8_t *out, int length); -void AES_cbc_decrypt(AES_CTX *ks, const uint8_t *in, uint8_t *out, int length); -void AES_convert_key(AES_CTX *ctx); - -/************************************************************************** - * RC4 declarations - **************************************************************************/ - -typedef struct -{ - uint8_t x, y, m[256]; -} RC4_CTX; - -void RC4_setup(RC4_CTX *s, const uint8_t *key, int length); -void RC4_crypt(RC4_CTX *s, const uint8_t *msg, uint8_t *data, int length); - -/************************************************************************** - * SHA1 declarations - **************************************************************************/ - -#define SHA1_SIZE 20 - -/* - * This structure will hold context information for the SHA-1 - * hashing operation - */ -typedef struct -{ - uint32_t Intermediate_Hash[SHA1_SIZE/4]; /* Message Digest */ - uint32_t Length_Low; /* Message length in bits */ - uint32_t Length_High; /* Message length in bits */ - uint16_t Message_Block_Index; /* Index into message block array */ - uint8_t Message_Block[64]; /* 512-bit message blocks */ -} SHA1_CTX; - -void SHA1_Init(SHA1_CTX *); -void SHA1_Update(SHA1_CTX *, const uint8_t * msg, int len); -void SHA1_Final(uint8_t *digest, SHA1_CTX *); - -/************************************************************************** - * MD2 declarations - **************************************************************************/ - -#define MD2_SIZE 16 - -typedef struct -{ - unsigned char cksum[16]; /* checksum of the data block */ - unsigned char state[48]; /* intermediate digest state */ - unsigned char buffer[16]; /* data block being processed */ - int left; /* amount of data in buffer */ -} MD2_CTX; - -EXP_FUNC void STDCALL MD2_Init(MD2_CTX *ctx); -EXP_FUNC void STDCALL MD2_Update(MD2_CTX *ctx, const uint8_t *input, int ilen); -EXP_FUNC void STDCALL MD2_Final(uint8_t *digest, MD2_CTX *ctx); - -/************************************************************************** - * MD5 declarations - **************************************************************************/ - -#define MD5_SIZE 16 - -typedef struct -{ - uint32_t state[4]; /* state (ABCD) */ - uint32_t count[2]; /* number of bits, modulo 2^64 (lsb first) */ - uint8_t buffer[64]; /* input buffer */ -} MD5_CTX; - -EXP_FUNC void STDCALL MD5_Init(MD5_CTX *); -EXP_FUNC void STDCALL MD5_Update(MD5_CTX *, const uint8_t *msg, int len); -EXP_FUNC void STDCALL MD5_Final(uint8_t *digest, MD5_CTX *); - -/************************************************************************** - * HMAC declarations - **************************************************************************/ -void hmac_md5(const uint8_t *msg, int length, const uint8_t *key, - int key_len, uint8_t *digest); -void hmac_sha1(const uint8_t *msg, int length, const uint8_t *key, - int key_len, uint8_t *digest); - -/************************************************************************** - * RSA declarations - **************************************************************************/ - -typedef struct -{ - bigint *m; /* modulus */ - bigint *e; /* public exponent */ - bigint *d; /* private exponent */ -#ifdef CONFIG_BIGINT_CRT - bigint *p; /* p as in m = pq */ - bigint *q; /* q as in m = pq */ - bigint *dP; /* d mod (p-1) */ - bigint *dQ; /* d mod (q-1) */ - bigint *qInv; /* q^-1 mod p */ -#endif - int num_octets; - BI_CTX *bi_ctx; -} RSA_CTX; - -void RSA_priv_key_new(RSA_CTX **rsa_ctx, - const uint8_t *modulus, int mod_len, - const uint8_t *pub_exp, int pub_len, - const uint8_t *priv_exp, int priv_len -#ifdef CONFIG_BIGINT_CRT - , const uint8_t *p, int p_len, - const uint8_t *q, int q_len, - const uint8_t *dP, int dP_len, - const uint8_t *dQ, int dQ_len, - const uint8_t *qInv, int qInv_len -#endif - ); -void RSA_pub_key_new(RSA_CTX **rsa_ctx, - const uint8_t *modulus, int mod_len, - const uint8_t *pub_exp, int pub_len); -void RSA_free(RSA_CTX *ctx); -int RSA_decrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint8_t *out_data, - int is_decryption); -bigint *RSA_private(const RSA_CTX *c, bigint *bi_msg); -#if defined(CONFIG_SSL_CERT_VERIFICATION) || defined(CONFIG_SSL_GENERATE_X509_CERT) -bigint *RSA_sign_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, - bigint *modulus, bigint *pub_exp); -bigint *RSA_public(const RSA_CTX * c, bigint *bi_msg); -int RSA_encrypt(const RSA_CTX *ctx, const uint8_t *in_data, uint16_t in_len, - uint8_t *out_data, int is_signing); -void RSA_print(const RSA_CTX *ctx); -#endif - -/************************************************************************** - * RNG declarations - **************************************************************************/ -EXP_FUNC void STDCALL RNG_initialize(const uint8_t *seed_buf, int size); -EXP_FUNC void STDCALL RNG_terminate(void); -EXP_FUNC void STDCALL get_random(int num_rand_bytes, uint8_t *rand_data); -void get_random_NZ(int num_rand_bytes, uint8_t *rand_data); - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/roms/ipxe/src/crypto/axtls/os_port.h b/roms/ipxe/src/crypto/axtls/os_port.h deleted file mode 100644 index 76313e204..000000000 --- a/roms/ipxe/src/crypto/axtls/os_port.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef AXTLS_OS_PORT_H -#define AXTLS_OS_PORT_H - -/** - * @file os_port.h - * - * Trick the axtls code into building within our build environment. - */ - -#include <stdint.h> -#include <byteswap.h> - -/** All imported axTLS files are licensed using the three-clause BSD licence */ -FILE_LICENCE ( BSD3 ); - -/** We can't actually abort, since we are effectively a kernel... */ -#define abort() assert ( 0 ) - -/** rsa.c uses alloca() */ -#define alloca( size ) __builtin_alloca ( size ) - -#include <ipxe/random_nz.h> -static inline void get_random_NZ ( int num_rand_bytes, uint8_t *rand_data ) { - /* AXTLS does not check for failures when generating random - * data. Rely on the fact that get_random_nz() does not - * request prediction resistance (and so cannot introduce new - * failures) and therefore any potential failure must already - * have been encountered by e.g. tls_generate_random(), which - * does check for failures. - */ - get_random_nz ( rand_data, num_rand_bytes ); -} - -/* Expose AES_encrypt() and AES_decrypt() in aes.o */ -#define aes 1 -#if OBJECT - -struct aes_key_st; - -static void AES_encrypt ( const struct aes_key_st *ctx, uint32_t *data ); -static void AES_decrypt ( const struct aes_key_st *ctx, uint32_t *data ); - -void axtls_aes_encrypt ( void *ctx, uint32_t *data ) { - AES_encrypt ( ctx, data ); -} - -void axtls_aes_decrypt ( void *ctx, uint32_t *data ) { - AES_decrypt ( ctx, data ); -} - -#endif -#undef aes - -#endif diff --git a/roms/ipxe/src/crypto/axtls_aes.c b/roms/ipxe/src/crypto/axtls_aes.c deleted file mode 100644 index 7f93c0ed7..000000000 --- a/roms/ipxe/src/crypto/axtls_aes.c +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <string.h> -#include <errno.h> -#include <assert.h> -#include <byteswap.h> -#include <ipxe/crypto.h> -#include <ipxe/cbc.h> -#include <ipxe/aes.h> -#include "crypto/axtls/crypto.h" - -/** @file - * - * AES algorithm - * - */ - -/** - * Set key - * - * @v ctx Context - * @v key Key - * @v keylen Key length - * @ret rc Return status code - */ -static int aes_setkey ( void *ctx, const void *key, size_t keylen ) { - struct aes_context *aes_ctx = ctx; - AES_MODE mode; - void *iv; - - switch ( keylen ) { - case ( 128 / 8 ): - mode = AES_MODE_128; - break; - case ( 256 / 8 ): - mode = AES_MODE_256; - break; - default: - return -EINVAL; - } - - /* IV is not a relevant concept at this stage; use a dummy - * value that will have no side-effects. - */ - iv = &aes_ctx->axtls_ctx.iv; - - AES_set_key ( &aes_ctx->axtls_ctx, key, iv, mode ); - - aes_ctx->decrypting = 0; - - return 0; -} - -/** - * Set initialisation vector - * - * @v ctx Context - * @v iv Initialisation vector - */ -static void aes_setiv ( void *ctx __unused, const void *iv __unused ) { - /* Nothing to do */ -} - -/** - * Call AXTLS' AES_encrypt() or AES_decrypt() functions - * - * @v axtls_ctx AXTLS AES context - * @v src Data to process - * @v dst Buffer for output - * @v func AXTLS AES function to call - */ -static void aes_call_axtls ( AES_CTX *axtls_ctx, const void *src, void *dst, - void ( * func ) ( const AES_CTX *axtls_ctx, - uint32_t *data ) ){ - const uint32_t *srcl = src; - uint32_t *dstl = dst; - unsigned int i; - - /* AXTLS' AES_encrypt() and AES_decrypt() functions both - * expect to deal with an array of four dwords in host-endian - * order. - */ - for ( i = 0 ; i < 4 ; i++ ) - dstl[i] = ntohl ( srcl[i] ); - func ( axtls_ctx, dstl ); - for ( i = 0 ; i < 4 ; i++ ) - dstl[i] = htonl ( dstl[i] ); -} - -/** - * Encrypt data - * - * @v ctx Context - * @v src Data to encrypt - * @v dst Buffer for encrypted data - * @v len Length of data - */ -static void aes_encrypt ( void *ctx, const void *src, void *dst, - size_t len ) { - struct aes_context *aes_ctx = ctx; - - assert ( len == AES_BLOCKSIZE ); - if ( aes_ctx->decrypting ) - assert ( 0 ); - aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_encrypt ); -} - -/** - * Decrypt data - * - * @v ctx Context - * @v src Data to decrypt - * @v dst Buffer for decrypted data - * @v len Length of data - */ -static void aes_decrypt ( void *ctx, const void *src, void *dst, - size_t len ) { - struct aes_context *aes_ctx = ctx; - - assert ( len == AES_BLOCKSIZE ); - if ( ! aes_ctx->decrypting ) { - AES_convert_key ( &aes_ctx->axtls_ctx ); - aes_ctx->decrypting = 1; - } - aes_call_axtls ( &aes_ctx->axtls_ctx, src, dst, axtls_aes_decrypt ); -} - -/** Basic AES algorithm */ -struct cipher_algorithm aes_algorithm = { - .name = "aes", - .ctxsize = sizeof ( struct aes_context ), - .blocksize = AES_BLOCKSIZE, - .setkey = aes_setkey, - .setiv = aes_setiv, - .encrypt = aes_encrypt, - .decrypt = aes_decrypt, -}; - -/* AES with cipher-block chaining */ -CBC_CIPHER ( aes_cbc, aes_cbc_algorithm, - aes_algorithm, struct aes_context, AES_BLOCKSIZE ); diff --git a/roms/ipxe/src/crypto/bigint.c b/roms/ipxe/src/crypto/bigint.c index 340128e2f..50f320302 100644 --- a/roms/ipxe/src/crypto/bigint.c +++ b/roms/ipxe/src/crypto/bigint.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/crypto/cbc.c b/roms/ipxe/src/crypto/cbc.c index 9bf0e8b49..0ba17ee48 100644 --- a/roms/ipxe/src/crypto/cbc.c +++ b/roms/ipxe/src/crypto/cbc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <assert.h> diff --git a/roms/ipxe/src/crypto/certstore.c b/roms/ipxe/src/crypto/certstore.c index 77cf6ebb6..503ce499e 100644 --- a/roms/ipxe/src/crypto/certstore.c +++ b/roms/ipxe/src/crypto/certstore.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdlib.h> diff --git a/roms/ipxe/src/crypto/chap.c b/roms/ipxe/src/crypto/chap.c index db64371c7..c90c16def 100644 --- a/roms/ipxe/src/crypto/chap.c +++ b/roms/ipxe/src/crypto/chap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> diff --git a/roms/ipxe/src/crypto/cms.c b/roms/ipxe/src/crypto/cms.c index b4a41de6c..bc2148e8a 100644 --- a/roms/ipxe/src/crypto/cms.c +++ b/roms/ipxe/src/crypto/cms.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/crypto_null.c b/roms/ipxe/src/crypto/crypto_null.c index ba05f7269..15a1c538b 100644 --- a/roms/ipxe/src/crypto/crypto_null.c +++ b/roms/ipxe/src/crypto/crypto_null.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/crypto/deflate.c b/roms/ipxe/src/crypto/deflate.c index 91a489961..e1c87d5fe 100644 --- a/roms/ipxe/src/crypto/deflate.c +++ b/roms/ipxe/src/crypto/deflate.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <strings.h> diff --git a/roms/ipxe/src/crypto/drbg.c b/roms/ipxe/src/crypto/drbg.c index 9e0175d25..5c8b5e612 100644 --- a/roms/ipxe/src/crypto/drbg.c +++ b/roms/ipxe/src/crypto/drbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/ecb.c b/roms/ipxe/src/crypto/ecb.c new file mode 100644 index 000000000..3c9cf340c --- /dev/null +++ b/roms/ipxe/src/crypto/ecb.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2009 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <assert.h> +#include <ipxe/crypto.h> +#include <ipxe/ecb.h> + +/** @file + * + * Electronic codebook (ECB) + * + */ + +/** + * Encrypt data + * + * @v ctx Context + * @v src Data to encrypt + * @v dst Buffer for encrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + */ +void ecb_encrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cipher_encrypt ( raw_cipher, ctx, src, dst, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} + +/** + * Decrypt data + * + * @v ctx Context + * @v src Data to decrypt + * @v dst Buffer for decrypted data + * @v len Length of data + * @v raw_cipher Underlying cipher algorithm + */ +void ecb_decrypt ( void *ctx, const void *src, void *dst, size_t len, + struct cipher_algorithm *raw_cipher ) { + size_t blocksize = raw_cipher->blocksize; + + assert ( ( len % blocksize ) == 0 ); + + while ( len ) { + cipher_decrypt ( raw_cipher, ctx, src, dst, blocksize ); + dst += blocksize; + src += blocksize; + len -= blocksize; + } +} diff --git a/roms/ipxe/src/crypto/entropy.c b/roms/ipxe/src/crypto/entropy.c index c7045840e..5acbc0258 100644 --- a/roms/ipxe/src/crypto/entropy.c +++ b/roms/ipxe/src/crypto/entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/hash_df.c b/roms/ipxe/src/crypto/hash_df.c index adf1d87e4..c1417e683 100644 --- a/roms/ipxe/src/crypto/hash_df.c +++ b/roms/ipxe/src/crypto/hash_df.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/hmac.c b/roms/ipxe/src/crypto/hmac.c index e9459198c..95a46195c 100644 --- a/roms/ipxe/src/crypto/hmac.c +++ b/roms/ipxe/src/crypto/hmac.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/crypto/hmac_drbg.c b/roms/ipxe/src/crypto/hmac_drbg.c index 1e5f732e2..6c1d5deb2 100644 --- a/roms/ipxe/src/crypto/hmac_drbg.c +++ b/roms/ipxe/src/crypto/hmac_drbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/md5.c b/roms/ipxe/src/crypto/md5.c index 122c7d59e..f9738b0ac 100644 --- a/roms/ipxe/src/crypto/md5.c +++ b/roms/ipxe/src/crypto/md5.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c b/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c new file mode 100644 index 000000000..06722c0e1 --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha1.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <byteswap.h> +#include <ipxe/rsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha1.h> +#include <ipxe/tls.h> + +/** TLS_RSA_WITH_AES_128_CBC_SHA cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha __tls_cipher_suite (03) = { + .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), + .key_len = ( 128 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; + +/** TLS_RSA_WITH_AES_256_CBC_SHA cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha __tls_cipher_suite (04) = { + .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), + .key_len = ( 256 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha1_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c b/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c new file mode 100644 index 000000000..c609eacea --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_aes_cbc_sha256.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <byteswap.h> +#include <ipxe/rsa.h> +#include <ipxe/aes.h> +#include <ipxe/sha256.h> +#include <ipxe/tls.h> + +/** TLS_RSA_WITH_AES_128_CBC_SHA256 cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_128_cbc_sha256 __tls_cipher_suite(01)={ + .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), + .key_len = ( 128 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; + +/** TLS_RSA_WITH_AES_256_CBC_SHA256 cipher suite */ +struct tls_cipher_suite tls_rsa_with_aes_256_cbc_sha256 __tls_cipher_suite(02)={ + .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), + .key_len = ( 256 / 8 ), + .pubkey = &rsa_algorithm, + .cipher = &aes_cbc_algorithm, + .digest = &sha256_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_md5.c b/roms/ipxe/src/crypto/mishmash/rsa_md5.c new file mode 100644 index 000000000..ac828ac11 --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_md5.c @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/md5.h> +#include <ipxe/asn1.h> + +/** "md5WithRSAEncryption" object identifier */ +static uint8_t oid_md5_with_rsa_encryption[] = + { ASN1_OID_MD5WITHRSAENCRYPTION }; + +/** "md5WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm md5_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "md5WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &md5_algorithm, + .oid = ASN1_OID_CURSOR ( oid_md5_with_rsa_encryption ), +}; + +/** MD5 digestInfo prefix */ +static const uint8_t rsa_md5_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( MD5_DIGEST_SIZE, ASN1_OID_MD5 ) }; + +/** MD5 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_md5_prefix __rsa_digestinfo_prefix = { + .digest = &md5_algorithm, + .data = rsa_md5_prefix_data, + .len = sizeof ( rsa_md5_prefix_data ), +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_sha1.c b/roms/ipxe/src/crypto/mishmash/rsa_sha1.c new file mode 100644 index 000000000..39424bf2d --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_sha1.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/sha1.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "sha1WithRSAEncryption" object identifier */ +static uint8_t oid_sha1_with_rsa_encryption[] = + { ASN1_OID_SHA1WITHRSAENCRYPTION }; + +/** "sha1WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha1_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha1WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha1_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha1_with_rsa_encryption ), +}; + +/** SHA-1 digestInfo prefix */ +static const uint8_t rsa_sha1_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA1_DIGEST_SIZE, ASN1_OID_SHA1 ) }; + +/** SHA-1 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha1_prefix __rsa_digestinfo_prefix = { + .digest = &sha1_algorithm, + .data = rsa_sha1_prefix_data, + .len = sizeof ( rsa_sha1_prefix_data ), +}; + +/** RSA with SHA-1 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha1 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA1_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha1_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_sha224.c b/roms/ipxe/src/crypto/mishmash/rsa_sha224.c new file mode 100644 index 000000000..5e8755aab --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_sha224.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/sha256.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "sha224WithRSAEncryption" object identifier */ +static uint8_t oid_sha224_with_rsa_encryption[] = + { ASN1_OID_SHA224WITHRSAENCRYPTION }; + +/** "sha224WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha224_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha224WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha224_with_rsa_encryption ), +}; + +/** SHA-224 digestInfo prefix */ +static const uint8_t rsa_sha224_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA224_DIGEST_SIZE, ASN1_OID_SHA224 ) }; + +/** SHA-224 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha224_prefix __rsa_digestinfo_prefix = { + .digest = &sha224_algorithm, + .data = rsa_sha224_prefix_data, + .len = sizeof ( rsa_sha224_prefix_data ), +}; + +/** RSA with SHA-224 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha224 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA224_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha224_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_sha256.c b/roms/ipxe/src/crypto/mishmash/rsa_sha256.c new file mode 100644 index 000000000..b44af5f19 --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_sha256.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/sha256.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "sha256WithRSAEncryption" object identifier */ +static uint8_t oid_sha256_with_rsa_encryption[] = + { ASN1_OID_SHA256WITHRSAENCRYPTION }; + +/** "sha256WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha256_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha256WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha256_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha256_with_rsa_encryption ), +}; + +/** SHA-256 digestInfo prefix */ +static const uint8_t rsa_sha256_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA256_DIGEST_SIZE, ASN1_OID_SHA256 ) }; + +/** SHA-256 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha256_prefix __rsa_digestinfo_prefix = { + .digest = &sha256_algorithm, + .data = rsa_sha256_prefix_data, + .len = sizeof ( rsa_sha256_prefix_data ), +}; + +/** RSA with SHA-256 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha256 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA256_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha256_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_sha384.c b/roms/ipxe/src/crypto/mishmash/rsa_sha384.c new file mode 100644 index 000000000..af22a2bf0 --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_sha384.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/sha512.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "sha384WithRSAEncryption" object identifier */ +static uint8_t oid_sha384_with_rsa_encryption[] = + { ASN1_OID_SHA384WITHRSAENCRYPTION }; + +/** "sha384WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha384_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha384WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha384_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha384_with_rsa_encryption ), +}; + +/** SHA-384 digestInfo prefix */ +static const uint8_t rsa_sha384_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA384_DIGEST_SIZE, ASN1_OID_SHA384 ) }; + +/** SHA-384 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha384_prefix __rsa_digestinfo_prefix = { + .digest = &sha384_algorithm, + .data = rsa_sha384_prefix_data, + .len = sizeof ( rsa_sha384_prefix_data ), +}; + +/** RSA with SHA-384 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha384 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA384_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha384_algorithm, +}; diff --git a/roms/ipxe/src/crypto/mishmash/rsa_sha512.c b/roms/ipxe/src/crypto/mishmash/rsa_sha512.c new file mode 100644 index 000000000..29ee15493 --- /dev/null +++ b/roms/ipxe/src/crypto/mishmash/rsa_sha512.c @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/rsa.h> +#include <ipxe/sha512.h> +#include <ipxe/asn1.h> +#include <ipxe/tls.h> + +/** "sha512WithRSAEncryption" object identifier */ +static uint8_t oid_sha512_with_rsa_encryption[] = + { ASN1_OID_SHA512WITHRSAENCRYPTION }; + +/** "sha512WithRSAEncryption" OID-identified algorithm */ +struct asn1_algorithm sha512_with_rsa_encryption_algorithm __asn1_algorithm = { + .name = "sha512WithRSAEncryption", + .pubkey = &rsa_algorithm, + .digest = &sha512_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_with_rsa_encryption ), +}; + +/** SHA-512 digestInfo prefix */ +static const uint8_t rsa_sha512_prefix_data[] = + { RSA_DIGESTINFO_PREFIX ( SHA512_DIGEST_SIZE, ASN1_OID_SHA512 ) }; + +/** SHA-512 digestInfo prefix */ +struct rsa_digestinfo_prefix rsa_sha512_prefix __rsa_digestinfo_prefix = { + .digest = &sha512_algorithm, + .data = rsa_sha512_prefix_data, + .len = sizeof ( rsa_sha512_prefix_data ), +}; + +/** RSA with SHA-512 signature hash algorithm */ +struct tls_signature_hash_algorithm tls_rsa_sha512 __tls_sig_hash_algorithm = { + .code = { + .signature = TLS_RSA_ALGORITHM, + .hash = TLS_SHA512_ALGORITHM, + }, + .pubkey = &rsa_algorithm, + .digest = &sha512_algorithm, +}; diff --git a/roms/ipxe/src/crypto/null_entropy.c b/roms/ipxe/src/crypto/null_entropy.c index c56d5e76f..d1e1a6f73 100644 --- a/roms/ipxe/src/crypto/null_entropy.c +++ b/roms/ipxe/src/crypto/null_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/ocsp.c b/roms/ipxe/src/crypto/ocsp.c index 66e47c57e..5df55bc96 100644 --- a/roms/ipxe/src/crypto/ocsp.c +++ b/roms/ipxe/src/crypto/ocsp.c @@ -233,7 +233,7 @@ static int ocsp_uri_string ( struct ocsp_check *ocsp ) { goto err_path_base64; } base64_encode ( ocsp->request.builder.data, ocsp->request.builder.len, - path_base64_string ); + path_base64_string, path_len ); /* URI-encode the Base64-encoded request */ memset ( &path_uri, 0, sizeof ( path_uri ) ); diff --git a/roms/ipxe/src/crypto/privkey.c b/roms/ipxe/src/crypto/privkey.c index e010649c0..a6043bd1e 100644 --- a/roms/ipxe/src/crypto/privkey.c +++ b/roms/ipxe/src/crypto/privkey.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/crypto/random_nz.c b/roms/ipxe/src/crypto/random_nz.c index f1d2e187d..5fe576e05 100644 --- a/roms/ipxe/src/crypto/random_nz.c +++ b/roms/ipxe/src/crypto/random_nz.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/rbg.c b/roms/ipxe/src/crypto/rbg.c index e2d06978c..943b288c3 100644 --- a/roms/ipxe/src/crypto/rbg.c +++ b/roms/ipxe/src/crypto/rbg.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/rootcert.c b/roms/ipxe/src/crypto/rootcert.c index ae28905ac..00ea1647e 100644 --- a/roms/ipxe/src/crypto/rootcert.c +++ b/roms/ipxe/src/crypto/rootcert.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/crypto/rsa.c b/roms/ipxe/src/crypto/rsa.c index 0ab7b2ad3..36109280d 100644 --- a/roms/ipxe/src/crypto/rsa.c +++ b/roms/ipxe/src/crypto/rsa.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -28,9 +32,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/crypto.h> #include <ipxe/bigint.h> #include <ipxe/random_nz.h> -#include <ipxe/md5.h> -#include <ipxe/sha1.h> -#include <ipxe/sha256.h> #include <ipxe/rsa.h> /** @file @@ -49,18 +50,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** "rsaEncryption" object identifier */ static uint8_t oid_rsa_encryption[] = { ASN1_OID_RSAENCRYPTION }; -/** "md5WithRSAEncryption" object identifier */ -static uint8_t oid_md5_with_rsa_encryption[] = - { ASN1_OID_MD5WITHRSAENCRYPTION }; - -/** "sha1WithRSAEncryption" object identifier */ -static uint8_t oid_sha1_with_rsa_encryption[] = - { ASN1_OID_SHA1WITHRSAENCRYPTION }; - -/** "sha256WithRSAEncryption" object identifier */ -static uint8_t oid_sha256_with_rsa_encryption[] = - { ASN1_OID_SHA256WITHRSAENCRYPTION }; - /** "rsaEncryption" OID-identified algorithm */ struct asn1_algorithm rsa_encryption_algorithm __asn1_algorithm = { .name = "rsaEncryption", @@ -69,63 +58,6 @@ struct asn1_algorithm rsa_encryption_algorithm __asn1_algorithm = { .oid = ASN1_OID_CURSOR ( oid_rsa_encryption ), }; -/** "md5WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm md5_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "md5WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &md5_algorithm, - .oid = ASN1_OID_CURSOR ( oid_md5_with_rsa_encryption ), -}; - -/** "sha1WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm sha1_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "sha1WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &sha1_algorithm, - .oid = ASN1_OID_CURSOR ( oid_sha1_with_rsa_encryption ), -}; - -/** "sha256WithRSAEncryption" OID-identified algorithm */ -struct asn1_algorithm sha256_with_rsa_encryption_algorithm __asn1_algorithm = { - .name = "sha256WithRSAEncryption", - .pubkey = &rsa_algorithm, - .digest = &sha256_algorithm, - .oid = ASN1_OID_CURSOR ( oid_sha256_with_rsa_encryption ), -}; - -/** MD5 digestInfo prefix */ -static const uint8_t rsa_md5_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( MD5_DIGEST_SIZE, ASN1_OID_MD5 ) }; - -/** SHA-1 digestInfo prefix */ -static const uint8_t rsa_sha1_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( SHA1_DIGEST_SIZE, ASN1_OID_SHA1 ) }; - -/** SHA-256 digestInfo prefix */ -static const uint8_t rsa_sha256_prefix_data[] = - { RSA_DIGESTINFO_PREFIX ( SHA256_DIGEST_SIZE, ASN1_OID_SHA256 ) }; - -/** MD5 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_md5_prefix __rsa_digestinfo_prefix = { - .digest = &md5_algorithm, - .data = rsa_md5_prefix_data, - .len = sizeof ( rsa_md5_prefix_data ), -}; - -/** SHA-1 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_sha1_prefix __rsa_digestinfo_prefix = { - .digest = &sha1_algorithm, - .data = rsa_sha1_prefix_data, - .len = sizeof ( rsa_sha1_prefix_data ), -}; - -/** SHA-256 digestInfo prefix */ -struct rsa_digestinfo_prefix rsa_sha256_prefix __rsa_digestinfo_prefix = { - .digest = &sha256_algorithm, - .data = rsa_sha256_prefix_data, - .len = sizeof ( rsa_sha256_prefix_data ), -}; - /** * Identify RSA prefix * diff --git a/roms/ipxe/src/crypto/sha1.c b/roms/ipxe/src/crypto/sha1.c index e1bef669e..51866f4b7 100644 --- a/roms/ipxe/src/crypto/sha1.c +++ b/roms/ipxe/src/crypto/sha1.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/crypto/sha224.c b/roms/ipxe/src/crypto/sha224.c new file mode 100644 index 000000000..be25f24e9 --- /dev/null +++ b/roms/ipxe/src/crypto/sha224.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-224 algorithm + * + */ + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha256.h> + +/** SHA-224 initial digest values */ +static const struct sha256_digest sha224_init_digest = { + .h = { + cpu_to_be32 ( 0xc1059ed8 ), + cpu_to_be32 ( 0x367cd507 ), + cpu_to_be32 ( 0x3070dd17 ), + cpu_to_be32 ( 0xf70e5939 ), + cpu_to_be32 ( 0xffc00b31 ), + cpu_to_be32 ( 0x68581511 ), + cpu_to_be32 ( 0x64f98fa7 ), + cpu_to_be32 ( 0xbefa4fa4 ), + }, +}; + +/** + * Initialise SHA-224 algorithm + * + * @v ctx SHA-224 context + */ +static void sha224_init ( void *ctx ) { + struct sha256_context *context = ctx; + + sha256_family_init ( context, &sha224_init_digest, SHA224_DIGEST_SIZE ); +} + +/** SHA-224 algorithm */ +struct digest_algorithm sha224_algorithm = { + .name = "sha224", + .ctxsize = sizeof ( struct sha256_context ), + .blocksize = sizeof ( union sha256_block ), + .digestsize = SHA224_DIGEST_SIZE, + .init = sha224_init, + .update = sha256_update, + .final = sha256_final, +}; + +/** "sha224" object identifier */ +static uint8_t oid_sha224[] = { ASN1_OID_SHA224 }; + +/** "sha224" OID-identified algorithm */ +struct asn1_algorithm oid_sha224_algorithm __asn1_algorithm = { + .name = "sha224", + .digest = &sha224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha224 ), +}; diff --git a/roms/ipxe/src/crypto/sha256.c b/roms/ipxe/src/crypto/sha256.c index 36e02b3c2..0360d8d16 100644 --- a/roms/ipxe/src/crypto/sha256.c +++ b/roms/ipxe/src/crypto/sha256.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -47,11 +51,11 @@ struct sha256_variables { uint32_t f; uint32_t g; uint32_t h; - uint32_t w[64]; + uint32_t w[SHA256_ROUNDS]; } __attribute__ (( packed )); /** SHA-256 constants */ -static const uint32_t k[64] = { +static const uint32_t k[SHA256_ROUNDS] = { 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, 0xe49b69c1, 0xefbe4786, @@ -65,6 +69,37 @@ static const uint32_t k[64] = { 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2 }; +/** SHA-256 initial digest values */ +static const struct sha256_digest sha256_init_digest = { + .h = { + cpu_to_be32 ( 0x6a09e667 ), + cpu_to_be32 ( 0xbb67ae85 ), + cpu_to_be32 ( 0x3c6ef372 ), + cpu_to_be32 ( 0xa54ff53a ), + cpu_to_be32 ( 0x510e527f ), + cpu_to_be32 ( 0x9b05688c ), + cpu_to_be32 ( 0x1f83d9ab ), + cpu_to_be32 ( 0x5be0cd19 ), + }, +}; + +/** + * Initialise SHA-256 family algorithm + * + * @v context SHA-256 context + * @v init Initial digest values + * @v digestsize Digest size + */ +void sha256_family_init ( struct sha256_context *context, + const struct sha256_digest *init, + size_t digestsize ) { + + context->len = 0; + context->digestsize = digestsize; + memcpy ( &context->ddd.dd.digest, init, + sizeof ( context->ddd.dd.digest ) ); +} + /** * Initialise SHA-256 algorithm * @@ -73,15 +108,8 @@ static const uint32_t k[64] = { static void sha256_init ( void *ctx ) { struct sha256_context *context = ctx; - context->ddd.dd.digest.h[0] = cpu_to_be32 ( 0x6a09e667 ); - context->ddd.dd.digest.h[1] = cpu_to_be32 ( 0xbb67ae85 ); - context->ddd.dd.digest.h[2] = cpu_to_be32 ( 0x3c6ef372 ); - context->ddd.dd.digest.h[3] = cpu_to_be32 ( 0xa54ff53a ); - context->ddd.dd.digest.h[4] = cpu_to_be32 ( 0x510e527f ); - context->ddd.dd.digest.h[5] = cpu_to_be32 ( 0x9b05688c ); - context->ddd.dd.digest.h[6] = cpu_to_be32 ( 0x1f83d9ab ); - context->ddd.dd.digest.h[7] = cpu_to_be32 ( 0x5be0cd19 ); - context->len = 0; + sha256_family_init ( context, &sha256_init_digest, + sizeof ( struct sha256_digest ) ); } /** @@ -139,7 +167,7 @@ static void sha256_digest ( struct sha256_context *context ) { } /* Initialise w[16..63] */ - for ( i = 16 ; i < 64 ; i++ ) { + for ( i = 16 ; i < SHA256_ROUNDS ; i++ ) { s0 = ( ror32 ( w[i-15], 7 ) ^ ror32 ( w[i-15], 18 ) ^ ( w[i-15] >> 3 ) ); s1 = ( ror32 ( w[i-2], 17 ) ^ ror32 ( w[i-2], 19 ) ^ @@ -148,7 +176,7 @@ static void sha256_digest ( struct sha256_context *context ) { } /* Main loop */ - for ( i = 0 ; i < 64 ; i++ ) { + for ( i = 0 ; i < SHA256_ROUNDS ; i++ ) { s0 = ( ror32 ( *a, 2 ) ^ ror32 ( *a, 13 ) ^ ror32 ( *a, 22 ) ); maj = ( ( *a & *b ) ^ ( *a & *c ) ^ ( *b & *c ) ); t2 = ( s0 + maj ); @@ -186,7 +214,7 @@ static void sha256_digest ( struct sha256_context *context ) { * @v data Data * @v len Length of data */ -static void sha256_update ( void *ctx, const void *data, size_t len ) { +void sha256_update ( void *ctx, const void *data, size_t len ) { struct sha256_context *context = ctx; const uint8_t *byte = data; size_t offset; @@ -209,7 +237,7 @@ static void sha256_update ( void *ctx, const void *data, size_t len ) { * @v ctx SHA-256 context * @v out Output buffer */ -static void sha256_final ( void *ctx, void *out ) { +void sha256_final ( void *ctx, void *out ) { struct sha256_context *context = ctx; uint64_t len_bits; uint8_t pad; @@ -230,8 +258,7 @@ static void sha256_final ( void *ctx, void *out ) { assert ( ( context->len % sizeof ( context->ddd.dd.data ) ) == 0 ); /* Copy out final digest */ - memcpy ( out, &context->ddd.dd.digest, - sizeof ( context->ddd.dd.digest ) ); + memcpy ( out, &context->ddd.dd.digest, context->digestsize ); } /** SHA-256 algorithm */ diff --git a/roms/ipxe/src/crypto/sha384.c b/roms/ipxe/src/crypto/sha384.c new file mode 100644 index 000000000..017751826 --- /dev/null +++ b/roms/ipxe/src/crypto/sha384.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-384 algorithm + * + */ + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha512.h> + +/** SHA-384 initial digest values */ +static const struct sha512_digest sha384_init_digest = { + .h = { + cpu_to_be64 ( 0xcbbb9d5dc1059ed8ULL ), + cpu_to_be64 ( 0x629a292a367cd507ULL ), + cpu_to_be64 ( 0x9159015a3070dd17ULL ), + cpu_to_be64 ( 0x152fecd8f70e5939ULL ), + cpu_to_be64 ( 0x67332667ffc00b31ULL ), + cpu_to_be64 ( 0x8eb44a8768581511ULL ), + cpu_to_be64 ( 0xdb0c2e0d64f98fa7ULL ), + cpu_to_be64 ( 0x47b5481dbefa4fa4ULL ), + }, +}; + +/** + * Initialise SHA-384 algorithm + * + * @v ctx SHA-384 context + */ +static void sha384_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha384_init_digest, SHA384_DIGEST_SIZE ); +} + +/** SHA-384 algorithm */ +struct digest_algorithm sha384_algorithm = { + .name = "sha384", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA384_DIGEST_SIZE, + .init = sha384_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha384" object identifier */ +static uint8_t oid_sha384[] = { ASN1_OID_SHA384 }; + +/** "sha384" OID-identified algorithm */ +struct asn1_algorithm oid_sha384_algorithm __asn1_algorithm = { + .name = "sha384", + .digest = &sha384_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha384 ), +}; diff --git a/roms/ipxe/src/crypto/sha512.c b/roms/ipxe/src/crypto/sha512.c new file mode 100644 index 000000000..814f44563 --- /dev/null +++ b/roms/ipxe/src/crypto/sha512.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512 algorithm + * + */ + +#include <stdint.h> +#include <string.h> +#include <byteswap.h> +#include <assert.h> +#include <ipxe/rotate.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha512.h> + +/** SHA-512 variables */ +struct sha512_variables { + /* This layout matches that of struct sha512_digest_data, + * allowing for efficient endianness-conversion, + */ + uint64_t a; + uint64_t b; + uint64_t c; + uint64_t d; + uint64_t e; + uint64_t f; + uint64_t g; + uint64_t h; + uint64_t w[SHA512_ROUNDS]; +} __attribute__ (( packed )); + +/** SHA-512 constants */ +static const uint64_t k[SHA512_ROUNDS] = { + 0x428a2f98d728ae22ULL, 0x7137449123ef65cdULL, 0xb5c0fbcfec4d3b2fULL, + 0xe9b5dba58189dbbcULL, 0x3956c25bf348b538ULL, 0x59f111f1b605d019ULL, + 0x923f82a4af194f9bULL, 0xab1c5ed5da6d8118ULL, 0xd807aa98a3030242ULL, + 0x12835b0145706fbeULL, 0x243185be4ee4b28cULL, 0x550c7dc3d5ffb4e2ULL, + 0x72be5d74f27b896fULL, 0x80deb1fe3b1696b1ULL, 0x9bdc06a725c71235ULL, + 0xc19bf174cf692694ULL, 0xe49b69c19ef14ad2ULL, 0xefbe4786384f25e3ULL, + 0x0fc19dc68b8cd5b5ULL, 0x240ca1cc77ac9c65ULL, 0x2de92c6f592b0275ULL, + 0x4a7484aa6ea6e483ULL, 0x5cb0a9dcbd41fbd4ULL, 0x76f988da831153b5ULL, + 0x983e5152ee66dfabULL, 0xa831c66d2db43210ULL, 0xb00327c898fb213fULL, + 0xbf597fc7beef0ee4ULL, 0xc6e00bf33da88fc2ULL, 0xd5a79147930aa725ULL, + 0x06ca6351e003826fULL, 0x142929670a0e6e70ULL, 0x27b70a8546d22ffcULL, + 0x2e1b21385c26c926ULL, 0x4d2c6dfc5ac42aedULL, 0x53380d139d95b3dfULL, + 0x650a73548baf63deULL, 0x766a0abb3c77b2a8ULL, 0x81c2c92e47edaee6ULL, + 0x92722c851482353bULL, 0xa2bfe8a14cf10364ULL, 0xa81a664bbc423001ULL, + 0xc24b8b70d0f89791ULL, 0xc76c51a30654be30ULL, 0xd192e819d6ef5218ULL, + 0xd69906245565a910ULL, 0xf40e35855771202aULL, 0x106aa07032bbd1b8ULL, + 0x19a4c116b8d2d0c8ULL, 0x1e376c085141ab53ULL, 0x2748774cdf8eeb99ULL, + 0x34b0bcb5e19b48a8ULL, 0x391c0cb3c5c95a63ULL, 0x4ed8aa4ae3418acbULL, + 0x5b9cca4f7763e373ULL, 0x682e6ff3d6b2b8a3ULL, 0x748f82ee5defb2fcULL, + 0x78a5636f43172f60ULL, 0x84c87814a1f0ab72ULL, 0x8cc702081a6439ecULL, + 0x90befffa23631e28ULL, 0xa4506cebde82bde9ULL, 0xbef9a3f7b2c67915ULL, + 0xc67178f2e372532bULL, 0xca273eceea26619cULL, 0xd186b8c721c0c207ULL, + 0xeada7dd6cde0eb1eULL, 0xf57d4f7fee6ed178ULL, 0x06f067aa72176fbaULL, + 0x0a637dc5a2c898a6ULL, 0x113f9804bef90daeULL, 0x1b710b35131c471bULL, + 0x28db77f523047d84ULL, 0x32caab7b40c72493ULL, 0x3c9ebe0a15c9bebcULL, + 0x431d67c49c100d4cULL, 0x4cc5d4becb3e42b6ULL, 0x597f299cfc657e2aULL, + 0x5fcb6fab3ad6faecULL, 0x6c44198c4a475817ULL +}; + +/** SHA-512 initial digest values */ +static const struct sha512_digest sha512_init_digest = { + .h = { + cpu_to_be64 ( 0x6a09e667f3bcc908ULL ), + cpu_to_be64 ( 0xbb67ae8584caa73bULL ), + cpu_to_be64 ( 0x3c6ef372fe94f82bULL ), + cpu_to_be64 ( 0xa54ff53a5f1d36f1ULL ), + cpu_to_be64 ( 0x510e527fade682d1ULL ), + cpu_to_be64 ( 0x9b05688c2b3e6c1fULL ), + cpu_to_be64 ( 0x1f83d9abfb41bd6bULL ), + cpu_to_be64 ( 0x5be0cd19137e2179ULL ), + }, +}; + +/** + * Initialise SHA-512 family algorithm + * + * @v context SHA-512 context + * @v init Initial digest values + * @v digestsize Digest size + */ +void sha512_family_init ( struct sha512_context *context, + const struct sha512_digest *init, + size_t digestsize ) { + + context->len = 0; + context->digestsize = digestsize; + memcpy ( &context->ddq.dd.digest, init, + sizeof ( context->ddq.dd.digest ) ); +} + +/** + * Initialise SHA-512 algorithm + * + * @v ctx SHA-512 context + */ +static void sha512_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_init_digest, + sizeof ( struct sha512_digest ) ); +} + +/** + * Calculate SHA-512 digest of accumulated data + * + * @v context SHA-512 context + */ +static void sha512_digest ( struct sha512_context *context ) { + union { + union sha512_digest_data_qwords ddq; + struct sha512_variables v; + } u; + uint64_t *a = &u.v.a; + uint64_t *b = &u.v.b; + uint64_t *c = &u.v.c; + uint64_t *d = &u.v.d; + uint64_t *e = &u.v.e; + uint64_t *f = &u.v.f; + uint64_t *g = &u.v.g; + uint64_t *h = &u.v.h; + uint64_t *w = u.v.w; + uint64_t s0; + uint64_t s1; + uint64_t maj; + uint64_t t1; + uint64_t t2; + uint64_t ch; + unsigned int i; + + /* Sanity checks */ + assert ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ); + linker_assert ( &u.ddq.dd.digest.h[0] == a, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[1] == b, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[2] == c, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[3] == d, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[4] == e, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[5] == f, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[6] == g, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.digest.h[7] == h, sha512_bad_layout ); + linker_assert ( &u.ddq.dd.data.qword[0] == w, sha512_bad_layout ); + + DBGC ( context, "SHA512 digesting:\n" ); + DBGC_HDA ( context, 0, &context->ddq.dd.digest, + sizeof ( context->ddq.dd.digest ) ); + DBGC_HDA ( context, context->len, &context->ddq.dd.data, + sizeof ( context->ddq.dd.data ) ); + + /* Convert h[0..7] to host-endian, and initialise a, b, c, d, + * e, f, g, h, and w[0..15] + */ + for ( i = 0 ; i < ( sizeof ( u.ddq.qword ) / + sizeof ( u.ddq.qword[0] ) ) ; i++ ) { + be64_to_cpus ( &context->ddq.qword[i] ); + u.ddq.qword[i] = context->ddq.qword[i]; + } + + /* Initialise w[16..79] */ + for ( i = 16 ; i < SHA512_ROUNDS ; i++ ) { + s0 = ( ror64 ( w[i-15], 1 ) ^ ror64 ( w[i-15], 8 ) ^ + ( w[i-15] >> 7 ) ); + s1 = ( ror64 ( w[i-2], 19 ) ^ ror64 ( w[i-2], 61 ) ^ + ( w[i-2] >> 6 ) ); + w[i] = ( w[i-16] + s0 + w[i-7] + s1 ); + } + + /* Main loop */ + for ( i = 0 ; i < SHA512_ROUNDS ; i++ ) { + s0 = ( ror64 ( *a, 28 ) ^ ror64 ( *a, 34 ) ^ ror64 ( *a, 39 ) ); + maj = ( ( *a & *b ) ^ ( *a & *c ) ^ ( *b & *c ) ); + t2 = ( s0 + maj ); + s1 = ( ror64 ( *e, 14 ) ^ ror64 ( *e, 18 ) ^ ror64 ( *e, 41 ) ); + ch = ( ( *e & *f ) ^ ( (~*e) & *g ) ); + t1 = ( *h + s1 + ch + k[i] + w[i] ); + *h = *g; + *g = *f; + *f = *e; + *e = ( *d + t1 ); + *d = *c; + *c = *b; + *b = *a; + *a = ( t1 + t2 ); + DBGC2 ( context, "%2d : %016llx %016llx %016llx %016llx " + "%016llx %016llx %016llx %016llx\n", + i, *a, *b, *c, *d, *e, *f, *g, *h ); + } + + /* Add chunk to hash and convert back to big-endian */ + for ( i = 0 ; i < 8 ; i++ ) { + context->ddq.dd.digest.h[i] = + cpu_to_be64 ( context->ddq.dd.digest.h[i] + + u.ddq.dd.digest.h[i] ); + } + + DBGC ( context, "SHA512 digested:\n" ); + DBGC_HDA ( context, 0, &context->ddq.dd.digest, + sizeof ( context->ddq.dd.digest ) ); +} + +/** + * Accumulate data with SHA-512 algorithm + * + * @v ctx SHA-512 context + * @v data Data + * @v len Length of data + */ +void sha512_update ( void *ctx, const void *data, size_t len ) { + struct sha512_context *context = ctx; + const uint8_t *byte = data; + size_t offset; + + /* Accumulate data a byte at a time, performing the digest + * whenever we fill the data buffer + */ + while ( len-- ) { + offset = ( context->len % sizeof ( context->ddq.dd.data ) ); + context->ddq.dd.data.byte[offset] = *(byte++); + context->len++; + if ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ) + sha512_digest ( context ); + } +} + +/** + * Generate SHA-512 digest + * + * @v ctx SHA-512 context + * @v out Output buffer + */ +void sha512_final ( void *ctx, void *out ) { + struct sha512_context *context = ctx; + uint64_t len_bits_hi; + uint64_t len_bits_lo; + uint8_t pad; + + /* Record length before pre-processing */ + len_bits_hi = 0; + len_bits_lo = cpu_to_be64 ( ( ( uint64_t ) context->len ) * 8 ); + + /* Pad with a single "1" bit followed by as many "0" bits as required */ + pad = 0x80; + do { + sha512_update ( ctx, &pad, sizeof ( pad ) ); + pad = 0x00; + } while ( ( context->len % sizeof ( context->ddq.dd.data ) ) != + offsetof ( typeof ( context->ddq.dd.data ), final.len_hi ) ); + + /* Append length (in bits) */ + sha512_update ( ctx, &len_bits_hi, sizeof ( len_bits_hi ) ); + sha512_update ( ctx, &len_bits_lo, sizeof ( len_bits_lo ) ); + assert ( ( context->len % sizeof ( context->ddq.dd.data ) ) == 0 ); + + /* Copy out final digest */ + memcpy ( out, &context->ddq.dd.digest, context->digestsize ); +} + +/** SHA-512 algorithm */ +struct digest_algorithm sha512_algorithm = { + .name = "sha512", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = sizeof ( struct sha512_digest ), + .init = sha512_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512" object identifier */ +static uint8_t oid_sha512[] = { ASN1_OID_SHA512 }; + +/** "sha512" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_algorithm __asn1_algorithm = { + .name = "sha512", + .digest = &sha512_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512 ), +}; diff --git a/roms/ipxe/src/crypto/sha512_224.c b/roms/ipxe/src/crypto/sha512_224.c new file mode 100644 index 000000000..8c37b566b --- /dev/null +++ b/roms/ipxe/src/crypto/sha512_224.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512/224 algorithm + * + */ + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha512.h> + +/** SHA-512/224 initial digest values */ +static const struct sha512_digest sha512_224_init_digest = { + .h = { + cpu_to_be64 ( 0x8c3d37c819544da2ULL ), + cpu_to_be64 ( 0x73e1996689dcd4d6ULL ), + cpu_to_be64 ( 0x1dfab7ae32ff9c82ULL ), + cpu_to_be64 ( 0x679dd514582f9fcfULL ), + cpu_to_be64 ( 0x0f6d2b697bd44da8ULL ), + cpu_to_be64 ( 0x77e36f7304c48942ULL ), + cpu_to_be64 ( 0x3f9d85a86a1d36c8ULL ), + cpu_to_be64 ( 0x1112e6ad91d692a1ULL ), + }, +}; + +/** + * Initialise SHA-512/224 algorithm + * + * @v ctx SHA-512/224 context + */ +static void sha512_224_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_224_init_digest, + SHA512_224_DIGEST_SIZE ); +} + +/** SHA-512/224 algorithm */ +struct digest_algorithm sha512_224_algorithm = { + .name = "sha512/224", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA512_224_DIGEST_SIZE, + .init = sha512_224_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512_224" object identifier */ +static uint8_t oid_sha512_224[] = { ASN1_OID_SHA512_224 }; + +/** "sha512_224" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_224_algorithm __asn1_algorithm = { + .name = "sha512/224", + .digest = &sha512_224_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_224 ), +}; diff --git a/roms/ipxe/src/crypto/sha512_256.c b/roms/ipxe/src/crypto/sha512_256.c new file mode 100644 index 000000000..f8afaf3e3 --- /dev/null +++ b/roms/ipxe/src/crypto/sha512_256.c @@ -0,0 +1,83 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512/256 algorithm + * + */ + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/crypto.h> +#include <ipxe/asn1.h> +#include <ipxe/sha512.h> + +/** SHA-512/256 initial digest values */ +static const struct sha512_digest sha512_256_init_digest = { + .h = { + cpu_to_be64 ( 0x22312194fc2bf72cULL ), + cpu_to_be64 ( 0x9f555fa3c84c64c2ULL ), + cpu_to_be64 ( 0x2393b86b6f53b151ULL ), + cpu_to_be64 ( 0x963877195940eabdULL ), + cpu_to_be64 ( 0x96283ee2a88effe3ULL ), + cpu_to_be64 ( 0xbe5e1e2553863992ULL ), + cpu_to_be64 ( 0x2b0199fc2c85b8aaULL ), + cpu_to_be64 ( 0x0eb72ddc81c52ca2ULL ), + }, +}; + +/** + * Initialise SHA-512/256 algorithm + * + * @v ctx SHA-512/256 context + */ +static void sha512_256_init ( void *ctx ) { + struct sha512_context *context = ctx; + + sha512_family_init ( context, &sha512_256_init_digest, + SHA512_256_DIGEST_SIZE ); +} + +/** SHA-512/256 algorithm */ +struct digest_algorithm sha512_256_algorithm = { + .name = "sha512/256", + .ctxsize = sizeof ( struct sha512_context ), + .blocksize = sizeof ( union sha512_block ), + .digestsize = SHA512_256_DIGEST_SIZE, + .init = sha512_256_init, + .update = sha512_update, + .final = sha512_final, +}; + +/** "sha512_256" object identifier */ +static uint8_t oid_sha512_256[] = { ASN1_OID_SHA512_256 }; + +/** "sha512_256" OID-identified algorithm */ +struct asn1_algorithm oid_sha512_256_algorithm __asn1_algorithm = { + .name = "sha512/256", + .digest = &sha512_256_algorithm, + .oid = ASN1_OID_CURSOR ( oid_sha512_256 ), +}; diff --git a/roms/ipxe/src/crypto/x509.c b/roms/ipxe/src/crypto/x509.c index 4a02dad14..43a4ca17a 100644 --- a/roms/ipxe/src/crypto/x509.c +++ b/roms/ipxe/src/crypto/x509.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> @@ -139,7 +143,8 @@ const char * x509_name ( struct x509_certificate *cert ) { } else { /* Certificate has no commonName: use SHA-1 fingerprint */ x509_fingerprint ( cert, digest, fingerprint ); - base16_encode ( fingerprint, sizeof ( fingerprint ), buf ); + base16_encode ( fingerprint, sizeof ( fingerprint ), + buf, sizeof ( buf ) ); } return buf; } @@ -1761,5 +1766,11 @@ int x509_validate_chain ( struct x509_chain *chain, time_t time, return -EACCES_USELESS; } +/* Drag in objects via x509_validate() */ +REQUIRING_SYMBOL ( x509_validate ); + /* Drag in certificate store */ REQUIRE_OBJECT ( certstore ); + +/* Drag in crypto configuration */ +REQUIRE_OBJECT ( config_crypto ); diff --git a/roms/ipxe/src/drivers/bitbash/bitbash.c b/roms/ipxe/src/drivers/bitbash/bitbash.c index 23ca30356..9b24f716c 100644 --- a/roms/ipxe/src/drivers/bitbash/bitbash.c +++ b/roms/ipxe/src/drivers/bitbash/bitbash.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/bitbash.h> diff --git a/roms/ipxe/src/drivers/bitbash/i2c_bit.c b/roms/ipxe/src/drivers/bitbash/i2c_bit.c index decc8d80e..707d9447d 100644 --- a/roms/ipxe/src/drivers/bitbash/i2c_bit.c +++ b/roms/ipxe/src/drivers/bitbash/i2c_bit.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> diff --git a/roms/ipxe/src/drivers/bitbash/spi_bit.c b/roms/ipxe/src/drivers/bitbash/spi_bit.c index 1b39d72fa..04fddc20b 100644 --- a/roms/ipxe/src/drivers/bitbash/spi_bit.c +++ b/roms/ipxe/src/drivers/bitbash/spi_bit.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> diff --git a/roms/ipxe/src/drivers/block/ata.c b/roms/ipxe/src/drivers/block/ata.c index c9b87c20c..b1c6855a0 100644 --- a/roms/ipxe/src/drivers/block/ata.c +++ b/roms/ipxe/src/drivers/block/ata.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/block/scsi.c b/roms/ipxe/src/drivers/block/scsi.c index 64d692986..fd5f82b9f 100644 --- a/roms/ipxe/src/drivers/block/scsi.c +++ b/roms/ipxe/src/drivers/block/scsi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/bus/cdc.c b/roms/ipxe/src/drivers/bus/cdc.c new file mode 100644 index 000000000..373a03072 --- /dev/null +++ b/roms/ipxe/src/drivers/bus/cdc.c @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> +#include <ipxe/usb.h> +#include <ipxe/cdc.h> + +/** @file + * + * USB Communications Device Class (CDC) + * + */ + +/** + * Locate CDC union functional descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @ret desc Union functional descriptor, or NULL if not found + */ +struct cdc_union_descriptor * +cdc_union_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ) { + struct cdc_union_descriptor *desc; + + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) && + ( desc->subtype == CDC_SUBTYPE_UNION ) ) + return desc; + } + return NULL; +} diff --git a/roms/ipxe/src/drivers/bus/pci.c b/roms/ipxe/src/drivers/bus/pci.c index 4a8d00b54..6fbedd940 100644 --- a/roms/ipxe/src/drivers/bus/pci.c +++ b/roms/ipxe/src/drivers/bus/pci.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -58,8 +62,8 @@ static unsigned long pci_bar ( struct pci_device *pci, unsigned int reg ) { uint32_t high; pci_read_config_dword ( pci, reg, &low ); - if ( ( low & (PCI_BASE_ADDRESS_SPACE|PCI_BASE_ADDRESS_MEM_TYPE_MASK) ) - == (PCI_BASE_ADDRESS_SPACE_MEMORY|PCI_BASE_ADDRESS_MEM_TYPE_64) ){ + if ( ( low & (PCI_BASE_ADDRESS_SPACE_IO|PCI_BASE_ADDRESS_MEM_TYPE_MASK)) + == PCI_BASE_ADDRESS_MEM_TYPE_64 ) { pci_read_config_dword ( pci, reg + 4, &high ); if ( high ) { if ( sizeof ( unsigned long ) > sizeof ( uint32_t ) ) { @@ -93,10 +97,10 @@ unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg ) { unsigned long bar; bar = pci_bar ( pci, reg ); - if ( (bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_MEMORY ){ - return ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { + return ( bar & ~PCI_BASE_ADDRESS_IO_MASK ); } else { - return ( bar & PCI_BASE_ADDRESS_IO_MASK ); + return ( bar & ~PCI_BASE_ADDRESS_MEM_MASK ); } } @@ -122,11 +126,11 @@ static void pci_read_bases ( struct pci_device *pci ) { if ( bar & PCI_BASE_ADDRESS_SPACE_IO ) { if ( ! pci->ioaddr ) pci->ioaddr = - ( bar & PCI_BASE_ADDRESS_IO_MASK ); + ( bar & ~PCI_BASE_ADDRESS_IO_MASK ); } else { if ( ! pci->membase ) pci->membase = - ( bar & PCI_BASE_ADDRESS_MEM_MASK ); + ( bar & ~PCI_BASE_ADDRESS_MEM_MASK ); /* Skip next BAR if 64-bit */ if ( bar & PCI_BASE_ADDRESS_MEM_TYPE_64 ) reg += 4; @@ -181,7 +185,7 @@ int pci_read_config ( struct pci_device *pci ) { pci->busdevfn = PCI_FIRST_FUNC ( pci->busdevfn ); pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdrtype ); pci->busdevfn = busdevfn; - if ( ! ( hdrtype & 0x80 ) ) + if ( ! ( hdrtype & PCI_HEADER_TYPE_MULTI ) ) return -ENODEV; } @@ -253,6 +257,8 @@ int pci_find_driver ( struct pci_device *pci ) { unsigned int i; for_each_table_entry ( driver, PCI_DRIVERS ) { + if ( ( driver->class.class ^ pci->class ) & driver->class.mask ) + continue; for ( i = 0 ; i < driver->id_count ; i++ ) { id = &driver->ids[i]; if ( ( id->vendor != PCI_ANY_ID ) && @@ -334,14 +340,15 @@ static int pcibus_probe ( struct root_device *rootdev ) { /* Look for a driver */ if ( ( rc = pci_find_driver ( pci ) ) != 0 ) { - DBGC ( pci, PCI_FMT " (%04x:%04x) has no driver\n", - PCI_ARGS ( pci ), pci->vendor, pci->device ); + DBGC ( pci, PCI_FMT " (%04x:%04x class %06x) has no " + "driver\n", PCI_ARGS ( pci ), pci->vendor, + pci->device, pci->class ); continue; } /* Add to device hierarchy */ pci->dev.parent = &rootdev->dev; - list_add ( &pci->dev.siblings, &rootdev->dev.children); + list_add ( &pci->dev.siblings, &rootdev->dev.children ); /* Look for a driver */ if ( ( rc = pci_probe ( pci ) ) == 0 ) { diff --git a/roms/ipxe/src/drivers/bus/pci_settings.c b/roms/ipxe/src/drivers/bus/pci_settings.c index db20452e0..1cb9fa5a3 100644 --- a/roms/ipxe/src/drivers/bus/pci_settings.c +++ b/roms/ipxe/src/drivers/bus/pci_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/drivers/bus/pcibackup.c b/roms/ipxe/src/drivers/bus/pcibackup.c index 6b592e893..fecad8192 100644 --- a/roms/ipxe/src/drivers/bus/pcibackup.c +++ b/roms/ipxe/src/drivers/bus/pcibackup.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/pci.h> diff --git a/roms/ipxe/src/drivers/bus/pciextra.c b/roms/ipxe/src/drivers/bus/pciextra.c index c4417e0cb..82287fb86 100644 --- a/roms/ipxe/src/drivers/bus/pciextra.c +++ b/roms/ipxe/src/drivers/bus/pciextra.c @@ -1,4 +1,4 @@ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/pci.h> @@ -26,7 +26,7 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { return 0; pci_read_config_byte ( pci, PCI_HEADER_TYPE, &hdr_type ); - switch ( hdr_type & 0x7F ) { + switch ( hdr_type & PCI_HEADER_TYPE_MASK ) { case PCI_HEADER_TYPE_NORMAL: case PCI_HEADER_TYPE_BRIDGE: default: @@ -38,13 +38,13 @@ int pci_find_capability ( struct pci_device *pci, int cap ) { } while ( ttl-- && pos >= 0x40 ) { pos &= ~3; - pci_read_config_byte ( pci, pos + PCI_CAP_LIST_ID, &id ); + pci_read_config_byte ( pci, pos + PCI_CAP_ID, &id ); DBG ( "PCI Capability: %d\n", id ); if ( id == 0xff ) break; if ( id == cap ) return pos; - pci_read_config_byte ( pci, pos + PCI_CAP_LIST_NEXT, &pos ); + pci_read_config_byte ( pci, pos + PCI_CAP_NEXT, &pos ); } return 0; } @@ -76,9 +76,9 @@ unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg ) { /* Restore the original command register. This reenables decoding. */ pci_write_config_word ( pci, PCI_COMMAND, cmd ); if ( start & PCI_BASE_ADDRESS_SPACE_IO ) { - size &= PCI_BASE_ADDRESS_IO_MASK; + size &= ~PCI_BASE_ADDRESS_IO_MASK; } else { - size &= PCI_BASE_ADDRESS_MEM_MASK; + size &= ~PCI_BASE_ADDRESS_MEM_MASK; } /* Find the lowest bit set */ size = size & ~( size - 1 ); diff --git a/roms/ipxe/src/drivers/bus/pcivpd.c b/roms/ipxe/src/drivers/bus/pcivpd.c index 0b7a879fe..243b1f779 100644 --- a/roms/ipxe/src/drivers/bus/pcivpd.c +++ b/roms/ipxe/src/drivers/bus/pcivpd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/bus/usb.c b/roms/ipxe/src/drivers/bus/usb.c new file mode 100644 index 000000000..2019e3341 --- /dev/null +++ b/roms/ipxe/src/drivers/bus/usb.c @@ -0,0 +1,2128 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <strings.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/usb.h> +#include <ipxe/cdc.h> + +/** @file + * + * Universal Serial Bus (USB) + * + */ + +/** List of USB buses */ +struct list_head usb_buses = LIST_HEAD_INIT ( usb_buses ); + +/** List of changed ports */ +static struct list_head usb_changed = LIST_HEAD_INIT ( usb_changed ); + +/** List of halted endpoints */ +static struct list_head usb_halted = LIST_HEAD_INIT ( usb_halted ); + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + +/** + * Get USB speed name (for debugging) + * + * @v speed Speed + * @ret name Speed name + */ +static inline const char * usb_speed_name ( unsigned int speed ) { + static const char *exponents[4] = { "", "k", "M", "G" }; + static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; + unsigned int mantissa; + unsigned int exponent; + + /* Extract mantissa and exponent */ + mantissa = USB_SPEED_MANTISSA ( speed ); + exponent = USB_SPEED_EXPONENT ( speed ); + + /* Name speed */ + switch ( speed ) { + case USB_SPEED_NONE: return "DETACHED"; + case USB_SPEED_LOW: return "low"; + case USB_SPEED_FULL: return "full"; + case USB_SPEED_HIGH: return "high"; + case USB_SPEED_SUPER: return "super"; + default: + snprintf ( buf, sizeof ( buf ), "%d%sbps", + mantissa, exponents[exponent] ); + return buf; + } +} + +/** + * Transcribe USB BCD-coded value (for debugging) + * + * @v bcd BCD-coded value + * @ret string Transcribed value + */ +static inline const char * usb_bcd ( uint16_t bcd ) { + static char buf[ 6 /* "xx.xx" + NUL */ ]; + uint8_t high = ( bcd >> 8 ); + uint8_t low = ( bcd >> 0 ); + + snprintf ( buf, sizeof ( buf ), "%x.%02x", high, low ); + return buf; +} + +/****************************************************************************** + * + * USB descriptors + * + ****************************************************************************** + */ + +/** + * Locate USB interface association descriptor + * + * @v config Configuraton descriptor + * @v first First interface number + * @ret desc Interface association descriptor, or NULL if not found + */ +static struct usb_interface_association_descriptor * +usb_interface_association_descriptor ( struct usb_configuration_descriptor + *config, + unsigned int first ) { + struct usb_interface_association_descriptor *desc; + + /* Find a matching interface association descriptor */ + for_each_config_descriptor ( desc, config ) { + if ( ( desc->header.type == + USB_INTERFACE_ASSOCIATION_DESCRIPTOR ) && + ( desc->first == first ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB interface descriptor + * + * @v config Configuraton descriptor + * @v interface Interface number + * @v alternate Alternate setting + * @ret desc Interface descriptor, or NULL if not found + */ +struct usb_interface_descriptor * +usb_interface_descriptor ( struct usb_configuration_descriptor *config, + unsigned int interface, unsigned int alternate ) { + struct usb_interface_descriptor *desc; + + /* Find a matching interface descriptor */ + for_each_config_descriptor ( desc, config ) { + if ( ( desc->header.type == USB_INTERFACE_DESCRIPTOR ) && + ( desc->interface == interface ) && + ( desc->alternate == alternate ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB endpoint descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @v type Endpoint (internal) type + * @v index Endpoint index + * @ret desc Descriptor, or NULL if not found + */ +struct usb_endpoint_descriptor * +usb_endpoint_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ) { + struct usb_endpoint_descriptor *desc; + unsigned int attributes = ( type & USB_ENDPOINT_ATTR_TYPE_MASK ); + unsigned int direction = ( type & USB_DIR_IN ); + + /* Find a matching endpoint descriptor */ + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_ENDPOINT_DESCRIPTOR ) && + ( ( desc->attributes & + USB_ENDPOINT_ATTR_TYPE_MASK ) == attributes ) && + ( ( desc->endpoint & USB_DIR_IN ) == direction ) && + ( index-- == 0 ) ) + return desc; + } + return NULL; +} + +/** + * Locate USB endpoint companion descriptor + * + * @v config Configuration descriptor + * @v desc Endpoint descriptor + * @ret descx Companion descriptor, or NULL if not found + */ +struct usb_endpoint_companion_descriptor * +usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config, + struct usb_endpoint_descriptor *desc ) { + struct usb_endpoint_companion_descriptor *descx; + + /* Get companion descriptor, if present */ + descx = container_of ( usb_next_descriptor ( &desc->header ), + struct usb_endpoint_companion_descriptor, + header ); + return ( ( usb_is_within_config ( config, &descx->header ) && + descx->header.type == USB_ENDPOINT_COMPANION_DESCRIPTOR ) + ? descx : NULL ); +} + +/****************************************************************************** + * + * USB endpoint + * + ****************************************************************************** + */ + +/** + * Get USB endpoint name (for debugging) + * + * @v ep USB endpoint + * @ret name Endpoint name + */ +const char * usb_endpoint_name ( struct usb_endpoint *ep ) { + static char buf[ 9 /* "EPxx OUT" + NUL */ ]; + unsigned int address = ep->address; + + snprintf ( buf, sizeof ( buf ), "EP%d%s", + ( address & USB_ENDPOINT_MAX ), + ( address ? + ( ( address & USB_ENDPOINT_IN ) ? " IN" : " OUT" ) : "" )); + return buf; +} + +/** + * Describe USB endpoint from device configuration + * + * @v ep USB endpoint + * @v config Configuration descriptor + * @v interface Interface descriptor + * @v type Endpoint (internal) type + * @v index Endpoint index + * @ret rc Return status code + */ +int usb_endpoint_described ( struct usb_endpoint *ep, + struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + struct usb_endpoint_descriptor *desc; + struct usb_endpoint_companion_descriptor *descx; + unsigned int sizes; + unsigned int burst; + unsigned int interval; + size_t mtu; + + /* Locate endpoint descriptor */ + desc = usb_endpoint_descriptor ( config, interface, type, index ); + if ( ! desc ) + return -ENOENT; + + /* Locate companion descriptor, if any */ + descx = usb_endpoint_companion_descriptor ( config, desc ); + + /* Calculate MTU and burst size */ + sizes = le16_to_cpu ( desc->sizes ); + mtu = USB_ENDPOINT_MTU ( sizes ); + burst = ( descx ? descx->burst : USB_ENDPOINT_BURST ( sizes ) ); + + /* Calculate interval */ + if ( ( type & USB_ENDPOINT_ATTR_TYPE_MASK ) == + USB_ENDPOINT_ATTR_INTERRUPT ) { + if ( port->speed >= USB_SPEED_HIGH ) { + /* 2^(desc->interval-1) is a microframe count */ + interval = ( 1 << ( desc->interval - 1 ) ); + } else { + /* desc->interval is a (whole) frame count */ + interval = ( desc->interval << 3 ); + } + } else { + /* desc->interval is a microframe count */ + interval = desc->interval; + } + + /* Describe endpoint */ + usb_endpoint_describe ( ep, desc->endpoint, desc->attributes, + mtu, burst, interval ); + return 0; +} + +/** + * Open USB endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); + int rc; + + /* Populate host controller operations */ + ep->host = &usb->port->hub->bus->op->endpoint; + + /* Add to endpoint list */ + if ( usb->ep[idx] != NULL ) { + DBGC ( usb, "USB %s %s is already open\n", + usb->name, usb_endpoint_name ( ep ) ); + rc = -EALREADY; + goto err_already; + } + usb->ep[idx] = ep; + INIT_LIST_HEAD ( &ep->halted ); + + /* Open endpoint */ + if ( ( rc = ep->host->open ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not open: %s\n", usb->name, + usb_endpoint_name ( ep ), strerror ( rc ) ); + goto err_open; + } + ep->open = 1; + + DBGC2 ( usb, "USB %s %s opened with MTU %zd, burst %d, interval %d\n", + usb->name, usb_endpoint_name ( ep ), ep->mtu, ep->burst, + ep->interval ); + return 0; + + ep->open = 0; + ep->host->close ( ep ); + err_open: + usb->ep[idx] = NULL; + err_already: + if ( ep->max ) + usb_flush ( ep ); + return rc; +} + +/** + * Clear transaction translator (if applicable) + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int usb_endpoint_clear_tt ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct usb_port *tt; + int rc; + + /* Do nothing if this is a periodic endpoint */ + if ( ep->attributes & USB_ENDPOINT_ATTR_PERIODIC ) + return 0; + + /* Do nothing if this endpoint is not behind a transaction translator */ + tt = usb_transaction_translator ( usb ); + if ( ! tt ) + return 0; + + /* Clear transaction translator buffer */ + if ( ( rc = tt->hub->driver->clear_tt ( tt->hub, tt, ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not clear transaction translator: " + "%s\n", usb->name, usb_endpoint_name ( ep ), + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Close USB endpoint + * + * @v ep USB endpoint + */ +void usb_endpoint_close ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int idx = USB_ENDPOINT_IDX ( ep->address ); + + /* Sanity checks */ + assert ( usb->ep[idx] == ep ); + + /* Close endpoint */ + ep->open = 0; + ep->host->close ( ep ); + assert ( ep->fill == 0 ); + + /* Remove from endpoint list */ + usb->ep[idx] = NULL; + list_del ( &ep->halted ); + + /* Discard any recycled buffers, if applicable */ + if ( ep->max ) + usb_flush ( ep ); + + /* Clear transaction translator, if applicable */ + usb_endpoint_clear_tt ( ep ); +} + +/** + * Reset USB endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int usb_endpoint_reset ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int type; + int rc; + + /* Sanity check */ + assert ( ! list_empty ( &ep->halted ) ); + + /* Reset endpoint */ + if ( ( rc = ep->host->reset ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not reset: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Clear transaction translator, if applicable */ + if ( ( rc = usb_endpoint_clear_tt ( ep ) ) != 0 ) + return rc; + + /* Clear endpoint halt, if applicable */ + type = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + if ( ( type != USB_ENDPOINT_ATTR_CONTROL ) && + ( ( rc = usb_clear_feature ( usb, USB_RECIP_ENDPOINT, + USB_ENDPOINT_HALT, + ep->address ) ) != 0 ) ) { + DBGC ( usb, "USB %s %s could not clear endpoint halt: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Remove from list of halted endpoints */ + list_del ( &ep->halted ); + INIT_LIST_HEAD ( &ep->halted ); + + DBGC ( usb, "USB %s %s reset\n", + usb->name, usb_endpoint_name ( ep ) ); + return 0; +} + +/** + * Update endpoint MTU + * + * @v ep USB endpoint + * @v mtu New MTU + * @ret rc Return status code + */ +static int usb_endpoint_mtu ( struct usb_endpoint *ep, size_t mtu ) { + struct usb_device *usb = ep->usb; + int rc; + + /* Update MTU */ + ep->mtu = mtu; + if ( ( rc = ep->host->mtu ( ep ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not update MTU: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Enqueue USB message transfer + * + * @v ep USB endpoint + * @v request Request + * @v value Value parameter + * @v index Index parameter + * @v iobuf I/O buffer + * @ret rc Return status code + * + * The I/O buffer must have sufficient headroom to contain a setup + * packet. + */ +int usb_message ( struct usb_endpoint *ep, unsigned int request, + unsigned int value, unsigned int index, + struct io_buffer *iobuf ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + struct usb_setup_packet *packet; + size_t len = iob_len ( iobuf ); + int rc; + + /* Sanity check */ + assert ( iob_headroom ( iobuf ) >= sizeof ( *packet ) ); + + /* Fail immediately if device has been unplugged */ + if ( port->speed == USB_SPEED_NONE ) + return -ENODEV; + + /* Reset endpoint if required */ + if ( ( ! list_empty ( &ep->halted ) ) && + ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) + return rc; + + /* Zero input data buffer (if applicable) */ + if ( request & USB_DIR_IN ) + memset ( iobuf->data, 0, len ); + + /* Construct setup packet */ + packet = iob_push ( iobuf, sizeof ( *packet ) ); + packet->request = cpu_to_le16 ( request ); + packet->value = cpu_to_le16 ( value ); + packet->index = cpu_to_le16 ( index ); + packet->len = cpu_to_le16 ( len ); + + /* Enqueue message transfer */ + if ( ( rc = ep->host->message ( ep, iobuf ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not enqueue message transfer: " + "%s\n", usb->name, usb_endpoint_name ( ep ), + strerror ( rc ) ); + return rc; + } + + /* Increment fill level */ + ep->fill++; + + return 0; +} + +/** + * Enqueue USB stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ) { + struct usb_device *usb = ep->usb; + struct usb_port *port = usb->port; + int rc; + + /* Fail immediately if device has been unplugged */ + if ( port->speed == USB_SPEED_NONE ) + return -ENODEV; + + /* Reset endpoint if required */ + if ( ( ! list_empty ( &ep->halted ) ) && + ( ( rc = usb_endpoint_reset ( ep ) ) != 0 ) ) + return rc; + + /* Enqueue stream transfer */ + if ( ( rc = ep->host->stream ( ep, iobuf, terminate ) ) != 0 ) { + DBGC ( usb, "USB %s %s could not enqueue stream transfer: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return rc; + } + + /* Increment fill level */ + ep->fill++; + + return 0; +} + +/** + * Complete transfer (possibly with error) + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +void usb_complete_err ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct usb_device *usb = ep->usb; + + /* Decrement fill level */ + assert ( ep->fill > 0 ); + ep->fill--; + + /* Schedule reset, if applicable */ + if ( ( rc != 0 ) && ep->open ) { + DBGC ( usb, "USB %s %s completion failed: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + list_del ( &ep->halted ); + list_add_tail ( &ep->halted, &usb_halted ); + } + + /* Report completion */ + ep->driver->complete ( ep, iobuf, rc ); +} + +/****************************************************************************** + * + * Endpoint refilling + * + ****************************************************************************** + */ + +/** + * Prefill endpoint recycled buffer list + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_prefill ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + size_t len = ( ep->len ? ep->len : ep->mtu ); + unsigned int fill; + int rc; + + /* Sanity checks */ + assert ( ep->fill == 0 ); + assert ( ep->max > 0 ); + assert ( list_empty ( &ep->recycled ) ); + + /* Fill recycled buffer list */ + for ( fill = 0 ; fill < ep->max ; fill++ ) { + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Add to recycled buffer list */ + list_add_tail ( &iobuf->list, &ep->recycled ); + } + + return 0; + + err_alloc: + usb_flush ( ep ); + return rc; +} + +/** + * Refill endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +int usb_refill ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + size_t len = ( ep->len ? ep->len : ep->mtu ); + int rc; + + /* Sanity checks */ + assert ( ep->open ); + assert ( ep->max > 0 ); + + /* Refill endpoint */ + while ( ep->fill < ep->max ) { + + /* Get or allocate buffer */ + if ( list_empty ( &ep->recycled ) ) { + /* Recycled buffer list is empty; allocate new buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) + return -ENOMEM; + } else { + /* Get buffer from recycled buffer list */ + iobuf = list_first_entry ( &ep->recycled, + struct io_buffer, list ); + assert ( iobuf != NULL ); + list_del ( &iobuf->list ); + } + + /* Reset buffer to maximum size */ + assert ( iob_len ( iobuf ) <= len ); + iob_put ( iobuf, ( len - iob_len ( iobuf ) ) ); + + /* Enqueue buffer */ + if ( ( rc = usb_stream ( ep, iobuf, 0 ) ) != 0 ) { + list_add ( &iobuf->list, &ep->recycled ); + return rc; + } + } + + return 0; +} + +/** + * Discard endpoint recycled buffer list + * + * @v ep USB endpoint + */ +void usb_flush ( struct usb_endpoint *ep ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Sanity checks */ + assert ( ! ep->open ); + assert ( ep->max > 0 ); + + /* Free all I/O buffers */ + list_for_each_entry_safe ( iobuf, tmp, &ep->recycled, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } +} + +/****************************************************************************** + * + * Control endpoint + * + ****************************************************************************** + */ + +/** USB control transfer pseudo-header */ +struct usb_control_pseudo_header { + /** Completion status */ + int rc; +}; + +/** + * Complete USB control transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void usb_control_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_device *usb = ep->usb; + struct usb_control_pseudo_header *pshdr; + + /* Record completion status in buffer */ + pshdr = iob_push ( iobuf, sizeof ( *pshdr ) ); + pshdr->rc = rc; + + /* Add to list of completed I/O buffers */ + list_add_tail ( &iobuf->list, &usb->complete ); +} + +/** USB control endpoint driver operations */ +static struct usb_endpoint_driver_operations usb_control_operations = { + .complete = usb_control_complete, +}; + +/** + * Issue USB control transaction + * + * @v usb USB device + * @v request Request + * @v value Value parameter + * @v index Index parameter + * @v data Data buffer (if any) + * @v len Length of data + * @ret rc Return status code + */ +int usb_control ( struct usb_device *usb, unsigned int request, + unsigned int value, unsigned int index, void *data, + size_t len ) { + struct usb_bus *bus = usb->port->hub->bus; + struct usb_endpoint *ep = &usb->control; + struct io_buffer *iobuf; + struct io_buffer *cmplt; + union { + struct usb_setup_packet setup; + struct usb_control_pseudo_header pshdr; + } *headroom; + struct usb_control_pseudo_header *pshdr; + unsigned int i; + int rc; + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( sizeof ( *headroom ) + len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + iob_reserve ( iobuf, sizeof ( *headroom ) ); + iob_put ( iobuf, len ); + if ( request & USB_DIR_IN ) { + memset ( data, 0, len ); + } else { + memcpy ( iobuf->data, data, len ); + } + + /* Enqueue message */ + if ( ( rc = usb_message ( ep, request, value, index, iobuf ) ) != 0 ) + goto err_message; + + /* Wait for completion */ + for ( i = 0 ; i < USB_CONTROL_MAX_WAIT_MS ; i++ ) { + + /* Poll bus */ + usb_poll ( bus ); + + /* Check for completion */ + while ( ( cmplt = list_first_entry ( &usb->complete, + struct io_buffer, + list ) ) ) { + + /* Remove from completion list */ + list_del ( &cmplt->list ); + + /* Extract and strip completion status */ + pshdr = cmplt->data; + iob_pull ( cmplt, sizeof ( *pshdr ) ); + rc = pshdr->rc; + + /* Discard stale completions */ + if ( cmplt != iobuf ) { + DBGC ( usb, "USB %s stale control completion: " + "%s\n", usb->name, strerror ( rc ) ); + DBGC_HDA ( usb, 0, cmplt->data, + iob_len ( cmplt ) ); + free_iob ( cmplt ); + continue; + } + + /* Fail immediately if completion was in error */ + if ( rc != 0 ) { + DBGC ( usb, "USB %s control %04x:%04x:%04x " + "failed: %s\n", usb->name, request, + value, index, strerror ( rc ) ); + free_iob ( cmplt ); + return rc; + } + + /* Copy completion to data buffer, if applicable */ + assert ( iob_len ( cmplt ) <= len ); + if ( request & USB_DIR_IN ) + memcpy ( data, cmplt->data, iob_len ( cmplt ) ); + free_iob ( cmplt ); + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( usb, "USB %s timed out waiting for control %04x:%04x:%04x\n", + usb->name, request, value, index ); + return -ETIMEDOUT; + + err_message: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Get USB string descriptor + * + * @v usb USB device + * @v index String index + * @v language Language ID + * @v buf Data buffer + * @v len Length of buffer + * @ret len String length (excluding NUL), or negative error + */ +int usb_get_string_descriptor ( struct usb_device *usb, unsigned int index, + unsigned int language, char *buf, size_t len ) { + size_t max = ( len ? ( len - 1 /* NUL */ ) : 0 ); + struct { + struct usb_descriptor_header header; + uint16_t character[max]; + } __attribute__ (( packed )) *desc; + unsigned int actual; + unsigned int i; + int rc; + + /* Allocate buffer for string */ + desc = malloc ( sizeof ( *desc ) ); + if ( ! desc ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Get descriptor */ + if ( ( rc = usb_get_descriptor ( usb, 0, USB_STRING_DESCRIPTOR, index, + language, &desc->header, + sizeof ( *desc ) ) ) != 0 ) + goto err_get_descriptor; + + /* Copy to buffer */ + actual = ( ( desc->header.len - sizeof ( desc->header ) ) / + sizeof ( desc->character[0] ) ); + for ( i = 0 ; ( ( i < actual ) && ( i < max ) ) ; i++ ) + buf[i] = le16_to_cpu ( desc->character[i] ); + if ( len ) + buf[i] = '\0'; + + /* Free buffer */ + free ( desc ); + + return actual; + + err_get_descriptor: + free ( desc ); + err_alloc: + return rc; +} + +/****************************************************************************** + * + * USB device driver + * + ****************************************************************************** + */ + +/** + * Describe USB function + * + * @v func USB function + * @v config Configuration descriptor + * @v first First interface number + * @ret rc Return status code + */ +static int usb_function ( struct usb_function *func, + struct usb_configuration_descriptor *config, + unsigned int first ) { + struct usb_device *usb = func->usb; + struct usb_interface_association_descriptor *association; + struct usb_interface_descriptor *interface; + struct cdc_union_descriptor *cdc_union; + unsigned int i; + + /* First, look for an interface association descriptor */ + association = usb_interface_association_descriptor ( config, first ); + if ( association ) { + + /* Sanity check */ + if ( association->count > config->interfaces ) { + DBGC ( usb, "USB %s has invalid association [%d-%d)\n", + func->name, association->first, + ( association->first + association->count ) ); + return -ERANGE; + } + + /* Describe function */ + memcpy ( &func->class, &association->class, + sizeof ( func->class ) ); + func->count = association->count; + for ( i = 0 ; i < association->count ; i++ ) + func->interface[i] = ( association->first + i ); + return 0; + } + + /* Next, look for an interface descriptor */ + interface = usb_interface_descriptor ( config, first, 0 ); + if ( ! interface ) { + DBGC ( usb, "USB %s has no interface descriptor\n", + func->name ); + return -ENOENT; + } + + /* Describe function */ + memcpy ( &func->class, &interface->class, sizeof ( func->class ) ); + func->count = 1; + func->interface[0] = first; + + /* Look for a CDC union descriptor, if applicable */ + if ( ( func->class.class == USB_CLASS_CDC ) && + ( cdc_union = cdc_union_descriptor ( config, interface ) ) ) { + + /* Determine interface count */ + func->count = ( ( cdc_union->header.len - + offsetof ( typeof ( *cdc_union ), + interface[0] ) ) / + sizeof ( cdc_union->interface[0] ) ); + if ( func->count > config->interfaces ) { + DBGC ( usb, "USB %s has invalid union functional " + "descriptor with %d interfaces\n", + func->name, func->count ); + return -ERANGE; + } + + /* Describe function */ + for ( i = 0 ; i < func->count ; i++ ) + func->interface[i] = cdc_union->interface[i]; + + return 0; + } + + return 0; +} + +/** + * Check for a USB device ID match + * + * @v func USB function + * @v id Device ID + * @ret matches Device ID matches + */ +static int +usb_device_id_matches ( struct usb_function *func, struct usb_device_id *id ) { + + return ( ( ( id->vendor == func->dev.desc.vendor ) || + ( id->vendor == USB_ANY_ID ) ) && + ( ( id->product == func->dev.desc.device ) || + ( id->product == USB_ANY_ID ) ) && + ( id->class.class == func->class.class ) && + ( id->class.subclass == func->class.subclass ) && + ( id->class.protocol == func->class.protocol ) ); +} + +/** + * Probe USB device driver + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usb_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_driver *driver; + struct usb_device_id *id; + unsigned int i; + int rc; + + /* Look for a matching driver */ + for_each_table_entry ( driver, USB_DRIVERS ) { + for ( i = 0 ; i < driver->id_count ; i++ ) { + + /* Check for a matching ID */ + id = &driver->ids[i]; + if ( ! usb_device_id_matches ( func, id ) ) + continue; + + /* Probe driver */ + if ( ( rc = driver->probe ( func, config ) ) != 0 ) { + DBGC ( usb, "USB %s failed to probe driver %s: " + "%s\n", func->name, id->name, + strerror ( rc ) ); + /* Continue trying other drivers */ + continue; + } + + /* Record driver */ + func->driver = driver; + func->dev.driver_name = id->name; + return 0; + } + } + + /* No driver found */ + DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d has no driver\n", + func->name, func->dev.desc.vendor, func->dev.desc.device, + func->class.class, func->class.subclass, func->class.protocol ); + return -ENOENT; +} + +/** + * Remove USB device driver + * + * @v func USB function + */ +static void usb_remove ( struct usb_function *func ) { + + /* Remove driver */ + func->driver->remove ( func ); +} + +/** + * Probe all USB device drivers + * + * @v usb USB device + * @v config Configuration descriptor + */ +static void +usb_probe_all ( struct usb_device *usb, + struct usb_configuration_descriptor *config ) { + struct usb_bus *bus = usb->port->hub->bus; + struct usb_function *func; + uint8_t used[config->interfaces]; + unsigned int first; + unsigned int i; + int rc; + + /* Identify each function in turn */ + memset ( used, 0, sizeof ( used ) ); + for ( first = 0 ; first < config->interfaces ; first++ ) { + + /* Skip interfaces already used */ + if ( used[first] ) + continue; + + /* Allocate and initialise structure */ + func = zalloc ( sizeof ( *func ) + + ( config->interfaces * + sizeof ( func->interface[0] ) ) ); + if ( ! func ) + goto err_alloc; + func->name = func->dev.name; + func->usb = usb; + func->dev.desc.bus_type = BUS_TYPE_USB; + func->dev.desc.location = usb->address; + func->dev.desc.vendor = le16_to_cpu ( usb->device.vendor ); + func->dev.desc.device = le16_to_cpu ( usb->device.product ); + snprintf ( func->dev.name, sizeof ( func->dev.name ), + "%s-%d.%d", usb->name, config->config, first ); + INIT_LIST_HEAD ( &func->dev.children ); + func->dev.parent = bus->dev; + + /* Identify function */ + if ( ( rc = usb_function ( func, config, first ) ) != 0 ) + goto err_function; + assert ( func->count <= config->interfaces ); + + /* Mark interfaces as used */ + for ( i = 0 ; i < func->count ; i++ ) { + if ( func->interface[i] >= config->interfaces ) { + DBGC ( usb, "USB %s has invalid interface %d\n", + func->name, func->interface[i] ); + goto err_interface; + } + used[ func->interface[i] ] = 1; + } + + /* Probe device driver */ + if ( ( rc = usb_probe ( func, config ) ) != 0 ) + goto err_probe; + DBGC ( usb, "USB %s %04x:%04x class %d:%d:%d interfaces ", + func->name, func->dev.desc.vendor, func->dev.desc.device, + func->class.class, func->class.subclass, + func->class.protocol ); + for ( i = 0 ; i < func->count ; i++ ) + DBGC ( usb, "%s%d", ( i ? "," : "" ), + func->interface[i] ); + DBGC ( usb, " using driver %s\n", func->dev.driver_name ); + + /* Add to list of functions */ + list_add ( &func->list, &usb->functions ); + + /* Add to device hierarchy */ + list_add_tail ( &func->dev.siblings, &bus->dev->children ); + + continue; + + list_del ( &func->dev.siblings ); + list_del ( &func->list ); + usb_remove ( func ); + err_probe: + free ( func ); + err_alloc: + err_interface: + err_function: + /* Continue registering other functions */ + continue; + } +} + +/** + * Remove all device drivers + * + * @v usb USB device + */ +static void usb_remove_all ( struct usb_device *usb ) { + struct usb_function *func; + struct usb_function *tmp; + + /* Remove all functions */ + list_for_each_entry_safe ( func, tmp, &usb->functions, list ) { + + /* Remove device driver */ + usb_remove ( func ); + + /* Remove from device hierarchy */ + assert ( list_empty ( &func->dev.children ) ); + list_del ( &func->dev.siblings ); + + /* Remove from list of functions */ + list_del ( &func->list ); + + /* Free function */ + free ( func ); + } +} + +/** + * Select USB device configuration + * + * @v usb USB device + * @v index Configuration index + * @ret rc Return status code + */ +static int usb_configure ( struct usb_device *usb, unsigned int index ) { + struct usb_configuration_descriptor partial; + struct usb_configuration_descriptor *config; + size_t len; + int rc; + + /* Read first part of configuration descriptor to get size */ + if ( ( rc = usb_get_config_descriptor ( usb, index, &partial, + sizeof ( partial ) ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_partial; + } + len = le16_to_cpu ( partial.len ); + if ( len < sizeof ( partial ) ) { + DBGC ( usb, "USB %s underlength configuraton descriptor %d\n", + usb->name, index ); + rc = -EINVAL; + goto err_partial_len; + } + + /* Allocate buffer for whole configuration descriptor */ + config = malloc ( len ); + if ( ! config ) { + rc = -ENOMEM; + goto err_alloc_config; + } + + /* Read whole configuration descriptor */ + if ( ( rc = usb_get_config_descriptor ( usb, index, config, + len ) ) != 0 ) { + DBGC ( usb, "USB %s could not get configuration descriptor %d: " + "%s\n", usb->name, index, strerror ( rc ) ); + goto err_get_config_descriptor; + } + if ( config->len != partial.len ) { + DBGC ( usb, "USB %s bad configuration descriptor %d length\n", + usb->name, index ); + rc = -EINVAL; + goto err_config_len; + } + + /* Set configuration */ + if ( ( rc = usb_set_configuration ( usb, config->config ) ) != 0){ + DBGC ( usb, "USB %s could not set configuration %d: %s\n", + usb->name, config->config, strerror ( rc ) ); + goto err_set_configuration; + } + + /* Probe USB device drivers */ + usb_probe_all ( usb, config ); + + /* Free configuration descriptor */ + free ( config ); + + return 0; + + usb_remove_all ( usb ); + usb_set_configuration ( usb, 0 ); + err_set_configuration: + err_config_len: + err_get_config_descriptor: + free ( config ); + err_alloc_config: + err_partial_len: + err_get_partial: + return rc; +} + +/** + * Clear USB device configuration + * + * @v usb USB device + */ +static void usb_deconfigure ( struct usb_device *usb ) { + unsigned int i; + + /* Remove device drivers */ + usb_remove_all ( usb ); + + /* Sanity checks */ + for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++){ + if ( i != USB_ENDPOINT_IDX ( USB_EP0_ADDRESS ) ) + assert ( usb->ep[i] == NULL ); + } + + /* Clear device configuration */ + usb_set_configuration ( usb, 0 ); +} + +/** + * Find and select a supported USB device configuration + * + * @v usb USB device + * @ret rc Return status code + */ +static int usb_configure_any ( struct usb_device *usb ) { + unsigned int index; + int rc = -ENOENT; + + /* Attempt all configuration indexes */ + for ( index = 0 ; index < usb->device.configurations ; index++ ) { + + /* Attempt this configuration index */ + if ( ( rc = usb_configure ( usb, index ) ) != 0 ) + continue; + + /* If we have no drivers, then try the next configuration */ + if ( list_empty ( &usb->functions ) ) { + rc = -ENOTSUP; + usb_deconfigure ( usb ); + continue; + } + + return 0; + } + + return rc; +} + +/****************************************************************************** + * + * USB device + * + ****************************************************************************** + */ + +/** + * Allocate USB device + * + * @v port USB port + * @ret usb USB device, or NULL on allocation failure + */ +static struct usb_device * alloc_usb ( struct usb_port *port ) { + struct usb_hub *hub = port->hub; + struct usb_bus *bus = hub->bus; + struct usb_device *usb; + + /* Allocate and initialise structure */ + usb = zalloc ( sizeof ( *usb ) ); + if ( ! usb ) + return NULL; + snprintf ( usb->name, sizeof ( usb->name ), "%s%c%d", hub->name, + ( hub->usb ? '.' : '-' ), port->address ); + usb->port = port; + INIT_LIST_HEAD ( &usb->functions ); + usb->host = &bus->op->device; + usb_endpoint_init ( &usb->control, usb, &usb_control_operations ); + INIT_LIST_HEAD ( &usb->complete ); + + return usb; +} + +/** + * Register USB device + * + * @v usb USB device + * @ret rc Return status code + */ +static int register_usb ( struct usb_device *usb ) { + struct usb_port *port = usb->port; + struct usb_hub *hub = port->hub; + struct usb_bus *bus = hub->bus; + unsigned int protocol; + size_t mtu; + int rc; + + /* Add to port */ + if ( port->usb != NULL ) { + DBGC ( hub, "USB hub %s port %d is already registered to %s\n", + hub->name, port->address, port->usb->name ); + rc = -EALREADY; + goto err_already; + } + port->usb = usb; + + /* Add to bus device list */ + list_add_tail ( &usb->list, &bus->devices ); + + /* Enable device */ + if ( ( rc = hub->driver->enable ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not enable: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_enable; + } + + /* Allow recovery interval since port may have been reset */ + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Get device speed */ + if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_speed; + } + DBGC2 ( usb, "USB %s attached as %s-speed device\n", + usb->name, usb_speed_name ( port->speed ) ); + + /* Open device */ + if ( ( rc = usb->host->open ( usb ) ) != 0 ) { + DBGC ( usb, "USB %s could not open: %s\n", + usb->name, strerror ( rc ) ); + goto err_open; + } + + /* Describe control endpoint */ + mtu = USB_EP0_DEFAULT_MTU ( port->speed ); + usb_endpoint_describe ( &usb->control, USB_EP0_ADDRESS, + USB_EP0_ATTRIBUTES, mtu, USB_EP0_BURST, + USB_EP0_INTERVAL ); + + /* Open control endpoint */ + if ( ( rc = usb_endpoint_open ( &usb->control ) ) != 0 ) + goto err_open_control; + assert ( usb_endpoint ( usb, USB_EP0_ADDRESS ) == &usb->control ); + + /* Assign device address */ + if ( ( rc = usb->host->address ( usb ) ) != 0 ) { + DBGC ( usb, "USB %s could not set address: %s\n", + usb->name, strerror ( rc ) ); + goto err_address; + } + DBGC2 ( usb, "USB %s assigned address %d\n", usb->name, usb->address ); + + /* Allow recovery interval after Set Address command */ + mdelay ( USB_SET_ADDRESS_RECOVER_DELAY_MS ); + + /* Read first part of device descriptor to get EP0 MTU */ + if ( ( rc = usb_get_mtu ( usb, &usb->device ) ) != 0 ) { + DBGC ( usb, "USB %s could not get MTU: %s\n", + usb->name, strerror ( rc ) ); + goto err_get_mtu; + } + + /* Calculate EP0 MTU */ + protocol = le16_to_cpu ( usb->device.protocol ); + mtu = ( ( protocol < USB_PROTO_3_0 ) ? + usb->device.mtu : ( 1 << usb->device.mtu ) ); + DBGC2 ( usb, "USB %s has control MTU %zd (guessed %zd)\n", + usb->name, mtu, usb->control.mtu ); + + /* Update MTU */ + if ( ( rc = usb_endpoint_mtu ( &usb->control, mtu ) ) != 0 ) + goto err_mtu; + + /* Read whole device descriptor */ + if ( ( rc = usb_get_device_descriptor ( usb, &usb->device ) ) != 0 ) { + DBGC ( usb, "USB %s could not get device descriptor: %s\n", + usb->name, strerror ( rc ) ); + goto err_get_device_descriptor; + } + DBGC ( usb, "USB %s addr %d %04x:%04x class %d:%d:%d (v%s, %s-speed, " + "MTU %zd)\n", usb->name, usb->address, + le16_to_cpu ( usb->device.vendor ), + le16_to_cpu ( usb->device.product ), usb->device.class.class, + usb->device.class.subclass, usb->device.class.protocol, + usb_bcd ( le16_to_cpu ( usb->device.protocol ) ), + usb_speed_name ( port->speed ), usb->control.mtu ); + + /* Configure device */ + if ( ( rc = usb_configure_any ( usb ) ) != 0 ) + goto err_configure_any; + + return 0; + + usb_deconfigure ( usb ); + err_configure_any: + err_get_device_descriptor: + err_mtu: + err_get_mtu: + err_address: + usb_endpoint_close ( &usb->control ); + err_open_control: + usb->host->close ( usb ); + err_open: + err_speed: + hub->driver->disable ( hub, port ); + err_enable: + list_del ( &usb->list ); + port->usb = NULL; + err_already: + return rc; +} + +/** + * Unregister USB device + * + * @v usb USB device + */ +static void unregister_usb ( struct usb_device *usb ) { + struct usb_port *port = usb->port; + struct usb_hub *hub = port->hub; + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Sanity checks */ + assert ( port->usb == usb ); + + /* Clear device configuration */ + usb_deconfigure ( usb ); + + /* Close control endpoint */ + usb_endpoint_close ( &usb->control ); + + /* Discard any stale control completions */ + list_for_each_entry_safe ( iobuf, tmp, &usb->complete, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + + /* Close device */ + usb->host->close ( usb ); + + /* Disable port */ + hub->driver->disable ( hub, port ); + + /* Remove from bus device list */ + list_del ( &usb->list ); + + /* Remove from port */ + port->usb = NULL; +} + +/** + * Free USB device + * + * @v usb USB device + */ +static void free_usb ( struct usb_device *usb ) { + unsigned int i; + + /* Sanity checks */ + for ( i = 0 ; i < ( sizeof ( usb->ep ) / sizeof ( usb->ep[0] ) ) ; i++ ) + assert ( usb->ep[i] == NULL ); + assert ( list_empty ( &usb->functions ) ); + assert ( list_empty ( &usb->complete ) ); + + /* Free device */ + free ( usb ); +} + +/****************************************************************************** + * + * USB device hotplug event handling + * + ****************************************************************************** + */ + +/** + * Handle newly attached USB device + * + * @v port USB port + * @ret rc Return status code + */ +static int usb_attached ( struct usb_port *port ) { + struct usb_device *usb; + int rc; + + /* Mark port as attached */ + port->attached = 1; + + /* Sanity checks */ + assert ( port->usb == NULL ); + + /* Allocate USB device */ + usb = alloc_usb ( port ); + if ( ! usb ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Register USB device */ + if ( ( rc = register_usb ( usb ) ) != 0 ) + goto err_register; + + return 0; + + unregister_usb ( usb ); + err_register: + free_usb ( usb ); + err_alloc: + return rc; +} + +/** + * Handle newly detached USB device + * + * @v port USB port + */ +static void usb_detached ( struct usb_port *port ) { + struct usb_device *usb = port->usb; + + /* Mark port as detached */ + port->attached = 0; + + /* Do nothing if we have no USB device */ + if ( ! usb ) + return; + + /* Unregister USB device */ + unregister_usb ( usb ); + + /* Free USB device */ + free_usb ( usb ); +} + +/** + * Handle newly attached or detached USB device + * + * @v port USB port + * @ret rc Return status code + */ +static int usb_hotplugged ( struct usb_port *port ) { + struct usb_hub *hub = port->hub; + int rc; + + /* Get current port speed */ + if ( ( rc = hub->driver->speed ( hub, port ) ) != 0 ) { + DBGC ( hub, "USB hub %s port %d could not get speed: %s\n", + hub->name, port->address, strerror ( rc ) ); + goto err_speed; + } + + /* Detach device, if applicable */ + if ( port->attached && ( port->disconnected || ! port->speed ) ) + usb_detached ( port ); + + /* Attach device, if applicable */ + if ( port->speed && ( ! port->attached ) && + ( ( rc = usb_attached ( port ) ) != 0 ) ) + goto err_attached; + + err_attached: + err_speed: + /* Clear any recorded disconnections */ + port->disconnected = 0; + return rc; +} + +/****************************************************************************** + * + * USB process + * + ****************************************************************************** + */ + +/** + * Report port status change + * + * @v port USB port + */ +void usb_port_changed ( struct usb_port *port ) { + + /* Record hub port status change */ + list_del ( &port->changed ); + list_add_tail ( &port->changed, &usb_changed ); +} + +/** + * Handle newly attached or detached USB device + * + */ +static void usb_hotplug ( void ) { + struct usb_port *port; + + /* Handle any changed ports, allowing for the fact that the + * port list may change as we perform hotplug actions. + */ + while ( ! list_empty ( &usb_changed ) ) { + + /* Get first changed port */ + port = list_first_entry ( &usb_changed, struct usb_port, + changed ); + assert ( port != NULL ); + + /* Remove from list of changed ports */ + list_del ( &port->changed ); + INIT_LIST_HEAD ( &port->changed ); + + /* Perform appropriate hotplug action */ + usb_hotplugged ( port ); + } +} + +/** + * USB process + * + * @v process USB process + */ +static void usb_step ( struct process *process __unused ) { + struct usb_bus *bus; + struct usb_endpoint *ep; + + /* Poll all buses */ + for_each_usb_bus ( bus ) + usb_poll ( bus ); + + /* Attempt to reset first halted endpoint in list, if any. We + * do not attempt to process the complete list, since this + * would require extra code to allow for the facts that the + * halted endpoint list may change as we do so, and that + * resetting an endpoint may fail. + */ + if ( ( ep = list_first_entry ( &usb_halted, struct usb_endpoint, + halted ) ) != NULL ) + usb_endpoint_reset ( ep ); + + /* Handle any changed ports */ + usb_hotplug(); +} + +/** USB process */ +PERMANENT_PROCESS ( usb_process, usb_step ); + +/****************************************************************************** + * + * USB hub + * + ****************************************************************************** + */ + +/** + * Allocate USB hub + * + * @v bus USB bus + * @v usb Underlying USB device, if any + * @v ports Number of ports + * @v driver Hub driver operations + * @ret hub USB hub, or NULL on allocation failure + */ +struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, struct usb_device *usb, + unsigned int ports, + struct usb_hub_driver_operations *driver ) { + struct usb_hub *hub; + struct usb_port *port; + unsigned int i; + + /* Allocate and initialise structure */ + hub = zalloc ( sizeof ( *hub ) + ( ports * sizeof ( hub->port[0] ) ) ); + if ( ! hub ) + return NULL; + hub->name = ( usb ? usb->name : bus->name ); + hub->bus = bus; + hub->usb = usb; + if ( usb ) + hub->protocol = usb->port->protocol; + hub->ports = ports; + hub->driver = driver; + hub->host = &bus->op->hub; + + /* Initialise port list */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + port->hub = hub; + port->address = i; + if ( usb ) + port->protocol = usb->port->protocol; + INIT_LIST_HEAD ( &port->changed ); + } + + return hub; +} + +/** + * Register USB hub + * + * @v hub USB hub + * @ret rc Return status code + */ +int register_usb_hub ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct usb_port *port; + unsigned int i; + int rc; + + /* Add to hub list */ + list_add_tail ( &hub->list, &bus->hubs ); + + /* Open hub (host controller) */ + if ( ( rc = hub->host->open ( hub ) ) != 0 ) { + DBGC ( hub, "USB hub %s could not open: %s\n", + hub->name, strerror ( rc ) ); + goto err_host_open; + } + + /* Open hub (driver) */ + if ( ( rc = hub->driver->open ( hub ) ) != 0 ) { + DBGC ( hub, "USB hub %s could not open: %s\n", + hub->name, strerror ( rc ) ); + goto err_driver_open; + } + + /* Delay to allow ports to stabilise */ + mdelay ( USB_PORT_DELAY_MS ); + + /* Mark all ports as changed */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + usb_port_changed ( port ); + } + + /* Some hubs seem to defer reporting device connections until + * their interrupt endpoint is polled for the first time. + * Poll the bus once now in order to pick up any such + * connections. + */ + usb_poll ( bus ); + + return 0; + + hub->driver->close ( hub ); + err_driver_open: + hub->host->close ( hub ); + err_host_open: + list_del ( &hub->list ); + return rc; +} + +/** + * Unregister USB hub + * + * @v hub USB hub + */ +void unregister_usb_hub ( struct usb_hub *hub ) { + struct usb_port *port; + unsigned int i; + + /* Detach all devices */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + if ( port->attached ) + usb_detached ( port ); + } + + /* Close hub (driver) */ + hub->driver->close ( hub ); + + /* Close hub (host controller) */ + hub->host->close ( hub ); + + /* Cancel any pending port status changes */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + list_del ( &port->changed ); + INIT_LIST_HEAD ( &port->changed ); + } + + /* Remove from hub list */ + list_del ( &hub->list ); +} + +/** + * Free USB hub + * + * @v hub USB hub + */ +void free_usb_hub ( struct usb_hub *hub ) { + struct usb_port *port; + unsigned int i; + + /* Sanity checks */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + assert ( ! port->attached ); + assert ( port->usb == NULL ); + assert ( list_empty ( &port->changed ) ); + } + + /* Free hub */ + free ( hub ); +} + +/****************************************************************************** + * + * USB bus + * + ****************************************************************************** + */ + +/** + * Allocate USB bus + * + * @v dev Underlying hardware device + * @v ports Number of root hub ports + * @v mtu Largest transfer allowed on the bus + * @v op Host controller operations + * @ret bus USB bus, or NULL on allocation failure + */ +struct usb_bus * alloc_usb_bus ( struct device *dev, unsigned int ports, + size_t mtu, struct usb_host_operations *op ) { + struct usb_bus *bus; + + /* Allocate and initialise structure */ + bus = zalloc ( sizeof ( *bus ) ); + if ( ! bus ) + goto err_alloc_bus; + bus->name = dev->name; + bus->dev = dev; + bus->mtu = mtu; + bus->op = op; + INIT_LIST_HEAD ( &bus->devices ); + INIT_LIST_HEAD ( &bus->hubs ); + bus->host = &bus->op->bus; + + /* Allocate root hub */ + bus->hub = alloc_usb_hub ( bus, NULL, ports, &op->root ); + if ( ! bus->hub ) + goto err_alloc_hub; + + return bus; + + free_usb_hub ( bus->hub ); + err_alloc_hub: + free ( bus ); + err_alloc_bus: + return NULL; +} + +/** + * Register USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +int register_usb_bus ( struct usb_bus *bus ) { + int rc; + + /* Sanity checks */ + assert ( bus->hub != NULL ); + + /* Open bus */ + if ( ( rc = bus->host->open ( bus ) ) != 0 ) + goto err_open; + + /* Add to list of USB buses */ + list_add_tail ( &bus->list, &usb_buses ); + + /* Register root hub */ + if ( ( rc = register_usb_hub ( bus->hub ) ) != 0 ) + goto err_register_hub; + + /* Attach any devices already present */ + usb_hotplug(); + + return 0; + + unregister_usb_hub ( bus->hub ); + err_register_hub: + list_del ( &bus->list ); + bus->host->close ( bus ); + err_open: + return rc; +} + +/** + * Unregister USB bus + * + * @v bus USB bus + */ +void unregister_usb_bus ( struct usb_bus *bus ) { + + /* Sanity checks */ + assert ( bus->hub != NULL ); + + /* Unregister root hub */ + unregister_usb_hub ( bus->hub ); + + /* Remove from list of USB buses */ + list_del ( &bus->list ); + + /* Close bus */ + bus->host->close ( bus ); + + /* Sanity checks */ + assert ( list_empty ( &bus->devices ) ); + assert ( list_empty ( &bus->hubs ) ); +} + +/** + * Free USB bus + * + * @v bus USB bus + */ +void free_usb_bus ( struct usb_bus *bus ) { + struct usb_endpoint *ep; + struct usb_port *port; + + /* Sanity checks */ + assert ( list_empty ( &bus->devices ) ); + assert ( list_empty ( &bus->hubs ) ); + list_for_each_entry ( ep, &usb_halted, halted ) + assert ( ep->usb->port->hub->bus != bus ); + list_for_each_entry ( port, &usb_changed, changed ) + assert ( port->hub->bus != bus ); + + /* Free root hub */ + free_usb_hub ( bus->hub ); + + /* Free bus */ + free ( bus ); +} + +/** + * Find USB bus by device location + * + * @v bus_type Bus type + * @v location Bus location + * @ret bus USB bus, or NULL + */ +struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type, + unsigned int location ) { + struct usb_bus *bus; + + for_each_usb_bus ( bus ) { + if ( ( bus->dev->desc.bus_type == bus_type ) && + ( bus->dev->desc.location == location ) ) + return bus; + } + + return NULL; +} + +/****************************************************************************** + * + * USB address assignment + * + ****************************************************************************** + */ + +/** + * Allocate device address + * + * @v bus USB bus + * @ret address Device address, or negative error + */ +int usb_alloc_address ( struct usb_bus *bus ) { + unsigned int address; + + /* Find first free device address */ + address = ffsll ( ~bus->addresses ); + if ( ! address ) + return -ENOENT; + + /* Mark address as used */ + bus->addresses |= ( 1ULL << ( address - 1 ) ); + + return address; +} + +/** + * Free device address + * + * @v bus USB bus + * @v address Device address + */ +void usb_free_address ( struct usb_bus *bus, unsigned int address ) { + + /* Sanity check */ + assert ( address > 0 ); + assert ( bus->addresses & ( 1ULL << ( address - 1 ) ) ); + + /* Mark address as free */ + bus->addresses &= ~( 1ULL << ( address - 1 ) ); +} + +/****************************************************************************** + * + * USB bus topology + * + ****************************************************************************** + */ + +/** + * Get USB route string + * + * @v usb USB device + * @ret route USB route string + */ +unsigned int usb_route_string ( struct usb_device *usb ) { + struct usb_device *parent; + unsigned int route; + + /* Navigate up to root hub, constructing route string as we go */ + for ( route = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) { + route <<= 4; + route |= ( ( usb->port->address > 0xf ) ? + 0xf : usb->port->address ); + } + + return route; +} + +/** + * Get USB depth + * + * @v usb USB device + * @ret depth Hub depth + */ +unsigned int usb_depth ( struct usb_device *usb ) { + struct usb_device *parent; + unsigned int depth; + + /* Navigate up to root hub, constructing depth as we go */ + for ( depth = 0 ; ( parent = usb->port->hub->usb ) ; usb = parent ) + depth++; + + return depth; +} + +/** + * Get USB root hub port + * + * @v usb USB device + * @ret port Root hub port + */ +struct usb_port * usb_root_hub_port ( struct usb_device *usb ) { + struct usb_device *parent; + + /* Navigate up to root hub */ + while ( ( parent = usb->port->hub->usb ) ) + usb = parent; + + return usb->port; +} + +/** + * Get USB transaction translator + * + * @v usb USB device + * @ret port Transaction translator port, or NULL + */ +struct usb_port * usb_transaction_translator ( struct usb_device *usb ) { + struct usb_device *parent; + + /* Navigate up to root hub. If we find a low-speed or + * full-speed port with a higher-speed parent device, then + * that port is the transaction translator. + */ + for ( ; ( parent = usb->port->hub->usb ) ; usb = parent ) { + if ( ( usb->port->speed <= USB_SPEED_FULL ) && + ( parent->port->speed > USB_SPEED_FULL ) ) + return usb->port; + } + + return NULL; +} + +/* Drag in objects via register_usb_bus() */ +REQUIRING_SYMBOL ( register_usb_bus ); + +/* Drag in USB configuration */ +REQUIRE_OBJECT ( config_usb ); + +/* Drag in hub driver */ +REQUIRE_OBJECT ( usbhub ); diff --git a/roms/ipxe/src/drivers/infiniband/arbel.c b/roms/ipxe/src/drivers/infiniband/arbel.c index 1a56ff9af..2a6c32dec 100644 --- a/roms/ipxe/src/drivers/infiniband/arbel.c +++ b/roms/ipxe/src/drivers/infiniband/arbel.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/infiniband/arbel.h b/roms/ipxe/src/drivers/infiniband/arbel.h index c0303f1bc..73394cd9a 100644 --- a/roms/ipxe/src/drivers/infiniband/arbel.h +++ b/roms/ipxe/src/drivers/infiniband/arbel.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/drivers/infiniband/linda.c b/roms/ipxe/src/drivers/infiniband/linda.c index 4afda1208..a6ae9f529 100644 --- a/roms/ipxe/src/drivers/infiniband/linda.c +++ b/roms/ipxe/src/drivers/infiniband/linda.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/infiniband/linda.h b/roms/ipxe/src/drivers/infiniband/linda.h index 72ce70868..46a920a17 100644 --- a/roms/ipxe/src/drivers/infiniband/linda.h +++ b/roms/ipxe/src/drivers/infiniband/linda.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/infiniband/qib7322.c b/roms/ipxe/src/drivers/infiniband/qib7322.c index 9979b346e..e22f2349a 100644 --- a/roms/ipxe/src/drivers/infiniband/qib7322.c +++ b/roms/ipxe/src/drivers/infiniband/qib7322.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/infiniband/qib7322.h b/roms/ipxe/src/drivers/infiniband/qib7322.h index 63abe221b..72797b240 100644 --- a/roms/ipxe/src/drivers/infiniband/qib7322.h +++ b/roms/ipxe/src/drivers/infiniband/qib7322.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/net/amd8111e.h b/roms/ipxe/src/drivers/net/amd8111e.h index 2000df158..8ecd159af 100644 --- a/roms/ipxe/src/drivers/net/amd8111e.h +++ b/roms/ipxe/src/drivers/net/amd8111e.h @@ -16,6 +16,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. * USA Module Name: @@ -36,7 +40,7 @@ Revision History: 3.0.1 */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef _AMD811E_H #define _AMD811E_H diff --git a/roms/ipxe/src/drivers/net/ath/ath9k/ani.h b/roms/ipxe/src/drivers/net/ath/ath9k/ani.h index dbd4d4d5b..ba87ba0fd 100644 --- a/roms/ipxe/src/drivers/net/ath/ath9k/ani.h +++ b/roms/ipxe/src/drivers/net/ath/ath9k/ani.h @@ -125,7 +125,7 @@ struct ar5416AniState { u8 mrcCCKOff; u8 spurImmunityLevel; u8 firstepLevel; - u8 ofdmWeakSigDetectOff; + u8 ofdmWeakSigDetect; u8 cckWeakSigThreshold; u32 listenTime; int32_t rssiThrLow; diff --git a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c index ff7df497f..76ca79cba 100644 --- a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c +++ b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ani.c @@ -177,7 +177,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) rssi = BEACON_RSSI(ah); if (rssi > aniState->rssiThrHigh) { - if (!aniState->ofdmWeakSigDetectOff) { + if (aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 0)) { @@ -192,7 +192,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) return; } } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 1); @@ -202,7 +202,7 @@ static void ath9k_hw_ani_ofdm_err_trigger_old(struct ath_hw *ah) return; } else { if ((ah->dev->channels + ah->dev->channel)->band == NET80211_BAND_2GHZ) { - if (!aniState->ofdmWeakSigDetectOff) + if (aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 0); @@ -360,7 +360,7 @@ static void ath9k_hw_ani_lower_immunity_old(struct ath_hw *ah) if (rssi > aniState->rssiThrHigh) { /* XXX: Handle me */ } else if (rssi > aniState->rssiThrLow) { - if (aniState->ofdmWeakSigDetectOff) { + if (!aniState->ofdmWeakSigDetect) { if (ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, 1) == 1) @@ -436,9 +436,9 @@ static void ath9k_ani_reset_old(struct ath_hw *ah) if (aniState->spurImmunityLevel != 0) ath9k_hw_ani_control(ah, ATH9K_ANI_SPUR_IMMUNITY_LEVEL, aniState->spurImmunityLevel); - if (aniState->ofdmWeakSigDetectOff) + if (!aniState->ofdmWeakSigDetect) ath9k_hw_ani_control(ah, ATH9K_ANI_OFDM_WEAK_SIGNAL_DETECTION, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); if (aniState->cckWeakSigThreshold) ath9k_hw_ani_control(ah, ATH9K_ANI_CCK_WEAK_SIGNAL_THR, aniState->cckWeakSigThreshold); @@ -709,8 +709,8 @@ void ath9k_hw_ani_init(struct ath_hw *ah) ani->rssiThrHigh = ATH9K_ANI_RSSI_THR_HIGH; ani->rssiThrLow = ATH9K_ANI_RSSI_THR_LOW; - ani->ofdmWeakSigDetectOff = - !ATH9K_ANI_USE_OFDM_WEAK_SIG; + ani->ofdmWeakSigDetect = + ATH9K_ANI_USE_OFDM_WEAK_SIG; ani->cckNoiseImmunityLevel = ATH9K_ANI_CCK_DEF_LEVEL; } diff --git a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c index 60e87e9e2..2b6c133cb 100644 --- a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c +++ b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar5008_phy.c @@ -1141,12 +1141,12 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1215,10 +1215,10 @@ static int ar5008_hw_ani_control_old(struct ath_hw *ah, DBG2("ath9k: ANI parameters:\n"); DBG2( - "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetectOff=%d\n", + "noiseImmunityLevel=%d, spurImmunityLevel=%d, ofdmWeakSigDetect=%d\n", aniState->noiseImmunityLevel, aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff); + aniState->ofdmWeakSigDetect); DBG2( "cckWeakSigThreshold=%d, firstepLevel=%d, listenTime=%d\n", aniState->cckWeakSigThreshold, @@ -1307,18 +1307,18 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { DBG2("ath9k: " "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1467,7 +1467,7 @@ static int ar5008_hw_ani_control_new(struct ath_hw *ah, DBG2("ath9k: " "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1554,7 +1554,7 @@ static void ar5008_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = 1; /* not available on pre AR9003 */ } diff --git a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c index 6103040ab..2244b775a 100644 --- a/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c +++ b/roms/ipxe/src/drivers/net/ath/ath9k/ath9k_ar9003_phy.c @@ -859,18 +859,18 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, REG_CLR_BIT(ah, AR_PHY_SFCORR_LOW, AR_PHY_SFCORR_LOW_USE_SELF_CORR_LOW); - if (!on != aniState->ofdmWeakSigDetectOff) { + if (on != aniState->ofdmWeakSigDetect) { DBG2("ath9k: " "** ch %d: ofdm weak signal: %s=>%s\n", chan->channel, - !aniState->ofdmWeakSigDetectOff ? + aniState->ofdmWeakSigDetect ? "on" : "off", on ? "on" : "off"); if (on) ah->stats.ast_ani_ofdmon++; else ah->stats.ast_ani_ofdmoff++; - aniState->ofdmWeakSigDetectOff = !on; + aniState->ofdmWeakSigDetect = on; } break; } @@ -1013,7 +1013,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, AR_PHY_MRC_CCK_ENABLE, is_on); REG_RMW_FIELD(ah, AR_PHY_MRC_CCK_CTRL, AR_PHY_MRC_CCK_MUX_REG, is_on); - if (!is_on != aniState->mrcCCKOff) { + if (!(is_on != aniState->mrcCCKOff)) { DBG2("ath9k: " "** ch %d: MRC CCK: %s=>%s\n", chan->channel, @@ -1037,7 +1037,7 @@ static int ar9003_hw_ani_control(struct ath_hw *ah, DBG2("ath9k: " "ANI parameters: SI=%d, ofdmWS=%s FS=%d MRCcck=%s listenTime=%d ofdmErrs=%d cckErrs=%d\n", aniState->spurImmunityLevel, - !aniState->ofdmWeakSigDetectOff ? "on" : "off", + aniState->ofdmWeakSigDetect ? "on" : "off", aniState->firstepLevel, !aniState->mrcCCKOff ? "on" : "off", aniState->listenTime, @@ -1137,7 +1137,7 @@ static void ar9003_hw_ani_cache_ini_regs(struct ath_hw *ah) /* these levels just got reset to defaults by the INI */ aniState->spurImmunityLevel = ATH9K_ANI_SPUR_IMMUNE_LVL_NEW; aniState->firstepLevel = ATH9K_ANI_FIRSTEP_LVL_NEW; - aniState->ofdmWeakSigDetectOff = !ATH9K_ANI_USE_OFDM_WEAK_SIG; + aniState->ofdmWeakSigDetect = ATH9K_ANI_USE_OFDM_WEAK_SIG; aniState->mrcCCKOff = !ATH9K_ANI_ENABLE_MRC_CCK; } diff --git a/roms/ipxe/src/drivers/net/atl1e.c b/roms/ipxe/src/drivers/net/atl1e.c index 1ff0f0d10..d010d8c4a 100644 --- a/roms/ipxe/src/drivers/net/atl1e.c +++ b/roms/ipxe/src/drivers/net/atl1e.c @@ -224,7 +224,7 @@ static int atl1e_sw_init(struct atl1e_adapter *adapter) adapter->link_duplex = FULL_DUPLEX; /* PCI config space info */ - pci_read_config_byte(pdev, PCI_REVISION_ID, &rev_id); + pci_read_config_byte(pdev, PCI_REVISION, &rev_id); phy_status_data = AT_READ_REG(hw, REG_PHY_STATUS); /* nic type */ diff --git a/roms/ipxe/src/drivers/net/davicom.c b/roms/ipxe/src/drivers/net/davicom.c index a4870a729..9d3d8b915 100644 --- a/roms/ipxe/src/drivers/net/davicom.c +++ b/roms/ipxe/src/drivers/net/davicom.c @@ -340,6 +340,7 @@ static void davicom_media_chk(struct nic * nic __unused) csr6 = 0x00200000; /* SF */ outl(csr6, ioaddr + CSR6); +#define PCI_VENDOR_ID_DAVICOM 0x1282 #define PCI_DEVICE_ID_DM9009 0x9009 if (vendor == PCI_VENDOR_ID_DAVICOM && dev_id == PCI_DEVICE_ID_DM9009) { /* Set to 10BaseT mode for DM9009 */ diff --git a/roms/ipxe/src/drivers/net/dm96xx.c b/roms/ipxe/src/drivers/net/dm96xx.c new file mode 100644 index 000000000..58d8dd964 --- /dev/null +++ b/roms/ipxe/src/drivers/net/dm96xx.c @@ -0,0 +1,671 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ipxe/ethernet.h> +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include "dm96xx.h" + +/** @file + * + * Davicom DM96xx USB Ethernet driver + * + */ + +/****************************************************************************** + * + * Register operations + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_reset ( struct dm96xx_device *dm96xx ) { + int ncr; + int rc; + + /* Reset device */ + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_NCR, + DM96XX_NCR_RST ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Wait for reset to complete */ + udelay ( DM96XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + ncr = dm96xx_read_register ( dm96xx, DM96XX_NCR ); + if ( ncr < 0 ) { + rc = ncr; + DBGC ( dm96xx, "DM96XX %p failed to reset: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + if ( ncr & DM96XX_NCR_RST ) { + DBGC ( dm96xx, "DM96XX %p failed to reset (NCR=%#02x)\n", + dm96xx, ncr ); + return -EIO; + } + + return 0; +} + +/** + * Read MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address to fill in + * @ret rc Return status code + */ +static int dm96xx_read_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not read MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write MAC address + * + * @v dm96xx DM96xx device + * @v mac MAC address + * @ret rc Return status code + */ +static int dm96xx_write_mac ( struct dm96xx_device *dm96xx, uint8_t *mac ) { + int rc; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_registers ( dm96xx, DM96XX_PAR, mac, + ETH_ALEN ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write MAC address: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Update link status based on network status register + * + * @v dm96xx DM96xx device + * @v nsr Network status register + */ +static void dm96xx_link_nsr ( struct dm96xx_device *dm96xx, unsigned int nsr ) { + struct net_device *netdev = dm96xx->netdev; + + if ( nsr & DM96XX_NSR_LINKST ) { + if ( ! netdev_link_ok ( netdev ) ) + netdev_link_up ( netdev ); + } else { + if ( netdev_link_ok ( netdev ) ) + netdev_link_down ( netdev ); + } +} + +/** + * Get link status + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_check_link ( struct dm96xx_device *dm96xx ) { + int nsr; + int rc; + + /* Read network status register */ + nsr = dm96xx_read_register ( dm96xx, DM96XX_NSR ); + if ( nsr < 0 ) { + rc = nsr; + DBGC ( dm96xx, "DM96XX %p could not read network status: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, nsr ); + + return 0; +} + +/** + * Set DM9601-compatible RX header mode + * + * @v dm96xx DM96xx device + * @ret rc Return status code + */ +static int dm96xx_rx_mode ( struct dm96xx_device *dm96xx ) { + int chipr; + int mode_ctl; + int rc; + + /* Get chip revision */ + chipr = dm96xx_read_register ( dm96xx, DM96XX_CHIPR ); + if ( chipr < 0 ) { + rc = chipr; + DBGC ( dm96xx, "DM96XX %p could not read chip revision: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Do nothing if device is a DM9601 anyway */ + if ( chipr == DM96XX_CHIPR_9601 ) + return 0; + + /* Read current mode control */ + mode_ctl = dm96xx_read_register ( dm96xx, DM96XX_MODE_CTL ); + if ( mode_ctl < 0 ) { + rc = mode_ctl; + DBGC ( dm96xx, "DM96XX %p could not read mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + /* Write mode control */ + mode_ctl &= ~DM96XX_MODE_CTL_MODE; + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_MODE_CTL, + mode_ctl ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write mode control: %s\n", + dm96xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.intr ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_interrupt *intr; + size_t len = iob_len ( iobuf ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p interrupt failed: %s\n", + dm96xx, strerror ( rc ) ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract message header */ + if ( len < sizeof ( *intr ) ) { + DBGC ( dm96xx, "DM96XX %p underlength interrupt:\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, -EINVAL ); + goto done; + } + intr = iobuf->data; + + /* Update link status */ + dm96xx_link_nsr ( dm96xx, intr->nsr ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_intr_operations = { + .complete = dm96xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.in ); + struct net_device *netdev = dm96xx->netdev; + struct dm96xx_rx_header *header; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( dm96xx, "DM96XX %p bulk IN failed: %s\n", + dm96xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) + 4 /* CRC */ ) ) { + DBGC ( dm96xx, "DM96XX %p underlength bulk IN\n", dm96xx ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header and CRC */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + iob_unput ( iobuf, 4 /* CRC */ ); + + /* Check status */ + if ( header->rsr & ~DM96XX_RSR_MF ) { + DBGC ( dm96xx, "DM96XX %p receive error %02x:\n", + dm96xx, header->rsr ); + DBGC_HDA ( dm96xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_in_operations = { + .complete = dm96xx_in_complete, +}; + +/** + * Transmit packet + * + * @v dm96xx DM96xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_out_transmit ( struct dm96xx_device *dm96xx, + struct io_buffer *iobuf ) { + struct dm96xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->len = cpu_to_le16 ( len ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &dm96xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void dm96xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct dm96xx_device *dm96xx = container_of ( ep, struct dm96xx_device, + usbnet.out ); + struct net_device *netdev = dm96xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations dm96xx_out_operations = { + .complete = dm96xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int dm96xx_open ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + unsigned int rcr; + int rc; + + /* Set DM9601-compatible RX header mode */ + if ( ( rc = dm96xx_rx_mode ( dm96xx ) ) != 0 ) + goto err_rx_mode; + + /* Write MAC address */ + if ( ( rc = dm96xx_write_mac ( dm96xx, netdev->ll_addr ) ) != 0 ) + goto err_write_mac; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &dm96xx->usbnet ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not open: %s\n", + dm96xx, strerror ( rc ) ); + goto err_open; + } + + /* Set receive filters */ + rcr = ( DM96XX_RCR_ALL | DM96XX_RCR_RUNT | DM96XX_RCR_PRMSC | + DM96XX_RCR_RXEN ); + if ( ( rc = dm96xx_write_register ( dm96xx, DM96XX_RCR, rcr ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not write receive filters: " + "%s\n", dm96xx, strerror ( rc ) ); + goto err_write_rcr; + } + + /* Update link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + return 0; + + err_check_link: + err_write_rcr: + usbnet_close ( &dm96xx->usbnet ); + err_open: + err_write_mac: + err_rx_mode: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void dm96xx_close ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &dm96xx->usbnet ); + + /* Reset device */ + dm96xx_reset ( dm96xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int dm96xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = dm96xx_out_transmit ( dm96xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void dm96xx_poll ( struct net_device *netdev ) { + struct dm96xx_device *dm96xx = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( dm96xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &dm96xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** DM96xx network device operations */ +static struct net_device_operations dm96xx_operations = { + .open = dm96xx_open, + .close = dm96xx_close, + .transmit = dm96xx_transmit, + .poll = dm96xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int dm96xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct dm96xx_device *dm96xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *dm96xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &dm96xx_operations ); + netdev->dev = &func->dev; + dm96xx = netdev->priv; + memset ( dm96xx, 0, sizeof ( *dm96xx ) ); + dm96xx->usb = usb; + dm96xx->bus = usb->port->hub->bus; + dm96xx->netdev = netdev; + usbnet_init ( &dm96xx->usbnet, func, &dm96xx_intr_operations, + &dm96xx_in_operations, &dm96xx_out_operations ); + usb_refill_init ( &dm96xx->usbnet.intr, 0, DM96XX_INTR_MAX_FILL ); + usb_refill_init ( &dm96xx->usbnet.in, DM96XX_IN_MTU, + DM96XX_IN_MAX_FILL ); + DBGC ( dm96xx, "DM96XX %p on %s\n", dm96xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &dm96xx->usbnet, config ) ) != 0 ) { + DBGC ( dm96xx, "DM96XX %p could not describe: %s\n", + dm96xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = dm96xx_reset ( dm96xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = dm96xx_read_mac ( dm96xx, netdev->hw_addr ) ) != 0 ) + goto err_read_mac; + + /* Get initial link status */ + if ( ( rc = dm96xx_check_link ( dm96xx ) ) != 0 ) + goto err_check_link; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_check_link: + err_read_mac: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void dm96xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** DM96xx device IDs */ +static struct usb_device_id dm96xx_ids[] = { + { + .name = "dm9601-corega", + .vendor = 0x07aa, + .product = 0x9601, + }, + { + .name = "dm9601", + .vendor = 0x0a46, + .product = 0x9601, + }, + { + .name = "zt6688", + .vendor = 0x0a46, + .product = 0x6688, + }, + { + .name = "st268", + .vendor = 0x0a46, + .product = 0x0268, + }, + { + .name = "adm8515", + .vendor = 0x0a46, + .product = 0x8515, + }, + { + .name = "dm9601-hirose", + .vendor = 0x0a47, + .product = 0x9601, + }, + { + .name = "dm9601-8101", + .vendor = 0x0fe6, + .product = 0x8101, + }, + { + .name = "dm9601-9700", + .vendor = 0x0fe6, + .product = 0x9700, + }, + { + .name = "dm9000e", + .vendor = 0x0a46, + .product = 0x9000, + }, + { + .name = "dm9620", + .vendor = 0x0a46, + .product = 0x9620, + }, + { + .name = "dm9621A", + .vendor = 0x0a46, + .product = 0x9621, + }, + { + .name = "dm9622", + .vendor = 0x0a46, + .product = 0x9622, + }, + { + .name = "dm962Oa", + .vendor = 0x0a46, + .product = 0x0269, + }, + { + .name = "dm9621a", + .vendor = 0x0a46, + .product = 0x1269, + }, +}; + +/** Davicom DM96xx driver */ +struct usb_driver dm96xx_driver __usb_driver = { + .ids = dm96xx_ids, + .id_count = ( sizeof ( dm96xx_ids ) / sizeof ( dm96xx_ids[0] ) ), + .probe = dm96xx_probe, + .remove = dm96xx_remove, +}; diff --git a/roms/ipxe/src/drivers/net/dm96xx.h b/roms/ipxe/src/drivers/net/dm96xx.h new file mode 100644 index 000000000..43a1a4e30 --- /dev/null +++ b/roms/ipxe/src/drivers/net/dm96xx.h @@ -0,0 +1,194 @@ +#ifndef _DM96XX_H +#define _DM96XX_H + +/** @file + * + * Davicom DM96xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include <ipxe/if_ether.h> + +/** Read register(s) */ +#define DM96XX_READ_REGISTER \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Write register(s) */ +#define DM96XX_WRITE_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x01 ) ) + +/** Write single register */ +#define DM96XX_WRITE1_REGISTER \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0x03 ) ) + +/** Network control register */ +#define DM96XX_NCR 0x00 +#define DM96XX_NCR_RST 0x01 /**< Software reset */ + +/** Network status register */ +#define DM96XX_NSR 0x01 +#define DM96XX_NSR_LINKST 0x40 /**< Link status */ + +/** Receive control register */ +#define DM96XX_RCR 0x05 +#define DM96XX_RCR_ALL 0x08 /**< Pass all multicast */ +#define DM96XX_RCR_RUNT 0x04 /**< Pass runt packet */ +#define DM96XX_RCR_PRMSC 0x02 /**< Promiscuous mode */ +#define DM96XX_RCR_RXEN 0x01 /**< RX enable */ + +/** Receive status register */ +#define DM96XX_RSR 0x06 +#define DM96XX_RSR_MF 0x40 /**< Multicast frame */ + +/** PHY address registers */ +#define DM96XX_PAR 0x10 + +/** Chip revision register */ +#define DM96XX_CHIPR 0x2c +#define DM96XX_CHIPR_9601 0x00 /**< DM9601 */ +#define DM96XX_CHIPR_9620 0x01 /**< DM9620 */ + +/** RX header control/status register (DM9620+ only) */ +#define DM96XX_MODE_CTL 0x91 +#define DM96XX_MODE_CTL_MODE 0x80 /**< 4-byte header mode */ + +/** DM96xx interrupt data */ +struct dm96xx_interrupt { + /** Network status register */ + uint8_t nsr; + /** Transmit status registers */ + uint8_t tsr[2]; + /** Receive status register */ + uint8_t rsr; + /** Receive overflow counter register */ + uint8_t rocr; + /** Receive packet counter */ + uint8_t rxc; + /** Transmit packet counter */ + uint8_t txc; + /** General purpose register */ + uint8_t gpr; +} __attribute__ (( packed )); + +/** DM96xx receive header */ +struct dm96xx_rx_header { + /** Packet status */ + uint8_t rsr; + /** Packet length (excluding this header, including CRC) */ + uint16_t len; +} __attribute__ (( packed )); + +/** DM96xx transmit header */ +struct dm96xx_tx_header { + /** Packet length (excluding this header) */ + uint16_t len; +} __attribute__ (( packed )); + +/** A DM96xx network device */ +struct dm96xx_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** + * Read registers + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_read_registers ( struct dm96xx_device *dm96xx, unsigned int offset, + void *data, size_t len ) { + + return usb_control ( dm96xx->usb, DM96XX_READ_REGISTER, 0, offset, + data, len ); +} + +/** + * Read register + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @ret value Register value, or negative error + */ +static inline __attribute__ (( always_inline )) int +dm96xx_read_register ( struct dm96xx_device *dm96xx, unsigned int offset ) { + uint8_t value; + int rc; + + if ( ( rc = dm96xx_read_registers ( dm96xx, offset, &value, + sizeof ( value ) ) ) != 0 ) + return rc; + return value; +} + +/** + * Write registers + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_write_registers ( struct dm96xx_device *dm96xx, unsigned int offset, + void *data, size_t len ) { + + return usb_control ( dm96xx->usb, DM96XX_WRITE_REGISTER, 0, offset, + data, len ); +} + +/** + * Write register + * + * @v dm96xx DM96xx device + * @v offset Register offset + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +dm96xx_write_register ( struct dm96xx_device *dm96xx, unsigned int offset, + uint8_t value ) { + + return usb_control ( dm96xx->usb, DM96XX_WRITE1_REGISTER, value, + offset, NULL, 0 ); +} + +/** Reset delay (in microseconds) */ +#define DM96XX_RESET_DELAY_US 10 + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define DM96XX_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define DM96XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define DM96XX_IN_MTU \ + ( 4 /* DM96xx header */ + ETH_FRAME_LEN + \ + 4 /* possible VLAN header */ + 4 /* CRC */ ) + +#endif /* _DM96XX_H */ diff --git a/roms/ipxe/src/drivers/net/dmfe.c b/roms/ipxe/src/drivers/net/dmfe.c index aae40fce7..2ea0d2b2b 100644 --- a/roms/ipxe/src/drivers/net/dmfe.c +++ b/roms/ipxe/src/drivers/net/dmfe.c @@ -462,7 +462,7 @@ static int dmfe_probe ( struct nic *nic, struct pci_device *pci ) { pci->id->name, pci->vendor, pci->device); /* Read Chip revision */ - pci_read_config_dword(pci, PCI_REVISION_ID, &dev_rev); + pci_read_config_dword(pci, PCI_REVISION, &dev_rev); dprintf(("Revision %lX\n", dev_rev)); /* point to private storage */ diff --git a/roms/ipxe/src/drivers/net/ecm.c b/roms/ipxe/src/drivers/net/ecm.c new file mode 100644 index 000000000..8c84ea9e9 --- /dev/null +++ b/roms/ipxe/src/drivers/net/ecm.c @@ -0,0 +1,520 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <errno.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/base16.h> +#include <ipxe/profile.h> +#include <ipxe/usb.h> +#include "ecm.h" + +/** @file + * + * CDC-ECM USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler ecm_intr_profiler __profiler = + { .name = "ecm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler ecm_in_profiler __profiler = + { .name = "ecm.in" }; + +/** Bulk OUT profiler */ +static struct profiler ecm_out_profiler __profiler = + { .name = "ecm.out" }; + +/****************************************************************************** + * + * Ethernet functional descriptor + * + ****************************************************************************** + */ + +/** + * Locate Ethernet functional descriptor + * + * @v config Configuration descriptor + * @v interface Interface descriptor + * @ret desc Descriptor, or NULL if not found + */ +struct ecm_ethernet_descriptor * +ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ) { + struct ecm_ethernet_descriptor *desc; + + for_each_interface_descriptor ( desc, config, interface ) { + if ( ( desc->header.type == USB_CS_INTERFACE_DESCRIPTOR ) && + ( desc->subtype == CDC_SUBTYPE_ETHERNET ) ) + return desc; + } + return NULL; +} + +/** + * Get hardware MAC address + * + * @v usb USB device + * @v desc Ethernet functional descriptor + * @v hw_addr Hardware address to fill in + * @ret rc Return status code + */ +int ecm_fetch_mac ( struct usb_device *usb, + struct ecm_ethernet_descriptor *desc, uint8_t *hw_addr ) { + char buf[ base16_encoded_len ( ETH_ALEN ) + 1 /* NUL */ ]; + int len; + int rc; + + /* Fetch MAC address string */ + len = usb_get_string_descriptor ( usb, desc->mac, 0, buf, + sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; + return rc; + } + + /* Sanity check */ + if ( len != ( ( int ) ( sizeof ( buf ) - 1 /* NUL */ ) ) ) + return -EINVAL; + + /* Decode MAC address */ + len = base16_decode ( buf, hw_addr, ETH_ALEN ); + if ( len < 0 ) { + rc = len; + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * CDC-ECM communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.intr ); + struct net_device *netdev = ecm->netdev; + struct usb_setup_packet *message; + size_t len = iob_len ( iobuf ); + + /* Profile completions */ + profile_start ( &ecm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Drop packets with errors */ + if ( rc != 0 ) { + DBGC ( ecm, "ECM %p interrupt failed: %s\n", + ecm, strerror ( rc ) ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *message ) ) { + DBGC ( ecm, "ECM %p underlength interrupt:\n", ecm ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) : + if ( message->value && ! netdev_link_ok ( netdev ) ) { + DBGC ( ecm, "ECM %p link up\n", ecm ); + netdev_link_up ( netdev ); + } else if ( netdev_link_ok ( netdev ) && ! message->value ) { + DBGC ( ecm, "ECM %p link down\n", ecm ); + netdev_link_down ( netdev ); + } + break; + + case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) : + /* Ignore */ + break; + + default: + DBGC ( ecm, "ECM %p unrecognised interrupt:\n", ecm ); + DBGC_HDA ( ecm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -ENOTSUP; + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &ecm_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations ecm_intr_operations = { + .complete = ecm_intr_complete, +}; + +/****************************************************************************** + * + * CDC-ECM data interface + * + ****************************************************************************** + */ + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.in ); + struct net_device *netdev = ecm->netdev; + + /* Profile receive completions */ + profile_start ( &ecm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( ecm, "ECM %p bulk IN failed: %s\n", + ecm, strerror ( rc ) ); + goto error; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &ecm_in_profiler ); + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations ecm_in_operations = { + .complete = ecm_in_complete, +}; + +/** + * Transmit packet + * + * @v ecm CDC-ECM device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ecm_out_transmit ( struct ecm_device *ecm, + struct io_buffer *iobuf ) { + int rc; + + /* Profile transmissions */ + profile_start ( &ecm_out_profiler ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &ecm->usbnet.out, iobuf, 1 ) ) != 0 ) + return rc; + + profile_stop ( &ecm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ecm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ecm_device *ecm = container_of ( ep, struct ecm_device, + usbnet.out ); + struct net_device *netdev = ecm->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations ecm_out_operations = { + .complete = ecm_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ecm_open ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + struct usb_device *usb = ecm->usb; + unsigned int filter; + int rc; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &ecm->usbnet ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not open: %s\n", + ecm, strerror ( rc ) ); + goto err_open; + } + + /* Set packet filter */ + filter = ( ECM_PACKET_TYPE_PROMISCUOUS | + ECM_PACKET_TYPE_ALL_MULTICAST | + ECM_PACKET_TYPE_DIRECTED | + ECM_PACKET_TYPE_BROADCAST ); + if ( ( rc = usb_control ( usb, ECM_SET_ETHERNET_PACKET_FILTER, + filter, ecm->usbnet.comms, NULL, 0 ) ) != 0 ){ + DBGC ( ecm, "ECM %p could not set packet filter: %s\n", + ecm, strerror ( rc ) ); + goto err_set_filter; + } + + return 0; + + err_set_filter: + usbnet_close ( &ecm->usbnet ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ecm_close ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &ecm->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ecm_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct ecm_device *ecm = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = ecm_out_transmit ( ecm, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ecm_poll ( struct net_device *netdev ) { + struct ecm_device *ecm = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( ecm->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &ecm->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); +} + +/** CDC-ECM network device operations */ +static struct net_device_operations ecm_operations = { + .open = ecm_open, + .close = ecm_close, + .transmit = ecm_transmit, + .poll = ecm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int ecm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct ecm_device *ecm; + struct usb_interface_descriptor *comms; + struct ecm_ethernet_descriptor *ethernet; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *ecm ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ecm_operations ); + netdev->dev = &func->dev; + ecm = netdev->priv; + memset ( ecm, 0, sizeof ( *ecm ) ); + ecm->usb = usb; + ecm->bus = usb->port->hub->bus; + ecm->netdev = netdev; + usbnet_init ( &ecm->usbnet, func, &ecm_intr_operations, + &ecm_in_operations, &ecm_out_operations ); + usb_refill_init ( &ecm->usbnet.intr, 0, ECM_INTR_MAX_FILL ); + usb_refill_init ( &ecm->usbnet.in, ECM_IN_MTU, ECM_IN_MAX_FILL ); + DBGC ( ecm, "ECM %p on %s\n", ecm, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &ecm->usbnet, config ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not describe: %s\n", + ecm, strerror ( rc ) ); + goto err_describe; + } + + /* Locate Ethernet descriptor */ + comms = usb_interface_descriptor ( config, ecm->usbnet.comms, 0 ); + assert ( comms != NULL ); + ethernet = ecm_ethernet_descriptor ( config, comms ); + if ( ! ethernet ) { + DBGC ( ecm, "ECM %p has no Ethernet descriptor\n", ecm ); + rc = -EINVAL; + goto err_ethernet; + } + + /* Fetch MAC address */ + if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) { + DBGC ( ecm, "ECM %p could not fetch MAC address: %s\n", + ecm, strerror ( rc ) ); + goto err_fetch_mac; + } + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, ecm ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_fetch_mac: + err_ethernet: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void ecm_remove ( struct usb_function *func ) { + struct ecm_device *ecm = usb_func_get_drvdata ( func ); + struct net_device *netdev = ecm->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** CDC-ECM device IDs */ +static struct usb_device_id ecm_ids[] = { + { + .name = "cdc-ecm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_CDC, + .subclass = USB_SUBCLASS_CDC_ECM, + .protocol = 0, + }, + }, +}; + +/** CDC-ECM driver */ +struct usb_driver ecm_driver __usb_driver = { + .ids = ecm_ids, + .id_count = ( sizeof ( ecm_ids ) / sizeof ( ecm_ids[0] ) ), + .probe = ecm_probe, + .remove = ecm_remove, +}; diff --git a/roms/ipxe/src/drivers/net/ecm.h b/roms/ipxe/src/drivers/net/ecm.h new file mode 100644 index 000000000..83d324bdc --- /dev/null +++ b/roms/ipxe/src/drivers/net/ecm.h @@ -0,0 +1,93 @@ +#ifndef _ECM_H +#define _ECM_H + +/** @file + * + * CDC-ECM USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include <ipxe/cdc.h> + +/** CDC-ECM subclass */ +#define USB_SUBCLASS_CDC_ECM 0x06 + +/** Set Ethernet packet filter */ +#define ECM_SET_ETHERNET_PACKET_FILTER \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x43 ) ) + +/** Ethernet packet types */ +enum ecm_ethernet_packet_filter { + /** Promiscuous mode */ + ECM_PACKET_TYPE_PROMISCUOUS = 0x0001, + /** All multicast packets */ + ECM_PACKET_TYPE_ALL_MULTICAST = 0x0002, + /** Unicast packets */ + ECM_PACKET_TYPE_DIRECTED = 0x0004, + /** Broadcast packets */ + ECM_PACKET_TYPE_BROADCAST = 0x0008, + /** Specified multicast packets */ + ECM_PACKET_TYPE_MULTICAST = 0x0010, +}; + +/** An Ethernet Functional Descriptor */ +struct ecm_ethernet_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Descriptor subtype */ + uint8_t subtype; + /** MAC address string */ + uint8_t mac; + /** Ethernet statistics bitmap */ + uint32_t statistics; + /** Maximum segment size */ + uint16_t mtu; + /** Multicast filter configuration */ + uint16_t mcast; + /** Number of wake-on-LAN filters */ + uint8_t wol; +} __attribute__ (( packed )); + +/** A CDC-ECM network device */ +struct ecm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; +}; + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define ECM_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define ECM_IN_MAX_FILL 8 + +/** Bulk IN buffer size + * + * This is a policy decision. + */ +#define ECM_IN_MTU ( ETH_FRAME_LEN + 4 /* possible VLAN header */ ) + +extern struct ecm_ethernet_descriptor * +ecm_ethernet_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ); +extern int ecm_fetch_mac ( struct usb_device *usb, + struct ecm_ethernet_descriptor *desc, + uint8_t *hw_addr ); + +#endif /* _ECM_H */ diff --git a/roms/ipxe/src/drivers/net/eepro.c b/roms/ipxe/src/drivers/net/eepro.c index 909482bcc..97b4c4061 100644 --- a/roms/ipxe/src/drivers/net/eepro.c +++ b/roms/ipxe/src/drivers/net/eepro.c @@ -27,8 +27,18 @@ has 34 pins, the top row of 2 are not used. /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -591,9 +601,9 @@ static int eepro_probe ( struct nic *nic, struct isa_device *isa ) { l_eepro = 0; name = "Intel 82595-based LAN card"; } - station_addr.saddr[0] = swap16(station_addr.saddr[0]); - station_addr.saddr[1] = swap16(station_addr.saddr[1]); - station_addr.saddr[2] = swap16(station_addr.saddr[2]); + station_addr.saddr[0] = bswap_16(station_addr.saddr[0]); + station_addr.saddr[1] = bswap_16(station_addr.saddr[1]); + station_addr.saddr[2] = bswap_16(station_addr.saddr[2]); for (i = 0; i < ETH_ALEN; i++) { nic->node_addr[i] = station_addr.caddr[i]; } diff --git a/roms/ipxe/src/drivers/net/eepro100.c b/roms/ipxe/src/drivers/net/eepro100.c index ede0a1a4b..1046cda39 100644 --- a/roms/ipxe/src/drivers/net/eepro100.c +++ b/roms/ipxe/src/drivers/net/eepro100.c @@ -1136,7 +1136,6 @@ PCI_ROM(0x8086, 0x2449, "82562em", "Intel EtherExpressPro100 82562EM", 0), PCI_ROM(0x8086, 0x2459, "82562-1", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x245d, "82562-2", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x1050, "82562ez", "Intel 82562EZ Network Connection", 0), -PCI_ROM(0x8086, 0x1051, "eepro100-1051", "Intel 82801EB/ER (ICH5/ICH5R) Chipset Ethernet Controller", 0), PCI_ROM(0x8086, 0x1065, "82562-3", "Intel 82562 based Fast Ethernet Connection", 0), PCI_ROM(0x8086, 0x5200, "eepro100-5200", "Intel EtherExpress PRO/100 Intelligent Server", 0), PCI_ROM(0x8086, 0x5201, "eepro100-5201", "Intel EtherExpress PRO/100 Intelligent Server", 0), diff --git a/roms/ipxe/src/drivers/net/efi/nii.c b/roms/ipxe/src/drivers/net/efi/nii.c index d0d7da95a..b91848f5c 100644 --- a/roms/ipxe/src/drivers/net/efi/nii.c +++ b/roms/ipxe/src/drivers/net/efi/nii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <strings.h> @@ -168,6 +172,9 @@ struct nii_nic { /** Saved task priority level */ EFI_TPL saved_tpl; + /** Media status is supported */ + int media; + /** Current transmit buffer */ struct io_buffer *txbuf; /** Current receive buffer */ @@ -408,6 +415,13 @@ static int nii_issue_cpb_db ( struct nii_nic *nii, unsigned int op, void *cpb, cdb.IFnum = nii->nii->IfNum; /* Issue command */ + DBGC2 ( nii, "NII %s issuing %02x:%04x ifnum %d%s%s\n", + nii->dev.name, cdb.OpCode, cdb.OpFlags, cdb.IFnum, + ( cpb ? " cpb" : "" ), ( db ? " db" : "" ) ); + if ( cpb ) + DBGC2_HD ( nii, cpb, cpb_len ); + if ( db ) + DBGC2_HD ( nii, db, db_len ); nii->issue ( ( intptr_t ) &cdb ); /* Check completion status */ @@ -552,6 +566,7 @@ static int nii_get_init_info ( struct nii_nic *nii, nii->buffer_len = db.MemoryRequired; nii->mtu = ( db.FrameDataLen + db.MediaHeaderLen ); netdev->max_pkt_len = nii->mtu; + nii->media = ( stat & PXE_STATFLAGS_GET_STATUS_NO_MEDIA_SUPPORTED ); return 0; } @@ -560,10 +575,12 @@ static int nii_get_init_info ( struct nii_nic *nii, * Initialise UNDI * * @v nii NII NIC + * @v flags Flags * @ret rc Return status code */ -static int nii_initialise ( struct nii_nic *nii ) { +static int nii_initialise_flags ( struct nii_nic *nii, unsigned int flags ) { PXE_CPB_INITIALIZE cpb; + PXE_DB_INITIALIZE db; unsigned int op; int stat; int rc; @@ -580,10 +597,13 @@ static int nii_initialise ( struct nii_nic *nii ) { cpb.MemoryAddr = ( ( intptr_t ) nii->buffer ); cpb.MemoryLength = nii->buffer_len; + /* Construct data block */ + memset ( &db, 0, sizeof ( db ) ); + /* Issue command */ - op = NII_OP ( PXE_OPCODE_INITIALIZE, - PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE ); - if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_INITIALIZE, flags ); + if ( ( stat = nii_issue_cpb_db ( nii, op, &cpb, sizeof ( cpb ), + &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not initialise: %s\n", nii->dev.name, strerror ( rc ) ); @@ -599,6 +619,36 @@ static int nii_initialise ( struct nii_nic *nii ) { } /** + * Initialise UNDI + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_initialise ( struct nii_nic *nii ) { + unsigned int flags; + + /* Initialise UNDI */ + flags = PXE_OPFLAGS_INITIALIZE_DO_NOT_DETECT_CABLE; + return nii_initialise_flags ( nii, flags ); +} + +/** + * Initialise UNDI and detect cable + * + * @v nii NII NIC + * @ret rc Return status code + */ +static int nii_initialise_and_detect ( struct nii_nic *nii ) { + unsigned int flags; + + /* Initialise UNDI and detect cable. This is required to work + * around bugs in some Emulex NII drivers. + */ + flags = PXE_OPFLAGS_INITIALIZE_DETECT_CABLE; + return nii_initialise_flags ( nii, flags ); +} + +/** * Shut down UNDI * * @v nii NII NIC @@ -630,6 +680,7 @@ static void nii_shutdown ( struct nii_nic *nii ) { static int nii_get_station_address ( struct nii_nic *nii, struct net_device *netdev ) { PXE_DB_STATION_ADDRESS db; + unsigned int op; int stat; int rc; @@ -638,8 +689,9 @@ static int nii_get_station_address ( struct nii_nic *nii, goto err_initialise; /* Issue command */ - if ( ( stat = nii_issue_db ( nii, PXE_OPCODE_STATION_ADDRESS, &db, - sizeof ( db ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_STATION_ADDRESS, + PXE_OPFLAGS_STATION_ADDRESS_READ ); + if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get station address: %s\n", nii->dev.name, strerror ( rc ) ); @@ -669,18 +721,25 @@ static int nii_get_station_address ( struct nii_nic *nii, */ static int nii_set_station_address ( struct nii_nic *nii, struct net_device *netdev ) { + uint32_t implementation = nii->undi->Implementation; PXE_CPB_STATION_ADDRESS cpb; + unsigned int op; int stat; int rc; + /* Fail if setting station address is unsupported */ + if ( ! ( implementation & PXE_ROMID_IMP_STATION_ADDR_SETTABLE ) ) + return -ENOTSUP; + /* Construct parameter block */ memset ( &cpb, 0, sizeof ( cpb ) ); memcpy ( cpb.StationAddr, netdev->ll_addr, netdev->ll_protocol->ll_addr_len ); /* Issue command */ - if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_STATION_ADDRESS, - &cpb, sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_STATION_ADDRESS, + PXE_OPFLAGS_STATION_ADDRESS_WRITE ); + if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not set station address: %s\n", nii->dev.name, strerror ( rc ) ); @@ -697,21 +756,28 @@ static int nii_set_station_address ( struct nii_nic *nii, * @ret rc Return status code */ static int nii_set_rx_filters ( struct nii_nic *nii ) { + uint32_t implementation = nii->undi->Implementation; + unsigned int flags; unsigned int op; int stat; int rc; + /* Construct receive filter set */ + flags = ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | + PXE_OPFLAGS_RECEIVE_FILTER_UNICAST ); + if ( implementation & PXE_ROMID_IMP_BROADCAST_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST; + if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS; + if ( implementation & PXE_ROMID_IMP_PROMISCUOUS_MULTICAST_RX_SUPPORTED ) + flags |= PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST; + /* Issue command */ - op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, - ( PXE_OPFLAGS_RECEIVE_FILTER_ENABLE | - PXE_OPFLAGS_RECEIVE_FILTER_UNICAST | - PXE_OPFLAGS_RECEIVE_FILTER_BROADCAST | - PXE_OPFLAGS_RECEIVE_FILTER_PROMISCUOUS | - PXE_OPFLAGS_RECEIVE_FILTER_ALL_MULTICAST ) ); + op = NII_OP ( PXE_OPCODE_RECEIVE_FILTERS, flags ); if ( ( stat = nii_issue ( nii, op ) ) < 0 ) { rc = -EIO_STAT ( stat ); - DBGC ( nii, "NII %s could not set receive filters: %s\n", - nii->dev.name, strerror ( rc ) ); + DBGC ( nii, "NII %s could not set receive filters %#04x: %s\n", + nii->dev.name, flags, strerror ( rc ) ); return rc; } @@ -729,6 +795,7 @@ static int nii_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { struct nii_nic *nii = netdev->priv; PXE_CPB_TRANSMIT cpb; + unsigned int op; int stat; int rc; @@ -745,8 +812,10 @@ static int nii_transmit ( struct net_device *netdev, cpb.MediaheaderLen = netdev->ll_protocol->ll_header_len; /* Transmit packet */ - if ( ( stat = nii_issue_cpb ( nii, PXE_OPCODE_TRANSMIT, &cpb, - sizeof ( cpb ) ) ) < 0 ) { + op = NII_OP ( PXE_OPCODE_TRANSMIT, + ( PXE_OPFLAGS_TRANSMIT_WHOLE | + PXE_OPFLAGS_TRANSMIT_DONT_BLOCK ) ); + if ( ( stat = nii_issue_cpb ( nii, op, &cpb, sizeof ( cpb ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not transmit: %s\n", nii->dev.name, strerror ( rc ) ); @@ -772,12 +841,7 @@ static void nii_poll_tx ( struct net_device *netdev, unsigned int stat ) { return; /* Sanity check */ - if ( ! nii->txbuf ) { - DBGC ( nii, "NII %s reported spurious TX completion\n", - nii->dev.name ); - netdev_tx_err ( netdev, NULL, -EPIPE ); - return; - } + assert ( nii->txbuf != NULL ); /* Complete transmission */ iobuf = nii->txbuf; @@ -869,11 +933,14 @@ static void nii_poll ( struct net_device *netdev ) { int stat; int rc; + /* Construct data block */ + memset ( &db, 0, sizeof ( db ) ); + /* Get status */ op = NII_OP ( PXE_OPCODE_GET_STATUS, ( PXE_OPFLAGS_GET_INTERRUPT_STATUS | - PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS | - PXE_OPFLAGS_GET_MEDIA_STATUS ) ); + ( nii->txbuf ? PXE_OPFLAGS_GET_TRANSMITTED_BUFFERS : 0)| + ( nii->media ? PXE_OPFLAGS_GET_MEDIA_STATUS : 0 ) ) ); if ( ( stat = nii_issue_db ( nii, op, &db, sizeof ( db ) ) ) < 0 ) { rc = -EIO_STAT ( stat ); DBGC ( nii, "NII %s could not get status: %s\n", @@ -882,13 +949,15 @@ static void nii_poll ( struct net_device *netdev ) { } /* Process any TX completions */ - nii_poll_tx ( netdev, stat ); + if ( nii->txbuf ) + nii_poll_tx ( netdev, stat ); /* Process any RX completions */ nii_poll_rx ( netdev ); /* Check for link state changes */ - nii_poll_link ( netdev, stat ); + if ( nii->media ) + nii_poll_link ( netdev, stat ); } /** @@ -901,8 +970,18 @@ static int nii_open ( struct net_device *netdev ) { struct nii_nic *nii = netdev->priv; int rc; - /* Initialise NIC */ - if ( ( rc = nii_initialise ( nii ) ) != 0 ) + /* Initialise NIC + * + * Some Emulex NII drivers have a bug which prevents packets + * from being sent or received unless we specifically ask it + * to detect cable presence during initialisation. Work + * around these buggy drivers by requesting cable detection at + * this point, even though we don't care about link state here + * (and would prefer to have the NIC initialise even if no + * cable is present, to match the behaviour of all other iPXE + * drivers). + */ + if ( ( rc = nii_initialise_and_detect ( nii ) ) != 0 ) goto err_initialise; /* Attempt to set station address */ @@ -1023,8 +1102,9 @@ int nii_start ( struct efi_device *efidev ) { nii->issue = ( ( ( void * ) nii->undi ) + nii->undi->EntryPoint ); } - DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p\n", nii->dev.name, - nii->nii->MajorVer, nii->nii->MinorVer, nii->undi, nii->issue ); + DBGC ( nii, "NII %s using UNDI v%x.%x at %p entry %p impl %#08x\n", + nii->dev.name, nii->nii->MajorVer, nii->nii->MinorVer, + nii->undi, nii->issue, nii->undi->Implementation ); /* Open PCI I/O protocols and locate BARs */ if ( ( rc = nii_pci_open ( nii ) ) != 0 ) @@ -1048,6 +1128,10 @@ int nii_start ( struct efi_device *efidev ) { DBGC ( nii, "NII %s registered as %s for %p %s\n", nii->dev.name, netdev->name, device, efi_handle_name ( device ) ); + /* Set initial link state (if media detection is not supported) */ + if ( ! nii->media ) + netdev_link_up ( netdev ); + return 0; unregister_netdev ( netdev ); diff --git a/roms/ipxe/src/drivers/net/efi/nii.h b/roms/ipxe/src/drivers/net/efi/nii.h index de0ac687b..c10be9db5 100644 --- a/roms/ipxe/src/drivers/net/efi/nii.h +++ b/roms/ipxe/src/drivers/net/efi/nii.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct efi_device; diff --git a/roms/ipxe/src/drivers/net/efi/snp.c b/roms/ipxe/src/drivers/net/efi/snp.c index 2b5fc8618..acfcfba9f 100644 --- a/roms/ipxe/src/drivers/net/efi/snp.c +++ b/roms/ipxe/src/drivers/net/efi/snp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/efi/efi.h> diff --git a/roms/ipxe/src/drivers/net/efi/snponly.c b/roms/ipxe/src/drivers/net/efi/snponly.c index 99f264bca..73abfdbf4 100644 --- a/roms/ipxe/src/drivers/net/efi/snponly.c +++ b/roms/ipxe/src/drivers/net/efi/snponly.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/drivers/net/etherfabric.c b/roms/ipxe/src/drivers/net/etherfabric.c index 5e0efb1e1..29d117443 100644 --- a/roms/ipxe/src/drivers/net/etherfabric.c +++ b/roms/ipxe/src/drivers/net/etherfabric.c @@ -3176,7 +3176,7 @@ falcon_probe_nic_variant ( struct efab_nic *efab, struct pci_device *pci ) uint8_t revision; /* PCI revision */ - pci_read_config_byte ( pci, PCI_CLASS_REVISION, &revision ); + pci_read_config_byte ( pci, PCI_REVISION, &revision ); efab->pci_revision = revision; /* Asic vs FPGA */ diff --git a/roms/ipxe/src/drivers/net/forcedeth.c b/roms/ipxe/src/drivers/net/forcedeth.c index d8ece9a7a..79938cbbb 100644 --- a/roms/ipxe/src/drivers/net/forcedeth.c +++ b/roms/ipxe/src/drivers/net/forcedeth.c @@ -1749,10 +1749,8 @@ forcedeth_map_regs ( struct forcedeth_private *priv ) for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4 ) { pci_read_config_dword ( priv->pci_dev, reg, &bar ); - if ( ( ( bar & PCI_BASE_ADDRESS_SPACE ) == - PCI_BASE_ADDRESS_SPACE_MEMORY ) && - ( pci_bar_size ( priv->pci_dev, reg ) >= - register_size ) ) { + if ( ( ! ( bar & PCI_BASE_ADDRESS_SPACE_IO ) ) && + ( pci_bar_size ( priv->pci_dev, reg ) >= register_size ) ){ addr = pci_bar_start ( priv->pci_dev, reg ); break; } diff --git a/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c b/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c index aace5ad56..fc7021c38 100644 --- a/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c +++ b/roms/ipxe/src/drivers/net/igbvf/igbvf_main.c @@ -461,7 +461,7 @@ static int __devinit igbvf_sw_init ( struct igbvf_adapter *adapter ) hw->vendor_id = pdev->vendor; hw->device_id = pdev->device; - pci_read_config_byte ( pdev, PCI_REVISION_ID, &hw->revision_id ); + pci_read_config_byte ( pdev, PCI_REVISION, &hw->revision_id ); pci_read_config_word ( pdev, PCI_COMMAND, &hw->bus.pci_cmd_word ); diff --git a/roms/ipxe/src/drivers/net/intel.c b/roms/ipxe/src/drivers/net/intel.c index a89f947b2..6309e9aa5 100644 --- a/roms/ipxe/src/drivers/net/intel.c +++ b/roms/ipxe/src/drivers/net/intel.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -248,32 +252,6 @@ static int intel_fetch_mac ( struct intel_nic *intel, uint8_t *hw_addr ) { /****************************************************************************** * - * Diagnostics - * - ****************************************************************************** - */ - -/** - * Dump diagnostic information - * - * @v intel Intel device - */ -static void __attribute__ (( unused )) intel_diag ( struct intel_nic *intel ) { - - DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " - "RX %04x(%02x)/%04x(%02x)\n", intel, - ( intel->tx.cons & 0xffff ), - readl ( intel->regs + intel->tx.reg + INTEL_xDH ), - ( intel->tx.prod & 0xffff ), - readl ( intel->regs + intel->tx.reg + INTEL_xDT ), - ( intel->rx.cons & 0xffff ), - readl ( intel->regs + intel->rx.reg + INTEL_xDH ), - ( intel->rx.prod & 0xffff ), - readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); -} - -/****************************************************************************** - * * Device reset * ****************************************************************************** @@ -371,6 +349,67 @@ static void intel_check_link ( struct net_device *netdev ) { /****************************************************************************** * + * Descriptors + * + ****************************************************************************** + */ + +/** + * Populate transmit descriptor + * + * @v tx Transmit descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_tx ( struct intel_descriptor *tx, physaddr_t addr, + size_t len ) { + + /* Populate transmit descriptor */ + tx->address = cpu_to_le64 ( addr ); + tx->length = cpu_to_le16 ( len ); + tx->flags = 0; + tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS | + INTEL_DESC_CMD_EOP ); + tx->status = 0; +} + +/** + * Populate advanced transmit descriptor + * + * @v tx Transmit descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_tx_adv ( struct intel_descriptor *tx, physaddr_t addr, + size_t len ) { + + /* Populate advanced transmit descriptor */ + tx->address = cpu_to_le64 ( addr ); + tx->length = cpu_to_le16 ( len ); + tx->flags = INTEL_DESC_FL_DTYP_DATA; + tx->command = ( INTEL_DESC_CMD_DEXT | INTEL_DESC_CMD_RS | + INTEL_DESC_CMD_IFCS | INTEL_DESC_CMD_EOP ); + tx->status = cpu_to_le32 ( INTEL_DESC_STATUS_PAYLEN ( len ) ); +} + +/** + * Populate receive descriptor + * + * @v rx Receive descriptor + * @v addr Data buffer address + * @v len Length of data + */ +void intel_describe_rx ( struct intel_descriptor *rx, physaddr_t addr, + size_t len __unused ) { + + /* Populate transmit descriptor */ + rx->address = cpu_to_le64 ( addr ); + rx->length = 0; + rx->status = 0; +} + +/****************************************************************************** + * * Network device interface * ****************************************************************************** @@ -479,10 +518,7 @@ void intel_refill_rx ( struct intel_nic *intel ) { /* Populate receive descriptor */ address = virt_to_bus ( iobuf->data ); - rx->address = cpu_to_le64 ( address ); - rx->length = 0; - rx->status = 0; - rx->errors = 0; + intel->rx.describe ( rx, address, 0 ); /* Record I/O buffer */ assert ( intel->rx_iobuf[rx_idx] == NULL ); @@ -568,6 +604,13 @@ static int intel_open ( struct net_device *netdev ) { /* Update link state */ intel_check_link ( netdev ); + /* Apply required errata */ + if ( intel->flags & INTEL_VMWARE ) { + DBGC ( intel, "INTEL %p applying VMware errata workaround\n", + intel ); + intel->force_icr = INTEL_IRQ_RXT0; + } + return 0; intel_destroy_ring ( intel, &intel->rx ); @@ -617,6 +660,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { unsigned int tx_idx; unsigned int tx_tail; physaddr_t address; + size_t len; /* Get next transmit descriptor */ if ( ( intel->tx.prod - intel->tx.cons ) >= INTEL_TX_FILL ) { @@ -629,11 +673,8 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { /* Populate transmit descriptor */ address = virt_to_bus ( iobuf->data ); - tx->address = cpu_to_le64 ( address ); - tx->length = cpu_to_le16 ( iob_len ( iobuf ) ); - tx->command = ( INTEL_DESC_CMD_RS | INTEL_DESC_CMD_IFCS | - INTEL_DESC_CMD_EOP ); - tx->status = 0; + len = iob_len ( iobuf ); + intel->tx.describe ( tx, address, len ); wmb(); /* Notify card that there are packets ready to transmit */ @@ -644,7 +685,7 @@ int intel_transmit ( struct net_device *netdev, struct io_buffer *iobuf ) { DBGC2 ( intel, "INTEL %p TX %d is [%llx,%llx)\n", intel, tx_idx, ( ( unsigned long long ) address ), - ( ( unsigned long long ) address + iob_len ( iobuf ) ) ); + ( ( unsigned long long ) address + len ) ); return 0; } @@ -667,7 +708,7 @@ void intel_poll_tx ( struct net_device *netdev ) { tx = &intel->tx.desc[tx_idx]; /* Stop if descriptor is still in use */ - if ( ! ( tx->status & INTEL_DESC_STATUS_DD ) ) + if ( ! ( tx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) ) return; DBGC2 ( intel, "INTEL %p TX %d complete\n", intel, tx_idx ); @@ -698,7 +739,7 @@ void intel_poll_rx ( struct net_device *netdev ) { rx = &intel->rx.desc[rx_idx]; /* Stop if descriptor is still in use */ - if ( ! ( rx->status & INTEL_DESC_STATUS_DD ) ) + if ( ! ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_DD ) ) ) return; /* Populate I/O buffer */ @@ -708,10 +749,10 @@ void intel_poll_rx ( struct net_device *netdev ) { iob_put ( iobuf, len ); /* Hand off to network stack */ - if ( rx->errors ) { + if ( rx->status & cpu_to_le32 ( INTEL_DESC_STATUS_RXE ) ) { DBGC ( intel, "INTEL %p RX %d error (length %zd, " - "errors %02x)\n", - intel, rx_idx, len, rx->errors ); + "status %08x)\n", intel, rx_idx, len, + le32_to_cpu ( rx->status ) ); netdev_rx_err ( netdev, iobuf, -EIO ); } else { DBGC2 ( intel, "INTEL %p RX %d complete (length %zd)\n", @@ -736,6 +777,7 @@ static void intel_poll ( struct net_device *netdev ) { icr = readl ( intel->regs + INTEL_ICR ); profile_stop ( &intel_vm_poll_profiler ); profile_exclude ( &intel_vm_poll_profiler ); + icr |= intel->force_icr; if ( ! icr ) return; @@ -755,6 +797,14 @@ static void intel_poll ( struct net_device *netdev ) { if ( icr & INTEL_IRQ_LSC ) intel_check_link ( netdev ); + /* Check for unexpected interrupts */ + if ( icr & ~( INTEL_IRQ_TXDW | INTEL_IRQ_TXQE | INTEL_IRQ_LSC | + INTEL_IRQ_RXDMT0 | INTEL_IRQ_RXT0 | INTEL_IRQ_RXO ) ) { + DBGC ( intel, "INTEL %p unexpected ICR %08x\n", intel, icr ); + /* Report as a TX error */ + netdev_tx_err ( netdev, NULL, -ENOTSUP ); + } + /* Refill RX ring */ intel_refill_rx ( intel ); } @@ -817,8 +867,10 @@ static int intel_probe ( struct pci_device *pci ) { memset ( intel, 0, sizeof ( *intel ) ); intel->port = PCI_FUNC ( pci->busdevfn ); intel->flags = pci->id->driver_data; - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTEL_TD, + intel_describe_tx ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTEL_RD, + intel_describe_rx ); /* Fix up PCI device */ adjust_pci_device ( pci ); @@ -895,7 +947,7 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x100c, "82544gc", "82544GC (Copper)", 0 ), PCI_ROM ( 0x8086, 0x100d, "82544gc-l", "82544GC (LOM)", 0 ), PCI_ROM ( 0x8086, 0x100e, "82540em", "82540EM", 0 ), - PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", 0 ), + PCI_ROM ( 0x8086, 0x100f, "82545em", "82545EM (Copper)", INTEL_VMWARE ), PCI_ROM ( 0x8086, 0x1010, "82546eb", "82546EB (Copper)", 0 ), PCI_ROM ( 0x8086, 0x1011, "82545em-f", "82545EM (Fiber)", 0 ), PCI_ROM ( 0x8086, 0x1012, "82546eb-f", "82546EB (Fiber)", 0 ), @@ -998,6 +1050,12 @@ static struct pci_device_id intel_nics[] = { PCI_ROM ( 0x8086, 0x1533, "i210", "I210", 0 ), PCI_ROM ( 0x8086, 0x153a, "i217lm", "I217-LM", 0 ), PCI_ROM ( 0x8086, 0x153b, "i217v", "I217-V", 0 ), + PCI_ROM ( 0x8086, 0x1559, "i218v", "I218-V", 0), + PCI_ROM ( 0x8086, 0x155a, "i218lm", "I218-LM", 0), + PCI_ROM ( 0x8086, 0x15a0, "i218lm-2", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a1, "i218v-2", "I218-V", 0 ), + PCI_ROM ( 0x8086, 0x15a2, "i218lm-3", "I218-LM", 0 ), + PCI_ROM ( 0x8086, 0x15a3, "i218v-3", "I218-V", 0 ), PCI_ROM ( 0x8086, 0x294c, "82566dc-2", "82566DC-2", 0 ), PCI_ROM ( 0x8086, 0x2e6e, "cemedia", "CE Media Processor", 0 ), }; diff --git a/roms/ipxe/src/drivers/net/intel.h b/roms/ipxe/src/drivers/net/intel.h index 8c4479bb4..ce9e3f467 100644 --- a/roms/ipxe/src/drivers/net/intel.h +++ b/roms/ipxe/src/drivers/net/intel.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/if_ether.h> @@ -22,33 +22,38 @@ struct intel_descriptor { uint64_t address; /** Length */ uint16_t length; - /** Reserved */ - uint8_t reserved_a; + /** Flags */ + uint8_t flags; /** Command */ uint8_t command; /** Status */ - uint8_t status; - /** Errors */ - uint8_t errors; - /** Reserved */ - uint16_t reserved_b; + uint32_t status; } __attribute__ (( packed )); -/** Packet descriptor command bits */ -enum intel_descriptor_command { - /** Report status */ - INTEL_DESC_CMD_RS = 0x08, - /** Insert frame checksum (CRC) */ - INTEL_DESC_CMD_IFCS = 0x02, - /** End of packet */ - INTEL_DESC_CMD_EOP = 0x01, -}; +/** Descriptor type */ +#define INTEL_DESC_FL_DTYP( dtyp ) ( (dtyp) << 4 ) +#define INTEL_DESC_FL_DTYP_DATA INTEL_DESC_FL_DTYP ( 0x03 ) -/** Packet descriptor status bits */ -enum intel_descriptor_status { - /** Descriptor done */ - INTEL_DESC_STATUS_DD = 0x01, -}; +/** Descriptor extension */ +#define INTEL_DESC_CMD_DEXT 0x20 + +/** Report status */ +#define INTEL_DESC_CMD_RS 0x08 + +/** Insert frame checksum (CRC) */ +#define INTEL_DESC_CMD_IFCS 0x02 + +/** End of packet */ +#define INTEL_DESC_CMD_EOP 0x01 + +/** Descriptor done */ +#define INTEL_DESC_STATUS_DD 0x00000001UL + +/** Receive error */ +#define INTEL_DESC_STATUS_RXE 0x00000100UL + +/** Payload length */ +#define INTEL_DESC_STATUS_PAYLEN( len ) ( (len) << 14 ) /** Device Control Register */ #define INTEL_CTRL 0x00000UL @@ -91,7 +96,9 @@ enum intel_descriptor_status { /** Interrupt Cause Read Register */ #define INTEL_ICR 0x000c0UL #define INTEL_IRQ_TXDW 0x00000001UL /**< Transmit descriptor done */ +#define INTEL_IRQ_TXQE 0x00000002UL /**< Transmit queue empty */ #define INTEL_IRQ_LSC 0x00000004UL /**< Link status change */ +#define INTEL_IRQ_RXDMT0 0x00000010UL /**< Receive queue low */ #define INTEL_IRQ_RXT0 0x00000080UL /**< Receive timer */ #define INTEL_IRQ_RXO 0x00000400UL /**< Receive overrun */ @@ -207,6 +214,15 @@ struct intel_ring { unsigned int reg; /** Length (in bytes) */ size_t len; + + /** Populate descriptor + * + * @v desc Descriptor + * @v addr Data buffer address + * @v len Length of data + */ + void ( * describe ) ( struct intel_descriptor *desc, physaddr_t addr, + size_t len ); }; /** @@ -215,12 +231,39 @@ struct intel_ring { * @v ring Descriptor ring * @v count Number of descriptors * @v reg Descriptor register block + * @v describe Method to populate descriptor */ static inline __attribute__ (( always_inline)) void -intel_init_ring ( struct intel_ring *ring, unsigned int count, - unsigned int reg ) { +intel_init_ring ( struct intel_ring *ring, unsigned int count, unsigned int reg, + void ( * describe ) ( struct intel_descriptor *desc, + physaddr_t addr, size_t len ) ) { + ring->len = ( count * sizeof ( ring->desc[0] ) ); ring->reg = reg; + ring->describe = describe; +} + +/** An Intel virtual function mailbox */ +struct intel_mailbox { + /** Mailbox control register */ + unsigned int ctrl; + /** Mailbox memory base */ + unsigned int mem; +}; + +/** + * Initialise mailbox + * + * @v mbox Mailbox + * @v ctrl Mailbox control register + * @v mem Mailbox memory register base + */ +static inline __attribute__ (( always_inline )) void +intel_init_mbox ( struct intel_mailbox *mbox, unsigned int ctrl, + unsigned int mem ) { + + mbox->ctrl = ctrl; + mbox->mem = mem; } /** An Intel network card */ @@ -231,6 +274,8 @@ struct intel_nic { unsigned int port; /** Flags */ unsigned int flags; + /** Forced interrupts */ + unsigned int force_icr; /** EEPROM */ struct nvs_device eeprom; @@ -239,6 +284,9 @@ struct intel_nic { /** EEPROM address shift */ unsigned int eerd_addr_shift; + /** Mailbox */ + struct intel_mailbox mbox; + /** Transmit descriptor ring */ struct intel_ring tx; /** Receive descriptor ring */ @@ -251,8 +299,35 @@ struct intel_nic { enum intel_flags { /** PBS/PBA errata workaround required */ INTEL_PBS_ERRATA = 0x0001, + /** VMware missing interrupt workaround required */ + INTEL_VMWARE = 0x0002, }; +/** + * Dump diagnostic information + * + * @v intel Intel device + */ +static inline void intel_diag ( struct intel_nic *intel ) { + + DBGC ( intel, "INTEL %p TX %04x(%02x)/%04x(%02x) " + "RX %04x(%02x)/%04x(%02x)\n", intel, + ( intel->tx.cons & 0xffff ), + readl ( intel->regs + intel->tx.reg + INTEL_xDH ), + ( intel->tx.prod & 0xffff ), + readl ( intel->regs + intel->tx.reg + INTEL_xDT ), + ( intel->rx.cons & 0xffff ), + readl ( intel->regs + intel->rx.reg + INTEL_xDH ), + ( intel->rx.prod & 0xffff ), + readl ( intel->regs + intel->rx.reg + INTEL_xDT ) ); +} + +extern void intel_describe_tx ( struct intel_descriptor *tx, + physaddr_t addr, size_t len ); +extern void intel_describe_tx_adv ( struct intel_descriptor *tx, + physaddr_t addr, size_t len ); +extern void intel_describe_rx ( struct intel_descriptor *rx, + physaddr_t addr, size_t len ); extern int intel_create_ring ( struct intel_nic *intel, struct intel_ring *ring ); extern void intel_destroy_ring ( struct intel_nic *intel, diff --git a/roms/ipxe/src/drivers/net/intelvf.c b/roms/ipxe/src/drivers/net/intelvf.c new file mode 100644 index 000000000..ac6fea745 --- /dev/null +++ b/roms/ipxe/src/drivers/net/intelvf.c @@ -0,0 +1,340 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ipxe/io.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include "intelvf.h" + +/** @file + * + * Intel 10/100/1000 virtual function network card driver + * + */ + +/****************************************************************************** + * + * Mailbox messages + * + ****************************************************************************** + */ + +/** + * Write message to mailbox + * + * @v intel Intel device + * @v msg Message + */ +static void intelvf_mbox_write ( struct intel_nic *intel, + const union intelvf_msg *msg ) { + unsigned int i; + + /* Write message */ + DBGC2 ( intel, "INTEL %p sending message", intel ); + for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){ + DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] ); + writel ( msg->dword[i], ( intel->regs + intel->mbox.mem + + ( i * sizeof ( msg->dword[0] ) ) ) ); + } + DBGC2 ( intel, "\n" ); +} + +/** + * Read message from mailbox + * + * @v intel Intel device + * @v msg Message + */ +static void intelvf_mbox_read ( struct intel_nic *intel, + union intelvf_msg *msg ) { + unsigned int i; + + /* Read message */ + DBGC2 ( intel, "INTEL %p received message", intel ); + for ( i = 0 ; i < ( sizeof ( *msg ) / sizeof ( msg->dword[0] ) ) ; i++){ + msg->dword[i] = readl ( intel->regs + intel->mbox.mem + + ( i * sizeof ( msg->dword[0] ) ) ); + DBGC2 ( intel, "%c%08x", ( i ? ':' : ' ' ), msg->dword[i] ); + } + DBGC2 ( intel, "\n" ); +} + +/** + * Poll mailbox + * + * @v intel Intel device + * @ret rc Return status code + * + * Note that polling the mailbox may fail if the underlying PF is + * reset. + */ +int intelvf_mbox_poll ( struct intel_nic *intel ) { + struct intel_mailbox *mbox = &intel->mbox; + union intelvf_msg msg; + uint32_t ctrl; + + /* Get mailbox status */ + ctrl = readl ( intel->regs + mbox->ctrl ); + + /* Fail if a reset is in progress */ + if ( ctrl & INTELVF_MBCTRL_RSTI ) + return -EPIPE; + + /* Acknowledge (and ignore) any received messages */ + if ( ctrl & INTELVF_MBCTRL_PFSTS ) { + intelvf_mbox_read ( intel, &msg ); + writel ( INTELVF_MBCTRL_ACK, intel->regs + mbox->ctrl ); + } + + return 0; +} + +/** + * Wait for PF reset to complete + * + * @v intel Intel device + * @ret rc Return status code + */ +int intelvf_mbox_wait ( struct intel_nic *intel ) { + unsigned int i; + int rc; + + /* Wait until a poll completes successfully */ + for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) { + + /* Check for successful poll */ + if ( ( rc = intelvf_mbox_poll ( intel ) ) == 0 ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p timed out waiting for reset\n", intel ); + return -ETIMEDOUT; +} + +/** + * Send/receive mailbox message + * + * @v intel Intel device + * @v msg Message buffer + * @ret rc Return status code + */ +int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg ) { + struct intel_mailbox *mbox = &intel->mbox; + uint32_t ctrl; + uint32_t seen = 0; + unsigned int i; + + /* Sanity check */ + assert ( ! ( msg->hdr & INTELVF_MSG_RESPONSE ) ); + + /* Handle mailbox */ + for ( i = 0 ; i < INTELVF_MBOX_MAX_WAIT_MS ; i++ ) { + + /* Attempt to claim mailbox, if we have not yet sent + * our message. + */ + if ( ! ( seen & INTELVF_MBCTRL_VFU ) ) + writel ( INTELVF_MBCTRL_VFU, intel->regs + mbox->ctrl ); + + /* Get mailbox status and record observed flags */ + ctrl = readl ( intel->regs + mbox->ctrl ); + seen |= ctrl; + + /* If a reset is in progress, clear VFU and abort */ + if ( ctrl & INTELVF_MBCTRL_RSTI ) { + writel ( 0, intel->regs + mbox->ctrl ); + return -EPIPE; + } + + /* Write message to mailbox, if applicable. This + * potentially overwrites a message sent by the PF (if + * the PF has simultaneously released PFU (thus + * allowing our VFU) and asserted PFSTS), but that + * doesn't really matter since there are no + * unsolicited PF->VF messages that require the actual + * message content to be observed. + */ + if ( ctrl & INTELVF_MBCTRL_VFU ) + intelvf_mbox_write ( intel, msg ); + + /* Read message from mailbox, if applicable. */ + if ( ( seen & INTELVF_MBCTRL_VFU ) && + ( seen & INTELVF_MBCTRL_PFACK ) && + ( ctrl & INTELVF_MBCTRL_PFSTS ) ) + intelvf_mbox_read ( intel, msg ); + + /* Acknowledge received message (if applicable), + * release VFU lock, and send message (if applicable). + */ + ctrl = ( ( ( ctrl & INTELVF_MBCTRL_PFSTS ) ? + INTELVF_MBCTRL_ACK : 0 ) | + ( ( ctrl & INTELVF_MBCTRL_VFU ) ? + INTELVF_MBCTRL_REQ : 0 ) ); + writel ( ctrl, intel->regs + mbox->ctrl ); + + /* Exit successfully if we have received a response */ + if ( msg->hdr & INTELVF_MSG_RESPONSE ) { + + /* Sanity check */ + assert ( seen & INTELVF_MBCTRL_VFU ); + assert ( seen & INTELVF_MBCTRL_PFACK ); + assert ( seen & INTELVF_MBCTRL_PFSTS ); + + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( intel, "INTEL %p timed out waiting for mailbox (seen %08x)\n", + intel, seen ); + return -ETIMEDOUT; +} + +/** + * Send reset message and get initial MAC address + * + * @v intel Intel device + * @v hw_addr Hardware address to fill in, or NULL + * @ret rc Return status code + */ +int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ) { + union intelvf_msg msg; + int rc; + + /* Send reset message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_RESET; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p reset failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_RESET ) { + DBGC ( intel, "INTEL %p reset unexpected response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Fill in MAC address, if applicable */ + if ( hw_addr ) { + if ( msg.hdr & INTELVF_MSG_ACK ) { + memcpy ( hw_addr, msg.mac.mac, sizeof ( msg.mac.mac ) ); + DBGC ( intel, "INTEL %p reset assigned MAC address " + "%s\n", intel, eth_ntoa ( hw_addr ) ); + } else { + eth_random_addr ( hw_addr ); + DBGC ( intel, "INTEL %p reset generated MAC address " + "%s\n", intel, eth_ntoa ( hw_addr ) ); + } + } + + return 0; +} + +/** + * Send set MAC address message + * + * @v intel Intel device + * @v ll_addr Link-layer address + * @ret rc Return status code + */ +int intelvf_mbox_set_mac ( struct intel_nic *intel, const uint8_t *ll_addr ) { + union intelvf_msg msg; + int rc; + + /* Send set MAC address message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_SET_MAC; + memcpy ( msg.mac.mac, ll_addr, sizeof ( msg.mac.mac ) ); + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p set MAC address failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MAC ) { + DBGC ( intel, "INTEL %p set MAC address unexpected response:\n", + intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to set the MAC address */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p set MAC address refused\n", intel ); + return -EPERM; + } + + return 0; +} + +/** + * Send set MTU message + * + * @v intel Intel device + * @v mtu Maximum packet size + * @ret rc Return status code + */ +int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ) { + union intelvf_msg msg; + int rc; + + /* Send set MTU message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELVF_MSG_TYPE_SET_MTU; + msg.mtu.mtu = mtu; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p set MTU failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELVF_MSG_TYPE_SET_MTU ) { + DBGC ( intel, "INTEL %p set MTU unexpected response:\n", + intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that we were allowed to set the MTU */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p set MTU refused\n", intel ); + return -EPERM; + } + + return 0; +} diff --git a/roms/ipxe/src/drivers/net/intelvf.h b/roms/ipxe/src/drivers/net/intelvf.h new file mode 100644 index 000000000..d2f98d874 --- /dev/null +++ b/roms/ipxe/src/drivers/net/intelvf.h @@ -0,0 +1,109 @@ +#ifndef _INTELVF_H +#define _INTELVF_H + +/** @file + * + * Intel 10/100/1000 virtual function network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "intel.h" + +/** Intel VF BAR size */ +#define INTELVF_BAR_SIZE ( 16 * 1024 ) + +/** Mailbox Control Register */ +#define INTELVF_MBCTRL 0x0c40UL +#define INTELVF_MBCTRL_REQ 0x00000001UL /**< Request for PF ready */ +#define INTELVF_MBCTRL_ACK 0x00000002UL /**< PF message received */ +#define INTELVF_MBCTRL_VFU 0x00000004UL /**< Buffer taken by VF */ +#define INTELVF_MBCTRL_PFU 0x00000008UL /**< Buffer taken to PF */ +#define INTELVF_MBCTRL_PFSTS 0x00000010UL /**< PF wrote a message */ +#define INTELVF_MBCTRL_PFACK 0x00000020UL /**< PF acknowledged message */ +#define INTELVF_MBCTRL_RSTI 0x00000040UL /**< PF reset in progress */ +#define INTELVF_MBCTRL_RSTD 0x00000080UL /**< PF reset complete */ + +/** Mailbox Memory Register Base */ +#define INTELVF_MBMEM 0x0800UL + +/** Reset mailbox message */ +#define INTELVF_MSG_TYPE_RESET 0x00000001UL + +/** Set MAC address mailbox message */ +#define INTELVF_MSG_TYPE_SET_MAC 0x00000002UL + +/** Set MTU mailbox message */ +#define INTELVF_MSG_TYPE_SET_MTU 0x00000005UL + +/** Control ("ping") mailbox message */ +#define INTELVF_MSG_TYPE_CONTROL 0x00000100UL + +/** Message type mask */ +#define INTELVF_MSG_TYPE_MASK 0x0000ffffUL + +/** Message NACK flag */ +#define INTELVF_MSG_NACK 0x40000000UL + +/** Message ACK flag */ +#define INTELVF_MSG_ACK 0x80000000UL + +/** Message is a response */ +#define INTELVF_MSG_RESPONSE ( INTELVF_MSG_ACK | INTELVF_MSG_NACK ) + +/** MAC address mailbox message */ +struct intelvf_msg_mac { + /** Message header */ + uint32_t hdr; + /** MAC address */ + uint8_t mac[ETH_ALEN]; + /** Alignment padding */ + uint8_t reserved[ (-ETH_ALEN) & 0x3 ]; +} __attribute__ (( packed )); + +/** Version number mailbox message */ +struct intelvf_msg_version { + /** Message header */ + uint32_t hdr; + /** API version */ + uint32_t version; +} __attribute__ (( packed )); + +/** MTU mailbox message */ +struct intelvf_msg_mtu { + /** Message header */ + uint32_t hdr; + /** Maximum packet size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Mailbox message */ +union intelvf_msg { + /** Message header */ + uint32_t hdr; + /** MAC address message */ + struct intelvf_msg_mac mac; + /** Version number message */ + struct intelvf_msg_version version; + /** MTU message */ + struct intelvf_msg_mtu mtu; + /** Raw dwords */ + uint32_t dword[0]; +}; + +/** Maximum time to wait for mailbox message + * + * This is a policy decision. + */ +#define INTELVF_MBOX_MAX_WAIT_MS 500 + +extern int intelvf_mbox_msg ( struct intel_nic *intel, union intelvf_msg *msg ); +extern int intelvf_mbox_poll ( struct intel_nic *intel ); +extern int intelvf_mbox_wait ( struct intel_nic *intel ); +extern int intelvf_mbox_reset ( struct intel_nic *intel, uint8_t *hw_addr ); +extern int intelvf_mbox_set_mac ( struct intel_nic *intel, + const uint8_t *ll_addr ); +extern int intelvf_mbox_set_mtu ( struct intel_nic *intel, size_t mtu ); + +#endif /* _INTELVF_H */ diff --git a/roms/ipxe/src/drivers/net/intelx.c b/roms/ipxe/src/drivers/net/intelx.c index d69900e41..982b74f12 100644 --- a/roms/ipxe/src/drivers/net/intelx.c +++ b/roms/ipxe/src/drivers/net/intelx.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -392,8 +396,10 @@ static int intelx_probe ( struct pci_device *pci ) { netdev->dev = &pci->dev; memset ( intel, 0, sizeof ( *intel ) ); intel->port = PCI_FUNC ( pci->busdevfn ); - intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD ); - intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELX_TD, + intel_describe_tx ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELX_RD, + intel_describe_rx ); /* Fix up PCI device */ adjust_pci_device ( pci ); @@ -458,10 +464,15 @@ static void intelx_remove ( struct pci_device *pci ) { /** PCI device IDs */ static struct pci_device_id intelx_nics[] = { - PCI_ROM ( 0x8086, 0x10fb, "82599", "82599", 0 ), - PCI_ROM ( 0x8086, 0x1528, "x540at2", "X540-AT2", 0 ), - PCI_ROM ( 0x8086, 0x154d, "x520", "X520", 0 ), - PCI_ROM ( 0x8086, 0x1557, "82599", "82599", 0 ), + PCI_ROM ( 0x8086, 0x10f7, "82599-kx4", "82599 (KX/KX4)", 0 ), + PCI_ROM ( 0x8086, 0x10f8, "82599-combo-backplane", "82599 (combined backplane; KR/KX4/KX)", 0 ), + PCI_ROM ( 0x8086, 0x10f9, "82599-cx4", "82599 (CX4)", 0 ), + PCI_ROM ( 0x8086, 0x10fb, "82599-sfp", "82599 (SFI/SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x10fc, "82599-xaui", "82599 (XAUI/BX4)", 0 ), + PCI_ROM ( 0x8086, 0x1528, "x540t", "X540-AT2/X540-BT2", 0 ), + PCI_ROM ( 0x8086, 0x154d, "82599-sfp-sf2", "82599 (SFI/SFP+)", 0 ), + PCI_ROM ( 0x8086, 0x1557, "82599en-sfp", "82599 (Single Port SFI Only)", 0 ), + PCI_ROM ( 0x8086, 0x1560, "x540t1", "X540-AT2/X540-BT2 (with single port NVM)", 0 ), }; /** PCI driver */ diff --git a/roms/ipxe/src/drivers/net/intelx.h b/roms/ipxe/src/drivers/net/intelx.h index 60bb294d5..6383dfcad 100644 --- a/roms/ipxe/src/drivers/net/intelx.h +++ b/roms/ipxe/src/drivers/net/intelx.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/if_ether.h> diff --git a/roms/ipxe/src/drivers/net/intelxvf.c b/roms/ipxe/src/drivers/net/intelxvf.c new file mode 100644 index 000000000..05e34c127 --- /dev/null +++ b/roms/ipxe/src/drivers/net/intelxvf.c @@ -0,0 +1,466 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <ipxe/io.h> +#include <ipxe/pci.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include "intelxvf.h" + +/** @file + * + * Intel 10 Gigabit Ethernet virtual function network card driver + * + */ + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump statistics + * + * @v intel Intel device + */ +static __attribute__ (( unused )) void +intelxvf_stats ( struct intel_nic *intel ) { + + DBGC ( intel, "INTEL %p TX %d (%#x%08x) RX %d (%#x%08x) multi %d\n", + intel, readl ( intel->regs + INTELXVF_GPTC ), + readl ( intel->regs + INTELXVF_GOTCH ), + readl ( intel->regs + INTELXVF_GOTCL ), + readl ( intel->regs + INTELXVF_GPRC ), + readl ( intel->regs + INTELXVF_GORCH ), + readl ( intel->regs + INTELXVF_GORCL ), + readl ( intel->regs + INTELXVF_MPRC ) ); +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset hardware + * + * @v intel Intel device + */ +static void intelxvf_reset ( struct intel_nic *intel ) { + + /* Perform a function-level reset */ + writel ( INTELXVF_CTRL_RST, intel->regs + INTELXVF_CTRL ); +} + +/****************************************************************************** + * + * Link state + * + ****************************************************************************** + */ + +/** + * Check link state + * + * @v netdev Network device + */ +static void intelxvf_check_link ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t links; + + /* Read link status */ + links = readl ( intel->regs + INTELXVF_LINKS ); + DBGC ( intel, "INTEL %p link status is %08x\n", intel, links ); + + /* Update network device */ + if ( links & INTELXVF_LINKS_UP ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } +} + +/****************************************************************************** + * + * Mailbox messages + * + ****************************************************************************** + */ + +/** + * Send negotiate API version message + * + * @v intel Intel device + * @v version Requested version + * @ret rc Return status code + */ +static int intelxvf_mbox_version ( struct intel_nic *intel, + unsigned int version ) { + union intelvf_msg msg; + int rc; + + /* Send set MTU message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.hdr = INTELXVF_MSG_TYPE_VERSION; + msg.version.version = version; + if ( ( rc = intelvf_mbox_msg ( intel, &msg ) ) != 0 ) { + DBGC ( intel, "INTEL %p negotiate API version failed: %s\n", + intel, strerror ( rc ) ); + return rc; + } + + /* Check response */ + if ( ( msg.hdr & INTELVF_MSG_TYPE_MASK ) != INTELXVF_MSG_TYPE_VERSION ){ + DBGC ( intel, "INTEL %p negotiate API version unexpected " + "response:\n", intel ); + DBGC_HDA ( intel, 0, &msg, sizeof ( msg ) ); + return -EPROTO; + } + + /* Check that this version is supported */ + if ( ! ( msg.hdr & INTELVF_MSG_ACK ) ) { + DBGC ( intel, "INTEL %p negotiate API version failed\n", + intel ); + return -EPERM; + } + + return 0; +} + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int intelxvf_open ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t srrctl; + uint32_t dca_rxctrl; + int rc; + + /* Reset the function */ + intelxvf_reset ( intel ); + + /* Notify PF that reset is complete */ + if ( ( rc = intelvf_mbox_reset ( intel, NULL ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not reset: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_reset; + } + + /* Negotiate API version 1.1. If we do not negotiate at least + * this version, then the RX datapath will remain disabled if + * the PF has jumbo frames enabled. + * + * Ignore failures, since the host may not actually support + * v1.1. + */ + intelxvf_mbox_version ( intel, INTELXVF_MSG_VERSION_1_1 ); + + /* Set MAC address */ + if ( ( rc = intelvf_mbox_set_mac ( intel, netdev->ll_addr ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not set MAC address: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_set_mac; + } + + /* Set MTU */ + if ( ( rc = intelvf_mbox_set_mtu ( intel, netdev->max_pkt_len ) ) != 0){ + DBGC ( intel, "INTEL %p could not set MTU %zd: %s\n", + intel, netdev->max_pkt_len, strerror ( rc ) ); + goto err_mbox_set_mtu; + } + + /* Create transmit descriptor ring */ + if ( ( rc = intel_create_ring ( intel, &intel->tx ) ) != 0 ) + goto err_create_tx; + + /* Create receive descriptor ring */ + if ( ( rc = intel_create_ring ( intel, &intel->rx ) ) != 0 ) + goto err_create_rx; + + /* Allocate interrupt vectors */ + writel ( ( INTELXVF_IVAR_RX0_DEFAULT | INTELXVF_IVAR_RX0_VALID | + INTELXVF_IVAR_TX0_DEFAULT | INTELXVF_IVAR_TX0_VALID ), + intel->regs + INTELXVF_IVAR ); + writel ( ( INTELXVF_IVARM_MBOX_DEFAULT | INTELXVF_IVARM_MBOX_VALID ), + intel->regs + INTELXVF_IVARM ); + + /* Configure receive buffer sizes and set receive descriptor type */ + srrctl = readl ( intel->regs + INTELXVF_SRRCTL ); + srrctl &= ~( INTELXVF_SRRCTL_BSIZE_MASK | + INTELXVF_SRRCTL_DESCTYPE_MASK ); + srrctl |= ( INTELXVF_SRRCTL_BSIZE_DEFAULT | + INTELXVF_SRRCTL_DESCTYPE_DEFAULT ); + writel ( srrctl, intel->regs + INTELXVF_SRRCTL ); + + /* Clear "must-be-zero" bit for direct cache access (DCA). We + * leave DCA disabled anyway, but if we do not clear this bit + * then the received packets contain garbage data. + */ + dca_rxctrl = readl ( intel->regs + INTELXVF_DCA_RXCTRL ); + dca_rxctrl &= ~INTELXVF_DCA_RXCTRL_MUST_BE_ZERO; + writel ( dca_rxctrl, intel->regs + INTELXVF_DCA_RXCTRL ); + + /* Fill receive ring */ + intel_refill_rx ( intel ); + + /* Update link state */ + intelxvf_check_link ( netdev ); + + return 0; + + intel_destroy_ring ( intel, &intel->rx ); + err_create_rx: + intel_destroy_ring ( intel, &intel->tx ); + err_create_tx: + err_mbox_set_mtu: + err_mbox_set_mac: + err_mbox_reset: + intelxvf_reset ( intel ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void intelxvf_close ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + + /* Destroy receive descriptor ring */ + intel_destroy_ring ( intel, &intel->rx ); + + /* Discard any unused receive buffers */ + intel_empty_rx ( intel ); + + /* Destroy transmit descriptor ring */ + intel_destroy_ring ( intel, &intel->tx ); + + /* Reset the function */ + intelxvf_reset ( intel ); +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void intelxvf_poll ( struct net_device *netdev ) { + struct intel_nic *intel = netdev->priv; + uint32_t eicr; + int rc; + + /* Check for and acknowledge interrupts */ + eicr = readl ( intel->regs + INTELXVF_EICR ); + if ( ! eicr ) + return; + + /* Poll for TX completions, if applicable */ + if ( eicr & INTELXVF_EIRQ_TX0 ) + intel_poll_tx ( netdev ); + + /* Poll for RX completions, if applicable */ + if ( eicr & INTELXVF_EIRQ_RX0 ) + intel_poll_rx ( netdev ); + + /* Poll for mailbox messages, if applicable */ + if ( eicr & INTELXVF_EIRQ_MBOX ) { + + /* Poll mailbox */ + if ( ( rc = intelvf_mbox_poll ( intel ) ) != 0 ) { + DBGC ( intel, "INTEL %p mailbox poll failed!\n", + intel ); + netdev_rx_err ( netdev, NULL, rc ); + } + + /* Update link state */ + intelxvf_check_link ( netdev ); + } + + /* Refill RX ring */ + intel_refill_rx ( intel ); +} + +/** + * Enable or disable interrupts + * + * @v netdev Network device + * @v enable Interrupts should be enabled + */ +static void intelxvf_irq ( struct net_device *netdev, int enable ) { + struct intel_nic *intel = netdev->priv; + uint32_t mask; + + mask = ( INTELXVF_EIRQ_MBOX | INTELXVF_EIRQ_TX0 | INTELXVF_EIRQ_RX0 ); + if ( enable ) { + writel ( mask, intel->regs + INTELXVF_EIMS ); + } else { + writel ( mask, intel->regs + INTELXVF_EIMC ); + } +} + +/** Network device operations */ +static struct net_device_operations intelxvf_operations = { + .open = intelxvf_open, + .close = intelxvf_close, + .transmit = intel_transmit, + .poll = intelxvf_poll, + .irq = intelxvf_irq, +}; + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int intelxvf_probe ( struct pci_device *pci ) { + struct net_device *netdev; + struct intel_nic *intel; + int rc; + + /* Allocate and initialise net device */ + netdev = alloc_etherdev ( sizeof ( *intel ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &intelxvf_operations ); + intel = netdev->priv; + pci_set_drvdata ( pci, netdev ); + netdev->dev = &pci->dev; + memset ( intel, 0, sizeof ( *intel ) ); + intel_init_mbox ( &intel->mbox, INTELXVF_MBCTRL, INTELXVF_MBMEM ); + intel_init_ring ( &intel->tx, INTEL_NUM_TX_DESC, INTELXVF_TD, + intel_describe_tx_adv ); + intel_init_ring ( &intel->rx, INTEL_NUM_RX_DESC, INTELXVF_RD, + intel_describe_rx ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + intel->regs = ioremap ( pci->membase, INTELVF_BAR_SIZE ); + if ( ! intel->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset the function */ + intelxvf_reset ( intel ); + + /* Send reset message and fetch MAC address */ + if ( ( rc = intelvf_mbox_reset ( intel, netdev->hw_addr ) ) != 0 ) { + DBGC ( intel, "INTEL %p could not reset and fetch MAC: %s\n", + intel, strerror ( rc ) ); + goto err_mbox_reset; + } + + /* Reset the function (since we will not respond to Control + * ("ping") mailbox messages until the network device is opened. + */ + intelxvf_reset ( intel ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register_netdev; + + /* Set initial link state */ + intelxvf_check_link ( netdev ); + + return 0; + + unregister_netdev ( netdev ); + err_register_netdev: + err_mbox_reset: + intelxvf_reset ( intel ); + iounmap ( intel->regs ); + err_ioremap: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void intelxvf_remove ( struct pci_device *pci ) { + struct net_device *netdev = pci_get_drvdata ( pci ); + struct intel_nic *intel = netdev->priv; + + /* Unregister network device */ + unregister_netdev ( netdev ); + + /* Reset the NIC */ + intelxvf_reset ( intel ); + + /* Free network device */ + iounmap ( intel->regs ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** PCI device IDs */ +static struct pci_device_id intelxvf_nics[] = { + PCI_ROM ( 0x8086, 0x10ed, "82599-vf", "82599 VF", 0 ), + PCI_ROM ( 0x8086, 0x1515, "x540-vf", "X540 VF", 0 ), + PCI_ROM ( 0x8086, 0x1565, "x550-vf", "X550 VF", 0 ), + PCI_ROM ( 0x8086, 0x15a8, "x552-vf", "X552 VF", 0 ), +}; + +/** PCI driver */ +struct pci_driver intelxvf_driver __pci_driver = { + .ids = intelxvf_nics, + .id_count = ( sizeof ( intelxvf_nics ) / sizeof ( intelxvf_nics[0] ) ), + .probe = intelxvf_probe, + .remove = intelxvf_remove, +}; diff --git a/roms/ipxe/src/drivers/net/intelxvf.h b/roms/ipxe/src/drivers/net/intelxvf.h new file mode 100644 index 000000000..ad046a65c --- /dev/null +++ b/roms/ipxe/src/drivers/net/intelxvf.h @@ -0,0 +1,104 @@ +#ifndef _INTELXVF_H +#define _INTELXVF_H + +/** @file + * + * Intel 10 Gigabit Ethernet virtual function network card driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include "intelvf.h" + +/** Control Register */ +#define INTELXVF_CTRL 0x0000UL +#define INTELXVF_CTRL_RST 0x04000000UL /**< Function-level reset */ + +/** Link Status Register */ +#define INTELXVF_LINKS 0x0010UL +#define INTELXVF_LINKS_UP 0x40000000UL /**< Link up */ + +/** Extended Interrupt Cause Read Register */ +#define INTELXVF_EICR 0x0100UL +#define INTELXVF_EIRQ_RX0 0x00000001UL /**< RX queue 0 (via IVAR) */ +#define INTELXVF_EIRQ_TX0 0x00000002UL /**< TX queue 0 (via IVAR) */ +#define INTELXVF_EIRQ_MBOX 0x00000004UL /**< Mailbox (via IVARM) */ + +/** Extended Interrupt Mask Set/Read Register */ +#define INTELXVF_EIMS 0x0108UL + +/** Extended Interrupt Mask Clear Register */ +#define INTELXVF_EIMC 0x010cUL + +/** Interrupt Vector Allocation Register */ +#define INTELXVF_IVAR 0x0120UL +#define INTELXVF_IVAR_RX0(bit) ( (bit) << 0 ) /**< RX queue 0 allocation */ +#define INTELXVF_IVAR_RX0_DEFAULT INTELXVF_IVAR_RX0 ( 0x00 ) +#define INTELXVF_IVAR_RX0_MASK INTELXVF_IVAR_RX0 ( 0x01 ) +#define INTELXVF_IVAR_RX0_VALID 0x00000080UL /**< RX queue 0 valid */ +#define INTELXVF_IVAR_TX0(bit) ( (bit) << 8 ) /**< TX queue 0 allocation */ +#define INTELXVF_IVAR_TX0_DEFAULT INTELXVF_IVAR_TX0 ( 0x01 ) +#define INTELXVF_IVAR_TX0_MASK INTELXVF_IVAR_TX0 ( 0x01 ) +#define INTELXVF_IVAR_TX0_VALID 0x00008000UL /**< TX queue 0 valid */ + +/** Interrupt Vector Allocation Miscellaneous Register */ +#define INTELXVF_IVARM 0x0140UL +#define INTELXVF_IVARM_MBOX(bit) ( (bit) << 0 ) /**< Mailbox allocation */ +#define INTELXVF_IVARM_MBOX_DEFAULT INTELXVF_IVARM_MBOX ( 0x02 ) +#define INTELXVF_IVARM_MBOX_MASK INTELXVF_IVARM_MBOX ( 0x03 ) +#define INTELXVF_IVARM_MBOX_VALID 0x00000080UL /**< Mailbox valid */ + +/** Mailbox Memory Register Base */ +#define INTELXVF_MBMEM 0x0200UL + +/** Mailbox Control Register */ +#define INTELXVF_MBCTRL 0x02fcUL + +/** Receive Descriptor register block */ +#define INTELXVF_RD 0x1000UL + +/** RX DCA Control Register */ +#define INTELXVF_DCA_RXCTRL 0x100cUL +#define INTELXVF_DCA_RXCTRL_MUST_BE_ZERO 0x00001000UL /**< Must be zero */ + +/** Split Receive Control Register */ +#define INTELXVF_SRRCTL 0x1014UL +#define INTELXVF_SRRCTL_BSIZE(kb) ( (kb) << 0 ) /**< Receive buffer size */ +#define INTELXVF_SRRCTL_BSIZE_DEFAULT INTELXVF_SRRCTL_BSIZE ( 0x02 ) +#define INTELXVF_SRRCTL_BSIZE_MASK INTELXVF_SRRCTL_BSIZE ( 0x1f ) +#define INTELXVF_SRRCTL_DESCTYPE(typ) ( (typ) << 25 ) /**< Descriptor type */ +#define INTELXVF_SRRCTL_DESCTYPE_DEFAULT INTELXVF_SRRCTL_DESCTYPE ( 0x00 ) +#define INTELXVF_SRRCTL_DESCTYPE_MASK INTELXVF_SRRCTL_DESCTYPE ( 0x07 ) + +/** Good Packets Received Count */ +#define INTELXVF_GPRC 0x101c + +/** Good Packets Received Count Low */ +#define INTELXVF_GORCL 0x1020 + +/** Good Packets Received Count High */ +#define INTELXVF_GORCH 0x1024 + +/* Multicast Packets Received Count */ +#define INTELXVF_MPRC 0x1034 + +/** Transmit Descriptor register block */ +#define INTELXVF_TD 0x2000UL + +/** Good Packets Transmitted Count */ +#define INTELXVF_GPTC 0x201c + +/** Good Packets Transmitted Count Low */ +#define INTELXVF_GOTCL 0x2020 + +/** Good Packets Transmitted Count High */ +#define INTELXVF_GOTCH 0x2024 + +/** Negotiate API version mailbox message */ +#define INTELXVF_MSG_TYPE_VERSION 0x00000008UL + +/** API version 1.1 */ +#define INTELXVF_MSG_VERSION_1_1 0x00000002UL + +#endif /* _INTELXVF_H */ diff --git a/roms/ipxe/src/drivers/net/ipoib.c b/roms/ipxe/src/drivers/net/ipoib.c index 1b5391776..6552d764e 100644 --- a/roms/ipxe/src/drivers/net/ipoib.c +++ b/roms/ipxe/src/drivers/net/ipoib.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -29,8 +33,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/errortab.h> #include <ipxe/malloc.h> #include <ipxe/if_arp.h> +#include <ipxe/arp.h> #include <ipxe/if_ether.h> #include <ipxe/ethernet.h> +#include <ipxe/ip.h> #include <ipxe/iobuf.h> #include <ipxe/netdevice.h> #include <ipxe/infiniband.h> @@ -44,6 +50,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); * IP over Infiniband */ +/* Disambiguate the various error causes */ +#define ENXIO_ARP_REPLY __einfo_error ( EINFO_ENXIO_ARP_REPLY ) +#define EINFO_ENXIO_ARP_REPLY \ + __einfo_uniqify ( EINFO_ENXIO, 0x01, \ + "Missing REMAC for ARP reply target address" ) +#define ENXIO_NON_IPV4 __einfo_error ( EINFO_ENXIO_NON_IPV4 ) +#define EINFO_ENXIO_NON_IPV4 \ + __einfo_uniqify ( EINFO_ENXIO, 0x02, \ + "Missing REMAC for non-IPv4 packet" ) +#define ENXIO_ARP_SENT __einfo_error ( EINFO_ENXIO_ARP_SENT ) +#define EINFO_ENXIO_ARP_SENT \ + __einfo_uniqify ( EINFO_ENXIO, 0x03, \ + "Missing REMAC for IPv4 packet (ARP sent)" ) + /** Number of IPoIB send work queue entries */ #define IPOIB_NUM_SEND_WQES 2 @@ -96,6 +116,8 @@ struct errortab ipoib_errors[] __errortab = { __einfo_errortab ( EINFO_EINPROGRESS_JOINING ), }; +static struct net_device_operations ipoib_operations; + /**************************************************************************** * * IPoIB REMAC cache @@ -124,8 +146,10 @@ static struct ipoib_mac * ipoib_find_remac ( struct ipoib_device *ipoib, const struct ipoib_remac *remac ) { struct ipoib_peer *peer; - /* Check for broadcast REMAC */ - if ( is_broadcast_ether_addr ( remac ) ) + /* Check for broadcast or multicast REMAC. We transmit + * multicasts as broadcasts for simplicity. + */ + if ( is_multicast_ether_addr ( remac ) ) return &ipoib->broadcast; /* Try to find via REMAC cache */ @@ -202,14 +226,20 @@ static void ipoib_flush_remac ( struct ipoib_device *ipoib ) { * @ret discarded Number of cached items discarded */ static unsigned int ipoib_discard_remac ( void ) { - struct ib_device *ibdev; + struct net_device *netdev; struct ipoib_device *ipoib; struct ipoib_peer *peer; unsigned int discarded = 0; /* Try to discard one cache entry for each IPoIB device */ - for_each_ibdev ( ibdev ) { - ipoib = ib_get_ownerdata ( ibdev ); + for_each_netdev ( netdev ) { + + /* Skip non-IPoIB devices */ + if ( netdev->op != &ipoib_operations ) + continue; + ipoib = netdev->priv; + + /* Discard least recently used cache entry (if any) */ list_for_each_entry_reverse ( peer, &ipoib->peers, list ) { list_del ( &peer->list ); free ( peer ); @@ -222,7 +252,7 @@ static unsigned int ipoib_discard_remac ( void ) { } /** IPoIB cache discarder */ -struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_NORMAL ) = { +struct cache_discarder ipoib_discarder __cache_discarder ( CACHE_EXPENSIVE ) = { .discard = ipoib_discard_remac, }; @@ -324,8 +354,11 @@ static int ipoib_translate_tx_arp ( struct net_device *netdev, /* Look up REMAC, if applicable */ if ( arphdr->ar_op == ARPOP_REPLY ) { target_ha = ipoib_find_remac ( ipoib, arp_target_pa ( arphdr )); - if ( ! target_ha ) - return -ENXIO; + if ( ! target_ha ) { + DBGC ( ipoib, "IPoIB %p no REMAC for %s ARP reply\n", + ipoib, eth_ntoa ( arp_target_pa ( arphdr ) ) ); + return -ENXIO_ARP_REPLY; + } } /* Construct new packet */ @@ -461,6 +494,7 @@ static int ipoib_transmit ( struct net_device *netdev, struct ipoib_device *ipoib = netdev->priv; struct ib_device *ibdev = ipoib->ibdev; struct ethhdr *ethhdr; + struct iphdr *iphdr; struct ipoib_hdr *ipoib_hdr; struct ipoib_mac *mac; struct ib_address_vector dest; @@ -485,9 +519,34 @@ static int ipoib_transmit ( struct net_device *netdev, iob_pull ( iobuf, sizeof ( *ethhdr ) ); /* Identify destination address */ - mac = ipoib_find_remac ( ipoib, ( ( void *) ethhdr->h_dest ) ); - if ( ! mac ) - return -ENXIO; + mac = ipoib_find_remac ( ipoib, ( ( void * ) ethhdr->h_dest ) ); + if ( ! mac ) { + /* Generate a new ARP request (if possible) to trigger + * population of the REMAC cache entry. + */ + if ( ( net_proto != htons ( ETH_P_IP ) ) || + ( iob_len ( iobuf ) < sizeof ( *iphdr ) ) ) { + DBGC ( ipoib, "IPoIB %p no REMAC for %s non-IPv4 " + "packet type %04x\n", ipoib, + eth_ntoa ( ethhdr->h_dest ), + ntohs ( net_proto ) ); + return -ENXIO_NON_IPV4; + } + iphdr = iobuf->data; + if ( ( rc = arp_tx_request ( netdev, &ipv4_protocol, + &iphdr->dest, &iphdr->src ) ) !=0){ + DBGC ( ipoib, "IPoIB %p could not ARP for %s/%s/", + ipoib, eth_ntoa ( ethhdr->h_dest ), + inet_ntoa ( iphdr->dest ) ); + DBGC ( ipoib, "%s: %s\n", inet_ntoa ( iphdr->src ), + strerror ( rc ) ); + return rc; + } + DBGC ( ipoib, "IPoIB %p no REMAC for %s/%s/", ipoib, + eth_ntoa ( ethhdr->h_dest ), inet_ntoa ( iphdr->dest ) ); + DBGC ( ipoib, "%s\n", inet_ntoa ( iphdr->src ) ); + return -ENXIO_ARP_SENT; + } /* Translate packet if applicable */ if ( ( rc = ipoib_translate_tx ( netdev, iobuf, net_proto ) ) != 0 ) @@ -732,7 +791,8 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) { int rc; /* Leave existing broadcast group */ - ipoib_leave_broadcast_group ( ipoib ); + if ( ipoib->qp ) + ipoib_leave_broadcast_group ( ipoib ); /* Update MAC address based on potentially-new GID prefix */ memcpy ( &ipoib->mac.gid.s.prefix, &ibdev->gid.s.prefix, @@ -747,7 +807,7 @@ static void ipoib_link_state_changed ( struct ib_device *ibdev ) { netdev_link_err ( netdev, ( rc ? rc : -EINPROGRESS_JOINING ) ); /* Join new broadcast group */ - if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && + if ( ib_is_open ( ibdev ) && ib_link_ok ( ibdev ) && ipoib->qp && ( ( rc = ipoib_join_broadcast_group ( ipoib ) ) != 0 ) ) { DBGC ( ipoib, "IPoIB %p could not rejoin broadcast group: " "%s\n", ipoib, strerror ( rc ) ); @@ -835,7 +895,9 @@ static void ipoib_close ( struct net_device *netdev ) { /* Tear down the queues */ ib_destroy_qp ( ibdev, ipoib->qp ); + ipoib->qp = NULL; ib_destroy_cq ( ibdev, ipoib->cq ); + ipoib->cq = NULL; /* Close IB device */ ib_close ( ibdev ); diff --git a/roms/ipxe/src/drivers/net/legacy.c b/roms/ipxe/src/drivers/net/legacy.c index 4edbef162..73a80194f 100644 --- a/roms/ipxe/src/drivers/net/legacy.c +++ b/roms/ipxe/src/drivers/net/legacy.c @@ -17,7 +17,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct nic nic; diff --git a/roms/ipxe/src/drivers/net/mii.c b/roms/ipxe/src/drivers/net/mii.c index c4d32514d..9b297029a 100644 --- a/roms/ipxe/src/drivers/net/mii.c +++ b/roms/ipxe/src/drivers/net/mii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <unistd.h> @@ -111,3 +115,35 @@ int mii_reset ( struct mii_interface *mii ) { DBGC ( mii, "MII %p timed out waiting for reset\n", mii ); return -ETIMEDOUT; } + +/** + * Update link status via MII + * + * @v mii MII interface + * @v netdev Network device + * @ret rc Return status code + */ +int mii_check_link ( struct mii_interface *mii, struct net_device *netdev ) { + int bmsr; + int link; + int rc; + + /* Read BMSR */ + bmsr = mii_read ( mii, MII_BMSR ); + if ( bmsr < 0 ) { + rc = bmsr; + return rc; + } + + /* Report link status */ + link = ( bmsr & BMSR_LSTATUS ); + DBGC ( mii, "MII %p link %s (BMSR %#04x)\n", + mii, ( link ? "up" : "down" ), bmsr ); + if ( link ) { + netdev_link_up ( netdev ); + } else { + netdev_link_down ( netdev ); + } + + return 0; +} diff --git a/roms/ipxe/src/drivers/net/myson.c b/roms/ipxe/src/drivers/net/myson.c index 6abb55660..84a550596 100644 --- a/roms/ipxe/src/drivers/net/myson.c +++ b/roms/ipxe/src/drivers/net/myson.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/drivers/net/myson.h b/roms/ipxe/src/drivers/net/myson.h index 8d7cc5855..05a6b8a58 100644 --- a/roms/ipxe/src/drivers/net/myson.h +++ b/roms/ipxe/src/drivers/net/myson.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/if_ether.h> diff --git a/roms/ipxe/src/drivers/net/ncm.c b/roms/ipxe/src/drivers/net/ncm.c new file mode 100644 index 000000000..10728d2a1 --- /dev/null +++ b/roms/ipxe/src/drivers/net/ncm.c @@ -0,0 +1,672 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/if_ether.h> +#include <ipxe/profile.h> +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include "ecm.h" +#include "ncm.h" + +/** @file + * + * CDC-NCM USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler ncm_intr_profiler __profiler = + { .name = "ncm.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler ncm_in_profiler __profiler = + { .name = "ncm.in" }; + +/** Bulk IN per-datagram profiler */ +static struct profiler ncm_in_datagram_profiler __profiler = + { .name = "ncm.in_dgram" }; + +/** Bulk OUT profiler */ +static struct profiler ncm_out_profiler __profiler = + { .name = "ncm.out" }; + +/****************************************************************************** + * + * CDC-NCM communications interface + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.intr ); + struct net_device *netdev = ncm->netdev; + struct usb_setup_packet *message; + size_t len = iob_len ( iobuf ); + + /* Profile completions */ + profile_start ( &ncm_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( ncm, "NCM %p interrupt failed: %s\n", + ncm, strerror ( rc ) ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Extract message header */ + if ( len < sizeof ( *message ) ) { + DBGC ( ncm, "NCM %p underlength interrupt:\n", ncm ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto error; + } + message = iobuf->data; + + /* Parse message header */ + switch ( message->request ) { + + case cpu_to_le16 ( CDC_NETWORK_CONNECTION ) : + if ( message->value ) { + DBGC ( ncm, "NCM %p link up\n", ncm ); + netdev_link_up ( netdev ); + } else { + DBGC ( ncm, "NCM %p link down\n", ncm ); + netdev_link_down ( netdev ); + } + break; + + case cpu_to_le16 ( CDC_CONNECTION_SPEED_CHANGE ) : + /* Ignore */ + break; + + default: + DBGC ( ncm, "NCM %p unrecognised interrupt:\n", ncm ); + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + goto error; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + profile_stop ( &ncm_intr_profiler ); + + return; + + error: + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); + ignore: + free_iob ( iobuf ); + return; +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations ncm_intr_operations = { + .complete = ncm_intr_complete, +}; + +/****************************************************************************** + * + * CDC-NCM data interface + * + ****************************************************************************** + */ + +/** + * Prefill bulk IN endpoint + * + * @v ncm CDC-NCM device + * @ret rc Return status code + */ +static int ncm_in_prefill ( struct ncm_device *ncm ) { + struct usb_bus *bus = ncm->bus; + size_t mtu; + unsigned int count; + int rc; + + /* Some devices have a very small number of internal buffers, + * and rely on being able to pack multiple packets into each + * buffer. We therefore want to use large buffers if + * possible. However, large allocations have a reasonable + * chance of failure, especially if this is not the first or + * only device to be opened. + * + * We therefore attempt to find a usable buffer size, starting + * large and working downwards until allocation succeeds. + * Smaller buffers will still work, albeit with a higher + * chance of packet loss and so lower overall throughput. + */ + for ( mtu = ncm->mtu ; mtu >= NCM_MIN_NTB_INPUT_SIZE ; mtu >>= 1 ) { + + /* Attempt allocation at this MTU */ + if ( mtu > NCM_MAX_NTB_INPUT_SIZE ) + continue; + if ( mtu > bus->mtu ) + continue; + count = ( NCM_IN_MIN_SIZE / mtu ); + if ( count < NCM_IN_MIN_COUNT ) + count = NCM_IN_MIN_COUNT; + if ( ( count * mtu ) > NCM_IN_MAX_SIZE ) + continue; + usb_refill_init ( &ncm->usbnet.in, mtu, count ); + if ( ( rc = usb_prefill ( &ncm->usbnet.in ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not prefill %dx %zd-byte " + "buffers for bulk IN\n", ncm, count, mtu ); + continue; + } + + DBGC ( ncm, "NCM %p using %dx %zd-byte buffers for bulk IN\n", + ncm, count, mtu ); + return 0; + } + + DBGC ( ncm, "NCM %p could not prefill bulk IN endpoint\n", ncm ); + return -ENOMEM; +} + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_in_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.in ); + struct net_device *netdev = ncm->netdev; + struct ncm_transfer_header *nth; + struct ncm_datagram_pointer *ndp; + struct ncm_datagram_descriptor *desc; + struct io_buffer *pkt; + unsigned int remaining; + size_t ndp_offset; + size_t ndp_len; + size_t pkt_offset; + size_t pkt_len; + size_t headroom; + size_t len; + + /* Profile overall bulk IN completion */ + profile_start ( &ncm_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto ignore; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( ncm, "NCM %p bulk IN failed: %s\n", + ncm, strerror ( rc ) ); + goto error; + } + + /* Locate transfer header */ + len = iob_len ( iobuf ); + if ( sizeof ( *nth ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NTH:\n", ncm ); + rc = -EINVAL; + goto error; + } + nth = iobuf->data; + + /* Locate datagram pointer */ + ndp_offset = le16_to_cpu ( nth->offset ); + if ( ( ndp_offset + sizeof ( *ndp ) ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm ); + rc = -EINVAL; + goto error; + } + ndp = ( iobuf->data + ndp_offset ); + ndp_len = le16_to_cpu ( ndp->header_len ); + if ( ndp_len < offsetof ( typeof ( *ndp ), desc ) ) { + DBGC ( ncm, "NCM %p NDP header length too short:\n", ncm ); + rc = -EINVAL; + goto error; + } + if ( ( ndp_offset + ndp_len ) > len ) { + DBGC ( ncm, "NCM %p packet too short for NDP:\n", ncm ); + rc = -EINVAL; + goto error; + } + + /* Process datagrams */ + remaining = ( ( ndp_len - offsetof ( typeof ( *ndp ), desc ) ) / + sizeof ( ndp->desc[0] ) ); + for ( desc = ndp->desc ; remaining && desc->offset ; remaining-- ) { + + /* Profile individual datagrams */ + profile_start ( &ncm_in_datagram_profiler ); + + /* Locate datagram */ + pkt_offset = le16_to_cpu ( desc->offset ); + pkt_len = le16_to_cpu ( desc->len ); + if ( pkt_len < ETH_HLEN ) { + DBGC ( ncm, "NCM %p underlength datagram:\n", ncm ); + rc = -EINVAL; + goto error; + } + if ( ( pkt_offset + pkt_len ) > len ) { + DBGC ( ncm, "NCM %p datagram exceeds packet:\n", ncm ); + rc = -EINVAL; + goto error; + } + + /* Move to next descriptor */ + desc++; + + /* Copy data to a new I/O buffer. Our USB buffers may + * be very large and so we choose to recycle the + * buffers directly rather than attempt reallocation + * while the device is running. We therefore copy the + * data to a new I/O buffer even if this is the only + * (or last) packet within the buffer. + * + * We reserve enough space at the start of each buffer + * to allow for our own transmission header, to + * support protocols such as ARP which may modify the + * received packet and reuse the same I/O buffer for + * transmission. + */ + headroom = ( sizeof ( struct ncm_ntb_header ) + ncm->padding ); + pkt = alloc_iob ( headroom + pkt_len ); + if ( ! pkt ) { + /* Record error and continue */ + netdev_rx_err ( netdev, NULL, -ENOMEM ); + continue; + } + iob_reserve ( pkt, headroom ); + memcpy ( iob_put ( pkt, pkt_len ), + ( iobuf->data + pkt_offset ), pkt_len ); + + /* Strip CRC, if present */ + if ( ndp->magic & cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC_CRC)) + iob_unput ( pkt, 4 /* CRC32 */ ); + + /* Hand off to network stack */ + netdev_rx ( netdev, pkt ); + profile_stop ( &ncm_in_datagram_profiler ); + } + + /* Recycle I/O buffer */ + usb_recycle ( &ncm->usbnet.in, iobuf ); + profile_stop ( &ncm_in_profiler ); + + return; + + error: + /* Record error against network device */ + DBGC_HDA ( ncm, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + ignore: + usb_recycle ( &ncm->usbnet.in, iobuf ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations ncm_in_operations = { + .complete = ncm_in_complete, +}; + +/** + * Transmit packet + * + * @v ncm CDC-NCM device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ncm_out_transmit ( struct ncm_device *ncm, + struct io_buffer *iobuf ) { + struct ncm_ntb_header *header; + size_t len = iob_len ( iobuf ); + size_t header_len = ( sizeof ( *header ) + ncm->padding ); + int rc; + + /* Profile transmissions */ + profile_start ( &ncm_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, header_len ) ) != 0 ) + return rc; + header = iob_push ( iobuf, header_len ); + + /* Populate header */ + header->nth.magic = cpu_to_le32 ( NCM_TRANSFER_HEADER_MAGIC ); + header->nth.header_len = cpu_to_le16 ( sizeof ( header->nth ) ); + header->nth.sequence = cpu_to_le16 ( ncm->sequence ); + header->nth.len = cpu_to_le16 ( iob_len ( iobuf ) ); + header->nth.offset = + cpu_to_le16 ( offsetof ( typeof ( *header ), ndp ) ); + header->ndp.magic = cpu_to_le32 ( NCM_DATAGRAM_POINTER_MAGIC ); + header->ndp.header_len = cpu_to_le16 ( sizeof ( header->ndp ) + + sizeof ( header->desc ) ); + header->ndp.offset = cpu_to_le16 ( 0 ); + header->desc[0].offset = cpu_to_le16 ( header_len ); + header->desc[0].len = cpu_to_le16 ( len ); + memset ( &header->desc[1], 0, sizeof ( header->desc[1] ) ); + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &ncm->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + /* Increment sequence number */ + ncm->sequence++; + + profile_stop ( &ncm_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void ncm_out_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int rc ) { + struct ncm_device *ncm = container_of ( ep, struct ncm_device, + usbnet.out ); + struct net_device *netdev = ncm->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations ncm_out_operations = { + .complete = ncm_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int ncm_open ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + struct usb_device *usb = ncm->usb; + struct ncm_set_ntb_input_size size; + int rc; + + /* Reset sequence number */ + ncm->sequence = 0; + + /* Prefill I/O buffers */ + if ( ( rc = ncm_in_prefill ( ncm ) ) != 0 ) + goto err_prefill; + + /* Set maximum input size */ + memset ( &size, 0, sizeof ( size ) ); + size.mtu = cpu_to_le32 ( ncm->usbnet.in.len ); + if ( ( rc = usb_control ( usb, NCM_SET_NTB_INPUT_SIZE, 0, + ncm->usbnet.comms, &size, + sizeof ( size ) ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not set input size to %zd: %s\n", + ncm, ncm->usbnet.in.len, strerror ( rc ) ); + goto err_set_ntb_input_size; + } + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &ncm->usbnet ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not open: %s\n", + ncm, strerror ( rc ) ); + goto err_open; + } + + return 0; + + usbnet_close ( &ncm->usbnet ); + err_open: + err_set_ntb_input_size: + usb_flush ( &ncm->usbnet.in ); + err_prefill: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void ncm_close ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &ncm->usbnet ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ncm_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct ncm_device *ncm = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = ncm_out_transmit ( ncm, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void ncm_poll ( struct net_device *netdev ) { + struct ncm_device *ncm = netdev->priv; + int rc; + + /* Poll USB bus */ + usb_poll ( ncm->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &ncm->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + +} + +/** CDC-NCM network device operations */ +static struct net_device_operations ncm_operations = { + .open = ncm_open, + .close = ncm_close, + .transmit = ncm_transmit, + .poll = ncm_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int ncm_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct ncm_device *ncm; + struct usb_interface_descriptor *comms; + struct ecm_ethernet_descriptor *ethernet; + struct ncm_ntb_parameters params; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *ncm ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &ncm_operations ); + netdev->dev = &func->dev; + ncm = netdev->priv; + memset ( ncm, 0, sizeof ( *ncm ) ); + ncm->usb = usb; + ncm->bus = usb->port->hub->bus; + ncm->netdev = netdev; + usbnet_init ( &ncm->usbnet, func, &ncm_intr_operations, + &ncm_in_operations, &ncm_out_operations ); + usb_refill_init ( &ncm->usbnet.intr, 0, NCM_INTR_COUNT ); + DBGC ( ncm, "NCM %p on %s\n", ncm, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &ncm->usbnet, config ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not describe: %s\n", + ncm, strerror ( rc ) ); + goto err_describe; + } + + /* Locate Ethernet descriptor */ + comms = usb_interface_descriptor ( config, ncm->usbnet.comms, 0 ); + assert ( comms != NULL ); + ethernet = ecm_ethernet_descriptor ( config, comms ); + if ( ! ethernet ) { + DBGC ( ncm, "NCM %p has no Ethernet descriptor\n", ncm ); + rc = -EINVAL; + goto err_ethernet; + } + + /* Fetch MAC address */ + if ( ( rc = ecm_fetch_mac ( usb, ethernet, netdev->hw_addr ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not fetch MAC address: %s\n", + ncm, strerror ( rc ) ); + goto err_fetch_mac; + } + + /* Get NTB parameters */ + if ( ( rc = usb_control ( usb, NCM_GET_NTB_PARAMETERS, 0, + ncm->usbnet.comms, ¶ms, + sizeof ( params ) ) ) != 0 ) { + DBGC ( ncm, "NCM %p could not get NTB parameters: %s\n", + ncm, strerror ( rc ) ); + goto err_ntb_parameters; + } + + /* Get maximum supported input size */ + ncm->mtu = le32_to_cpu ( params.in.mtu ); + DBGC2 ( ncm, "NCM %p maximum IN size is %zd bytes\n", ncm, ncm->mtu ); + + /* Calculate transmit padding */ + ncm->padding = ( ( le16_to_cpu ( params.out.remainder ) - + sizeof ( struct ncm_ntb_header ) - ETH_HLEN ) & + ( le16_to_cpu ( params.out.divisor ) - 1 ) ); + DBGC2 ( ncm, "NCM %p using %zd-byte transmit padding\n", + ncm, ncm->padding ); + assert ( ( ( sizeof ( struct ncm_ntb_header ) + ncm->padding + + ETH_HLEN ) % le16_to_cpu ( params.out.divisor ) ) == + le16_to_cpu ( params.out.remainder ) ); + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, ncm ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_ntb_parameters: + err_fetch_mac: + err_ethernet: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void ncm_remove ( struct usb_function *func ) { + struct ncm_device *ncm = usb_func_get_drvdata ( func ); + struct net_device *netdev = ncm->netdev; + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** CDC-NCM device IDs */ +static struct usb_device_id ncm_ids[] = { + { + .name = "cdc-ncm", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_CDC, + .subclass = USB_SUBCLASS_CDC_NCM, + .protocol = 0, + }, + }, +}; + +/** CDC-NCM driver */ +struct usb_driver ncm_driver __usb_driver = { + .ids = ncm_ids, + .id_count = ( sizeof ( ncm_ids ) / sizeof ( ncm_ids[0] ) ), + .probe = ncm_probe, + .remove = ncm_remove, +}; diff --git a/roms/ipxe/src/drivers/net/ncm.h b/roms/ipxe/src/drivers/net/ncm.h new file mode 100644 index 000000000..a9565a56b --- /dev/null +++ b/roms/ipxe/src/drivers/net/ncm.h @@ -0,0 +1,173 @@ +#ifndef _NCM_H +#define _NCM_H + +/** @file + * + * CDC-NCM USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/usb.h> +#include <ipxe/cdc.h> +#include <byteswap.h> +#include "ecm.h" + +/** CDC-NCM subclass */ +#define USB_SUBCLASS_CDC_NCM 0x0d + +/** Get NTB parameters */ +#define NCM_GET_NTB_PARAMETERS \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x80 ) ) + +/** NTB datagram parameters */ +struct ncm_ntb_datagram_parameters { + /** Maximum size */ + uint32_t mtu; + /** Alignment divisor */ + uint16_t divisor; + /** Alignment remainder */ + uint16_t remainder; + /** Alignment modulus */ + uint16_t modulus; +} __attribute__ (( packed )); + +/** NTB parameters */ +struct ncm_ntb_parameters { + /** Length */ + uint16_t len; + /** Supported formats */ + uint16_t formats; + /** IN datagram parameters */ + struct ncm_ntb_datagram_parameters in; + /** Reserved */ + uint16_t reserved; + /** OUT datagram parameters */ + struct ncm_ntb_datagram_parameters out; + /** Maximum number of datagrams per OUT NTB */ + uint16_t max; +} __attribute__ (( packed )); + +/** Set NTB input size */ +#define NCM_SET_NTB_INPUT_SIZE \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x86 ) ) + +/** Set NTB input size */ +struct ncm_set_ntb_input_size { + /** Maximum size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Minimum allowed NTB input size */ +#define NCM_MIN_NTB_INPUT_SIZE 2048 + +/** Maximum allowed NTB input size (16-bit) */ +#define NCM_MAX_NTB_INPUT_SIZE 65536 + +/** CDC-NCM transfer header (16-bit) */ +struct ncm_transfer_header { + /** Signature */ + uint32_t magic; + /** Header length */ + uint16_t header_len; + /** Sequence number */ + uint16_t sequence; + /** Total length */ + uint16_t len; + /** Offset of first datagram pointer */ + uint16_t offset; +} __attribute__ (( packed )); + +/** CDC-NCM transfer header magic */ +#define NCM_TRANSFER_HEADER_MAGIC 0x484d434eUL + +/** CDC-NCM datagram descriptor (16-bit) */ +struct ncm_datagram_descriptor { + /** Starting offset */ + uint16_t offset; + /** Length */ + uint16_t len; +} __attribute__ (( packed )); + +/** CDC-NCM datagram pointer (16-bit) */ +struct ncm_datagram_pointer { + /** Signature */ + uint32_t magic; + /** Header length */ + uint16_t header_len; + /** Offset of next datagram pointer */ + uint16_t offset; + /** Datagram descriptors + * + * Must be terminated by an empty descriptor. + */ + struct ncm_datagram_descriptor desc[0]; +} __attribute__ (( packed )); + +/** CDC-NCM datagram pointer magic */ +#define NCM_DATAGRAM_POINTER_MAGIC 0x304d434eUL + +/** CDC-NCM datagram pointer CRC present flag */ +#define NCM_DATAGRAM_POINTER_MAGIC_CRC 0x01000000UL + +/** NTB constructed for transmitted packets (excluding padding) + * + * This is a policy decision. + */ +struct ncm_ntb_header { + /** Transfer header */ + struct ncm_transfer_header nth; + /** Datagram pointer */ + struct ncm_datagram_pointer ndp; + /** Datagram descriptors */ + struct ncm_datagram_descriptor desc[2]; +} __attribute__ (( packed )); + +/** A CDC-NCM network device */ +struct ncm_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + + /** Maximum supported NTB input size */ + size_t mtu; + /** Transmitted packet sequence number */ + uint16_t sequence; + /** Alignment padding required on transmitted packets */ + size_t padding; +}; + +/** Bulk IN ring minimum buffer count + * + * This is a policy decision. + */ +#define NCM_IN_MIN_COUNT 3 + +/** Bulk IN ring minimum total buffer size + * + * This is a policy decision. + */ +#define NCM_IN_MIN_SIZE 16384 + +/** Bulk IN ring maximum total buffer size + * + * This is a policy decision. + */ +#define NCM_IN_MAX_SIZE 131072 + +/** Interrupt ring buffer count + * + * This is a policy decision. + */ +#define NCM_INTR_COUNT 2 + +#endif /* _NCM_H */ diff --git a/roms/ipxe/src/drivers/net/netfront.c b/roms/ipxe/src/drivers/net/netfront.c index 4b816329e..2f4bbf2a0 100644 --- a/roms/ipxe/src/drivers/net/netfront.c +++ b/roms/ipxe/src/drivers/net/netfront.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -135,7 +139,7 @@ static int netfront_read_mac ( struct netfront_nic *netfront, void *hw_addr ) { xendev->key, mac ); /* Decode MAC address */ - len = hex_decode ( mac, ':', hw_addr, ETH_ALEN ); + len = hex_decode ( ':', mac, hw_addr, ETH_ALEN ); if ( len < 0 ) { rc = len; DBGC ( netfront, "NETFRONT %s could not decode MAC address " @@ -593,6 +597,11 @@ static int netfront_open ( struct net_device *netdev ) { "feature-no-csum-offload" ) ) != 0 ) goto err_feature_no_csum_offload; + /* Inform backend that we will send notifications for RX requests */ + if ( ( rc = netfront_write_flag ( netfront, + "feature-rx-notify" ) ) != 0 ) + goto err_feature_rx_notify; + /* Set state to Connected */ if ( ( rc = xenbus_set_state ( xendev, XenbusStateConnected ) ) != 0 ) { DBGC ( netfront, "NETFRONT %s could not set state=\"%d\": %s\n", @@ -618,6 +627,8 @@ static int netfront_open ( struct net_device *netdev ) { err_backend_wait: netfront_reset ( netfront ); err_set_state: + netfront_rm ( netfront, "feature-rx-notify" ); + err_feature_rx_notify: netfront_rm ( netfront, "feature-no-csum-offload" ); err_feature_no_csum_offload: netfront_rm ( netfront, "request-rx-copy" ); @@ -661,6 +672,7 @@ static void netfront_close ( struct net_device *netdev ) { } /* Delete flags */ + netfront_rm ( netfront, "feature-rx-notify" ); netfront_rm ( netfront, "feature-no-csum-offload" ); netfront_rm ( netfront, "request-rx-copy" ); diff --git a/roms/ipxe/src/drivers/net/netfront.h b/roms/ipxe/src/drivers/net/netfront.h index b3f899f3c..38fd0a77e 100644 --- a/roms/ipxe/src/drivers/net/netfront.h +++ b/roms/ipxe/src/drivers/net/netfront.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/xen.h> #include <xen/io/netif.h> diff --git a/roms/ipxe/src/drivers/net/netvsc.c b/roms/ipxe/src/drivers/net/netvsc.c new file mode 100644 index 000000000..d269cd63e --- /dev/null +++ b/roms/ipxe/src/drivers/net/netvsc.c @@ -0,0 +1,848 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V network virtual service client + * + * The network virtual service client (NetVSC) connects to the network + * virtual service provider (NetVSP) via the Hyper-V virtual machine + * bus (VMBus). It provides a transport layer for RNDIS packets. + */ + +#include <errno.h> +#include <unistd.h> +#include <byteswap.h> +#include <ipxe/umalloc.h> +#include <ipxe/rndis.h> +#include <ipxe/vmbus.h> +#include "netvsc.h" + +/** + * Send control message and wait for completion + * + * @v netvsc NetVSC device + * @v xrid Relative transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_control ( struct netvsc_device *netvsc, unsigned int xrid, + const void *data, size_t len ) { + uint64_t xid = ( NETVSC_BASE_XID + xrid ); + unsigned int i; + int rc; + + /* Send control message */ + if ( ( rc = vmbus_send_control ( netvsc->vmdev, xid, data, len ) ) !=0){ + DBGC ( netvsc, "NETVSC %s could not send control message: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + /* Record transaction ID */ + netvsc->wait_xrid = xrid; + + /* Wait for operation to complete */ + for ( i = 0 ; i < NETVSC_MAX_WAIT_MS ; i++ ) { + + /* Check for completion */ + if ( ! netvsc->wait_xrid ) + return netvsc->wait_rc; + + /* Poll VMBus device */ + vmbus_poll ( netvsc->vmdev ); + + /* Delay for 1ms */ + mdelay ( 1 ); + } + + DBGC ( netvsc, "NETVSC %s timed out waiting for XRID %d\n", + netvsc->name, xrid ); + vmbus_dump_channel ( netvsc->vmdev ); + return -ETIMEDOUT; +} + +/** + * Handle generic completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_completed ( struct netvsc_device *netvsc __unused, + const void *data __unused, size_t len __unused ) { + return 0; +} + +/** + * Initialise communication + * + * @v netvsc NetVSC device + * @ret rc Return status code + */ +static int netvsc_initialise ( struct netvsc_device *netvsc ) { + struct netvsc_init_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_INIT_MSG ); + msg.min = cpu_to_le32 ( NETVSC_VERSION_1 ); + msg.max = cpu_to_le32 ( NETVSC_VERSION_1 ); + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, NETVSC_INIT_XRID, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not initialise: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle initialisation completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int +netvsc_initialised ( struct netvsc_device *netvsc, const void *data, + size_t len ) { + const struct netvsc_init_completion *cmplt = data; + + /* Check completion */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( netvsc, "NETVSC %s underlength initialisation " + "completion (%zd bytes)\n", netvsc->name, len ); + return -EINVAL; + } + if ( cmplt->header.type != cpu_to_le32 ( NETVSC_INIT_CMPLT ) ) { + DBGC ( netvsc, "NETVSC %s unexpected initialisation completion " + "type %d\n", netvsc->name, + le32_to_cpu ( cmplt->header.type ) ); + return -EPROTO; + } + if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { + DBGC ( netvsc, "NETVSC %s initialisation failure status %d\n", + netvsc->name, le32_to_cpu ( cmplt->status ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Set NDIS version + * + * @v netvsc NetVSC device + * @ret rc Return status code + */ +static int netvsc_ndis_version ( struct netvsc_device *netvsc ) { + struct netvsc_ndis_version_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_NDIS_VERSION_MSG ); + msg.major = cpu_to_le32 ( NETVSC_NDIS_MAJOR ); + msg.minor = cpu_to_le32 ( NETVSC_NDIS_MINOR ); + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, NETVSC_NDIS_VERSION_XRID, + &msg, sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not set NDIS version: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Establish data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_establish_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct netvsc_establish_buffer_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( buffer->establish_type ); + msg.gpadl = cpu_to_le32 ( buffer->gpadl ); + msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, buffer->establish_xrid, &msg, + sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not establish buffer: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle establish receive data buffer completion + * + * @v netvsc NetVSC device + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_rx_established_buffer ( struct netvsc_device *netvsc, + const void *data, size_t len ) { + const struct netvsc_rx_establish_buffer_completion *cmplt = data; + + /* Check completion */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( netvsc, "NETVSC %s underlength buffer completion (%zd " + "bytes)\n", netvsc->name, len ); + return -EINVAL; + } + if ( cmplt->header.type != cpu_to_le32 ( NETVSC_RX_ESTABLISH_CMPLT ) ) { + DBGC ( netvsc, "NETVSC %s unexpected buffer completion type " + "%d\n", netvsc->name, le32_to_cpu ( cmplt->header.type)); + return -EPROTO; + } + if ( cmplt->status != cpu_to_le32 ( NETVSC_OK ) ) { + DBGC ( netvsc, "NETVSC %s buffer failure status %d\n", + netvsc->name, le32_to_cpu ( cmplt->status ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Revoke data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_revoke_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct netvsc_revoke_buffer_message msg; + int rc; + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( buffer->revoke_type ); + msg.pageset = buffer->pages.pageset; /* Already protocol-endian */ + + /* Send message and wait for completion */ + if ( ( rc = netvsc_control ( netvsc, buffer->revoke_xrid, + &msg, sizeof ( msg ) ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not revoke buffer: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Handle received control packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_recv_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + + DBGC ( netvsc, "NETVSC %s received unsupported control packet " + "(%08llx):\n", netvsc->name, xid ); + DBGC_HDA ( netvsc, 0, data, len ); + return -ENOTSUP; +} + +/** + * Handle received data packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v list List of I/O buffers + * @ret rc Return status code + */ +static int netvsc_recv_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct list_head *list ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + const struct netvsc_rndis_message *msg = data; + struct io_buffer *iobuf; + struct io_buffer *tmp; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( netvsc, "NETVSC %s received underlength RNDIS packet " + "(%zd bytes)\n", netvsc->name, len ); + rc = -EINVAL; + goto err_sanity; + } + if ( msg->header.type != cpu_to_le32 ( NETVSC_RNDIS_MSG ) ) { + DBGC ( netvsc, "NETVSC %s received unexpected RNDIS packet " + "type %d\n", netvsc->name, + le32_to_cpu ( msg->header.type ) ); + rc = -EINVAL; + goto err_sanity; + } + + /* Send completion back to host */ + if ( ( rc = vmbus_send_completion ( vmdev, xid, NULL, 0 ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not send completion: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_completion; + } + + /* Hand off to RNDIS */ + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + rndis_rx ( rndis, iob_disown ( iobuf ) ); + } + + return 0; + + err_completion: + err_sanity: + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + return rc; +} + +/** + * Handle received completion packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static int netvsc_recv_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + struct io_buffer *iobuf; + int ( * completion ) ( struct netvsc_device *netvsc, + const void *data, size_t len ); + unsigned int xrid = ( xid - NETVSC_BASE_XID ); + unsigned int tx_id; + int rc; + + /* Handle transmit completion, if applicable */ + tx_id = ( xrid - NETVSC_TX_BASE_XRID ); + if ( ( tx_id < NETVSC_TX_NUM_DESC ) && + ( ( iobuf = netvsc->tx.iobufs[tx_id] ) != NULL ) ) { + + /* Free buffer ID */ + netvsc->tx.iobufs[tx_id] = NULL; + netvsc->tx.ids[ ( netvsc->tx.id_cons++ ) & + ( netvsc->tx.count - 1 ) ] = tx_id; + + /* Hand back to RNDIS */ + rndis_tx_complete ( rndis, iobuf ); + return 0; + } + + /* Otherwise determine completion handler */ + if ( xrid == NETVSC_INIT_XRID ) { + completion = netvsc_initialised; + } else if ( xrid == NETVSC_RX_ESTABLISH_XRID ) { + completion = netvsc_rx_established_buffer; + } else if ( ( netvsc->wait_xrid != 0 ) && + ( xrid == netvsc->wait_xrid ) ) { + completion = netvsc_completed; + } else { + DBGC ( netvsc, "NETVSC %s received unexpected completion " + "(%08llx)\n", netvsc->name, xid ); + return -EPIPE; + } + + /* Hand off to completion handler */ + rc = completion ( netvsc, data, len ); + + /* Record completion handler result if applicable */ + if ( xrid == netvsc->wait_xrid ) { + netvsc->wait_xrid = 0; + netvsc->wait_rc = rc; + } + + return rc; +} + +/** + * Handle received cancellation packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + */ +static int netvsc_recv_cancellation ( struct vmbus_device *vmdev, + uint64_t xid ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + struct netvsc_device *netvsc = rndis->priv; + + DBGC ( netvsc, "NETVSC %s received unsupported cancellation packet " + "(%08llx):\n", netvsc->name, xid ); + return -ENOTSUP; +} + +/** VMBus channel operations */ +static struct vmbus_channel_operations netvsc_channel_operations = { + .recv_control = netvsc_recv_control, + .recv_data = netvsc_recv_data, + .recv_completion = netvsc_recv_completion, + .recv_cancellation = netvsc_recv_cancellation, +}; + +/** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ +static void netvsc_poll ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + struct vmbus_device *vmdev = netvsc->vmdev; + + /* Poll VMBus device */ + while ( vmbus_has_data ( vmdev ) ) + vmbus_poll ( vmdev ); +} + +/** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success then the RNDIS device must + * eventually report completion via rndis_tx_complete(). + */ +static int netvsc_transmit ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct netvsc_device *netvsc = rndis->priv; + struct rndis_header *header = iobuf->data; + struct netvsc_rndis_message msg; + unsigned int tx_id; + unsigned int xrid; + uint64_t xid; + int rc; + + /* Sanity check */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + assert ( iob_len ( iobuf ) == le32_to_cpu ( header->len ) ); + + /* Check that we have space in the transmit ring */ + if ( netvsc_ring_is_full ( &netvsc->tx ) ) + return rndis_tx_defer ( rndis, iobuf ); + + /* Allocate buffer ID and calculate transaction ID */ + tx_id = netvsc->tx.ids[ netvsc->tx.id_prod & ( netvsc->tx.count - 1 ) ]; + assert ( netvsc->tx.iobufs[tx_id] == NULL ); + xrid = ( NETVSC_TX_BASE_XRID + tx_id ); + xid = ( NETVSC_BASE_XID + xrid ); + + /* Construct message */ + memset ( &msg, 0, sizeof ( msg ) ); + msg.header.type = cpu_to_le32 ( NETVSC_RNDIS_MSG ); + msg.channel = ( ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) ? + NETVSC_RNDIS_DATA : NETVSC_RNDIS_CONTROL ); + msg.buffer = cpu_to_le32 ( NETVSC_RNDIS_NO_BUFFER ); + + /* Send message */ + if ( ( rc = vmbus_send_data ( netvsc->vmdev, xid, &msg, sizeof ( msg ), + iobuf ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not send RNDIS message: %s\n", + netvsc->name, strerror ( rc ) ); + return rc; + } + + /* Store I/O buffer and consume buffer ID */ + netvsc->tx.iobufs[tx_id] = iobuf; + netvsc->tx.id_prod++; + + return 0; +} + +/** + * Cancel transmission + * + * @v netvsc NetVSC device + * @v iobuf I/O buffer + * @v tx_id Transmission ID + */ +static void netvsc_cancel_transmit ( struct netvsc_device *netvsc, + struct io_buffer *iobuf, + unsigned int tx_id ) { + unsigned int xrid; + uint64_t xid; + + /* Send cancellation */ + xrid = ( NETVSC_TX_BASE_XRID + tx_id ); + xid = ( NETVSC_BASE_XID + xrid ); + DBGC ( netvsc, "NETVSC %s cancelling transmission %#x\n", + netvsc->name, tx_id ); + vmbus_send_cancellation ( netvsc->vmdev, xid ); + + /* Report back to RNDIS */ + rndis_tx_complete_err ( netvsc->rndis, iobuf, -ECANCELED ); +} + +/** + * Create descriptor ring + * + * @v netvsc NetVSC device + * @v ring Descriptor ring + * @ret rc Return status code + */ +static int netvsc_create_ring ( struct netvsc_device *netvsc __unused, + struct netvsc_ring *ring ) { + unsigned int i; + + /* Initialise buffer ID ring */ + for ( i = 0 ; i < ring->count ; i++ ) { + ring->ids[i] = i; + assert ( ring->iobufs[i] == NULL ); + } + ring->id_prod = 0; + ring->id_cons = 0; + + return 0; +} + +/** + * Destroy descriptor ring + * + * @v netvsc NetVSC device + * @v ring Descriptor ring + * @v discard Method used to discard outstanding buffer, or NULL + */ +static void netvsc_destroy_ring ( struct netvsc_device *netvsc, + struct netvsc_ring *ring, + void ( * discard ) ( struct netvsc_device *, + struct io_buffer *, + unsigned int ) ) { + struct io_buffer *iobuf; + unsigned int i; + + /* Flush any outstanding buffers */ + for ( i = 0 ; i < ring->count ; i++ ) { + iobuf = ring->iobufs[i]; + if ( ! iobuf ) + continue; + ring->iobufs[i] = NULL; + ring->ids[ ( ring->id_cons++ ) & ( ring->count - 1 ) ] = i; + if ( discard ) + discard ( netvsc, iobuf, i ); + } + + /* Sanity check */ + assert ( netvsc_ring_is_empty ( ring ) ); +} + +/** + * Copy data from data buffer + * + * @v pages Transfer page set + * @v data Data buffer + * @v offset Offset within page set + * @v len Length within page set + * @ret rc Return status code + */ +static int netvsc_buffer_copy ( struct vmbus_xfer_pages *pages, void *data, + size_t offset, size_t len ) { + struct netvsc_buffer *buffer = + container_of ( pages, struct netvsc_buffer, pages ); + + /* Sanity check */ + if ( ( offset > buffer->len ) || ( len > ( buffer->len - offset ) ) ) + return -ERANGE; + + /* Copy data from buffer */ + copy_from_user ( data, buffer->data, offset, len ); + + return 0; +} + +/** Transfer page set operations */ +static struct vmbus_xfer_pages_operations netvsc_xfer_pages_operations = { + .copy = netvsc_buffer_copy, +}; + +/** + * Create data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + * @ret rc Return status code + */ +static int netvsc_create_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct vmbus_device *vmdev = netvsc->vmdev; + int gpadl; + int rc; + + /* Allocate receive buffer */ + buffer->data = umalloc ( buffer->len ); + if ( ! buffer->data ) { + DBGC ( netvsc, "NETVSC %s could not allocate %zd-byte buffer\n", + netvsc->name, buffer->len ); + rc = -ENOMEM; + goto err_alloc; + } + + /* Establish GPA descriptor list */ + gpadl = vmbus_establish_gpadl ( vmdev, buffer->data, buffer->len ); + if ( gpadl < 0 ) { + rc = gpadl; + DBGC ( netvsc, "NETVSC %s could not establish GPADL: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_establish_gpadl; + } + buffer->gpadl = gpadl; + + /* Register transfer page set */ + if ( ( rc = vmbus_register_pages ( vmdev, &buffer->pages ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not register transfer pages: " + "%s\n", netvsc->name, strerror ( rc ) ); + goto err_register_pages; + } + + return 0; + + vmbus_unregister_pages ( vmdev, &buffer->pages ); + err_register_pages: + vmbus_gpadl_teardown ( vmdev, gpadl ); + err_establish_gpadl: + ufree ( buffer->data ); + err_alloc: + return rc; +} + +/** + * Destroy data buffer + * + * @v netvsc NetVSC device + * @v buffer Data buffer + */ +static void netvsc_destroy_buffer ( struct netvsc_device *netvsc, + struct netvsc_buffer *buffer ) { + struct vmbus_device *vmdev = netvsc->vmdev; + int rc; + + /* Unregister transfer pages */ + vmbus_unregister_pages ( vmdev, &buffer->pages ); + + /* Tear down GPA descriptor list */ + if ( ( rc = vmbus_gpadl_teardown ( vmdev, buffer->gpadl ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not tear down GPADL: %s\n", + netvsc->name, strerror ( rc ) ); + /* Death is imminent. The host may well continue to + * write to the data buffer. The best we can do is + * leak memory for now and hope that the host doesn't + * write to this region after we load an OS. + */ + return; + } + + /* Free buffer */ + ufree ( buffer->data ); +} + +/** + * Open device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int netvsc_open ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + int rc; + + /* Initialise receive buffer */ + if ( ( rc = netvsc_create_buffer ( netvsc, &netvsc->rx ) ) != 0 ) + goto err_create_rx; + + /* Open channel */ + if ( ( rc = vmbus_open ( netvsc->vmdev, &netvsc_channel_operations, + PAGE_SIZE, PAGE_SIZE, NETVSC_MTU ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not open VMBus: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_vmbus_open; + } + + /* Initialise communication with NetVSP */ + if ( ( rc = netvsc_initialise ( netvsc ) ) != 0 ) + goto err_initialise; + if ( ( rc = netvsc_ndis_version ( netvsc ) ) != 0 ) + goto err_ndis_version; + + /* Initialise transmit ring */ + if ( ( rc = netvsc_create_ring ( netvsc, &netvsc->tx ) ) != 0 ) + goto err_create_tx; + + /* Establish receive buffer */ + if ( ( rc = netvsc_establish_buffer ( netvsc, &netvsc->rx ) ) != 0 ) + goto err_establish_rx; + + return 0; + + netvsc_revoke_buffer ( netvsc, &netvsc->rx ); + err_establish_rx: + netvsc_destroy_ring ( netvsc, &netvsc->tx, NULL ); + err_create_tx: + err_ndis_version: + err_initialise: + vmbus_close ( netvsc->vmdev ); + err_vmbus_open: + netvsc_destroy_buffer ( netvsc, &netvsc->rx ); + err_create_rx: + return rc; +} + +/** + * Close device + * + * @v rndis RNDIS device + */ +static void netvsc_close ( struct rndis_device *rndis ) { + struct netvsc_device *netvsc = rndis->priv; + + /* Revoke receive buffer */ + netvsc_revoke_buffer ( netvsc, &netvsc->rx ); + + /* Destroy transmit ring */ + netvsc_destroy_ring ( netvsc, &netvsc->tx, netvsc_cancel_transmit ); + + /* Close channel */ + vmbus_close ( netvsc->vmdev ); + + /* Destroy receive buffer */ + netvsc_destroy_buffer ( netvsc, &netvsc->rx ); +} + +/** RNDIS operations */ +static struct rndis_operations netvsc_operations = { + .open = netvsc_open, + .close = netvsc_close, + .transmit = netvsc_transmit, + .poll = netvsc_poll, +}; + +/** + * Probe device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +static int netvsc_probe ( struct vmbus_device *vmdev ) { + struct netvsc_device *netvsc; + struct rndis_device *rndis; + int rc; + + /* Allocate and initialise structure */ + rndis = alloc_rndis ( sizeof ( *netvsc ) ); + if ( ! rndis ) { + rc = -ENOMEM; + goto err_alloc; + } + rndis_init ( rndis, &netvsc_operations ); + rndis->netdev->dev = &vmdev->dev; + netvsc = rndis->priv; + netvsc->vmdev = vmdev; + netvsc->rndis = rndis; + netvsc->name = vmdev->dev.name; + netvsc_init_ring ( &netvsc->tx, NETVSC_TX_NUM_DESC, + netvsc->tx_iobufs, netvsc->tx_ids ); + netvsc_init_buffer ( &netvsc->rx, NETVSC_RX_BUF_PAGESET, + &netvsc_xfer_pages_operations, + NETVSC_RX_ESTABLISH_MSG, NETVSC_RX_ESTABLISH_XRID, + NETVSC_RX_REVOKE_MSG, NETVSC_RX_REVOKE_XRID, + NETVSC_RX_BUF_LEN ); + vmbus_set_drvdata ( vmdev, rndis ); + + /* Register RNDIS device */ + if ( ( rc = register_rndis ( rndis ) ) != 0 ) { + DBGC ( netvsc, "NETVSC %s could not register: %s\n", + netvsc->name, strerror ( rc ) ); + goto err_register; + } + + return 0; + + unregister_rndis ( rndis ); + err_register: + free_rndis ( rndis ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v vmdev VMBus device + */ +static void netvsc_remove ( struct vmbus_device *vmdev ) { + struct rndis_device *rndis = vmbus_get_drvdata ( vmdev ); + + /* Unregister RNDIS device */ + unregister_rndis ( rndis ); + + /* Free RNDIS device */ + free_rndis ( rndis ); +} + +/** NetVSC driver */ +struct vmbus_driver netvsc_driver __vmbus_driver = { + .name = "netvsc", + .type = VMBUS_TYPE ( 0xf8615163, 0xdf3e, 0x46c5, 0x913f, + 0xf2, 0xd2, 0xf9, 0x65, 0xed, 0x0e ), + .probe = netvsc_probe, + .remove = netvsc_remove, +}; diff --git a/roms/ipxe/src/drivers/net/netvsc.h b/roms/ipxe/src/drivers/net/netvsc.h new file mode 100644 index 000000000..39eeb891c --- /dev/null +++ b/roms/ipxe/src/drivers/net/netvsc.h @@ -0,0 +1,365 @@ +#ifndef _NETVSC_H +#define _NETVSC_H + +/** @file + * + * Hyper-V network virtual service client + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** Maximum supported NetVSC message length */ +#define NETVSC_MTU 512 + +/** Maximum time to wait for a transaction to complete + * + * This is a policy decision. + */ +#define NETVSC_MAX_WAIT_MS 1000 + +/** Number of transmit ring entries + * + * Must be a power of two. This is a policy decision. This value + * must be sufficiently small to guarantee that we never run out of + * space in the VMBus outbound ring buffer. + */ +#define NETVSC_TX_NUM_DESC 32 + +/** RX data buffer page set ID + * + * This is a policy decision. + */ +#define NETVSC_RX_BUF_PAGESET 0xbead + +/** RX data buffer length + * + * This is a policy decision. + */ +#define NETVSC_RX_BUF_LEN ( 16 * PAGE_SIZE ) + +/** Base transaction ID + * + * This is a policy decision. + */ +#define NETVSC_BASE_XID 0x18ae0000UL + +/** Relative transaction IDs */ +enum netvsc_xrid { + /** Transmit descriptors (one per transmit buffer ID) */ + NETVSC_TX_BASE_XRID = 0, + /** Initialisation */ + NETVSC_INIT_XRID = ( NETVSC_TX_BASE_XRID + NETVSC_TX_NUM_DESC ), + /** NDIS version */ + NETVSC_NDIS_VERSION_XRID, + /** Establish receive buffer */ + NETVSC_RX_ESTABLISH_XRID, + /** Revoke receive buffer */ + NETVSC_RX_REVOKE_XRID, +}; + +/** NetVSC status codes */ +enum netvsc_status { + NETVSC_NONE = 0, + NETVSC_OK = 1, + NETVSC_FAIL = 2, + NETVSC_TOO_NEW = 3, + NETVSC_TOO_OLD = 4, + NETVSC_BAD_PACKET = 5, + NETVSC_BUSY = 6, + NETVSC_UNSUPPORTED = 7, +}; + +/** NetVSC message header */ +struct netvsc_header { + /** Type */ + uint32_t type; +} __attribute__ (( packed )); + +/** NetVSC initialisation message */ +#define NETVSC_INIT_MSG 1 + +/** NetVSC initialisation message */ +struct netvsc_init_message { + /** Message header */ + struct netvsc_header header; + /** Minimum supported protocol version */ + uint32_t min; + /** Maximum supported protocol version */ + uint32_t max; + /** Reserved */ + uint8_t reserved[20]; +} __attribute__ (( packed )); + +/** Oldest known NetVSC protocol version */ +#define NETVSC_VERSION_1 2 /* sic */ + +/** NetVSC initialisation completion */ +#define NETVSC_INIT_CMPLT 2 + +/** NetVSC initialisation completion */ +struct netvsc_init_completion { + /** Message header */ + struct netvsc_header header; + /** Protocol version */ + uint32_t version; + /** Maximum memory descriptor list length */ + uint32_t max_mdl_len; + /** Status */ + uint32_t status; + /** Reserved */ + uint8_t reserved[16]; +} __attribute__ (( packed )); + +/** NetVSC NDIS version message */ +#define NETVSC_NDIS_VERSION_MSG 100 + +/** NetVSC NDIS version message */ +struct netvsc_ndis_version_message { + /** Message header */ + struct netvsc_header header; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Reserved */ + uint8_t reserved[20]; +} __attribute__ (( packed )); + +/** NetVSC NDIS major version */ +#define NETVSC_NDIS_MAJOR 6 + +/** NetVSC NDIS minor version */ +#define NETVSC_NDIS_MINOR 1 + +/** NetVSC establish receive data buffer message */ +#define NETVSC_RX_ESTABLISH_MSG 101 + +/** NetVSC establish receive data buffer completion */ +#define NETVSC_RX_ESTABLISH_CMPLT 102 + +/** NetVSC revoke receive data buffer message */ +#define NETVSC_RX_REVOKE_MSG 103 + +/** NetVSC establish transmit data buffer message */ +#define NETVSC_TX_ESTABLISH_MSG 104 + +/** NetVSC establish transmit data buffer completion */ +#define NETVSC_TX_ESTABLISH_CMPLT 105 + +/** NetVSC revoke transmit data buffer message */ +#define NETVSC_TX_REVOKE_MSG 106 + +/** NetVSC establish data buffer message */ +struct netvsc_establish_buffer_message { + /** Message header */ + struct netvsc_header header; + /** GPADL ID */ + uint32_t gpadl; + /** Page set ID */ + uint16_t pageset; + /** Reserved */ + uint8_t reserved[22]; +} __attribute__ (( packed )); + +/** NetVSC receive data buffer section */ +struct netvsc_rx_buffer_section { + /** Starting offset */ + uint32_t start; + /** Subsection length */ + uint32_t len; + /** Number of subsections */ + uint32_t count; + /** Ending offset */ + uint32_t end; +} __attribute__ (( packed )); + +/** NetVSC establish receive data buffer completion */ +struct netvsc_rx_establish_buffer_completion { + /** Message header */ + struct netvsc_header header; + /** Status */ + uint32_t status; + /** Number of sections (must be 1) */ + uint32_t count; + /** Section descriptors */ + struct netvsc_rx_buffer_section section[1]; +} __attribute__ (( packed )); + +/** NetVSC establish transmit data buffer completion */ +struct netvsc_tx_establish_buffer_completion { + /** Message header */ + struct netvsc_header header; + /** Status */ + uint32_t status; + /** Section length */ + uint32_t len; +} __attribute__ (( packed )); + +/** NetVSC revoke data buffer message */ +struct netvsc_revoke_buffer_message { + /** Message header */ + struct netvsc_header header; + /** Page set ID */ + uint16_t pageset; + /** Reserved */ + uint8_t reserved[26]; +} __attribute__ (( packed )); + +/** NetVSC RNDIS message */ +#define NETVSC_RNDIS_MSG 107 + +/** NetVSC RNDIS message */ +struct netvsc_rndis_message { + /** Message header */ + struct netvsc_header header; + /** RNDIS channel */ + uint32_t channel; + /** Buffer index (or NETVSC_RNDIS_NO_BUFFER) */ + uint32_t buffer; + /** Buffer length */ + uint32_t len; + /** Reserved */ + uint8_t reserved[16]; +} __attribute__ (( packed )); + +/** RNDIS data channel (for RNDIS_PACKET_MSG only) */ +#define NETVSC_RNDIS_DATA 0 + +/** RNDIS control channel (for all other RNDIS messages) */ +#define NETVSC_RNDIS_CONTROL 1 + +/** "No buffer used" index */ +#define NETVSC_RNDIS_NO_BUFFER 0xffffffffUL + +/** A NetVSC descriptor ring */ +struct netvsc_ring { + /** Number of descriptors */ + unsigned int count; + /** I/O buffers, indexed by buffer ID */ + struct io_buffer **iobufs; + /** Buffer ID ring */ + uint8_t *ids; + /** Buffer ID producer counter */ + unsigned int id_prod; + /** Buffer ID consumer counter */ + unsigned int id_cons; +}; + +/** + * Initialise descriptor ring + * + * @v ring Descriptor ring + * @v count Maximum number of used descriptors + * @v iobufs I/O buffers + * @v ids Buffer IDs + */ +static inline __attribute__ (( always_inline )) void +netvsc_init_ring ( struct netvsc_ring *ring, unsigned int count, + struct io_buffer **iobufs, uint8_t *ids ) { + + ring->count = count; + ring->iobufs = iobufs; + ring->ids = ids; +} + +/** + * Check whether or not descriptor ring is full + * + * @v ring Descriptor ring + * @v is_full Ring is full + */ +static inline __attribute__ (( always_inline )) int +netvsc_ring_is_full ( struct netvsc_ring *ring ) { + unsigned int fill_level; + + fill_level = ( ring->id_prod - ring->id_cons ); + assert ( fill_level <= ring->count ); + return ( fill_level >= ring->count ); +} + +/** + * Check whether or not descriptor ring is empty + * + * @v ring Descriptor ring + * @v is_empty Ring is empty + */ +static inline __attribute__ (( always_inline )) int +netvsc_ring_is_empty ( struct netvsc_ring *ring ) { + + return ( ring->id_prod == ring->id_cons ); +} + +/** A NetVSC data buffer */ +struct netvsc_buffer { + /** Transfer page set */ + struct vmbus_xfer_pages pages; + /** Establish data buffer message type */ + uint8_t establish_type; + /** Establish data buffer relative transaction ID */ + uint8_t establish_xrid; + /** Revoke data buffer message type */ + uint8_t revoke_type; + /** Revoke data buffer relative transaction ID */ + uint8_t revoke_xrid; + /** Buffer length */ + size_t len; + /** Buffer */ + userptr_t data; + /** GPADL ID */ + unsigned int gpadl; +}; + +/** + * Initialise data buffer + * + * @v buffer Data buffer + * @v pageset Page set ID + * @v op Page set operations + * @v establish_type Establish data buffer message type + * @v establish_xrid Establish data buffer relative transaction ID + * @v revoke_type Revoke data buffer message type + * @v revoke_type Revoke data buffer relative transaction ID + * @v len Required length + */ +static inline __attribute__ (( always_inline )) void +netvsc_init_buffer ( struct netvsc_buffer *buffer, uint16_t pageset, + struct vmbus_xfer_pages_operations *op, + uint8_t establish_type, uint8_t establish_xrid, + uint8_t revoke_type, uint8_t revoke_xrid, size_t len ) { + + buffer->pages.pageset = cpu_to_le16 ( pageset ); + buffer->pages.op = op; + buffer->establish_type = establish_type; + buffer->establish_xrid = establish_xrid; + buffer->revoke_type = revoke_type; + buffer->revoke_xrid = revoke_xrid; + buffer->len = len; +} + +/** A NetVSC device */ +struct netvsc_device { + /** VMBus device */ + struct vmbus_device *vmdev; + /** RNDIS device */ + struct rndis_device *rndis; + /** Name */ + const char *name; + + /** Transmit ring */ + struct netvsc_ring tx; + /** Transmit buffer IDs */ + uint8_t tx_ids[NETVSC_TX_NUM_DESC]; + /** Transmit I/O buffers */ + struct io_buffer *tx_iobufs[NETVSC_TX_NUM_DESC]; + + /** Receive buffer */ + struct netvsc_buffer rx; + + /** Relative transaction ID for current blocking transaction */ + unsigned int wait_xrid; + /** Return status code for current blocking transaction */ + int wait_rc; +}; + +#endif /* _NETVSC_H */ diff --git a/roms/ipxe/src/drivers/net/phantom/nx_bitops.h b/roms/ipxe/src/drivers/net/phantom/nx_bitops.h index 15f3d3767..1687b6952 100644 --- a/roms/ipxe/src/drivers/net/phantom/nx_bitops.h +++ b/roms/ipxe/src/drivers/net/phantom/nx_bitops.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/net/phantom/phantom.c b/roms/ipxe/src/drivers/net/phantom/phantom.c index e70ded08c..38b66743c 100644 --- a/roms/ipxe/src/drivers/net/phantom/phantom.c +++ b/roms/ipxe/src/drivers/net/phantom/phantom.c @@ -16,9 +16,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/drivers/net/phantom/phantom.h b/roms/ipxe/src/drivers/net/phantom/phantom.h index 1647168ba..967603409 100644 --- a/roms/ipxe/src/drivers/net/phantom/phantom.h +++ b/roms/ipxe/src/drivers/net/phantom/phantom.h @@ -19,9 +19,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/net/phantom/phantom_hw.h b/roms/ipxe/src/drivers/net/phantom/phantom_hw.h index 7dfff52b2..016730de3 100644 --- a/roms/ipxe/src/drivers/net/phantom/phantom_hw.h +++ b/roms/ipxe/src/drivers/net/phantom/phantom_hw.h @@ -19,9 +19,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/net/pnic.c b/roms/ipxe/src/drivers/net/pnic.c index 4170cc640..ca64299ea 100644 --- a/roms/ipxe/src/drivers/net/pnic.c +++ b/roms/ipxe/src/drivers/net/pnic.c @@ -6,8 +6,18 @@ Bochs Pseudo NIC driver for Etherboot /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. * * See pnic_api.h for an explanation of the Bochs Pseudo NIC. */ diff --git a/roms/ipxe/src/drivers/net/prism2.c b/roms/ipxe/src/drivers/net/prism2.c index ab974264c..4331f2cd0 100644 --- a/roms/ipxe/src/drivers/net/prism2.c +++ b/roms/ipxe/src/drivers/net/prism2.c @@ -9,8 +9,18 @@ $Id$ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); diff --git a/roms/ipxe/src/drivers/net/prism2_pci.c b/roms/ipxe/src/drivers/net/prism2_pci.c index 72549babf..69ddf0fb0 100644 --- a/roms/ipxe/src/drivers/net/prism2_pci.c +++ b/roms/ipxe/src/drivers/net/prism2_pci.c @@ -10,8 +10,18 @@ $Id$ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -40,8 +50,6 @@ static void prism2_pci_disable ( struct nic *nic ) { static struct pci_device_id prism2_pci_nics[] = { PCI_ROM(0x1260, 0x3873, "prism2_pci", "Harris Semiconductor Prism2.5 clone", 0), -PCI_ROM(0x1260, 0x3873, "hwp01170", "ActionTec HWP01170", 0), -PCI_ROM(0x1260, 0x3873, "dwl520", "DLink DWL-520", 0), }; PCI_DRIVER ( prism2_pci_driver, prism2_pci_nics, PCI_NO_CLASS ); diff --git a/roms/ipxe/src/drivers/net/prism2_plx.c b/roms/ipxe/src/drivers/net/prism2_plx.c index 2098f7f09..a73b0e087 100644 --- a/roms/ipxe/src/drivers/net/prism2_plx.c +++ b/roms/ipxe/src/drivers/net/prism2_plx.c @@ -10,8 +10,18 @@ $Id$ /* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -44,10 +54,10 @@ static int prism2_find_plx ( hfa384x_t *hw, struct pci_device *p ) /* Obtain all memory and IO base addresses */ pci_read_config_dword( p, PLX_LOCAL_CONFIG_REGISTER_BASE, &plx_lcr); - plx_lcr &= PCI_BASE_ADDRESS_IO_MASK; + plx_lcr &= ~PCI_BASE_ADDRESS_IO_MASK; pci_read_config_dword( p, PRISM2_PLX_ATTR_MEM_BASE, &attr_mem); pci_read_config_dword( p, PRISM2_PLX_IO_BASE, &iobase); - iobase &= PCI_BASE_ADDRESS_IO_MASK; + iobase &= ~PCI_BASE_ADDRESS_IO_MASK; /* Fill out hw structure */ hw->iobase = iobase; diff --git a/roms/ipxe/src/drivers/net/realtek.c b/roms/ipxe/src/drivers/net/realtek.c index 0aca8c77f..022b59324 100644 --- a/roms/ipxe/src/drivers/net/realtek.c +++ b/roms/ipxe/src/drivers/net/realtek.c @@ -17,9 +17,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -194,7 +198,6 @@ static int realtek_init_eeprom ( struct net_device *netdev ) { DBGC ( rtl, "REALTEK %p EEPROM is a 93C46\n", rtl ); init_at93c46 ( &rtl->eeprom, 16 ); } - rtl->eeprom.bus = &rtl->spibit.bus; /* Check for EEPROM presence. Some onboard NICs will have no * EEPROM connected, with the BIOS being responsible for @@ -1085,6 +1088,7 @@ static void realtek_detect ( struct realtek_nic *rtl ) { rtl ); rtl->legacy = 1; } + rtl->eeprom.bus = &rtl->spibit.bus; } } @@ -1132,7 +1136,8 @@ static int realtek_probe ( struct pci_device *pci ) { realtek_detect ( rtl ); /* Initialise EEPROM */ - if ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) { + if ( rtl->eeprom.bus && + ( ( rc = realtek_init_eeprom ( netdev ) ) == 0 ) ) { /* Read MAC address from EEPROM */ if ( ( rc = nvs_read ( &rtl->eeprom.nvs, RTL_EEPROM_MAC, diff --git a/roms/ipxe/src/drivers/net/realtek.h b/roms/ipxe/src/drivers/net/realtek.h index ac33405e8..b1ce7f98f 100644 --- a/roms/ipxe/src/drivers/net/realtek.h +++ b/roms/ipxe/src/drivers/net/realtek.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/spi.h> #include <ipxe/spi_bit.h> diff --git a/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c b/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c index 8851d1bfb..5f97480fa 100644 --- a/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c +++ b/roms/ipxe/src/drivers/net/rtl818x/rtl8180.c @@ -3,15 +3,23 @@ FILE_LICENCE(GPL2_OR_LATER); #include <ipxe/pci.h> +#include "rtl818x.h" -REQUIRE_OBJECT(rtl818x); -REQUIRE_OBJECT(rtl8180_grf5101); -REQUIRE_OBJECT(rtl8180_max2820); -REQUIRE_OBJECT(rtl8180_sa2400); - -static struct pci_device_id rtl8180_nics[] __unused = { +static struct pci_device_id rtl8180_nics[] = { PCI_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0), PCI_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0), PCI_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0), PCI_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0), }; + +struct pci_driver rtl8180_driver __pci_driver = { + .ids = rtl8180_nics, + .id_count = sizeof(rtl8180_nics) / sizeof(rtl8180_nics[0]), + .probe = rtl818x_probe, + .remove = rtl818x_remove, +}; + +REQUIRING_SYMBOL(rtl8180_driver); +REQUIRE_OBJECT(rtl8180_grf5101); +REQUIRE_OBJECT(rtl8180_max2820); +REQUIRE_OBJECT(rtl8180_sa2400); diff --git a/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c b/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c index fd27e5c8c..234978cea 100644 --- a/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c +++ b/roms/ipxe/src/drivers/net/rtl818x/rtl8185.c @@ -3,12 +3,20 @@ FILE_LICENCE(GPL2_OR_LATER); #include <ipxe/pci.h> - -REQUIRE_OBJECT(rtl818x); -REQUIRE_OBJECT(rtl8185_rtl8225); +#include "rtl818x.h" static struct pci_device_id rtl8185_nics[] __unused = { PCI_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0), PCI_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0), PCI_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0), }; + +struct pci_driver rtl8185_driver __pci_driver = { + .ids = rtl8185_nics, + .id_count = sizeof(rtl8185_nics) / sizeof(rtl8185_nics[0]), + .probe = rtl818x_probe, + .remove = rtl818x_remove, +}; + +REQUIRING_SYMBOL(rtl8185_driver); +REQUIRE_OBJECT(rtl8185_rtl8225); diff --git a/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c b/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c index cf4c7556f..8b3c206d4 100644 --- a/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c +++ b/roms/ipxe/src/drivers/net/rtl818x/rtl818x.c @@ -649,7 +649,7 @@ struct net80211_device_operations rtl818x_operations = { .config = rtl818x_config, }; -static int rtl818x_probe(struct pci_device *pdev ) +int rtl818x_probe(struct pci_device *pdev ) { struct net80211_device *dev; struct rtl818x_priv *priv; @@ -820,7 +820,7 @@ static int rtl818x_probe(struct pci_device *pdev ) return err; } -static void rtl818x_remove(struct pci_device *pdev) +void rtl818x_remove(struct pci_device *pdev) { struct net80211_device *dev = pci_get_drvdata(pdev); @@ -830,25 +830,3 @@ static void rtl818x_remove(struct pci_device *pdev) net80211_unregister(dev); net80211_free(dev); } - -/* Hide PCI_ROM definitions in here from parserom.pl; the definitions - that should be used are in rtl8180.c and rtl8185.c. */ -#define RTL_ROM PCI_ROM - -static struct pci_device_id rtl818x_nics[] = { - RTL_ROM(0x10ec, 0x8185, "rtl8185", "Realtek 8185", 0), - RTL_ROM(0x1799, 0x700f, "f5d7000", "Belkin F5D7000", 0), - RTL_ROM(0x1799, 0x701f, "f5d7010", "Belkin F5D7010", 0), - - RTL_ROM(0x10ec, 0x8180, "rtl8180", "Realtek 8180", 0), - RTL_ROM(0x1799, 0x6001, "f5d6001", "Belkin F5D6001", 0), - RTL_ROM(0x1799, 0x6020, "f5d6020", "Belkin F5D6020", 0), - RTL_ROM(0x1186, 0x3300, "dwl510", "D-Link DWL-510", 0), -}; - -struct pci_driver rtl818x_driver __pci_driver = { - .ids = rtl818x_nics, - .id_count = sizeof(rtl818x_nics) / sizeof(rtl818x_nics[0]), - .probe = rtl818x_probe, - .remove = rtl818x_remove, -}; diff --git a/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h b/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h index 4e57d0bd3..ae4b8a96f 100644 --- a/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h +++ b/roms/ipxe/src/drivers/net/rtl818x/rtl818x.h @@ -19,6 +19,7 @@ #include <ipxe/spi_bit.h> #include <ipxe/tables.h> +#include <ipxe/net80211.h> FILE_LICENCE(GPL2_ONLY); @@ -356,4 +357,7 @@ struct rtl818x_rf_ops { void (*conf_erp)(struct net80211_device *dev); /* set based on dev->erp_flags */ }; +extern int rtl818x_probe(struct pci_device *pdev ); +extern void rtl818x_remove(struct pci_device *pdev); + #endif /* RTL818X_H */ diff --git a/roms/ipxe/src/drivers/net/skeleton.c b/roms/ipxe/src/drivers/net/skeleton.c index 365111b8d..0435b9d0e 100644 --- a/roms/ipxe/src/drivers/net/skeleton.c +++ b/roms/ipxe/src/drivers/net/skeleton.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/drivers/net/skeleton.h b/roms/ipxe/src/drivers/net/skeleton.h index 3de2afa5b..2ab01bd56 100644 --- a/roms/ipxe/src/drivers/net/skeleton.h +++ b/roms/ipxe/src/drivers/net/skeleton.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Skeleton BAR size */ #define SKELETON_BAR_SIZE 256 diff --git a/roms/ipxe/src/drivers/net/smsc75xx.c b/roms/ipxe/src/drivers/net/smsc75xx.c new file mode 100644 index 000000000..017e02a59 --- /dev/null +++ b/roms/ipxe/src/drivers/net/smsc75xx.c @@ -0,0 +1,1057 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/ethernet.h> +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include <ipxe/profile.h> +#include "smsc75xx.h" + +/** @file + * + * SMSC LAN75xx USB Ethernet driver + * + */ + +/** Interrupt completion profiler */ +static struct profiler smsc75xx_intr_profiler __profiler = + { .name = "smsc75xx.intr" }; + +/** Bulk IN completion profiler */ +static struct profiler smsc75xx_in_profiler __profiler = + { .name = "smsc75xx.in" }; + +/** Bulk OUT profiler */ +static struct profiler smsc75xx_out_profiler __profiler = + { .name = "smsc75xx.out" }; + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Write register (without byte-swapping) + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static int smsc75xx_raw_writel ( struct smsc75xx_device *smsc75xx, + unsigned int address, uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_WRITE, 0, + address, &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not write %03x: %s\n", + smsc75xx, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Write register + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @v value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc75xx_writel ( struct smsc75xx_device *smsc75xx, unsigned int address, + uint32_t value ) { + int rc; + + /* Write register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, address, + cpu_to_le32 ( value ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Read register (without byte-swapping) + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static int smsc75xx_raw_readl ( struct smsc75xx_device *smsc75xx, + unsigned int address, uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_REGISTER_READ, 0, + address, value, sizeof ( *value ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not read %03x: %s\n", + smsc75xx, address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Read register + * + * @v smsc75xx SMSC75xx device + * @v address Register address + * @ret value Register value + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +smsc75xx_readl ( struct smsc75xx_device *smsc75xx, unsigned int address, + uint32_t *value ) { + int rc; + + /* Read register */ + if ( ( rc = smsc75xx_raw_readl ( smsc75xx, address, value ) ) != 0 ) + return rc; + le32_to_cpus ( value ); + + return 0; +} + +/****************************************************************************** + * + * EEPROM access + * + ****************************************************************************** + */ + +/** + * Wait for EEPROM to become idle + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_eeprom_wait ( struct smsc75xx_device *smsc75xx ) { + uint32_t e2p_cmd; + unsigned int i; + int rc; + + /* Wait for EPC_BSY to become clear */ + for ( i = 0 ; i < SMSC75XX_EEPROM_MAX_WAIT_MS ; i++ ) { + + /* Read E2P_CMD and check EPC_BSY */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_CMD, + &e2p_cmd ) ) != 0 ) + return rc; + if ( ! ( e2p_cmd & SMSC75XX_E2P_CMD_EPC_BSY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for EEPROM\n", + smsc75xx ); + return -ETIMEDOUT; +} + +/** + * Read byte from EEPROM + * + * @v smsc75xx SMSC75xx device + * @v address EEPROM address + * @ret byte Byte read, or negative error + */ +static int smsc75xx_eeprom_read_byte ( struct smsc75xx_device *smsc75xx, + unsigned int address ) { + uint32_t e2p_cmd; + uint32_t e2p_data; + int rc; + + /* Wait for EEPROM to become idle */ + if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + e2p_cmd = ( SMSC75XX_E2P_CMD_EPC_BSY | SMSC75XX_E2P_CMD_EPC_CMD_READ | + SMSC75XX_E2P_CMD_EPC_ADDR ( address ) ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_E2P_CMD, + e2p_cmd ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_eeprom_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Read EEPROM data */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_E2P_DATA, + &e2p_data ) ) != 0 ) + return rc; + + return SMSC75XX_E2P_DATA_GET ( e2p_data ); +} + +/** + * Read data from EEPROM + * + * @v smsc75xx SMSC75xx device + * @v address EEPROM address + * @v data Data buffer + * @v len Length of data + * @ret rc Return status code + */ +static int smsc75xx_eeprom_read ( struct smsc75xx_device *smsc75xx, + unsigned int address, void *data, + size_t len ) { + uint8_t *bytes; + int byte; + + /* Read bytes */ + for ( bytes = data ; len-- ; address++, bytes++ ) { + byte = smsc75xx_eeprom_read_byte ( smsc75xx, address ); + if ( byte < 0 ) + return byte; + *bytes = byte; + } + + return 0; +} + +/****************************************************************************** + * + * MII access + * + ****************************************************************************** + */ + +/** + * Wait for MII to become idle + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_mii_wait ( struct smsc75xx_device *smsc75xx ) { + uint32_t mii_access; + unsigned int i; + int rc; + + /* Wait for MIIBZY to become clear */ + for ( i = 0 ; i < SMSC75XX_MII_MAX_WAIT_MS ; i++ ) { + + /* Read MII_ACCESS and check MIIBZY */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_ACCESS, + &mii_access ) ) != 0 ) + return rc; + if ( ! ( mii_access & SMSC75XX_MII_ACCESS_MIIBZY ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( smsc75xx, "SMSC75XX %p timed out waiting for MII\n", + smsc75xx ); + return -ETIMEDOUT; +} + +/** + * Read from MII register + * + * @v mii MII interface + * @v reg Register address + * @ret value Data read, or negative error + */ +static int smsc75xx_mii_read ( struct mii_interface *mii, unsigned int reg ) { + struct smsc75xx_device *smsc75xx = + container_of ( mii, struct smsc75xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Initiate read command */ + mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | + SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC75XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Read MII data */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_MII_DATA, + &mii_data ) ) != 0 ) + return rc; + + return SMSC75XX_MII_DATA_GET ( mii_data ); +} + +/** + * Write to MII register + * + * @v mii MII interface + * @v reg Register address + * @v data Data to write + * @ret rc Return status code + */ +static int smsc75xx_mii_write ( struct mii_interface *mii, unsigned int reg, + unsigned int data ) { + struct smsc75xx_device *smsc75xx = + container_of ( mii, struct smsc75xx_device, mii ); + uint32_t mii_access; + uint32_t mii_data; + int rc; + + /* Wait for MII to become idle */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + /* Write MII data */ + mii_data = SMSC75XX_MII_DATA_SET ( data ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_DATA, + mii_data ) ) != 0 ) + return rc; + + /* Initiate write command */ + mii_access = ( SMSC75XX_MII_ACCESS_PHY_ADDRESS | + SMSC75XX_MII_ACCESS_MIIRINDA ( reg ) | + SMSC75XX_MII_ACCESS_MIIWNR | + SMSC75XX_MII_ACCESS_MIIBZY ); + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MII_ACCESS, + mii_access ) ) != 0 ) + return rc; + + /* Wait for command to complete */ + if ( ( rc = smsc75xx_mii_wait ( smsc75xx ) ) != 0 ) + return rc; + + return 0; +} + +/** MII operations */ +static struct mii_operations smsc75xx_mii_operations = { + .read = smsc75xx_mii_read, + .write = smsc75xx_mii_write, +}; + +/** + * Check link status + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_check_link ( struct smsc75xx_device *smsc75xx ) { + struct net_device *netdev = smsc75xx->netdev; + int intr; + int rc; + + /* Read PHY interrupt source */ + intr = mii_read ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE ); + if ( intr < 0 ) { + rc = intr; + DBGC ( smsc75xx, "SMSC75XX %p could not get PHY interrupt " + "source: %s\n", smsc75xx, strerror ( rc ) ); + return rc; + } + + /* Acknowledge PHY interrupt */ + if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_SOURCE, + intr ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not acknowledge PHY " + "interrupt: %s\n", smsc75xx, strerror ( rc ) ); + return rc; + } + + /* Check link status */ + if ( ( rc = mii_check_link ( &smsc75xx->mii, netdev ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not check link: %s\n", + smsc75xx, strerror ( rc ) ); + return rc; + } + + DBGC ( smsc75xx, "SMSC75XX %p link %s (intr %#04x)\n", + smsc75xx, ( netdev_link_ok ( netdev ) ? "up" : "down" ), intr ); + return 0; +} + +/****************************************************************************** + * + * Statistics (for debugging) + * + ****************************************************************************** + */ + +/** + * Get statistics + * + * @v smsc75xx SMSC75xx device + * @v stats Statistics to fill in + * @ret rc Return status code + */ +static int smsc75xx_get_statistics ( struct smsc75xx_device *smsc75xx, + struct smsc75xx_statistics *stats ) { + int rc; + + /* Get statistics */ + if ( ( rc = usb_control ( smsc75xx->usb, SMSC75XX_GET_STATISTICS, 0, 0, + stats, sizeof ( *stats ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not get statistics: %s\n", + smsc75xx, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Dump statistics (for debugging) + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_dump_statistics ( struct smsc75xx_device *smsc75xx ) { + struct smsc75xx_statistics stats; + int rc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return 0; + + /* Get statistics */ + if ( ( rc = smsc75xx_get_statistics ( smsc75xx, &stats ) ) != 0 ) + return rc; + + /* Dump statistics */ + DBGC ( smsc75xx, "SMSC75XX %p RXE fcs %d aln %d frg %d jab %d und %d " + "ovr %d drp %d\n", smsc75xx, le32_to_cpu ( stats.rx.err.fcs ), + le32_to_cpu ( stats.rx.err.alignment ), + le32_to_cpu ( stats.rx.err.fragment ), + le32_to_cpu ( stats.rx.err.jabber ), + le32_to_cpu ( stats.rx.err.undersize ), + le32_to_cpu ( stats.rx.err.oversize ), + le32_to_cpu ( stats.rx.err.dropped ) ); + DBGC ( smsc75xx, "SMSC75XX %p RXB ucast %d bcast %d mcast %d\n", + smsc75xx, le32_to_cpu ( stats.rx.byte.unicast ), + le32_to_cpu ( stats.rx.byte.broadcast ), + le32_to_cpu ( stats.rx.byte.multicast ) ); + DBGC ( smsc75xx, "SMSC75XX %p RXF ucast %d bcast %d mcast %d pause " + "%d\n", smsc75xx, le32_to_cpu ( stats.rx.frame.unicast ), + le32_to_cpu ( stats.rx.frame.broadcast ), + le32_to_cpu ( stats.rx.frame.multicast ), + le32_to_cpu ( stats.rx.frame.pause ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXE fcs %d def %d car %d cnt %d sgl %d " + "mul %d exc %d lat %d\n", smsc75xx, + le32_to_cpu ( stats.tx.err.fcs ), + le32_to_cpu ( stats.tx.err.deferral ), + le32_to_cpu ( stats.tx.err.carrier ), + le32_to_cpu ( stats.tx.err.count ), + le32_to_cpu ( stats.tx.err.single ), + le32_to_cpu ( stats.tx.err.multiple ), + le32_to_cpu ( stats.tx.err.excessive ), + le32_to_cpu ( stats.tx.err.late ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXB ucast %d bcast %d mcast %d\n", + smsc75xx, le32_to_cpu ( stats.tx.byte.unicast ), + le32_to_cpu ( stats.tx.byte.broadcast ), + le32_to_cpu ( stats.tx.byte.multicast ) ); + DBGC ( smsc75xx, "SMSC75XX %p TXF ucast %d bcast %d mcast %d pause " + "%d\n", smsc75xx, le32_to_cpu ( stats.tx.frame.unicast ), + le32_to_cpu ( stats.tx.frame.broadcast ), + le32_to_cpu ( stats.tx.frame.multicast ), + le32_to_cpu ( stats.tx.frame.pause ) ); + + return 0; +} + +/****************************************************************************** + * + * Device reset + * + ****************************************************************************** + */ + +/** + * Reset device + * + * @v smsc75xx SMSC75xx device + * @ret rc Return status code + */ +static int smsc75xx_reset ( struct smsc75xx_device *smsc75xx ) { + uint32_t hw_cfg; + int rc; + + /* Reset device */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_LRST ) ) != 0 ) + return rc; + + /* Wait for reset to complete */ + udelay ( SMSC75XX_RESET_DELAY_US ); + + /* Check that reset has completed */ + if ( ( rc = smsc75xx_readl ( smsc75xx, SMSC75XX_HW_CFG, + &hw_cfg ) ) != 0 ) + return rc; + if ( hw_cfg & SMSC75XX_HW_CFG_LRST ) { + DBGC ( smsc75xx, "SMSC75XX %p failed to reset\n", smsc75xx ); + return -ETIMEDOUT; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_intr_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.intr ); + struct net_device *netdev = smsc75xx->netdev; + struct smsc75xx_interrupt *intr; + + /* Profile completions */ + profile_start ( &smsc75xx_intr_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p interrupt failed: %s\n", + smsc75xx, strerror ( rc ) ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + + /* Extract interrupt data */ + if ( iob_len ( iobuf ) != sizeof ( *intr ) ) { + DBGC ( smsc75xx, "SMSC75XX %p malformed interrupt\n", + smsc75xx ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + netdev_rx_err ( netdev, NULL, rc ); + goto done; + } + intr = iobuf->data; + + /* Record interrupt status */ + smsc75xx->int_sts = le32_to_cpu ( intr->int_sts ); + profile_stop ( &smsc75xx_intr_profiler ); + + done: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_intr_operations = { + .complete = smsc75xx_intr_complete, +}; + +/** + * Complete bulk IN transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_in_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.in ); + struct net_device *netdev = smsc75xx->netdev; + struct smsc75xx_rx_header *header; + + /* Profile completions */ + profile_start ( &smsc75xx_in_profiler ); + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) { + free_iob ( iobuf ); + return; + } + + /* Record USB errors against the network device */ + if ( rc != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p bulk IN failed: %s\n", + smsc75xx, strerror ( rc ) ); + goto err; + } + + /* Sanity check */ + if ( iob_len ( iobuf ) < ( sizeof ( *header ) ) ) { + DBGC ( smsc75xx, "SMSC75XX %p underlength bulk IN\n", + smsc75xx ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto err; + } + + /* Strip header */ + header = iobuf->data; + iob_pull ( iobuf, sizeof ( *header ) ); + + /* Check for errors */ + if ( header->command & cpu_to_le32 ( SMSC75XX_RX_RED ) ) { + DBGC ( smsc75xx, "SMSC75XX %p receive error (%08x):\n", + smsc75xx, le32_to_cpu ( header->command ) ); + DBGC_HDA ( smsc75xx, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EIO; + goto err; + } + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + profile_stop ( &smsc75xx_in_profiler ); + return; + + err: + /* Hand off to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** Bulk IN endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_in_operations = { + .complete = smsc75xx_in_complete, +}; + +/** + * Transmit packet + * + * @v smsc75xx SMSC75xx device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc75xx_out_transmit ( struct smsc75xx_device *smsc75xx, + struct io_buffer *iobuf ) { + struct smsc75xx_tx_header *header; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile transmissions */ + profile_start ( &smsc75xx_out_profiler ); + + /* Prepend header */ + if ( ( rc = iob_ensure_headroom ( iobuf, sizeof ( *header ) ) ) != 0 ) + return rc; + header = iob_push ( iobuf, sizeof ( *header ) ); + header->command = cpu_to_le32 ( SMSC75XX_TX_FCS | len ); + header->tag = 0; + header->mss = 0; + + /* Enqueue I/O buffer */ + if ( ( rc = usb_stream ( &smsc75xx->usbnet.out, iobuf, 0 ) ) != 0 ) + return rc; + + profile_stop ( &smsc75xx_out_profiler ); + return 0; +} + +/** + * Complete bulk OUT transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void smsc75xx_out_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct smsc75xx_device *smsc75xx = + container_of ( ep, struct smsc75xx_device, usbnet.out ); + struct net_device *netdev = smsc75xx->netdev; + + /* Report TX completion */ + netdev_tx_complete_err ( netdev, iobuf, rc ); +} + +/** Bulk OUT endpoint operations */ +static struct usb_endpoint_driver_operations smsc75xx_out_operations = { + .complete = smsc75xx_out_complete, +}; + +/****************************************************************************** + * + * Network device interface + * + ****************************************************************************** + */ + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int smsc75xx_open ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + union smsc75xx_mac mac; + int rc; + + /* Clear stored interrupt status */ + smsc75xx->int_sts = 0; + + /* Copy MAC address */ + memset ( &mac, 0, sizeof ( mac ) ); + memcpy ( mac.raw, netdev->ll_addr, ETH_ALEN ); + + /* Configure bulk IN empty response */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_HW_CFG, + SMSC75XX_HW_CFG_BIR ) ) != 0 ) + goto err_hw_cfg; + + /* Open USB network device */ + if ( ( rc = usbnet_open ( &smsc75xx->usbnet ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not open: %s\n", + smsc75xx, strerror ( rc ) ); + goto err_open; + } + + /* Configure interrupt endpoint */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_EP_CTL, + ( SMSC75XX_INT_EP_CTL_RDFO_EN | + SMSC75XX_INT_EP_CTL_PHY_EN ) ) ) != 0 ) + goto err_int_ep_ctl; + + /* Configure bulk IN delay */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_BULK_IN_DLY, + SMSC75XX_BULK_IN_DLY_SET ( 0 ) ) ) != 0 ) + goto err_bulk_in_dly; + + /* Configure receive filters */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_RFE_CTL, + ( SMSC75XX_RFE_CTL_AB | + SMSC75XX_RFE_CTL_AM | + SMSC75XX_RFE_CTL_AU ) ) ) != 0 ) + goto err_rfe_ctl; + + /* Configure receive FIFO */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_RX_CTL, + ( SMSC75XX_FCT_RX_CTL_EN | + SMSC75XX_FCT_RX_CTL_BAD ) ) ) != 0 ) + goto err_fct_rx_ctl; + + /* Configure transmit FIFO */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_FCT_TX_CTL, + SMSC75XX_FCT_TX_CTL_EN ) ) != 0 ) + goto err_fct_tx_ctl; + + /* Configure receive datapath */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_RX, + ( SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT | + SMSC75XX_MAC_RX_FCS | + SMSC75XX_MAC_RX_EN ) ) ) != 0 ) + goto err_mac_rx; + + /* Configure transmit datapath */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_MAC_TX, + SMSC75XX_MAC_TX_EN ) ) != 0 ) + goto err_mac_tx; + + /* Write MAC address high register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRH, + mac.addr.h ) ) != 0 ) + goto err_rx_addrh; + + /* Write MAC address low register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_RX_ADDRL, + mac.addr.l ) ) != 0 ) + goto err_rx_addrl; + + /* Write MAC address perfect filter high register */ + mac.addr.h |= cpu_to_le32 ( SMSC75XX_ADDR_FILTH_VALID ); + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTH ( 0 ), + mac.addr.h ) ) != 0 ) + goto err_addr_filth; + + /* Write MAC address perfect filter low register */ + if ( ( rc = smsc75xx_raw_writel ( smsc75xx, SMSC75XX_ADDR_FILTL ( 0 ), + mac.addr.l ) ) != 0 ) + goto err_addr_filtl; + + /* Enable PHY interrupts */ + if ( ( rc = mii_write ( &smsc75xx->mii, SMSC75XX_MII_PHY_INTR_MASK, + ( SMSC75XX_PHY_INTR_ANEG_DONE | + SMSC75XX_PHY_INTR_LINK_DOWN ) ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not set PHY interrupt " + "mask: %s\n", smsc75xx, strerror ( rc ) ); + goto err_phy_intr_mask; + } + + /* Update link status */ + smsc75xx_check_link ( smsc75xx ); + + return 0; + + err_phy_intr_mask: + err_addr_filtl: + err_addr_filth: + err_rx_addrl: + err_rx_addrh: + err_mac_tx: + err_mac_rx: + err_fct_tx_ctl: + err_fct_rx_ctl: + err_rfe_ctl: + err_bulk_in_dly: + err_int_ep_ctl: + usbnet_close ( &smsc75xx->usbnet ); + err_open: + err_hw_cfg: + smsc75xx_reset ( smsc75xx ); + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void smsc75xx_close ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + + /* Close USB network device */ + usbnet_close ( &smsc75xx->usbnet ); + + /* Dump statistics (for debugging) */ + smsc75xx_dump_statistics ( smsc75xx ); + + /* Reset device */ + smsc75xx_reset ( smsc75xx ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int smsc75xx_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + int rc; + + /* Transmit packet */ + if ( ( rc = smsc75xx_out_transmit ( smsc75xx, iobuf ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void smsc75xx_poll ( struct net_device *netdev ) { + struct smsc75xx_device *smsc75xx = netdev->priv; + uint32_t int_sts; + int rc; + + /* Poll USB bus */ + usb_poll ( smsc75xx->bus ); + + /* Refill endpoints */ + if ( ( rc = usbnet_refill ( &smsc75xx->usbnet ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + + /* Do nothing more unless there are interrupts to handle */ + int_sts = smsc75xx->int_sts; + if ( ! int_sts ) + return; + + /* Check link status if applicable */ + if ( int_sts & SMSC75XX_INT_STS_PHY_INT ) { + smsc75xx_check_link ( smsc75xx ); + int_sts &= ~SMSC75XX_INT_STS_PHY_INT; + } + + /* Record RX FIFO overflow if applicable */ + if ( int_sts & SMSC75XX_INT_STS_RDFO_INT ) { + DBGC2 ( smsc75xx, "SMSC75XX %p RX FIFO overflowed\n", + smsc75xx ); + netdev_rx_err ( netdev, NULL, -ENOBUFS ); + int_sts &= ~SMSC75XX_INT_STS_RDFO_INT; + } + + /* Check for unexpected interrupts */ + if ( int_sts ) { + DBGC ( smsc75xx, "SMSC75XX %p unexpected interrupt %#08x\n", + smsc75xx, int_sts ); + netdev_rx_err ( netdev, NULL, -ENOTTY ); + } + + /* Clear interrupts */ + if ( ( rc = smsc75xx_writel ( smsc75xx, SMSC75XX_INT_STS, + smsc75xx->int_sts ) ) != 0 ) + netdev_rx_err ( netdev, NULL, rc ); + smsc75xx->int_sts = 0; +} + +/** SMSC75xx network device operations */ +static struct net_device_operations smsc75xx_operations = { + .open = smsc75xx_open, + .close = smsc75xx_close, + .transmit = smsc75xx_transmit, + .poll = smsc75xx_poll, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int smsc75xx_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct net_device *netdev; + struct smsc75xx_device *smsc75xx; + int rc; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *smsc75xx ) ); + if ( ! netdev ) { + rc = -ENOMEM; + goto err_alloc; + } + netdev_init ( netdev, &smsc75xx_operations ); + netdev->dev = &func->dev; + smsc75xx = netdev->priv; + memset ( smsc75xx, 0, sizeof ( *smsc75xx ) ); + smsc75xx->usb = usb; + smsc75xx->bus = usb->port->hub->bus; + smsc75xx->netdev = netdev; + usbnet_init ( &smsc75xx->usbnet, func, &smsc75xx_intr_operations, + &smsc75xx_in_operations, &smsc75xx_out_operations ); + usb_refill_init ( &smsc75xx->usbnet.intr, 0, SMSC75XX_INTR_MAX_FILL ); + usb_refill_init ( &smsc75xx->usbnet.in, SMSC75XX_IN_MTU, + SMSC75XX_IN_MAX_FILL ); + mii_init ( &smsc75xx->mii, &smsc75xx_mii_operations ); + DBGC ( smsc75xx, "SMSC75XX %p on %s\n", smsc75xx, func->name ); + + /* Describe USB network device */ + if ( ( rc = usbnet_describe ( &smsc75xx->usbnet, config ) ) != 0 ) { + DBGC ( smsc75xx, "SMSC75XX %p could not describe: %s\n", + smsc75xx, strerror ( rc ) ); + goto err_describe; + } + + /* Reset device */ + if ( ( rc = smsc75xx_reset ( smsc75xx ) ) != 0 ) + goto err_reset; + + /* Read MAC address */ + if ( ( rc = smsc75xx_eeprom_read ( smsc75xx, SMSC75XX_EEPROM_MAC, + netdev->hw_addr, ETH_ALEN ) ) != 0 ) + goto err_eeprom_read; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) + goto err_register; + + usb_func_set_drvdata ( func, netdev ); + return 0; + + unregister_netdev ( netdev ); + err_register: + err_eeprom_read: + err_reset: + err_describe: + netdev_nullify ( netdev ); + netdev_put ( netdev ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void smsc75xx_remove ( struct usb_function *func ) { + struct net_device *netdev = usb_func_get_drvdata ( func ); + + unregister_netdev ( netdev ); + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} + +/** SMSC75xx device IDs */ +static struct usb_device_id smsc75xx_ids[] = { + { + .name = "smsc7500", + .vendor = 0x0424, + .product = 0x7500, + .class = { 0xff, 0x00, 0xff }, + }, + { + .name = "smsc7505", + .vendor = 0x0424, + .product = 0x7505, + .class = { 0xff, 0x00, 0xff }, + }, +}; + +/** SMSC LAN75xx driver */ +struct usb_driver smsc75xx_driver __usb_driver = { + .ids = smsc75xx_ids, + .id_count = ( sizeof ( smsc75xx_ids ) / sizeof ( smsc75xx_ids[0] ) ), + .probe = smsc75xx_probe, + .remove = smsc75xx_remove, +}; diff --git a/roms/ipxe/src/drivers/net/smsc75xx.h b/roms/ipxe/src/drivers/net/smsc75xx.h new file mode 100644 index 000000000..2463b72a1 --- /dev/null +++ b/roms/ipxe/src/drivers/net/smsc75xx.h @@ -0,0 +1,309 @@ +#ifndef _SMSC75XX_H +#define _SMSC75XX_H + +/** @file + * + * SMSC LAN75xx USB Ethernet driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> +#include <ipxe/if_ether.h> +#include <ipxe/mii.h> + +/** Register write command */ +#define SMSC75XX_REGISTER_WRITE \ + ( USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa0 ) ) + +/** Register read command */ +#define SMSC75XX_REGISTER_READ \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa1 ) ) + +/** Get statistics command */ +#define SMSC75XX_GET_STATISTICS \ + ( USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 0xa2 ) ) + +/** Interrupt status register */ +#define SMSC75XX_INT_STS 0x00c +#define SMSC75XX_INT_STS_RDFO_INT 0x00400000UL /**< RX FIFO overflow */ +#define SMSC75XX_INT_STS_PHY_INT 0x00020000UL /**< PHY interrupt */ + +/** Hardware configuration register */ +#define SMSC75XX_HW_CFG 0x010 +#define SMSC75XX_HW_CFG_BIR 0x00000080UL /**< Bulk IN use NAK */ +#define SMSC75XX_HW_CFG_LRST 0x00000002UL /**< Soft lite reset */ + +/** Interrupt endpoint control register */ +#define SMSC75XX_INT_EP_CTL 0x038 +#define SMSC75XX_INT_EP_CTL_RDFO_EN 0x00400000UL /**< RX FIFO overflow */ +#define SMSC75XX_INT_EP_CTL_PHY_EN 0x00020000UL /**< PHY interrupt */ + +/** Bulk IN delay register */ +#define SMSC75XX_BULK_IN_DLY 0x03c +#define SMSC75XX_BULK_IN_DLY_SET(ticks) ( (ticks) << 0 ) /**< Delay / 16.7ns */ + +/** EEPROM command register */ +#define SMSC75XX_E2P_CMD 0x040 +#define SMSC75XX_E2P_CMD_EPC_BSY 0x80000000UL /**< EPC busy */ +#define SMSC75XX_E2P_CMD_EPC_CMD_READ 0x00000000UL /**< READ command */ +#define SMSC75XX_E2P_CMD_EPC_ADDR(addr) ( (addr) << 0 ) /**< EPC address */ + +/** EEPROM data register */ +#define SMSC75XX_E2P_DATA 0x044 +#define SMSC75XX_E2P_DATA_GET(e2p_data) \ + ( ( (e2p_data) >> 0 ) & 0xff ) /**< EEPROM data */ + +/** MAC address EEPROM address */ +#define SMSC75XX_EEPROM_MAC 0x01 + +/** Receive filtering engine control register */ +#define SMSC75XX_RFE_CTL 0x060 +#define SMSC75XX_RFE_CTL_AB 0x00000400UL /**< Accept broadcast */ +#define SMSC75XX_RFE_CTL_AM 0x00000200UL /**< Accept multicast */ +#define SMSC75XX_RFE_CTL_AU 0x00000100UL /**< Accept unicast */ + +/** FIFO controller RX FIFO control register */ +#define SMSC75XX_FCT_RX_CTL 0x090 +#define SMSC75XX_FCT_RX_CTL_EN 0x80000000UL /**< FCT RX enable */ +#define SMSC75XX_FCT_RX_CTL_BAD 0x02000000UL /**< Store bad frames */ + +/** FIFO controller TX FIFO control register */ +#define SMSC75XX_FCT_TX_CTL 0x094 +#define SMSC75XX_FCT_TX_CTL_EN 0x80000000UL /**< FCT TX enable */ + +/** MAC receive register */ +#define SMSC75XX_MAC_RX 0x104 +#define SMSC75XX_MAC_RX_MAX_SIZE(mtu) ( (mtu) << 16 ) /**< Max frame size */ +#define SMSC75XX_MAC_RX_MAX_SIZE_DEFAULT \ + SMSC75XX_MAC_RX_MAX_SIZE ( ETH_FRAME_LEN + 4 /* VLAN */ + 4 /* CRC */ ) +#define SMSC75XX_MAC_RX_FCS 0x00000010UL /**< FCS stripping */ +#define SMSC75XX_MAC_RX_EN 0x00000001UL /**< RX enable */ + +/** MAC transmit register */ +#define SMSC75XX_MAC_TX 0x108 +#define SMSC75XX_MAC_TX_EN 0x00000001UL /**< TX enable */ + +/** MAC receive address high register */ +#define SMSC75XX_RX_ADDRH 0x118 + +/** MAC receive address low register */ +#define SMSC75XX_RX_ADDRL 0x11c + +/** MII access register */ +#define SMSC75XX_MII_ACCESS 0x120 +#define SMSC75XX_MII_ACCESS_PHY_ADDRESS 0x00000800UL /**< PHY address */ +#define SMSC75XX_MII_ACCESS_MIIRINDA(addr) ( (addr) << 6 ) /**< MII register */ +#define SMSC75XX_MII_ACCESS_MIIWNR 0x00000002UL /**< MII write */ +#define SMSC75XX_MII_ACCESS_MIIBZY 0x00000001UL /**< MII busy */ + +/** MII data register */ +#define SMSC75XX_MII_DATA 0x124 +#define SMSC75XX_MII_DATA_SET(data) ( (data) << 0 ) /**< Set data */ +#define SMSC75XX_MII_DATA_GET(mii_data) \ + ( ( (mii_data) >> 0 ) & 0xffff ) /**< Get data */ + +/** PHY interrupt source MII register */ +#define SMSC75XX_MII_PHY_INTR_SOURCE 29 + +/** PHY interrupt mask MII register */ +#define SMSC75XX_MII_PHY_INTR_MASK 30 + +/** PHY interrupt: auto-negotiation complete */ +#define SMSC75XX_PHY_INTR_ANEG_DONE 0x0040 + +/** PHY interrupt: link down */ +#define SMSC75XX_PHY_INTR_LINK_DOWN 0x0010 + +/** MAC address perfect filter N high register */ +#define SMSC75XX_ADDR_FILTH(n) ( 0x300 + ( 8 * (n) ) ) +#define SMSC75XX_ADDR_FILTH_VALID 0x80000000UL /**< Address valid */ + +/** MAC address perfect filter N low register */ +#define SMSC75XX_ADDR_FILTL(n) ( 0x304 + ( 8 * (n) ) ) + +/** MAC address */ +union smsc75xx_mac { + /** MAC receive address registers */ + struct { + /** MAC receive address low register */ + uint32_t l; + /** MAC receive address high register */ + uint32_t h; + } __attribute__ (( packed )) addr; + /** Raw MAC address */ + uint8_t raw[ETH_ALEN]; +}; + +/** Receive packet header */ +struct smsc75xx_rx_header { + /** RX command word */ + uint32_t command; + /** VLAN tag */ + uint16_t vtag; + /** Checksum */ + uint16_t csum; + /** Two-byte padding used to align Ethernet payload */ + uint16_t pad; +} __attribute__ (( packed )); + +/** Receive error detected */ +#define SMSC75XX_RX_RED 0x00400000UL + +/** Transmit packet header */ +struct smsc75xx_tx_header { + /** TX command word */ + uint32_t command; + /** VLAN tag */ + uint16_t tag; + /** Maximum segment size */ + uint16_t mss; +} __attribute__ (( packed )); + +/** Insert frame checksum and pad */ +#define SMSC75XX_TX_FCS 0x00400000UL + +/** Interrupt packet format */ +struct smsc75xx_interrupt { + /** Current value of INT_STS register */ + uint32_t int_sts; +} __attribute__ (( packed )); + +/** Byte count statistics */ +struct smsc75xx_byte_statistics { + /** Unicast byte count */ + uint32_t unicast; + /** Broadcast byte count */ + uint32_t broadcast; + /** Multicast byte count */ + uint32_t multicast; +} __attribute__ (( packed )); + +/** Frame count statistics */ +struct smsc75xx_frame_statistics { + /** Unicast frames */ + uint32_t unicast; + /** Broadcast frames */ + uint32_t broadcast; + /** Multicast frames */ + uint32_t multicast; + /** Pause frames */ + uint32_t pause; + /** Frames by length category */ + uint32_t len[7]; +} __attribute__ (( packed )); + +/** Receive error statistics */ +struct smsc75xx_rx_error_statistics { + /** FCS errors */ + uint32_t fcs; + /** Alignment errors */ + uint32_t alignment; + /** Fragment errors */ + uint32_t fragment; + /** Jabber errors */ + uint32_t jabber; + /** Undersize frame errors */ + uint32_t undersize; + /** Oversize frame errors */ + uint32_t oversize; + /** Dropped frame errors */ + uint32_t dropped; +} __attribute__ (( packed )); + +/** Receive statistics */ +struct smsc75xx_rx_statistics { + /** Error statistics */ + struct smsc75xx_rx_error_statistics err; + /** Byte count statistics */ + struct smsc75xx_byte_statistics byte; + /** Frame count statistics */ + struct smsc75xx_frame_statistics frame; +} __attribute__ (( packed )); + +/** Transmit error statistics */ +struct smsc75xx_tx_error_statistics { + /** FCS errors */ + uint32_t fcs; + /** Excess deferral errors */ + uint32_t deferral; + /** Carrier errors */ + uint32_t carrier; + /** Bad byte count */ + uint32_t count; + /** Single collisions */ + uint32_t single; + /** Multiple collisions */ + uint32_t multiple; + /** Excession collisions */ + uint32_t excessive; + /** Late collisions */ + uint32_t late; +} __attribute__ (( packed )); + +/** Transmit statistics */ +struct smsc75xx_tx_statistics { + /** Error statistics */ + struct smsc75xx_tx_error_statistics err; + /** Byte count statistics */ + struct smsc75xx_byte_statistics byte; + /** Frame count statistics */ + struct smsc75xx_frame_statistics frame; +} __attribute__ (( packed )); + +/** Statistics */ +struct smsc75xx_statistics { + /** Receive statistics */ + struct smsc75xx_rx_statistics rx; + /** Transmit statistics */ + struct smsc75xx_tx_statistics tx; +} __attribute__ (( packed )); + +/** A SMSC75xx network device */ +struct smsc75xx_device { + /** USB device */ + struct usb_device *usb; + /** USB bus */ + struct usb_bus *bus; + /** Network device */ + struct net_device *netdev; + /** USB network device */ + struct usbnet_device usbnet; + /** MII interface */ + struct mii_interface mii; + /** Interrupt status */ + uint32_t int_sts; +}; + +/** Reset delay (in microseconds) */ +#define SMSC75XX_RESET_DELAY_US 2 + +/** Maximum time to wait for EEPROM (in milliseconds) */ +#define SMSC75XX_EEPROM_MAX_WAIT_MS 100 + +/** Maximum time to wait for MII (in milliseconds) */ +#define SMSC75XX_MII_MAX_WAIT_MS 100 + +/** Interrupt maximum fill level + * + * This is a policy decision. + */ +#define SMSC75XX_INTR_MAX_FILL 2 + +/** Bulk IN maximum fill level + * + * This is a policy decision. + */ +#define SMSC75XX_IN_MAX_FILL 8 + +/** Bulk IN buffer size */ +#define SMSC75XX_IN_MTU \ + ( sizeof ( struct smsc75xx_rx_header ) + \ + ETH_FRAME_LEN + 4 /* possible VLAN header */ ) + +#endif /* _SMSC75XX_H */ diff --git a/roms/ipxe/src/drivers/net/sundance.c b/roms/ipxe/src/drivers/net/sundance.c index eef7c9c7c..9127fa2cd 100644 --- a/roms/ipxe/src/drivers/net/sundance.c +++ b/roms/ipxe/src/drivers/net/sundance.c @@ -601,7 +601,7 @@ static int sundance_probe ( struct nic *nic, struct pci_device *pci ) { sdc->nic_name = pci->id->name; sdc->mtu = mtu; - pci_read_config_byte(pci, PCI_REVISION_ID, &sdc->pci_rev_id); + pci_read_config_byte(pci, PCI_REVISION, &sdc->pci_rev_id); DBG ( "Device revision id: %hx\n", sdc->pci_rev_id ); diff --git a/roms/ipxe/src/drivers/net/tg3/tg3.c b/roms/ipxe/src/drivers/net/tg3/tg3.c index 32ca1609c..42bfa2d99 100644 --- a/roms/ipxe/src/drivers/net/tg3/tg3.c +++ b/roms/ipxe/src/drivers/net/tg3/tg3.c @@ -928,6 +928,7 @@ static struct pci_device_id tg3_nics[] = { PCI_ROM(0x14e4, 0x16b6, "14e4-16b6", "14e4-16b6", 0), PCI_ROM(0x14e4, 0x1657, "14e4-1657", "14e4-1657", 0), PCI_ROM(0x14e4, 0x165f, "14e4-165f", "14e4-165f", 0), + PCI_ROM(0x14e4, 0x1686, "14e4-1686", "14e4-1686", 0), PCI_ROM(0x1148, 0x4400, "1148-4400", "1148-4400", 0), PCI_ROM(0x1148, 0x4500, "1148-4500", "1148-4500", 0), PCI_ROM(0x173b, 0x03e8, "173b-03e8", "173b-03e8", 0), diff --git a/roms/ipxe/src/drivers/net/tg3/tg3.h b/roms/ipxe/src/drivers/net/tg3/tg3.h index 660368394..2b85b065b 100644 --- a/roms/ipxe/src/drivers/net/tg3/tg3.h +++ b/roms/ipxe/src/drivers/net/tg3/tg3.h @@ -131,6 +131,10 @@ #define PCI_DEVICE_ID_TIGON3_5901_2 0x170e #define PCI_DEVICE_ID_TIGON3_5906 0x1712 #define PCI_DEVICE_ID_TIGON3_5906M 0x1713 +#define PCI_VENDOR_ID_COMPAQ 0x0e11 +#define PCI_VENDOR_ID_IBM 0x1014 +#define PCI_VENDOR_ID_DELL 0x1028 +#define PCI_VENDOR_ID_3COM 0x10b7 /* </pci_ids.h> */ #define SPEED_10 10 @@ -185,6 +189,7 @@ #define TG3PCI_DEVICE_TIGON3_57761 0x16b0 #define TG3PCI_DEVICE_TIGON3_57762 0x1682 #define TG3PCI_DEVICE_TIGON3_57765 0x16b4 +#define TG3PCI_DEVICE_TIGON3_57766 0x1686 #define TG3PCI_DEVICE_TIGON3_57791 0x16b2 #define TG3PCI_DEVICE_TIGON3_57795 0x16b6 #define TG3PCI_DEVICE_TIGON3_5719 0x1657 diff --git a/roms/ipxe/src/drivers/net/tg3/tg3_hw.c b/roms/ipxe/src/drivers/net/tg3/tg3_hw.c index 3a481aba3..50353cf36 100644 --- a/roms/ipxe/src/drivers/net/tg3/tg3_hw.c +++ b/roms/ipxe/src/drivers/net/tg3/tg3_hw.c @@ -436,6 +436,7 @@ int tg3_get_invariants(struct tg3 *tp) tp->pdev->device == TG3PCI_DEVICE_TIGON3_57761 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57762 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57765 || + tp->pdev->device == TG3PCI_DEVICE_TIGON3_57766 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57791 || tp->pdev->device == TG3PCI_DEVICE_TIGON3_57795) pci_read_config_dword(tp->pdev, diff --git a/roms/ipxe/src/drivers/net/virtio-net.c b/roms/ipxe/src/drivers/net/virtio-net.c index d5fd81979..533ccb0c6 100644 --- a/roms/ipxe/src/drivers/net/virtio-net.c +++ b/roms/ipxe/src/drivers/net/virtio-net.c @@ -20,7 +20,7 @@ * See the COPYING file in the top-level directory. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <stdlib.h> @@ -131,8 +131,8 @@ static void virtnet_enqueue_iob ( struct net_device *netdev, }, }; - DBGC ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n", - virtnet, iobuf, vq_idx ); + DBGC2 ( virtnet, "VIRTIO-NET %p enqueuing iobuf %p on vq %d\n", + virtnet, iobuf, vq_idx ); vring_add_buf ( vq, list, out, in, iobuf, 0 ); vring_kick ( virtnet->ioaddr, vq, 1 ); @@ -256,8 +256,8 @@ static void virtnet_process_tx_packets ( struct net_device *netdev ) { while ( vring_more_used ( tx_vq ) ) { struct io_buffer *iobuf = vring_get_buf ( tx_vq, NULL ); - DBGC ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n", - virtnet, iobuf ); + DBGC2 ( virtnet, "VIRTIO-NET %p tx complete iobuf %p\n", + virtnet, iobuf ); netdev_tx_complete ( netdev, iobuf ); } @@ -283,8 +283,8 @@ static void virtnet_process_rx_packets ( struct net_device *netdev ) { iob_unput ( iobuf, RX_BUF_SIZE ); iob_put ( iobuf, len - sizeof ( struct virtio_net_hdr ) ); - DBGC ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", - virtnet, iobuf, iob_len ( iobuf ) ); + DBGC2 ( virtnet, "VIRTIO-NET %p rx complete iobuf %p len %zd\n", + virtnet, iobuf, iob_len ( iobuf ) ); /* Pass completed packet to the network stack */ netdev_rx ( netdev, iobuf ); diff --git a/roms/ipxe/src/drivers/net/vmxnet3.c b/roms/ipxe/src/drivers/net/vmxnet3.c index 31082bf6f..8d4f4b843 100644 --- a/roms/ipxe/src/drivers/net/vmxnet3.c +++ b/roms/ipxe/src/drivers/net/vmxnet3.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> diff --git a/roms/ipxe/src/drivers/net/vmxnet3.h b/roms/ipxe/src/drivers/net/vmxnet3.h index db313d4b8..a1671d9dd 100644 --- a/roms/ipxe/src/drivers/net/vmxnet3.h +++ b/roms/ipxe/src/drivers/net/vmxnet3.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/drivers/net/vxge/vxge.c b/roms/ipxe/src/drivers/net/vxge/vxge.c index bf20ec43c..d50ac05b5 100644 --- a/roms/ipxe/src/drivers/net/vxge/vxge.c +++ b/roms/ipxe/src/drivers/net/vxge/vxge.c @@ -5,10 +5,11 @@ * as "vxge" even though the code is in vxge_* named files. */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE(GPL2_OR_LATER_OR_UBDL); #include <ipxe/pci.h> +PROVIDE_REQUIRING_SYMBOL(); REQUIRE_OBJECT(vxge_main); /** vxge PCI IDs for util/parserom.pl which are put into bin/NIC */ diff --git a/roms/ipxe/src/drivers/net/vxge/vxge_main.c b/roms/ipxe/src/drivers/net/vxge/vxge_main.c index 130eab617..8b099c0e2 100644 --- a/roms/ipxe/src/drivers/net/vxge/vxge_main.c +++ b/roms/ipxe/src/drivers/net/vxge/vxge_main.c @@ -509,7 +509,7 @@ vxge_probe(struct pci_device *pdev) vxge_debug(VXGE_INFO, "vxge_probe for device " PCI_FMT "\n", PCI_ARGS(pdev)); - pci_read_config_byte(pdev, PCI_REVISION_ID, &revision); + pci_read_config_byte(pdev, PCI_REVISION, &revision); titan1 = is_titan1(pdev->device, revision); mmio_start = pci_bar_start(pdev, PCI_BASE_ADDRESS_0); diff --git a/roms/ipxe/src/drivers/net/w89c840.c b/roms/ipxe/src/drivers/net/w89c840.c index ce638ab99..d8144a8ce 100644 --- a/roms/ipxe/src/drivers/net/w89c840.c +++ b/roms/ipxe/src/drivers/net/w89c840.c @@ -641,7 +641,9 @@ static int w89c840_probe ( struct nic *nic, struct pci_device *p ) { ioaddr = ioaddr & ~3; /* Mask the bit that says "this is an io addr" */ +#define PCI_VENDOR_ID_WINBOND2 0x1050 #define PCI_DEVICE_ID_WINBOND2_89C840 0x0840 +#define PCI_VENDOR_ID_COMPEX 0x11f6 #define PCI_DEVICE_ID_COMPEX_RL100ATX 0x2011 /* From Matt Hortman <mbhortman@acpthinclient.com> */ diff --git a/roms/ipxe/src/drivers/nvs/nvs.c b/roms/ipxe/src/drivers/nvs/nvs.c index ccb2145bd..af7c466c4 100644 --- a/roms/ipxe/src/drivers/nvs/nvs.c +++ b/roms/ipxe/src/drivers/nvs/nvs.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/drivers/nvs/nvsvpd.c b/roms/ipxe/src/drivers/nvs/nvsvpd.c index 33148d5b9..3e88531c7 100644 --- a/roms/ipxe/src/drivers/nvs/nvsvpd.c +++ b/roms/ipxe/src/drivers/nvs/nvsvpd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/drivers/nvs/spi.c b/roms/ipxe/src/drivers/nvs/spi.c index 84613b9dd..dcfe1af91 100644 --- a/roms/ipxe/src/drivers/nvs/spi.c +++ b/roms/ipxe/src/drivers/nvs/spi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <errno.h> diff --git a/roms/ipxe/src/drivers/nvs/threewire.c b/roms/ipxe/src/drivers/nvs/threewire.c index 53f1ad8de..547f35382 100644 --- a/roms/ipxe/src/drivers/nvs/threewire.c +++ b/roms/ipxe/src/drivers/nvs/threewire.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <string.h> diff --git a/roms/ipxe/src/drivers/usb/ehci.c b/roms/ipxe/src/drivers/usb/ehci.c new file mode 100644 index 000000000..4124692a6 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/ehci.c @@ -0,0 +1,1994 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/malloc.h> +#include <ipxe/pci.h> +#include <ipxe/usb.h> +#include <ipxe/init.h> +#include "ehci.h" + +/** @file + * + * USB Enhanced Host Controller Interface (EHCI) driver + * + */ + +/** + * Construct error code from transfer descriptor status + * + * @v status Transfer descriptor status + * @ret rc Error code + * + * Bits 2-5 of the status code provide some indication as to the root + * cause of the error. We incorporate these into the error code as + * reported to usb_complete_err(). + */ +#define EIO_STATUS( status ) EUNIQ ( EINFO_EIO, ( ( (status) >> 2 ) & 0xf ) ) + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Initialise device + * + * @v ehci EHCI device + * @v regs MMIO registers + */ +static void ehci_init ( struct ehci_device *ehci, void *regs ) { + uint32_t hcsparams; + uint32_t hccparams; + size_t caplength; + + /* Locate capability and operational registers */ + ehci->cap = regs; + caplength = readb ( ehci->cap + EHCI_CAP_CAPLENGTH ); + ehci->op = ( ehci->cap + caplength ); + DBGC2 ( ehci, "EHCI %s cap %08lx op %08lx\n", ehci->name, + virt_to_phys ( ehci->cap ), virt_to_phys ( ehci->op ) ); + + /* Read structural parameters */ + hcsparams = readl ( ehci->cap + EHCI_CAP_HCSPARAMS ); + ehci->ports = EHCI_HCSPARAMS_PORTS ( hcsparams ); + DBGC ( ehci, "EHCI %s has %d ports\n", ehci->name, ehci->ports ); + + /* Read capability parameters 1 */ + hccparams = readl ( ehci->cap + EHCI_CAP_HCCPARAMS ); + ehci->addr64 = EHCI_HCCPARAMS_ADDR64 ( hccparams ); + ehci->flsize = ( EHCI_HCCPARAMS_FLSIZE ( hccparams ) ? + EHCI_FLSIZE_SMALL : EHCI_FLSIZE_DEFAULT ); + ehci->eecp = EHCI_HCCPARAMS_EECP ( hccparams ); + DBGC2 ( ehci, "EHCI %s %d-bit flsize %d\n", ehci->name, + ( ehci->addr64 ? 64 : 32 ), ehci->flsize ); +} + +/** + * Find extended capability + * + * @v ehci EHCI device + * @v pci PCI device + * @v id Capability ID + * @v offset Offset to previous extended capability instance, or zero + * @ret offset Offset to extended capability, or zero if not found + */ +static unsigned int ehci_extended_capability ( struct ehci_device *ehci, + struct pci_device *pci, + unsigned int id, + unsigned int offset ) { + uint32_t eecp; + + /* Locate the extended capability */ + while ( 1 ) { + + /* Locate first or next capability as applicable */ + if ( offset ) { + pci_read_config_dword ( pci, offset, &eecp ); + offset = EHCI_EECP_NEXT ( eecp ); + } else { + offset = ehci->eecp; + } + if ( ! offset ) + return 0; + + /* Check if this is the requested capability */ + pci_read_config_dword ( pci, offset, &eecp ); + if ( EHCI_EECP_ID ( eecp ) == id ) + return offset; + } +} + +/** + * Calculate buffer alignment + * + * @v len Length + * @ret align Buffer alignment + * + * Determine alignment required for a buffer which must be aligned to + * at least EHCI_MIN_ALIGN and which must not cross a page boundary. + */ +static inline size_t ehci_align ( size_t len ) { + size_t align; + + /* Align to own length (rounded up to a power of two) */ + align = ( 1 << fls ( len - 1 ) ); + + /* Round up to EHCI_MIN_ALIGN if needed */ + if ( align < EHCI_MIN_ALIGN ) + align = EHCI_MIN_ALIGN; + + return align; +} + +/** + * Check control data structure reachability + * + * @v ehci EHCI device + * @v ptr Data structure pointer + * @ret rc Return status code + */ +static int ehci_ctrl_reachable ( struct ehci_device *ehci, void *ptr ) { + physaddr_t phys = virt_to_phys ( ptr ); + uint32_t segment; + + /* Always reachable in a 32-bit build */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 0; + + /* Reachable only if control segment matches in a 64-bit build */ + segment = ( ( ( uint64_t ) phys ) >> 32 ); + if ( segment == ehci->ctrldssegment ) + return 0; + + return -ENOTSUP; +} + +/****************************************************************************** + * + * USB legacy support + * + ****************************************************************************** + */ + +/** Prevent the release of ownership back to BIOS */ +static int ehci_legacy_prevent_release; + +/** + * Initialise USB legacy support + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_init ( struct ehci_device *ehci, + struct pci_device *pci ) { + unsigned int legacy; + uint8_t bios; + + /* Locate USB legacy support capability (if present) */ + legacy = ehci_extended_capability ( ehci, pci, EHCI_EECP_ID_LEGACY, 0 ); + if ( ! legacy ) { + /* Not an error; capability may not be present */ + DBGC ( ehci, "EHCI %s has no USB legacy support capability\n", + ehci->name ); + return; + } + + /* Check if legacy USB support is enabled */ + pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), &bios ); + if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { + /* Not an error; already owned by OS */ + DBGC ( ehci, "EHCI %s USB legacy support already disabled\n", + ehci->name ); + return; + } + + /* Record presence of USB legacy support capability */ + ehci->legacy = legacy; +} + +/** + * Claim ownership from BIOS + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_claim ( struct ehci_device *ehci, + struct pci_device *pci ) { + unsigned int legacy = ehci->legacy; + uint32_t ctlsts; + uint8_t bios; + unsigned int i; + + /* Do nothing unless legacy support capability is present */ + if ( ! legacy ) + return; + + /* Claim ownership */ + pci_write_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_OS ), + EHCI_USBLEGSUP_OS_OWNED ); + + /* Wait for BIOS to release ownership */ + for ( i = 0 ; i < EHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { + + /* Check if BIOS has released ownership */ + pci_read_config_byte ( pci, ( legacy + EHCI_USBLEGSUP_BIOS ), + &bios ); + if ( ! ( bios & EHCI_USBLEGSUP_BIOS_OWNED ) ) { + DBGC ( ehci, "EHCI %s claimed ownership from BIOS\n", + ehci->name ); + pci_read_config_dword ( pci, ( legacy + + EHCI_USBLEGSUP_CTLSTS ), + &ctlsts ); + if ( ctlsts ) { + DBGC ( ehci, "EHCI %s warning: BIOS retained " + "SMIs: %08x\n", ehci->name, ctlsts ); + } + return; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* BIOS did not release ownership. Claim it forcibly by + * disabling all SMIs. + */ + DBGC ( ehci, "EHCI %s could not claim ownership from BIOS: forcibly " + "disabling SMIs\n", ehci->name ); + pci_write_config_dword ( pci, ( legacy + EHCI_USBLEGSUP_CTLSTS ), 0 ); +} + +/** + * Release ownership back to BIOS + * + * @v ehci EHCI device + * @v pci PCI device + */ +static void ehci_legacy_release ( struct ehci_device *ehci, + struct pci_device *pci ) { + + /* Do nothing unless legacy support capability is present */ + if ( ! ehci->legacy ) + return; + + /* Do nothing if releasing ownership is prevented */ + if ( ehci_legacy_prevent_release ) { + DBGC ( ehci, "EHCI %s not releasing ownership to BIOS\n", + ehci->name ); + return; + } + + /* Release ownership */ + pci_write_config_byte ( pci, ( ehci->legacy + EHCI_USBLEGSUP_OS ), 0 ); + DBGC ( ehci, "EHCI %s released ownership to BIOS\n", ehci->name ); +} + +/****************************************************************************** + * + * Companion controllers + * + ****************************************************************************** + */ + +/** + * Poll child companion controllers + * + * @v ehci EHCI device + */ +static void ehci_poll_companions ( struct ehci_device *ehci ) { + struct usb_bus *bus; + struct device_description *desc; + + /* Poll any USB buses belonging to child companion controllers */ + for_each_usb_bus ( bus ) { + + /* Get underlying devices description */ + desc = &bus->dev->desc; + + /* Skip buses that are not PCI devices */ + if ( desc->bus_type != BUS_TYPE_PCI ) + continue; + + /* Skip buses that are not part of the same PCI device */ + if ( PCI_FIRST_FUNC ( desc->location ) != + PCI_FIRST_FUNC ( ehci->bus->dev->desc.location ) ) + continue; + + /* Skip buses that are not UHCI or OHCI PCI devices */ + if ( ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_UHCI ))&& + ( desc->class != PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_OHCI ) )) + continue; + + /* Poll child companion controller bus */ + DBGC2 ( ehci, "EHCI %s polling companion %s\n", + ehci->name, bus->name ); + usb_poll ( bus ); + } +} + +/** + * Locate EHCI companion controller + * + * @v pci PCI device + * @ret busdevfn EHCI companion controller bus:dev.fn (if any) + */ +unsigned int ehci_companion ( struct pci_device *pci ) { + struct pci_device tmp; + unsigned int busdevfn; + int rc; + + /* Look for an EHCI function on the same PCI device */ + busdevfn = pci->busdevfn; + while ( ++busdevfn <= PCI_LAST_FUNC ( pci->busdevfn ) ) { + pci_init ( &tmp, busdevfn ); + if ( ( rc = pci_read_config ( &tmp ) ) != 0 ) + continue; + if ( tmp.class == PCI_CLASS ( PCI_CLASS_SERIAL, + PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_EHCI ) ) + return busdevfn; + } + + return 0; +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start EHCI device + * + * @v ehci EHCI device + */ +static void ehci_run ( struct ehci_device *ehci ) { + uint32_t usbcmd; + + /* Set run/stop bit */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd &= ~EHCI_USBCMD_FLSIZE_MASK; + usbcmd |= ( EHCI_USBCMD_RUN | EHCI_USBCMD_FLSIZE ( ehci->flsize ) | + EHCI_USBCMD_PERIODIC | EHCI_USBCMD_ASYNC ); + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); +} + +/** + * Stop EHCI device + * + * @v ehci EHCI device + * @ret rc Return status code + */ +static int ehci_stop ( struct ehci_device *ehci ) { + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd &= ~( EHCI_USBCMD_RUN | EHCI_USBCMD_PERIODIC | + EHCI_USBCMD_ASYNC ); + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < EHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + if ( usbsts & EHCI_USBSTS_HCH ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s timed out waiting for stop\n", ehci->name ); + return -ETIMEDOUT; +} + +/** + * Reset EHCI device + * + * @v ehci EHCI device + * @ret rc Return status code + */ +static int ehci_reset ( struct ehci_device *ehci ) { + uint32_t usbcmd; + unsigned int i; + int rc; + + /* The EHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = ehci_stop ( ehci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + writel ( EHCI_USBCMD_HCRST, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < EHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + if ( ! ( usbcmd & EHCI_USBCMD_HCRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s timed out waiting for reset\n", ehci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer descriptor rings + * + ****************************************************************************** + */ + +/** + * Allocate transfer descriptor ring + * + * @v ehci EHCI device + * @v ring Transfer descriptor ring + * @ret rc Return status code + */ +static int ehci_ring_alloc ( struct ehci_device *ehci, + struct ehci_ring *ring ) { + struct ehci_transfer_descriptor *desc; + struct ehci_transfer_descriptor *next; + unsigned int i; + size_t len; + uint32_t link; + int rc; + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + + /* Allocate I/O buffers */ + ring->iobuf = zalloc ( EHCI_RING_COUNT * sizeof ( ring->iobuf[0] ) ); + if ( ! ring->iobuf ) { + rc = -ENOMEM; + goto err_alloc_iobuf; + } + + /* Allocate queue head */ + ring->head = malloc_dma ( sizeof ( *ring->head ), + ehci_align ( sizeof ( *ring->head ) ) ); + if ( ! ring->head ) { + rc = -ENOMEM; + goto err_alloc_queue; + } + if ( ( rc = ehci_ctrl_reachable ( ehci, ring->head ) ) != 0 ) { + DBGC ( ehci, "EHCI %s queue head unreachable\n", ehci->name ); + goto err_unreachable_queue; + } + memset ( ring->head, 0, sizeof ( *ring->head ) ); + + /* Allocate transfer descriptors */ + len = ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ); + ring->desc = malloc_dma ( len, sizeof ( ring->desc[0] ) ); + if ( ! ring->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + memset ( ring->desc, 0, len ); + + /* Initialise transfer descriptors */ + for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) { + desc = &ring->desc[i]; + if ( ( rc = ehci_ctrl_reachable ( ehci, desc ) ) != 0 ) { + DBGC ( ehci, "EHCI %s descriptor unreachable\n", + ehci->name ); + goto err_unreachable_desc; + } + next = &ring->desc[ ( i + 1 ) % EHCI_RING_COUNT ]; + link = virt_to_phys ( next ); + desc->next = cpu_to_le32 ( link ); + desc->alt = cpu_to_le32 ( link ); + } + + /* Initialise queue head */ + link = virt_to_phys ( &ring->desc[0] ); + ring->head->cache.next = cpu_to_le32 ( link ); + + return 0; + + err_unreachable_desc: + free_dma ( ring->desc, len ); + err_alloc_desc: + err_unreachable_queue: + free_dma ( ring->head, sizeof ( *ring->head ) ); + err_alloc_queue: + free ( ring->iobuf ); + err_alloc_iobuf: + return rc; +} + +/** + * Free transfer descriptor ring + * + * @v ring Transfer descriptor ring + */ +static void ehci_ring_free ( struct ehci_ring *ring ) { + unsigned int i; + + /* Sanity checks */ + assert ( ehci_ring_fill ( ring ) == 0 ); + for ( i = 0 ; i < EHCI_RING_COUNT ; i++ ) + assert ( ring->iobuf[i] == NULL ); + + /* Free transfer descriptors */ + free_dma ( ring->desc, ( EHCI_RING_COUNT * sizeof ( ring->desc[0] ) ) ); + + /* Free queue head */ + free_dma ( ring->head, sizeof ( *ring->head ) ); + + /* Free I/O buffers */ + free ( ring->iobuf ); +} + +/** + * Enqueue transfer descriptors + * + * @v ehci EHCI device + * @v ring Transfer descriptor ring + * @v iobuf I/O buffer + * @v xfers Transfers + * @v count Number of transfers + * @ret rc Return status code + */ +static int ehci_enqueue ( struct ehci_device *ehci, struct ehci_ring *ring, + struct io_buffer *iobuf, + const struct ehci_transfer *xfer, + unsigned int count ) { + struct ehci_transfer_descriptor *desc; + physaddr_t phys; + void *data; + size_t len; + size_t offset; + size_t frag_len; + unsigned int toggle; + unsigned int index; + unsigned int i; + + /* Sanity check */ + assert ( iobuf != NULL ); + assert ( count > 0 ); + + /* Fail if ring does not have sufficient space */ + if ( ehci_ring_remaining ( ring ) < count ) + return -ENOBUFS; + + /* Fail if any portion is unreachable */ + for ( i = 0 ; i < count ; i++ ) { + phys = ( virt_to_phys ( xfer[i].data ) + xfer[i].len - 1 ); + if ( ( phys > 0xffffffffUL ) && ( ! ehci->addr64 ) ) + return -ENOTSUP; + } + + /* Enqueue each transfer, recording the I/O buffer with the last */ + for ( ; count ; ring->prod++, xfer++ ) { + + /* Populate descriptor header */ + index = ( ring->prod % EHCI_RING_COUNT ); + desc = &ring->desc[index]; + toggle = ( xfer->flags & EHCI_FL_TOGGLE ); + assert ( xfer->len <= EHCI_LEN_MASK ); + assert ( EHCI_FL_TOGGLE == EHCI_LEN_TOGGLE ); + desc->len = cpu_to_le16 ( xfer->len | toggle ); + desc->flags = ( xfer->flags | EHCI_FL_CERR_MAX ); + + /* Populate buffer pointers */ + data = xfer->data; + len = xfer->len; + for ( i = 0 ; len ; i++ ) { + + /* Calculate length of this fragment */ + phys = virt_to_phys ( data ); + offset = ( phys & ( EHCI_PAGE_ALIGN - 1 ) ); + frag_len = ( EHCI_PAGE_ALIGN - offset ); + if ( frag_len > len ) + frag_len = len; + + /* Sanity checks */ + assert ( ( i == 0 ) || ( offset == 0 ) ); + assert ( i < ( sizeof ( desc->low ) / + sizeof ( desc->low[0] ) ) ); + + /* Populate buffer pointer */ + desc->low[i] = cpu_to_le32 ( phys ); + if ( sizeof ( physaddr_t ) > sizeof ( uint32_t ) ) { + desc->high[i] = + cpu_to_le32 ( ((uint64_t) phys) >> 32 ); + } + + /* Move to next fragment */ + data += frag_len; + len -= frag_len; + } + + /* Ensure everything is valid before activating descriptor */ + wmb(); + desc->status = EHCI_STATUS_ACTIVE; + + /* Record I/O buffer against last ring index */ + if ( --count == 0 ) + ring->iobuf[index] = iobuf; + } + + return 0; +} + +/** + * Dequeue a transfer descriptor + * + * @v ring Transfer descriptor ring + * @ret iobuf I/O buffer (or NULL) + */ +static struct io_buffer * ehci_dequeue ( struct ehci_ring *ring ) { + struct ehci_transfer_descriptor *desc; + struct io_buffer *iobuf; + unsigned int index = ( ring->cons % EHCI_RING_COUNT ); + + /* Sanity check */ + assert ( ehci_ring_fill ( ring ) > 0 ); + + /* Mark descriptor as inactive (and not halted) */ + desc = &ring->desc[index]; + desc->status = 0; + + /* Retrieve I/O buffer */ + iobuf = ring->iobuf[index]; + ring->iobuf[index] = NULL; + + /* Update consumer counter */ + ring->cons++; + + return iobuf; +} + +/****************************************************************************** + * + * Schedule management + * + ****************************************************************************** + */ + +/** + * Get link value for a queue head + * + * @v queue Queue head + * @ret link Link value + */ +static inline uint32_t ehci_link_qh ( struct ehci_queue_head *queue ) { + + return ( virt_to_phys ( queue ) | EHCI_LINK_TYPE_QH ); +} + +/** + * (Re)build asynchronous schedule + * + * @v ehci EHCI device + */ +static void ehci_async_schedule ( struct ehci_device *ehci ) { + struct ehci_endpoint *endpoint; + struct ehci_queue_head *queue; + uint32_t link; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + link = ehci_link_qh ( ehci->head ); + list_for_each_entry_reverse ( endpoint, &ehci->async, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + link = ehci_link_qh ( queue ); + } + ehci->head->link = cpu_to_le32 ( link ); + wmb(); +} + +/** + * Add endpoint to asynchronous schedule + * + * @v endpoint Endpoint + */ +static void ehci_async_add ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + + /* Add to end of schedule */ + list_add_tail ( &endpoint->schedule, &ehci->async ); + + /* Rebuild schedule */ + ehci_async_schedule ( ehci ); +} + +/** + * Remove endpoint from asynchronous schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_async_del ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &ehci->async, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + ehci_async_schedule ( ehci ); + + /* Request notification when asynchronous schedule advances */ + usbcmd = readl ( ehci->op + EHCI_OP_USBCMD ); + usbcmd |= EHCI_USBCMD_ASYNC_ADVANCE; + writel ( usbcmd, ehci->op + EHCI_OP_USBCMD ); + + /* Wait for asynchronous schedule to advance */ + for ( i = 0 ; i < EHCI_ASYNC_ADVANCE_MAX_WAIT_MS ; i++ ) { + + /* Check for asynchronous schedule advancing */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + if ( usbsts & EHCI_USBSTS_ASYNC_ADVANCE ) { + usbsts &= ~EHCI_USBSTS_CHANGE; + usbsts |= EHCI_USBSTS_ASYNC_ADVANCE; + writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* Bad things will probably happen now */ + DBGC ( ehci, "EHCI %s timed out waiting for asynchronous schedule " + "to advance\n", ehci->name ); + return -ETIMEDOUT; +} + +/** + * (Re)build periodic schedule + * + * @v ehci EHCI device + */ +static void ehci_periodic_schedule ( struct ehci_device *ehci ) { + struct ehci_endpoint *endpoint; + struct ehci_queue_head *queue; + uint32_t link; + unsigned int frames; + unsigned int max_interval; + unsigned int i; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + DBGCP ( ehci, "EHCI %s periodic schedule: ", ehci->name ); + link = EHCI_LINK_TERMINATE; + list_for_each_entry_reverse ( endpoint, &ehci->periodic, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + DBGCP ( ehci, "%s%d", + ( ( link == EHCI_LINK_TERMINATE ) ? "" : "<-" ), + endpoint->ep->interval ); + link = ehci_link_qh ( queue ); + } + DBGCP ( ehci, "\n" ); + + /* Populate periodic frame list */ + DBGCP ( ehci, "EHCI %s periodic frame list:", ehci->name ); + frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + for ( i = 0 ; i < frames ; i++ ) { + + /* Calculate maximum interval (in microframes) which + * may appear as part of this frame list. + */ + if ( i == 0 ) { + /* Start of list: include all endpoints */ + max_interval = -1U; + } else { + /* Calculate highest power-of-two frame interval */ + max_interval = ( 1 << ( ffs ( i ) - 1 ) ); + /* Convert to microframes */ + max_interval <<= 3; + /* Round up to nearest 2^n-1 */ + max_interval = ( ( max_interval << 1 ) - 1 ); + } + + /* Find first endpoint in schedule satisfying this + * maximum interval constraint. + */ + link = EHCI_LINK_TERMINATE; + list_for_each_entry ( endpoint, &ehci->periodic, schedule ) { + if ( endpoint->ep->interval <= max_interval ) { + queue = endpoint->ring.head; + link = ehci_link_qh ( queue ); + DBGCP ( ehci, " %d:%d", + i, endpoint->ep->interval ); + break; + } + } + ehci->frame[i].link = cpu_to_le32 ( link ); + } + wmb(); + DBGCP ( ehci, "\n" ); +} + +/** + * Add endpoint to periodic schedule + * + * @v endpoint Endpoint + */ +static void ehci_periodic_add ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + struct ehci_endpoint *before; + unsigned int interval = endpoint->ep->interval; + + /* Find first endpoint with a smaller interval */ + list_for_each_entry ( before, &ehci->periodic, schedule ) { + if ( before->ep->interval < interval ) + break; + } + list_add_tail ( &endpoint->schedule, &before->schedule ); + + /* Rebuild schedule */ + ehci_periodic_schedule ( ehci ); +} + +/** + * Remove endpoint from periodic schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_periodic_del ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &ehci->periodic, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + ehci_periodic_schedule ( ehci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); + + return 0; +} + +/** + * Add endpoint to appropriate schedule + * + * @v endpoint Endpoint + */ +static void ehci_schedule_add ( struct ehci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + ehci_periodic_add ( endpoint ); + } else { + ehci_async_add ( endpoint ); + } +} + +/** + * Remove endpoint from appropriate schedule + * + * @v endpoint Endpoint + * @ret rc Return status code + */ +static int ehci_schedule_del ( struct ehci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + return ehci_periodic_del ( endpoint ); + } else { + return ehci_async_del ( endpoint ); + } +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Determine endpoint characteristics + * + * @v ep USB endpoint + * @ret chr Endpoint characteristics + */ +static uint32_t ehci_endpoint_characteristics ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + uint32_t chr; + + /* Determine basic characteristics */ + chr = ( EHCI_CHR_ADDRESS ( usb->address ) | + EHCI_CHR_ENDPOINT ( ep->address ) | + EHCI_CHR_MAX_LEN ( ep->mtu ) ); + + /* Control endpoints require manual control of the data toggle */ + if ( attr == USB_ENDPOINT_ATTR_CONTROL ) + chr |= EHCI_CHR_TOGGLE; + + /* Determine endpoint speed */ + if ( usb->port->speed == USB_SPEED_HIGH ) { + chr |= EHCI_CHR_EPS_HIGH; + } else { + if ( usb->port->speed == USB_SPEED_FULL ) { + chr |= EHCI_CHR_EPS_FULL; + } else { + chr |= EHCI_CHR_EPS_LOW; + } + if ( attr == USB_ENDPOINT_ATTR_CONTROL ) + chr |= EHCI_CHR_CONTROL; + } + + return chr; +} + +/** + * Determine endpoint capabilities + * + * @v ep USB endpoint + * @ret cap Endpoint capabilities + */ +static uint32_t ehci_endpoint_capabilities ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct usb_port *tt = usb_transaction_translator ( usb ); + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + uint32_t cap; + unsigned int i; + + /* Determine basic capabilities */ + cap = EHCI_CAP_MULT ( ep->burst + 1 ); + + /* Determine interrupt schedule mask, if applicable */ + if ( ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) && + ( ( ep->interval != 0 ) /* avoid infinite loop */ ) ) { + for ( i = 0 ; i < 8 /* microframes per frame */ ; + i += ep->interval ) { + cap |= EHCI_CAP_INTR_SCHED ( i ); + } + } + + /* Set transaction translator hub address and port, if applicable */ + if ( tt ) { + assert ( tt->hub->usb ); + cap |= ( EHCI_CAP_TT_HUB ( tt->hub->usb->address ) | + EHCI_CAP_TT_PORT ( tt->address ) ); + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) + cap |= EHCI_CAP_SPLIT_SCHED_DEFAULT; + } + + return cap; +} + +/** + * Update endpoint characteristics and capabilities + * + * @v ep USB endpoint + */ +static void ehci_endpoint_update ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_queue_head *head; + + /* Update queue characteristics and capabilities */ + head = endpoint->ring.head; + head->chr = cpu_to_le32 ( ehci_endpoint_characteristics ( ep ) ); + head->cap = cpu_to_le32 ( ehci_endpoint_capabilities ( ep ) ); +} + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct ehci_endpoint *endpoint; + int rc; + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + endpoint->ehci = ehci; + endpoint->ep = ep; + usb_endpoint_set_hostdata ( ep, endpoint ); + + /* Initialise descriptor ring */ + if ( ( rc = ehci_ring_alloc ( ehci, &endpoint->ring ) ) != 0 ) + goto err_ring_alloc; + + /* Update queue characteristics and capabilities */ + ehci_endpoint_update ( ep ); + + /* Add to list of endpoints */ + list_add_tail ( &endpoint->list, &ehci->endpoints ); + + /* Add to schedule */ + ehci_schedule_add ( endpoint ); + + return 0; + + ehci_ring_free ( &endpoint->ring ); + err_ring_alloc: + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void ehci_endpoint_close ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + struct usb_device *usb = ep->usb; + struct io_buffer *iobuf; + int rc; + + /* Remove from schedule */ + if ( ( rc = ehci_schedule_del ( endpoint ) ) != 0 ) { + /* No way to prevent hardware from continuing to + * access the memory, so leak it. + */ + DBGC ( ehci, "EHCI %s %s could not unschedule: %s\n", + usb->name, usb_endpoint_name ( ep ), strerror ( rc ) ); + return; + } + + /* Cancel any incomplete transfers */ + while ( ehci_ring_fill ( &endpoint->ring ) ) { + iobuf = ehci_dequeue ( &endpoint->ring ); + if ( iobuf ) + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Remove from list of endpoints */ + list_del ( &endpoint->list ); + + /* Free descriptor ring */ + ehci_ring_free ( &endpoint->ring ); + + /* Free endpoint */ + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_reset ( struct usb_endpoint *ep ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_ring *ring = &endpoint->ring; + struct ehci_transfer_descriptor *cache = &ring->head->cache; + uint32_t link; + + /* Sanity checks */ + assert ( ! ( cache->status & EHCI_STATUS_ACTIVE ) ); + assert ( cache->status & EHCI_STATUS_HALTED ); + + /* Reset residual count */ + ring->residual = 0; + + /* Reset data toggle */ + cache->len = 0; + + /* Prepare to restart at next unconsumed descriptor */ + link = virt_to_phys ( &ring->desc[ ring->cons % EHCI_RING_COUNT ] ); + cache->next = cpu_to_le32 ( link ); + + /* Restart ring */ + wmb(); + cache->status = 0; + + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_endpoint_mtu ( struct usb_endpoint *ep ) { + + /* Update endpoint characteristics and capabilities */ + ehci_endpoint_update ( ep ); + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int ehci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + struct usb_setup_packet *packet; + unsigned int input; + struct ehci_transfer xfers[3]; + struct ehci_transfer *xfer = xfers; + size_t len; + int rc; + + /* Construct setup stage */ + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + packet = iobuf->data; + iob_pull ( iobuf, sizeof ( *packet ) ); + xfer->data = packet; + xfer->len = sizeof ( *packet ); + xfer->flags = EHCI_FL_PID_SETUP; + xfer++; + + /* Construct data stage, if applicable */ + len = iob_len ( iobuf ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) { + xfer->data = iobuf->data; + xfer->len = len; + xfer->flags = ( EHCI_FL_TOGGLE | + ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); + xfer++; + } + + /* Construct status stage */ + xfer->data = NULL; + xfer->len = 0; + xfer->flags = ( EHCI_FL_TOGGLE | EHCI_FL_IOC | + ( ( len && input ) ? EHCI_FL_PID_OUT : EHCI_FL_PID_IN)); + xfer++; + + /* Enqueue transfer */ + if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, + ( xfer - xfers ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int ehci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct ehci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct ehci_device *ehci = endpoint->ehci; + unsigned int input = ( ep->address & USB_DIR_IN ); + struct ehci_transfer xfers[2]; + struct ehci_transfer *xfer = xfers; + size_t len = iob_len ( iobuf ); + int rc; + + /* Create transfer */ + xfer->data = iobuf->data; + xfer->len = len; + xfer->flags = ( EHCI_FL_IOC | + ( input ? EHCI_FL_PID_IN : EHCI_FL_PID_OUT ) ); + xfer++; + if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { + xfer->data = NULL; + xfer->len = 0; + assert ( ! input ); + xfer->flags = ( EHCI_FL_IOC | EHCI_FL_PID_OUT ); + xfer++; + } + + /* Enqueue transfer */ + if ( ( rc = ehci_enqueue ( ehci, &endpoint->ring, iobuf, xfers, + ( xfer - xfers ) ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Poll for completions + * + * @v endpoint Endpoint + */ +static void ehci_endpoint_poll ( struct ehci_endpoint *endpoint ) { + struct ehci_device *ehci = endpoint->ehci; + struct ehci_ring *ring = &endpoint->ring; + struct ehci_transfer_descriptor *desc; + struct usb_endpoint *ep = endpoint->ep; + struct usb_device *usb = ep->usb; + struct io_buffer *iobuf; + unsigned int index; + unsigned int status; + int rc; + + /* Consume all completed descriptors */ + while ( ehci_ring_fill ( &endpoint->ring ) ) { + + /* Stop if we reach an uncompleted descriptor */ + rmb(); + index = ( ring->cons % EHCI_RING_COUNT ); + desc = &ring->desc[index]; + status = desc->status; + if ( status & EHCI_STATUS_ACTIVE ) + break; + + /* Consume this descriptor */ + iobuf = ehci_dequeue ( ring ); + + /* If we have encountered an error, then consume all + * remaining descriptors in this transaction, report + * the error to the USB core, and stop further + * processing. + */ + if ( status & EHCI_STATUS_HALTED ) { + rc = -EIO_STATUS ( status ); + DBGC ( ehci, "EHCI %s %s completion %d failed (status " + "%02x): %s\n", usb->name, + usb_endpoint_name ( ep ), index, status, + strerror ( rc ) ); + while ( ! iobuf ) + iobuf = ehci_dequeue ( ring ); + usb_complete_err ( endpoint->ep, iobuf, rc ); + return; + } + + /* Accumulate residual data count */ + ring->residual += ( le16_to_cpu ( desc->len ) & EHCI_LEN_MASK ); + + /* If this is not the end of a transaction (i.e. has + * no I/O buffer), then continue to next descriptor. + */ + if ( ! iobuf ) + continue; + + /* Update I/O buffer length */ + iob_unput ( iobuf, ring->residual ); + ring->residual = 0; + + /* Report completion to USB core */ + usb_complete ( endpoint->ep, iobuf ); + } +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int ehci_device_open ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( usb->port->hub->bus ); + + usb_set_hostdata ( usb, ehci ); + return 0; +} + +/** + * Close device + * + * @v usb USB device + */ +static void ehci_device_close ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct usb_bus *bus = ehci->bus; + + /* Free device address, if assigned */ + if ( usb->address ) + usb_free_address ( bus, usb->address ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int ehci_device_address ( struct usb_device *usb ) { + struct ehci_device *ehci = usb_get_hostdata ( usb ); + struct usb_bus *bus = ehci->bus; + struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); + int address; + int rc; + + /* Sanity checks */ + assert ( usb->address == 0 ); + assert ( ep0 != NULL ); + + /* Allocate device address */ + address = usb_alloc_address ( bus ); + if ( address < 0 ) { + rc = address; + DBGC ( ehci, "EHCI %s could not allocate address: %s\n", + usb->name, strerror ( rc ) ); + goto err_alloc_address; + } + + /* Set address */ + if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) + goto err_set_address; + + /* Update device address */ + usb->address = address; + + /* Update control endpoint characteristics and capabilities */ + ehci_endpoint_update ( ep0 ); + + return 0; + + err_set_address: + usb_free_address ( bus, address ); + err_alloc_address: + return rc; +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int ehci_hub_open ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void ehci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int ehci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + uint32_t portsc; + unsigned int i; + + /* Route all ports to EHCI controller */ + writel ( EHCI_CONFIGFLAG_CF, ehci->op + EHCI_OP_CONFIGFLAG ); + + /* Enable power to all ports */ + for ( i = 1 ; i <= ehci->ports ; i++ ) { + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( i ) ); + portsc &= ~EHCI_PORTSC_CHANGE; + portsc |= EHCI_PORTSC_PP; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( i ) ); + } + + /* Wait 20ms after potentially enabling power to a port */ + mdelay ( EHCI_PORT_POWER_DELAY_MS ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, ehci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void ehci_root_close ( struct usb_hub *hub ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + + /* Route all ports back to companion controllers */ + writel ( 0, ehci->op + EHCI_OP_CONFIGFLAG ); + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int line; + unsigned int i; + + /* Check for a low-speed device */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + line = EHCI_PORTSC_LINE_STATUS ( portsc ); + if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { + DBGC ( ehci, "EHCI %s-%d detected low-speed device: " + "disowning\n", ehci->name, port->address ); + goto disown; + } + + /* Reset port */ + portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); + portsc |= EHCI_PORTSC_PR; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + mdelay ( USB_RESET_DELAY_MS ); + portsc &= ~EHCI_PORTSC_PR; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < EHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + if ( ! ( portsc & EHCI_PORTSC_PR ) ) { + if ( portsc & EHCI_PORTSC_PED ) + return 0; + DBGC ( ehci, "EHCI %s-%d not enabled after reset: " + "disowning\n", ehci->name, port->address ); + goto disown; + } + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( ehci, "EHCI %s-%d timed out waiting for port to reset\n", + ehci->name, port->address ); + return -ETIMEDOUT; + + disown: + /* Disown port */ + portsc &= ~EHCI_PORTSC_CHANGE; + portsc |= EHCI_PORTSC_OWNER; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Delay to allow child companion controllers to settle */ + mdelay ( EHCI_DISOWN_DELAY_MS ); + + /* Poll child companion controllers */ + ehci_poll_companions ( ehci ); + + return -ENODEV; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + + /* Disable port */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + portsc &= ~( EHCI_PORTSC_PED | EHCI_PORTSC_CHANGE ); + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int ehci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int speed; + unsigned int line; + int ccs; + int csc; + int ped; + + /* Read port status */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + DBGC2 ( ehci, "EHCI %s-%d status is %08x\n", + ehci->name, port->address, portsc ); + ccs = ( portsc & EHCI_PORTSC_CCS ); + csc = ( portsc & EHCI_PORTSC_CSC ); + ped = ( portsc & EHCI_PORTSC_PED ); + line = EHCI_PORTSC_LINE_STATUS ( portsc ); + + /* Record disconnections and clear changes */ + port->disconnected |= csc; + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Determine port speed */ + if ( ! ccs ) { + /* Port not connected */ + speed = USB_SPEED_NONE; + } else if ( line == EHCI_PORTSC_LINE_STATUS_LOW ) { + /* Detected as low-speed */ + speed = USB_SPEED_LOW; + } else if ( ped ) { + /* Port already enabled: must be high-speed */ + speed = USB_SPEED_HIGH; + } else { + /* Not low-speed and not yet enabled. Could be either + * full-speed or high-speed; we can't yet tell. + */ + speed = USB_SPEED_FULL; + } + port->speed = speed; + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int ehci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( ehci, "EHCI %s-%d nonsensical CLEAR_TT for %s %s\n", ehci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/** + * Poll for port status changes + * + * @v hub USB hub + * @v port USB port + */ +static void ehci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { + struct ehci_device *ehci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + uint32_t change; + + /* Do nothing unless something has changed */ + portsc = readl ( ehci->op + EHCI_OP_PORTSC ( port->address ) ); + change = ( portsc & EHCI_PORTSC_CHANGE ); + if ( ! change ) + return; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & EHCI_PORTSC_CSC ); + writel ( portsc, ehci->op + EHCI_OP_PORTSC ( port->address ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int ehci_bus_open ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + unsigned int frames; + size_t len; + int rc; + + /* Sanity checks */ + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + + /* Allocate and initialise asynchronous queue head */ + ehci->head = malloc_dma ( sizeof ( *ehci->head ), + ehci_align ( sizeof ( *ehci->head ) ) ); + if ( ! ehci->head ) { + rc = -ENOMEM; + goto err_alloc_head; + } + memset ( ehci->head, 0, sizeof ( *ehci->head ) ); + ehci->head->chr = cpu_to_le32 ( EHCI_CHR_HEAD ); + ehci->head->cache.next = cpu_to_le32 ( EHCI_LINK_TERMINATE ); + ehci->head->cache.status = EHCI_STATUS_HALTED; + ehci_async_schedule ( ehci ); + writel ( virt_to_phys ( ehci->head ), + ehci->op + EHCI_OP_ASYNCLISTADDR ); + + /* Use async queue head to determine control data structure segment */ + ehci->ctrldssegment = + ( ( ( uint64_t ) virt_to_phys ( ehci->head ) ) >> 32 ); + if ( ehci->addr64 ) { + writel ( ehci->ctrldssegment, ehci->op + EHCI_OP_CTRLDSSEGMENT); + } else if ( ehci->ctrldssegment ) { + DBGC ( ehci, "EHCI %s CTRLDSSEGMENT not supported\n", + ehci->name ); + rc = -ENOTSUP; + goto err_ctrldssegment; + } + + /* Allocate periodic frame list */ + frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + len = ( frames * sizeof ( ehci->frame[0] ) ); + ehci->frame = malloc_dma ( len, EHCI_PAGE_ALIGN ); + if ( ! ehci->frame ) { + rc = -ENOMEM; + goto err_alloc_frame; + } + if ( ( rc = ehci_ctrl_reachable ( ehci, ehci->frame ) ) != 0 ) { + DBGC ( ehci, "EHCI %s frame list unreachable\n", ehci->name ); + goto err_unreachable_frame; + } + ehci_periodic_schedule ( ehci ); + writel ( virt_to_phys ( ehci->frame ), + ehci->op + EHCI_OP_PERIODICLISTBASE ); + + /* Start controller */ + ehci_run ( ehci ); + + return 0; + + ehci_stop ( ehci ); + err_unreachable_frame: + free_dma ( ehci->frame, len ); + err_alloc_frame: + err_ctrldssegment: + free_dma ( ehci->head, sizeof ( *ehci->head ) ); + err_alloc_head: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void ehci_bus_close ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + unsigned int frames = EHCI_PERIODIC_FRAMES ( ehci->flsize ); + + /* Sanity checks */ + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + + /* Stop controller */ + ehci_stop ( ehci ); + + /* Free periodic frame list */ + free_dma ( ehci->frame, ( frames * sizeof ( ehci->frame[0] ) ) ); + + /* Free asynchronous schedule */ + free_dma ( ehci->head, sizeof ( *ehci->head ) ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void ehci_bus_poll ( struct usb_bus *bus ) { + struct ehci_device *ehci = usb_bus_get_hostdata ( bus ); + struct usb_hub *hub = bus->hub; + struct ehci_endpoint *endpoint; + unsigned int i; + uint32_t usbsts; + uint32_t change; + + /* Do nothing unless something has changed */ + usbsts = readl ( ehci->op + EHCI_OP_USBSTS ); + assert ( usbsts & EHCI_USBSTS_ASYNC ); + assert ( usbsts & EHCI_USBSTS_PERIODIC ); + assert ( ! ( usbsts & EHCI_USBSTS_HCH ) ); + change = ( usbsts & EHCI_USBSTS_CHANGE ); + if ( ! change ) + return; + + /* Acknowledge changes */ + writel ( usbsts, ehci->op + EHCI_OP_USBSTS ); + + /* Process completions, if applicable */ + if ( change & ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT ) ) { + + /* Iterate over all endpoints looking for completed + * descriptors. We trust that completion handlers are + * minimal and will not do anything that could + * plausibly affect the endpoint list itself. + */ + list_for_each_entry ( endpoint, &ehci->endpoints, list ) + ehci_endpoint_poll ( endpoint ); + } + + /* Process port status changes, if applicable */ + if ( change & EHCI_USBSTS_PORT ) { + + /* Iterate over all ports looking for status changes */ + for ( i = 1 ; i <= ehci->ports ; i++ ) + ehci_root_poll ( hub, usb_port ( hub, i ) ); + } + + /* Report fatal errors */ + if ( change & EHCI_USBSTS_SYSERR ) + DBGC ( ehci, "EHCI %s host system error\n", ehci->name ); +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations ehci_operations = { + .endpoint = { + .open = ehci_endpoint_open, + .close = ehci_endpoint_close, + .reset = ehci_endpoint_reset, + .mtu = ehci_endpoint_mtu, + .message = ehci_endpoint_message, + .stream = ehci_endpoint_stream, + }, + .device = { + .open = ehci_device_open, + .close = ehci_device_close, + .address = ehci_device_address, + }, + .bus = { + .open = ehci_bus_open, + .close = ehci_bus_close, + .poll = ehci_bus_poll, + }, + .hub = { + .open = ehci_hub_open, + .close = ehci_hub_close, + }, + .root = { + .open = ehci_root_open, + .close = ehci_root_close, + .enable = ehci_root_enable, + .disable = ehci_root_disable, + .speed = ehci_root_speed, + .clear_tt = ehci_root_clear_tt, + }, +}; + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int ehci_probe ( struct pci_device *pci ) { + struct ehci_device *ehci; + struct usb_port *port; + unsigned long bar_start; + size_t bar_size; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + ehci = zalloc ( sizeof ( *ehci ) ); + if ( ! ehci ) { + rc = -ENOMEM; + goto err_alloc; + } + ehci->name = pci->dev.name; + INIT_LIST_HEAD ( &ehci->endpoints ); + INIT_LIST_HEAD ( &ehci->async ); + INIT_LIST_HEAD ( &ehci->periodic ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bar_start = pci_bar_start ( pci, EHCI_BAR ); + bar_size = pci_bar_size ( pci, EHCI_BAR ); + ehci->regs = ioremap ( bar_start, bar_size ); + if ( ! ehci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Initialise EHCI device */ + ehci_init ( ehci, ehci->regs ); + + /* Initialise USB legacy support and claim ownership */ + ehci_legacy_init ( ehci, pci ); + ehci_legacy_claim ( ehci, pci ); + + /* Reset device */ + if ( ( rc = ehci_reset ( ehci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + ehci->bus = alloc_usb_bus ( &pci->dev, ehci->ports, EHCI_MTU, + &ehci_operations ); + if ( ! ehci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( ehci->bus, ehci ); + usb_hub_set_drvdata ( ehci->bus->hub, ehci ); + + /* Set port protocols */ + for ( i = 1 ; i <= ehci->ports ; i++ ) { + port = usb_port ( ehci->bus->hub, i ); + port->protocol = USB_PROTO_2_0; + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( ehci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, ehci ); + return 0; + + unregister_usb_bus ( ehci->bus ); + err_register: + free_usb_bus ( ehci->bus ); + err_alloc_bus: + ehci_reset ( ehci ); + err_reset: + ehci_legacy_release ( ehci, pci ); + iounmap ( ehci->regs ); + err_ioremap: + free ( ehci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void ehci_remove ( struct pci_device *pci ) { + struct ehci_device *ehci = pci_get_drvdata ( pci ); + struct usb_bus *bus = ehci->bus; + + unregister_usb_bus ( bus ); + assert ( list_empty ( &ehci->async ) ); + assert ( list_empty ( &ehci->periodic ) ); + free_usb_bus ( bus ); + ehci_reset ( ehci ); + ehci_legacy_release ( ehci, pci ); + iounmap ( ehci->regs ); + free ( ehci ); +} + +/** EHCI PCI device IDs */ +static struct pci_device_id ehci_ids[] = { + PCI_ROM ( 0xffff, 0xffff, "ehci", "EHCI", 0 ), +}; + +/** EHCI PCI driver */ +struct pci_driver ehci_driver __pci_driver = { + .ids = ehci_ids, + .id_count = ( sizeof ( ehci_ids ) / sizeof ( ehci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_EHCI ), + .probe = ehci_probe, + .remove = ehci_remove, +}; + +/** + * Prepare for exit + * + * @v booting System is shutting down for OS boot + */ +static void ehci_shutdown ( int booting ) { + /* If we are shutting down to boot an OS, then prevent the + * release of ownership back to BIOS. + */ + ehci_legacy_prevent_release = booting; +} + +/** Startup/shutdown function */ +struct startup_fn ehci_startup __startup_fn ( STARTUP_LATE ) = { + .shutdown = ehci_shutdown, +}; diff --git a/roms/ipxe/src/drivers/usb/ehci.h b/roms/ipxe/src/drivers/usb/ehci.h new file mode 100644 index 000000000..42e282e92 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/ehci.h @@ -0,0 +1,544 @@ +#ifndef _IPXE_EHCI_H +#define _IPXE_EHCI_H + +/** @file + * + * USB Enhanced Host Controller Interface (EHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/pci.h> +#include <ipxe/usb.h> + +/** Minimum alignment required for data structures + * + * With the exception of the periodic frame list (which is + * page-aligned), data structures used by EHCI generally require + * 32-byte alignment and must not cross a 4kB page boundary. We + * simplify this requirement by aligning each structure on its own + * size, with a minimum of a 32 byte alignment. + */ +#define EHCI_MIN_ALIGN 32 + +/** Maximum transfer size + * + * EHCI allows for transfers of up to 20kB with page-alignment, or + * 16kB with arbitrary alignment. + */ +#define EHCI_MTU 16384 + +/** Page-alignment required for some data structures */ +#define EHCI_PAGE_ALIGN 4096 + +/** EHCI PCI BAR */ +#define EHCI_BAR PCI_BASE_ADDRESS_0 + +/** Capability register length */ +#define EHCI_CAP_CAPLENGTH 0x00 + +/** Host controller interface version number */ +#define EHCI_CAP_HCIVERSION 0x02 + +/** Structural parameters */ +#define EHCI_CAP_HCSPARAMS 0x04 + +/** Number of ports */ +#define EHCI_HCSPARAMS_PORTS(params) ( ( (params) >> 0 ) & 0x0f ) + +/** Capability parameters */ +#define EHCI_CAP_HCCPARAMS 0x08 + +/** 64-bit addressing capability */ +#define EHCI_HCCPARAMS_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) + +/** Programmable frame list flag */ +#define EHCI_HCCPARAMS_FLSIZE(params) ( ( (params) >> 1 ) & 0x1 ) + +/** EHCI extended capabilities pointer */ +#define EHCI_HCCPARAMS_EECP(params) ( ( ( (params) >> 8 ) & 0xff ) ) + +/** EHCI extended capability ID */ +#define EHCI_EECP_ID(eecp) ( ( (eecp) >> 0 ) & 0xff ) + +/** Next EHCI extended capability pointer */ +#define EHCI_EECP_NEXT(eecp) ( ( ( (eecp) >> 8 ) & 0xff ) ) + +/** USB legacy support extended capability */ +#define EHCI_EECP_ID_LEGACY 1 + +/** USB legacy support BIOS owned semaphore */ +#define EHCI_USBLEGSUP_BIOS 0x02 + +/** USB legacy support BIOS ownership flag */ +#define EHCI_USBLEGSUP_BIOS_OWNED 0x01 + +/** USB legacy support OS owned semaphore */ +#define EHCI_USBLEGSUP_OS 0x03 + +/** USB legacy support OS ownership flag */ +#define EHCI_USBLEGSUP_OS_OWNED 0x01 + +/** USB legacy support control/status */ +#define EHCI_USBLEGSUP_CTLSTS 0x04 + +/** USB command register */ +#define EHCI_OP_USBCMD 0x00 + +/** Run/stop */ +#define EHCI_USBCMD_RUN 0x00000001UL + +/** Host controller reset */ +#define EHCI_USBCMD_HCRST 0x00000002UL + +/** Frame list size */ +#define EHCI_USBCMD_FLSIZE(flsize) ( (flsize) << 2 ) + +/** Frame list size mask */ +#define EHCI_USBCMD_FLSIZE_MASK EHCI_USBCMD_FLSIZE ( 3 ) + +/** Default frame list size */ +#define EHCI_FLSIZE_DEFAULT 0 + +/** Smallest allowed frame list size */ +#define EHCI_FLSIZE_SMALL 2 + +/** Number of elements in frame list */ +#define EHCI_PERIODIC_FRAMES(flsize) ( 1024 >> (flsize) ) + +/** Periodic schedule enable */ +#define EHCI_USBCMD_PERIODIC 0x00000010UL + +/** Asynchronous schedule enable */ +#define EHCI_USBCMD_ASYNC 0x00000020UL + +/** Asyncchronous schedule advance doorbell */ +#define EHCI_USBCMD_ASYNC_ADVANCE 0x000040UL + +/** USB status register */ +#define EHCI_OP_USBSTS 0x04 + +/** USB interrupt */ +#define EHCI_USBSTS_USBINT 0x00000001UL + +/** USB error interrupt */ +#define EHCI_USBSTS_USBERRINT 0x00000002UL + +/** Port change detect */ +#define EHCI_USBSTS_PORT 0x00000004UL + +/** Frame list rollover */ +#define EHCI_USBSTS_ROLLOVER 0x00000008UL + +/** Host system error */ +#define EHCI_USBSTS_SYSERR 0x00000010UL + +/** Asynchronous schedule advanced */ +#define EHCI_USBSTS_ASYNC_ADVANCE 0x00000020UL + +/** Periodic schedule enabled */ +#define EHCI_USBSTS_PERIODIC 0x00004000UL + +/** Asynchronous schedule enabled */ +#define EHCI_USBSTS_ASYNC 0x00008000UL + +/** Host controller halted */ +#define EHCI_USBSTS_HCH 0x00001000UL + +/** USB status change mask */ +#define EHCI_USBSTS_CHANGE \ + ( EHCI_USBSTS_USBINT | EHCI_USBSTS_USBERRINT | \ + EHCI_USBSTS_PORT | EHCI_USBSTS_ROLLOVER | \ + EHCI_USBSTS_SYSERR | EHCI_USBSTS_ASYNC_ADVANCE ) + +/** USB interrupt enable register */ +#define EHCI_OP_USBINTR 0x08 + +/** Frame index register */ +#define EHCI_OP_FRINDEX 0x0c + +/** Control data structure segment register */ +#define EHCI_OP_CTRLDSSEGMENT 0x10 + +/** Periodic frame list base address register */ +#define EHCI_OP_PERIODICLISTBASE 0x14 + +/** Current asynchronous list address register */ +#define EHCI_OP_ASYNCLISTADDR 0x18 + +/** Configure flag register */ +#define EHCI_OP_CONFIGFLAG 0x40 + +/** Configure flag */ +#define EHCI_CONFIGFLAG_CF 0x00000001UL + +/** Port status and control register */ +#define EHCI_OP_PORTSC(port) ( 0x40 + ( (port) << 2 ) ) + +/** Current connect status */ +#define EHCI_PORTSC_CCS 0x00000001UL + +/** Connect status change */ +#define EHCI_PORTSC_CSC 0x00000002UL + +/** Port enabled */ +#define EHCI_PORTSC_PED 0x00000004UL + +/** Port enabled/disabled change */ +#define EHCI_PORTSC_PEC 0x00000008UL + +/** Over-current change */ +#define EHCI_PORTSC_OCC 0x00000020UL + +/** Port reset */ +#define EHCI_PORTSC_PR 0x00000100UL + +/** Line status */ +#define EHCI_PORTSC_LINE_STATUS(portsc) ( ( (portsc) >> 10 ) & 0x3 ) + +/** Line status: low-speed device */ +#define EHCI_PORTSC_LINE_STATUS_LOW 0x1 + +/** Port power */ +#define EHCI_PORTSC_PP 0x00001000UL + +/** Port owner */ +#define EHCI_PORTSC_OWNER 0x00002000UL + +/** Port status change mask */ +#define EHCI_PORTSC_CHANGE \ + ( EHCI_PORTSC_CSC | EHCI_PORTSC_PEC | EHCI_PORTSC_OCC ) + +/** List terminator */ +#define EHCI_LINK_TERMINATE 0x00000001UL + +/** Frame list type */ +#define EHCI_LINK_TYPE(type) ( (type) << 1 ) + +/** Queue head type */ +#define EHCI_LINK_TYPE_QH EHCI_LINK_TYPE ( 1 ) + +/** A periodic frame list entry */ +struct ehci_periodic_frame { + /** First queue head */ + uint32_t link; +} __attribute__ (( packed )); + +/** A transfer descriptor */ +struct ehci_transfer_descriptor { + /** Next transfer descriptor */ + uint32_t next; + /** Alternate next transfer descriptor */ + uint32_t alt; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Transfer length */ + uint16_t len; + /** Buffer pointers (low 32 bits) */ + uint32_t low[5]; + /** Extended buffer pointers (high 32 bits) */ + uint32_t high[5]; + /** Reserved */ + uint8_t reserved[12]; +} __attribute__ (( packed )); + +/** Transaction error */ +#define EHCI_STATUS_XACT_ERR 0x08 + +/** Babble detected */ +#define EHCI_STATUS_BABBLE 0x10 + +/** Data buffer error */ +#define EHCI_STATUS_BUFFER 0x20 + +/** Halted */ +#define EHCI_STATUS_HALTED 0x40 + +/** Active */ +#define EHCI_STATUS_ACTIVE 0x80 + +/** PID code */ +#define EHCI_FL_PID(code) ( (code) << 0 ) + +/** OUT token */ +#define EHCI_FL_PID_OUT EHCI_FL_PID ( 0 ) + +/** IN token */ +#define EHCI_FL_PID_IN EHCI_FL_PID ( 1 ) + +/** SETUP token */ +#define EHCI_FL_PID_SETUP EHCI_FL_PID ( 2 ) + +/** Error counter */ +#define EHCI_FL_CERR( count ) ( (count) << 2 ) + +/** Error counter maximum value */ +#define EHCI_FL_CERR_MAX EHCI_FL_CERR ( 3 ) + +/** Interrupt on completion */ +#define EHCI_FL_IOC 0x80 + +/** Length mask */ +#define EHCI_LEN_MASK 0x7fff + +/** Data toggle */ +#define EHCI_LEN_TOGGLE 0x8000 + +/** A queue head */ +struct ehci_queue_head { + /** Horizontal link pointer */ + uint32_t link; + /** Endpoint characteristics */ + uint32_t chr; + /** Endpoint capabilities */ + uint32_t cap; + /** Current transfer descriptor */ + uint32_t current; + /** Transfer descriptor cache */ + struct ehci_transfer_descriptor cache; +} __attribute__ (( packed )); + +/** Device address */ +#define EHCI_CHR_ADDRESS( address ) ( (address) << 0 ) + +/** Endpoint number */ +#define EHCI_CHR_ENDPOINT( address ) ( ( (address) & 0xf ) << 8 ) + +/** Endpoint speed */ +#define EHCI_CHR_EPS( eps ) ( (eps) << 12 ) + +/** Full-speed endpoint */ +#define EHCI_CHR_EPS_FULL EHCI_CHR_EPS ( 0 ) + +/** Low-speed endpoint */ +#define EHCI_CHR_EPS_LOW EHCI_CHR_EPS ( 1 ) + +/** High-speed endpoint */ +#define EHCI_CHR_EPS_HIGH EHCI_CHR_EPS ( 2 ) + +/** Explicit data toggles */ +#define EHCI_CHR_TOGGLE 0x00004000UL + +/** Head of reclamation list flag */ +#define EHCI_CHR_HEAD 0x00008000UL + +/** Maximum packet length */ +#define EHCI_CHR_MAX_LEN( len ) ( (len) << 16 ) + +/** Control endpoint flag */ +#define EHCI_CHR_CONTROL 0x08000000UL + +/** Interrupt schedule mask */ +#define EHCI_CAP_INTR_SCHED( uframe ) ( 1 << ( (uframe) + 0 ) ) + +/** Split completion schedule mask */ +#define EHCI_CAP_SPLIT_SCHED( uframe ) ( 1 << ( (uframe) + 8 ) ) + +/** Default split completion schedule mask + * + * We schedule all split starts in microframe 0, on the assumption + * that we will never have to deal with more than sixteen actively + * interrupting devices via the same transaction translator. We + * schedule split completions for all remaining microframes after + * microframe 1 (in which the low-speed or full-speed transaction is + * assumed to execute). This is a very crude approximation designed + * to avoid the need for calculating exactly when low-speed and + * full-speed transactions will execute. Since we only ever deal with + * interrupt endpoints (rather than isochronous endpoints), the volume + * of periodic traffic is extremely low, and this approximation should + * remain valid. + */ +#define EHCI_CAP_SPLIT_SCHED_DEFAULT \ + ( EHCI_CAP_SPLIT_SCHED ( 2 ) | EHCI_CAP_SPLIT_SCHED ( 3 ) | \ + EHCI_CAP_SPLIT_SCHED ( 4 ) | EHCI_CAP_SPLIT_SCHED ( 5 ) | \ + EHCI_CAP_SPLIT_SCHED ( 6 ) | EHCI_CAP_SPLIT_SCHED ( 7 ) ) + +/** Transaction translator hub address */ +#define EHCI_CAP_TT_HUB( address ) ( (address) << 16 ) + +/** Transaction translator port number */ +#define EHCI_CAP_TT_PORT( port ) ( (port) << 23 ) + +/** High-bandwidth pipe multiplier */ +#define EHCI_CAP_MULT( mult ) ( (mult) << 30 ) + +/** A transfer descriptor ring */ +struct ehci_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + + /** Residual untransferred data */ + size_t residual; + + /** I/O buffers */ + struct io_buffer **iobuf; + + /** Queue head */ + struct ehci_queue_head *head; + /** Transfer descriptors */ + struct ehci_transfer_descriptor *desc; +}; + +/** Number of transfer descriptors in a ring + * + * This is a policy decision. + */ +#define EHCI_RING_COUNT 64 + +/** + * Calculate space used in transfer descriptor ring + * + * @v ring Transfer descriptor ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +ehci_ring_fill ( struct ehci_ring *ring ) { + unsigned int fill; + + fill = ( ring->prod - ring->cons ); + assert ( fill <= EHCI_RING_COUNT ); + return fill; +} + +/** + * Calculate space remaining in transfer descriptor ring + * + * @v ring Transfer descriptor ring + * @ret remaining Number of entries remaining + */ +static inline __attribute__ (( always_inline )) unsigned int +ehci_ring_remaining ( struct ehci_ring *ring ) { + unsigned int fill = ehci_ring_fill ( ring ); + + return ( EHCI_RING_COUNT - fill ); +} + +/** Time to delay after enabling power to a port + * + * This is not mandated by EHCI; we use the value given for xHCI. + */ +#define EHCI_PORT_POWER_DELAY_MS 20 + +/** Time to delay after releasing ownership of a port + * + * This is a policy decision. + */ +#define EHCI_DISOWN_DELAY_MS 100 + +/** Maximum time to wait for BIOS to release ownership + * + * This is a policy decision. + */ +#define EHCI_USBLEGSUP_MAX_WAIT_MS 100 + +/** Maximum time to wait for asynchronous schedule to advance + * + * This is a policy decision. + */ +#define EHCI_ASYNC_ADVANCE_MAX_WAIT_MS 100 + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define EHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define EHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a port reset to complete + * + * This is a policy decision. + */ +#define EHCI_PORT_RESET_MAX_WAIT_MS 500 + +/** An EHCI transfer */ +struct ehci_transfer { + /** Data buffer */ + void *data; + /** Length */ + size_t len; + /** Flags + * + * This is the bitwise OR of zero or more EHCI_FL_XXX values. + * The low 8 bits are copied to the flags byte within the + * transfer descriptor; the remaining bits hold flags + * meaningful only to our driver code. + */ + unsigned int flags; +}; + +/** Set initial data toggle */ +#define EHCI_FL_TOGGLE 0x8000 + +/** An EHCI device */ +struct ehci_device { + /** Registers */ + void *regs; + /** Name */ + const char *name; + + /** Capability registers */ + void *cap; + /** Operational registers */ + void *op; + + /** Number of ports */ + unsigned int ports; + /** 64-bit addressing capability */ + int addr64; + /** Frame list size */ + unsigned int flsize; + /** EHCI extended capabilities offset */ + unsigned int eecp; + + /** USB legacy support capability (if present and enabled) */ + unsigned int legacy; + + /** Control data structure segment */ + uint32_t ctrldssegment; + /** Asynchronous queue head */ + struct ehci_queue_head *head; + /** Periodic frame list */ + struct ehci_periodic_frame *frame; + + /** List of all endpoints */ + struct list_head endpoints; + /** Asynchronous schedule */ + struct list_head async; + /** Periodic schedule + * + * Listed in decreasing order of endpoint interval. + */ + struct list_head periodic; + + /** USB bus */ + struct usb_bus *bus; +}; + +/** An EHCI endpoint */ +struct ehci_endpoint { + /** EHCI device */ + struct ehci_device *ehci; + /** USB endpoint */ + struct usb_endpoint *ep; + /** List of all endpoints */ + struct list_head list; + /** Endpoint schedule */ + struct list_head schedule; + + /** Transfer descriptor ring */ + struct ehci_ring ring; +}; + +extern unsigned int ehci_companion ( struct pci_device *pci ); + +#endif /* _IPXE_EHCI_H */ diff --git a/roms/ipxe/src/drivers/usb/uhci.c b/roms/ipxe/src/drivers/usb/uhci.c new file mode 100644 index 000000000..b6bb92560 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/uhci.c @@ -0,0 +1,1577 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <strings.h> +#include <unistd.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/malloc.h> +#include <ipxe/pci.h> +#include <ipxe/usb.h> +#include "ehci.h" +#include "uhci.h" + +/** @file + * + * USB Universal Host Controller Interface (UHCI) driver + * + */ + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Check that address is reachable + * + * @v addr Address + * @v len Length + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline)) int +uhci_reachable ( void *addr, size_t len ) { + physaddr_t phys = virt_to_phys ( addr ); + + /* Always reachable in a 32-bit build */ + if ( sizeof ( physaddr_t ) <= sizeof ( uint32_t ) ) + return 0; + + /* Reachable if below 4GB */ + if ( ( ( phys + len - 1 ) & ~0xffffffffULL ) == 0 ) + return 0; + + return -ENOTSUP; +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start UHCI device + * + * @v uhci UHCI device + */ +static void uhci_run ( struct uhci_device *uhci ) { + uint16_t usbcmd; + + /* Set run/stop bit */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + usbcmd |= ( UHCI_USBCMD_RUN | UHCI_USBCMD_MAX64 ); + outw ( usbcmd, uhci->regs + UHCI_USBCMD ); +} + +/** + * Stop UHCI device + * + * @v uhci UHCI device + * @ret rc Return status code + */ +static int uhci_stop ( struct uhci_device *uhci ) { + uint16_t usbcmd; + uint16_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + usbcmd &= ~UHCI_USBCMD_RUN; + outw ( usbcmd, uhci->regs + UHCI_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < UHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = inw ( uhci->regs + UHCI_USBSTS ); + if ( usbsts & UHCI_USBSTS_HCHALTED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s timed out waiting for stop\n", uhci->name ); + return -ETIMEDOUT; +} + +/** + * Reset UHCI device + * + * @v uhci UHCI device + * @ret rc Return status code + */ +static int uhci_reset ( struct uhci_device *uhci ) { + uint16_t usbcmd; + unsigned int i; + int rc; + + /* The UHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = uhci_stop ( uhci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + outw ( UHCI_USBCMD_HCRESET, uhci->regs + UHCI_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < UHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = inw ( uhci->regs + UHCI_USBCMD ); + if ( ! ( usbcmd & UHCI_USBCMD_HCRESET ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s timed out waiting for reset\n", uhci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer descriptor rings + * + ****************************************************************************** + */ + +/** + * Allocate transfer ring + * + * @v ring Transfer ring + * @ret rc Return status code + */ +static int uhci_ring_alloc ( struct uhci_ring *ring ) { + int rc; + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + + /* Allocate queue head */ + ring->head = malloc_dma ( sizeof ( *ring->head ), UHCI_ALIGN ); + if ( ! ring->head ) { + rc = -ENOMEM; + goto err_alloc; + } + if ( ( rc = uhci_reachable ( ring->head, + sizeof ( *ring->head ) ) ) != 0 ) + goto err_unreachable; + + /* Initialise queue head */ + ring->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + + return 0; + + err_unreachable: + free_dma ( ring->head, sizeof ( *ring->head ) ); + err_alloc: + return rc; +} + +/** + * Free transfer ring + * + * @v ring Transfer ring + */ +static void uhci_ring_free ( struct uhci_ring *ring ) { + unsigned int i; + + /* Sanity checks */ + assert ( uhci_ring_fill ( ring ) == 0 ); + for ( i = 0 ; i < UHCI_RING_COUNT ; i++ ) + assert ( ring->xfer[i] == NULL ); + + /* Free queue head */ + free_dma ( ring->head, sizeof ( *ring->head ) ); +} + +/** + * Enqueue new transfer + * + * @v ring Transfer ring + * @v iobuf I/O buffer + * @v count Number of descriptors + * @ret rc Return status code + */ +static int uhci_enqueue ( struct uhci_ring *ring, struct io_buffer *iobuf, + unsigned int count ) { + struct uhci_transfer *xfer; + struct uhci_transfer *end; + struct uhci_transfer_descriptor *desc; + unsigned int index = ( ring->prod % UHCI_RING_COUNT ); + uint32_t link; + size_t len; + int rc; + + /* Sanity check */ + assert ( count > 0 ); + assert ( iobuf != NULL ); + + /* Check for space in ring */ + if ( ! uhci_ring_remaining ( ring ) ) { + rc = -ENOBUFS; + goto err_ring_full; + } + + /* Check for reachability of I/O buffer */ + if ( ( rc = uhci_reachable ( iobuf->data, iob_len ( iobuf ) ) ) != 0 ) + goto err_unreachable_iobuf; + + /* Allocate transfer */ + xfer = malloc ( sizeof ( *xfer ) ); + if ( ! xfer ) { + rc = -ENOMEM; + goto err_alloc_xfer; + } + + /* Initialise transfer */ + xfer->prod = 0; + xfer->cons = 0; + xfer->len = 0; + xfer->iobuf = iobuf; + + /* Allocate transfer descriptors */ + len = ( count * sizeof ( xfer->desc[0] ) ); + xfer->desc = malloc_dma ( len, UHCI_ALIGN ); + if ( ! xfer->desc ) { + rc = -ENOMEM; + goto err_alloc_desc; + } + if ( ( rc = uhci_reachable ( xfer->desc, len ) ) != 0 ) + goto err_unreachable_desc; + + /* Initialise transfer descriptors */ + memset ( xfer->desc, 0, len ); + desc = xfer->desc; + for ( ; --count ; desc++ ) { + link = ( virt_to_phys ( desc + 1 ) | UHCI_LINK_DEPTH_FIRST ); + desc->link = cpu_to_le32 ( link ); + desc->flags = ring->flags; + } + desc->link = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + desc->flags = ( ring->flags | UHCI_FL_IOC ); + + /* Add to ring */ + wmb(); + link = virt_to_phys ( xfer->desc ); + if ( uhci_ring_fill ( ring ) > 0 ) { + end = ring->end; + end->desc[ end->prod - 1 ].link = cpu_to_le32 ( link ); + } else { + ring->head->current = cpu_to_le32 ( link ); + } + assert ( ring->xfer[index] == NULL ); + ring->xfer[index] = xfer; + ring->end = xfer; + ring->prod++; + + return 0; + + err_unreachable_desc: + free_dma ( xfer->desc, len ); + err_alloc_desc: + free ( xfer ); + err_alloc_xfer: + err_unreachable_iobuf: + err_ring_full: + return rc; +} + +/** + * Describe transfer + * + * @v ring Transfer ring + * @v data Data + * @v len Length of data + * @v pid Packet ID + */ +static void uhci_describe ( struct uhci_ring *ring, void *data, + size_t len, uint8_t pid ) { + struct uhci_transfer *xfer = ring->end; + struct uhci_transfer_descriptor *desc; + size_t frag_len; + uint32_t control; + + do { + /* Calculate fragment length */ + frag_len = len; + if ( frag_len > ring->mtu ) + frag_len = ring->mtu; + + /* Populate descriptor */ + desc = &xfer->desc[xfer->prod++]; + if ( pid == USB_PID_IN ) + desc->flags |= UHCI_FL_SPD; + control = ( ring->control | UHCI_CONTROL_PID ( pid ) | + UHCI_CONTROL_LEN ( frag_len ) ); + desc->control = cpu_to_le32 ( control ); + if ( data ) + desc->data = virt_to_phys ( data ); + wmb(); + desc->status = UHCI_STATUS_ACTIVE; + + /* Update data toggle */ + ring->control ^= UHCI_CONTROL_TOGGLE; + + /* Move to next descriptor */ + data += frag_len; + len -= frag_len; + + } while ( len ); +} + +/** + * Dequeue transfer + * + * @v ring Transfer ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * uhci_dequeue ( struct uhci_ring *ring ) { + unsigned int index = ( ring->cons % UHCI_RING_COUNT ); + struct io_buffer *iobuf; + struct uhci_transfer *xfer; + size_t len; + + /* Sanity checks */ + assert ( uhci_ring_fill ( ring ) > 0 ); + + /* Consume transfer */ + xfer = ring->xfer[index]; + assert ( xfer != NULL ); + assert ( xfer->desc != NULL ); + iobuf = xfer->iobuf; + assert ( iobuf != NULL ); + ring->xfer[index] = NULL; + ring->cons++; + + /* Free transfer descriptors */ + len = ( xfer->prod * sizeof ( xfer->desc[0] ) ); + free_dma ( xfer->desc, len ); + + /* Free transfer */ + free ( xfer ); + + return iobuf; +} + +/** + * Restart ring + * + * @v ring Transfer ring + * @v toggle Expected data toggle for next descriptor + */ +static void uhci_restart ( struct uhci_ring *ring, uint32_t toggle ) { + struct uhci_transfer *xfer; + struct uhci_transfer_descriptor *desc; + struct uhci_transfer_descriptor *first; + uint32_t link; + unsigned int i; + unsigned int j; + + /* Sanity check */ + assert ( ring->head->current == cpu_to_le32 ( UHCI_LINK_TERMINATE ) ); + + /* If ring is empty, then just update the data toggle for the + * next descriptor. + */ + if ( uhci_ring_fill ( ring ) == 0 ) { + ring->control &= ~UHCI_CONTROL_TOGGLE; + ring->control |= toggle; + return; + } + + /* If expected toggle does not match the toggle in the first + * unconsumed descriptor, then invert all toggles. + */ + xfer = ring->xfer[ ring->cons % UHCI_RING_COUNT ]; + assert ( xfer != NULL ); + assert ( xfer->cons == 0 ); + first = &xfer->desc[0]; + if ( ( le32_to_cpu ( first->control ) ^ toggle ) & UHCI_CONTROL_TOGGLE){ + + /* Invert toggle on all unconsumed transfer descriptors */ + for ( i = ring->cons ; i != ring->prod ; i++ ) { + xfer = ring->xfer[ i % UHCI_RING_COUNT ]; + assert ( xfer != NULL ); + assert ( xfer->cons == 0 ); + for ( j = 0 ; j < xfer->prod ; j++ ) { + desc = &xfer->desc[j]; + desc->control ^= + cpu_to_le32 ( UHCI_CONTROL_TOGGLE ); + } + } + + /* Invert toggle for next descriptor to be enqueued */ + ring->control ^= UHCI_CONTROL_TOGGLE; + } + + /* Restart ring at first unconsumed transfer */ + link = virt_to_phys ( first ); + wmb(); + ring->head->current = cpu_to_le32 ( link ); +} + +/****************************************************************************** + * + * Schedule management + * + ****************************************************************************** + */ + +/** + * Get link value for a queue head + * + * @v queue Queue head + * @ret link Link value + */ +static inline uint32_t uhci_link_qh ( struct uhci_queue_head *queue ) { + + return ( virt_to_phys ( queue ) | UHCI_LINK_TYPE_QH ); +} + +/** + * (Re)build asynchronous schedule + * + * @v uhci UHCI device + */ +static void uhci_async_schedule ( struct uhci_device *uhci ) { + struct uhci_endpoint *endpoint; + struct uhci_queue_head *queue; + uint32_t end; + uint32_t link; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + link = end = uhci_link_qh ( uhci->head ); + list_for_each_entry_reverse ( endpoint, &uhci->async, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + link = uhci_link_qh ( queue ); + } + if ( link == end ) + link = UHCI_LINK_TERMINATE; + uhci->head->link = cpu_to_le32 ( link ); + wmb(); +} + +/** + * Add endpoint to asynchronous schedule + * + * @v endpoint Endpoint + */ +static void uhci_async_add ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Add to end of schedule */ + list_add_tail ( &endpoint->schedule, &uhci->async ); + + /* Rebuild schedule */ + uhci_async_schedule ( uhci ); +} + +/** + * Remove endpoint from asynchronous schedule + * + * @v endpoint Endpoint + */ +static void uhci_async_del ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &uhci->async, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + uhci_async_schedule ( uhci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); +} + +/** + * (Re)build periodic schedule + * + * @v uhci UHCI device + */ +static void uhci_periodic_schedule ( struct uhci_device *uhci ) { + struct uhci_endpoint *endpoint; + struct uhci_queue_head *queue; + uint32_t link; + uint32_t end; + unsigned int max_interval; + unsigned int i; + + /* Build schedule in reverse order of execution. Provided + * that we only ever add or remove single endpoints, this can + * safely run concurrently with hardware execution of the + * schedule. + */ + DBGCP ( uhci, "UHCI %s periodic schedule: ", uhci->name ); + link = end = uhci_link_qh ( uhci->head ); + list_for_each_entry_reverse ( endpoint, &uhci->periodic, schedule ) { + queue = endpoint->ring.head; + queue->link = cpu_to_le32 ( link ); + wmb(); + DBGCP ( uhci, "%s%d", ( ( link == end ) ? "" : "<-" ), + endpoint->ep->interval ); + link = uhci_link_qh ( queue ); + } + DBGCP ( uhci, "\n" ); + + /* Populate periodic frame list */ + DBGCP ( uhci, "UHCI %s periodic frame list:", uhci->name ); + for ( i = 0 ; i < UHCI_FRAMES ; i++ ) { + + /* Calculate maximum interval (in microframes) which + * may appear as part of this frame list. + */ + if ( i == 0 ) { + /* Start of list: include all endpoints */ + max_interval = -1U; + } else { + /* Calculate highest power-of-two frame interval */ + max_interval = ( 1 << ( ffs ( i ) - 1 ) ); + /* Convert to microframes */ + max_interval <<= 3; + /* Round up to nearest 2^n-1 */ + max_interval = ( ( max_interval << 1 ) - 1 ); + } + + /* Find first endpoint in schedule satisfying this + * maximum interval constraint. + */ + link = uhci_link_qh ( uhci->head ); + list_for_each_entry ( endpoint, &uhci->periodic, schedule ) { + if ( endpoint->ep->interval <= max_interval ) { + queue = endpoint->ring.head; + link = uhci_link_qh ( queue ); + DBGCP ( uhci, " %d:%d", + i, endpoint->ep->interval ); + break; + } + } + uhci->frame->link[i] = cpu_to_le32 ( link ); + } + wmb(); + DBGCP ( uhci, "\n" ); +} + +/** + * Add endpoint to periodic schedule + * + * @v endpoint Endpoint + */ +static void uhci_periodic_add ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + struct uhci_endpoint *before; + unsigned int interval = endpoint->ep->interval; + + /* Find first endpoint with a smaller interval */ + list_for_each_entry ( before, &uhci->periodic, schedule ) { + if ( before->ep->interval < interval ) + break; + } + list_add_tail ( &endpoint->schedule, &before->schedule ); + + /* Rebuild schedule */ + uhci_periodic_schedule ( uhci ); +} + +/** + * Remove endpoint from periodic schedule + * + * @v endpoint Endpoint + */ +static void uhci_periodic_del ( struct uhci_endpoint *endpoint ) { + struct uhci_device *uhci = endpoint->uhci; + + /* Remove from schedule */ + list_check_contains_entry ( endpoint, &uhci->periodic, schedule ); + list_del ( &endpoint->schedule ); + + /* Rebuild schedule */ + uhci_periodic_schedule ( uhci ); + + /* Delay for a whole USB frame (with a 100% safety margin) */ + mdelay ( 2 ); +} + +/** + * Add endpoint to appropriate schedule + * + * @v endpoint Endpoint + */ +static void uhci_schedule_add ( struct uhci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + uhci_periodic_add ( endpoint ); + } else { + uhci_async_add ( endpoint ); + } +} + +/** + * Remove endpoint from appropriate schedule + * + * @v endpoint Endpoint + */ +static void uhci_schedule_del ( struct uhci_endpoint *endpoint ) { + struct usb_endpoint *ep = endpoint->ep; + unsigned int attr = ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + + if ( attr == USB_ENDPOINT_ATTR_INTERRUPT ) { + uhci_periodic_del ( endpoint ); + } else { + uhci_async_del ( endpoint ); + } +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct uhci_endpoint *endpoint; + int rc; + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + endpoint->uhci = uhci; + endpoint->ep = ep; + usb_endpoint_set_hostdata ( ep, endpoint ); + + /* Initialise descriptor ring */ + if ( ( rc = uhci_ring_alloc ( &endpoint->ring ) ) != 0 ) + goto err_ring_alloc; + endpoint->ring.mtu = ep->mtu; + endpoint->ring.flags = UHCI_FL_CERR_MAX; + if ( usb->port->speed < USB_SPEED_FULL ) + endpoint->ring.flags |= UHCI_FL_LS; + endpoint->ring.control = ( UHCI_CONTROL_DEVICE ( usb->address ) | + UHCI_CONTROL_ENDPOINT ( ep->address ) ); + + /* Add to list of endpoints */ + list_add_tail ( &endpoint->list, &uhci->endpoints ); + + /* Add to schedule */ + uhci_schedule_add ( endpoint ); + + return 0; + + uhci_ring_free ( &endpoint->ring ); + err_ring_alloc: + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void uhci_endpoint_close ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct io_buffer *iobuf; + + /* Remove from schedule */ + uhci_schedule_del ( endpoint ); + + /* Cancel any incomplete transfers */ + while ( uhci_ring_fill ( &endpoint->ring ) ) { + iobuf = uhci_dequeue ( &endpoint->ring ); + if ( iobuf ) + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Remove from list of endpoints */ + list_del ( &endpoint->list ); + + /* Free descriptor ring */ + uhci_ring_free ( &endpoint->ring ); + + /* Free endpoint */ + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_reset ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + + /* Restart ring */ + uhci_restart ( ring, 0 ); + + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_endpoint_mtu ( struct usb_endpoint *ep ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + + /* Update endpoint MTU */ + endpoint->ring.mtu = ep->mtu; + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int uhci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + struct usb_setup_packet *packet; + unsigned int count; + size_t len; + int input; + int rc; + + /* Calculate number of descriptors */ + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + len = ( iob_len ( iobuf ) - sizeof ( *packet ) ); + count = ( 1 /* setup stage */ + + ( ( len + ring->mtu - 1 ) / ring->mtu ) /* data stage */ + + 1 /* status stage */ ); + + /* Enqueue transfer */ + if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) + return rc; + + /* Describe setup stage */ + packet = iobuf->data; + ring->control &= ~UHCI_CONTROL_TOGGLE; + uhci_describe ( ring, packet, sizeof ( *packet ), USB_PID_SETUP ); + iob_pull ( iobuf, sizeof ( *packet ) ); + + /* Describe data stage, if applicable */ + assert ( ring->control & UHCI_CONTROL_TOGGLE ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) { + uhci_describe ( ring, iobuf->data, len, + ( input ? USB_PID_IN : USB_PID_OUT ) ); + } + + /* Describe status stage */ + ring->control |= UHCI_CONTROL_TOGGLE; + uhci_describe ( ring, NULL, 0, + ( ( len && input ) ? USB_PID_OUT : USB_PID_IN ) ); + + /* Sanity check */ + assert ( ring->end->prod == count ); + + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int uhci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct uhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct uhci_ring *ring = &endpoint->ring; + unsigned int count; + size_t len; + int input; + int zlp; + int rc; + + /* Calculate number of descriptors */ + len = iob_len ( iobuf ); + zlp = ( terminate && ( ( len & ( ring->mtu - 1 ) ) == 0 ) ); + count = ( ( ( len + ring->mtu - 1 ) / ring->mtu ) + ( zlp ? 1 : 0 ) ); + + /* Enqueue transfer */ + if ( ( rc = uhci_enqueue ( ring, iobuf, count ) ) != 0 ) + return rc; + + /* Describe data packet */ + input = ( ep->address & USB_DIR_IN ); + uhci_describe ( ring, iobuf->data, len, + ( input ? USB_PID_IN : USB_PID_OUT ) ); + + /* Describe zero-length packet, if applicable */ + if ( zlp ) + uhci_describe ( ring, NULL, 0, USB_PID_OUT ); + + /* Sanity check */ + assert ( ring->end->prod == count ); + + return 0; +} + +/** + * Check if transfer is a message transfer + * + * @v xfer UHCI transfer + * @ret is_message Transfer is a message transfer + */ +static inline int uhci_is_message ( struct uhci_transfer *xfer ) { + struct uhci_transfer_descriptor *desc = &xfer->desc[0]; + + return ( ( desc->control & cpu_to_le32 ( UHCI_CONTROL_PID_MASK ) ) == + cpu_to_le32 ( UHCI_CONTROL_PID ( USB_PID_SETUP ) ) ); +} + +/** + * Poll for completions + * + * @v endpoint Endpoint + */ +static void uhci_endpoint_poll ( struct uhci_endpoint *endpoint ) { + struct uhci_ring *ring = &endpoint->ring; + struct uhci_device *uhci = endpoint->uhci; + struct usb_endpoint *ep = endpoint->ep; + struct usb_device *usb = ep->usb; + struct uhci_transfer *xfer; + struct uhci_transfer_descriptor *desc; + struct io_buffer *iobuf; + unsigned int index; + uint32_t link; + uint32_t toggle; + uint32_t control; + uint16_t actual; + size_t len; + + /* Consume all completed descriptors */ + while ( uhci_ring_fill ( ring ) ) { + + /* Stop if we reach an uncompleted descriptor */ + index = ( ring->cons % UHCI_RING_COUNT ); + xfer = ring->xfer[index]; + assert ( xfer != NULL ); + assert ( xfer->cons < xfer->prod ); + desc = &xfer->desc[xfer->cons]; + rmb(); + if ( desc->status & UHCI_STATUS_ACTIVE ) + break; + control = le32_to_cpu ( desc->control ); + actual = le16_to_cpu ( desc->actual ); + + /* Update data length, if applicable */ + if ( UHCI_DATA_PACKET ( control ) ) + xfer->len += UHCI_ACTUAL_LEN ( actual ); + + /* If we have encountered an error, then deactivate + * the queue head (to prevent further hardware + * accesses to this transfer), consume the transfer, + * and report the error to the USB core. + */ + if ( desc->status & UHCI_STATUS_STALLED ) { + DBGC ( uhci, "UHCI %s %s completion %d.%d failed " + "(status %02x)\n", usb->name, + usb_endpoint_name ( ep ), index, + xfer->cons, desc->status ); + link = UHCI_LINK_TERMINATE; + ring->head->current = cpu_to_le32 ( link ); + wmb(); + iobuf = uhci_dequeue ( ring ); + usb_complete_err ( ep, iobuf, -EIO ); + break; + } + + /* Consume this descriptor */ + xfer->cons++; + + /* Check for short packets */ + if ( UHCI_SHORT_PACKET ( control, actual ) ) { + + /* Sanity checks */ + assert ( desc->flags & UHCI_FL_SPD ); + link = virt_to_phys ( desc ); + assert ( ( le32_to_cpu ( ring->head->current ) & + ~( UHCI_ALIGN - 1 ) ) == link ); + + /* If this is a message transfer, then restart + * at the status stage. + */ + if ( uhci_is_message ( xfer ) ) { + xfer->cons = ( xfer->prod - 1 ); + link = virt_to_phys ( &xfer->desc[xfer->cons] ); + ring->head->current = cpu_to_le32 ( link ); + break; + } + + /* Otherwise, this is a stream transfer. + * First, prevent further hardware access to + * this transfer. + */ + link = UHCI_LINK_TERMINATE; + ring->head->current = cpu_to_le32 ( link ); + wmb(); + + /* Determine expected data toggle for next descriptor */ + toggle = ( ( control ^ UHCI_CONTROL_TOGGLE ) & + UHCI_CONTROL_TOGGLE ); + + /* Consume this transfer */ + len = xfer->len; + iobuf = uhci_dequeue ( ring ); + + /* Update packet length */ + assert ( len <= iob_len ( iobuf ) ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - len ) ); + + /* Restart ring */ + uhci_restart ( ring, toggle ); + + } else if ( xfer->cons == xfer->prod ) { + + /* Completed a transfer: consume it */ + len = xfer->len; + iobuf = uhci_dequeue ( ring ); + assert ( len == iob_len ( iobuf ) ); + + } else { + + /* Not a short packet and not yet complete: + * continue processing. + */ + continue; + } + + /* Report completion to USB core */ + usb_complete ( ep, iobuf ); + } +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int uhci_device_open ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( usb->port->hub->bus ); + + usb_set_hostdata ( usb, uhci ); + return 0; +} + +/** + * Close device + * + * @v usb USB device + */ +static void uhci_device_close ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct usb_bus *bus = uhci->bus; + + /* Free device address, if assigned */ + if ( usb->address ) + usb_free_address ( bus, usb->address ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int uhci_device_address ( struct usb_device *usb ) { + struct uhci_device *uhci = usb_get_hostdata ( usb ); + struct usb_bus *bus = uhci->bus; + struct usb_endpoint *ep0 = usb_endpoint ( usb, USB_EP0_ADDRESS ); + struct uhci_endpoint *endpoint0 = usb_endpoint_get_hostdata ( ep0 ); + int address; + int rc; + + /* Sanity checks */ + assert ( usb->address == 0 ); + assert ( ep0 != NULL ); + + /* Allocate device address */ + address = usb_alloc_address ( bus ); + if ( address < 0 ) { + rc = address; + DBGC ( uhci, "UHCI %s could not allocate address: %s\n", + usb->name, strerror ( rc ) ); + goto err_alloc_address; + } + + /* Set address */ + if ( ( rc = usb_set_address ( usb, address ) ) != 0 ) + goto err_set_address; + + /* Update device address */ + usb->address = address; + endpoint0->ring.control |= UHCI_CONTROL_DEVICE ( address ); + + return 0; + + err_set_address: + usb_free_address ( bus, address ); + err_alloc_address: + return rc; +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int uhci_hub_open ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void uhci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int uhci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, uhci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void uhci_root_close ( struct usb_hub *hub ) { + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + unsigned int i; + + /* Reset port */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + portsc |= UHCI_PORTSC_PR; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_DELAY_MS ); + portsc &= ~UHCI_PORTSC_PR; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Enable port */ + portsc |= UHCI_PORTSC_PED; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + mdelay ( USB_RESET_RECOVER_DELAY_MS ); + + /* Wait for port to become enabled */ + for ( i = 0 ; i < UHCI_PORT_ENABLE_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + if ( portsc & UHCI_PORTSC_PED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( uhci, "UHCI %s-%d timed out waiting for port to enable " + "(status %04x)\n", uhci->name, port->address, portsc ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + + /* Disable port */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + portsc &= ~UHCI_PORTSC_PED; + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int uhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + struct pci_device pci; + uint16_t portsc; + unsigned int speed; + + /* Read port status */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + if ( ! ( portsc & UHCI_PORTSC_CCS ) ) { + /* Port not connected */ + speed = USB_SPEED_NONE; + } else if ( uhci->companion && + ! find_usb_bus_by_location ( BUS_TYPE_PCI, + uhci->companion ) ) { + /* Defer connection detection until companion + * controller has been enumerated. + */ + pci_init ( &pci, uhci->companion ); + DBGC ( uhci, "UHCI %s-%d deferring for companion " PCI_FMT "\n", + uhci->name, port->address, PCI_ARGS ( &pci ) ); + speed = USB_SPEED_NONE; + } else if ( portsc & UHCI_PORTSC_LS ) { + /* Low-speed device */ + speed = USB_SPEED_LOW; + } else { + /* Full-speed device */ + speed = USB_SPEED_FULL; + } + port->speed = speed; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int uhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( uhci, "UHCI %s-%d nonsensical CLEAR_TT for %s %s\n", uhci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/** + * Poll for port status changes + * + * @v hub USB hub + * @v port USB port + */ +static void uhci_root_poll ( struct usb_hub *hub, struct usb_port *port ) { + struct uhci_device *uhci = usb_hub_get_drvdata ( hub ); + uint16_t portsc; + uint16_t change; + + /* Do nothing unless something has changed */ + portsc = inw ( uhci->regs + UHCI_PORTSC ( port->address ) ); + change = ( portsc & UHCI_PORTSC_CHANGE ); + if ( ! change ) + return; + + /* Record disconnections and clear changes */ + port->disconnected |= ( portsc & UHCI_PORTSC_CSC ); + outw ( portsc, uhci->regs + UHCI_PORTSC ( port->address ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int uhci_bus_open ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + int rc; + + /* Sanity checks */ + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + + /* Allocate and initialise asynchronous queue head */ + uhci->head = malloc_dma ( sizeof ( *uhci->head ), UHCI_ALIGN ); + if ( ! uhci->head ) { + rc = -ENOMEM; + goto err_alloc_head; + } + if ( ( rc = uhci_reachable ( uhci->head, sizeof ( *uhci->head ) ) ) !=0) + goto err_unreachable_head; + memset ( uhci->head, 0, sizeof ( *uhci->head ) ); + uhci->head->current = cpu_to_le32 ( UHCI_LINK_TERMINATE ); + uhci_async_schedule ( uhci ); + + /* Allocate periodic frame list */ + uhci->frame = malloc_dma ( sizeof ( *uhci->frame ), + sizeof ( *uhci->frame ) ); + if ( ! uhci->frame ) { + rc = -ENOMEM; + goto err_alloc_frame; + } + if ( ( rc = uhci_reachable ( uhci->frame, + sizeof ( *uhci->frame ) ) ) != 0 ) + goto err_unreachable_frame; + uhci_periodic_schedule ( uhci ); + outl ( virt_to_phys ( uhci->frame ), uhci->regs + UHCI_FLBASEADD ); + + /* Start controller */ + uhci_run ( uhci ); + + return 0; + + uhci_stop ( uhci ); + err_unreachable_frame: + free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); + err_alloc_frame: + err_unreachable_head: + free_dma ( uhci->head, sizeof ( *uhci->head ) ); + err_alloc_head: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void uhci_bus_close ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + + /* Sanity checks */ + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + + /* Stop controller */ + uhci_stop ( uhci ); + + /* Free periodic frame list */ + free_dma ( uhci->frame, sizeof ( *uhci->frame ) ); + + /* Free asynchronous schedule */ + free_dma ( uhci->head, sizeof ( *uhci->head ) ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void uhci_bus_poll ( struct usb_bus *bus ) { + struct uhci_device *uhci = usb_bus_get_hostdata ( bus ); + struct usb_hub *hub = bus->hub; + struct uhci_endpoint *endpoint; + unsigned int i; + + /* UHCI defers interrupts (including short packet detection) + * until the end of the frame. This can result in bulk IN + * endpoints remaining halted for much of the time, waiting + * for software action to reset the data toggles. We + * therefore ignore USBSTS and unconditionally poll all + * endpoints for completed transfer descriptors. + * + * As with EHCI, we trust that completion handlers are minimal + * and will not do anything that could plausibly affect the + * endpoint list itself. + */ + list_for_each_entry ( endpoint, &uhci->endpoints, list ) + uhci_endpoint_poll ( endpoint ); + + /* UHCI provides no single bit to indicate that a port status + * change has occurred. We therefore unconditionally iterate + * over all ports looking for status changes. + */ + for ( i = 1 ; i <= UHCI_PORTS ; i++ ) + uhci_root_poll ( hub, usb_port ( hub, i ) ); +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations uhci_operations = { + .endpoint = { + .open = uhci_endpoint_open, + .close = uhci_endpoint_close, + .reset = uhci_endpoint_reset, + .mtu = uhci_endpoint_mtu, + .message = uhci_endpoint_message, + .stream = uhci_endpoint_stream, + }, + .device = { + .open = uhci_device_open, + .close = uhci_device_close, + .address = uhci_device_address, + }, + .bus = { + .open = uhci_bus_open, + .close = uhci_bus_close, + .poll = uhci_bus_poll, + }, + .hub = { + .open = uhci_hub_open, + .close = uhci_hub_close, + }, + .root = { + .open = uhci_root_open, + .close = uhci_root_close, + .enable = uhci_root_enable, + .disable = uhci_root_disable, + .speed = uhci_root_speed, + .clear_tt = uhci_root_clear_tt, + }, +}; + +/** + * Locate EHCI companion controller (when no EHCI support is present) + * + * @v pci PCI device + * @ret busdevfn EHCI companion controller bus:dev.fn (if any) + */ +__weak unsigned int ehci_companion ( struct pci_device *pci __unused ) { + return 0; +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int uhci_probe ( struct pci_device *pci ) { + struct uhci_device *uhci; + struct usb_port *port; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + uhci = zalloc ( sizeof ( *uhci ) ); + if ( ! uhci ) { + rc = -ENOMEM; + goto err_alloc; + } + uhci->name = pci->dev.name; + INIT_LIST_HEAD ( &uhci->endpoints ); + INIT_LIST_HEAD ( &uhci->async ); + INIT_LIST_HEAD ( &uhci->periodic ); + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Identify EHCI companion controller, if any */ + uhci->companion = ehci_companion ( pci ); + + /* Claim ownership from BIOS. (There is no release mechanism + * for UHCI.) + */ + pci_write_config_word ( pci, UHCI_USBLEGSUP, UHCI_USBLEGSUP_DEFAULT ); + + /* Map registers */ + uhci->regs = pci->ioaddr; + if ( ! uhci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Reset device */ + if ( ( rc = uhci_reset ( uhci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + uhci->bus = alloc_usb_bus ( &pci->dev, UHCI_PORTS, UHCI_MTU, + &uhci_operations ); + if ( ! uhci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( uhci->bus, uhci ); + usb_hub_set_drvdata ( uhci->bus->hub, uhci ); + + /* Set port protocols */ + for ( i = 1 ; i <= UHCI_PORTS ; i++ ) { + port = usb_port ( uhci->bus->hub, i ); + port->protocol = USB_PROTO_2_0; + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( uhci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, uhci ); + return 0; + + unregister_usb_bus ( uhci->bus ); + err_register: + free_usb_bus ( uhci->bus ); + err_alloc_bus: + uhci_reset ( uhci ); + err_reset: + err_ioremap: + free ( uhci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void uhci_remove ( struct pci_device *pci ) { + struct uhci_device *uhci = pci_get_drvdata ( pci ); + struct usb_bus *bus = uhci->bus; + + unregister_usb_bus ( bus ); + assert ( list_empty ( &uhci->async ) ); + assert ( list_empty ( &uhci->periodic ) ); + free_usb_bus ( bus ); + uhci_reset ( uhci ); + free ( uhci ); +} + +/** UHCI PCI device IDs */ +static struct pci_device_id uhci_ids[] = { + PCI_ROM ( 0xffff, 0xffff, "uhci", "UHCI", 0 ), +}; + +/** UHCI PCI driver */ +struct pci_driver uhci_driver __pci_driver = { + .ids = uhci_ids, + .id_count = ( sizeof ( uhci_ids ) / sizeof ( uhci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_UHCI ), + .probe = uhci_probe, + .remove = uhci_remove, +}; diff --git a/roms/ipxe/src/drivers/usb/uhci.h b/roms/ipxe/src/drivers/usb/uhci.h new file mode 100644 index 000000000..ba4c28f7e --- /dev/null +++ b/roms/ipxe/src/drivers/usb/uhci.h @@ -0,0 +1,350 @@ +#ifndef _IPXE_UHCI_H +#define _IPXE_UHCI_H + +/** @file + * + * USB Universal Host Controller Interface (UHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <assert.h> +#include <ipxe/pci.h> +#include <ipxe/usb.h> + +/** Minimum alignment required for data structures + * + * With the exception of the frame list (which is page-aligned), data + * structures used by UHCI generally require 16-byte alignment. + */ +#define UHCI_ALIGN 16 + +/** Number of ports */ +#define UHCI_PORTS 2 + +/** Maximum transfer size */ +#define UHCI_MTU 1280 + +/** I/O BAR size */ +#define UHCI_BAR_SIZE 0x14 + +/** USB command register */ +#define UHCI_USBCMD 0x00 + +/** Max packet is 64 bytes */ +#define UHCI_USBCMD_MAX64 0x0080 + +/** Host controller reset */ +#define UHCI_USBCMD_HCRESET 0x0002 + +/** Run/stop */ +#define UHCI_USBCMD_RUN 0x0001 + +/** USB status register */ +#define UHCI_USBSTS 0x02 + +/** Host controller halted */ +#define UHCI_USBSTS_HCHALTED 0x0020 + +/** USB interrupt */ +#define UHCI_USBSTS_USBINT 0x0001 + +/** Frame list base address register */ +#define UHCI_FLBASEADD 0x08 + +/** Port status and control register */ +#define UHCI_PORTSC(port) ( 0x0e + ( (port) << 1 ) ) + +/** Port reset */ +#define UHCI_PORTSC_PR 0x0200 + +/** Low-speed device attached */ +#define UHCI_PORTSC_LS 0x0100 + +/** Port enabled/disabled change */ +#define UHCI_PORTSC_PEC 0x0008 + +/** Port enabled */ +#define UHCI_PORTSC_PED 0x0004 + +/** Connect status change */ +#define UHCI_PORTSC_CSC 0x0002 + +/** Current connect status */ +#define UHCI_PORTSC_CCS 0x0001 + +/** Port status change mask */ +#define UHCI_PORTSC_CHANGE ( UHCI_PORTSC_CSC | UHCI_PORTSC_PEC ) + +/** Depth-first processing */ +#define UHCI_LINK_DEPTH_FIRST 0x00000004UL + +/** Queue head type */ +#define UHCI_LINK_TYPE_QH 0x00000002UL + +/** List terminator */ +#define UHCI_LINK_TERMINATE 0x00000001UL + +/** Number of frames in frame list */ +#define UHCI_FRAMES 1024 + +/** A frame list */ +struct uhci_frame_list { + /** Link pointer */ + uint32_t link[UHCI_FRAMES]; +} __attribute__ (( packed )); + +/** A transfer descriptor */ +struct uhci_transfer_descriptor { + /** Link pointer */ + uint32_t link; + /** Actual length */ + uint16_t actual; + /** Status */ + uint8_t status; + /** Flags */ + uint8_t flags; + /** Control */ + uint32_t control; + /** Buffer pointer */ + uint32_t data; +} __attribute__ (( packed )); + +/** Length mask */ +#define UHCI_LEN_MASK 0x7ff + +/** Actual length */ +#define UHCI_ACTUAL_LEN( actual ) ( ( (actual) + 1 ) & UHCI_LEN_MASK ) + +/** Active */ +#define UHCI_STATUS_ACTIVE 0x80 + +/** Stalled */ +#define UHCI_STATUS_STALLED 0x40 + +/** Data buffer error */ +#define UHCI_STATUS_BUFFER 0x20 + +/** Babble detected */ +#define UHCI_STATUS_BABBLE 0x10 + +/** NAK received */ +#define UHCI_STATUS_NAK 0x08 + +/** CRC/timeout error */ +#define UHCI_STATUS_CRC_TIMEOUT 0x04 + +/** Bitstuff error */ +#define UHCI_STATUS_BITSTUFF 0x02 + +/** Short packet detect */ +#define UHCI_FL_SPD 0x20 + +/** Error counter */ +#define UHCI_FL_CERR( count ) ( (count) << 3 ) + +/** Error counter maximum value */ +#define UHCI_FL_CERR_MAX UHCI_FL_CERR ( 3 ) + +/** Low speed device */ +#define UHCI_FL_LS 0x04 + +/** Interrupt on completion */ +#define UHCI_FL_IOC 0x01 + +/** Packet ID */ +#define UHCI_CONTROL_PID( pid ) ( (pid) << 0 ) + +/** Packet ID mask */ +#define UHCI_CONTROL_PID_MASK UHCI_CONTROL_PID ( 0xff ) + +/** Device address */ +#define UHCI_CONTROL_DEVICE( address ) ( (address) << 8 ) + +/** Endpoint address */ +#define UHCI_CONTROL_ENDPOINT( address ) ( (address) << 15 ) + +/** Data toggle */ +#define UHCI_CONTROL_TOGGLE ( 1 << 19 ) + +/** Data length */ +#define UHCI_CONTROL_LEN( len ) ( ( ( (len) - 1 ) & UHCI_LEN_MASK ) << 21 ) + +/** Check for data packet + * + * This check is based on the fact that only USB_PID_SETUP has bit 2 + * set. + */ +#define UHCI_DATA_PACKET( control ) ( ! ( control & 0x04 ) ) + +/** Check for short packet */ +#define UHCI_SHORT_PACKET( control, actual ) \ + ( ( ( (control) >> 21 ) ^ (actual) ) & UHCI_LEN_MASK ) + +/** USB legacy support register (in PCI configuration space) */ +#define UHCI_USBLEGSUP 0xc0 + +/** USB legacy support default value */ +#define UHCI_USBLEGSUP_DEFAULT 0x2000 + +/** A queue head */ +struct uhci_queue_head { + /** Horizontal link pointer */ + uint32_t link; + /** Current transfer descriptor */ + uint32_t current; +} __attribute__ (( packed )); + +/** A single UHCI transfer + * + * UHCI hardware is extremely simple, and requires software to build + * the entire packet schedule (including manually handling all of the + * data toggles). The hardware requires at least 16 bytes of transfer + * descriptors per 64 bytes of transmitted/received data. We allocate + * the transfer descriptors at the time that the transfer is enqueued, + * to avoid the need to allocate unreasonably large blocks when the + * endpoint is opened. + */ +struct uhci_transfer { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Completed data length */ + size_t len; + + /** Transfer descriptors */ + struct uhci_transfer_descriptor *desc; + + /** I/O buffer */ + struct io_buffer *iobuf; +}; + +/** Number of transfer descriptors in a ring + * + * This is a policy decision. + */ +#define UHCI_RING_COUNT 16 + +/** A transfer ring */ +struct uhci_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + + /** Maximum packet length */ + size_t mtu; + /** Base flags + * + * This incorporates the CERR and LS bits + */ + uint8_t flags; + /** Base control word + * + * This incorporates the device address, the endpoint address, + * and the data toggle for the next descriptor to be enqueued. + */ + uint32_t control; + + /** Transfers */ + struct uhci_transfer *xfer[UHCI_RING_COUNT]; + /** End of transfer ring (if non-empty) */ + struct uhci_transfer *end; + + /** Queue head */ + struct uhci_queue_head *head; +}; + +/** + * Calculate space used in transfer ring + * + * @v ring Transfer ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +uhci_ring_fill ( struct uhci_ring *ring ) { + unsigned int fill; + + fill = ( ring->prod - ring->cons ); + assert ( fill <= UHCI_RING_COUNT ); + return fill; +} + +/** + * Calculate space remaining in transfer ring + * + * @v ring Transfer ring + * @ret remaining Number of entries remaining + */ +static inline __attribute__ (( always_inline )) unsigned int +uhci_ring_remaining ( struct uhci_ring *ring ) { + unsigned int fill = uhci_ring_fill ( ring ); + + return ( UHCI_RING_COUNT - fill ); +} + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define UHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define UHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a port to be enabled + * + * This is a policy decision. + */ +#define UHCI_PORT_ENABLE_MAX_WAIT_MS 500 + +/** A UHCI device */ +struct uhci_device { + /** Registers */ + unsigned long regs; + /** Name */ + const char *name; + + /** EHCI companion controller bus:dev.fn address (if any) */ + unsigned int companion; + + /** Asynchronous queue head */ + struct uhci_queue_head *head; + /** Frame list */ + struct uhci_frame_list *frame; + + /** List of all endpoints */ + struct list_head endpoints; + /** Asynchronous schedule */ + struct list_head async; + /** Periodic schedule + * + * Listed in decreasing order of endpoint interval. + */ + struct list_head periodic; + + /** USB bus */ + struct usb_bus *bus; +}; + +/** A UHCI endpoint */ +struct uhci_endpoint { + /** UHCI device */ + struct uhci_device *uhci; + /** USB endpoint */ + struct usb_endpoint *ep; + /** List of all endpoints */ + struct list_head list; + /** Endpoint schedule */ + struct list_head schedule; + + /** Transfer ring */ + struct uhci_ring ring; +}; + +#endif /* _IPXE_UHCI_H */ diff --git a/roms/ipxe/src/drivers/usb/usbhid.c b/roms/ipxe/src/drivers/usb/usbhid.c new file mode 100644 index 000000000..c74535a05 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbhid.c @@ -0,0 +1,151 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/usb.h> +#include <ipxe/usbhid.h> + +/** @file + * + * USB human interface devices (HID) + * + */ + +/** + * Open USB human interface device + * + * @v hid USB human interface device + * @ret rc Return status code + */ +int usbhid_open ( struct usb_hid *hid ) { + int rc; + + /* Open interrupt IN endpoint */ + if ( ( rc = usb_endpoint_open ( &hid->in ) ) != 0 ) { + DBGC ( hid, "HID %s could not open interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_open_in; + } + + /* Refill interrupt IN endpoint */ + if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) { + DBGC ( hid, "HID %s could not refill interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_refill_in; + } + + /* Open interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && + ( ( rc = usb_endpoint_open ( &hid->out ) ) != 0 ) ) { + DBGC ( hid, "HID %s could not open interrupt OUT: %s\n", + hid->func->name, strerror ( rc ) ); + goto err_open_out; + } + + return 0; + + usb_endpoint_close ( &hid->out ); + err_open_out: + err_refill_in: + usb_endpoint_close ( &hid->in ); + err_open_in: + return rc; +} + +/** + * Close USB human interface device + * + * @v hid USB human interface device + */ +void usbhid_close ( struct usb_hid *hid ) { + + /* Close interrupt OUT endpoint, if applicable */ + if ( hid->out.usb ) + usb_endpoint_close ( &hid->out ); + + /* Close interrupt IN endpoint */ + usb_endpoint_close ( &hid->in ); +} + +/** + * Refill USB human interface device endpoints + * + * @v hid USB human interface device + * @ret rc Return status code + */ +int usbhid_refill ( struct usb_hid *hid ) { + int rc; + + /* Refill interrupt IN endpoint */ + if ( ( rc = usb_refill ( &hid->in ) ) != 0 ) + return rc; + + /* Refill interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && ( ( rc = usb_refill ( &hid->out ) ) != 0 ) ) + return rc; + + return 0; +} + +/** + * Describe USB human interface device + * + * @v hid USB human interface device + * @v config Configuration descriptor + * @ret rc Return status code + */ +int usbhid_describe ( struct usb_hid *hid, + struct usb_configuration_descriptor *config ) { + struct usb_interface_descriptor *desc; + int rc; + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, hid->func->interface[0], 0 ); + if ( ! desc ) { + DBGC ( hid, "HID %s has no interface descriptor\n", + hid->func->name ); + return -EINVAL; + } + + /* Describe interrupt IN endpoint */ + if ( ( rc = usb_endpoint_described ( &hid->in, config, desc, + USB_INTERRUPT_IN, 0 ) ) != 0 ) { + DBGC ( hid, "HID %s could not describe interrupt IN: %s\n", + hid->func->name, strerror ( rc ) ); + return rc; + } + + /* Describe interrupt OUT endpoint, if applicable */ + if ( hid->out.usb && + ( ( rc = usb_endpoint_described ( &hid->out, config, desc, + USB_INTERRUPT_OUT, 0 ) ) != 0 )){ + DBGC ( hid, "HID %s could not describe interrupt OUT: %s\n", + hid->func->name, strerror ( rc ) ); + return rc; + } + + return 0; +} diff --git a/roms/ipxe/src/drivers/usb/usbhub.c b/roms/ipxe/src/drivers/usb/usbhub.c new file mode 100644 index 000000000..bf2a20005 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbhub.c @@ -0,0 +1,547 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/usb.h> +#include "usbhub.h" + +/** @file + * + * USB hub driver + * + */ + +/** + * Refill interrupt ring + * + * @v hubdev Hub device + */ +static void hub_refill ( struct usb_hub_device *hubdev ) { + int rc; + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &hubdev->intr ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not refill interrupt: %s\n", + hubdev->name, strerror ( rc ) ); + /* Continue attempting to refill */ + return; + } + + /* Stop refill process */ + process_del ( &hubdev->refill ); +} + +/** Refill process descriptor */ +static struct process_descriptor hub_refill_desc = + PROC_DESC ( struct usb_hub_device, refill, hub_refill ); + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void hub_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_hub_device *hubdev = + container_of ( ep, struct usb_hub_device, intr ); + struct usb_hub *hub = hubdev->hub; + uint8_t *data = iobuf->data; + unsigned int bits = ( 8 * iob_len ( iobuf ) ); + unsigned int i; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto done; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( hubdev, "HUB %s interrupt failed: %s\n", + hubdev->name, strerror ( rc ) ); + DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); + goto done; + } + + /* Report any port status changes */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + + /* Sanity check */ + if ( i > bits ) { + DBGC ( hubdev, "HUB %s underlength interrupt:\n", + hubdev->name ); + DBGC_HDA ( hubdev, 0, iobuf->data, iob_len ( iobuf ) ); + goto done; + } + + /* Report port status change if applicable */ + if ( data[ i / 8 ] & ( 1 << ( i % 8 ) ) ) { + DBGC2 ( hubdev, "HUB %s port %d status changed\n", + hubdev->name, i ); + usb_port_changed ( usb_port ( hub, i ) ); + } + } + + done: + /* Start refill process */ + process_add ( &hubdev->refill ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations usb_hub_intr_operations = { + .complete = hub_complete, +}; + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int hub_open ( struct usb_hub *hub ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + unsigned int i; + int rc; + + /* Ensure ports are powered */ + for ( i = 1 ; i <= hub->ports ; i++ ) { + if ( ( rc = usb_hub_set_port_feature ( usb, i, + USB_HUB_PORT_POWER, + 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not apply power: " + "%s\n", hubdev->name, i, strerror ( rc ) ); + goto err_power; + } + } + + /* Open interrupt endpoint */ + if ( ( rc = usb_endpoint_open ( &hubdev->intr ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not register interrupt: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_open; + } + + /* Start refill process */ + process_add ( &hubdev->refill ); + + /* Refill interrupt ring */ + hub_refill ( hubdev ); + + return 0; + + usb_endpoint_close ( &hubdev->intr ); + err_open: + err_power: + return rc; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void hub_close ( struct usb_hub *hub ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + + /* Close interrupt endpoint */ + usb_endpoint_close ( &hubdev->intr ); + + /* Stop refill process */ + process_del ( &hubdev->refill ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + struct usb_hub_port_status status; + unsigned int current; + unsigned int i; + int rc; + + /* Initiate reset if applicable */ + if ( ( hub->protocol < USB_PROTO_3_0 ) && + ( ( rc = usb_hub_set_port_feature ( usb, port->address, + USB_HUB_PORT_RESET, 0 ) )!=0)){ + DBGC ( hubdev, "HUB %s port %d could not initiate reset: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + /* Wait for port to become enabled */ + for ( i = 0 ; i < USB_HUB_ENABLE_MAX_WAIT_MS ; i++ ) { + + /* Check for port being enabled */ + if ( ( rc = usb_hub_get_port_status ( usb, port->address, + &status ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not get status: " + "%s\n", hubdev->name, port->address, + strerror ( rc ) ); + return rc; + } + current = le16_to_cpu ( status.current ); + if ( current & ( 1 << USB_HUB_PORT_ENABLE ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( hubdev, "HUB %s port %d timed out waiting for enable\n", + hubdev->name, port->address ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + int rc; + + /* Disable port */ + if ( ( rc = usb_hub_clear_port_feature ( usb, port->address, + USB_HUB_PORT_ENABLE, 0 ) )!=0){ + DBGC ( hubdev, "HUB %s port %d could not disable: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Clear port status change bits + * + * @v hubdev USB hub device + * @v port Port number + * @v changed Port status change bits + * @ret rc Return status code + */ +static int hub_clear_changes ( struct usb_hub_device *hubdev, + unsigned int port, uint16_t changed ) { + struct usb_device *usb = hubdev->usb; + unsigned int bit; + unsigned int feature; + int rc; + + /* Clear each set bit */ + for ( bit = 0 ; bit < 16 ; bit++ ) { + + /* Skip unset bits */ + if ( ! ( changed & ( 1 << bit ) ) ) + continue; + + /* Skip unused features */ + feature = USB_HUB_C_FEATURE ( bit ); + if ( ! ( hubdev->features & ( 1 << feature ) ) ) + continue; + + /* Clear bit */ + if ( ( rc = usb_hub_clear_port_feature ( usb, port, + feature, 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not clear feature " + "%d: %s\n", hubdev->name, port, feature, + strerror ( rc ) ); + return rc; + } + } + + return 0; +} + +/** + * Update port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int hub_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + struct usb_hub_port_status status; + unsigned int current; + unsigned int changed; + int rc; + + /* Get port status */ + if ( ( rc = usb_hub_get_port_status ( usb, port->address, + &status ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not get status: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + current = le16_to_cpu ( status.current ); + changed = le16_to_cpu ( status.changed ); + DBGC2 ( hubdev, "HUB %s port %d status is %04x:%04x\n", + hubdev->name, port->address, changed, current ); + + /* Update port speed */ + if ( current & ( 1 << USB_HUB_PORT_CONNECTION ) ) { + if ( hub->protocol >= USB_PROTO_3_0 ) { + port->speed = USB_SPEED_SUPER; + } else if ( current & ( 1 << USB_HUB_PORT_LOW_SPEED ) ) { + port->speed = USB_SPEED_LOW; + } else if ( current & ( 1 << USB_HUB_PORT_HIGH_SPEED ) ) { + port->speed = USB_SPEED_HIGH; + } else { + port->speed = USB_SPEED_FULL; + } + } else { + port->speed = USB_SPEED_NONE; + } + + /* Record disconnections */ + port->disconnected |= ( changed & ( 1 << USB_HUB_PORT_CONNECTION ) ); + + /* Clear port status change bits */ + if ( ( rc = hub_clear_changes ( hubdev, port->address, changed ) ) != 0) + return rc; + + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int hub_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct usb_hub_device *hubdev = usb_hub_get_drvdata ( hub ); + struct usb_device *usb = hubdev->usb; + int rc; + + /* Clear transaction translator buffer. All hubs must support + * single-TT operation; we simplify our code by supporting + * only this configuration. + */ + if ( ( rc = usb_hub_clear_tt_buffer ( usb, ep->usb->address, + ep->address, ep->attributes, + USB_HUB_TT_SINGLE ) ) != 0 ) { + DBGC ( hubdev, "HUB %s port %d could not clear TT buffer: %s\n", + hubdev->name, port->address, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** USB hub operations */ +static struct usb_hub_driver_operations hub_operations = { + .open = hub_open, + .close = hub_close, + .enable = hub_enable, + .disable = hub_disable, + .speed = hub_speed, + .clear_tt = hub_clear_tt, +}; + +/** + * Probe USB hub + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int hub_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_bus *bus = usb->port->hub->bus; + struct usb_hub_device *hubdev; + struct usb_interface_descriptor *interface; + union usb_hub_descriptor desc; + unsigned int depth; + unsigned int ports; + int enhanced; + int rc; + + /* Allocate and initialise structure */ + hubdev = zalloc ( sizeof ( *hubdev ) ); + if ( ! hubdev ) { + rc = -ENOMEM; + goto err_alloc; + } + enhanced = ( usb->port->protocol >= USB_PROTO_3_0 ); + hubdev->name = func->name; + hubdev->usb = usb; + hubdev->features = + ( enhanced ? USB_HUB_FEATURES_ENHANCED : USB_HUB_FEATURES ); + usb_endpoint_init ( &hubdev->intr, usb, &usb_hub_intr_operations ); + usb_refill_init ( &hubdev->intr, 0, USB_HUB_INTR_FILL ); + process_init_stopped ( &hubdev->refill, &hub_refill_desc, NULL ); + + /* Locate hub interface descriptor */ + interface = usb_interface_descriptor ( config, func->interface[0], 0 ); + if ( ! interface ) { + DBGC ( hubdev, "HUB %s has no interface descriptor\n", + hubdev->name ); + rc = -EINVAL; + goto err_interface; + } + + /* Locate interrupt endpoint descriptor */ + if ( ( rc = usb_endpoint_described ( &hubdev->intr, config, interface, + USB_INTERRUPT_IN, 0 ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not describe interrupt endpoint: " + "%s\n", hubdev->name, strerror ( rc ) ); + goto err_endpoint; + } + + /* Set hub depth */ + depth = usb_depth ( usb ); + if ( enhanced ) { + if ( ( rc = usb_hub_set_hub_depth ( usb, depth ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not set hub depth to %d: " + "%s\n", hubdev->name, depth, strerror ( rc ) ); + goto err_set_hub_depth; + } + } + + /* Get hub descriptor */ + if ( ( rc = usb_hub_get_descriptor ( usb, enhanced, &desc ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not get hub descriptor: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_hub_descriptor; + } + ports = desc.basic.ports; + DBGC ( hubdev, "HUB %s has %d ports at depth %d%s\n", hubdev->name, + ports, depth, ( enhanced ? " (enhanced)" : "" ) ); + + /* Allocate hub */ + hubdev->hub = alloc_usb_hub ( bus, usb, ports, &hub_operations ); + if ( ! hubdev->hub ) { + rc = -ENOMEM; + goto err_alloc_hub; + } + usb_hub_set_drvdata ( hubdev->hub, hubdev ); + + /* Register hub */ + if ( ( rc = register_usb_hub ( hubdev->hub ) ) != 0 ) { + DBGC ( hubdev, "HUB %s could not register: %s\n", + hubdev->name, strerror ( rc ) ); + goto err_register_hub; + } + + usb_func_set_drvdata ( func, hubdev ); + return 0; + + unregister_usb_hub ( hubdev->hub ); + err_register_hub: + free_usb_hub ( hubdev->hub ); + err_alloc_hub: + err_hub_descriptor: + err_set_hub_depth: + err_endpoint: + err_interface: + free ( hubdev ); + err_alloc: + return rc; +} + +/** + * Remove USB hub + * + * @v func USB function + * @ret rc Return status code + */ +static void hub_remove ( struct usb_function *func ) { + struct usb_hub_device *hubdev = usb_func_get_drvdata ( func ); + struct usb_hub *hub = hubdev->hub; + struct usb_device *usb = hubdev->usb; + struct usb_port *port; + unsigned int i; + + /* If hub has been unplugged, mark all ports as unplugged */ + if ( usb->port->speed == USB_SPEED_NONE ) { + for ( i = 1 ; i <= hub->ports ; i++ ) { + port = usb_port ( hub, i ); + port->speed = USB_SPEED_NONE; + } + } + + /* Unregister hub */ + unregister_usb_hub ( hubdev->hub ); + assert ( ! process_running ( &hubdev->refill ) ); + + /* Free hub */ + free_usb_hub ( hubdev->hub ); + + /* Free hub device */ + free ( hubdev ); +} + +/** USB hub device IDs */ +static struct usb_device_id hub_ids[] = { + { + .name = "hub-1", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HUB, + .subclass = 0, + .protocol = 0, + }, + }, + { + .name = "hub-2", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HUB, + .subclass = 0, + .protocol = 1, + }, + }, +}; + +/** USB hub driver */ +struct usb_driver usb_hub_driver __usb_driver = { + .ids = hub_ids, + .id_count = ( sizeof ( hub_ids ) / sizeof ( hub_ids[0] ) ), + .probe = hub_probe, + .remove = hub_remove, +}; diff --git a/roms/ipxe/src/drivers/usb/usbhub.h b/roms/ipxe/src/drivers/usb/usbhub.h new file mode 100644 index 000000000..d7d8f9610 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbhub.h @@ -0,0 +1,279 @@ +#ifndef _USBHUB_H +#define _USBHUB_H + +/** @file + * + * USB hubs + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> +#include <ipxe/list.h> +#include <ipxe/process.h> + +/** Request recipient is a port */ +#define USB_HUB_RECIP_PORT ( 3 << 0 ) + +/** A basic USB hub descriptor */ +struct usb_hub_descriptor_basic { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Number of ports */ + uint8_t ports; + /** Characteristics */ + uint16_t characteristics; + /** Power-on delay (in 2ms intervals */ + uint8_t delay; + /** Controller current (in mA) */ + uint8_t current; +} __attribute__ (( packed )); + +/** A basic USB hub descriptor */ +#define USB_HUB_DESCRIPTOR 41 + +/** An enhanced USB hub descriptor */ +struct usb_hub_descriptor_enhanced { + /** Basic USB hub descriptor */ + struct usb_hub_descriptor_basic basic; + /** Header decode latency */ + uint8_t latency; + /** Maximum delay */ + uint16_t delay; + /** Removable device bitmask */ + uint16_t removable; +} __attribute__ (( packed )); + +/** An enhanced USB hub descriptor */ +#define USB_HUB_DESCRIPTOR_ENHANCED 42 + +/** A USB hub descriptor */ +union usb_hub_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Basic hub descriptor */ + struct usb_hub_descriptor_basic basic; + /** Enhanced hub descriptor */ + struct usb_hub_descriptor_enhanced enhanced; +} __attribute__ (( packed )); + +/** Port status */ +struct usb_hub_port_status { + /** Current status */ + uint16_t current; + /** Changed status */ + uint16_t changed; +} __attribute__ (( packed )); + +/** Current connect status feature */ +#define USB_HUB_PORT_CONNECTION 0 + +/** Port enabled/disabled feature */ +#define USB_HUB_PORT_ENABLE 1 + +/** Port reset feature */ +#define USB_HUB_PORT_RESET 4 + +/** Port power feature */ +#define USB_HUB_PORT_POWER 8 + +/** Low-speed device attached */ +#define USB_HUB_PORT_LOW_SPEED 9 + +/** High-speed device attached */ +#define USB_HUB_PORT_HIGH_SPEED 10 + +/** Connect status changed */ +#define USB_HUB_C_PORT_CONNECTION 16 + +/** Port enable/disable changed */ +#define USB_HUB_C_PORT_ENABLE 17 + +/** Suspend changed */ +#define USB_HUB_C_PORT_SUSPEND 18 + +/** Over-current indicator changed */ +#define USB_HUB_C_PORT_OVER_CURRENT 19 + +/** Reset changed */ +#define USB_HUB_C_PORT_RESET 20 + +/** Link state changed */ +#define USB_HUB_C_PORT_LINK_STATE 25 + +/** Configuration error */ +#define USB_HUB_C_PORT_CONFIG_ERROR 26 + +/** Calculate feature from change bit number */ +#define USB_HUB_C_FEATURE( bit ) ( 16 + (bit) ) + +/** USB features */ +#define USB_HUB_FEATURES \ + ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ + ( 1 << USB_HUB_C_PORT_ENABLE ) | \ + ( 1 << USB_HUB_C_PORT_SUSPEND ) | \ + ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ + ( 1 << USB_HUB_C_PORT_RESET ) ) + +/** USB features for enhanced hubs */ +#define USB_HUB_FEATURES_ENHANCED \ + ( ( 1 << USB_HUB_C_PORT_CONNECTION ) | \ + ( 1 << USB_HUB_C_PORT_OVER_CURRENT ) | \ + ( 1 << USB_HUB_C_PORT_RESET ) | \ + ( 1 << USB_HUB_C_PORT_LINK_STATE ) | \ + ( 1 << USB_HUB_C_PORT_CONFIG_ERROR ) ) + +/** Set hub depth */ +#define USB_HUB_SET_HUB_DEPTH \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_DEVICE | \ + USB_REQUEST_TYPE ( 12 ) ) + +/** Clear transaction translator buffer */ +#define USB_HUB_CLEAR_TT_BUFFER \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_HUB_RECIP_PORT | \ + USB_REQUEST_TYPE ( 8 ) ) + +/** + * Get hub descriptor + * + * @v usb USB device + * @v enhanced Hub is an enhanced hub + * @v data Hub descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_get_descriptor ( struct usb_device *usb, int enhanced, + union usb_hub_descriptor *data ) { + unsigned int desc; + size_t len; + + /* Determine descriptor type and length */ + desc = ( enhanced ? USB_HUB_DESCRIPTOR_ENHANCED : USB_HUB_DESCRIPTOR ); + len = ( enhanced ? sizeof ( data->enhanced ) : sizeof ( data->basic ) ); + + return usb_get_descriptor ( usb, USB_TYPE_CLASS, desc, 0, 0, + &data->header, len ); +} + +/** + * Get port status + * + * @v usb USB device + * @v port Port address + * @v status Port status descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_get_port_status ( struct usb_device *usb, unsigned int port, + struct usb_hub_port_status *status ) { + + return usb_get_status ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + port, status, sizeof ( *status ) ); +} + +/** + * Clear port feature + * + * @v usb USB device + * @v port Port address + * @v feature Feature to clear + * @v index Index (when clearing a port indicator) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_clear_port_feature ( struct usb_device *usb, unsigned int port, + unsigned int feature, unsigned int index ) { + + return usb_clear_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + feature, ( ( index << 8 ) | port ) ); +} + +/** + * Set port feature + * + * @v usb USB device + * @v port Port address + * @v feature Feature to clear + * @v index Index (when clearing a port indicator) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_set_port_feature ( struct usb_device *usb, unsigned int port, + unsigned int feature, unsigned int index ) { + + return usb_set_feature ( usb, ( USB_TYPE_CLASS | USB_HUB_RECIP_PORT ), + feature, ( ( index << 8 ) | port ) ); +} + +/** + * Set hub depth + * + * @v usb USB device + * @v depth Hub depth + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_set_hub_depth ( struct usb_device *usb, unsigned int depth ) { + + return usb_control ( usb, USB_HUB_SET_HUB_DEPTH, depth, 0, NULL, 0 ); +} + +/** + * Clear transaction translator buffer + * + * @v usb USB device + * @v device Device address + * @v endpoint Endpoint address + * @v attributes Endpoint attributes + * @v tt_port Transaction translator port (or 1 for single-TT hubs) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_hub_clear_tt_buffer ( struct usb_device *usb, unsigned int device, + unsigned int endpoint, unsigned int attributes, + unsigned int tt_port ) { + unsigned int value; + + /* Calculate value */ + value = ( ( ( endpoint & USB_ENDPOINT_MAX ) << 0 ) | ( device << 4 ) | + ( ( attributes & USB_ENDPOINT_ATTR_TYPE_MASK ) << 11 ) | + ( ( endpoint & USB_ENDPOINT_IN ) << 8 ) ); + + return usb_control ( usb, USB_HUB_CLEAR_TT_BUFFER, value, + tt_port, NULL, 0 ); +} + +/** Transaction translator port value for single-TT hubs */ +#define USB_HUB_TT_SINGLE 1 + +/** A USB hub device */ +struct usb_hub_device { + /** Name */ + const char *name; + /** USB device */ + struct usb_device *usb; + /** USB hub */ + struct usb_hub *hub; + /** Features */ + unsigned int features; + + /** Interrupt endpoint */ + struct usb_endpoint intr; + /** Interrupt endpoint refill process */ + struct process refill; +}; + +/** Interrupt ring fill level + * + * This is a policy decision. + */ +#define USB_HUB_INTR_FILL 4 + +/** Maximum time to wait for port to become enabled + * + * This is a policy decision. + */ +#define USB_HUB_ENABLE_MAX_WAIT_MS 100 + +#endif /* _USBHUB_H */ diff --git a/roms/ipxe/src/drivers/usb/usbkbd.c b/roms/ipxe/src/drivers/usb/usbkbd.c new file mode 100644 index 000000000..ea94f2e63 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbkbd.c @@ -0,0 +1,509 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/console.h> +#include <ipxe/keys.h> +#include <ipxe/usb.h> +#include "usbkbd.h" + +/** @file + * + * USB keyboard driver + * + */ + +/** List of USB keyboards */ +static LIST_HEAD ( usb_keyboards ); + +/****************************************************************************** + * + * Keyboard map + * + ****************************************************************************** + */ + +/** + * Map USB keycode to iPXE key + * + * @v keycode Keycode + * @v modifiers Modifiers + * @ret key iPXE key + * + * Key codes are defined in the USB HID Usage Tables Keyboard/Keypad + * page. + */ +static unsigned int usbkbd_map ( unsigned int keycode, + unsigned int modifiers ) { + unsigned int key; + + if ( keycode < USBKBD_KEY_A ) { + /* Not keys */ + key = 0; + } else if ( keycode <= USBKBD_KEY_Z ) { + /* Alphabetic keys */ + key = ( keycode - USBKBD_KEY_A + 'a' ); + if ( modifiers & USBKBD_CTRL ) { + key -= ( 'a' - CTRL_A ); + } else if ( modifiers & USBKBD_SHIFT ) { + key -= ( 'a' - 'A' ); + } + } else if ( keycode <= USBKBD_KEY_0 ) { + /* Numeric key row */ + if ( modifiers & USBKBD_SHIFT ) { + key = "!@#$%^&*()" [ keycode - USBKBD_KEY_1 ]; + } else { + key = ( ( ( keycode - USBKBD_KEY_1 + 1 ) % 10 ) + '0' ); + } + } else if ( keycode <= USBKBD_KEY_SPACE ) { + /* Unmodifiable keys */ + static const uint8_t unmodifable[] = + { LF, ESC, BACKSPACE, TAB, ' ' }; + key = unmodifable[ keycode - USBKBD_KEY_ENTER ]; + } else if ( keycode <= USBKBD_KEY_SLASH ) { + /* Punctuation keys */ + if ( modifiers & USBKBD_SHIFT ) { + key = "_+{}|~:\"~<>?" [ keycode - USBKBD_KEY_MINUS ]; + } else { + key = "-=[]\\#;'`,./" [ keycode - USBKBD_KEY_MINUS ]; + } + } else if ( keycode <= USBKBD_KEY_UP ) { + /* Special keys */ + static const uint16_t special[] = { + 0, 0, 0, 0, 0, KEY_F5, KEY_F6, KEY_F7, KEY_F8, KEY_F9, + KEY_F10, KEY_F11, KEY_F12, 0, 0, 0, KEY_IC, KEY_HOME, + KEY_PPAGE, KEY_DC, KEY_END, KEY_NPAGE, KEY_RIGHT, + KEY_LEFT, KEY_DOWN, KEY_UP + }; + key = special[ keycode - USBKBD_KEY_CAPSLOCK ]; + } else { + key = 0; + } + + return key; +} + +/****************************************************************************** + * + * Keyboard buffer + * + ****************************************************************************** + */ + +/** + * Insert keypress into keyboard buffer + * + * @v kbd USB keyboard + * @v keycode Keycode + * @v modifiers Modifiers + */ +static void usbkbd_produce ( struct usb_keyboard *kbd, unsigned int keycode, + unsigned int modifiers ) { + unsigned int key; + + /* Map to iPXE key */ + key = usbkbd_map ( keycode, modifiers ); + + /* Do nothing if this keycode has no corresponding iPXE key */ + if ( ! key ) { + DBGC ( kbd, "KBD %s has no key for keycode %#02x:%#02x\n", + kbd->name, modifiers, keycode ); + return; + } + + /* Check for buffer overrun */ + if ( usbkbd_fill ( kbd ) >= USBKBD_BUFSIZE ) { + DBGC ( kbd, "KBD %s buffer overrun (key %#02x)\n", + kbd->name, key ); + return; + } + + /* Insert into buffer */ + kbd->key[ ( kbd->prod++ ) % USBKBD_BUFSIZE ] = key; + DBGC2 ( kbd, "KBD %s key %#02x produced\n", kbd->name, key ); +} + +/** + * Consume character from keyboard buffer + * + * @v kbd USB keyboard + * @ret character Character + */ +static unsigned int usbkbd_consume ( struct usb_keyboard *kbd ) { + static char buf[] = "\x1b[xx~"; + char *tmp = &buf[2]; + unsigned int key; + unsigned int character; + unsigned int ansi_n; + unsigned int len; + + /* Sanity check */ + assert ( usbkbd_fill ( kbd ) > 0 ); + + /* Get current keypress */ + key = kbd->key[ kbd->cons % USBKBD_BUFSIZE ]; + + /* If this is a straightforward key, just consume and return it */ + if ( key < KEY_MIN ) { + kbd->cons++; + DBGC2 ( kbd, "KBD %s key %#02x consumed\n", kbd->name, key ); + return key; + } + + /* Construct ANSI sequence */ + ansi_n = KEY_ANSI_N ( key ); + if ( ansi_n ) + tmp += sprintf ( tmp, "%d", ansi_n ); + *(tmp++) = KEY_ANSI_TERMINATOR ( key ); + *tmp = '\0'; + len = ( tmp - buf ); + assert ( len < sizeof ( buf ) ); + if ( kbd->subcons == 0 ) { + DBGC2 ( kbd, "KBD %s key %#02x consumed as ^[%s\n", + kbd->name, key, &buf[1] ); + } + + /* Extract character from ANSI sequence */ + assert ( kbd->subcons < len ); + character = buf[ kbd->subcons++ ]; + + /* Consume key if applicable */ + if ( kbd->subcons == len ) { + kbd->cons++; + kbd->subcons = 0; + } + + return character; +} + +/****************************************************************************** + * + * Keyboard report + * + ****************************************************************************** + */ + +/** + * Check for presence of keycode in report + * + * @v report Keyboard report + * @v keycode Keycode (must be non-zero) + * @ret has_keycode Keycode is present in report + */ +static int usbkbd_has_keycode ( struct usb_keyboard_report *report, + unsigned int keycode ) { + unsigned int i; + + /* Check for keycode */ + for ( i = 0 ; i < ( sizeof ( report->keycode ) / + sizeof ( report->keycode[0] ) ) ; i++ ) { + if ( report->keycode[i] == keycode ) + return keycode; + } + + return 0; +} + +/** + * Handle keyboard report + * + * @v kbd USB keyboard + * @v new New keyboard report + */ +static void usbkbd_report ( struct usb_keyboard *kbd, + struct usb_keyboard_report *new ) { + struct usb_keyboard_report *old = &kbd->report; + unsigned int keycode; + unsigned int i; + + /* Check if current key has been released */ + if ( kbd->keycode && ! usbkbd_has_keycode ( new, kbd->keycode ) ) { + DBGC2 ( kbd, "KBD %s keycode %#02x released\n", + kbd->name, kbd->keycode ); + kbd->keycode = 0; + } + + /* Decrement auto-repeat hold-off timer, if applicable */ + if ( kbd->holdoff ) + kbd->holdoff--; + + /* Check if a new key has been pressed */ + for ( i = 0 ; i < ( sizeof ( new->keycode ) / + sizeof ( new->keycode[0] ) ) ; i++ ) { + + /* Ignore keys present in the previous report */ + keycode = new->keycode[i]; + if ( ( keycode == 0 ) || usbkbd_has_keycode ( old, keycode ) ) + continue; + DBGC2 ( kbd, "KBD %s keycode %#02x pressed\n", + kbd->name, keycode ); + + /* Insert keypress into keyboard buffer */ + usbkbd_produce ( kbd, keycode, new->modifiers ); + + /* Record as most recent keycode */ + kbd->keycode = keycode; + + /* Start auto-repeat hold-off timer */ + kbd->holdoff = USBKBD_HOLDOFF; + } + + /* Insert auto-repeated keypress into keyboard buffer, if applicable */ + if ( kbd->keycode && ! kbd->holdoff ) + usbkbd_produce ( kbd, kbd->keycode, new->modifiers ); + + /* Record report */ + memcpy ( old, new, sizeof ( *old ) ); +} + +/****************************************************************************** + * + * Interrupt endpoint + * + ****************************************************************************** + */ + +/** + * Complete interrupt transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ +static void usbkbd_complete ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ) { + struct usb_keyboard *kbd = container_of ( ep, struct usb_keyboard, + hid.in ); + struct usb_keyboard_report *report; + + /* Ignore packets cancelled when the endpoint closes */ + if ( ! ep->open ) + goto drop; + + /* Ignore packets with errors */ + if ( rc != 0 ) { + DBGC ( kbd, "KBD %s interrupt IN failed: %s\n", + kbd->name, strerror ( rc ) ); + goto drop; + } + + /* Ignore underlength packets */ + if ( iob_len ( iobuf ) < sizeof ( *report ) ) { + DBGC ( kbd, "KBD %s underlength report:\n", kbd->name ); + DBGC_HDA ( kbd, 0, iobuf->data, iob_len ( iobuf ) ); + goto drop; + } + report = iobuf->data; + + /* Handle keyboard report */ + usbkbd_report ( kbd, report ); + + drop: + /* Recycle I/O buffer */ + usb_recycle ( &kbd->hid.in, iobuf ); +} + +/** Interrupt endpoint operations */ +static struct usb_endpoint_driver_operations usbkbd_operations = { + .complete = usbkbd_complete, +}; + +/****************************************************************************** + * + * USB interface + * + ****************************************************************************** + */ + +/** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbkbd_probe ( struct usb_function *func, + struct usb_configuration_descriptor *config ) { + struct usb_device *usb = func->usb; + struct usb_keyboard *kbd; + int rc; + + /* Allocate and initialise structure */ + kbd = zalloc ( sizeof ( *kbd ) ); + if ( ! kbd ) { + rc = -ENOMEM; + goto err_alloc; + } + kbd->name = func->name; + kbd->bus = usb->port->hub->bus; + usbhid_init ( &kbd->hid, func, &usbkbd_operations, NULL ); + usb_refill_init ( &kbd->hid.in, sizeof ( kbd->report ), + USBKBD_INTR_MAX_FILL ); + + /* Describe USB human interface device */ + if ( ( rc = usbhid_describe ( &kbd->hid, config ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not describe: %s\n", + kbd->name, strerror ( rc ) ); + goto err_describe; + } + DBGC ( kbd, "KBD %s using %s (len %zd)\n", + kbd->name, usb_endpoint_name ( &kbd->hid.in ), kbd->hid.in.mtu ); + + /* Set boot protocol */ + if ( ( rc = usbhid_set_protocol ( usb, func->interface[0], + USBHID_PROTOCOL_BOOT ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set boot protocol: %s\n", + kbd->name, strerror ( rc ) ); + goto err_set_protocol; + } + + /* Set idle time */ + if ( ( rc = usbhid_set_idle ( usb, func->interface[0], 0, + USBKBD_IDLE_DURATION ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not set idle time: %s\n", + kbd->name, strerror ( rc ) ); + goto err_set_idle; + } + + /* Open USB human interface device */ + if ( ( rc = usbhid_open ( &kbd->hid ) ) != 0 ) { + DBGC ( kbd, "KBD %s could not open: %s\n", + kbd->name, strerror ( rc ) ); + goto err_open; + } + + /* Add to list of USB keyboards */ + list_add_tail ( &kbd->list, &usb_keyboards ); + + usb_func_set_drvdata ( func, kbd ); + return 0; + + usbhid_close ( &kbd->hid ); + err_open: + err_set_idle: + err_set_protocol: + err_describe: + free ( kbd ); + err_alloc: + return rc; +} + +/** + * Remove device + * + * @v func USB function + */ +static void usbkbd_remove ( struct usb_function *func ) { + struct usb_keyboard *kbd = usb_func_get_drvdata ( func ); + + /* Remove from list of USB keyboards */ + list_del ( &kbd->list ); + + /* Close USB human interface device */ + usbhid_close ( &kbd->hid ); + + /* Free device */ + free ( kbd ); +} + +/** USB keyboard device IDs */ +static struct usb_device_id usbkbd_ids[] = { + { + .name = "kbd", + .vendor = USB_ANY_ID, + .product = USB_ANY_ID, + .class = { + .class = USB_CLASS_HID, + .subclass = USB_SUBCLASS_HID_BOOT, + .protocol = USBKBD_PROTOCOL, + }, + }, +}; + +/** USB keyboard driver */ +struct usb_driver usbkbd_driver __usb_driver = { + .ids = usbkbd_ids, + .id_count = ( sizeof ( usbkbd_ids ) / sizeof ( usbkbd_ids[0] ) ), + .probe = usbkbd_probe, + .remove = usbkbd_remove, +}; + +/****************************************************************************** + * + * Console interface + * + ****************************************************************************** + */ + +/** + * Read a character from the console + * + * @ret character Character read + */ +static int usbkbd_getchar ( void ) { + struct usb_keyboard *kbd; + + /* Consume first available key */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + if ( usbkbd_fill ( kbd ) ) + return usbkbd_consume ( kbd ); + } + + return 0; +} + +/** + * Check for available input + * + * @ret is_available Input is available + */ +static int usbkbd_iskey ( void ) { + struct usb_keyboard *kbd; + unsigned int fill; + + /* Poll all USB keyboards and refill endpoints */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + usb_poll ( kbd->bus ); + usb_refill ( &kbd->hid.in ); + } + + /* Check for a non-empty keyboard buffer */ + list_for_each_entry ( kbd, &usb_keyboards, list ) { + fill = usbkbd_fill ( kbd ); + if ( fill ) + return fill; + } + + return 0; +} + +/** USB keyboard console */ +struct console_driver usbkbd_console __console_driver = { + .getchar = usbkbd_getchar, + .iskey = usbkbd_iskey, +}; diff --git a/roms/ipxe/src/drivers/usb/usbkbd.h b/roms/ipxe/src/drivers/usb/usbkbd.h new file mode 100644 index 000000000..7eab24e46 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbkbd.h @@ -0,0 +1,154 @@ +#ifndef _USBKBD_H +#define _USBKBD_H + +/** @file + * + * USB keyboard driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <assert.h> +#include <ipxe/usb.h> +#include <ipxe/usbhid.h> + +/** Keyboard protocol */ +#define USBKBD_PROTOCOL 1 + +/** A USB keyboard report */ +struct usb_keyboard_report { + /** Modifier keys */ + uint8_t modifiers; + /** Reserved */ + uint8_t reserved; + /** Keycodes */ + uint8_t keycode[6]; +} __attribute__ (( packed )); + +/** USB modifier keys */ +enum usb_keyboard_modifier { + /** Left Ctrl key */ + USBKBD_CTRL_LEFT = 0x01, + /** Left Shift key */ + USBKBD_SHIFT_LEFT = 0x02, + /** Left Alt key */ + USBKBD_ALT_LEFT = 0x04, + /** Left GUI key */ + USBKBD_GUI_LEFT = 0x08, + /** Right Ctrl key */ + USBKBD_CTRL_RIGHT = 0x10, + /** Right Shift key */ + USBKBD_SHIFT_RIGHT = 0x20, + /** Right Alt key */ + USBKBD_ALT_RIGHT = 0x40, + /** Right GUI key */ + USBKBD_GUI_RIGHT = 0x80, +}; + +/** Either Ctrl key */ +#define USBKBD_CTRL ( USBKBD_CTRL_LEFT | USBKBD_CTRL_RIGHT ) + +/** Either Shift key */ +#define USBKBD_SHIFT ( USBKBD_SHIFT_LEFT | USBKBD_SHIFT_RIGHT ) + +/** Either Alt key */ +#define USBKBD_ALT ( USBKBD_ALT_LEFT | USBKBD_ALT_RIGHT ) + +/** Either GUI key */ +#define USBKBD_GUI ( USBKBD_GUI_LEFT | USBKBD_GUI_RIGHT ) + +/** USB keycodes */ +enum usb_keycode { + USBKBD_KEY_A = 0x04, + USBKBD_KEY_Z = 0x1d, + USBKBD_KEY_1 = 0x1e, + USBKBD_KEY_0 = 0x27, + USBKBD_KEY_ENTER = 0x28, + USBKBD_KEY_SPACE = 0x2c, + USBKBD_KEY_MINUS = 0x2d, + USBKBD_KEY_SLASH = 0x38, + USBKBD_KEY_CAPSLOCK = 0x39, + USBKBD_KEY_UP = 0x52, +}; + +/** Keyboard idle duration (in 4ms units) + * + * This is a policy decision. We choose to use an autorepeat rate of + * approximately 40ms. + */ +#define USBKBD_IDLE_DURATION 10 /* 10 x 4ms = 40ms */ + +/** Keyboard auto-repeat hold-off (in units of USBKBD_IDLE_DURATION) + * + * This is a policy decision. We choose to use an autorepeat delay of + * approximately 500ms. + */ +#define USBKBD_HOLDOFF 12 /* 12 x 40ms = 480ms */ + +/** Interrupt endpoint maximum fill level + * + * When idling, we are likely to poll the USB endpoint at only the + * 18.2Hz system timer tick rate. With a typical observed bInterval + * of 10ms (which will be rounded down to 8ms by the HCI drivers), + * this gives approximately 7 completions per poll. + */ +#define USBKBD_INTR_MAX_FILL 8 + +/** Keyboard buffer size + * + * Must be a power of two. + */ +#define USBKBD_BUFSIZE 8 + +/** A USB keyboard device */ +struct usb_keyboard { + /** Name */ + const char *name; + /** List of all USB keyboards */ + struct list_head list; + + /** USB bus */ + struct usb_bus *bus; + /** USB human interface device */ + struct usb_hid hid; + + /** Most recent keyboard report */ + struct usb_keyboard_report report; + /** Most recently pressed non-modifier key (if any) */ + unsigned int keycode; + /** Autorepeat hold-off time (in number of completions reported) */ + unsigned int holdoff; + + /** Keyboard buffer + * + * This stores iPXE key values. + */ + unsigned int key[USBKBD_BUFSIZE]; + /** Keyboard buffer producer counter */ + unsigned int prod; + /** Keyboard buffer consumer counter */ + unsigned int cons; + /** Keyboard buffer sub-consumer counter + * + * This represents the index within the ANSI escape sequence + * corresponding to an iPXE key value. + */ + unsigned int subcons; +}; + +/** + * Calculate keyboard buffer fill level + * + * @v kbd USB keyboard + * @ret fill Keyboard buffer fill level + */ +static inline __attribute__ (( always_inline )) unsigned int +usbkbd_fill ( struct usb_keyboard *kbd ) { + unsigned int fill = ( kbd->prod - kbd->cons ); + + assert ( fill <= USBKBD_BUFSIZE ); + return fill; +} + +#endif /* _USBKBD_H */ diff --git a/roms/ipxe/src/drivers/usb/usbnet.c b/roms/ipxe/src/drivers/usb/usbnet.c new file mode 100644 index 000000000..b92336d05 --- /dev/null +++ b/roms/ipxe/src/drivers/usb/usbnet.c @@ -0,0 +1,284 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <ipxe/usb.h> +#include <ipxe/usbnet.h> + +/** @file + * + * USB network devices + * + * USB network devices use a variety of packet formats and interface + * descriptors, but tend to have several features in common: + * + * - a single interrupt endpoint using the generic refill mechanism + * + * - a single bulk IN endpoint using the generic refill mechanism + * + * - a single bulk OUT endpoint + * + * - optional use of an alternate setting to enable the data interface + * + */ + +/** + * Open USB network device + * + * @v usbnet USB network device + * @ret rc Return status code + */ +int usbnet_open ( struct usbnet_device *usbnet ) { + struct usb_device *usb = usbnet->func->usb; + int rc; + + /* Open interrupt endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->intr ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open interrupt: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_intr; + } + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not refill interrupt: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_refill_intr; + } + + /* Select alternate setting for data interface, if applicable */ + if ( usbnet->alternate && + ( ( rc = usb_set_interface ( usb, usbnet->data, + usbnet->alternate ) ) != 0 ) ) { + DBGC ( usbnet, "USBNET %s could not set alternate interface " + "%d: %s\n", usbnet->func->name, usbnet->alternate, + strerror ( rc ) ); + goto err_set_interface; + } + + /* Open bulk IN endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->in ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open bulk IN: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_in; + } + + /* Open bulk OUT endpoint */ + if ( ( rc = usb_endpoint_open ( &usbnet->out ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not open bulk OUT: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_open_out; + } + + /* Refill bulk IN endpoint */ + if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) { + DBGC ( usbnet, "USBNET %s could not refill bulk IN: %s\n", + usbnet->func->name, strerror ( rc ) ); + goto err_refill_in; + } + + return 0; + + err_refill_in: + usb_endpoint_close ( &usbnet->out ); + err_open_out: + usb_endpoint_close ( &usbnet->in ); + err_open_in: + if ( usbnet->alternate ) + usb_set_interface ( usb, usbnet->data, 0 ); + err_set_interface: + err_refill_intr: + usb_endpoint_close ( &usbnet->intr ); + err_open_intr: + return rc; +} + +/** + * Close USB network device + * + * @v usbnet USB network device + */ +void usbnet_close ( struct usbnet_device *usbnet ) { + struct usb_device *usb = usbnet->func->usb; + + /* Close bulk OUT endpoint */ + usb_endpoint_close ( &usbnet->out ); + + /* Close bulk IN endpoint */ + usb_endpoint_close ( &usbnet->in ); + + /* Reset alternate setting for data interface, if applicable */ + if ( usbnet->alternate ) + usb_set_interface ( usb, usbnet->data, 0 ); + + /* Close interrupt endpoint */ + usb_endpoint_close ( &usbnet->intr ); +} + +/** + * Refill USB network device bulk IN and interrupt endpoints + * + * @v usbnet USB network device + * @ret rc Return status code + */ +int usbnet_refill ( struct usbnet_device *usbnet ) { + int rc; + + /* Refill bulk IN endpoint */ + if ( ( rc = usb_refill ( &usbnet->in ) ) != 0 ) + return rc; + + /* Refill interrupt endpoint */ + if ( ( rc = usb_refill ( &usbnet->intr ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Describe communications interface and interrupt endpoint + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbnet_comms_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config){ + struct usb_interface_descriptor *desc; + unsigned int comms; + unsigned int i; + int rc; + + /* Iterate over all available interfaces */ + for ( i = 0 ; i < usbnet->func->count ; i++ ) { + + /* Get interface number */ + comms = usbnet->func->interface[i]; + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, comms, 0 ); + if ( ! desc ) + continue; + + /* Describe interrupt endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->intr, config, + desc, USB_INTERRUPT_IN, + 0 ) ) != 0 ) + continue; + + /* Record communications interface */ + usbnet->comms = comms; + DBGC ( usbnet, "USBNET %s found communications interface %d\n", + usbnet->func->name, comms ); + return 0; + } + + DBGC ( usbnet, "USBNET %s found no communications interface\n", + usbnet->func->name ); + return -ENOENT; +} + +/** + * Describe data interface and bulk endpoints + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +static int usbnet_data_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ){ + struct usb_interface_descriptor *desc; + unsigned int data; + unsigned int alt; + unsigned int i; + int rc; + + /* Iterate over all available interfaces */ + for ( i = 0 ; i < usbnet->func->count ; i++ ) { + + /* Get interface number */ + data = usbnet->func->interface[i]; + + /* Iterate over all existent alternate settings */ + for ( alt = 0 ; ; alt++ ) { + + /* Locate interface descriptor */ + desc = usb_interface_descriptor ( config, data, alt ); + if ( ! desc ) + break; + + /* Describe bulk IN endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->in, + config, desc, + USB_BULK_IN, + 0 ) ) != 0 ) + continue; + + /* Describe bulk OUT endpoint */ + if ( ( rc = usb_endpoint_described ( &usbnet->out, + config, desc, + USB_BULK_OUT, + 0 ) ) != 0 ) + continue; + + /* Record data interface and alternate setting */ + usbnet->data = data; + usbnet->alternate = alt; + DBGC ( usbnet, "USBNET %s found data interface %d", + usbnet->func->name, data ); + if ( alt ) + DBGC ( usbnet, " using alternate %d", alt ); + DBGC ( usbnet, "\n" ); + return 0; + } + } + + DBGC ( usbnet, "USBNET %s found no data interface\n", + usbnet->func->name ); + return -ENOENT; +} + +/** + * Describe USB network device interfaces + * + * @v usbnet USB network device + * @v config Configuration descriptor + * @ret rc Return status code + */ +int usbnet_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ) { + int rc; + + /* Describe communications interface */ + if ( ( rc = usbnet_comms_describe ( usbnet, config ) ) != 0 ) + return rc; + + /* Describe data interface */ + if ( ( rc = usbnet_data_describe ( usbnet, config ) ) != 0 ) + return rc; + + return 0; +} diff --git a/roms/ipxe/src/drivers/usb/xhci.c b/roms/ipxe/src/drivers/usb/xhci.c new file mode 100644 index 000000000..49e67316b --- /dev/null +++ b/roms/ipxe/src/drivers/usb/xhci.c @@ -0,0 +1,3321 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <strings.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/malloc.h> +#include <ipxe/umalloc.h> +#include <ipxe/pci.h> +#include <ipxe/usb.h> +#include <ipxe/init.h> +#include <ipxe/profile.h> +#include "xhci.h" + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +/** Message transfer profiler */ +static struct profiler xhci_message_profiler __profiler = + { .name = "xhci.message" }; + +/** Stream transfer profiler */ +static struct profiler xhci_stream_profiler __profiler = + { .name = "xhci.stream" }; + +/** Event ring profiler */ +static struct profiler xhci_event_profiler __profiler = + { .name = "xhci.event" }; + +/** Transfer event profiler */ +static struct profiler xhci_transfer_profiler __profiler = + { .name = "xhci.transfer" }; + +/* Disambiguate the various error causes */ +#define EIO_DATA \ + __einfo_error ( EINFO_EIO_DATA ) +#define EINFO_EIO_DATA \ + __einfo_uniqify ( EINFO_EIO, ( 2 - 0 ), \ + "Data buffer error" ) +#define EIO_BABBLE \ + __einfo_error ( EINFO_EIO_BABBLE ) +#define EINFO_EIO_BABBLE \ + __einfo_uniqify ( EINFO_EIO, ( 3 - 0 ), \ + "Babble detected" ) +#define EIO_USB \ + __einfo_error ( EINFO_EIO_USB ) +#define EINFO_EIO_USB \ + __einfo_uniqify ( EINFO_EIO, ( 4 - 0 ), \ + "USB transaction error" ) +#define EIO_TRB \ + __einfo_error ( EINFO_EIO_TRB ) +#define EINFO_EIO_TRB \ + __einfo_uniqify ( EINFO_EIO, ( 5 - 0 ), \ + "TRB error" ) +#define EIO_STALL \ + __einfo_error ( EINFO_EIO_STALL ) +#define EINFO_EIO_STALL \ + __einfo_uniqify ( EINFO_EIO, ( 6 - 0 ), \ + "Stall error" ) +#define EIO_RESOURCE \ + __einfo_error ( EINFO_EIO_RESOURCE ) +#define EINFO_EIO_RESOURCE \ + __einfo_uniqify ( EINFO_EIO, ( 7 - 0 ), \ + "Resource error" ) +#define EIO_BANDWIDTH \ + __einfo_error ( EINFO_EIO_BANDWIDTH ) +#define EINFO_EIO_BANDWIDTH \ + __einfo_uniqify ( EINFO_EIO, ( 8 - 0 ), \ + "Bandwidth error" ) +#define EIO_NO_SLOTS \ + __einfo_error ( EINFO_EIO_NO_SLOTS ) +#define EINFO_EIO_NO_SLOTS \ + __einfo_uniqify ( EINFO_EIO, ( 9 - 0 ), \ + "No slots available" ) +#define EIO_STREAM_TYPE \ + __einfo_error ( EINFO_EIO_STREAM_TYPE ) +#define EINFO_EIO_STREAM_TYPE \ + __einfo_uniqify ( EINFO_EIO, ( 10 - 0 ), \ + "Invalid stream type" ) +#define EIO_SLOT \ + __einfo_error ( EINFO_EIO_SLOT ) +#define EINFO_EIO_SLOT \ + __einfo_uniqify ( EINFO_EIO, ( 11 - 0 ), \ + "Slot not enabled" ) +#define EIO_ENDPOINT \ + __einfo_error ( EINFO_EIO_ENDPOINT ) +#define EINFO_EIO_ENDPOINT \ + __einfo_uniqify ( EINFO_EIO, ( 12 - 0 ), \ + "Endpoint not enabled" ) +#define EIO_SHORT \ + __einfo_error ( EINFO_EIO_SHORT ) +#define EINFO_EIO_SHORT \ + __einfo_uniqify ( EINFO_EIO, ( 13 - 0 ), \ + "Short packet" ) +#define EIO_UNDERRUN \ + __einfo_error ( EINFO_EIO_UNDERRUN ) +#define EINFO_EIO_UNDERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 14 - 0 ), \ + "Ring underrun" ) +#define EIO_OVERRUN \ + __einfo_error ( EINFO_EIO_OVERRUN ) +#define EINFO_EIO_OVERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 15 - 0 ), \ + "Ring overrun" ) +#define EIO_VF_RING_FULL \ + __einfo_error ( EINFO_EIO_VF_RING_FULL ) +#define EINFO_EIO_VF_RING_FULL \ + __einfo_uniqify ( EINFO_EIO, ( 16 - 0 ), \ + "Virtual function event ring full" ) +#define EIO_PARAMETER \ + __einfo_error ( EINFO_EIO_PARAMETER ) +#define EINFO_EIO_PARAMETER \ + __einfo_uniqify ( EINFO_EIO, ( 17 - 0 ), \ + "Parameter error" ) +#define EIO_BANDWIDTH_OVERRUN \ + __einfo_error ( EINFO_EIO_BANDWIDTH_OVERRUN ) +#define EINFO_EIO_BANDWIDTH_OVERRUN \ + __einfo_uniqify ( EINFO_EIO, ( 18 - 0 ), \ + "Bandwidth overrun" ) +#define EIO_CONTEXT \ + __einfo_error ( EINFO_EIO_CONTEXT ) +#define EINFO_EIO_CONTEXT \ + __einfo_uniqify ( EINFO_EIO, ( 19 - 0 ), \ + "Context state error" ) +#define EIO_NO_PING \ + __einfo_error ( EINFO_EIO_NO_PING ) +#define EINFO_EIO_NO_PING \ + __einfo_uniqify ( EINFO_EIO, ( 20 - 0 ), \ + "No ping response" ) +#define EIO_RING_FULL \ + __einfo_error ( EINFO_EIO_RING_FULL ) +#define EINFO_EIO_RING_FULL \ + __einfo_uniqify ( EINFO_EIO, ( 21 - 0 ), \ + "Event ring full" ) +#define EIO_INCOMPATIBLE \ + __einfo_error ( EINFO_EIO_INCOMPATIBLE ) +#define EINFO_EIO_INCOMPATIBLE \ + __einfo_uniqify ( EINFO_EIO, ( 22 - 0 ), \ + "Incompatible device" ) +#define EIO_MISSED \ + __einfo_error ( EINFO_EIO_MISSED ) +#define EINFO_EIO_MISSED \ + __einfo_uniqify ( EINFO_EIO, ( 23 - 0 ), \ + "Missed service error" ) +#define EIO_CMD_STOPPED \ + __einfo_error ( EINFO_EIO_CMD_STOPPED ) +#define EINFO_EIO_CMD_STOPPED \ + __einfo_uniqify ( EINFO_EIO, ( 24 - 0 ), \ + "Command ring stopped" ) +#define EIO_CMD_ABORTED \ + __einfo_error ( EINFO_EIO_CMD_ABORTED ) +#define EINFO_EIO_CMD_ABORTED \ + __einfo_uniqify ( EINFO_EIO, ( 25 - 0 ), \ + "Command aborted" ) +#define EIO_STOP \ + __einfo_error ( EINFO_EIO_STOP ) +#define EINFO_EIO_STOP \ + __einfo_uniqify ( EINFO_EIO, ( 26 - 0 ), \ + "Stopped" ) +#define EIO_STOP_LEN \ + __einfo_error ( EINFO_EIO_STOP_LEN ) +#define EINFO_EIO_STOP_LEN \ + __einfo_uniqify ( EINFO_EIO, ( 27 - 0 ), \ + "Stopped - length invalid" ) +#define EIO_STOP_SHORT \ + __einfo_error ( EINFO_EIO_STOP_SHORT ) +#define EINFO_EIO_STOP_SHORT \ + __einfo_uniqify ( EINFO_EIO, ( 28 - 0 ), \ + "Stopped - short packet" ) +#define EIO_LATENCY \ + __einfo_error ( EINFO_EIO_LATENCY ) +#define EINFO_EIO_LATENCY \ + __einfo_uniqify ( EINFO_EIO, ( 29 - 0 ), \ + "Maximum exit latency too large" ) +#define EIO_ISOCH \ + __einfo_error ( EINFO_EIO_ISOCH ) +#define EINFO_EIO_ISOCH \ + __einfo_uniqify ( EINFO_EIO, ( 31 - 0 ), \ + "Isochronous buffer overrun" ) +#define EPROTO_LOST \ + __einfo_error ( EINFO_EPROTO_LOST ) +#define EINFO_EPROTO_LOST \ + __einfo_uniqify ( EINFO_EPROTO, ( 32 - 32 ), \ + "Event lost" ) +#define EPROTO_UNDEFINED \ + __einfo_error ( EINFO_EPROTO_UNDEFINED ) +#define EINFO_EPROTO_UNDEFINED \ + __einfo_uniqify ( EINFO_EPROTO, ( 33 - 32 ), \ + "Undefined error" ) +#define EPROTO_STREAM_ID \ + __einfo_error ( EINFO_EPROTO_STREAM_ID ) +#define EINFO_EPROTO_STREAM_ID \ + __einfo_uniqify ( EINFO_EPROTO, ( 34 - 32 ), \ + "Invalid stream ID" ) +#define EPROTO_SECONDARY \ + __einfo_error ( EINFO_EPROTO_SECONDARY ) +#define EINFO_EPROTO_SECONDARY \ + __einfo_uniqify ( EINFO_EPROTO, ( 35 - 32 ), \ + "Secondary bandwidth error" ) +#define EPROTO_SPLIT \ + __einfo_error ( EINFO_EPROTO_SPLIT ) +#define EINFO_EPROTO_SPLIT \ + __einfo_uniqify ( EINFO_EPROTO, ( 36 - 32 ), \ + "Split transaction error" ) +#define ECODE(code) \ + ( ( (code) < 32 ) ? \ + EUNIQ ( EINFO_EIO, ( (code) & 31 ), EIO_DATA, EIO_BABBLE, \ + EIO_USB, EIO_TRB, EIO_STALL, EIO_RESOURCE, \ + EIO_BANDWIDTH, EIO_NO_SLOTS, EIO_STREAM_TYPE, \ + EIO_SLOT, EIO_ENDPOINT, EIO_SHORT, EIO_UNDERRUN, \ + EIO_OVERRUN, EIO_VF_RING_FULL, EIO_PARAMETER, \ + EIO_BANDWIDTH_OVERRUN, EIO_CONTEXT, EIO_NO_PING, \ + EIO_RING_FULL, EIO_INCOMPATIBLE, EIO_MISSED, \ + EIO_CMD_STOPPED, EIO_CMD_ABORTED, EIO_STOP, \ + EIO_STOP_LEN, EIO_STOP_SHORT, EIO_LATENCY, \ + EIO_ISOCH ) : \ + ( (code) < 64 ) ? \ + EUNIQ ( EINFO_EPROTO, ( (code) & 31 ), EPROTO_LOST, \ + EPROTO_UNDEFINED, EPROTO_STREAM_ID, \ + EPROTO_SECONDARY, EPROTO_SPLIT ) : \ + EFAULT ) + +/****************************************************************************** + * + * Register access + * + ****************************************************************************** + */ + +/** + * Initialise device + * + * @v xhci xHCI device + * @v regs MMIO registers + */ +static void xhci_init ( struct xhci_device *xhci, void *regs ) { + uint32_t hcsparams1; + uint32_t hcsparams2; + uint32_t hccparams1; + uint32_t pagesize; + size_t caplength; + size_t rtsoff; + size_t dboff; + + /* Locate capability, operational, runtime, and doorbell registers */ + xhci->cap = regs; + caplength = readb ( xhci->cap + XHCI_CAP_CAPLENGTH ); + rtsoff = readl ( xhci->cap + XHCI_CAP_RTSOFF ); + dboff = readl ( xhci->cap + XHCI_CAP_DBOFF ); + xhci->op = ( xhci->cap + caplength ); + xhci->run = ( xhci->cap + rtsoff ); + xhci->db = ( xhci->cap + dboff ); + DBGC2 ( xhci, "XHCI %s cap %08lx op %08lx run %08lx db %08lx\n", + xhci->name, virt_to_phys ( xhci->cap ), + virt_to_phys ( xhci->op ), virt_to_phys ( xhci->run ), + virt_to_phys ( xhci->db ) ); + + /* Read structural parameters 1 */ + hcsparams1 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS1 ); + xhci->slots = XHCI_HCSPARAMS1_SLOTS ( hcsparams1 ); + xhci->intrs = XHCI_HCSPARAMS1_INTRS ( hcsparams1 ); + xhci->ports = XHCI_HCSPARAMS1_PORTS ( hcsparams1 ); + DBGC ( xhci, "XHCI %s has %d slots %d intrs %d ports\n", + xhci->name, xhci->slots, xhci->intrs, xhci->ports ); + + /* Read structural parameters 2 */ + hcsparams2 = readl ( xhci->cap + XHCI_CAP_HCSPARAMS2 ); + xhci->scratchpads = XHCI_HCSPARAMS2_SCRATCHPADS ( hcsparams2 ); + DBGC2 ( xhci, "XHCI %s needs %d scratchpads\n", + xhci->name, xhci->scratchpads ); + + /* Read capability parameters 1 */ + hccparams1 = readl ( xhci->cap + XHCI_CAP_HCCPARAMS1 ); + xhci->addr64 = XHCI_HCCPARAMS1_ADDR64 ( hccparams1 ); + xhci->csz_shift = XHCI_HCCPARAMS1_CSZ_SHIFT ( hccparams1 ); + xhci->xecp = XHCI_HCCPARAMS1_XECP ( hccparams1 ); + + /* Read page size */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + xhci->pagesize = XHCI_PAGESIZE ( pagesize ); + assert ( xhci->pagesize != 0 ); + assert ( ( ( xhci->pagesize ) & ( xhci->pagesize - 1 ) ) == 0 ); + DBGC2 ( xhci, "XHCI %s page size %zd bytes\n", + xhci->name, xhci->pagesize ); +} + +/** + * Find extended capability + * + * @v xhci xHCI device + * @v id Capability ID + * @v offset Offset to previous extended capability instance, or zero + * @ret offset Offset to extended capability, or zero if not found + */ +static unsigned int xhci_extended_capability ( struct xhci_device *xhci, + unsigned int id, + unsigned int offset ) { + uint32_t xecp; + unsigned int next; + + /* Locate the extended capability */ + while ( 1 ) { + + /* Locate first or next capability as applicable */ + if ( offset ) { + xecp = readl ( xhci->cap + offset ); + next = XHCI_XECP_NEXT ( xecp ); + } else { + next = xhci->xecp; + } + if ( ! next ) + return 0; + offset += next; + + /* Check if this is the requested capability */ + xecp = readl ( xhci->cap + offset ); + if ( XHCI_XECP_ID ( xecp ) == id ) + return offset; + } +} + +/** + * Write potentially 64-bit register + * + * @v xhci xHCI device + * @v value Value + * @v reg Register address + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +xhci_writeq ( struct xhci_device *xhci, physaddr_t value, void *reg ) { + + /* If this is a 32-bit build, then this can never fail + * (allowing the compiler to optimise out the error path). + */ + if ( sizeof ( value ) <= sizeof ( uint32_t ) ) { + writel ( value, reg ); + writel ( 0, ( reg + sizeof ( uint32_t ) ) ); + return 0; + } + + /* If the device does not support 64-bit addresses and this + * address is outside the 32-bit address space, then fail. + */ + if ( ( value & ~0xffffffffULL ) && ! xhci->addr64 ) { + DBGC ( xhci, "XHCI %s cannot access address %lx\n", + xhci->name, value ); + return -ENOTSUP; + } + + /* If this is a 64-bit build, then writeq() is available */ + writeq ( value, reg ); + return 0; +} + +/** + * Calculate buffer alignment + * + * @v len Length + * @ret align Buffer alignment + * + * Determine alignment required for a buffer which must be aligned to + * at least XHCI_MIN_ALIGN and which must not cross a page boundary. + */ +static inline size_t xhci_align ( size_t len ) { + size_t align; + + /* Align to own length (rounded up to a power of two) */ + align = ( 1 << fls ( len - 1 ) ); + + /* Round up to XHCI_MIN_ALIGN if needed */ + if ( align < XHCI_MIN_ALIGN ) + align = XHCI_MIN_ALIGN; + + return align; +} + +/** + * Calculate device context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_device_context_offset ( struct xhci_device *xhci, + unsigned int ctx ) { + + return ( XHCI_DCI ( ctx ) << xhci->csz_shift ); +} + +/** + * Calculate input context offset + * + * @v xhci xHCI device + * @v ctx Context index + */ +static inline size_t xhci_input_context_offset ( struct xhci_device *xhci, + unsigned int ctx ) { + + return ( XHCI_ICI ( ctx ) << xhci->csz_shift ); +} + +/****************************************************************************** + * + * Diagnostics + * + ****************************************************************************** + */ + +/** + * Dump host controller registers + * + * @v xhci xHCI device + */ +static inline void xhci_dump ( struct xhci_device *xhci ) { + uint32_t usbcmd; + uint32_t usbsts; + uint32_t pagesize; + uint32_t dnctrl; + uint32_t config; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump USBCMD */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + DBGC ( xhci, "XHCI %s USBCMD %08x%s%s\n", xhci->name, usbcmd, + ( ( usbcmd & XHCI_USBCMD_RUN ) ? " run" : "" ), + ( ( usbcmd & XHCI_USBCMD_HCRST ) ? " hcrst" : "" ) ); + + /* Dump USBSTS */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + DBGC ( xhci, "XHCI %s USBSTS %08x%s\n", xhci->name, usbsts, + ( ( usbsts & XHCI_USBSTS_HCH ) ? " hch" : "" ) ); + + /* Dump PAGESIZE */ + pagesize = readl ( xhci->op + XHCI_OP_PAGESIZE ); + DBGC ( xhci, "XHCI %s PAGESIZE %08x\n", xhci->name, pagesize ); + + /* Dump DNCTRL */ + dnctrl = readl ( xhci->op + XHCI_OP_DNCTRL ); + DBGC ( xhci, "XHCI %s DNCTRL %08x\n", xhci->name, dnctrl ); + + /* Dump CONFIG */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + DBGC ( xhci, "XHCI %s CONFIG %08x\n", xhci->name, config ); +} + +/** + * Dump port registers + * + * @v xhci xHCI device + * @v port Port number + */ +static inline void xhci_dump_port ( struct xhci_device *xhci, + unsigned int port ) { + uint32_t portsc; + uint32_t portpmsc; + uint32_t portli; + uint32_t porthlpmc; + + /* Do nothing unless debugging is enabled */ + if ( ! DBG_LOG ) + return; + + /* Dump PORTSC */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTSC %08x%s%s%s%s psiv=%d\n", + xhci->name, port, portsc, + ( ( portsc & XHCI_PORTSC_CCS ) ? " ccs" : "" ), + ( ( portsc & XHCI_PORTSC_PED ) ? " ped" : "" ), + ( ( portsc & XHCI_PORTSC_PR ) ? " pr" : "" ), + ( ( portsc & XHCI_PORTSC_PP ) ? " pp" : "" ), + XHCI_PORTSC_PSIV ( portsc ) ); + + /* Dump PORTPMSC */ + portpmsc = readl ( xhci->op + XHCI_OP_PORTPMSC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTPMSC %08x\n", xhci->name, port, portpmsc ); + + /* Dump PORTLI */ + portli = readl ( xhci->op + XHCI_OP_PORTLI ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTLI %08x\n", xhci->name, port, portli ); + + /* Dump PORTHLPMC */ + porthlpmc = readl ( xhci->op + XHCI_OP_PORTHLPMC ( port ) ); + DBGC ( xhci, "XHCI %s-%d PORTHLPMC %08x\n", + xhci->name, port, porthlpmc ); +} + +/****************************************************************************** + * + * USB legacy support + * + ****************************************************************************** + */ + +/** Prevent the release of ownership back to BIOS */ +static int xhci_legacy_prevent_release; + +/** + * Initialise USB legacy support + * + * @v xhci xHCI device + */ +static void xhci_legacy_init ( struct xhci_device *xhci ) { + unsigned int legacy; + uint8_t bios; + + /* Locate USB legacy support capability (if present) */ + legacy = xhci_extended_capability ( xhci, XHCI_XECP_ID_LEGACY, 0 ); + if ( ! legacy ) { + /* Not an error; capability may not be present */ + DBGC ( xhci, "XHCI %s has no USB legacy support capability\n", + xhci->name ); + return; + } + + /* Check if legacy USB support is enabled */ + bios = readb ( xhci->cap + legacy + XHCI_USBLEGSUP_BIOS ); + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + /* Not an error; already owned by OS */ + DBGC ( xhci, "XHCI %s USB legacy support already disabled\n", + xhci->name ); + return; + } + + /* Record presence of USB legacy support capability */ + xhci->legacy = legacy; +} + +/** + * Claim ownership from BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_claim ( struct xhci_device *xhci ) { + uint32_t ctlsts; + uint8_t bios; + unsigned int i; + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Claim ownership */ + writeb ( XHCI_USBLEGSUP_OS_OWNED, + xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + + /* Wait for BIOS to release ownership */ + for ( i = 0 ; i < XHCI_USBLEGSUP_MAX_WAIT_MS ; i++ ) { + + /* Check if BIOS has released ownership */ + bios = readb ( xhci->cap + xhci->legacy + XHCI_USBLEGSUP_BIOS ); + if ( ! ( bios & XHCI_USBLEGSUP_BIOS_OWNED ) ) { + DBGC ( xhci, "XHCI %s claimed ownership from BIOS\n", + xhci->name ); + ctlsts = readl ( xhci->cap + xhci->legacy + + XHCI_USBLEGSUP_CTLSTS ); + if ( ctlsts ) { + DBGC ( xhci, "XHCI %s warning: BIOS retained " + "SMIs: %08x\n", xhci->name, ctlsts ); + } + return; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* BIOS did not release ownership. Claim it forcibly by + * disabling all SMIs. + */ + DBGC ( xhci, "XHCI %s could not claim ownership from BIOS: forcibly " + "disabling SMIs\n", xhci->name ); + writel ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_CTLSTS ); +} + +/** + * Release ownership back to BIOS + * + * @v xhci xHCI device + */ +static void xhci_legacy_release ( struct xhci_device *xhci ) { + + /* Do nothing unless legacy support capability is present */ + if ( ! xhci->legacy ) + return; + + /* Do nothing if releasing ownership is prevented */ + if ( xhci_legacy_prevent_release ) { + DBGC ( xhci, "XHCI %s not releasing ownership to BIOS\n", + xhci->name ); + return; + } + + /* Release ownership */ + writeb ( 0, xhci->cap + xhci->legacy + XHCI_USBLEGSUP_OS ); + DBGC ( xhci, "XHCI %s released ownership to BIOS\n", xhci->name ); +} + +/****************************************************************************** + * + * Supported protocols + * + ****************************************************************************** + */ + +/** + * Transcribe port speed (for debugging) + * + * @v psi Protocol speed ID + * @ret speed Transcribed speed + */ +static inline const char * xhci_speed_name ( uint32_t psi ) { + static const char *exponents[4] = { "", "k", "M", "G" }; + static char buf[ 10 /* "xxxxxXbps" + NUL */ ]; + unsigned int mantissa; + unsigned int exponent; + + /* Extract mantissa and exponent */ + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + + /* Transcribe speed */ + snprintf ( buf, sizeof ( buf ), "%d%sbps", + mantissa, exponents[exponent] ); + return buf; +} + +/** + * Find supported protocol extended capability for a port + * + * @v xhci xHCI device + * @v port Port number + * @ret supported Offset to extended capability, or zero if not found + */ +static unsigned int xhci_supported_protocol ( struct xhci_device *xhci, + unsigned int port ) { + unsigned int supported = 0; + unsigned int offset; + unsigned int count; + uint32_t ports; + + /* Iterate over all supported protocol structures */ + while ( ( supported = xhci_extended_capability ( xhci, + XHCI_XECP_ID_SUPPORTED, + supported ) ) ) { + + /* Determine port range */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + offset = XHCI_SUPPORTED_PORTS_OFFSET ( ports ); + count = XHCI_SUPPORTED_PORTS_COUNT ( ports ); + + /* Check if port lies within this range */ + if ( ( port - offset ) < count ) + return supported; + } + + DBGC ( xhci, "XHCI %s-%d has no supported protocol\n", + xhci->name, port ); + return 0; +} + +/** + * Find port protocol + * + * @v xhci xHCI device + * @v port Port number + * @ret protocol USB protocol, or zero if not found + */ +static unsigned int xhci_port_protocol ( struct xhci_device *xhci, + unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + union { + uint32_t raw; + char text[5]; + } name; + unsigned int protocol; + unsigned int type; + unsigned int psic; + unsigned int psiv; + unsigned int i; + uint32_t revision; + uint32_t ports; + uint32_t slot; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return 0; + + /* Determine protocol version */ + revision = readl ( xhci->cap + supported + XHCI_SUPPORTED_REVISION ); + protocol = XHCI_SUPPORTED_REVISION_VER ( revision ); + + /* Describe port protocol */ + if ( DBG_EXTRA ) { + name.raw = cpu_to_le32 ( readl ( xhci->cap + supported + + XHCI_SUPPORTED_NAME ) ); + name.text[4] = '\0'; + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + DBGC2 ( xhci, "XHCI %s-%d %sv%04x type %d", + xhci->name, port, name.text, protocol, type ); + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + if ( psic ) { + DBGC2 ( xhci, " speeds" ); + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + + XHCI_SUPPORTED_PSI ( i ) ); + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + DBGC2 ( xhci, " %d:%s", psiv, + xhci_speed_name ( psi ) ); + } + } + if ( xhci->quirks & XHCI_BAD_PSIV ) + DBGC2 ( xhci, " (ignored)" ); + DBGC2 ( xhci, "\n" ); + } + + return protocol; +} + +/** + * Find port slot type + * + * @v xhci xHCI device + * @v port Port number + * @ret type Slot type, or negative error + */ +static int xhci_port_slot_type ( struct xhci_device *xhci, unsigned int port ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int type; + uint32_t slot; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get slot type */ + slot = readl ( xhci->cap + supported + XHCI_SUPPORTED_SLOT ); + type = XHCI_SUPPORTED_SLOT_TYPE ( slot ); + + return type; +} + +/** + * Find port speed + * + * @v xhci xHCI device + * @v port Port number + * @v psiv Protocol speed ID value + * @ret speed Port speed, or negative error + */ +static int xhci_port_speed ( struct xhci_device *xhci, unsigned int port, + unsigned int psiv ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int speed; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use the default mappings if applicable */ + if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { + switch ( psiv ) { + case XHCI_SPEED_LOW : return USB_SPEED_LOW; + case XHCI_SPEED_FULL : return USB_SPEED_FULL; + case XHCI_SPEED_HIGH : return USB_SPEED_HIGH; + case XHCI_SPEED_SUPER : return USB_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d non-standard PSI value %d\n", + xhci->name, port, psiv ); + return -ENOTSUP; + } + } + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); + if ( psiv == XHCI_SUPPORTED_PSI_VALUE ( psi ) ) { + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + speed = USB_SPEED ( mantissa, exponent ); + return speed; + } + } + + DBGC ( xhci, "XHCI %s-%d spurious PSI value %d\n", + xhci->name, port, psiv ); + return -ENOENT; +} + +/** + * Find protocol speed ID value + * + * @v xhci xHCI device + * @v port Port number + * @v speed USB speed + * @ret psiv Protocol speed ID value, or negative error + */ +static int xhci_port_psiv ( struct xhci_device *xhci, unsigned int port, + unsigned int speed ) { + unsigned int supported = xhci_supported_protocol ( xhci, port ); + unsigned int psic; + unsigned int mantissa; + unsigned int exponent; + unsigned int psiv; + unsigned int i; + uint32_t ports; + uint32_t psi; + + /* Fail if there is no supported protocol */ + if ( ! supported ) + return -ENOTSUP; + + /* Get protocol speed ID count */ + ports = readl ( xhci->cap + supported + XHCI_SUPPORTED_PORTS ); + psic = XHCI_SUPPORTED_PORTS_PSIC ( ports ); + + /* Use the default mappings if applicable */ + if ( ( psic == 0 ) || ( xhci->quirks & XHCI_BAD_PSIV ) ) { + switch ( speed ) { + case USB_SPEED_LOW : return XHCI_SPEED_LOW; + case USB_SPEED_FULL : return XHCI_SPEED_FULL; + case USB_SPEED_HIGH : return XHCI_SPEED_HIGH; + case USB_SPEED_SUPER : return XHCI_SPEED_SUPER; + default: + DBGC ( xhci, "XHCI %s-%d non-standard speed %d\n", + xhci->name, port, speed ); + return -ENOTSUP; + } + } + + /* Iterate over PSI dwords looking for a match */ + for ( i = 0 ; i < psic ; i++ ) { + psi = readl ( xhci->cap + supported + XHCI_SUPPORTED_PSI ( i )); + mantissa = XHCI_SUPPORTED_PSI_MANTISSA ( psi ); + exponent = XHCI_SUPPORTED_PSI_EXPONENT ( psi ); + if ( speed == USB_SPEED ( mantissa, exponent ) ) { + psiv = XHCI_SUPPORTED_PSI_VALUE ( psi ); + return psiv; + } + } + + DBGC ( xhci, "XHCI %s-%d unrepresentable speed %#x\n", + xhci->name, port, speed ); + return -ENOENT; +} + +/****************************************************************************** + * + * Device context base address array + * + ****************************************************************************** + */ + +/** + * Allocate device context base address array + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_dcbaa_alloc ( struct xhci_device *xhci ) { + size_t len; + physaddr_t dcbaap; + int rc; + + /* Allocate and initialise structure. Must be at least + * 64-byte aligned and must not cross a page boundary, so + * align on its own size (rounded up to a power of two and + * with a minimum of 64 bytes). + */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); + xhci->dcbaa = malloc_dma ( len, xhci_align ( len ) ); + if ( ! xhci->dcbaa ) { + DBGC ( xhci, "XHCI %s could not allocate DCBAA\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset ( xhci->dcbaa, 0, len ); + + /* Program DCBAA pointer */ + dcbaap = virt_to_phys ( xhci->dcbaa ); + if ( ( rc = xhci_writeq ( xhci, dcbaap, + xhci->op + XHCI_OP_DCBAAP ) ) != 0 ) + goto err_writeq; + + DBGC2 ( xhci, "XHCI %s DCBAA at [%08lx,%08lx)\n", + xhci->name, dcbaap, ( dcbaap + len ) ); + return 0; + + err_writeq: + free_dma ( xhci->dcbaa, len ); + err_alloc: + return rc; +} + +/** + * Free device context base address array + * + * @v xhci xHCI device + */ +static void xhci_dcbaa_free ( struct xhci_device *xhci ) { + size_t len; + unsigned int i; + + /* Sanity check */ + for ( i = 0 ; i <= xhci->slots ; i++ ) + assert ( xhci->dcbaa[i] == 0 ); + + /* Clear DCBAA pointer */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_DCBAAP ); + + /* Free DCBAA */ + len = ( ( xhci->slots + 1 ) * sizeof ( xhci->dcbaa[0] ) ); + free_dma ( xhci->dcbaa, len ); +} + +/****************************************************************************** + * + * Scratchpad buffers + * + ****************************************************************************** + */ + +/** + * Allocate scratchpad buffers + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_scratchpad_alloc ( struct xhci_device *xhci ) { + size_t array_len; + size_t len; + physaddr_t phys; + unsigned int i; + int rc; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! xhci->scratchpads ) + return 0; + + /* Allocate scratchpads */ + len = ( xhci->scratchpads * xhci->pagesize ); + xhci->scratchpad = umalloc ( len ); + if ( ! xhci->scratchpad ) { + DBGC ( xhci, "XHCI %s could not allocate scratchpad buffers\n", + xhci->name ); + rc = -ENOMEM; + goto err_alloc; + } + memset_user ( xhci->scratchpad, 0, 0, len ); + + /* Allocate scratchpad array */ + array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); + xhci->scratchpad_array = + malloc_dma ( array_len, xhci_align ( array_len ) ); + if ( ! xhci->scratchpad_array ) { + DBGC ( xhci, "XHCI %s could not allocate scratchpad buffer " + "array\n", xhci->name ); + rc = -ENOMEM; + goto err_alloc_array; + } + + /* Populate scratchpad array */ + for ( i = 0 ; i < xhci->scratchpads ; i++ ) { + phys = user_to_phys ( xhci->scratchpad, ( i * xhci->pagesize )); + xhci->scratchpad_array[i] = phys; + } + + /* Set scratchpad array pointer */ + assert ( xhci->dcbaa != NULL ); + xhci->dcbaa[0] = cpu_to_le64 ( virt_to_phys ( xhci->scratchpad_array )); + + DBGC2 ( xhci, "XHCI %s scratchpad [%08lx,%08lx) array [%08lx,%08lx)\n", + xhci->name, user_to_phys ( xhci->scratchpad, 0 ), + user_to_phys ( xhci->scratchpad, len ), + virt_to_phys ( xhci->scratchpad_array ), + ( virt_to_phys ( xhci->scratchpad_array ) + array_len ) ); + return 0; + + free_dma ( xhci->scratchpad_array, array_len ); + err_alloc_array: + ufree ( xhci->scratchpad ); + err_alloc: + return rc; +} + +/** + * Free scratchpad buffers + * + * @v xhci xHCI device + */ +static void xhci_scratchpad_free ( struct xhci_device *xhci ) { + size_t array_len; + + /* Do nothing if no scratchpad buffers are used */ + if ( ! xhci->scratchpads ) + return; + + /* Clear scratchpad array pointer */ + assert ( xhci->dcbaa != NULL ); + xhci->dcbaa[0] = 0; + + /* Free scratchpad array */ + array_len = ( xhci->scratchpads * sizeof ( xhci->scratchpad_array[0] )); + free_dma ( xhci->scratchpad_array, array_len ); + + /* Free scratchpads */ + ufree ( xhci->scratchpad ); +} + +/****************************************************************************** + * + * Run / stop / reset + * + ****************************************************************************** + */ + +/** + * Start xHCI device + * + * @v xhci xHCI device + */ +static void xhci_run ( struct xhci_device *xhci ) { + uint32_t config; + uint32_t usbcmd; + + /* Configure number of device slots */ + config = readl ( xhci->op + XHCI_OP_CONFIG ); + config &= ~XHCI_CONFIG_MAX_SLOTS_EN_MASK; + config |= XHCI_CONFIG_MAX_SLOTS_EN ( xhci->slots ); + writel ( config, xhci->op + XHCI_OP_CONFIG ); + + /* Set run/stop bit */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd |= XHCI_USBCMD_RUN; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); +} + +/** + * Stop xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_stop ( struct xhci_device *xhci ) { + uint32_t usbcmd; + uint32_t usbsts; + unsigned int i; + + /* Clear run/stop bit */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + usbcmd &= ~XHCI_USBCMD_RUN; + writel ( usbcmd, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for device to stop */ + for ( i = 0 ; i < XHCI_STOP_MAX_WAIT_MS ; i++ ) { + + /* Check if device is stopped */ + usbsts = readl ( xhci->op + XHCI_OP_USBSTS ); + if ( usbsts & XHCI_USBSTS_HCH ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s timed out waiting for stop\n", xhci->name ); + return -ETIMEDOUT; +} + +/** + * Reset xHCI device + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_reset ( struct xhci_device *xhci ) { + uint32_t usbcmd; + unsigned int i; + int rc; + + /* The xHCI specification states that resetting a running + * device may result in undefined behaviour, so try stopping + * it first. + */ + if ( ( rc = xhci_stop ( xhci ) ) != 0 ) { + /* Ignore errors and attempt to reset the device anyway */ + } + + /* Reset device */ + writel ( XHCI_USBCMD_HCRST, xhci->op + XHCI_OP_USBCMD ); + + /* Wait for reset to complete */ + for ( i = 0 ; i < XHCI_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check if reset is complete */ + usbcmd = readl ( xhci->op + XHCI_OP_USBCMD ); + if ( ! ( usbcmd & XHCI_USBCMD_HCRST ) ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s timed out waiting for reset\n", xhci->name ); + return -ETIMEDOUT; +} + +/****************************************************************************** + * + * Transfer request blocks + * + ****************************************************************************** + */ + +/** + * Allocate transfer request block ring + * + * @v xhci xHCI device + * @v ring TRB ring + * @v shift Ring size (log2) + * @v slot Device slot + * @v target Doorbell target + * @v stream Doorbell stream ID + * @ret rc Return status code + */ +static int xhci_ring_alloc ( struct xhci_device *xhci, + struct xhci_trb_ring *ring, + unsigned int shift, unsigned int slot, + unsigned int target, unsigned int stream ) { + struct xhci_trb_link *link; + unsigned int count; + int rc; + + /* Sanity check */ + assert ( shift > 0 ); + + /* Initialise structure */ + memset ( ring, 0, sizeof ( *ring ) ); + ring->shift = shift; + count = ( 1U << shift ); + ring->mask = ( count - 1 ); + ring->len = ( ( count + 1 /* Link TRB */ ) * sizeof ( ring->trb[0] ) ); + ring->db = ( xhci->db + ( slot * sizeof ( ring->dbval ) ) ); + ring->dbval = XHCI_DBVAL ( target, stream ); + + /* Allocate I/O buffers */ + ring->iobuf = zalloc ( count * sizeof ( ring->iobuf[0] ) ); + if ( ! ring->iobuf ) { + rc = -ENOMEM; + goto err_alloc_iobuf; + } + + /* Allocate TRBs */ + ring->trb = malloc_dma ( ring->len, xhci_align ( ring->len ) ); + if ( ! ring->trb ) { + rc = -ENOMEM; + goto err_alloc_trb; + } + memset ( ring->trb, 0, ring->len ); + + /* Initialise Link TRB */ + link = &ring->trb[count].link; + link->next = cpu_to_le64 ( virt_to_phys ( ring->trb ) ); + link->flags = XHCI_TRB_TC; + link->type = XHCI_TRB_LINK; + ring->link = link; + + return 0; + + free_dma ( ring->trb, ring->len ); + err_alloc_trb: + free ( ring->iobuf ); + err_alloc_iobuf: + return rc; +} + +/** + * Reset transfer request block ring + * + * @v ring TRB ring + */ +static void xhci_ring_reset ( struct xhci_trb_ring *ring ) { + unsigned int count = ( 1U << ring->shift ); + + /* Reset producer and consumer counters */ + ring->prod = 0; + ring->cons = 0; + + /* Reset TRBs (except Link TRB) */ + memset ( ring->trb, 0, ( count * sizeof ( ring->trb[0] ) ) ); +} + +/** + * Free transfer request block ring + * + * @v ring TRB ring + */ +static void xhci_ring_free ( struct xhci_trb_ring *ring ) { + unsigned int count = ( 1U << ring->shift ); + unsigned int i; + + /* Sanity checks */ + assert ( ring->cons == ring->prod ); + for ( i = 0 ; i < count ; i++ ) + assert ( ring->iobuf[i] == NULL ); + + /* Free TRBs */ + free_dma ( ring->trb, ring->len ); + + /* Free I/O buffers */ + free ( ring->iobuf ); +} + +/** + * Enqueue a transfer request block + * + * @v ring TRB ring + * @v iobuf I/O buffer (if any) + * @v trb Transfer request block (with empty Cycle flag) + * @ret rc Return status code + * + * This operation does not implicitly ring the doorbell register. + */ +static int xhci_enqueue ( struct xhci_trb_ring *ring, struct io_buffer *iobuf, + const union xhci_trb *trb ) { + union xhci_trb *dest; + unsigned int prod; + unsigned int mask; + unsigned int index; + unsigned int cycle; + + /* Sanity check */ + assert ( ! ( trb->common.flags & XHCI_TRB_C ) ); + + /* Fail if ring is full */ + if ( ! xhci_ring_remaining ( ring ) ) + return -ENOBUFS; + + /* Update producer counter (and link TRB, if applicable) */ + prod = ring->prod++; + mask = ring->mask; + cycle = ( ( ~( prod >> ring->shift ) ) & XHCI_TRB_C ); + index = ( prod & mask ); + if ( index == 0 ) + ring->link->flags = ( XHCI_TRB_TC | ( cycle ^ XHCI_TRB_C ) ); + + /* Record I/O buffer */ + ring->iobuf[index] = iobuf; + + /* Enqueue TRB */ + dest = &ring->trb[index]; + dest->template.parameter = trb->template.parameter; + dest->template.status = trb->template.status; + wmb(); + dest->template.control = ( trb->template.control | + cpu_to_le32 ( cycle ) ); + + return 0; +} + +/** + * Dequeue a transfer request block + * + * @v ring TRB ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * xhci_dequeue ( struct xhci_trb_ring *ring ) { + struct io_buffer *iobuf; + unsigned int cons; + unsigned int mask; + unsigned int index; + + /* Sanity check */ + assert ( xhci_ring_fill ( ring ) != 0 ); + + /* Update consumer counter */ + cons = ring->cons++; + mask = ring->mask; + index = ( cons & mask ); + + /* Retrieve I/O buffer */ + iobuf = ring->iobuf[index]; + ring->iobuf[index] = NULL; + + return iobuf; +} + +/** + * Enqueue multiple transfer request blocks + * + * @v ring TRB ring + * @v iobuf I/O buffer + * @v trbs Transfer request blocks (with empty Cycle flag) + * @v count Number of transfer request blocks + * @ret rc Return status code + * + * This operation does not implicitly ring the doorbell register. + */ +static int xhci_enqueue_multi ( struct xhci_trb_ring *ring, + struct io_buffer *iobuf, + const union xhci_trb *trbs, + unsigned int count ) { + const union xhci_trb *trb = trbs; + int rc; + + /* Sanity check */ + assert ( iobuf != NULL ); + + /* Fail if ring does not have sufficient space */ + if ( xhci_ring_remaining ( ring ) < count ) + return -ENOBUFS; + + /* Enqueue each TRB, recording the I/O buffer with the final TRB */ + while ( count-- ) { + rc = xhci_enqueue ( ring, ( count ? NULL : iobuf ), trb++ ); + assert ( rc == 0 ); /* Should never be able to fail */ + } + + return 0; +} + +/** + * Dequeue multiple transfer request blocks + * + * @v ring TRB ring + * @ret iobuf I/O buffer + */ +static struct io_buffer * xhci_dequeue_multi ( struct xhci_trb_ring *ring ) { + struct io_buffer *iobuf; + + /* Dequeue TRBs until we reach the final TRB for an I/O buffer */ + do { + iobuf = xhci_dequeue ( ring ); + } while ( iobuf == NULL ); + + return iobuf; +} + +/** + * Ring doorbell register + * + * @v ring TRB ring + */ +static inline __attribute__ (( always_inline )) void +xhci_doorbell ( struct xhci_trb_ring *ring ) { + + wmb(); + writel ( ring->dbval, ring->db ); +} + +/****************************************************************************** + * + * Command and event rings + * + ****************************************************************************** + */ + +/** + * Allocate command ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_command_alloc ( struct xhci_device *xhci ) { + physaddr_t crp; + int rc; + + /* Allocate TRB ring */ + if ( ( rc = xhci_ring_alloc ( xhci, &xhci->command, XHCI_CMD_TRBS_LOG2, + 0, 0, 0 ) ) != 0 ) + goto err_ring_alloc; + + /* Program command ring control register */ + crp = virt_to_phys ( xhci->command.trb ); + if ( ( rc = xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), + xhci->op + XHCI_OP_CRCR ) ) != 0 ) + goto err_writeq; + + DBGC2 ( xhci, "XHCI %s CRCR at [%08lx,%08lx)\n", + xhci->name, crp, ( crp + xhci->command.len ) ); + return 0; + + err_writeq: + xhci_ring_free ( &xhci->command ); + err_ring_alloc: + return rc; +} + +/** + * Free command ring + * + * @v xhci xHCI device + */ +static void xhci_command_free ( struct xhci_device *xhci ) { + + /* Sanity check */ + assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Clear command ring control register */ + xhci_writeq ( xhci, 0, xhci->op + XHCI_OP_CRCR ); + + /* Free TRB ring */ + xhci_ring_free ( &xhci->command ); +} + +/** + * Allocate event ring + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static int xhci_event_alloc ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + unsigned int count; + size_t len; + int rc; + + /* Allocate event ring */ + count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); + len = ( count * sizeof ( event->trb[0] ) ); + event->trb = malloc_dma ( len, xhci_align ( len ) ); + if ( ! event->trb ) { + rc = -ENOMEM; + goto err_alloc_trb; + } + memset ( event->trb, 0, len ); + + /* Allocate event ring segment table */ + event->segment = malloc_dma ( sizeof ( event->segment[0] ), + xhci_align ( sizeof (event->segment[0]))); + if ( ! event->segment ) { + rc = -ENOMEM; + goto err_alloc_segment; + } + memset ( event->segment, 0, sizeof ( event->segment[0] ) ); + event->segment[0].base = cpu_to_le64 ( virt_to_phys ( event->trb ) ); + event->segment[0].count = cpu_to_le32 ( count ); + + /* Program event ring registers */ + writel ( 1, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); + if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->trb ), + xhci->run + XHCI_RUN_ERDP ( 0 ) ) ) != 0 ) + goto err_writeq_erdp; + if ( ( rc = xhci_writeq ( xhci, virt_to_phys ( event->segment ), + xhci->run + XHCI_RUN_ERSTBA ( 0 ) ) ) != 0 ) + goto err_writeq_erstba; + + DBGC2 ( xhci, "XHCI %s event ring [%08lx,%08lx) table [%08lx,%08lx)\n", + xhci->name, virt_to_phys ( event->trb ), + ( virt_to_phys ( event->trb ) + len ), + virt_to_phys ( event->segment ), + ( virt_to_phys ( event->segment ) + + sizeof (event->segment[0] ) ) ); + return 0; + + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + err_writeq_erstba: + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + err_writeq_erdp: + free_dma ( event->trb, len ); + err_alloc_segment: + free_dma ( event->segment, sizeof ( event->segment[0] ) ); + err_alloc_trb: + return rc; +} + +/** + * Free event ring + * + * @v xhci xHCI device + */ +static void xhci_event_free ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + unsigned int count; + size_t len; + + /* Clear event ring registers */ + writel ( 0, xhci->run + XHCI_RUN_ERSTSZ ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERSTBA ( 0 ) ); + xhci_writeq ( xhci, 0, xhci->run + XHCI_RUN_ERDP ( 0 ) ); + + /* Free event ring segment table */ + free_dma ( event->segment, sizeof ( event->segment[0] ) ); + + /* Free event ring */ + count = ( 1 << XHCI_EVENT_TRBS_LOG2 ); + len = ( count * sizeof ( event->trb[0] ) ); + free_dma ( event->trb, len ); +} + +/** + * Handle transfer event + * + * @v xhci xHCI device + * @v trb Transfer event TRB + */ +static void xhci_transfer ( struct xhci_device *xhci, + struct xhci_trb_transfer *trb ) { + struct xhci_slot *slot; + struct xhci_endpoint *endpoint; + struct io_buffer *iobuf; + int rc; + + /* Profile transfer events */ + profile_start ( &xhci_transfer_profiler ); + + /* Identify slot */ + if ( ( trb->slot > xhci->slots ) || + ( ( slot = xhci->slot[trb->slot] ) == NULL ) ) { + DBGC ( xhci, "XHCI %s transfer event invalid slot %d:\n", + xhci->name, trb->slot ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Identify endpoint */ + if ( ( trb->endpoint > XHCI_CTX_END ) || + ( ( endpoint = slot->endpoint[trb->endpoint] ) == NULL ) ) { + DBGC ( xhci, "XHCI %s slot %d transfer event invalid epid " + "%d:\n", xhci->name, slot->id, trb->endpoint ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Dequeue TRB(s) */ + iobuf = xhci_dequeue_multi ( &endpoint->ring ); + assert ( iobuf != NULL ); + + /* Check for errors */ + if ( ! ( ( trb->code == XHCI_CMPLT_SUCCESS ) || + ( trb->code == XHCI_CMPLT_SHORT ) ) ) { + + /* Construct error */ + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s slot %d ctx %d failed (code %d): %s\n", + xhci->name, slot->id, endpoint->ctx, trb->code, + strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + + /* Sanity check */ + assert ( ( endpoint->context->state & XHCI_ENDPOINT_STATE_MASK ) + != XHCI_ENDPOINT_RUNNING ); + + /* Report failure to USB core */ + usb_complete_err ( endpoint->ep, iobuf, rc ); + return; + } + + /* Record actual transfer size */ + iob_unput ( iobuf, le16_to_cpu ( trb->residual ) ); + + /* Sanity check (for successful completions only) */ + assert ( xhci_ring_consumed ( &endpoint->ring ) == + le64_to_cpu ( trb->transfer ) ); + + /* Report completion to USB core */ + usb_complete ( endpoint->ep, iobuf ); + profile_stop ( &xhci_transfer_profiler ); +} + +/** + * Handle command completion event + * + * @v xhci xHCI device + * @v trb Command completion event + */ +static void xhci_complete ( struct xhci_device *xhci, + struct xhci_trb_complete *trb ) { + int rc; + + /* Ignore "command ring stopped" notifications */ + if ( trb->code == XHCI_CMPLT_CMD_STOPPED ) { + DBGC2 ( xhci, "XHCI %s command ring stopped\n", xhci->name ); + return; + } + + /* Ignore unexpected completions */ + if ( ! xhci->pending ) { + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s unexpected completion (code %d): %s\n", + xhci->name, trb->code, strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return; + } + + /* Dequeue command TRB */ + xhci_dequeue ( &xhci->command ); + + /* Sanity check */ + assert ( xhci_ring_consumed ( &xhci->command ) == + le64_to_cpu ( trb->command ) ); + + /* Record completion */ + memcpy ( xhci->pending, trb, sizeof ( *xhci->pending ) ); + xhci->pending = NULL; +} + +/** + * Handle port status event + * + * @v xhci xHCI device + * @v trb Port status event + */ +static void xhci_port_status ( struct xhci_device *xhci, + struct xhci_trb_port_status *trb ) { + struct usb_port *port = usb_port ( xhci->bus->hub, trb->port ); + uint32_t portsc; + + /* Sanity check */ + assert ( ( trb->port > 0 ) && ( trb->port <= xhci->ports ) ); + + /* Record disconnections and clear changes */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( trb->port ) ); + port->disconnected |= ( portsc & XHCI_PORTSC_CSC ); + portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( trb->port ) ); + + /* Report port status change */ + usb_port_changed ( port ); +} + +/** + * Handle host controller event + * + * @v xhci xHCI device + * @v trb Host controller event + */ +static void xhci_host_controller ( struct xhci_device *xhci, + struct xhci_trb_host_controller *trb ) { + int rc; + + /* Construct error */ + rc = -ECODE ( trb->code ); + DBGC ( xhci, "XHCI %s host controller event (code %d): %s\n", + xhci->name, trb->code, strerror ( rc ) ); +} + +/** + * Poll event ring + * + * @v xhci xHCI device + */ +static void xhci_event_poll ( struct xhci_device *xhci ) { + struct xhci_event_ring *event = &xhci->event; + union xhci_trb *trb; + unsigned int shift = XHCI_EVENT_TRBS_LOG2; + unsigned int count = ( 1 << shift ); + unsigned int mask = ( count - 1 ); + unsigned int consumed; + unsigned int type; + + /* Poll for events */ + profile_start ( &xhci_event_profiler ); + for ( consumed = 0 ; ; consumed++ ) { + + /* Stop if we reach an empty TRB */ + rmb(); + trb = &event->trb[ event->cons & mask ]; + if ( ! ( ( trb->common.flags ^ + ( event->cons >> shift ) ) & XHCI_TRB_C ) ) + break; + + /* Handle TRB */ + type = ( trb->common.type & XHCI_TRB_TYPE_MASK ); + switch ( type ) { + + case XHCI_TRB_TRANSFER : + xhci_transfer ( xhci, &trb->transfer ); + break; + + case XHCI_TRB_COMPLETE : + xhci_complete ( xhci, &trb->complete ); + break; + + case XHCI_TRB_PORT_STATUS: + xhci_port_status ( xhci, &trb->port ); + break; + + case XHCI_TRB_HOST_CONTROLLER: + xhci_host_controller ( xhci, &trb->host ); + break; + + default: + DBGC ( xhci, "XHCI %s unrecognised event %#x\n:", + xhci->name, event->cons ); + DBGC_HDA ( xhci, virt_to_phys ( trb ), + trb, sizeof ( *trb ) ); + break; + } + + /* Consume this TRB */ + event->cons++; + } + + /* Update dequeue pointer if applicable */ + if ( consumed ) { + xhci_writeq ( xhci, virt_to_phys ( trb ), + xhci->run + XHCI_RUN_ERDP ( 0 ) ); + profile_stop ( &xhci_event_profiler ); + } +} + +/** + * Abort command + * + * @v xhci xHCI device + */ +static void xhci_abort ( struct xhci_device *xhci ) { + physaddr_t crp; + + /* Abort the command */ + DBGC2 ( xhci, "XHCI %s aborting command\n", xhci->name ); + xhci_writeq ( xhci, XHCI_CRCR_CA, xhci->op + XHCI_OP_CRCR ); + + /* Allow time for command to abort */ + mdelay ( XHCI_COMMAND_ABORT_DELAY_MS ); + + /* Sanity check */ + assert ( ( readl ( xhci->op + XHCI_OP_CRCR ) & XHCI_CRCR_CRR ) == 0 ); + + /* Consume (and ignore) any final command status */ + xhci_event_poll ( xhci ); + + /* Reset the command ring control register */ + xhci_ring_reset ( &xhci->command ); + crp = virt_to_phys ( xhci->command.trb ); + xhci_writeq ( xhci, ( crp | XHCI_CRCR_RCS ), xhci->op + XHCI_OP_CRCR ); +} + +/** + * Issue command and wait for completion + * + * @v xhci xHCI device + * @v trb Transfer request block (with empty Cycle flag) + * @ret rc Return status code + * + * On a successful completion, the TRB will be overwritten with the + * completion. + */ +static int xhci_command ( struct xhci_device *xhci, union xhci_trb *trb ) { + struct xhci_trb_complete *complete = &trb->complete; + unsigned int i; + int rc; + + /* Record the pending command */ + xhci->pending = trb; + + /* Enqueue the command */ + if ( ( rc = xhci_enqueue ( &xhci->command, NULL, trb ) ) != 0 ) + goto err_enqueue; + + /* Ring the command doorbell */ + xhci_doorbell ( &xhci->command ); + + /* Wait for the command to complete */ + for ( i = 0 ; i < XHCI_COMMAND_MAX_WAIT_MS ; i++ ) { + + /* Poll event ring */ + xhci_event_poll ( xhci ); + + /* Check for completion */ + if ( ! xhci->pending ) { + if ( complete->code != XHCI_CMPLT_SUCCESS ) { + rc = -ECODE ( complete->code ); + DBGC ( xhci, "XHCI %s command failed (code " + "%d): %s\n", xhci->name, complete->code, + strerror ( rc ) ); + DBGC_HDA ( xhci, 0, trb, sizeof ( *trb ) ); + return rc; + } + return 0; + } + + /* Delay */ + mdelay ( 1 ); + } + + /* Timeout */ + DBGC ( xhci, "XHCI %s timed out waiting for completion\n", xhci->name ); + rc = -ETIMEDOUT; + + /* Abort command */ + xhci_abort ( xhci ); + + err_enqueue: + xhci->pending = NULL; + return rc; +} + +/** + * Issue NOP and wait for completion + * + * @v xhci xHCI device + * @ret rc Return status code + */ +static inline int xhci_nop ( struct xhci_device *xhci ) { + union xhci_trb trb; + struct xhci_trb_common *nop = &trb.common; + int rc; + + /* Construct command */ + memset ( nop, 0, sizeof ( *nop ) ); + nop->flags = XHCI_TRB_IOC; + nop->type = XHCI_TRB_NOP_CMD; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enable slot + * + * @v xhci xHCI device + * @v type Slot type + * @ret slot Device slot ID, or negative error + */ +static inline int xhci_enable_slot ( struct xhci_device *xhci, + unsigned int type ) { + union xhci_trb trb; + struct xhci_trb_enable_slot *enable = &trb.enable; + struct xhci_trb_complete *enabled = &trb.complete; + unsigned int slot; + int rc; + + /* Construct command */ + memset ( enable, 0, sizeof ( *enable ) ); + enable->slot = type; + enable->type = XHCI_TRB_ENABLE_SLOT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s could not enable new slot: %s\n", + xhci->name, strerror ( rc ) ); + return rc; + } + + /* Extract slot number */ + slot = enabled->slot; + + DBGC2 ( xhci, "XHCI %s slot %d enabled\n", xhci->name, slot ); + return slot; +} + +/** + * Disable slot + * + * @v xhci xHCI device + * @v slot Device slot + * @ret rc Return status code + */ +static inline int xhci_disable_slot ( struct xhci_device *xhci, + unsigned int slot ) { + union xhci_trb trb; + struct xhci_trb_disable_slot *disable = &trb.disable; + int rc; + + /* Construct command */ + memset ( disable, 0, sizeof ( *disable ) ); + disable->type = XHCI_TRB_DISABLE_SLOT; + disable->slot = slot; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s could not disable slot %d: %s\n", + xhci->name, slot, strerror ( rc ) ); + return rc; + } + + DBGC2 ( xhci, "XHCI %s slot %d disabled\n", xhci->name, slot ); + return 0; +} + +/** + * Issue context-based command and wait for completion + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v type TRB type + * @v populate Input context populater + * @ret rc Return status code + */ +static int xhci_context ( struct xhci_device *xhci, struct xhci_slot *slot, + struct xhci_endpoint *endpoint, unsigned int type, + void ( * populate ) ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) ) { + union xhci_trb trb; + struct xhci_trb_context *context = &trb.context; + size_t len; + void *input; + int rc; + + /* Allocate an input context */ + len = xhci_input_context_offset ( xhci, XHCI_CTX_END ); + input = malloc_dma ( len, xhci_align ( len ) ); + if ( ! input ) { + rc = -ENOMEM; + goto err_alloc; + } + memset ( input, 0, len ); + + /* Populate input context */ + populate ( xhci, slot, endpoint, input ); + + /* Construct command */ + memset ( context, 0, sizeof ( *context ) ); + context->type = type; + context->input = cpu_to_le64 ( virt_to_phys ( input ) ); + context->slot = slot->id; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) + goto err_command; + + err_command: + free_dma ( input, len ); + err_alloc: + return rc; +} + +/** + * Populate address device input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_address_device_input ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Sanity checks */ + assert ( endpoint->ctx == XHCI_CTX_EP0 ); + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << XHCI_CTX_EP0 ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( 1, 0, slot->psiv, + slot->route ) ); + slot_ctx->port = slot->port; + slot_ctx->tt_id = slot->tt_id; + slot_ctx->tt_port = slot->tt_port; + + /* Populate control endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_EP0 ) ); + ep_ctx->type = XHCI_EP_TYPE_CONTROL; + ep_ctx->burst = endpoint->ep->burst; + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); + ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | + XHCI_EP_DCS ); + ep_ctx->trb_len = cpu_to_le16 ( XHCI_EP0_TRB_LEN ); +} + +/** + * Address device + * + * @v xhci xHCI device + * @v slot Device slot + * @ret rc Return status code + */ +static inline int xhci_address_device ( struct xhci_device *xhci, + struct xhci_slot *slot ) { + struct usb_device *usb = slot->usb; + struct xhci_slot_context *slot_ctx; + int rc; + + /* Assign device address */ + if ( ( rc = xhci_context ( xhci, slot, slot->endpoint[XHCI_CTX_EP0], + XHCI_TRB_ADDRESS_DEVICE, + xhci_address_device_input ) ) != 0 ) + return rc; + + /* Get assigned address */ + slot_ctx = ( slot->context + + xhci_device_context_offset ( xhci, XHCI_CTX_SLOT ) ); + usb->address = slot_ctx->address; + DBGC2 ( xhci, "XHCI %s assigned address %d to %s\n", + xhci->name, usb->address, usb->name ); + + return 0; +} + +/** + * Populate configure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_configure_endpoint_input ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << endpoint->ctx ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + ( slot->ports ? 1 : 0 ), + slot->psiv, 0 ) ); + slot_ctx->ports = slot->ports; + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->interval = endpoint->interval; + ep_ctx->type = endpoint->type; + ep_ctx->burst = endpoint->ep->burst; + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); + ep_ctx->dequeue = cpu_to_le64 ( virt_to_phys ( endpoint->ring.trb ) | + XHCI_EP_DCS ); + ep_ctx->trb_len = cpu_to_le16 ( endpoint->ep->mtu ); /* best guess */ +} + +/** + * Configure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_configure_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_configure_endpoint_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d configured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Populate deconfigure endpoint input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void +xhci_deconfigure_endpoint_input ( struct xhci_device *xhci __unused, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( 1 << XHCI_CTX_SLOT ); + control_ctx->drop = cpu_to_le32 ( 1 << endpoint->ctx ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); +} + +/** + * Deconfigure endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_deconfigure_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Deconfigure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_CONFIGURE_ENDPOINT, + xhci_deconfigure_endpoint_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d deconfigured\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Populate evaluate context input context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @v input Input context + */ +static void xhci_evaluate_context_input ( struct xhci_device *xhci, + struct xhci_slot *slot __unused, + struct xhci_endpoint *endpoint, + void *input ) { + struct xhci_control_context *control_ctx; + struct xhci_slot_context *slot_ctx; + struct xhci_endpoint_context *ep_ctx; + + /* Populate control context */ + control_ctx = input; + control_ctx->add = cpu_to_le32 ( ( 1 << XHCI_CTX_SLOT ) | + ( 1 << endpoint->ctx ) ); + + /* Populate slot context */ + slot_ctx = ( input + xhci_input_context_offset ( xhci, XHCI_CTX_SLOT )); + slot_ctx->info = cpu_to_le32 ( XHCI_SLOT_INFO ( ( XHCI_CTX_END - 1 ), + 0, 0, 0 ) ); + + /* Populate endpoint context */ + ep_ctx = ( input + xhci_input_context_offset ( xhci, endpoint->ctx ) ); + ep_ctx->mtu = cpu_to_le16 ( endpoint->ep->mtu ); +} + +/** + * Evaluate context + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_evaluate_context ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + int rc; + + /* Configure endpoint */ + if ( ( rc = xhci_context ( xhci, slot, endpoint, + XHCI_TRB_EVALUATE_CONTEXT, + xhci_evaluate_context_input ) ) != 0 ) + return rc; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d (re-)evaluated\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Reset endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_reset_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_reset_endpoint *reset = &trb.reset; + int rc; + + /* Construct command */ + memset ( reset, 0, sizeof ( *reset ) ); + reset->slot = slot->id; + reset->endpoint = endpoint->ctx; + reset->type = XHCI_TRB_RESET_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not reset endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Stop endpoint + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int xhci_stop_endpoint ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_stop_endpoint *stop = &trb.stop; + int rc; + + /* Construct command */ + memset ( stop, 0, sizeof ( *stop ) ); + stop->slot = slot->id; + stop->endpoint = endpoint->ctx; + stop->type = XHCI_TRB_STOP_ENDPOINT; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not stop endpoint " + "in state %d: %s\n", xhci->name, slot->id, endpoint->ctx, + endpoint->context->state, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Set transfer ring dequeue pointer + * + * @v xhci xHCI device + * @v slot Device slot + * @v endpoint Endpoint + * @ret rc Return status code + */ +static inline int +xhci_set_tr_dequeue_pointer ( struct xhci_device *xhci, + struct xhci_slot *slot, + struct xhci_endpoint *endpoint ) { + union xhci_trb trb; + struct xhci_trb_set_tr_dequeue_pointer *dequeue = &trb.dequeue; + struct xhci_trb_ring *ring = &endpoint->ring; + unsigned int cons; + unsigned int mask; + unsigned int index; + unsigned int dcs; + int rc; + + /* Construct command */ + memset ( dequeue, 0, sizeof ( *dequeue ) ); + cons = ring->cons; + mask = ring->mask; + dcs = ( ( ~( cons >> ring->shift ) ) & XHCI_EP_DCS ); + index = ( cons & mask ); + dequeue->dequeue = + cpu_to_le64 ( virt_to_phys ( &ring->trb[index] ) | dcs ); + dequeue->slot = slot->id; + dequeue->endpoint = endpoint->ctx; + dequeue->type = XHCI_TRB_SET_TR_DEQUEUE_POINTER; + + /* Issue command and wait for completion */ + if ( ( rc = xhci_command ( xhci, &trb ) ) != 0 ) { + DBGC ( xhci, "XHCI %s slot %d ctx %d could not set TR dequeue " + "pointer in state %d: %s\n", xhci->name, slot->id, + endpoint->ctx, endpoint->context->state, strerror ( rc)); + return rc; + } + + return 0; +} + +/****************************************************************************** + * + * Endpoint operations + * + ****************************************************************************** + */ + +/** + * Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_open ( struct usb_endpoint *ep ) { + struct usb_device *usb = ep->usb; + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + struct xhci_endpoint *endpoint; + unsigned int ctx; + unsigned int type; + unsigned int interval; + int rc; + + /* Calculate context index */ + ctx = XHCI_CTX ( ep->address ); + assert ( slot->endpoint[ctx] == NULL ); + + /* Calculate endpoint type */ + type = XHCI_EP_TYPE ( ep->attributes & USB_ENDPOINT_ATTR_TYPE_MASK ); + if ( type == XHCI_EP_TYPE ( USB_ENDPOINT_ATTR_CONTROL ) ) + type = XHCI_EP_TYPE_CONTROL; + if ( ep->address & USB_DIR_IN ) + type |= XHCI_EP_TYPE_IN; + + /* Calculate interval */ + if ( type & XHCI_EP_TYPE_PERIODIC ) { + interval = ( fls ( ep->interval ) - 1 ); + } else { + interval = ep->interval; + } + + /* Allocate and initialise structure */ + endpoint = zalloc ( sizeof ( *endpoint ) ); + if ( ! endpoint ) { + rc = -ENOMEM; + goto err_alloc; + } + usb_endpoint_set_hostdata ( ep, endpoint ); + slot->endpoint[ctx] = endpoint; + endpoint->xhci = xhci; + endpoint->slot = slot; + endpoint->ep = ep; + endpoint->ctx = ctx; + endpoint->type = type; + endpoint->interval = interval; + endpoint->context = ( ( ( void * ) slot->context ) + + xhci_device_context_offset ( xhci, ctx ) ); + + /* Allocate transfer ring */ + if ( ( rc = xhci_ring_alloc ( xhci, &endpoint->ring, + XHCI_TRANSFER_TRBS_LOG2, + slot->id, ctx, 0 ) ) != 0 ) + goto err_ring_alloc; + + /* Configure endpoint, if applicable */ + if ( ( ctx != XHCI_CTX_EP0 ) && + ( ( rc = xhci_configure_endpoint ( xhci, slot, endpoint ) ) != 0 )) + goto err_configure_endpoint; + + DBGC2 ( xhci, "XHCI %s slot %d ctx %d ring [%08lx,%08lx)\n", + xhci->name, slot->id, ctx, virt_to_phys ( endpoint->ring.trb ), + ( virt_to_phys ( endpoint->ring.trb ) + endpoint->ring.len ) ); + return 0; + + xhci_deconfigure_endpoint ( xhci, slot, endpoint ); + err_configure_endpoint: + xhci_ring_free ( &endpoint->ring ); + err_ring_alloc: + slot->endpoint[ctx] = NULL; + free ( endpoint ); + err_alloc: + return rc; +} + +/** + * Close endpoint + * + * @v ep USB endpoint + */ +static void xhci_endpoint_close ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + struct io_buffer *iobuf; + unsigned int ctx = endpoint->ctx; + + /* Deconfigure endpoint, if applicable */ + if ( ctx != XHCI_CTX_EP0 ) + xhci_deconfigure_endpoint ( xhci, slot, endpoint ); + + /* Cancel any incomplete transfers */ + while ( xhci_ring_fill ( &endpoint->ring ) ) { + iobuf = xhci_dequeue_multi ( &endpoint->ring ); + usb_complete_err ( ep, iobuf, -ECANCELED ); + } + + /* Free endpoint */ + xhci_ring_free ( &endpoint->ring ); + slot->endpoint[ctx] = NULL; + free ( endpoint ); +} + +/** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_reset ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + int rc; + + /* Reset endpoint context */ + if ( ( rc = xhci_reset_endpoint ( xhci, slot, endpoint ) ) != 0 ) + return rc; + + /* Set transfer ring dequeue pointer */ + if ( ( rc = xhci_set_tr_dequeue_pointer ( xhci, slot, endpoint ) ) != 0) + return rc; + + /* Ring doorbell to resume processing */ + xhci_doorbell ( &endpoint->ring ); + + DBGC ( xhci, "XHCI %s slot %d ctx %d reset\n", + xhci->name, slot->id, endpoint->ctx ); + return 0; +} + +/** + * Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_endpoint_mtu ( struct usb_endpoint *ep ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct xhci_slot *slot = endpoint->slot; + struct xhci_device *xhci = slot->xhci; + int rc; + + /* Evalulate context */ + if ( ( rc = xhci_evaluate_context ( xhci, slot, endpoint ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int xhci_endpoint_message ( struct usb_endpoint *ep, + struct io_buffer *iobuf ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + struct usb_setup_packet *packet; + unsigned int input; + size_t len; + union xhci_trb trbs[ 1 /* setup */ + 1 /* possible data */ + + 1 /* status */ ]; + union xhci_trb *trb = trbs; + struct xhci_trb_setup *setup; + struct xhci_trb_data *data; + struct xhci_trb_status *status; + int rc; + + /* Profile message transfers */ + profile_start ( &xhci_message_profiler ); + + /* Construct setup stage TRB */ + memset ( trbs, 0, sizeof ( trbs ) ); + assert ( iob_len ( iobuf ) >= sizeof ( *packet ) ); + packet = iobuf->data; + iob_pull ( iobuf, sizeof ( *packet ) ); + setup = &(trb++)->setup; + memcpy ( &setup->packet, packet, sizeof ( setup->packet ) ); + setup->len = cpu_to_le32 ( sizeof ( *packet ) ); + setup->flags = XHCI_TRB_IDT; + setup->type = XHCI_TRB_SETUP; + len = iob_len ( iobuf ); + input = ( packet->request & cpu_to_le16 ( USB_DIR_IN ) ); + if ( len ) + setup->direction = ( input ? XHCI_SETUP_IN : XHCI_SETUP_OUT ); + + /* Construct data stage TRB, if applicable */ + if ( len ) { + data = &(trb++)->data; + data->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); + data->len = cpu_to_le32 ( len ); + data->type = XHCI_TRB_DATA; + data->direction = ( input ? XHCI_DATA_IN : XHCI_DATA_OUT ); + } + + /* Construct status stage TRB */ + status = &(trb++)->status; + status->flags = XHCI_TRB_IOC; + status->type = XHCI_TRB_STATUS; + status->direction = + ( ( len && input ) ? XHCI_STATUS_OUT : XHCI_STATUS_IN ); + + /* Enqueue TRBs */ + if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, + ( trb - trbs ) ) ) != 0 ) + return rc; + + /* Ring the doorbell */ + xhci_doorbell ( &endpoint->ring ); + + profile_stop ( &xhci_message_profiler ); + return 0; +} + +/** + * Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ +static int xhci_endpoint_stream ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int terminate ) { + struct xhci_endpoint *endpoint = usb_endpoint_get_hostdata ( ep ); + union xhci_trb trbs[ 1 /* Normal */ + 1 /* Possible zero-length */ ]; + union xhci_trb *trb = trbs; + struct xhci_trb_normal *normal; + size_t len = iob_len ( iobuf ); + int rc; + + /* Profile stream transfers */ + profile_start ( &xhci_stream_profiler ); + + /* Construct normal TRBs */ + memset ( &trbs, 0, sizeof ( trbs ) ); + normal = &(trb++)->normal; + normal->data = cpu_to_le64 ( virt_to_phys ( iobuf->data ) ); + normal->len = cpu_to_le32 ( len ); + normal->type = XHCI_TRB_NORMAL; + if ( terminate && ( ( len & ( ep->mtu - 1 ) ) == 0 ) ) { + normal->flags = XHCI_TRB_CH; + normal = &(trb++)->normal; + normal->type = XHCI_TRB_NORMAL; + } + normal->flags = XHCI_TRB_IOC; + + /* Enqueue TRBs */ + if ( ( rc = xhci_enqueue_multi ( &endpoint->ring, iobuf, trbs, + ( trb - trbs ) ) ) != 0 ) + return rc; + + /* Ring the doorbell */ + xhci_doorbell ( &endpoint->ring ); + + profile_stop ( &xhci_stream_profiler ); + return 0; +} + +/****************************************************************************** + * + * Device operations + * + ****************************************************************************** + */ + +/** + * Open device + * + * @v usb USB device + * @ret rc Return status code + */ +static int xhci_device_open ( struct usb_device *usb ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( usb->port->hub->bus ); + struct usb_port *tt = usb_transaction_translator ( usb ); + struct xhci_slot *slot; + struct xhci_slot *tt_slot; + size_t len; + int type; + int id; + int rc; + + /* Determine applicable slot type */ + type = xhci_port_slot_type ( xhci, usb->port->address ); + if ( type < 0 ) { + rc = type; + DBGC ( xhci, "XHCI %s-%d has no slot type\n", + xhci->name, usb->port->address ); + goto err_type; + } + + /* Allocate a device slot number */ + id = xhci_enable_slot ( xhci, type ); + if ( id < 0 ) { + rc = id; + goto err_enable_slot; + } + assert ( ( id > 0 ) && ( ( unsigned int ) id <= xhci->slots ) ); + assert ( xhci->slot[id] == NULL ); + + /* Allocate and initialise structure */ + slot = zalloc ( sizeof ( *slot ) ); + if ( ! slot ) { + rc = -ENOMEM; + goto err_alloc; + } + usb_set_hostdata ( usb, slot ); + xhci->slot[id] = slot; + slot->xhci = xhci; + slot->usb = usb; + slot->id = id; + if ( tt ) { + tt_slot = usb_get_hostdata ( tt->hub->usb ); + slot->tt_id = tt_slot->id; + slot->tt_port = tt->address; + } + + /* Allocate a device context */ + len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + slot->context = malloc_dma ( len, xhci_align ( len ) ); + if ( ! slot->context ) { + rc = -ENOMEM; + goto err_alloc_context; + } + memset ( slot->context, 0, len ); + + /* Set device context base address */ + assert ( xhci->dcbaa[id] == 0 ); + xhci->dcbaa[id] = cpu_to_le64 ( virt_to_phys ( slot->context ) ); + + DBGC2 ( xhci, "XHCI %s slot %d device context [%08lx,%08lx) for %s\n", + xhci->name, slot->id, virt_to_phys ( slot->context ), + ( virt_to_phys ( slot->context ) + len ), usb->name ); + return 0; + + xhci->dcbaa[id] = 0; + free_dma ( slot->context, len ); + err_alloc_context: + xhci->slot[id] = NULL; + free ( slot ); + err_alloc: + xhci_disable_slot ( xhci, id ); + err_enable_slot: + err_type: + return rc; +} + +/** + * Close device + * + * @v usb USB device + */ +static void xhci_device_close ( struct usb_device *usb ) { + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + size_t len = xhci_device_context_offset ( xhci, XHCI_CTX_END ); + unsigned int id = slot->id; + int rc; + + /* Disable slot */ + if ( ( rc = xhci_disable_slot ( xhci, id ) ) != 0 ) { + /* Slot is still enabled. Leak the slot context, + * since the controller may still write to this + * memory, and leave the DCBAA entry intact. + * + * If the controller later reports that this same slot + * has been re-enabled, then some assertions will be + * triggered. + */ + DBGC ( xhci, "XHCI %s slot %d leaking context memory\n", + xhci->name, slot->id ); + slot->context = NULL; + } + + /* Free slot */ + if ( slot->context ) { + free_dma ( slot->context, len ); + xhci->dcbaa[id] = 0; + } + xhci->slot[id] = NULL; + free ( slot ); +} + +/** + * Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ +static int xhci_device_address ( struct usb_device *usb ) { + struct xhci_slot *slot = usb_get_hostdata ( usb ); + struct xhci_device *xhci = slot->xhci; + struct usb_port *port = usb->port; + struct usb_port *root_port; + int psiv; + int rc; + + /* Calculate route string */ + slot->route = usb_route_string ( usb ); + + /* Calculate root hub port number */ + root_port = usb_root_hub_port ( usb ); + slot->port = root_port->address; + + /* Calculate protocol speed ID */ + psiv = xhci_port_psiv ( xhci, slot->port, port->speed ); + if ( psiv < 0 ) { + rc = psiv; + return rc; + } + slot->psiv = psiv; + + /* Address device */ + if ( ( rc = xhci_address_device ( xhci, slot ) ) != 0 ) + return rc; + + return 0; +} + +/****************************************************************************** + * + * Bus operations + * + ****************************************************************************** + */ + +/** + * Open USB bus + * + * @v bus USB bus + * @ret rc Return status code + */ +static int xhci_bus_open ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + int rc; + + /* Allocate device slot array */ + xhci->slot = zalloc ( ( xhci->slots + 1 ) * sizeof ( xhci->slot[0] ) ); + if ( ! xhci->slot ) { + rc = -ENOMEM; + goto err_slot_alloc; + } + + /* Allocate device context base address array */ + if ( ( rc = xhci_dcbaa_alloc ( xhci ) ) != 0 ) + goto err_dcbaa_alloc; + + /* Allocate scratchpad buffers */ + if ( ( rc = xhci_scratchpad_alloc ( xhci ) ) != 0 ) + goto err_scratchpad_alloc; + + /* Allocate command ring */ + if ( ( rc = xhci_command_alloc ( xhci ) ) != 0 ) + goto err_command_alloc; + + /* Allocate event ring */ + if ( ( rc = xhci_event_alloc ( xhci ) ) != 0 ) + goto err_event_alloc; + + /* Start controller */ + xhci_run ( xhci ); + + return 0; + + xhci_stop ( xhci ); + xhci_event_free ( xhci ); + err_event_alloc: + xhci_command_free ( xhci ); + err_command_alloc: + xhci_scratchpad_free ( xhci ); + err_scratchpad_alloc: + xhci_dcbaa_free ( xhci ); + err_dcbaa_alloc: + free ( xhci->slot ); + err_slot_alloc: + return rc; +} + +/** + * Close USB bus + * + * @v bus USB bus + */ +static void xhci_bus_close ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + unsigned int i; + + /* Sanity checks */ + assert ( xhci->slot != NULL ); + for ( i = 0 ; i <= xhci->slots ; i++ ) + assert ( xhci->slot[i] == NULL ); + + xhci_stop ( xhci ); + xhci_event_free ( xhci ); + xhci_command_free ( xhci ); + xhci_scratchpad_free ( xhci ); + xhci_dcbaa_free ( xhci ); + free ( xhci->slot ); +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static void xhci_bus_poll ( struct usb_bus *bus ) { + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + + /* Poll event ring */ + xhci_event_poll ( xhci ); +} + +/****************************************************************************** + * + * Hub operations + * + ****************************************************************************** + */ + +/** + * Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int xhci_hub_open ( struct usb_hub *hub ) { + struct xhci_slot *slot; + + /* Do nothing if this is the root hub */ + if ( ! hub->usb ) + return 0; + + /* Get device slot */ + slot = usb_get_hostdata ( hub->usb ); + + /* Update device slot hub parameters. We don't inform the + * hardware of this information until the hub's interrupt + * endpoint is opened, since the only mechanism for so doing + * provided by the xHCI specification is a Configure Endpoint + * command, and we can't issue that command until we have a + * non-EP0 endpoint to configure. + */ + slot->ports = hub->ports; + + return 0; +} + +/** + * Close hub + * + * @v hub USB hub + */ +static void xhci_hub_close ( struct usb_hub *hub __unused ) { + + /* Nothing to do */ +} + +/****************************************************************************** + * + * Root hub operations + * + ****************************************************************************** + */ + +/** + * Open root hub + * + * @v hub USB hub + * @ret rc Return status code + */ +static int xhci_root_open ( struct usb_hub *hub ) { + struct usb_bus *bus = hub->bus; + struct xhci_device *xhci = usb_bus_get_hostdata ( bus ); + struct usb_port *port; + uint32_t portsc; + unsigned int i; + + /* Enable power to all ports */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PP; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); + } + + /* xHCI spec requires us to potentially wait 20ms after + * enabling power to a port. + */ + mdelay ( XHCI_PORT_POWER_DELAY_MS ); + + /* USB3 ports may power up as Disabled */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( i ) ); + port = usb_port ( hub, i ); + if ( ( port->protocol >= USB_PROTO_3_0 ) && + ( ( portsc & XHCI_PORTSC_PLS_MASK ) == + XHCI_PORTSC_PLS_DISABLED ) ) { + /* Force link state to RxDetect */ + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= ( XHCI_PORTSC_PLS_RXDETECT | XHCI_PORTSC_LWS); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( i ) ); + } + } + + /* Some xHCI cards seem to require an additional delay after + * setting the link state to RxDetect. + */ + mdelay ( XHCI_LINK_STATE_DELAY_MS ); + + /* Record hub driver private data */ + usb_hub_set_drvdata ( hub, xhci ); + + return 0; +} + +/** + * Close root hub + * + * @v hub USB hub + */ +static void xhci_root_close ( struct usb_hub *hub ) { + + /* Clear hub driver private data */ + usb_hub_set_drvdata ( hub, NULL ); +} + +/** + * Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_enable ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int i; + + /* Reset port */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PR; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + /* Wait for port to become enabled */ + for ( i = 0 ; i < XHCI_PORT_RESET_MAX_WAIT_MS ; i++ ) { + + /* Check port status */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + if ( portsc & XHCI_PORTSC_PED ) + return 0; + + /* Delay */ + mdelay ( 1 ); + } + + DBGC ( xhci, "XHCI %s-%d timed out waiting for port to enable\n", + xhci->name, port->address ); + return -ETIMEDOUT; +} + +/** + * Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_disable ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + + /* Disable port */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + portsc &= XHCI_PORTSC_PRESERVE; + portsc |= XHCI_PORTSC_PED; + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + return 0; +} + +/** + * Update root hub port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ +static int xhci_root_speed ( struct usb_hub *hub, struct usb_port *port ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + uint32_t portsc; + unsigned int psiv; + int ccs; + int ped; + int csc; + int speed; + int rc; + + /* Read port status */ + portsc = readl ( xhci->op + XHCI_OP_PORTSC ( port->address ) ); + DBGC2 ( xhci, "XHCI %s-%d status is %08x\n", + xhci->name, port->address, portsc ); + ccs = ( portsc & XHCI_PORTSC_CCS ); + ped = ( portsc & XHCI_PORTSC_PED ); + csc = ( portsc & XHCI_PORTSC_CSC ); + psiv = XHCI_PORTSC_PSIV ( portsc ); + + /* Record disconnections and clear changes */ + port->disconnected |= csc; + portsc &= ( XHCI_PORTSC_PRESERVE | XHCI_PORTSC_CHANGE ); + writel ( portsc, xhci->op + XHCI_OP_PORTSC ( port->address ) ); + + /* Port speed is not valid unless port is connected */ + if ( ! ccs ) { + port->speed = USB_SPEED_NONE; + return 0; + } + + /* For USB2 ports, the PSIV field is not valid until the port + * completes reset and becomes enabled. + */ + if ( ( port->protocol < USB_PROTO_3_0 ) && ! ped ) { + port->speed = USB_SPEED_FULL; + return 0; + } + + /* Get port speed and map to generic USB speed */ + speed = xhci_port_speed ( xhci, port->address, psiv ); + if ( speed < 0 ) { + rc = speed; + return rc; + } + + port->speed = speed; + return 0; +} + +/** + * Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ +static int xhci_root_clear_tt ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ) { + struct xhci_device *xhci = usb_hub_get_drvdata ( hub ); + + /* Should never be called; this is a root hub */ + DBGC ( xhci, "XHCI %s-%d nonsensical CLEAR_TT for %s %s\n", xhci->name, + port->address, ep->usb->name, usb_endpoint_name ( ep ) ); + + return -ENOTSUP; +} + +/****************************************************************************** + * + * PCI interface + * + ****************************************************************************** + */ + +/** USB host controller operations */ +static struct usb_host_operations xhci_operations = { + .endpoint = { + .open = xhci_endpoint_open, + .close = xhci_endpoint_close, + .reset = xhci_endpoint_reset, + .mtu = xhci_endpoint_mtu, + .message = xhci_endpoint_message, + .stream = xhci_endpoint_stream, + }, + .device = { + .open = xhci_device_open, + .close = xhci_device_close, + .address = xhci_device_address, + }, + .bus = { + .open = xhci_bus_open, + .close = xhci_bus_close, + .poll = xhci_bus_poll, + }, + .hub = { + .open = xhci_hub_open, + .close = xhci_hub_close, + }, + .root = { + .open = xhci_root_open, + .close = xhci_root_close, + .enable = xhci_root_enable, + .disable = xhci_root_disable, + .speed = xhci_root_speed, + .clear_tt = xhci_root_clear_tt, + }, +}; + +/** + * Fix Intel PCH-specific quirks + * + * @v xhci xHCI device + * @v pci PCI device + */ +static void xhci_pch_fix ( struct xhci_device *xhci, struct pci_device *pci ) { + struct xhci_pch *pch = &xhci->pch; + uint32_t xusb2pr; + uint32_t xusb2prm; + uint32_t usb3pssen; + uint32_t usb3prm; + + /* Enable SuperSpeed capability. Do this before rerouting + * USB2 ports, so that USB3 devices connect at SuperSpeed. + */ + pci_read_config_dword ( pci, XHCI_PCH_USB3PSSEN, &usb3pssen ); + pci_read_config_dword ( pci, XHCI_PCH_USB3PRM, &usb3prm ); + if ( usb3prm & ~usb3pssen ) { + DBGC ( xhci, "XHCI %s enabling SuperSpeed on ports %08x\n", + xhci->name, ( usb3prm & ~usb3pssen ) ); + } + pch->usb3pssen = usb3pssen; + usb3pssen |= usb3prm; + pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, usb3pssen ); + + /* Route USB2 ports from EHCI to xHCI */ + pci_read_config_dword ( pci, XHCI_PCH_XUSB2PR, &xusb2pr ); + pci_read_config_dword ( pci, XHCI_PCH_XUSB2PRM, &xusb2prm ); + if ( xusb2prm & ~xusb2pr ) { + DBGC ( xhci, "XHCI %s routing ports %08x from EHCI to xHCI\n", + xhci->name, ( xusb2prm & ~xusb2pr ) ); + } + pch->xusb2pr = xusb2pr; + xusb2pr |= xusb2prm; + pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, xusb2pr ); +} + +/** + * Undo Intel PCH-specific quirk fixes + * + * @v xhci xHCI device + * @v pci PCI device + */ +static void xhci_pch_undo ( struct xhci_device *xhci, struct pci_device *pci ) { + struct xhci_pch *pch = &xhci->pch; + + /* Restore USB2 port routing to original state */ + pci_write_config_dword ( pci, XHCI_PCH_XUSB2PR, pch->xusb2pr ); + + /* Restore SuperSpeed capability to original state */ + pci_write_config_dword ( pci, XHCI_PCH_USB3PSSEN, pch->usb3pssen ); +} + +/** + * Probe PCI device + * + * @v pci PCI device + * @ret rc Return status code + */ +static int xhci_probe ( struct pci_device *pci ) { + struct xhci_device *xhci; + struct usb_port *port; + unsigned long bar_start; + size_t bar_size; + unsigned int i; + int rc; + + /* Allocate and initialise structure */ + xhci = zalloc ( sizeof ( *xhci ) ); + if ( ! xhci ) { + rc = -ENOMEM; + goto err_alloc; + } + xhci->name = pci->dev.name; + xhci->quirks = pci->id->driver_data; + + /* Fix up PCI device */ + adjust_pci_device ( pci ); + + /* Map registers */ + bar_start = pci_bar_start ( pci, XHCI_BAR ); + bar_size = pci_bar_size ( pci, XHCI_BAR ); + xhci->regs = ioremap ( bar_start, bar_size ); + if ( ! xhci->regs ) { + rc = -ENODEV; + goto err_ioremap; + } + + /* Initialise xHCI device */ + xhci_init ( xhci, xhci->regs ); + + /* Initialise USB legacy support and claim ownership */ + xhci_legacy_init ( xhci ); + xhci_legacy_claim ( xhci ); + + /* Fix Intel PCH-specific quirks, if applicable */ + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_fix ( xhci, pci ); + + /* Reset device */ + if ( ( rc = xhci_reset ( xhci ) ) != 0 ) + goto err_reset; + + /* Allocate USB bus */ + xhci->bus = alloc_usb_bus ( &pci->dev, xhci->ports, XHCI_MTU, + &xhci_operations ); + if ( ! xhci->bus ) { + rc = -ENOMEM; + goto err_alloc_bus; + } + usb_bus_set_hostdata ( xhci->bus, xhci ); + usb_hub_set_drvdata ( xhci->bus->hub, xhci ); + + /* Set port protocols */ + for ( i = 1 ; i <= xhci->ports ; i++ ) { + port = usb_port ( xhci->bus->hub, i ); + port->protocol = xhci_port_protocol ( xhci, i ); + } + + /* Register USB bus */ + if ( ( rc = register_usb_bus ( xhci->bus ) ) != 0 ) + goto err_register; + + pci_set_drvdata ( pci, xhci ); + return 0; + + unregister_usb_bus ( xhci->bus ); + err_register: + free_usb_bus ( xhci->bus ); + err_alloc_bus: + xhci_reset ( xhci ); + err_reset: + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_undo ( xhci, pci ); + xhci_legacy_release ( xhci ); + iounmap ( xhci->regs ); + err_ioremap: + free ( xhci ); + err_alloc: + return rc; +} + +/** + * Remove PCI device + * + * @v pci PCI device + */ +static void xhci_remove ( struct pci_device *pci ) { + struct xhci_device *xhci = pci_get_drvdata ( pci ); + struct usb_bus *bus = xhci->bus; + + unregister_usb_bus ( bus ); + free_usb_bus ( bus ); + xhci_reset ( xhci ); + if ( xhci->quirks & XHCI_PCH ) + xhci_pch_undo ( xhci, pci ); + xhci_legacy_release ( xhci ); + iounmap ( xhci->regs ); + free ( xhci ); +} + +/** XHCI PCI device IDs */ +static struct pci_device_id xhci_ids[] = { + PCI_ROM ( 0x8086, 0x9d2f, "xhci-skylake", "xHCI (Skylake)", ( XHCI_PCH | XHCI_BAD_PSIV ) ), + PCI_ROM ( 0x8086, 0xffff, "xhci-pch", "xHCI (Intel PCH)", XHCI_PCH ), + PCI_ROM ( 0xffff, 0xffff, "xhci", "xHCI", 0 ), +}; + +/** XHCI PCI driver */ +struct pci_driver xhci_driver __pci_driver = { + .ids = xhci_ids, + .id_count = ( sizeof ( xhci_ids ) / sizeof ( xhci_ids[0] ) ), + .class = PCI_CLASS_ID ( PCI_CLASS_SERIAL, PCI_CLASS_SERIAL_USB, + PCI_CLASS_SERIAL_USB_XHCI ), + .probe = xhci_probe, + .remove = xhci_remove, +}; + +/** + * Prepare for exit + * + * @v booting System is shutting down for OS boot + */ +static void xhci_shutdown ( int booting ) { + /* If we are shutting down to boot an OS, then prevent the + * release of ownership back to BIOS. + */ + xhci_legacy_prevent_release = booting; +} + +/** Startup/shutdown function */ +struct startup_fn xhci_startup __startup_fn ( STARTUP_LATE ) = { + .shutdown = xhci_shutdown, +}; diff --git a/roms/ipxe/src/drivers/usb/xhci.h b/roms/ipxe/src/drivers/usb/xhci.h new file mode 100644 index 000000000..83bf71e7e --- /dev/null +++ b/roms/ipxe/src/drivers/usb/xhci.h @@ -0,0 +1,1150 @@ +#ifndef _IPXE_XHCI_H +#define _IPXE_XHCI_H + +/** @file + * + * USB eXtensible Host Controller Interface (xHCI) driver + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <assert.h> +#include <ipxe/pci.h> +#include <ipxe/uaccess.h> +#include <ipxe/usb.h> + +/** Minimum alignment required for data structures + * + * With the exception of the scratchpad buffer pages (which are + * page-aligned), data structures used by xHCI generally require from + * 16 to 64 byte alignment and must not cross an (xHCI) page boundary. + * We simplify this requirement by aligning each structure on its own + * size, with a minimum of a 64 byte alignment. + */ +#define XHCI_MIN_ALIGN 64 + +/** Maximum transfer size */ +#define XHCI_MTU 65536 + +/** xHCI PCI BAR */ +#define XHCI_BAR PCI_BASE_ADDRESS_0 + +/** Capability register length */ +#define XHCI_CAP_CAPLENGTH 0x00 + +/** Host controller interface version number */ +#define XHCI_CAP_HCIVERSION 0x02 + +/** Structural parameters 1 */ +#define XHCI_CAP_HCSPARAMS1 0x04 + +/** Number of device slots */ +#define XHCI_HCSPARAMS1_SLOTS(params) ( ( (params) >> 0 ) & 0xff ) + +/** Number of interrupters */ +#define XHCI_HCSPARAMS1_INTRS(params) ( ( (params) >> 8 ) & 0x3ff ) + +/** Number of ports */ +#define XHCI_HCSPARAMS1_PORTS(params) ( ( (params) >> 24 ) & 0xff ) + +/** Structural parameters 2 */ +#define XHCI_CAP_HCSPARAMS2 0x08 + +/** Number of page-sized scratchpad buffers */ +#define XHCI_HCSPARAMS2_SCRATCHPADS(params) \ + ( ( ( (params) >> 16 ) & 0x3e0 ) | ( ( (params) >> 27 ) & 0x1f ) ) + +/** Capability parameters */ +#define XHCI_CAP_HCCPARAMS1 0x10 + +/** 64-bit addressing capability */ +#define XHCI_HCCPARAMS1_ADDR64(params) ( ( (params) >> 0 ) & 0x1 ) + +/** Context size shift */ +#define XHCI_HCCPARAMS1_CSZ_SHIFT(params) ( 5 + ( ( (params) >> 2 ) & 0x1 ) ) + +/** xHCI extended capabilities pointer */ +#define XHCI_HCCPARAMS1_XECP(params) ( ( ( (params) >> 16 ) & 0xffff ) << 2 ) + +/** Doorbell offset */ +#define XHCI_CAP_DBOFF 0x14 + +/** Runtime register space offset */ +#define XHCI_CAP_RTSOFF 0x18 + +/** xHCI extended capability ID */ +#define XHCI_XECP_ID(xecp) ( ( (xecp) >> 0 ) & 0xff ) + +/** Next xHCI extended capability pointer */ +#define XHCI_XECP_NEXT(xecp) ( ( ( (xecp) >> 8 ) & 0xff ) << 2 ) + +/** USB legacy support extended capability */ +#define XHCI_XECP_ID_LEGACY 1 + +/** USB legacy support BIOS owned semaphore */ +#define XHCI_USBLEGSUP_BIOS 0x02 + +/** USB legacy support BIOS ownership flag */ +#define XHCI_USBLEGSUP_BIOS_OWNED 0x01 + +/** USB legacy support OS owned semaphore */ +#define XHCI_USBLEGSUP_OS 0x03 + +/** USB legacy support OS ownership flag */ +#define XHCI_USBLEGSUP_OS_OWNED 0x01 + +/** USB legacy support control/status */ +#define XHCI_USBLEGSUP_CTLSTS 0x04 + +/** Supported protocol extended capability */ +#define XHCI_XECP_ID_SUPPORTED 2 + +/** Supported protocol revision */ +#define XHCI_SUPPORTED_REVISION 0x00 + +/** Supported protocol minor revision */ +#define XHCI_SUPPORTED_REVISION_VER(revision) ( ( (revision) >> 16 ) & 0xffff ) + +/** Supported protocol name */ +#define XHCI_SUPPORTED_NAME 0x04 + +/** Supported protocol ports */ +#define XHCI_SUPPORTED_PORTS 0x08 + +/** Supported protocol port offset */ +#define XHCI_SUPPORTED_PORTS_OFFSET(ports) ( ( (ports) >> 0 ) & 0xff ) + +/** Supported protocol port count */ +#define XHCI_SUPPORTED_PORTS_COUNT(ports) ( ( (ports) >> 8 ) & 0xff ) + +/** Supported protocol PSI count */ +#define XHCI_SUPPORTED_PORTS_PSIC(ports) ( ( (ports) >> 28 ) & 0x0f ) + +/** Supported protocol slot */ +#define XHCI_SUPPORTED_SLOT 0x0c + +/** Supported protocol slot type */ +#define XHCI_SUPPORTED_SLOT_TYPE(slot) ( ( (slot) >> 0 ) & 0x1f ) + +/** Supported protocol PSI */ +#define XHCI_SUPPORTED_PSI(index) ( 0x10 + ( (index) * 4 ) ) + +/** Supported protocol PSI value */ +#define XHCI_SUPPORTED_PSI_VALUE(psi) ( ( (psi) >> 0 ) & 0x0f ) + +/** Supported protocol PSI mantissa */ +#define XHCI_SUPPORTED_PSI_MANTISSA(psi) ( ( (psi) >> 16 ) & 0xffff ) + +/** Supported protocol PSI exponent */ +#define XHCI_SUPPORTED_PSI_EXPONENT(psi) ( ( (psi) >> 4 ) & 0x03 ) + +/** Default PSI values */ +enum xhci_default_psi_value { + /** Full speed (12Mbps) */ + XHCI_SPEED_FULL = 1, + /** Low speed (1.5Mbps) */ + XHCI_SPEED_LOW = 2, + /** High speed (480Mbps) */ + XHCI_SPEED_HIGH = 3, + /** Super speed */ + XHCI_SPEED_SUPER = 4, +}; + +/** USB command register */ +#define XHCI_OP_USBCMD 0x00 + +/** Run/stop */ +#define XHCI_USBCMD_RUN 0x00000001UL + +/** Host controller reset */ +#define XHCI_USBCMD_HCRST 0x00000002UL + +/** USB status register */ +#define XHCI_OP_USBSTS 0x04 + +/** Host controller halted */ +#define XHCI_USBSTS_HCH 0x00000001UL + +/** Page size register */ +#define XHCI_OP_PAGESIZE 0x08 + +/** Page size */ +#define XHCI_PAGESIZE(pagesize) ( (pagesize) << 12 ) + +/** Device notifcation control register */ +#define XHCI_OP_DNCTRL 0x14 + +/** Command ring control register */ +#define XHCI_OP_CRCR 0x18 + +/** Command ring cycle state */ +#define XHCI_CRCR_RCS 0x00000001UL + +/** Command abort */ +#define XHCI_CRCR_CA 0x00000004UL + +/** Command ring running */ +#define XHCI_CRCR_CRR 0x00000008UL + +/** Device context base address array pointer */ +#define XHCI_OP_DCBAAP 0x30 + +/** Configure register */ +#define XHCI_OP_CONFIG 0x38 + +/** Maximum device slots enabled */ +#define XHCI_CONFIG_MAX_SLOTS_EN(slots) ( (slots) << 0 ) + +/** Maximum device slots enabled mask */ +#define XHCI_CONFIG_MAX_SLOTS_EN_MASK \ + XHCI_CONFIG_MAX_SLOTS_EN ( 0xff ) + +/** Port status and control register */ +#define XHCI_OP_PORTSC(port) ( 0x400 - 0x10 + ( (port) << 4 ) ) + +/** Current connect status */ +#define XHCI_PORTSC_CCS 0x00000001UL + +/** Port enabled */ +#define XHCI_PORTSC_PED 0x00000002UL + +/** Port reset */ +#define XHCI_PORTSC_PR 0x00000010UL + +/** Port link state */ +#define XHCI_PORTSC_PLS(pls) ( (pls) << 5 ) + +/** Disabled port link state */ +#define XHCI_PORTSC_PLS_DISABLED XHCI_PORTSC_PLS ( 4 ) + +/** RxDetect port link state */ +#define XHCI_PORTSC_PLS_RXDETECT XHCI_PORTSC_PLS ( 5 ) + +/** Port link state mask */ +#define XHCI_PORTSC_PLS_MASK XHCI_PORTSC_PLS ( 0xf ) + +/** Port power */ +#define XHCI_PORTSC_PP 0x00000200UL + +/** Time to delay after enabling power to a port */ +#define XHCI_PORT_POWER_DELAY_MS 20 + +/** Port speed ID value */ +#define XHCI_PORTSC_PSIV(portsc) ( ( (portsc) >> 10 ) & 0xf ) + +/** Port indicator control */ +#define XHCI_PORTSC_PIC(indicators) ( (indicators) << 14 ) + +/** Port indicator control mask */ +#define XHCI_PORTSC_PIC_MASK XHCI_PORTSC_PIC ( 3 ) + +/** Port link state write strobe */ +#define XHCI_PORTSC_LWS 0x00010000UL + +/** Time to delay after writing the port link state */ +#define XHCI_LINK_STATE_DELAY_MS 20 + +/** Connect status change */ +#define XHCI_PORTSC_CSC 0x00020000UL + +/** Port enabled/disabled change */ +#define XHCI_PORTSC_PEC 0x00040000UL + +/** Warm port reset change */ +#define XHCI_PORTSC_WRC 0x00080000UL + +/** Over-current change */ +#define XHCI_PORTSC_OCC 0x00100000UL + +/** Port reset change */ +#define XHCI_PORTSC_PRC 0x00200000UL + +/** Port link state change */ +#define XHCI_PORTSC_PLC 0x00400000UL + +/** Port config error change */ +#define XHCI_PORTSC_CEC 0x00800000UL + +/** Port status change mask */ +#define XHCI_PORTSC_CHANGE \ + ( XHCI_PORTSC_CSC | XHCI_PORTSC_PEC | XHCI_PORTSC_WRC | \ + XHCI_PORTSC_OCC | XHCI_PORTSC_PRC | XHCI_PORTSC_PLC | \ + XHCI_PORTSC_CEC ) + +/** Port status and control bits which should be preserved + * + * The port status and control register is a horrendous mix of + * differing semantics. Some bits are written to only when a separate + * write strobe bit is set. Some bits should be preserved when + * modifying other bits. Some bits will be cleared if written back as + * a one. Most excitingly, the "port enabled" bit has the semantics + * that 1=enabled, 0=disabled, yet writing a 1 will disable the port. + */ +#define XHCI_PORTSC_PRESERVE ( XHCI_PORTSC_PP | XHCI_PORTSC_PIC_MASK ) + +/** Port power management status and control register */ +#define XHCI_OP_PORTPMSC(port) ( 0x404 - 0x10 + ( (port) << 4 ) ) + +/** Port link info register */ +#define XHCI_OP_PORTLI(port) ( 0x408 - 0x10 + ( (port) << 4 ) ) + +/** Port hardware link power management control register */ +#define XHCI_OP_PORTHLPMC(port) ( 0x40c - 0x10 + ( (port) << 4 ) ) + +/** Event ring segment table size register */ +#define XHCI_RUN_ERSTSZ(intr) ( 0x28 + ( (intr) << 5 ) ) + +/** Event ring segment table base address register */ +#define XHCI_RUN_ERSTBA(intr) ( 0x30 + ( (intr) << 5 ) ) + +/** Event ring dequeue pointer register */ +#define XHCI_RUN_ERDP(intr) ( 0x38 + ( (intr) << 5 ) ) + +/** A transfer request block template */ +struct xhci_trb_template { + /** Parameter */ + uint64_t parameter; + /** Status */ + uint32_t status; + /** Control */ + uint32_t control; +}; + +/** A transfer request block */ +struct xhci_trb_common { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** Transfer request block cycle bit flag */ +#define XHCI_TRB_C 0x01 + +/** Transfer request block toggle cycle bit flag */ +#define XHCI_TRB_TC 0x02 + +/** Transfer request block chain flag */ +#define XHCI_TRB_CH 0x10 + +/** Transfer request block interrupt on completion flag */ +#define XHCI_TRB_IOC 0x20 + +/** Transfer request block immediate data flag */ +#define XHCI_TRB_IDT 0x40 + +/** Transfer request block type */ +#define XHCI_TRB_TYPE(type) ( (type) << 2 ) + +/** Transfer request block type mask */ +#define XHCI_TRB_TYPE_MASK XHCI_TRB_TYPE ( 0x3f ) + +/** A normal transfer request block */ +struct xhci_trb_normal { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A normal transfer request block */ +#define XHCI_TRB_NORMAL XHCI_TRB_TYPE ( 1 ) + +/** Construct TD size field */ +#define XHCI_TD_SIZE(remaining) \ + ( ( ( (remaining) <= 0xf ) ? remaining : 0xf ) << 17 ) + +/** A setup stage transfer request block */ +struct xhci_trb_setup { + /** Setup packet */ + struct usb_setup_packet packet; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A setup stage transfer request block */ +#define XHCI_TRB_SETUP XHCI_TRB_TYPE ( 2 ) + +/** Setup stage input data direction */ +#define XHCI_SETUP_IN 3 + +/** Setup stage output data direction */ +#define XHCI_SETUP_OUT 2 + +/** A data stage transfer request block */ +struct xhci_trb_data { + /** Data buffer */ + uint64_t data; + /** Length */ + uint32_t len; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Transfer direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved; +} __attribute__ (( packed )); + +/** A data stage transfer request block */ +#define XHCI_TRB_DATA XHCI_TRB_TYPE ( 3 ) + +/** Input data direction */ +#define XHCI_DATA_IN 0x01 + +/** Output data direction */ +#define XHCI_DATA_OUT 0x00 + +/** A status stage transfer request block */ +struct xhci_trb_status { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Direction */ + uint8_t direction; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** A status stage transfer request block */ +#define XHCI_TRB_STATUS XHCI_TRB_TYPE ( 4 ) + +/** Input status direction */ +#define XHCI_STATUS_IN 0x01 + +/** Output status direction */ +#define XHCI_STATUS_OUT 0x00 + +/** A link transfer request block */ +struct xhci_trb_link { + /** Next ring segment */ + uint64_t next; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A link transfer request block */ +#define XHCI_TRB_LINK XHCI_TRB_TYPE ( 6 ) + +/** A no-op transfer request block */ +#define XHCI_TRB_NOP XHCI_TRB_TYPE ( 8 ) + +/** An enable slot transfer request block */ +struct xhci_trb_enable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Slot type */ + uint8_t slot; + /** Reserved */ + uint8_t reserved_c; +} __attribute__ (( packed )); + +/** An enable slot transfer request block */ +#define XHCI_TRB_ENABLE_SLOT XHCI_TRB_TYPE ( 9 ) + +/** A disable slot transfer request block */ +struct xhci_trb_disable_slot { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_c; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A disable slot transfer request block */ +#define XHCI_TRB_DISABLE_SLOT XHCI_TRB_TYPE ( 10 ) + +/** A context transfer request block */ +struct xhci_trb_context { + /** Input context */ + uint64_t input; + /** Reserved */ + uint32_t reserved_a; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint8_t reserved_b; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** An address device transfer request block */ +#define XHCI_TRB_ADDRESS_DEVICE XHCI_TRB_TYPE ( 11 ) + +/** A configure endpoint transfer request block */ +#define XHCI_TRB_CONFIGURE_ENDPOINT XHCI_TRB_TYPE ( 12 ) + +/** An evaluate context transfer request block */ +#define XHCI_TRB_EVALUATE_CONTEXT XHCI_TRB_TYPE ( 13 ) + +/** A reset endpoint transfer request block */ +struct xhci_trb_reset_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A reset endpoint transfer request block */ +#define XHCI_TRB_RESET_ENDPOINT XHCI_TRB_TYPE ( 14 ) + +/** A stop endpoint transfer request block */ +struct xhci_trb_stop_endpoint { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint32_t reserved_b; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A stop endpoint transfer request block */ +#define XHCI_TRB_STOP_ENDPOINT XHCI_TRB_TYPE ( 15 ) + +/** A set transfer ring dequeue pointer transfer request block */ +struct xhci_trb_set_tr_dequeue_pointer { + /** Dequeue pointer */ + uint64_t dequeue; + /** Reserved */ + uint32_t reserved; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A set transfer ring dequeue pointer transfer request block */ +#define XHCI_TRB_SET_TR_DEQUEUE_POINTER XHCI_TRB_TYPE ( 16 ) + +/** A no-op command transfer request block */ +#define XHCI_TRB_NOP_CMD XHCI_TRB_TYPE ( 23 ) + +/** A transfer event transfer request block */ +struct xhci_trb_transfer { + /** Transfer TRB pointer */ + uint64_t transfer; + /** Residual transfer length */ + uint16_t residual; + /** Reserved */ + uint8_t reserved; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Endpoint ID */ + uint8_t endpoint; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A transfer event transfer request block */ +#define XHCI_TRB_TRANSFER XHCI_TRB_TYPE ( 32 ) + +/** A command completion event transfer request block */ +struct xhci_trb_complete { + /** Command TRB pointer */ + uint64_t command; + /** Parameter */ + uint8_t parameter[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Virtual function ID */ + uint8_t vf; + /** Slot ID */ + uint8_t slot; +} __attribute__ (( packed )); + +/** A command completion event transfer request block */ +#define XHCI_TRB_COMPLETE XHCI_TRB_TYPE ( 33 ) + +/** xHCI completion codes */ +enum xhci_completion_code { + /** Success */ + XHCI_CMPLT_SUCCESS = 1, + /** Short packet */ + XHCI_CMPLT_SHORT = 13, + /** Command ring stopped */ + XHCI_CMPLT_CMD_STOPPED = 24, +}; + +/** A port status change transfer request block */ +struct xhci_trb_port_status { + /** Reserved */ + uint8_t reserved_a[3]; + /** Port ID */ + uint8_t port; + /** Reserved */ + uint8_t reserved_b[7]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_PORT_STATUS XHCI_TRB_TYPE ( 34 ) + +/** A port status change transfer request block */ +struct xhci_trb_host_controller { + /** Reserved */ + uint64_t reserved_a; + /** Reserved */ + uint8_t reserved_b[3]; + /** Completion code */ + uint8_t code; + /** Flags */ + uint8_t flags; + /** Type */ + uint8_t type; + /** Reserved */ + uint16_t reserved_c; +} __attribute__ (( packed )); + +/** A port status change transfer request block */ +#define XHCI_TRB_HOST_CONTROLLER XHCI_TRB_TYPE ( 37 ) + +/** A transfer request block */ +union xhci_trb { + /** Template */ + struct xhci_trb_template template; + /** Common fields */ + struct xhci_trb_common common; + /** Normal TRB */ + struct xhci_trb_normal normal; + /** Setup stage TRB */ + struct xhci_trb_setup setup; + /** Data stage TRB */ + struct xhci_trb_data data; + /** Status stage TRB */ + struct xhci_trb_status status; + /** Link TRB */ + struct xhci_trb_link link; + /** Enable slot TRB */ + struct xhci_trb_enable_slot enable; + /** Disable slot TRB */ + struct xhci_trb_disable_slot disable; + /** Input context TRB */ + struct xhci_trb_context context; + /** Reset endpoint TRB */ + struct xhci_trb_reset_endpoint reset; + /** Stop endpoint TRB */ + struct xhci_trb_stop_endpoint stop; + /** Set transfer ring dequeue pointer TRB */ + struct xhci_trb_set_tr_dequeue_pointer dequeue; + /** Transfer event */ + struct xhci_trb_transfer transfer; + /** Command completion event */ + struct xhci_trb_complete complete; + /** Port status changed event */ + struct xhci_trb_port_status port; + /** Host controller event */ + struct xhci_trb_host_controller host; +} __attribute__ (( packed )); + +/** An input control context */ +struct xhci_control_context { + /** Drop context flags */ + uint32_t drop; + /** Add context flags */ + uint32_t add; + /** Reserved */ + uint32_t reserved_a[5]; + /** Configuration value */ + uint8_t config; + /** Interface number */ + uint8_t intf; + /** Alternate setting */ + uint8_t alt; + /** Reserved */ + uint8_t reserved_b; +} __attribute__ (( packed )); + +/** A slot context */ +struct xhci_slot_context { + /** Device info */ + uint32_t info; + /** Maximum exit latency */ + uint16_t latency; + /** Root hub port number */ + uint8_t port; + /** Number of downstream ports */ + uint8_t ports; + /** TT hub slot ID */ + uint8_t tt_id; + /** TT port number */ + uint8_t tt_port; + /** Interrupter target */ + uint16_t intr; + /** USB address */ + uint8_t address; + /** Reserved */ + uint16_t reserved_a; + /** Slot state */ + uint8_t state; + /** Reserved */ + uint32_t reserved_b[4]; +} __attribute__ (( packed )); + +/** Construct slot context device info */ +#define XHCI_SLOT_INFO( entries, hub, speed, route ) \ + ( ( (entries) << 27 ) | ( (hub) << 26 ) | ( (speed) << 20 ) | (route) ) + +/** An endpoint context */ +struct xhci_endpoint_context { + /** Endpoint state */ + uint8_t state; + /** Stream configuration */ + uint8_t stream; + /** Polling interval */ + uint8_t interval; + /** Max ESIT payload high */ + uint8_t esit_high; + /** Endpoint type */ + uint8_t type; + /** Maximum burst size */ + uint8_t burst; + /** Maximum packet size */ + uint16_t mtu; + /** Transfer ring dequeue pointer */ + uint64_t dequeue; + /** Average TRB length */ + uint16_t trb_len; + /** Max ESIT payload low */ + uint16_t esit_low; + /** Reserved */ + uint32_t reserved[3]; +} __attribute__ (( packed )); + +/** Endpoint states */ +enum xhci_endpoint_state { + /** Endpoint is disabled */ + XHCI_ENDPOINT_DISABLED = 0, + /** Endpoint is running */ + XHCI_ENDPOINT_RUNNING = 1, + /** Endpoint is halted due to a USB Halt condition */ + XHCI_ENDPOINT_HALTED = 2, + /** Endpoint is stopped */ + XHCI_ENDPOINT_STOPPED = 3, + /** Endpoint is halted due to a TRB error */ + XHCI_ENDPOINT_ERROR = 4, +}; + +/** Endpoint state mask */ +#define XHCI_ENDPOINT_STATE_MASK 0x07 + +/** Endpoint type */ +#define XHCI_EP_TYPE(type) ( (type) << 3 ) + +/** Control endpoint type */ +#define XHCI_EP_TYPE_CONTROL XHCI_EP_TYPE ( 4 ) + +/** Input endpoint type */ +#define XHCI_EP_TYPE_IN XHCI_EP_TYPE ( 4 ) + +/** Periodic endpoint type */ +#define XHCI_EP_TYPE_PERIODIC XHCI_EP_TYPE ( 1 ) + +/** Endpoint dequeue cycle state */ +#define XHCI_EP_DCS 0x00000001UL + +/** Control endpoint average TRB length */ +#define XHCI_EP0_TRB_LEN 8 + +/** An event ring segment */ +struct xhci_event_ring_segment { + /** Base address */ + uint64_t base; + /** Number of TRBs */ + uint32_t count; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** A transfer request block command/transfer ring */ +struct xhci_trb_ring { + /** Producer counter */ + unsigned int prod; + /** Consumer counter */ + unsigned int cons; + /** Ring size (log2) */ + unsigned int shift; + /** Ring counter mask */ + unsigned int mask; + + /** I/O buffers */ + struct io_buffer **iobuf; + + /** Transfer request blocks */ + union xhci_trb *trb; + /** Length of transfer request blocks */ + size_t len; + /** Link TRB (if applicable) */ + struct xhci_trb_link *link; + + /** Doorbell register */ + void *db; + /** Doorbell register value */ + uint32_t dbval; +}; + +/** An event ring */ +struct xhci_event_ring { + /** Consumer counter */ + unsigned int cons; + /** Event ring segment table */ + struct xhci_event_ring_segment *segment; + /** Transfer request blocks */ + union xhci_trb *trb; +}; + +/** + * Calculate doorbell register value + * + * @v target Doorbell target + * @v stream Doorbell stream ID + * @ret dbval Doorbell register value + */ +#define XHCI_DBVAL( target, stream ) ( (target) | ( (stream) << 16 ) ) + +/** + * Calculate space used in TRB ring + * + * @v ring TRB ring + * @ret fill Number of entries used + */ +static inline __attribute__ (( always_inline )) unsigned int +xhci_ring_fill ( struct xhci_trb_ring *ring ) { + + return ( ring->prod - ring->cons ); +} + +/** + * Calculate space remaining in TRB ring + * + * @v ring TRB ring + * @ret remaining Number of entries remaining + * + * xHCI does not allow us to completely fill a ring; there must be at + * least one free entry (excluding the Link TRB). + */ +static inline __attribute__ (( always_inline )) unsigned int +xhci_ring_remaining ( struct xhci_trb_ring *ring ) { + unsigned int fill = xhci_ring_fill ( ring ); + + /* We choose to utilise rings with ( 2^n + 1 ) entries, with + * the final entry being a Link TRB. The maximum fill level + * is therefore + * + * ( ( 2^n + 1 ) - 1 (Link TRB) - 1 (one slot always empty) + * == ( 2^n - 1 ) + * + * which is therefore equal to the ring mask. + */ + assert ( fill <= ring->mask ); + return ( ring->mask - fill ); +} + +/** + * Calculate physical address of most recently consumed TRB + * + * @v ring TRB ring + * @ret trb TRB physical address + */ +static inline __attribute__ (( always_inline )) physaddr_t +xhci_ring_consumed ( struct xhci_trb_ring *ring ) { + unsigned int index = ( ( ring->cons - 1 ) & ring->mask ); + + return virt_to_phys ( &ring->trb[index] ); +} + +/** Slot context index */ +#define XHCI_CTX_SLOT 0 + +/** Calculate context index from USB endpoint address */ +#define XHCI_CTX(address) \ + ( (address) ? ( ( ( (address) & 0x0f ) << 1 ) | \ + ( ( (address) & 0x80 ) >> 7 ) ) : 1 ) + +/** Endpoint zero context index */ +#define XHCI_CTX_EP0 XHCI_CTX ( 0x00 ) + +/** End of contexts */ +#define XHCI_CTX_END 32 + +/** Device context index */ +#define XHCI_DCI(ctx) ( (ctx) + 0 ) + +/** Input context index */ +#define XHCI_ICI(ctx) ( (ctx) + 1 ) + +/** Number of TRBs (excluding Link TRB) in the command ring + * + * This is a policy decision. + */ +#define XHCI_CMD_TRBS_LOG2 2 + +/** Number of TRBs in the event ring + * + * This is a policy decision. + */ +#define XHCI_EVENT_TRBS_LOG2 6 + +/** Number of TRBs in a transfer ring + * + * This is a policy decision. + */ +#define XHCI_TRANSFER_TRBS_LOG2 6 + +/** Maximum time to wait for BIOS to release ownership + * + * This is a policy decision. + */ +#define XHCI_USBLEGSUP_MAX_WAIT_MS 100 + +/** Maximum time to wait for host controller to stop + * + * This is a policy decision. + */ +#define XHCI_STOP_MAX_WAIT_MS 100 + +/** Maximum time to wait for reset to complete + * + * This is a policy decision. + */ +#define XHCI_RESET_MAX_WAIT_MS 500 + +/** Maximum time to wait for a command to complete + * + * The "address device" command involves waiting for a response to a + * USB control transaction, and so we must wait for up to the 5000ms + * that USB allows for devices to respond to control transactions. + */ +#define XHCI_COMMAND_MAX_WAIT_MS USB_CONTROL_MAX_WAIT_MS + +/** Time to delay after aborting a command + * + * This is a policy decision + */ +#define XHCI_COMMAND_ABORT_DELAY_MS 500 + +/** Maximum time to wait for a port reset to complete + * + * This is a policy decision. + */ +#define XHCI_PORT_RESET_MAX_WAIT_MS 500 + +/** Intel PCH quirk */ +struct xhci_pch { + /** USB2 port routing register original value */ + uint32_t xusb2pr; + /** USB3 port SuperSpeed enable register original value */ + uint32_t usb3pssen; +}; + +/** Intel PCH quirk flag */ +#define XHCI_PCH 0x0001 + +/** Intel PCH USB2 port routing register */ +#define XHCI_PCH_XUSB2PR 0xd0 + +/** Intel PCH USB2 port routing mask register */ +#define XHCI_PCH_XUSB2PRM 0xd4 + +/** Intel PCH SuperSpeed enable register */ +#define XHCI_PCH_USB3PSSEN 0xd8 + +/** Intel PCH USB3 port routing mask register */ +#define XHCI_PCH_USB3PRM 0xdc + +/** Invalid protocol speed ID values quirk */ +#define XHCI_BAD_PSIV 0x0002 + +/** An xHCI device */ +struct xhci_device { + /** Registers */ + void *regs; + /** Name */ + const char *name; + /** Quirks */ + unsigned int quirks; + + /** Capability registers */ + void *cap; + /** Operational registers */ + void *op; + /** Runtime registers */ + void *run; + /** Doorbell registers */ + void *db; + + /** Number of device slots */ + unsigned int slots; + /** Number of interrupters */ + unsigned int intrs; + /** Number of ports */ + unsigned int ports; + + /** Number of page-sized scratchpad buffers */ + unsigned int scratchpads; + + /** 64-bit addressing capability */ + int addr64; + /** Context size shift */ + unsigned int csz_shift; + /** xHCI extended capabilities offset */ + unsigned int xecp; + + /** Page size */ + size_t pagesize; + + /** USB legacy support capability (if present and enabled) */ + unsigned int legacy; + + /** Device context base address array */ + uint64_t *dcbaa; + + /** Scratchpad buffer area */ + userptr_t scratchpad; + /** Scratchpad buffer array */ + uint64_t *scratchpad_array; + + /** Command ring */ + struct xhci_trb_ring command; + /** Event ring */ + struct xhci_event_ring event; + /** Current command (if any) */ + union xhci_trb *pending; + + /** Device slots, indexed by slot ID */ + struct xhci_slot **slot; + + /** USB bus */ + struct usb_bus *bus; + + /** Intel PCH quirk */ + struct xhci_pch pch; +}; + +/** An xHCI device slot */ +struct xhci_slot { + /** xHCI device */ + struct xhci_device *xhci; + /** USB device */ + struct usb_device *usb; + /** Slot ID */ + unsigned int id; + /** Slot context */ + struct xhci_slot_context *context; + /** Route string */ + unsigned int route; + /** Root hub port number */ + unsigned int port; + /** Protocol speed ID */ + unsigned int psiv; + /** Number of ports (if this device is a hub) */ + unsigned int ports; + /** Transaction translator slot ID */ + unsigned int tt_id; + /** Transaction translator port */ + unsigned int tt_port; + /** Endpoints, indexed by context ID */ + struct xhci_endpoint *endpoint[XHCI_CTX_END]; +}; + +/** An xHCI endpoint */ +struct xhci_endpoint { + /** xHCI device */ + struct xhci_device *xhci; + /** xHCI slot */ + struct xhci_slot *slot; + /** USB endpoint */ + struct usb_endpoint *ep; + /** Context index */ + unsigned int ctx; + /** Endpoint type */ + unsigned int type; + /** Endpoint interval */ + unsigned int interval; + /** Endpoint context */ + struct xhci_endpoint_context *context; + /** Transfer ring */ + struct xhci_trb_ring ring; +}; + +#endif /* _IPXE_XHCI_H */ diff --git a/roms/ipxe/src/hci/commands/autoboot_cmd.c b/roms/ipxe/src/hci/commands/autoboot_cmd.c index 62235a278..56f39a1ce 100644 --- a/roms/ipxe/src/hci/commands/autoboot_cmd.c +++ b/roms/ipxe/src/hci/commands/autoboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <stdio.h> @@ -25,7 +29,7 @@ #include <hci/ifmgmt_cmd.h> #include <usr/autoboot.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/config_cmd.c b/roms/ipxe/src/hci/commands/config_cmd.c index b81c866ff..ad415e045 100644 --- a/roms/ipxe/src/hci/commands/config_cmd.c +++ b/roms/ipxe/src/hci/commands/config_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <string.h> @@ -26,7 +30,7 @@ #include <ipxe/settings.h> #include <ipxe/settings_ui.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/console_cmd.c b/roms/ipxe/src/hci/commands/console_cmd.c index d2eae59f0..ba472b9f6 100644 --- a/roms/ipxe/src/hci/commands/console_cmd.c +++ b/roms/ipxe/src/hci/commands/console_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/dhcp_cmd.c b/roms/ipxe/src/hci/commands/dhcp_cmd.c index feeb55ee5..45a922b51 100644 --- a/roms/ipxe/src/hci/commands/dhcp_cmd.c +++ b/roms/ipxe/src/hci/commands/dhcp_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdint.h> diff --git a/roms/ipxe/src/hci/commands/fcmgmt_cmd.c b/roms/ipxe/src/hci/commands/fcmgmt_cmd.c index 1c199b5dc..97f10f4dd 100644 --- a/roms/ipxe/src/hci/commands/fcmgmt_cmd.c +++ b/roms/ipxe/src/hci/commands/fcmgmt_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/hci/commands/gdbstub_cmd.c b/roms/ipxe/src/hci/commands/gdbstub_cmd.c index 33890aebc..c4a831e7a 100644 --- a/roms/ipxe/src/hci/commands/gdbstub_cmd.c +++ b/roms/ipxe/src/hci/commands/gdbstub_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/hci/commands/ifmgmt_cmd.c b/roms/ipxe/src/hci/commands/ifmgmt_cmd.c index 5307c9423..c89af2e81 100644 --- a/roms/ipxe/src/hci/commands/ifmgmt_cmd.c +++ b/roms/ipxe/src/hci/commands/ifmgmt_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/hci/commands/image_cmd.c b/roms/ipxe/src/hci/commands/image_cmd.c index a9e831bf5..4a7c500a4 100644 --- a/roms/ipxe/src/hci/commands/image_cmd.c +++ b/roms/ipxe/src/hci/commands/image_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/hci/commands/image_trust_cmd.c b/roms/ipxe/src/hci/commands/image_trust_cmd.c index ca59a858a..f9d6b5b3e 100644 --- a/roms/ipxe/src/hci/commands/image_trust_cmd.c +++ b/roms/ipxe/src/hci/commands/image_trust_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> @@ -169,6 +173,9 @@ struct command image_trust_commands[] __command = { }, }; +/* Drag in objects via command list */ +REQUIRING_SYMBOL ( image_trust_commands ); + /* Drag in objects typically required for signature verification */ REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); diff --git a/roms/ipxe/src/hci/commands/ipstat_cmd.c b/roms/ipxe/src/hci/commands/ipstat_cmd.c index d565dc0ae..763e4dfd6 100644 --- a/roms/ipxe/src/hci/commands/ipstat_cmd.c +++ b/roms/ipxe/src/hci/commands/ipstat_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <getopt.h> diff --git a/roms/ipxe/src/hci/commands/login_cmd.c b/roms/ipxe/src/hci/commands/login_cmd.c index f5db427d5..c9e196437 100644 --- a/roms/ipxe/src/hci/commands/login_cmd.c +++ b/roms/ipxe/src/hci/commands/login_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <string.h> @@ -23,7 +27,7 @@ #include <ipxe/parseopt.h> #include <ipxe/login_ui.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/lotest_cmd.c b/roms/ipxe/src/hci/commands/lotest_cmd.c index 0fa031bcb..a989932d4 100644 --- a/roms/ipxe/src/hci/commands/lotest_cmd.c +++ b/roms/ipxe/src/hci/commands/lotest_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdlib.h> diff --git a/roms/ipxe/src/hci/commands/menu_cmd.c b/roms/ipxe/src/hci/commands/menu_cmd.c index 66a6262e6..76bce8695 100644 --- a/roms/ipxe/src/hci/commands/menu_cmd.c +++ b/roms/ipxe/src/hci/commands/menu_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/neighbour_cmd.c b/roms/ipxe/src/hci/commands/neighbour_cmd.c index a1e052439..816e87357 100644 --- a/roms/ipxe/src/hci/commands/neighbour_cmd.c +++ b/roms/ipxe/src/hci/commands/neighbour_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/nvo_cmd.c b/roms/ipxe/src/hci/commands/nvo_cmd.c index e63dab08e..ac0d60651 100644 --- a/roms/ipxe/src/hci/commands/nvo_cmd.c +++ b/roms/ipxe/src/hci/commands/nvo_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <stdint.h> @@ -29,7 +33,7 @@ #include <ipxe/parseopt.h> #include <readline/readline.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/param_cmd.c b/roms/ipxe/src/hci/commands/param_cmd.c index 6cf096d00..bff04f2ff 100644 --- a/roms/ipxe/src/hci/commands/param_cmd.c +++ b/roms/ipxe/src/hci/commands/param_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/pci_cmd.c b/roms/ipxe/src/hci/commands/pci_cmd.c index f5145fb35..a2a811aa0 100644 --- a/roms/ipxe/src/hci/commands/pci_cmd.c +++ b/roms/ipxe/src/hci/commands/pci_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <stdio.h> @@ -23,7 +27,7 @@ #include <ipxe/command.h> #include <ipxe/parseopt.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/ping_cmd.c b/roms/ipxe/src/hci/commands/ping_cmd.c index 34807696f..ab271e75a 100644 --- a/roms/ipxe/src/hci/commands/ping_cmd.c +++ b/roms/ipxe/src/hci/commands/ping_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/hci/commands/poweroff_cmd.c b/roms/ipxe/src/hci/commands/poweroff_cmd.c index 9d487d330..afdf12dde 100644 --- a/roms/ipxe/src/hci/commands/poweroff_cmd.c +++ b/roms/ipxe/src/hci/commands/poweroff_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <stdio.h> @@ -24,7 +28,7 @@ #include <ipxe/parseopt.h> #include <ipxe/reboot.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/profstat_cmd.c b/roms/ipxe/src/hci/commands/profstat_cmd.c index e4c9e5a24..dc6f649e3 100644 --- a/roms/ipxe/src/hci/commands/profstat_cmd.c +++ b/roms/ipxe/src/hci/commands/profstat_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <getopt.h> diff --git a/roms/ipxe/src/hci/commands/reboot_cmd.c b/roms/ipxe/src/hci/commands/reboot_cmd.c index 485939e42..45d54cc2c 100644 --- a/roms/ipxe/src/hci/commands/reboot_cmd.c +++ b/roms/ipxe/src/hci/commands/reboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <getopt.h> @@ -22,7 +26,7 @@ #include <ipxe/parseopt.h> #include <ipxe/reboot.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/route_cmd.c b/roms/ipxe/src/hci/commands/route_cmd.c index cc5ffc2f2..8aa535363 100644 --- a/roms/ipxe/src/hci/commands/route_cmd.c +++ b/roms/ipxe/src/hci/commands/route_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <getopt.h> diff --git a/roms/ipxe/src/hci/commands/sanboot_cmd.c b/roms/ipxe/src/hci/commands/sanboot_cmd.c index 5954b6326..24ec8bc4e 100644 --- a/roms/ipxe/src/hci/commands/sanboot_cmd.c +++ b/roms/ipxe/src/hci/commands/sanboot_cmd.c @@ -15,6 +15,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #include <stdio.h> @@ -27,7 +31,7 @@ #include <ipxe/sanboot.h> #include <usr/autoboot.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/commands/sync_cmd.c b/roms/ipxe/src/hci/commands/sync_cmd.c index adf7e3cc6..54799d422 100644 --- a/roms/ipxe/src/hci/commands/sync_cmd.c +++ b/roms/ipxe/src/hci/commands/sync_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> diff --git a/roms/ipxe/src/hci/commands/vlan_cmd.c b/roms/ipxe/src/hci/commands/vlan_cmd.c index 5d7298220..8a2f0c749 100644 --- a/roms/ipxe/src/hci/commands/vlan_cmd.c +++ b/roms/ipxe/src/hci/commands/vlan_cmd.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdlib.h> diff --git a/roms/ipxe/src/hci/editstring.c b/roms/ipxe/src/hci/editstring.c index 5f6f04d51..8cbce0767 100644 --- a/roms/ipxe/src/hci/editstring.c +++ b/roms/ipxe/src/hci/editstring.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <assert.h> #include <string.h> diff --git a/roms/ipxe/src/hci/jumpscroll.c b/roms/ipxe/src/hci/jumpscroll.c new file mode 100644 index 000000000..dd6bcac2b --- /dev/null +++ b/roms/ipxe/src/hci/jumpscroll.c @@ -0,0 +1,140 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * Jump scrolling + * + */ + +#include <assert.h> +#include <ipxe/keys.h> +#include <ipxe/jumpscroll.h> + +/** + * Handle keypress + * + * @v scroll Jump scroller + * @v key Key pressed by user + * @ret move Scroller movement, or zero + */ +int jump_scroll_key ( struct jump_scroller *scroll, int key ) { + + /* Sanity checks */ + assert ( scroll->rows != 0 ); + assert ( scroll->count != 0 ); + assert ( scroll->current < scroll->count ); + assert ( scroll->first < scroll->count ); + assert ( scroll->first <= scroll->current ); + assert ( scroll->current < ( scroll->first + scroll->rows ) ); + + /* Handle key, if applicable */ + switch ( key ) { + case KEY_UP: + return -1; + case KEY_DOWN: + return +1; + case KEY_PPAGE: + return ( scroll->first - scroll->current - 1 ); + case KEY_NPAGE: + return ( scroll->first - scroll->current + scroll->rows ); + case KEY_HOME: + return -( scroll->count ); + case KEY_END: + return +( scroll->count ); + default: + return 0; + } +} + +/** + * Move scroller + * + * @v scroll Jump scroller + * @v move Scroller movement + * @ret move Continuing scroller movement (if applicable) + */ +int jump_scroll_move ( struct jump_scroller *scroll, int move ) { + int current = scroll->current; + int last = ( scroll->count - 1 ); + + /* Sanity checks */ + assert ( move != 0 ); + assert ( scroll->count != 0 ); + + /* Move to the new current item */ + current += move; + + /* Check for start/end of list */ + if ( current < 0 ) { + /* We have attempted to move before the start of the + * list. Move to the start of the list and continue + * moving forwards (if applicable). + */ + scroll->current = 0; + return +1; + } else if ( current > last ) { + /* We have attempted to move after the end of the + * list. Move to the end of the list and continue + * moving backwards (if applicable). + */ + scroll->current = last; + return -1; + } else { + /* Update the current item and continue moving in the + * same direction (if applicable). + */ + scroll->current = current; + return ( ( move > 0 ) ? +1 : -1 ); + } +} + +/** + * Jump scroll to new page (if applicable) + * + * @v scroll Jump scroller + * @ret jumped Jumped to a new page + */ +int jump_scroll ( struct jump_scroller *scroll ) { + unsigned int index; + + /* Sanity checks */ + assert ( scroll->rows != 0 ); + assert ( scroll->count != 0 ); + assert ( scroll->current < scroll->count ); + assert ( scroll->first < scroll->count ); + + /* Do nothing if we are already on the correct page */ + index = ( scroll->current - scroll->first ); + if ( index < scroll->rows ) + return 0; + + /* Move to required page */ + while ( scroll->first < scroll->current ) + scroll->first += scroll->rows; + while ( scroll->first > scroll->current ) + scroll->first -= scroll->rows; + + return 1; +} diff --git a/roms/ipxe/src/hci/mucurses/alert.c b/roms/ipxe/src/hci/mucurses/alert.c index 00e959a89..7dc61c222 100644 --- a/roms/ipxe/src/hci/mucurses/alert.c +++ b/roms/ipxe/src/hci/mucurses/alert.c @@ -7,6 +7,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Audible signal * diff --git a/roms/ipxe/src/hci/mucurses/ansi_screen.c b/roms/ipxe/src/hci/mucurses/ansi_screen.c index 1d3143f89..1cf3309dd 100644 --- a/roms/ipxe/src/hci/mucurses/ansi_screen.c +++ b/roms/ipxe/src/hci/mucurses/ansi_screen.c @@ -3,7 +3,7 @@ #include <ipxe/ansicol.h> #include <ipxe/console.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static void ansiscr_reset(struct _curses_screen *scr) __nonnull; static void ansiscr_movetoyx(struct _curses_screen *scr, diff --git a/roms/ipxe/src/hci/mucurses/clear.c b/roms/ipxe/src/hci/mucurses/clear.c index f5e52ca20..2054f72cc 100644 --- a/roms/ipxe/src/hci/mucurses/clear.c +++ b/roms/ipxe/src/hci/mucurses/clear.c @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Clear a window to the bottom from current cursor position diff --git a/roms/ipxe/src/hci/mucurses/colour.c b/roms/ipxe/src/hci/mucurses/colour.c index c1359c868..b0c480b1f 100644 --- a/roms/ipxe/src/hci/mucurses/colour.c +++ b/roms/ipxe/src/hci/mucurses/colour.c @@ -1,6 +1,6 @@ #include <curses.h> -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct colour_pair { short fcol; diff --git a/roms/ipxe/src/hci/mucurses/cursor.h b/roms/ipxe/src/hci/mucurses/cursor.h index 16b7d27c2..2e0c896a6 100644 --- a/roms/ipxe/src/hci/mucurses/cursor.h +++ b/roms/ipxe/src/hci/mucurses/cursor.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct cursor_pos { unsigned int y, x; diff --git a/roms/ipxe/src/hci/mucurses/edging.c b/roms/ipxe/src/hci/mucurses/edging.c index eccd32422..e938d338b 100644 --- a/roms/ipxe/src/hci/mucurses/edging.c +++ b/roms/ipxe/src/hci/mucurses/edging.c @@ -8,6 +8,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Draw borders from single-byte characters and renditions around a * window diff --git a/roms/ipxe/src/hci/mucurses/kb.c b/roms/ipxe/src/hci/mucurses/kb.c index b38c8c146..8face14d8 100644 --- a/roms/ipxe/src/hci/mucurses/kb.c +++ b/roms/ipxe/src/hci/mucurses/kb.c @@ -8,6 +8,8 @@ * MuCurses keyboard input handling functions */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #define INPUT_DELAY 200 // half-blocking delay timer resolution (ms) #define INPUT_DELAY_TIMEOUT 1000 // half-blocking delay timeout diff --git a/roms/ipxe/src/hci/mucurses/mucurses.c b/roms/ipxe/src/hci/mucurses/mucurses.c index b67445baf..98a8a2c59 100644 --- a/roms/ipxe/src/hci/mucurses/mucurses.c +++ b/roms/ipxe/src/hci/mucurses/mucurses.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); static void _wupdcurs ( WINDOW *win ) __nonnull; void _wputch ( WINDOW *win, chtype ch, int wrap ) __nonnull; diff --git a/roms/ipxe/src/hci/mucurses/mucurses.h b/roms/ipxe/src/hci/mucurses/mucurses.h index 7ac1086ac..270394787 100644 --- a/roms/ipxe/src/hci/mucurses/mucurses.h +++ b/roms/ipxe/src/hci/mucurses/mucurses.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #define WRAP 0 #define NOWRAP 1 diff --git a/roms/ipxe/src/hci/mucurses/print.c b/roms/ipxe/src/hci/mucurses/print.c index 9c682588b..e8831c58f 100644 --- a/roms/ipxe/src/hci/mucurses/print.c +++ b/roms/ipxe/src/hci/mucurses/print.c @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Add a single-byte character and rendition to a window and advance diff --git a/roms/ipxe/src/hci/mucurses/print_nadv.c b/roms/ipxe/src/hci/mucurses/print_nadv.c index ee472e685..3a44e5bd2 100644 --- a/roms/ipxe/src/hci/mucurses/print_nadv.c +++ b/roms/ipxe/src/hci/mucurses/print_nadv.c @@ -8,6 +8,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Add string of single-byte characters and renditions to a window * diff --git a/roms/ipxe/src/hci/mucurses/slk.c b/roms/ipxe/src/hci/mucurses/slk.c index 600658e75..660eb65c0 100644 --- a/roms/ipxe/src/hci/mucurses/slk.c +++ b/roms/ipxe/src/hci/mucurses/slk.c @@ -11,6 +11,8 @@ * Soft label key functions */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #define MIN_SPACE_SIZE 2 #define SLK_MAX_LABEL_LEN 8 diff --git a/roms/ipxe/src/hci/mucurses/widgets/editbox.c b/roms/ipxe/src/hci/mucurses/widgets/editbox.c index 630a66e0b..210de4481 100644 --- a/roms/ipxe/src/hci/mucurses/widgets/editbox.c +++ b/roms/ipxe/src/hci/mucurses/widgets/editbox.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <assert.h> diff --git a/roms/ipxe/src/hci/mucurses/winattrs.c b/roms/ipxe/src/hci/mucurses/winattrs.c index f549d7519..97a5a18b3 100644 --- a/roms/ipxe/src/hci/mucurses/winattrs.c +++ b/roms/ipxe/src/hci/mucurses/winattrs.c @@ -6,7 +6,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Get the background rendition attributes for a window diff --git a/roms/ipxe/src/hci/mucurses/windows.c b/roms/ipxe/src/hci/mucurses/windows.c index 63d0af08c..7f39bdea2 100644 --- a/roms/ipxe/src/hci/mucurses/windows.c +++ b/roms/ipxe/src/hci/mucurses/windows.c @@ -9,6 +9,8 @@ * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + /** * Delete a window * diff --git a/roms/ipxe/src/hci/mucurses/wininit.c b/roms/ipxe/src/hci/mucurses/wininit.c index b75abba44..dd84d2f1d 100644 --- a/roms/ipxe/src/hci/mucurses/wininit.c +++ b/roms/ipxe/src/hci/mucurses/wininit.c @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Initialise console environment diff --git a/roms/ipxe/src/hci/readline.c b/roms/ipxe/src/hci/readline.c index 40aa59787..83a2e0b90 100644 --- a/roms/ipxe/src/hci/readline.c +++ b/roms/ipxe/src/hci/readline.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <string.h> diff --git a/roms/ipxe/src/hci/shell.c b/roms/ipxe/src/hci/shell.c index c1a543849..276eb3527 100644 --- a/roms/ipxe/src/hci/shell.c +++ b/roms/ipxe/src/hci/shell.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -28,6 +32,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/command.h> #include <ipxe/parseopt.h> #include <ipxe/shell.h> +#include <config/branding.h> /** @file * @@ -36,7 +41,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /** The shell prompt string */ -static const char shell_prompt[] = "iPXE> "; +static const char shell_prompt[] = PRODUCT_SHORT_NAME "> "; /** * "help" command diff --git a/roms/ipxe/src/hci/strerror.c b/roms/ipxe/src/hci/strerror.c index 9356e9e0a..1bba8c620 100644 --- a/roms/ipxe/src/hci/strerror.c +++ b/roms/ipxe/src/hci/strerror.c @@ -2,6 +2,7 @@ #include <string.h> #include <stdio.h> #include <ipxe/errortab.h> +#include <config/branding.h> /** @file * @@ -18,7 +19,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Find error description @@ -74,7 +75,7 @@ static struct errortab * find_closest_error ( int errno ) { * call to strerror(). * */ -const char * strerror ( int errno ) { +char * strerror ( int errno ) { static char errbuf[64]; struct errortab *errortab; @@ -88,11 +89,11 @@ const char * strerror ( int errno ) { /* Construct the error message */ if ( errortab ) { snprintf ( errbuf, sizeof ( errbuf ), - "%s (http://ipxe.org/%08x)", + "%s (" PRODUCT_ERROR_URI ")", errortab->text, errno ); } else { snprintf ( errbuf, sizeof ( errbuf ), - "Error %#08x (http://ipxe.org/%08x)", + "Error %#08x (" PRODUCT_ERROR_URI ")", errno, errno ); } diff --git a/roms/ipxe/src/hci/tui/login_ui.c b/roms/ipxe/src/hci/tui/login_ui.c index 996b68a0a..3c55325d5 100644 --- a/roms/ipxe/src/hci/tui/login_ui.c +++ b/roms/ipxe/src/hci/tui/login_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/hci/tui/menu_ui.c b/roms/ipxe/src/hci/tui/menu_ui.c index 0a9566def..f9dd9d100 100644 --- a/roms/ipxe/src/hci/tui/menu_ui.c +++ b/roms/ipxe/src/hci/tui/menu_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -32,6 +36,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/timer.h> #include <ipxe/console.h> #include <ipxe/ansicol.h> +#include <ipxe/jumpscroll.h> #include <ipxe/menu.h> /* Screen layout */ @@ -46,12 +51,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); struct menu_ui { /** Menu */ struct menu *menu; - /** Number of menu items */ - unsigned int count; - /** Currently selected item */ - int selected; - /** First visible item */ - int first_visible; + /** Jump scroller */ + struct jump_scroller scroll; /** Timeout (0=indefinite) */ unsigned long timeout; }; @@ -80,7 +81,7 @@ static struct menu_item * menu_item ( struct menu *menu, unsigned int index ) { * @v ui Menu user interface * @v index Index */ -static void draw_menu_item ( struct menu_ui *ui, int index ) { +static void draw_menu_item ( struct menu_ui *ui, unsigned int index ) { struct menu_item *item; unsigned int row_offset; char buf[ MENU_COLS + 1 /* NUL */ ]; @@ -90,7 +91,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { size_t len; /* Move to start of row */ - row_offset = ( index - ui->first_visible ); + row_offset = ( index - ui->scroll.first ); move ( ( MENU_ROW + row_offset ), MENU_COL ); /* Get menu item */ @@ -102,7 +103,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { color_set ( CPAIR_SEPARATOR, NULL ); /* Highlight if this is the selected item */ - if ( index == ui->selected ) { + if ( index == ui->scroll.current ) { color_set ( CPAIR_SELECT, NULL ); attron ( A_BOLD ); } @@ -121,7 +122,7 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { snprintf ( timeout_buf, sizeof ( timeout_buf ), "(%ld)", ( ( ui->timeout + TICKS_PER_SEC - 1 ) / TICKS_PER_SEC ) ); - if ( ( index == ui->selected ) && ( ui->timeout != 0 ) ) { + if ( ( index == ui->scroll.current ) && ( ui->timeout != 0 ) ) { memcpy ( ( buf + MENU_COLS - MENU_PAD - timeout_len ), timeout_buf, timeout_len ); } @@ -150,24 +151,17 @@ static void draw_menu_item ( struct menu_ui *ui, int index ) { static void draw_menu_items ( struct menu_ui *ui ) { unsigned int i; - /* Jump scroll to correct point in list */ - while ( ui->first_visible < ui->selected ) - ui->first_visible += MENU_ROWS; - while ( ui->first_visible > ui->selected ) - ui->first_visible -= MENU_ROWS; - /* Draw ellipses before and/or after the list as necessary */ color_set ( CPAIR_SEPARATOR, NULL ); mvaddstr ( ( MENU_ROW - 1 ), ( MENU_COL + MENU_PAD ), - ( ( ui->first_visible > 0 ) ? "..." : " " ) ); + ( jump_scroll_is_first ( &ui->scroll ) ? " " : "..." ) ); mvaddstr ( ( MENU_ROW + MENU_ROWS ), ( MENU_COL + MENU_PAD ), - ( ( ( ui->first_visible + MENU_ROWS ) < ui->count ) ? - "..." : " " ) ); + ( jump_scroll_is_last ( &ui->scroll ) ? " " : "..." ) ); color_set ( CPAIR_NORMAL, NULL ); /* Draw visible items */ for ( i = 0 ; i < MENU_ROWS ; i++ ) - draw_menu_item ( ui, ( ui->first_visible + i ) ); + draw_menu_item ( ui, ( ui->scroll.first + i ) ); } /** @@ -180,8 +174,7 @@ static void draw_menu_items ( struct menu_ui *ui ) { static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { struct menu_item *item; unsigned long timeout; - unsigned int delta; - int current; + unsigned int previous; int key; int i; int move; @@ -190,7 +183,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { do { /* Record current selection */ - current = ui->selected; + previous = ui->scroll.current; /* Calculate timeout as remainder of current second */ timeout = ( ui->timeout % TICKS_PER_SEC ); @@ -209,27 +202,11 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Cancel any timeout */ ui->timeout = 0; - /* Handle key */ + /* Handle scroll keys */ + move = jump_scroll_key ( &ui->scroll, key ); + + /* Handle other keys */ switch ( key ) { - case KEY_UP: - move = -1; - break; - case KEY_DOWN: - move = +1; - break; - case KEY_PPAGE: - move = ( ui->first_visible - ui->selected - 1 ); - break; - case KEY_NPAGE: - move = ( ui->first_visible - ui->selected - + MENU_ROWS ); - break; - case KEY_HOME: - move = -ui->count; - break; - case KEY_END: - move = +ui->count; - break; case ESC: case CTRL_C: rc = -ECANCELED; @@ -247,7 +224,7 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { i++; continue; } - ui->selected = i; + ui->scroll.current = i; if ( item->label ) { chosen = 1; } else { @@ -260,31 +237,22 @@ static int menu_loop ( struct menu_ui *ui, struct menu_item **selected ) { /* Move selection, if applicable */ while ( move ) { - ui->selected += move; - if ( ui->selected < 0 ) { - ui->selected = 0; - move = +1; - } else if ( ui->selected >= ( int ) ui->count ) { - ui->selected = ( ui->count - 1 ); - move = -1; - } - item = menu_item ( ui->menu, ui->selected ); + move = jump_scroll_move ( &ui->scroll, move ); + item = menu_item ( ui->menu, ui->scroll.current ); if ( item->label ) break; - move = ( ( move > 0 ) ? +1 : -1 ); } /* Redraw selection if necessary */ - if ( ( ui->selected != current ) || ( timeout != 0 ) ) { - draw_menu_item ( ui, current ); - delta = ( ui->selected - ui->first_visible ); - if ( delta >= MENU_ROWS ) + if ( ( ui->scroll.current != previous ) || ( timeout != 0 ) ) { + draw_menu_item ( ui, previous ); + if ( jump_scroll ( &ui->scroll ) ) draw_menu_items ( ui ); - draw_menu_item ( ui, ui->selected ); + draw_menu_item ( ui, ui->scroll.current ); } /* Record selection */ - item = menu_item ( ui->menu, ui->selected ); + item = menu_item ( ui->menu, ui->scroll.current ); assert ( item != NULL ); assert ( item->label != NULL ); *selected = item; @@ -313,21 +281,22 @@ int show_menu ( struct menu *menu, unsigned long timeout, /* Initialise UI */ memset ( &ui, 0, sizeof ( ui ) ); ui.menu = menu; + ui.scroll.rows = MENU_ROWS; ui.timeout = timeout; list_for_each_entry ( item, &menu->items, list ) { if ( item->label ) { if ( ! labelled_count ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; labelled_count++; if ( select ) { if ( strcmp ( select, item->label ) == 0 ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; } else { if ( item->is_default ) - ui.selected = ui.count; + ui.scroll.current = ui.scroll.count; } } - ui.count++; + ui.scroll.count++; } if ( ! labelled_count ) { /* Menus with no labelled items cannot be selected @@ -349,8 +318,9 @@ int show_menu ( struct menu *menu, unsigned long timeout, snprintf ( buf, sizeof ( buf ), "%s", ui.menu->title ); mvprintw ( TITLE_ROW, ( ( COLS - strlen ( buf ) ) / 2 ), "%s", buf ); attroff ( A_BOLD ); + jump_scroll ( &ui.scroll ); draw_menu_items ( &ui ); - draw_menu_item ( &ui, ui.selected ); + draw_menu_item ( &ui, ui.scroll.current ); /* Enter main loop */ rc = menu_loop ( &ui, selected ); diff --git a/roms/ipxe/src/hci/tui/settings_ui.c b/roms/ipxe/src/hci/tui/settings_ui.c index 221839730..be421cc0a 100644 --- a/roms/ipxe/src/hci/tui/settings_ui.c +++ b/roms/ipxe/src/hci/tui/settings_ui.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <stdarg.h> @@ -29,7 +33,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/editbox.h> #include <ipxe/keys.h> #include <ipxe/ansicol.h> +#include <ipxe/jumpscroll.h> #include <ipxe/settings_ui.h> +#include <config/branding.h> /** @file * @@ -47,7 +53,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define INSTRUCTION_ROW ( LINES - 2U ) #define INSTRUCTION_PAD " " -/** Layout of text within a setting widget */ +/** Layout of text within a setting row */ #define SETTING_ROW_TEXT( cols ) struct { \ char start[0]; \ char pad1[1]; \ @@ -63,8 +69,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); char nul; \ } __attribute__ (( packed )) -/** A setting row widget */ -struct setting_row_widget { +/** A settings user interface row */ +struct settings_ui_row { /** Target configuration settings block * * Valid only for rows that lead to new settings blocks. @@ -82,8 +88,6 @@ struct setting_row_widget { struct setting setting; /** Screen row */ unsigned int row; - /** Screen column */ - unsigned int col; /** Edit box widget used for editing setting */ struct edit_box editbox; /** Editing in progress flag */ @@ -92,28 +96,24 @@ struct setting_row_widget { char value[256]; /* enough size for a DHCP string */ }; -/** A settings widget */ -struct setting_widget { +/** A settings user interface */ +struct settings_ui { /** Settings block */ struct settings *settings; - /** Number of rows */ - unsigned int num_rows; - /** Current row index */ - unsigned int current; - /** Index of the first visible row, for scrolling. */ - unsigned int first_visible; - /** Active row */ - struct setting_row_widget row; + /** Jump scroller */ + struct jump_scroller scroll; + /** Current row */ + struct settings_ui_row row; }; /** - * Select a setting row + * Select a setting * - * @v widget Setting widget + * @v ui Settings user interface * @v index Index of setting row - * @ret count Number of settings rows + * @ret count Number of setting rows */ -static unsigned int select_setting_row ( struct setting_widget *widget, +static unsigned int select_setting_row ( struct settings_ui *ui, unsigned int index ) { SETTING_ROW_TEXT ( COLS ) *text; struct settings *settings; @@ -122,25 +122,22 @@ static unsigned int select_setting_row ( struct setting_widget *widget, unsigned int count = 0; /* Initialise structure */ - memset ( &widget->row, 0, sizeof ( widget->row ) ); - widget->current = index; - widget->row.row = ( SETTINGS_LIST_ROW + index - widget->first_visible ); - widget->row.col = SETTINGS_LIST_COL; + memset ( &ui->row, 0, sizeof ( ui->row ) ); + ui->row.row = ( SETTINGS_LIST_ROW + index - ui->scroll.first ); /* Include parent settings block, if applicable */ - if ( widget->settings->parent && ( count++ == index ) ) { - widget->row.settings = widget->settings->parent; - snprintf ( widget->row.value, sizeof ( widget->row.value ), + if ( ui->settings->parent && ( count++ == index ) ) { + ui->row.settings = ui->settings->parent; + snprintf ( ui->row.value, sizeof ( ui->row.value ), "../" ); } /* Include any child settings blocks, if applicable */ - list_for_each_entry ( settings, &widget->settings->children, siblings ){ + list_for_each_entry ( settings, &ui->settings->children, siblings ) { if ( count++ == index ) { - widget->row.settings = settings; - snprintf ( widget->row.value, - sizeof ( widget->row.value ), "%s/", - settings->name ); + ui->row.settings = settings; + snprintf ( ui->row.value, sizeof ( ui->row.value ), + "%s/", settings->name ); } } @@ -148,7 +145,7 @@ static unsigned int select_setting_row ( struct setting_widget *widget, for_each_table_entry ( setting, SETTINGS ) { /* Skip inapplicable settings */ - if ( ! setting_applies ( widget->settings, setting ) ) + if ( ! setting_applies ( ui->settings, setting ) ) continue; /* Skip duplicate settings */ @@ -158,18 +155,16 @@ static unsigned int select_setting_row ( struct setting_widget *widget, /* Read current setting value and origin */ if ( count++ == index ) { - fetchf_setting ( widget->settings, setting, - &widget->row.origin, - &widget->row.setting, - widget->row.value, - sizeof ( widget->row.value ) ); + fetchf_setting ( ui->settings, setting, &ui->row.origin, + &ui->row.setting, ui->row.value, + sizeof ( ui->row.value ) ); } } /* Initialise edit box */ - init_editbox ( &widget->row.editbox, widget->row.value, - sizeof ( widget->row.value ), NULL, widget->row.row, - ( widget->row.col + + init_editbox ( &ui->row.editbox, ui->row.value, + sizeof ( ui->row.value ), NULL, ui->row.row, + ( SETTINGS_LIST_COL + offsetof ( typeof ( *text ), u.setting.value ) ), sizeof ( text->u.setting.value ), 0 ); @@ -197,9 +192,9 @@ static size_t string_copy ( char *dest, const char *src, size_t len ) { /** * Draw setting row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_setting_row ( struct setting_widget *widget ) { +static void draw_setting_row ( struct settings_ui *ui ) { SETTING_ROW_TEXT ( COLS ) text; unsigned int curs_offset; char *value; @@ -209,12 +204,12 @@ static void draw_setting_row ( struct setting_widget *widget ) { text.nul = '\0'; /* Construct row content */ - if ( widget->row.settings ) { + if ( ui->row.settings ) { /* Construct space-padded name */ curs_offset = ( offsetof ( typeof ( text ), u.settings ) + string_copy ( text.u.settings, - widget->row.value, + ui->row.value, sizeof ( text.u.settings ) ) ); } else { @@ -222,11 +217,11 @@ static void draw_setting_row ( struct setting_widget *widget ) { /* Construct dot-padded name */ memset ( text.u.setting.name, '.', sizeof ( text.u.setting.name ) ); - string_copy ( text.u.setting.name, widget->row.setting.name, + string_copy ( text.u.setting.name, ui->row.setting.name, sizeof ( text.u.setting.name ) ); /* Construct space-padded value */ - value = widget->row.value; + value = ui->row.value; if ( ! *value ) value = "<not specified>"; curs_offset = ( offsetof ( typeof ( text ), u.setting.value ) + @@ -235,37 +230,34 @@ static void draw_setting_row ( struct setting_widget *widget ) { } /* Print row */ - if ( ( widget->row.origin == widget->settings ) || - ( widget->row.settings != NULL ) ) { + if ( ( ui->row.origin == ui->settings ) || ( ui->row.settings != NULL )) attron ( A_BOLD ); - } - mvprintw ( widget->row.row, widget->row.col, "%s", text.start ); + mvprintw ( ui->row.row, SETTINGS_LIST_COL, "%s", text.start ); attroff ( A_BOLD ); - move ( widget->row.row, widget->row.col + curs_offset ); + move ( ui->row.row, ( SETTINGS_LIST_COL + curs_offset ) ); } /** - * Edit setting widget + * Edit setting ui * - * @v widget Setting widget + * @v ui Settings UI * @v key Key pressed by user * @ret key Key returned to application, or zero */ -static int edit_setting ( struct setting_widget *widget, int key ) { - assert ( widget->row.setting.name != NULL ); - widget->row.editing = 1; - return edit_editbox ( &widget->row.editbox, key ); +static int edit_setting ( struct settings_ui *ui, int key ) { + assert ( ui->row.setting.name != NULL ); + ui->row.editing = 1; + return edit_editbox ( &ui->row.editbox, key ); } /** - * Save setting widget value back to configuration settings + * Save setting ui value back to configuration settings * - * @v widget Setting widget + * @v ui Settings UI */ -static int save_setting ( struct setting_widget *widget ) { - assert ( widget->row.setting.name != NULL ); - return storef_setting ( widget->settings, &widget->row.setting, - widget->row.value ); +static int save_setting ( struct settings_ui *ui ) { + assert ( ui->row.setting.name != NULL ); + return storef_setting ( ui->settings, &ui->row.setting, ui->row.value ); } /** @@ -340,15 +332,15 @@ static void alert ( const char *fmt, ... ) { /** * Draw title row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_title_row ( struct setting_widget *widget ) { +static void draw_title_row ( struct settings_ui *ui ) { const char *name; clearmsg ( TITLE_ROW ); - name = settings_name ( widget->settings ); + name = settings_name ( ui->settings ); attron ( A_BOLD ); - msg ( TITLE_ROW, "iPXE configuration settings%s%s", + msg ( TITLE_ROW, PRODUCT_SHORT_NAME " configuration settings%s%s", ( name[0] ? " - " : "" ), name ); attroff ( A_BOLD ); } @@ -356,89 +348,73 @@ static void draw_title_row ( struct setting_widget *widget ) { /** * Draw information row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_info_row ( struct setting_widget *widget ) { +static void draw_info_row ( struct settings_ui *ui ) { char buf[32]; /* Draw nothing unless this row represents a setting */ clearmsg ( INFO_ROW ); clearmsg ( INFO_ROW + 1 ); - if ( ! widget->row.setting.name ) + if ( ! ui->row.setting.name ) return; /* Determine a suitable setting name */ - setting_name ( ( widget->row.origin ? - widget->row.origin : widget->settings ), - &widget->row.setting, buf, sizeof ( buf ) ); + setting_name ( ( ui->row.origin ? + ui->row.origin : ui->settings ), + &ui->row.setting, buf, sizeof ( buf ) ); /* Draw row */ attron ( A_BOLD ); - msg ( INFO_ROW, "%s - %s", buf, widget->row.setting.description ); + msg ( INFO_ROW, "%s - %s", buf, ui->row.setting.description ); attroff ( A_BOLD ); color_set ( CPAIR_URL, NULL ); - msg ( ( INFO_ROW + 1 ), "http://ipxe.org/cfg/%s", - widget->row.setting.name ); + msg ( ( INFO_ROW + 1 ), PRODUCT_SETTING_URI, ui->row.setting.name ); color_set ( CPAIR_NORMAL, NULL ); } /** * Draw instruction row * - * @v widget Setting widget + * @v ui Settings UI */ -static void draw_instruction_row ( struct setting_widget *widget ) { +static void draw_instruction_row ( struct settings_ui *ui ) { clearmsg ( INSTRUCTION_ROW ); - if ( widget->row.editing ) { + if ( ui->row.editing ) { msg ( INSTRUCTION_ROW, "Enter - accept changes" INSTRUCTION_PAD "Ctrl-C - discard changes" ); } else { msg ( INSTRUCTION_ROW, "%sCtrl-X - exit configuration utility", - ( ( widget->row.origin == widget->settings ) ? + ( ( ui->row.origin == ui->settings ) ? "Ctrl-D - delete setting" INSTRUCTION_PAD : "" ) ); } } /** - * Reveal setting row + * Draw the current block of setting rows * - * @v widget Setting widget - * @v index Index of setting row + * @v ui Settings UI */ -static void reveal_setting_row ( struct setting_widget *widget, - unsigned int index ) { +static void draw_setting_rows ( struct settings_ui *ui ) { unsigned int i; - /* Simply return if setting N is already on-screen. */ - if ( index - widget->first_visible < SETTINGS_LIST_ROWS ) - return; - - /* Jump scroll to make the specified setting row visible. */ - while ( widget->first_visible < index ) - widget->first_visible += SETTINGS_LIST_ROWS; - while ( widget->first_visible > index ) - widget->first_visible -= SETTINGS_LIST_ROWS; - - /* Draw ellipses before and/or after the settings list to - * represent any invisible settings. - */ - mvaddstr ( SETTINGS_LIST_ROW - 1, - SETTINGS_LIST_COL + 1, - widget->first_visible > 0 ? "..." : " " ); - mvaddstr ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS, - SETTINGS_LIST_COL + 1, - ( ( widget->first_visible + SETTINGS_LIST_ROWS ) - < widget->num_rows ? "..." : " " ) ); + /* Draw ellipses before and/or after the list as necessary */ + color_set ( CPAIR_SEPARATOR, NULL ); + mvaddstr ( ( SETTINGS_LIST_ROW - 1 ), ( SETTINGS_LIST_COL + 1 ), + jump_scroll_is_first ( &ui->scroll ) ? " " : "..." ); + mvaddstr ( ( SETTINGS_LIST_ROW + SETTINGS_LIST_ROWS ), + ( SETTINGS_LIST_COL + 1 ), + jump_scroll_is_last ( &ui->scroll ) ? " " : "..." ); + color_set ( CPAIR_NORMAL, NULL ); /* Draw visible settings. */ - for ( i = 0; i < SETTINGS_LIST_ROWS; i++ ) { - if ( ( widget->first_visible + i ) < widget->num_rows ) { - select_setting_row ( widget, - widget->first_visible + i ); - draw_setting_row ( widget ); + for ( i = 0 ; i < SETTINGS_LIST_ROWS ; i++ ) { + if ( ( ui->scroll.first + i ) < ui->scroll.count ) { + select_setting_row ( ui, ( ui->scroll.first + i ) ); + draw_setting_row ( ui ); } else { clearmsg ( SETTINGS_LIST_ROW + i ); } @@ -446,69 +422,72 @@ static void reveal_setting_row ( struct setting_widget *widget, } /** - * Reveal setting row + * Select settings block * - * @v widget Setting widget + * @v ui Settings UI * @v settings Settings block */ -static void init_widget ( struct setting_widget *widget, - struct settings *settings ) { - - widget->settings = settings_target ( settings ); - widget->num_rows = select_setting_row ( widget, 0 ); - widget->first_visible = SETTINGS_LIST_ROWS; - draw_title_row ( widget ); - reveal_setting_row ( widget, 0 ); - select_setting_row ( widget, 0 ); +static void select_settings ( struct settings_ui *ui, + struct settings *settings ) { + + ui->settings = settings_target ( settings ); + ui->scroll.count = select_setting_row ( ui, 0 ); + ui->scroll.rows = SETTINGS_LIST_ROWS; + ui->scroll.current = 0; + ui->scroll.first = 0; + draw_title_row ( ui ); + draw_setting_rows ( ui ); + select_setting_row ( ui, 0 ); } static int main_loop ( struct settings *settings ) { - struct setting_widget widget; + struct settings_ui ui; + unsigned int previous; int redraw = 1; int move; - unsigned int next; int key; int rc; /* Print initial screen content */ color_set ( CPAIR_NORMAL, NULL ); - memset ( &widget, 0, sizeof ( widget ) ); - init_widget ( &widget, settings ); + memset ( &ui, 0, sizeof ( ui ) ); + select_settings ( &ui, settings ); while ( 1 ) { /* Redraw rows if necessary */ if ( redraw ) { - draw_info_row ( &widget ); - draw_instruction_row ( &widget ); - color_set ( ( widget.row.editing ? + draw_info_row ( &ui ); + draw_instruction_row ( &ui ); + color_set ( ( ui.row.editing ? CPAIR_EDIT : CPAIR_SELECT ), NULL ); - draw_setting_row ( &widget ); + draw_setting_row ( &ui ); color_set ( CPAIR_NORMAL, NULL ); - curs_set ( widget.row.editing ); + curs_set ( ui.row.editing ); redraw = 0; } - if ( widget.row.editing ) { + /* Edit setting, if we are currently editing */ + if ( ui.row.editing ) { /* Sanity check */ - assert ( widget.row.setting.name != NULL ); + assert ( ui.row.setting.name != NULL ); /* Redraw edit box */ color_set ( CPAIR_EDIT, NULL ); - draw_editbox ( &widget.row.editbox ); + draw_editbox ( &ui.row.editbox ); color_set ( CPAIR_NORMAL, NULL ); /* Process keypress */ - key = edit_setting ( &widget, getkey ( 0 ) ); + key = edit_setting ( &ui, getkey ( 0 ) ); switch ( key ) { case CR: case LF: - if ( ( rc = save_setting ( &widget ) ) != 0 ) + if ( ( rc = save_setting ( &ui ) ) != 0 ) alert ( " %s ", strerror ( rc ) ); /* Fall through */ case CTRL_C: - select_setting_row ( &widget, widget.current ); + select_setting_row ( &ui, ui.scroll.current ); redraw = 1; break; default: @@ -516,72 +495,52 @@ static int main_loop ( struct settings *settings ) { break; } - } else { + continue; + } - /* Process keypress */ - key = getkey ( 0 ); - move = 0; - switch ( key ) { - case KEY_UP: - move = -1; - break; - case KEY_DOWN: - move = +1; - break; - case KEY_PPAGE: - move = ( widget.first_visible - - widget.current - 1 ); - break; - case KEY_NPAGE: - move = ( widget.first_visible - widget.current - + SETTINGS_LIST_ROWS ); - break; - case KEY_HOME: - move = -widget.num_rows; - break; - case KEY_END: - move = +widget.num_rows; - break; - case CTRL_D: - if ( ! widget.row.setting.name ) - break; - if ( ( rc = delete_setting ( widget.settings, - &widget.row.setting ) ) != 0 ) { - alert ( " %s ", strerror ( rc ) ); - } - select_setting_row ( &widget, widget.current ); + /* Otherwise, navigate through settings */ + key = getkey ( 0 ); + move = jump_scroll_key ( &ui.scroll, key ); + if ( move ) { + previous = ui.scroll.current; + jump_scroll_move ( &ui.scroll, move ); + if ( ui.scroll.current != previous ) { + draw_setting_row ( &ui ); redraw = 1; + if ( jump_scroll ( &ui.scroll ) ) + draw_setting_rows ( &ui ); + select_setting_row ( &ui, ui.scroll.current ); + } + continue; + } + + /* Handle non-navigation keys */ + switch ( key ) { + case CTRL_D: + if ( ! ui.row.setting.name ) break; - case CTRL_X: - return 0; - case CR: - case LF: - if ( widget.row.settings ) { - init_widget ( &widget, - widget.row.settings ); - redraw = 1; - } - /* Fall through */ - default: - if ( widget.row.setting.name ) { - edit_setting ( &widget, key ); - redraw = 1; - } - break; + if ( ( rc = delete_setting ( ui.settings, + &ui.row.setting ) ) != 0 ){ + alert ( " %s ", strerror ( rc ) ); + } + select_setting_row ( &ui, ui.scroll.current ); + redraw = 1; + break; + case CTRL_X: + return 0; + case CR: + case LF: + if ( ui.row.settings ) { + select_settings ( &ui, ui.row.settings ); + redraw = 1; } - if ( move ) { - next = ( widget.current + move ); - if ( ( int ) next < 0 ) - next = 0; - if ( next >= widget.num_rows ) - next = ( widget.num_rows - 1 ); - if ( next != widget.current ) { - draw_setting_row ( &widget ); - redraw = 1; - reveal_setting_row ( &widget, next ); - select_setting_row ( &widget, next ); - } + /* Fall through */ + default: + if ( ui.row.setting.name ) { + edit_setting ( &ui, key ); + redraw = 1; } + break; } } } diff --git a/roms/ipxe/src/image/elf.c b/roms/ipxe/src/image/elf.c index 51636a8e9..5c2f9db25 100644 --- a/roms/ipxe/src/image/elf.c +++ b/roms/ipxe/src/image/elf.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -36,27 +40,54 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/image.h> #include <ipxe/elf.h> -typedef Elf32_Ehdr Elf_Ehdr; -typedef Elf32_Phdr Elf_Phdr; -typedef Elf32_Off Elf_Off; -#define ELFCLASS ELFCLASS32 - /** * Load ELF segment into memory * * @v image ELF file * @v phdr ELF program header + * @v dest Destination address + * @ret rc Return status code + */ +static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ) { + userptr_t buffer = phys_to_user ( dest ); + int rc; + + DBGC ( image, "ELF %p loading segment [%x,%x) to [%lx,%lx,%lx)\n", + image, phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), + dest, ( dest + phdr->p_filesz ), ( dest + phdr->p_memsz ) ); + + /* Verify and prepare segment */ + if ( ( rc = prep_segment ( buffer, phdr->p_filesz, + phdr->p_memsz ) ) != 0 ) { + DBGC ( image, "ELF %p could not prepare segment: %s\n", + image, strerror ( rc ) ); + return rc; + } + + /* Copy image to segment */ + memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); + + return 0; +} + +/** + * Process ELF segment + * + * @v image ELF file * @v ehdr ELF executable header + * @v phdr ELF program header + * @v process Segment processor * @ret entry Entry point, if found * @ret max Maximum used address * @ret rc Return status code */ -static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, - Elf_Ehdr *ehdr, physaddr_t *entry, - physaddr_t *max ) { +static int elf_segment ( struct image *image, Elf_Ehdr *ehdr, Elf_Phdr *phdr, + int ( * process ) ( struct image *image, + Elf_Phdr *phdr, physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ) { physaddr_t dest; physaddr_t end; - userptr_t buffer; unsigned long e_offset; int rc; @@ -82,28 +113,15 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, image ); return -ENOEXEC; } - buffer = phys_to_user ( dest ); end = ( dest + phdr->p_memsz ); - DBGC ( image, "ELF %p loading segment [%x,%x) to [%x,%x,%x)\n", image, - phdr->p_offset, ( phdr->p_offset + phdr->p_filesz ), - phdr->p_paddr, ( phdr->p_paddr + phdr->p_filesz ), - ( phdr->p_paddr + phdr->p_memsz ) ); - - /* Verify and prepare segment */ - if ( ( rc = prep_segment ( buffer, phdr->p_filesz, - phdr->p_memsz ) ) != 0 ) { - DBGC ( image, "ELF %p could not prepare segment: %s\n", - image, strerror ( rc ) ); - return rc; - } - /* Update maximum used address, if applicable */ if ( end > *max ) *max = end; - /* Copy image to segment */ - memcpy_user ( buffer, 0, image->data, phdr->p_offset, phdr->p_filesz ); + /* Process segment */ + if ( ( rc = process ( image, phdr, dest ) ) != 0 ) + return rc; /* Set execution address, if it lies within this segment */ if ( ( e_offset = ( ehdr->e_entry - dest ) ) < phdr->p_filesz ) { @@ -124,62 +142,85 @@ static int elf_load_segment ( struct image *image, Elf_Phdr *phdr, } /** - * Load ELF image into memory + * Process ELF segments * * @v image ELF file - * @ret entry Entry point + * @v ehdr ELF executable header + * @v process Segment processor + * @ret entry Entry point, if found * @ret max Maximum used address * @ret rc Return status code */ -int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { - static const uint8_t e_ident[] = { - [EI_MAG0] = ELFMAG0, - [EI_MAG1] = ELFMAG1, - [EI_MAG2] = ELFMAG2, - [EI_MAG3] = ELFMAG3, - [EI_CLASS] = ELFCLASS, - }; - Elf_Ehdr ehdr; +int elf_segments ( struct image *image, Elf_Ehdr *ehdr, + int ( * process ) ( struct image *image, Elf_Phdr *phdr, + physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ) { Elf_Phdr phdr; Elf_Off phoff; unsigned int phnum; int rc; - /* Read ELF header */ - copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); - if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident, - sizeof ( e_ident ) ) != 0 ) { - DBGC ( image, "ELF %p has invalid signature\n", image ); - return -ENOEXEC; - } - /* Initialise maximum used address */ *max = 0; /* Invalidate entry point */ *entry = 0; - /* Read ELF program headers */ - for ( phoff = ehdr.e_phoff , phnum = ehdr.e_phnum ; phnum ; - phoff += ehdr.e_phentsize, phnum-- ) { + /* Read and process ELF program headers */ + for ( phoff = ehdr->e_phoff , phnum = ehdr->e_phnum ; phnum ; + phoff += ehdr->e_phentsize, phnum-- ) { if ( phoff > image->len ) { DBGC ( image, "ELF %p program header %d outside " "image\n", image, phnum ); return -ENOEXEC; } copy_from_user ( &phdr, image->data, phoff, sizeof ( phdr ) ); - if ( ( rc = elf_load_segment ( image, &phdr, &ehdr, - entry, max ) ) != 0 ) { + if ( ( rc = elf_segment ( image, ehdr, &phdr, process, + entry, max ) ) != 0 ) return rc; - } } /* Check for a valid execution address */ if ( ! *entry ) { DBGC ( image, "ELF %p entry point %lx outside image\n", - image, ( ( unsigned long ) ehdr.e_entry ) ); + image, ( ( unsigned long ) ehdr->e_entry ) ); + return -ENOEXEC; + } + + return 0; +} + +/** + * Load ELF image into memory + * + * @v image ELF file + * @ret entry Entry point + * @ret max Maximum used address + * @ret rc Return status code + */ +int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ) { + static const uint8_t e_ident[] = { + [EI_MAG0] = ELFMAG0, + [EI_MAG1] = ELFMAG1, + [EI_MAG2] = ELFMAG2, + [EI_MAG3] = ELFMAG3, + [EI_CLASS] = ELFCLASS, + }; + Elf_Ehdr ehdr; + int rc; + + /* Read ELF header */ + copy_from_user ( &ehdr, image->data, 0, sizeof ( ehdr ) ); + if ( memcmp ( &ehdr.e_ident[EI_MAG0], e_ident, + sizeof ( e_ident ) ) != 0 ) { + DBGC ( image, "ELF %p has invalid signature\n", image ); return -ENOEXEC; } + /* Load ELF segments into memory */ + if ( ( rc = elf_segments ( image, &ehdr, elf_load_segment, + entry, max ) ) != 0 ) + return rc; + return 0; } diff --git a/roms/ipxe/src/image/embedded.c b/roms/ipxe/src/image/embedded.c index 6358378fb..48dd86851 100644 --- a/roms/ipxe/src/image/embedded.c +++ b/roms/ipxe/src/image/embedded.c @@ -6,7 +6,7 @@ * fetching over the network. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <ipxe/image.h> diff --git a/roms/ipxe/src/image/png.c b/roms/ipxe/src/image/png.c index c14608553..5c4bcb3a0 100644 --- a/roms/ipxe/src/image/png.c +++ b/roms/ipxe/src/image/png.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/image/pnm.c b/roms/ipxe/src/image/pnm.c index af9e571a2..f24b28841 100644 --- a/roms/ipxe/src/image/pnm.c +++ b/roms/ipxe/src/image/pnm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/image/script.c b/roms/ipxe/src/image/script.c index 5328da8b4..28050868a 100644 --- a/roms/ipxe/src/image/script.c +++ b/roms/ipxe/src/image/script.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/image/segment.c b/roms/ipxe/src/image/segment.c index 86fe42662..2d0f2f0fc 100644 --- a/roms/ipxe/src/image/segment.c +++ b/roms/ipxe/src/image/segment.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/include/.gitignore b/roms/ipxe/src/include/.gitignore deleted file mode 100644 index de1598ef3..000000000 --- a/roms/ipxe/src/include/.gitignore +++ /dev/null @@ -1 +0,0 @@ -.buildserial.h diff --git a/roms/ipxe/src/include/assert.h b/roms/ipxe/src/include/assert.h index a33f6017c..07f3ecb8c 100644 --- a/roms/ipxe/src/include/assert.h +++ b/roms/ipxe/src/include/assert.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NDEBUG #define ASSERTING 0 diff --git a/roms/ipxe/src/include/big_bswap.h b/roms/ipxe/src/include/big_bswap.h deleted file mode 100644 index 6c375a573..000000000 --- a/roms/ipxe/src/include/big_bswap.h +++ /dev/null @@ -1,35 +0,0 @@ -#ifndef ETHERBOOT_BIG_BSWAP_H -#define ETHERBOOT_BIG_BSWAP_H - -#define htonll(x) (x) -#define ntohll(x) (x) -#define ntohl(x) (x) -#define htonl(x) (x) -#define ntohs(x) (x) -#define htons(x) (x) -#define cpu_to_le64(x) __bswap_64(x) -#define cpu_to_le32(x) __bswap_32(x) -#define cpu_to_le16(x) __bswap_16(x) -#define cpu_to_be64(x) (x) -#define cpu_to_be32(x) (x) -#define cpu_to_be16(x) (x) -#define le64_to_cpu(x) __bswap_64(x) -#define le32_to_cpu(x) __bswap_32(x) -#define le16_to_cpu(x) __bswap_16(x) -#define be64_to_cpu(x) (x) -#define be32_to_cpu(x) (x) -#define be16_to_cpu(x) (x) -#define cpu_to_le64s(x) __bswap_64s(x) -#define cpu_to_le32s(x) __bswap_32s(x) -#define cpu_to_le16s(x) __bswap_16s(x) -#define cpu_to_be64s(x) do {} while (0) -#define cpu_to_be32s(x) do {} while (0) -#define cpu_to_be16s(x) do {} while (0) -#define le64_to_cpus(x) __bswap_64s(x) -#define le32_to_cpus(x) __bswap_32s(x) -#define le16_to_cpus(x) __bswap_16s(x) -#define be64_to_cpus(x) do {} while (0) -#define be32_to_cpus(x) do {} while (0) -#define be16_to_cpus(x) do {} while (0) - -#endif /* ETHERBOOT_BIG_BSWAP_H */ diff --git a/roms/ipxe/src/include/byteswap.h b/roms/ipxe/src/include/byteswap.h index 466759cf8..d1028c579 100644 --- a/roms/ipxe/src/include/byteswap.h +++ b/roms/ipxe/src/include/byteswap.h @@ -1,59 +1,138 @@ -#ifndef ETHERBOOT_BYTESWAP_H -#define ETHERBOOT_BYTESWAP_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include "endian.h" -#include "bits/byteswap.h" - -#define __bswap_constant_16(x) \ - ((uint16_t)((((uint16_t)(x) & 0x00ff) << 8) | \ - (((uint16_t)(x) & 0xff00) >> 8))) - -#define __bswap_constant_32(x) \ - ((uint32_t)((((uint32_t)(x) & 0x000000ffU) << 24) | \ - (((uint32_t)(x) & 0x0000ff00U) << 8) | \ - (((uint32_t)(x) & 0x00ff0000U) >> 8) | \ - (((uint32_t)(x) & 0xff000000U) >> 24))) - -#define __bswap_constant_64(x) \ - ((uint64_t)((((uint64_t)(x) & 0x00000000000000ffULL) << 56) | \ - (((uint64_t)(x) & 0x000000000000ff00ULL) << 40) | \ - (((uint64_t)(x) & 0x0000000000ff0000ULL) << 24) | \ - (((uint64_t)(x) & 0x00000000ff000000ULL) << 8) | \ - (((uint64_t)(x) & 0x000000ff00000000ULL) >> 8) | \ - (((uint64_t)(x) & 0x0000ff0000000000ULL) >> 24) | \ - (((uint64_t)(x) & 0x00ff000000000000ULL) >> 40) | \ - (((uint64_t)(x) & 0xff00000000000000ULL) >> 56))) - -#define __bswap_16(x) \ - ((uint16_t)(__builtin_constant_p(x) ? \ - __bswap_constant_16(x) : \ - __bswap_variable_16(x))) - -#define __bswap_32(x) \ - ((uint32_t)(__builtin_constant_p(x) ? \ - __bswap_constant_32(x) : \ - __bswap_variable_32(x))) - -#define __bswap_64(x) \ - ((uint64_t)(__builtin_constant_p(x) ? \ - __bswap_constant_64(x) : \ - __bswap_variable_64(x))) +#ifndef BYTESWAP_H +#define BYTESWAP_H + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <endian.h> +#include <bits/byteswap.h> + +/** + * Byte-swap a 16-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_16( value ) \ + ( ( ( (value) & 0x00ff ) << 8 ) | \ + ( ( (value) & 0xff00 ) >> 8 ) ) + +/** + * Byte-swap a 32-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_32( value ) \ + ( ( ( (value) & 0x000000ffUL ) << 24 ) | \ + ( ( (value) & 0x0000ff00UL ) << 8 ) | \ + ( ( (value) & 0x00ff0000UL ) >> 8 ) | \ + ( ( (value) & 0xff000000UL ) >> 24 ) ) + +/** + * Byte-swap a 64-bit constant + * + * @v value Constant value + * @ret swapped Byte-swapped value + */ +#define __bswap_constant_64( value ) \ + ( ( ( (value) & 0x00000000000000ffULL ) << 56 ) | \ + ( ( (value) & 0x000000000000ff00ULL ) << 40 ) | \ + ( ( (value) & 0x0000000000ff0000ULL ) << 24 ) | \ + ( ( (value) & 0x00000000ff000000ULL ) << 8 ) | \ + ( ( (value) & 0x000000ff00000000ULL ) >> 8 ) | \ + ( ( (value) & 0x0000ff0000000000ULL ) >> 24 ) | \ + ( ( (value) & 0x00ff000000000000ULL ) >> 40 ) | \ + ( ( (value) & 0xff00000000000000ULL ) >> 56 ) ) + +/** + * Byte-swap a 16-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_16( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint16_t ) __bswap_constant_16 ( ( uint16_t ) (value) ) ) \ + : __bswap_variable_16 (value) ) +#define bswap_16( value ) __bswap_16 (value) + +/** + * Byte-swap a 32-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_32( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint32_t ) __bswap_constant_32 ( ( uint32_t ) (value) ) ) \ + : __bswap_variable_32 (value) ) +#define bswap_32( value ) __bswap_32 (value) + +/** + * Byte-swap a 64-bit value + * + * @v value Value + * @ret swapped Byte-swapped value + */ +#define __bswap_64( value ) \ + ( __builtin_constant_p (value) ? \ + ( ( uint64_t ) __bswap_constant_64 ( ( uint64_t ) (value) ) ) \ + : __bswap_variable_64 (value) ) +#define bswap_64( value ) __bswap_64 (value) #if __BYTE_ORDER == __LITTLE_ENDIAN -#include "little_bswap.h" +#define __cpu_to_leNN( bits, value ) (value) +#define __cpu_to_beNN( bits, value ) __bswap_ ## bits (value) +#define __leNN_to_cpu( bits, value ) (value) +#define __beNN_to_cpu( bits, value ) __bswap_ ## bits (value) +#define __cpu_to_leNNs( bits, ptr ) do { } while ( 0 ) +#define __cpu_to_beNNs( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __leNN_to_cpus( bits, ptr ) do { } while ( 0 ) +#define __beNN_to_cpus( bits, ptr ) __bswap_ ## bits ## s (ptr) #endif + #if __BYTE_ORDER == __BIG_ENDIAN -#include "big_bswap.h" +#define __cpu_to_leNN( bits, value ) __bswap_ ## bits (value) +#define __cpu_to_beNN( bits, value ) (value) +#define __leNN_to_cpu( bits, value ) __bswap_ ## bits (value) +#define __beNN_to_cpu( bits, value ) (value) +#define __cpu_to_leNNs( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __cpu_to_beNNs( bits, ptr ) do { } while ( 0 ) +#define __leNN_to_cpus( bits, ptr ) __bswap_ ## bits ## s (ptr) +#define __beNN_to_cpus( bits, ptr ) do { } while ( 0 ) #endif -/* Make routines available to all */ -#define swap64(x) __bswap_64(x) -#define swap32(x) __bswap_32(x) -#define swap16(x) __bswap_16(x) -#define bswap_64(x) __bswap_64(x) -#define bswap_32(x) __bswap_32(x) -#define bswap_16(x) __bswap_16(x) - -#endif /* ETHERBOOT_BYTESWAP_H */ +#define cpu_to_le16( value ) __cpu_to_leNN ( 16, value ) +#define cpu_to_le32( value ) __cpu_to_leNN ( 32, value ) +#define cpu_to_le64( value ) __cpu_to_leNN ( 64, value ) +#define cpu_to_be16( value ) __cpu_to_beNN ( 16, value ) +#define cpu_to_be32( value ) __cpu_to_beNN ( 32, value ) +#define cpu_to_be64( value ) __cpu_to_beNN ( 64, value ) +#define le16_to_cpu( value ) __leNN_to_cpu ( 16, value ) +#define le32_to_cpu( value ) __leNN_to_cpu ( 32, value ) +#define le64_to_cpu( value ) __leNN_to_cpu ( 64, value ) +#define be16_to_cpu( value ) __beNN_to_cpu ( 16, value ) +#define be32_to_cpu( value ) __beNN_to_cpu ( 32, value ) +#define be64_to_cpu( value ) __beNN_to_cpu ( 64, value ) +#define cpu_to_le16s( ptr ) __cpu_to_leNNs ( 16, ptr ) +#define cpu_to_le32s( ptr ) __cpu_to_leNNs ( 32, ptr ) +#define cpu_to_le64s( ptr ) __cpu_to_leNNs ( 64, ptr ) +#define cpu_to_be16s( ptr ) __cpu_to_beNNs ( 16, ptr ) +#define cpu_to_be32s( ptr ) __cpu_to_beNNs ( 32, ptr ) +#define cpu_to_be64s( ptr ) __cpu_to_beNNs ( 64, ptr ) +#define le16_to_cpus( ptr ) __leNN_to_cpus ( 16, ptr ) +#define le32_to_cpus( ptr ) __leNN_to_cpus ( 32, ptr ) +#define le64_to_cpus( ptr ) __leNN_to_cpus ( 64, ptr ) +#define be16_to_cpus( ptr ) __beNN_to_cpus ( 16, ptr ) +#define be32_to_cpus( ptr ) __beNN_to_cpus ( 32, ptr ) +#define be64_to_cpus( ptr ) __beNN_to_cpus ( 64, ptr ) + +#define htonll( value ) cpu_to_be64 (value) +#define ntohll( value ) be64_to_cpu (value) +#define htonl( value ) cpu_to_be32 (value) +#define ntohl( value ) be32_to_cpu (value) +#define htons( value ) cpu_to_be16 (value) +#define ntohs( value ) be16_to_cpu (value) + +#endif /* BYTESWAP_H */ diff --git a/roms/ipxe/src/include/compiler.h b/roms/ipxe/src/include/compiler.h index 3f5c913a0..ca82f9523 100644 --- a/roms/ipxe/src/include/compiler.h +++ b/roms/ipxe/src/include/compiler.h @@ -57,101 +57,100 @@ * @{ */ -/** Provide a symbol within this object file */ +/** + * Provide a symbol within this object file + * + * @v symbol Symbol name + */ #ifdef ASSEMBLY -#define PROVIDE_SYMBOL( _sym ) \ - .section ".provided", "a", @nobits ; \ - .hidden _sym ; \ - .globl _sym ; \ - _sym: ; \ +#define PROVIDE_SYMBOL( symbol ) \ + .section ".provided", "a", @nobits ; \ + .hidden symbol ; \ + .globl symbol ; \ + symbol: ; \ .previous -#else /* ASSEMBLY */ -#define PROVIDE_SYMBOL( _sym ) \ - char _sym[0] \ +#else +#define PROVIDE_SYMBOL( symbol ) \ + char symbol[0] \ __attribute__ (( section ( ".provided" ) )) -#endif /* ASSEMBLY */ +#endif -/** Require a symbol within this object file +/** + * Request a symbol + * + * @v symbol Symbol name * - * The symbol is referenced by a relocation in a discarded section, so - * if it is not available at link time the link will fail. + * Request a symbol to be included within the link. If the symbol + * cannot be found, the link will succeed anyway. */ #ifdef ASSEMBLY -#define REQUIRE_SYMBOL( _sym ) \ - .section ".discard", "a", @progbits ; \ - .extern _sym ; \ - .long _sym ; \ - .previous -#else /* ASSEMBLY */ -#define REQUIRE_SYMBOL( _sym ) \ - extern char _sym; \ - static char * _C2 ( _C2 ( __require_, _sym ), _C2 ( _, __LINE__ ) ) \ - __attribute__ (( section ( ".discard" ), used )) \ - = &_sym +#define REQUEST_SYMBOL( symbol ) \ + .equ __request_ ## symbol, symbol +#else +#define REQUEST_SYMBOL( symbol ) \ + __asm__ ( ".equ __request_" #symbol ", " #symbol ) #endif -/** Request that a symbol be available at runtime +/** + * Require a symbol + * + * @v symbol Symbol name * - * The requested symbol is entered as undefined into the symbol table - * for this object, so the linker will pull in other object files as - * necessary to satisfy the reference. However, the undefined symbol - * is not referenced in any relocations, so the link can still succeed - * if no file contains it. + * Require a symbol to be included within the link. If the symbol + * cannot be found, the link will fail. * - * A symbol passed to this macro may not be referenced anywhere - * else in the file. If you want to do that, see IMPORT_SYMBOL(). + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. */ #ifdef ASSEMBLY -#define REQUEST_SYMBOL( _sym ) \ - .equ __need_ ## _sym, _sym -#else /* ASSEMBLY */ -#define REQUEST_SYMBOL( _sym ) \ - __asm__ ( ".equ\t__need_" #_sym ", " #_sym ) -#endif /* ASSEMBLY */ +#define REQUIRE_SYMBOL( symbol ) \ + .reloc __requiring_symbol__, RELOC_TYPE_NONE, symbol +#else +#define REQUIRE_SYMBOL( symbol ) \ + __asm__ ( ".reloc __requiring_symbol__, " \ + _S2 ( RELOC_TYPE_NONE ) ", " #symbol ) +#endif -/** Set up a symbol to be usable in another file by IMPORT_SYMBOL() +/** + * Specify the file's requiring symbol + * + * @v symbol Symbol name * - * The symbol must already be marked as global. + * REQUIRE_SYMBOL() works by defining a dummy relocation record + * against a nominated "requiring symbol". The presence of the + * nominated requiring symbol will drag in all of the symbols + * specified using REQUIRE_SYMBOL(). */ -#define EXPORT_SYMBOL( _sym ) PROVIDE_SYMBOL ( __export_ ## _sym ) +#ifdef ASSEMBLY +#define REQUIRING_SYMBOL( symbol ) \ + .equ __requiring_symbol__, symbol +#else +#define REQUIRING_SYMBOL( symbol ) \ + __asm__ ( ".equ __requiring_symbol__, " #symbol ) +#endif -/** Make a symbol usable to this file if available at link time - * - * If no file passed to the linker contains the symbol, it will have - * @c NULL value to future uses. Keep in mind that the symbol value is - * really the @e address of a variable or function; see the code - * snippet below. - * - * In C using IMPORT_SYMBOL, you must specify the declaration as the - * second argument, for instance - * - * @code - * IMPORT_SYMBOL ( my_func, int my_func ( int arg ) ); - * IMPORT_SYMBOL ( my_var, int my_var ); - * - * void use_imports ( void ) { - * if ( my_func && &my_var ) - * my_var = my_func ( my_var ); - * } - * @endcode - * - * GCC considers a weak declaration to override a strong one no matter - * which comes first, so it is safe to include a header file declaring - * the imported symbol normally, but providing the declaration to - * IMPORT_SYMBOL is still required. +/** + * Provide a file's requiring symbol * - * If no EXPORT_SYMBOL declaration exists for the imported symbol in - * another file, the behavior will be most likely be identical to that - * for an unavailable symbol. + * If the file contains no symbols that can be used as the requiring + * symbol, you can provide a dummy one-byte-long symbol using + * PROVIDE_REQUIRING_SYMBOL(). */ #ifdef ASSEMBLY -#define IMPORT_SYMBOL( _sym ) \ - REQUEST_SYMBOL ( __export_ ## _sym ) ; \ - .weak _sym -#else /* ASSEMBLY */ -#define IMPORT_SYMBOL( _sym, _decl ) \ - REQUEST_SYMBOL ( __export_ ## _sym ) ; \ - extern _decl __attribute__ (( weak )) +#define PROVIDE_REQUIRING_SYMBOL() \ + .section ".tbl.requiring_symbols", "a", @progbits ; \ + __requiring_symbol__: .byte 0 ; \ + .size __requiring_symbol__, . - __requiring_symbol__ ; \ + .previous +#else +#define PROVIDE_REQUIRING_SYMBOL() \ + __asm__ ( ".section \".tbl.requiring_symbols\", " \ + " \"a\", @progbits\n" \ + "__requiring_symbol__:\t.byte 0\n" \ + ".size __requiring_symbol__, " \ + " . - __requiring_symbol__\n" \ + ".previous" ) #endif /** @} */ @@ -163,20 +162,33 @@ #define PREFIX_OBJECT( _prefix ) _C2 ( _prefix, OBJECT ) #define OBJECT_SYMBOL PREFIX_OBJECT ( obj_ ) -#define REQUEST_EXPANDED( _sym ) REQUEST_SYMBOL ( _sym ) -#define CONFIG_SYMBOL PREFIX_OBJECT ( obj_config_ ) /** Always provide the symbol for the current object (defined by -DOBJECT) */ PROVIDE_SYMBOL ( OBJECT_SYMBOL ); -/** Pull in an object-specific configuration file if available */ -REQUEST_EXPANDED ( CONFIG_SYMBOL ); - -/** Explicitly require another object */ -#define REQUIRE_OBJECT( _obj ) REQUIRE_SYMBOL ( obj_ ## _obj ) +/** + * Request an object + * + * @v object Object name + * + * Request an object to be included within the link. If the object + * cannot be found, the link will succeed anyway. + */ +#define REQUEST_OBJECT( object ) REQUEST_SYMBOL ( obj_ ## object ) -/** Pull in another object if it exists */ -#define REQUEST_OBJECT( _obj ) REQUEST_SYMBOL ( obj_ ## _obj ) +/** + * Require an object + * + * @v object Object name + * + * Require an object to be included within the link. If the object + * cannot be found, the link will fail. + * + * To use this macro within a file, you must also specify the file's + * "requiring symbol" using the REQUIRING_SYMBOL() or + * PROVIDE_REQUIRING_SYMBOL() macros. + */ +#define REQUIRE_OBJECT( object ) REQUIRE_SYMBOL ( obj_ ## object ) /** @} */ @@ -195,14 +207,6 @@ REQUEST_EXPANDED ( CONFIG_SYMBOL ); */ #define __weak __attribute__ (( weak, noinline )) -/** Prevent a function from being optimized away without inlining - * - * Calls to functions with void return type that contain no code in their body - * may be removed by gcc's optimizer even when inlining is inhibited. Placing - * this macro in the body of the function prevents that from occurring. - */ -#define __keepme asm(""); - #endif /** @defgroup dbg Debugging infrastructure @@ -730,13 +734,24 @@ int __debug_disable; #define FILE_LICENCE_MIT \ PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__mit__ ) ) +/** Declare a file as being under GPLv2+ or UBDL + * + * This licence declaration is applicable when a file states itself to + * be licensed under the GNU GPL; "either version 2 of the License, or + * (at your option) any later version" and also states that it may be + * distributed under the terms of the Unmodified Binary Distribution + * Licence (as given in the file COPYING.UBDL). + */ +#define FILE_LICENCE_GPL2_OR_LATER_OR_UBDL \ + PROVIDE_SYMBOL ( PREFIX_OBJECT ( __licence__gpl2_or_later_or_ubdl__ ) ) + /** Declare a particular licence as applying to a file */ #define FILE_LICENCE( _licence ) FILE_LICENCE_ ## _licence /** @} */ -/* This file itself is under GPLv2-or-later */ -FILE_LICENCE ( GPL2_OR_LATER ); +/* This file itself is under GPLv2+/UBDL */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <bits/compiler.h> diff --git a/roms/ipxe/src/include/ctype.h b/roms/ipxe/src/include/ctype.h index e92ecb1c0..0d79ecd19 100644 --- a/roms/ipxe/src/include/ctype.h +++ b/roms/ipxe/src/include/ctype.h @@ -4,30 +4,114 @@ /** @file * * Character types + * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#define isdigit(c) ((c) >= '0' && (c) <= '9') -#define islower(c) ((c) >= 'a' && (c) <= 'z') -#define isupper(c) ((c) >= 'A' && (c) <= 'Z') -#define isxdigit(c) (isdigit(c) || ((c) >= 'A' && (c) <= 'F') || ((c) >= 'a' && (c) <= 'f')) -#define isprint(c) ((c) >= ' ' && (c) <= '~' ) +/** + * Check if character is a decimal digit + * + * @v character ASCII character + * @ret is_digit Character is a decimal digit + */ +static inline int isdigit ( int character ) { -static inline unsigned char tolower(unsigned char c) -{ - if (isupper(c)) - c -= 'A'-'a'; - return c; + return ( ( character >= '0' ) && ( character <= '9' ) ); } -static inline unsigned char toupper(unsigned char c) -{ - if (islower(c)) - c -= 'a'-'A'; - return c; +/** + * Check if character is a hexadecimal digit + * + * @v character ASCII character + * @ret is_xdigit Character is a hexadecimal digit + */ +static inline int isxdigit ( int character ) { + + return ( ( ( character >= '0' ) && ( character <= '9' ) ) || + ( ( character >= 'A' ) && ( character <= 'F' ) ) || + ( ( character >= 'a' ) && ( character <= 'f' ) ) ); +} + +/** + * Check if character is an upper-case letter + * + * @v character ASCII character + * @ret is_upper Character is an upper-case letter + */ +static inline int isupper ( int character ) { + + return ( ( character >= 'A' ) && ( character <= 'Z' ) ); +} + +/** + * Check if character is a lower-case letter + * + * @v character ASCII character + * @ret is_lower Character is a lower-case letter + */ +static inline int islower ( int character ) { + + return ( ( character >= 'a' ) && ( character <= 'z' ) ); +} + +/** + * Check if character is alphabetic + * + * @v character ASCII character + * @ret is_alpha Character is alphabetic + */ +static inline int isalpha ( int character ) { + + return ( isupper ( character ) || islower ( character ) ); +} + +/** + * Check if character is alphanumeric + * + * @v character ASCII character + * @ret is_alnum Character is alphanumeric + */ +static inline int isalnum ( int character ) { + + return ( isalpha ( character ) || isdigit ( character ) ); +} + +/** + * Check if character is printable + * + * @v character ASCII character + * @ret is_print Character is printable + */ +static inline int isprint ( int character ) { + + return ( ( character >= ' ' ) && ( character <= '~' ) ); +} + +/** + * Convert character to lower case + * + * @v character Character + * @v character Lower-case character + */ +static inline int tolower ( int character ) { + + return ( isupper ( character ) ? + ( character - 'A' + 'a' ) : character ); +} + +/** + * Convert character to upper case + * + * @v character Character + * @v character Upper-case character + */ +static inline int toupper ( int character ) { + + return ( islower ( character ) ? + ( character - 'a' + 'A' ) : character ); } -extern int isspace ( int c ); +extern int isspace ( int character ); #endif /* _CTYPE_H */ diff --git a/roms/ipxe/src/include/curses.h b/roms/ipxe/src/include/curses.h index f16f9d7d0..04060fe27 100644 --- a/roms/ipxe/src/include/curses.h +++ b/roms/ipxe/src/include/curses.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #undef ERR #define ERR (-1) diff --git a/roms/ipxe/src/include/elf.h b/roms/ipxe/src/include/elf.h index 04022b687..18f755a21 100644 --- a/roms/ipxe/src/include/elf.h +++ b/roms/ipxe/src/include/elf.h @@ -1,234 +1,81 @@ #ifndef ELF_H #define ELF_H -FILE_LICENCE ( GPL2_OR_LATER ); - -#define EI_NIDENT 16 /* Size of e_ident array. */ - -/* Values for e_type. */ -#define ET_NONE 0 /* No file type */ -#define ET_REL 1 /* Relocatable file */ -#define ET_EXEC 2 /* Executable file */ -#define ET_DYN 3 /* Shared object file */ -#define ET_CORE 4 /* Core file */ - -/* Values for e_machine (architecute). */ -#define EM_NONE 0 /* No machine */ -#define EM_M32 1 /* AT&T WE 32100 */ -#define EM_SPARC 2 /* SUN SPARC */ -#define EM_386 3 /* Intel 80386+ */ -#define EM_68K 4 /* Motorola m68k family */ -#define EM_88K 5 /* Motorola m88k family */ -#define EM_486 6 /* Perhaps disused */ -#define EM_860 7 /* Intel 80860 */ -#define EM_MIPS 8 /* MIPS R3000 big-endian */ -#define EM_S370 9 /* IBM System/370 */ -#define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ - -#define EM_PARISC 15 /* HPPA */ -#define EM_VPP500 17 /* Fujitsu VPP500 */ -#define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ -#define EM_960 19 /* Intel 80960 */ -#define EM_PPC 20 /* PowerPC */ -#define EM_PPC64 21 /* PowerPC 64-bit */ -#define EM_S390 22 /* IBM S390 */ - -#define EM_V800 36 /* NEC V800 series */ -#define EM_FR20 37 /* Fujitsu FR20 */ -#define EM_RH32 38 /* TRW RH-32 */ -#define EM_RCE 39 /* Motorola RCE */ -#define EM_ARM 40 /* ARM */ -#define EM_FAKE_ALPHA 41 /* Digital Alpha */ -#define EM_SH 42 /* Hitachi SH */ -#define EM_SPARCV9 43 /* SPARC v9 64-bit */ -#define EM_TRICORE 44 /* Siemens Tricore */ -#define EM_ARC 45 /* Argonaut RISC Core */ -#define EM_H8_300 46 /* Hitachi H8/300 */ -#define EM_H8_300H 47 /* Hitachi H8/300H */ -#define EM_H8S 48 /* Hitachi H8S */ -#define EM_H8_500 49 /* Hitachi H8/500 */ -#define EM_IA_64 50 /* Intel Merced */ -#define EM_MIPS_X 51 /* Stanford MIPS-X */ -#define EM_COLDFIRE 52 /* Motorola Coldfire */ -#define EM_68HC12 53 /* Motorola M68HC12 */ -#define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ -#define EM_PCP 55 /* Siemens PCP */ -#define EM_NCPU 56 /* Sony nCPU embeeded RISC */ -#define EM_NDR1 57 /* Denso NDR1 microprocessor */ -#define EM_STARCORE 58 /* Motorola Start*Core processor */ -#define EM_ME16 59 /* Toyota ME16 processor */ -#define EM_ST100 60 /* STMicroelectronic ST100 processor */ -#define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ -#define EM_X86_64 62 /* AMD x86-64 architecture */ -#define EM_PDSP 63 /* Sony DSP Processor */ - -#define EM_FX66 66 /* Siemens FX66 microcontroller */ -#define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ -#define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ -#define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ -#define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ -#define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ -#define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ -#define EM_SVX 73 /* Silicon Graphics SVx */ -#define EM_AT19 74 /* STMicroelectronics ST19 8 bit mc */ -#define EM_VAX 75 /* Digital VAX */ -#define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ -#define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ -#define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ -#define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ -#define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ -#define EM_HUANY 81 /* Harvard University machine-independent object files */ -#define EM_PRISM 82 /* SiTera Prism */ -#define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ -#define EM_FR30 84 /* Fujitsu FR30 */ -#define EM_D10V 85 /* Mitsubishi D10V */ -#define EM_D30V 86 /* Mitsubishi D30V */ -#define EM_V850 87 /* NEC v850 */ -#define EM_M32R 88 /* Mitsubishi M32R */ -#define EM_MN10300 89 /* Matsushita MN10300 */ -#define EM_MN10200 90 /* Matsushita MN10200 */ -#define EM_PJ 91 /* picoJava */ -#define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ -#define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ -#define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ -#define EM_NUM 95 - -/* Values for p_type. */ -#define PT_NULL 0 /* Unused entry. */ -#define PT_LOAD 1 /* Loadable segment. */ -#define PT_DYNAMIC 2 /* Dynamic linking information segment. */ -#define PT_INTERP 3 /* Pathname of interpreter. */ -#define PT_NOTE 4 /* Auxiliary information. */ -#define PT_SHLIB 5 /* Reserved (not used). */ -#define PT_PHDR 6 /* Location of program header itself. */ - -/* Values for p_flags. */ -#define PF_X 0x1 /* Executable. */ -#define PF_W 0x2 /* Writable. */ -#define PF_R 0x4 /* Readable. */ - - -#define ELF_PROGRAM_RETURNS_BIT 0x8000000 /* e_flags bit 31 */ - -#define EI_MAG0 0 -#define ELFMAG0 0x7f - -#define EI_MAG1 1 -#define ELFMAG1 'E' - -#define EI_MAG2 2 -#define ELFMAG2 'L' - -#define EI_MAG3 3 -#define ELFMAG3 'F' - -#define ELFMAG "\177ELF" -#define SELFMAG 4 - -#define EI_CLASS 4 /* File class byte index */ -#define ELFCLASSNONE 0 /* Invalid class */ -#define ELFCLASS32 1 /* 32-bit objects */ -#define ELFCLASS64 2 /* 64-bit objects */ +/** + * @file + * + * ELF headers + * + */ -#define EI_DATA 5 /* Data encodeing byte index */ -#define ELFDATANONE 0 /* Invalid data encoding */ -#define ELFDATA2LSB 1 /* 2's complement little endian */ -#define ELFDATA2MSB 2 /* 2's complement big endian */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#define EI_VERSION 6 /* File version byte index */ - /* Value must be EV_CURRENT */ +#include <stdint.h> -#define EV_NONE 0 /* Invalid ELF Version */ -#define EV_CURRENT 1 /* Current version */ +typedef uint32_t Elf32_Addr; +typedef uint16_t Elf32_Half; +typedef uint32_t Elf32_Off; +typedef int32_t Elf32_Sword; +typedef uint32_t Elf32_Word; -#define ELF32_PHDR_SIZE (8*4) /* Size of an elf program header */ +/** Length of ELF identifier */ +#define EI_NIDENT 16 -#ifndef ASSEMBLY +/** ELF header */ +typedef struct { + unsigned char e_ident[EI_NIDENT]; + Elf32_Half e_type; + Elf32_Half e_machine; + Elf32_Word e_version; + Elf32_Addr e_entry; + Elf32_Off e_phoff; + Elf32_Off e_shoff; + Elf32_Word e_flags; + Elf32_Half e_ehsize; + Elf32_Half e_phentsize; + Elf32_Half e_phnum; + Elf32_Half e_shentsize; + Elf32_Half e_shnum; + Elf32_Half e_shstrndx; +} Elf32_Ehdr; -#include <stdint.h> +/* ELF identifier indexes */ +#define EI_MAG0 0 +#define EI_MAG1 1 +#define EI_MAG2 2 +#define EI_MAG3 3 +#define EI_CLASS 4 +#define EI_DATA 5 +#define EI_VERSION 6 -/* - * ELF definitions common to all 32-bit architectures. - */ +/* ELF magic signature bytes */ +#define ELFMAG0 0x7f +#define ELFMAG1 'E' +#define ELFMAG2 'L' +#define ELFMAG3 'F' -typedef uint32_t Elf32_Addr; -typedef uint16_t Elf32_Half; -typedef uint32_t Elf32_Off; -typedef int32_t Elf32_Sword; -typedef uint32_t Elf32_Word; -typedef uint32_t Elf32_Size; +/* ELF classes */ +#define ELFCLASS32 1 -typedef uint64_t Elf64_Addr; -typedef uint16_t Elf64_Half; -typedef uint64_t Elf64_Off; -typedef int32_t Elf64_Sword; -typedef uint32_t Elf64_Word; -typedef uint64_t Elf64_Size; +/* ELF data encodings */ +#define ELFDATA2LSB 1 -/* - * ELF header. - */ -typedef struct { - unsigned char e_ident[EI_NIDENT]; /* File identification. */ - Elf32_Half e_type; /* File type. */ - Elf32_Half e_machine; /* Machine architecture. */ - Elf32_Word e_version; /* ELF format version. */ - Elf32_Addr e_entry; /* Entry point. */ - Elf32_Off e_phoff; /* Program header file offset. */ - Elf32_Off e_shoff; /* Section header file offset. */ - Elf32_Word e_flags; /* Architecture-specific flags. */ - Elf32_Half e_ehsize; /* Size of ELF header in bytes. */ - Elf32_Half e_phentsize; /* Size of program header entry. */ - Elf32_Half e_phnum; /* Number of program header entries. */ - Elf32_Half e_shentsize; /* Size of section header entry. */ - Elf32_Half e_shnum; /* Number of section header entries. */ - Elf32_Half e_shstrndx; /* Section name strings section. */ -} Elf32_Ehdr; - -typedef struct { - unsigned char e_ident[EI_NIDENT]; /* File identification. */ - Elf64_Half e_type; /* File type. */ - Elf64_Half e_machine; /* Machine architecture. */ - Elf64_Word e_version; /* ELF format version. */ - Elf64_Addr e_entry; /* Entry point. */ - Elf64_Off e_phoff; /* Program header file offset. */ - Elf64_Off e_shoff; /* Section header file offset. */ - Elf64_Word e_flags; /* Architecture-specific flags. */ - Elf64_Half e_ehsize; /* Size of ELF header in bytes. */ - Elf64_Half e_phentsize; /* Size of program header entry. */ - Elf64_Half e_phnum; /* Number of program header entries. */ - Elf64_Half e_shentsize; /* Size of section header entry. */ - Elf64_Half e_shnum; /* Number of section header entries. */ - Elf64_Half e_shstrndx; /* Section name strings section. */ -} Elf64_Ehdr; +/* ELF versions */ +#define EV_CURRENT 1 -/* - * Program header. - */ +/** ELF program header */ typedef struct { - Elf32_Word p_type; /* Entry type. */ - Elf32_Off p_offset; /* File offset of contents. */ - Elf32_Addr p_vaddr; /* Virtual address (not used). */ - Elf32_Addr p_paddr; /* Physical address. */ - Elf32_Size p_filesz; /* Size of contents in file. */ - Elf32_Size p_memsz; /* Size of contents in memory. */ - Elf32_Word p_flags; /* Access permission flags. */ - Elf32_Size p_align; /* Alignment in memory and file. */ + Elf32_Word p_type; + Elf32_Off p_offset; + Elf32_Addr p_vaddr; + Elf32_Addr p_paddr; + Elf32_Word p_filesz; + Elf32_Word p_memsz; + Elf32_Word p_flags; + Elf32_Word p_align; } Elf32_Phdr; -typedef struct { - Elf64_Word p_type; /* Entry type. */ - Elf64_Word p_flags; /* Access permission flags. */ - Elf64_Off p_offset; /* File offset of contents. */ - Elf64_Addr p_vaddr; /* Virtual address (not used). */ - Elf64_Addr p_paddr; /* Physical address. */ - Elf64_Size p_filesz; /* Size of contents in file. */ - Elf64_Size p_memsz; /* Size of contents in memory. */ - Elf64_Size p_align; /* Alignment in memory and file. */ -} Elf64_Phdr; - -/* Standardized Elf image notes for booting... The name for all of these is ELFBoot */ - -#endif /* ASSEMBLY */ +/* ELF segment types */ +#define PT_LOAD 1 #endif /* ELF_H */ diff --git a/roms/ipxe/src/include/endian.h b/roms/ipxe/src/include/endian.h index 9682cf9b4..79c3163ee 100644 --- a/roms/ipxe/src/include/endian.h +++ b/roms/ipxe/src/include/endian.h @@ -1,21 +1,22 @@ -#ifndef ETHERBOOT_ENDIAN_H -#define ETHERBOOT_ENDIAN_H +#ifndef _ENDIAN_H +#define _ENDIAN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* Definitions for byte order, according to significance of bytes, - from low addresses to high addresses. The value is what you get by - putting '4' in the most significant byte, '3' in the second most - significant byte, '2' in the second least significant byte, and '1' - in the least significant byte, and then writing down one digit for - each byte, starting with the byte at the lowest address at the left, - and proceeding to the byte with the highest address at the right. */ +/** Constant representing little-endian byte order + * + * Little-endian systems should define BYTE_ORDER as LITTLE_ENDIAN. + * This constant is intended to be used only at compile time. + */ +#define __LITTLE_ENDIAN 0x44332211UL -#define __LITTLE_ENDIAN 1234 -#define __BIG_ENDIAN 4321 -#define __PDP_ENDIAN 3412 +/** Constant representing big-endian byte order + * + * Big-endian systems should define BYTE_ORDER as BIG_ENDIAN. + * This constant is intended to be used only at compile time. + */ +#define __BIG_ENDIAN 0x11223344UL #include "bits/endian.h" - -#endif /* ETHERBOOT_ENDIAN_H */ +#endif /* _ENDIAN_H */ diff --git a/roms/ipxe/src/include/errno.h b/roms/ipxe/src/include/errno.h index bcc4a8816..036479aff 100644 --- a/roms/ipxe/src/include/errno.h +++ b/roms/ipxe/src/include/errno.h @@ -15,12 +15,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef ERRNO_H #define ERRNO_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/include/getopt.h b/roms/ipxe/src/include/getopt.h index 0fe43567e..db3de1786 100644 --- a/roms/ipxe/src/include/getopt.h +++ b/roms/ipxe/src/include/getopt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> diff --git a/roms/ipxe/src/include/hci/ifmgmt_cmd.h b/roms/ipxe/src/include/hci/ifmgmt_cmd.h index 913b911d8..5debf85c2 100644 --- a/roms/ipxe/src/include/hci/ifmgmt_cmd.h +++ b/roms/ipxe/src/include/hci/ifmgmt_cmd.h @@ -15,12 +15,16 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ #ifndef _IFMGMT_CMD_H #define _IFMGMT_CMD_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/parseopt.h> diff --git a/roms/ipxe/src/include/ipxe/acpi.h b/roms/ipxe/src/include/ipxe/acpi.h index 282b6d92d..2ccd691ed 100644 --- a/roms/ipxe/src/include/ipxe/acpi.h +++ b/roms/ipxe/src/include/ipxe/acpi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/interface.h> diff --git a/roms/ipxe/src/include/ipxe/aes.h b/roms/ipxe/src/include/ipxe/aes.h index 4e44f9853..0432e43ee 100644 --- a/roms/ipxe/src/include/ipxe/aes.h +++ b/roms/ipxe/src/include/ipxe/aes.h @@ -1,31 +1,51 @@ #ifndef _IPXE_AES_H #define _IPXE_AES_H -FILE_LICENCE ( GPL2_OR_LATER ); +/** @file + * + * AES algorithm + * + */ -struct cipher_algorithm; +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/** Basic AES blocksize */ +#include <ipxe/crypto.h> + +/** AES blocksize */ #define AES_BLOCKSIZE 16 -#include "crypto/axtls/crypto.h" +/** Maximum number of AES rounds */ +#define AES_MAX_ROUNDS 15 + +/** AES matrix */ +union aes_matrix { + /** Viewed as an array of bytes */ + uint8_t byte[16]; + /** Viewed as an array of four-byte columns */ + uint32_t column[4]; +} __attribute__ (( packed )); + +/** AES round keys */ +struct aes_round_keys { + /** Round keys */ + union aes_matrix key[AES_MAX_ROUNDS]; +}; /** AES context */ struct aes_context { - /** AES context for AXTLS */ - AES_CTX axtls_ctx; - /** Cipher is being used for decrypting */ - int decrypting; + /** Encryption keys */ + struct aes_round_keys encrypt; + /** Decryption keys */ + struct aes_round_keys decrypt; + /** Number of rounds */ + unsigned int rounds; }; /** AES context size */ #define AES_CTX_SIZE sizeof ( struct aes_context ) -/* AXTLS functions */ -extern void axtls_aes_encrypt ( const AES_CTX *ctx, uint32_t *data ); -extern void axtls_aes_decrypt ( const AES_CTX *ctx, uint32_t *data ); - extern struct cipher_algorithm aes_algorithm; +extern struct cipher_algorithm aes_ecb_algorithm; extern struct cipher_algorithm aes_cbc_algorithm; int aes_wrap ( const void *kek, const void *src, void *dest, int nblk ); diff --git a/roms/ipxe/src/include/ipxe/ansicol.h b/roms/ipxe/src/include/ipxe/ansicol.h index 707d1599d..2b54ecaca 100644 --- a/roms/ipxe/src/include/ipxe/ansicol.h +++ b/roms/ipxe/src/include/ipxe/ansicol.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <curses.h> /* For COLOR_RED etc. */ diff --git a/roms/ipxe/src/include/ipxe/ansiesc.h b/roms/ipxe/src/include/ipxe/ansiesc.h index c1c74481d..80bc83308 100644 --- a/roms/ipxe/src/include/ipxe/ansiesc.h +++ b/roms/ipxe/src/include/ipxe/ansiesc.h @@ -26,7 +26,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ansiesc_context; diff --git a/roms/ipxe/src/include/ipxe/aoe.h b/roms/ipxe/src/include/ipxe/aoe.h index 60f3bd959..0c656e7c2 100644 --- a/roms/ipxe/src/include/ipxe/aoe.h +++ b/roms/ipxe/src/include/ipxe/aoe.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/list.h> diff --git a/roms/ipxe/src/include/ipxe/api.h b/roms/ipxe/src/include/ipxe/api.h index 838b8936e..d05d3b07a 100644 --- a/roms/ipxe/src/include/ipxe/api.h +++ b/roms/ipxe/src/include/ipxe/api.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @defgroup Single-implementation APIs * diff --git a/roms/ipxe/src/include/ipxe/arp.h b/roms/ipxe/src/include/ipxe/arp.h index e30ae6b76..5822fa095 100644 --- a/roms/ipxe/src/include/ipxe/arp.h +++ b/roms/ipxe/src/include/ipxe/arp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> #include <ipxe/netdevice.h> @@ -57,4 +57,8 @@ static inline int arp_tx ( struct io_buffer *iobuf, struct net_device *netdev, &arp_discovery, net_source, ll_source ); } +extern int arp_tx_request ( struct net_device *netdev, + struct net_protocol *net_protocol, + const void *net_dest, const void *net_source ); + #endif /* _IPXE_ARP_H */ diff --git a/roms/ipxe/src/include/ipxe/asn1.h b/roms/ipxe/src/include/ipxe/asn1.h index d12524ddb..5fbd58281 100644 --- a/roms/ipxe/src/include/ipxe/asn1.h +++ b/roms/ipxe/src/include/ipxe/asn1.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <time.h> @@ -141,6 +141,24 @@ struct asn1_builder_header { ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 11 ) +/** ASN.1 OID for sha384WithRSAEncryption (1.2.840.113549.1.1.12) */ +#define ASN1_OID_SHA384WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 12 ) + +/** ASN.1 OID for sha512WithRSAEncryption (1.2.840.113549.1.1.13) */ +#define ASN1_OID_SHA512WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 13 ) + +/** ASN.1 OID for sha224WithRSAEncryption (1.2.840.113549.1.1.14) */ +#define ASN1_OID_SHA224WITHRSAENCRYPTION \ + ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_TRIPLE ( 113549 ), ASN1_OID_SINGLE ( 1 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 14 ) + /** ASN.1 OID for id-md5 (1.2.840.113549.2.5) */ #define ASN1_OID_MD5 \ ASN1_OID_INITIAL ( 1, 2 ), ASN1_OID_DOUBLE ( 840 ), \ @@ -160,6 +178,41 @@ struct asn1_builder_header { ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 1 ) +/** ASN.1 OID for id-sha384 (2.16.840.1.101.3.4.2.2) */ +#define ASN1_OID_SHA384 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 2 ) + +/** ASN.1 OID for id-sha512 (2.16.840.1.101.3.4.2.3) */ +#define ASN1_OID_SHA512 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 3 ) + +/** ASN.1 OID for id-sha224 (2.16.840.1.101.3.4.2.4) */ +#define ASN1_OID_SHA224 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 4 ) + +/** ASN.1 OID for id-sha512-224 (2.16.840.1.101.3.4.2.5) */ +#define ASN1_OID_SHA512_224 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 5 ) + +/** ASN.1 OID for id-sha512-256 (2.16.840.1.101.3.4.2.6) */ +#define ASN1_OID_SHA512_256 \ + ASN1_OID_INITIAL ( 2, 16 ), ASN1_OID_DOUBLE ( 840 ), \ + ASN1_OID_SINGLE ( 1 ), ASN1_OID_SINGLE ( 101 ), \ + ASN1_OID_SINGLE ( 3 ), ASN1_OID_SINGLE ( 4 ), \ + ASN1_OID_SINGLE ( 2 ), ASN1_OID_SINGLE ( 6 ) + /** ASN.1 OID for commonName (2.5.4.3) */ #define ASN1_OID_COMMON_NAME \ ASN1_OID_INITIAL ( 2, 5 ), ASN1_OID_SINGLE ( 4 ), \ diff --git a/roms/ipxe/src/include/ipxe/ata.h b/roms/ipxe/src/include/ipxe/ata.h index b7f02d655..a10cfafcc 100644 --- a/roms/ipxe/src/include/ipxe/ata.h +++ b/roms/ipxe/src/include/ipxe/ata.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * An ATA Logical Block Address diff --git a/roms/ipxe/src/include/ipxe/base16.h b/roms/ipxe/src/include/ipxe/base16.h index 60e3f2315..8c44da17e 100644 --- a/roms/ipxe/src/include/ipxe/base16.h +++ b/roms/ipxe/src/include/ipxe/base16.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -32,9 +32,36 @@ static inline size_t base16_decoded_max_len ( const char *encoded ) { return ( ( strlen ( encoded ) + 1 ) / 2 ); } -extern void base16_encode ( const uint8_t *raw, size_t len, char *encoded ); -extern int hex_decode ( const char *string, char separator, void *data, +extern size_t hex_encode ( char separator, const void *raw, size_t raw_len, + char *data, size_t len ); +extern int hex_decode ( char separator, const char *encoded, void *data, size_t len ); -extern int base16_decode ( const char *encoded, uint8_t *raw ); + +/** + * Base16-encode data + * + * @v raw Raw data + * @v raw_len Length of raw data + * @v data Buffer + * @v len Length of buffer + * @ret len Encoded length + */ +static inline __attribute__ (( always_inline )) size_t +base16_encode ( const void *raw, size_t raw_len, char *data, size_t len ) { + return hex_encode ( 0, raw, raw_len, data, len ); +} + +/** + * Base16-decode data + * + * @v encoded Encoded string + * @v data Buffer + * @v len Length of buffer + * @ret len Length of data, or negative error + */ +static inline __attribute__ (( always_inline )) int +base16_decode ( const char *encoded, void *data, size_t len ) { + return hex_decode ( 0, encoded, data, len ); +} #endif /* _IPXE_BASE16_H */ diff --git a/roms/ipxe/src/include/ipxe/base64.h b/roms/ipxe/src/include/ipxe/base64.h index 5fe134dc8..0c70d8382 100644 --- a/roms/ipxe/src/include/ipxe/base64.h +++ b/roms/ipxe/src/include/ipxe/base64.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -35,7 +35,8 @@ static inline size_t base64_decoded_max_len ( const char *encoded ) { return ( ( ( strlen ( encoded ) + 4 - 1 ) / 4 ) * 3 ); } -extern void base64_encode ( const uint8_t *raw, size_t len, char *encoded ); -extern int base64_decode ( const char *encoded, uint8_t *raw ); +extern size_t base64_encode ( const void *raw, size_t raw_len, char *data, + size_t len ); +extern int base64_decode ( const char *encoded, void *data, size_t len ); #endif /* _IPXE_BASE64_H */ diff --git a/roms/ipxe/src/include/ipxe/bigint.h b/roms/ipxe/src/include/ipxe/bigint.h index 97fbce245..2f99f8445 100644 --- a/roms/ipxe/src/include/ipxe/bigint.h +++ b/roms/ipxe/src/include/ipxe/bigint.h @@ -6,7 +6,7 @@ * Big integer support */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Define a big-integer type diff --git a/roms/ipxe/src/include/ipxe/bitbash.h b/roms/ipxe/src/include/ipxe/bitbash.h index 69d5d9e3e..2a2e475d0 100644 --- a/roms/ipxe/src/include/ipxe/bitbash.h +++ b/roms/ipxe/src/include/ipxe/bitbash.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct bit_basher; diff --git a/roms/ipxe/src/include/ipxe/bitmap.h b/roms/ipxe/src/include/ipxe/bitmap.h index b18584c1f..38aca694b 100644 --- a/roms/ipxe/src/include/ipxe/bitmap.h +++ b/roms/ipxe/src/include/ipxe/bitmap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> diff --git a/roms/ipxe/src/include/ipxe/bitops.h b/roms/ipxe/src/include/ipxe/bitops.h index 73e859f41..220ab0fe7 100644 --- a/roms/ipxe/src/include/ipxe/bitops.h +++ b/roms/ipxe/src/include/ipxe/bitops.h @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/include/ipxe/blockdev.h b/roms/ipxe/src/include/ipxe/blockdev.h index 9f0a9f787..418c43004 100644 --- a/roms/ipxe/src/include/ipxe/blockdev.h +++ b/roms/ipxe/src/include/ipxe/blockdev.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/include/ipxe/blocktrans.h b/roms/ipxe/src/include/ipxe/blocktrans.h new file mode 100644 index 000000000..fee71b96c --- /dev/null +++ b/roms/ipxe/src/include/ipxe/blocktrans.h @@ -0,0 +1,38 @@ +#ifndef _IPXE_BLOCKTRANS_H +#define _IPXE_BLOCKTRANS_H + +/** @file + * + * Block device translator + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/refcnt.h> +#include <ipxe/interface.h> +#include <ipxe/xferbuf.h> +#include <ipxe/uaccess.h> + +/** A block device translator */ +struct block_translator { + /** Reference count */ + struct refcnt refcnt; + /** Block device interface */ + struct interface block; + /** Data transfer interface */ + struct interface xfer; + + /** Data transfer buffer */ + struct xfer_buffer xferbuf; + /** Data buffer */ + userptr_t buffer; + /** Block size */ + size_t blksize; +}; + +extern int block_translate ( struct interface *block, + userptr_t buffer, size_t size ); + +#endif /* _IPXE_BLOCKTRANS_H */ diff --git a/roms/ipxe/src/include/ipxe/bofm.h b/roms/ipxe/src/include/ipxe/bofm.h index 1da47f651..bc994ea8b 100644 --- a/roms/ipxe/src/include/ipxe/bofm.h +++ b/roms/ipxe/src/include/ipxe/bofm.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/list.h> diff --git a/roms/ipxe/src/include/ipxe/cbc.h b/roms/ipxe/src/include/ipxe/cbc.h index fae376577..18a94e144 100644 --- a/roms/ipxe/src/include/ipxe/cbc.h +++ b/roms/ipxe/src/include/ipxe/cbc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/include/ipxe/cdc.h b/roms/ipxe/src/include/ipxe/cdc.h new file mode 100644 index 000000000..f1799cd9a --- /dev/null +++ b/roms/ipxe/src/include/ipxe/cdc.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_CDC_H +#define _IPXE_CDC_H + +/** @file + * + * USB Communications Device Class (CDC) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> + +/** Class code for communications devices */ +#define USB_CLASS_CDC 2 + +/** Union functional descriptor */ +struct cdc_union_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Descriptor subtype */ + uint8_t subtype; + /** Interfaces (variable-length) */ + uint8_t interface[1]; +} __attribute__ (( packed )); + +/** Union functional descriptor subtype */ +#define CDC_SUBTYPE_UNION 6 + +/** Ethernet descriptor subtype */ +#define CDC_SUBTYPE_ETHERNET 15 + +/** Network connection notification */ +#define CDC_NETWORK_CONNECTION \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x00 ) ) + +/** Connection speed change notification */ +#define CDC_CONNECTION_SPEED_CHANGE \ + ( USB_DIR_IN | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x2a ) ) + +/** Connection speed change notification */ +struct cdc_connection_speed_change { + /** Downlink bit rate, in bits per second */ + uint32_t down; + /** Uplink bit rate, in bits per second */ + uint32_t up; +} __attribute__ (( packed )); + +extern struct cdc_union_descriptor * +cdc_union_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface ); + +#endif /* _IPXE_CDC_H */ diff --git a/roms/ipxe/src/include/ipxe/certstore.h b/roms/ipxe/src/include/ipxe/certstore.h index 7456db621..49b3b512c 100644 --- a/roms/ipxe/src/include/ipxe/certstore.h +++ b/roms/ipxe/src/include/ipxe/certstore.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/asn1.h> #include <ipxe/x509.h> diff --git a/roms/ipxe/src/include/ipxe/chap.h b/roms/ipxe/src/include/ipxe/chap.h index fce48f3ea..7c693e29d 100644 --- a/roms/ipxe/src/include/ipxe/chap.h +++ b/roms/ipxe/src/include/ipxe/chap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/md5.h> diff --git a/roms/ipxe/src/include/ipxe/cms.h b/roms/ipxe/src/include/ipxe/cms.h index e026ebd2f..7adf724b2 100644 --- a/roms/ipxe/src/include/ipxe/cms.h +++ b/roms/ipxe/src/include/ipxe/cms.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <time.h> #include <ipxe/asn1.h> diff --git a/roms/ipxe/src/include/ipxe/command.h b/roms/ipxe/src/include/ipxe/command.h index 432da1abb..a208e7d8f 100644 --- a/roms/ipxe/src/include/ipxe/command.h +++ b/roms/ipxe/src/include/ipxe/command.h @@ -1,7 +1,7 @@ #ifndef _IPXE_COMMAND_H #define _IPXE_COMMAND_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/console.h b/roms/ipxe/src/include/ipxe/console.h index 4b90c9cec..1b764aaca 100644 --- a/roms/ipxe/src/include/ipxe/console.h +++ b/roms/ipxe/src/include/ipxe/console.h @@ -16,7 +16,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct pixel_buffer; diff --git a/roms/ipxe/src/include/ipxe/cpio.h b/roms/ipxe/src/include/ipxe/cpio.h index 277232808..0637c531d 100644 --- a/roms/ipxe/src/include/ipxe/cpio.h +++ b/roms/ipxe/src/include/ipxe/cpio.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A CPIO archive header * diff --git a/roms/ipxe/src/include/ipxe/crc32.h b/roms/ipxe/src/include/ipxe/crc32.h index 38ac1b31f..30d2fe66c 100644 --- a/roms/ipxe/src/include/ipxe/crc32.h +++ b/roms/ipxe/src/include/ipxe/crc32.h @@ -1,7 +1,7 @@ #ifndef _IPXE_CRC32_H #define _IPXE_CRC32_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/crypto.h b/roms/ipxe/src/include/ipxe/crypto.h index 3eda5ec6e..fc0d8b22b 100644 --- a/roms/ipxe/src/include/ipxe/crypto.h +++ b/roms/ipxe/src/include/ipxe/crypto.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> diff --git a/roms/ipxe/src/include/ipxe/deflate.h b/roms/ipxe/src/include/ipxe/deflate.h index 19c5125eb..b751aa9a3 100644 --- a/roms/ipxe/src/include/ipxe/deflate.h +++ b/roms/ipxe/src/include/ipxe/deflate.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/include/ipxe/device.h b/roms/ipxe/src/include/ipxe/device.h index 7202a6966..d81417e8e 100644 --- a/roms/ipxe/src/include/ipxe/device.h +++ b/roms/ipxe/src/include/ipxe/device.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> #include <ipxe/tables.h> @@ -63,10 +63,16 @@ struct device_description { /** Xen bus type */ #define BUS_TYPE_XEN 8 +/** Hyper-V bus type */ +#define BUS_TYPE_HV 9 + +/** USB bus type */ +#define BUS_TYPE_USB 10 + /** A hardware device */ struct device { /** Name */ - char name[16]; + char name[32]; /** Driver name */ const char *driver_name; /** Device description */ @@ -93,6 +99,8 @@ struct root_device { struct device dev; /** Root device driver */ struct root_driver *driver; + /** Driver-private data */ + void *priv; }; /** A root device driver */ @@ -123,6 +131,27 @@ struct root_driver { /** Declare a root device */ #define __root_device __table_entry ( ROOT_DEVICES, 01 ) +/** + * Set root device driver-private data + * + * @v rootdev Root device + * @v priv Private data + */ +static inline void rootdev_set_drvdata ( struct root_device *rootdev, + void *priv ){ + rootdev->priv = priv; +} + +/** + * Get root device driver-private data + * + * @v rootdev Root device + * @ret priv Private data + */ +static inline void * rootdev_get_drvdata ( struct root_device *rootdev ) { + return rootdev->priv; +} + extern int device_keep_count; /** diff --git a/roms/ipxe/src/include/ipxe/dhcp.h b/roms/ipxe/src/include/ipxe/dhcp.h index bcfb85cc1..a11db3497 100644 --- a/roms/ipxe/src/include/ipxe/dhcp.h +++ b/roms/ipxe/src/include/ipxe/dhcp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdarg.h> @@ -639,16 +639,6 @@ struct dhcphdr { */ #define DHCP_MIN_LEN 552 -/** Timeouts for sending DHCP packets */ -#define DHCP_MIN_TIMEOUT ( 1 * TICKS_PER_SEC ) -#define DHCP_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) - -/** Maximum time that we will wait for ProxyDHCP responses */ -#define PROXYDHCP_MAX_TIMEOUT ( 2 * TICKS_PER_SEC ) - -/** Maximum time that we will wait for Boot Server responses */ -#define PXEBS_MAX_TIMEOUT ( 3 * TICKS_PER_SEC ) - /** Settings block name used for DHCP responses */ #define DHCP_SETTINGS_NAME "dhcp" diff --git a/roms/ipxe/src/include/ipxe/dhcpopts.h b/roms/ipxe/src/include/ipxe/dhcpopts.h index c5af5d749..707fda4a8 100644 --- a/roms/ipxe/src/include/ipxe/dhcpopts.h +++ b/roms/ipxe/src/include/ipxe/dhcpopts.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/dhcppkt.h b/roms/ipxe/src/include/ipxe/dhcppkt.h index 3179a6bb0..f13dfc93d 100644 --- a/roms/ipxe/src/include/ipxe/dhcppkt.h +++ b/roms/ipxe/src/include/ipxe/dhcppkt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/dhcp.h> #include <ipxe/dhcpopts.h> diff --git a/roms/ipxe/src/include/ipxe/dhcpv6.h b/roms/ipxe/src/include/ipxe/dhcpv6.h index 2636b8ab2..9307b6cae 100644 --- a/roms/ipxe/src/include/ipxe/dhcpv6.h +++ b/roms/ipxe/src/include/ipxe/dhcpv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/in.h> diff --git a/roms/ipxe/src/include/ipxe/dns.h b/roms/ipxe/src/include/ipxe/dns.h index 4f6cab3a4..738dea6e4 100644 --- a/roms/ipxe/src/include/ipxe/dns.h +++ b/roms/ipxe/src/include/ipxe/dns.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/in.h> diff --git a/roms/ipxe/src/include/ipxe/downloader.h b/roms/ipxe/src/include/ipxe/downloader.h index de1a2e75e..ccb1abfef 100644 --- a/roms/ipxe/src/include/ipxe/downloader.h +++ b/roms/ipxe/src/include/ipxe/downloader.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct interface; struct image; diff --git a/roms/ipxe/src/include/ipxe/drbg.h b/roms/ipxe/src/include/ipxe/drbg.h index 6374e7787..ed2b3757a 100644 --- a/roms/ipxe/src/include/ipxe/drbg.h +++ b/roms/ipxe/src/include/ipxe/drbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/sha256.h> diff --git a/roms/ipxe/src/include/ipxe/ecb.h b/roms/ipxe/src/include/ipxe/ecb.h new file mode 100644 index 000000000..4e6aa3c81 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/ecb.h @@ -0,0 +1,55 @@ +#ifndef _IPXE_ECB_H +#define _IPXE_ECB_H + +/** @file + * + * Electronic codebook (ECB) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/crypto.h> + +extern void ecb_encrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher ); +extern void ecb_decrypt ( void *ctx, const void *src, void *dst, + size_t len, struct cipher_algorithm *raw_cipher ); + +/** + * Create a cipher-block chaining mode of behaviour of an existing cipher + * + * @v _ecb_name Name for the new ECB cipher + * @v _ecb_cipher New cipher algorithm + * @v _raw_cipher Underlying cipher algorithm + * @v _raw_context Context structure for the underlying cipher + * @v _blocksize Cipher block size + */ +#define ECB_CIPHER( _ecb_name, _ecb_cipher, _raw_cipher, _raw_context, \ + _blocksize ) \ +static int _ecb_name ## _setkey ( void *ctx, const void *key, \ + size_t keylen ) { \ + return cipher_setkey ( &_raw_cipher, ctx, key, keylen ); \ +} \ +static void _ecb_name ## _setiv ( void *ctx, const void *iv ) { \ + cipher_setiv ( &_raw_cipher, ctx, iv ); \ +} \ +static void _ecb_name ## _encrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + ecb_encrypt ( ctx, src, dst, len, &_raw_cipher ); \ +} \ +static void _ecb_name ## _decrypt ( void *ctx, const void *src, \ + void *dst, size_t len ) { \ + ecb_decrypt ( ctx, src, dst, len, &_raw_cipher ); \ +} \ +struct cipher_algorithm _ecb_cipher = { \ + .name = #_ecb_name, \ + .ctxsize = sizeof ( _raw_context ), \ + .blocksize = _blocksize, \ + .setkey = _ecb_name ## _setkey, \ + .setiv = _ecb_name ## _setiv, \ + .encrypt = _ecb_name ## _encrypt, \ + .decrypt = _ecb_name ## _decrypt, \ +}; + +#endif /* _IPXE_ECB_H */ diff --git a/roms/ipxe/src/include/ipxe/edd.h b/roms/ipxe/src/include/ipxe/edd.h index 0c25593d5..1914fd0b0 100644 --- a/roms/ipxe/src/include/ipxe/edd.h +++ b/roms/ipxe/src/include/ipxe/edd.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/interface.h> diff --git a/roms/ipxe/src/include/ipxe/editbox.h b/roms/ipxe/src/include/ipxe/editbox.h index 9122dbbf3..2c70e0b6b 100644 --- a/roms/ipxe/src/include/ipxe/editbox.h +++ b/roms/ipxe/src/include/ipxe/editbox.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <curses.h> #include <ipxe/editstring.h> diff --git a/roms/ipxe/src/include/ipxe/editstring.h b/roms/ipxe/src/include/ipxe/editstring.h index 2ef546a63..a00a8adaa 100644 --- a/roms/ipxe/src/include/ipxe/editstring.h +++ b/roms/ipxe/src/include/ipxe/editstring.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** An editable string */ struct edit_string { diff --git a/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h b/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h index 1294459f9..7466814fa 100644 --- a/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h +++ b/roms/ipxe/src/include/ipxe/efi/ProcessorBind.h @@ -1,7 +1,7 @@ #ifndef _IPXE_EFI_PROCESSOR_BIND_H #define _IPXE_EFI_PROCESSOR_BIND_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * EFI header files rely on having the CPU architecture directory diff --git a/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h b/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h new file mode 100644 index 000000000..f04efbb03 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/Protocol/Rng.h @@ -0,0 +1,158 @@ +/** @file + EFI_RNG_PROTOCOL as defined in UEFI 2.4. + The UEFI Random Number Generator Protocol is used to provide random bits for use + in applications, or entropy for seeding other random number generators. + +Copyright (c) 2013, Intel Corporation. All rights reserved.<BR> +This program and the accompanying materials are licensed and made available under +the terms and conditions of the BSD License that accompanies this distribution. +The full text of the license may be found at +http://opensource.org/licenses/bsd-license.php. + +THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS, +WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED. + +**/ + +#ifndef __EFI_RNG_PROTOCOL_H__ +#define __EFI_RNG_PROTOCOL_H__ + +FILE_LICENCE ( BSD3 ); + +/// +/// Global ID for the Random Number Generator Protocol +/// +#define EFI_RNG_PROTOCOL_GUID \ + { \ + 0x3152bca5, 0xeade, 0x433d, {0x86, 0x2e, 0xc0, 0x1c, 0xdc, 0x29, 0x1f, 0x44 } \ + } + +typedef struct _EFI_RNG_PROTOCOL EFI_RNG_PROTOCOL; + +/// +/// A selection of EFI_RNG_PROTOCOL algorithms. +/// The algorithms listed are optional, not meant to be exhaustive and be argmented by +/// vendors or other industry standards. +/// + +typedef EFI_GUID EFI_RNG_ALGORITHM; + +/// +/// The algorithms corresponds to SP800-90 as defined in +/// NIST SP 800-90, "Recommendation for Random Number Generation Using Deterministic Random +/// Bit Generators", March 2007. +/// +#define EFI_RNG_ALGORITHM_SP800_90_HASH_256_GUID \ + { \ + 0xa7af67cb, 0x603b, 0x4d42, {0xba, 0x21, 0x70, 0xbf, 0xb6, 0x29, 0x3f, 0x96 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_HMAC_256_GUID \ + { \ + 0xc5149b43, 0xae85, 0x4f53, {0x99, 0x82, 0xb9, 0x43, 0x35, 0xd3, 0xa9, 0xe7 } \ + } +#define EFI_RNG_ALGORITHM_SP800_90_CTR_256_GUID \ + { \ + 0x44f0de6e, 0x4d8c, 0x4045, {0xa8, 0xc7, 0x4d, 0xd1, 0x68, 0x85, 0x6b, 0x9e } \ + } +/// +/// The algorithms correspond to X9.31 as defined in +/// NIST, "Recommended Random Number Generator Based on ANSI X9.31 Appendix A.2.4 Using +/// the 3-Key Triple DES and AES Algorithm", January 2005. +/// +#define EFI_RNG_ALGORITHM_X9_31_3DES_GUID \ + { \ + 0x63c4785a, 0xca34, 0x4012, {0xa3, 0xc8, 0x0b, 0x6a, 0x32, 0x4f, 0x55, 0x46 } \ + } +#define EFI_RNG_ALGORITHM_X9_31_AES_GUID \ + { \ + 0xacd03321, 0x777e, 0x4d3d, {0xb1, 0xc8, 0x20, 0xcf, 0xd8, 0x88, 0x20, 0xc9 } \ + } +/// +/// The "raw" algorithm, when supported, is intended to provide entropy directly from +/// the source, without it going through some deterministic random bit generator. +/// +#define EFI_RNG_ALGORITHM_RAW \ + { \ + 0xe43176d7, 0xb6e8, 0x4827, {0xb7, 0x84, 0x7f, 0xfd, 0xc4, 0xb6, 0x85, 0x61 } \ + } + +/** + Returns information about the random number generation implementation. + + @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. + @param[in,out] RNGAlgorithmListSize On input, the size in bytes of RNGAlgorithmList. + On output with a return code of EFI_SUCCESS, the size + in bytes of the data returned in RNGAlgorithmList. On output + with a return code of EFI_BUFFER_TOO_SMALL, + the size of RNGAlgorithmList required to obtain the list. + @param[out] RNGAlgorithmList A caller-allocated memory buffer filled by the driver + with one EFI_RNG_ALGORITHM element for each supported + RNG algorithm. The list must not change across multiple + calls to the same driver. The first algorithm in the list + is the default algorithm for the driver. + + @retval EFI_SUCCESS The RNG algorithm list was returned successfully. + @retval EFI_UNSUPPORTED The services is not supported by this driver. + @retval EFI_DEVICE_ERROR The list of algorithms could not be retrieved due to a + hardware or firmware error. + @retval EFI_INVALID_PARAMETER One or more of the parameters are incorrect. + @retval EFI_BUFFER_TOO_SMALL The buffer RNGAlgorithmList is too small to hold the result. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_INFO) ( + IN EFI_RNG_PROTOCOL *This, + IN OUT UINTN *RNGAlgorithmListSize, + OUT EFI_RNG_ALGORITHM *RNGAlgorithmList + ); + +/** + Produces and returns an RNG value using either the default or specified RNG algorithm. + + @param[in] This A pointer to the EFI_RNG_PROTOCOL instance. + @param[in] RNGAlgorithm A pointer to the EFI_RNG_ALGORITHM that identifies the RNG + algorithm to use. May be NULL in which case the function will + use its default RNG algorithm. + @param[in] RNGValueLength The length in bytes of the memory buffer pointed to by + RNGValue. The driver shall return exactly this numbers of bytes. + @param[out] RNGValue A caller-allocated memory buffer filled by the driver with the + resulting RNG value. + + @retval EFI_SUCCESS The RNG value was returned successfully. + @retval EFI_UNSUPPORTED The algorithm specified by RNGAlgorithm is not supported by + this driver. + @retval EFI_DEVICE_ERROR An RNG value could not be retrieved due to a hardware or + firmware error. + @retval EFI_NOT_READY There is not enough random data available to satisfy the length + requested by RNGValueLength. + @retval EFI_INVALID_PARAMETER RNGValue is NULL or RNGValueLength is zero. + +**/ +typedef +EFI_STATUS +(EFIAPI *EFI_RNG_GET_RNG) ( + IN EFI_RNG_PROTOCOL *This, + IN EFI_RNG_ALGORITHM *RNGAlgorithm, OPTIONAL + IN UINTN RNGValueLength, + OUT UINT8 *RNGValue + ); + +/// +/// The Random Number Generator (RNG) protocol provides random bits for use in +/// applications, or entropy for seeding other random number generators. +/// +struct _EFI_RNG_PROTOCOL { + EFI_RNG_GET_INFO GetInfo; + EFI_RNG_GET_RNG GetRNG; +}; + +extern EFI_GUID gEfiRngProtocolGuid; +extern EFI_GUID gEfiRngAlgorithmSp80090Hash256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Hmac256Guid; +extern EFI_GUID gEfiRngAlgorithmSp80090Ctr256Guid; +extern EFI_GUID gEfiRngAlgorithmX9313DesGuid; +extern EFI_GUID gEfiRngAlgorithmX931AesGuid; +extern EFI_GUID gEfiRngAlgorithmRaw; + +#endif diff --git a/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h b/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h index d4a26850c..1d5ddc8c3 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_autoboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void efi_set_autoboot ( void ); diff --git a/roms/ipxe/src/include/ipxe/efi/efi_driver.h b/roms/ipxe/src/include/ipxe/efi/efi_driver.h index e16a24daa..f497df3e3 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_driver.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_driver.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/device.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/efi/efi_entropy.h b/roms/ipxe/src/include/ipxe/efi/efi_entropy.h new file mode 100644 index 000000000..39a667355 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_entropy.h @@ -0,0 +1,35 @@ +#ifndef _IPXE_EFI_ENTROPY_H +#define _IPXE_EFI_ENTROPY_H + +/** @file + * + * EFI entropy source + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +#ifdef ENTROPY_EFI +#define ENTROPY_PREFIX_efi +#else +#define ENTROPY_PREFIX_efi __efi_ +#endif + +/** + * min-entropy per sample + * + * @ret min_entropy min-entropy of each sample + */ +static inline __always_inline double +ENTROPY_INLINE ( efi, min_entropy_per_sample ) ( void ) { + + /* We use essentially the same mechanism as for the BIOS + * RTC-based entropy source, and so assume the same + * min-entropy per sample. + */ + return 1.3; +} + +#endif /* _IPXE_EFI_ENTROPY_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_hii.h b/roms/ipxe/src/include/ipxe/efi/efi_hii.h index 8e94bbe7e..bbec31194 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_hii.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_hii.h @@ -6,7 +6,7 @@ * EFI human interface infrastructure */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <ipxe/efi/Uefi/UefiInternalFormRepresentation.h> diff --git a/roms/ipxe/src/include/ipxe/efi/efi_pci.h b/roms/ipxe/src/include/ipxe/efi/efi_pci.h index af36613d9..6dd945f05 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_pci.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_pci.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/pci.h> #include <ipxe/efi/efi.h> diff --git a/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h b/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h index 498a0388b..887d5ee14 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_pci_api.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_EFI #define PCIAPI_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_reboot.h b/roms/ipxe/src/include/ipxe/efi/efi_reboot.h index 33921b913..249cae8c5 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_reboot.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_EFI #define REBOOT_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_smbios.h b/roms/ipxe/src/include/ipxe/efi/efi_smbios.h index 7642e5bc5..d890d5460 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_smbios.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_EFI #define SMBIOS_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_snp.h b/roms/ipxe/src/include/ipxe/efi/efi_snp.h index a18bced5f..1e5c66626 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_snp.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_snp.h @@ -18,6 +18,9 @@ #include <ipxe/efi/Protocol/HiiDatabase.h> #include <ipxe/efi/Protocol/LoadFile.h> +/** SNP transmit completion ring size */ +#define EFI_SNP_NUM_TX 32 + /** An SNP device */ struct efi_snp_device { /** List of SNP devices */ @@ -34,20 +37,16 @@ struct efi_snp_device { EFI_SIMPLE_NETWORK_MODE mode; /** Started flag */ int started; - /** Outstanding TX packet count (via "interrupt status") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_interrupts; - /** Outstanding TX packet count (via "recycled tx buffers") - * - * Used in order to generate TX completions. - */ - unsigned int tx_count_txbufs; - /** Outstanding RX packet count (via "interrupt status") */ - unsigned int rx_count_interrupts; - /** Outstanding RX packet count (via WaitForPacket event) */ - unsigned int rx_count_events; + /** Pending interrupt status */ + unsigned int interrupts; + /** Transmit completion ring */ + VOID *tx[EFI_SNP_NUM_TX]; + /** Transmit completion ring producer counter */ + unsigned int tx_prod; + /** Transmit completion ring consumer counter */ + unsigned int tx_cons; + /** Receive queue */ + struct list_head rx; /** The network interface identifier */ EFI_NETWORK_INTERFACE_IDENTIFIER_PROTOCOL nii; /** Component name protocol */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_strings.h b/roms/ipxe/src/include/ipxe/efi/efi_strings.h index 023ccda07..2f241537e 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_strings.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_strings.h @@ -6,7 +6,7 @@ * EFI strings */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/efi/efi_time.h b/roms/ipxe/src/include/ipxe/efi/efi_time.h new file mode 100644 index 000000000..099994b57 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_time.h @@ -0,0 +1,20 @@ +#ifndef _IPXE_EFI_TIME_H +#define _IPXE_EFI_TIME_H + +/** @file + * + * EFI time source + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +#ifdef TIME_EFI +#define TIME_PREFIX_efi +#else +#define TIME_PREFIX_efi __efi_ +#endif + +#endif /* _IPXE_EFI_TIME_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_timer.h b/roms/ipxe/src/include/ipxe/efi/efi_timer.h index b10543d6c..c03765393 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_timer.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_EFI #define TIMER_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h b/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h index 870a089b2..3cc750405 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_uaccess.h @@ -10,7 +10,7 @@ * no-ops. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UACCESS_EFI #define UACCESS_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h b/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h index 911e69a96..4eb2a5f9b 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_umalloc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef UMALLOC_EFI #define UMALLOC_PREFIX_efi diff --git a/roms/ipxe/src/include/ipxe/efi/efi_utils.h b/roms/ipxe/src/include/ipxe/efi/efi_utils.h index 9164be190..57268daf7 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_utils.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_utils.h @@ -6,7 +6,7 @@ * EFI utilities */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/Protocol/DevicePath.h> diff --git a/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h b/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h new file mode 100644 index 000000000..4a56b9a29 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/efi/efi_watchdog.h @@ -0,0 +1,31 @@ +#ifndef _IPXE_EFI_WATCHDOG_H +#define _IPXE_EFI_WATCHDOG_H + +/** @file + * + * EFI watchdog holdoff timer + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern struct retry_timer efi_watchdog; + +/** + * Start EFI watchdog holdoff timer + * + */ +static inline void efi_watchdog_start ( void ) { + + start_timer_nodelay ( &efi_watchdog ); +} + +/** + * Stop EFI watchdog holdoff timer + * + */ +static inline void efi_watchdog_stop ( void ) { + + stop_timer ( &efi_watchdog ); +} + +#endif /* _IPXE_EFI_WATCHDOG_H */ diff --git a/roms/ipxe/src/include/ipxe/efi/efi_wrap.h b/roms/ipxe/src/include/ipxe/efi/efi_wrap.h index 7579e0fe9..d8ed1a5cc 100644 --- a/roms/ipxe/src/include/ipxe/efi/efi_wrap.h +++ b/roms/ipxe/src/include/ipxe/efi/efi_wrap.h @@ -6,7 +6,7 @@ * EFI driver interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> diff --git a/roms/ipxe/src/include/ipxe/eisa.h b/roms/ipxe/src/include/ipxe/eisa.h index 22a1ed94e..e7dac1f39 100644 --- a/roms/ipxe/src/include/ipxe/eisa.h +++ b/roms/ipxe/src/include/ipxe/eisa.h @@ -1,7 +1,7 @@ #ifndef EISA_H #define EISA_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/isa_ids.h> diff --git a/roms/ipxe/src/include/ipxe/elf.h b/roms/ipxe/src/include/ipxe/elf.h index ec675c047..033c3f7a8 100644 --- a/roms/ipxe/src/include/ipxe/elf.h +++ b/roms/ipxe/src/include/ipxe/elf.h @@ -8,10 +8,21 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <stdint.h> +#include <ipxe/image.h> #include <elf.h> +typedef Elf32_Ehdr Elf_Ehdr; +typedef Elf32_Phdr Elf_Phdr; +typedef Elf32_Off Elf_Off; +#define ELFCLASS ELFCLASS32 + +extern int elf_segments ( struct image *image, Elf_Ehdr *ehdr, + int ( * process ) ( struct image *image, + Elf_Phdr *phdr, physaddr_t dest ), + physaddr_t *entry, physaddr_t *max ); extern int elf_load ( struct image *image, physaddr_t *entry, physaddr_t *max ); #endif /* _IPXE_ELF_H */ diff --git a/roms/ipxe/src/include/ipxe/eltorito.h b/roms/ipxe/src/include/ipxe/eltorito.h index 3302b38b6..27e361b16 100644 --- a/roms/ipxe/src/include/ipxe/eltorito.h +++ b/roms/ipxe/src/include/ipxe/eltorito.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/iso9660.h> diff --git a/roms/ipxe/src/include/ipxe/entropy.h b/roms/ipxe/src/include/ipxe/entropy.h index adf325e79..beeb3abfa 100644 --- a/roms/ipxe/src/include/ipxe/entropy.h +++ b/roms/ipxe/src/include/ipxe/entropy.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -54,6 +54,7 @@ typedef uint8_t entropy_sample_t; /* Include all architecture-independent entropy API headers */ #include <ipxe/null_entropy.h> +#include <ipxe/efi/efi_entropy.h> #include <ipxe/linux/linux_entropy.h> /* Include all architecture-dependent entropy API headers */ diff --git a/roms/ipxe/src/include/ipxe/errfile.h b/roms/ipxe/src/include/ipxe/errfile.h index f809337ff..e21c95938 100644 --- a/roms/ipxe/src/include/ipxe/errfile.h +++ b/roms/ipxe/src/include/ipxe/errfile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <bits/errfile.h> @@ -68,6 +68,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_fbcon ( ERRFILE_CORE | 0x001c0000 ) #define ERRFILE_ansicol ( ERRFILE_CORE | 0x001d0000 ) #define ERRFILE_ansicoldef ( ERRFILE_CORE | 0x001e0000 ) +#define ERRFILE_fault ( ERRFILE_CORE | 0x001f0000 ) +#define ERRFILE_blocktrans ( ERRFILE_CORE | 0x00200000 ) #define ERRFILE_eisa ( ERRFILE_DRIVER | 0x00000000 ) #define ERRFILE_isa ( ERRFILE_DRIVER | 0x00010000 ) @@ -76,12 +78,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_pci ( ERRFILE_DRIVER | 0x00040000 ) #define ERRFILE_linux ( ERRFILE_DRIVER | 0x00050000 ) #define ERRFILE_pcivpd ( ERRFILE_DRIVER | 0x00060000 ) +#define ERRFILE_usb ( ERRFILE_DRIVER | 0x00070000 ) +#define ERRFILE_usbhub ( ERRFILE_DRIVER | 0x00080000 ) +#define ERRFILE_xhci ( ERRFILE_DRIVER | 0x00090000 ) +#define ERRFILE_ehci ( ERRFILE_DRIVER | 0x000a0000 ) +#define ERRFILE_uhci ( ERRFILE_DRIVER | 0x000b0000 ) +#define ERRFILE_usbhid ( ERRFILE_DRIVER | 0x000c0000 ) +#define ERRFILE_usbkbd ( ERRFILE_DRIVER | 0x000d0000 ) #define ERRFILE_nvs ( ERRFILE_DRIVER | 0x00100000 ) #define ERRFILE_spi ( ERRFILE_DRIVER | 0x00110000 ) #define ERRFILE_i2c_bit ( ERRFILE_DRIVER | 0x00120000 ) #define ERRFILE_spi_bit ( ERRFILE_DRIVER | 0x00130000 ) #define ERRFILE_nvsvpd ( ERRFILE_DRIVER | 0x00140000 ) +#define ERRFILE_uart ( ERRFILE_DRIVER | 0x00150000 ) #define ERRFILE_3c509 ( ERRFILE_DRIVER | 0x00200000 ) #define ERRFILE_bnx2 ( ERRFILE_DRIVER | 0x00210000 ) @@ -157,7 +167,11 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_snp ( ERRFILE_DRIVER | 0x00680000 ) #define ERRFILE_netfront ( ERRFILE_DRIVER | 0x00690000 ) #define ERRFILE_nii ( ERRFILE_DRIVER | 0x006a0000 ) - +#define ERRFILE_netvsc ( ERRFILE_DRIVER | 0x006b0000 ) +#define ERRFILE_ecm ( ERRFILE_DRIVER | 0x006c0000 ) +#define ERRFILE_ncm ( ERRFILE_DRIVER | 0x006d0000 ) +#define ERRFILE_usbnet ( ERRFILE_DRIVER | 0x006e0000 ) +#define ERRFILE_dm96xx ( ERRFILE_DRIVER | 0x006f0000 ) #define ERRFILE_scsi ( ERRFILE_DRIVER | 0x00700000 ) #define ERRFILE_arbel ( ERRFILE_DRIVER | 0x00710000 ) #define ERRFILE_hermon ( ERRFILE_DRIVER | 0x00720000 ) @@ -165,6 +179,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ata ( ERRFILE_DRIVER | 0x00740000 ) #define ERRFILE_srp ( ERRFILE_DRIVER | 0x00750000 ) #define ERRFILE_qib7322 ( ERRFILE_DRIVER | 0x00760000 ) +#define ERRFILE_smsc75xx ( ERRFILE_DRIVER | 0x00770000 ) +#define ERRFILE_intelvf ( ERRFILE_DRIVER | 0x00780000 ) +#define ERRFILE_intelxvf ( ERRFILE_DRIVER | 0x00790000 ) #define ERRFILE_aoe ( ERRFILE_NET | 0x00000000 ) #define ERRFILE_arp ( ERRFILE_NET | 0x00010000 ) @@ -227,6 +244,17 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_ping ( ERRFILE_NET | 0x003a0000 ) #define ERRFILE_dhcpv6 ( ERRFILE_NET | 0x003b0000 ) #define ERRFILE_nfs_uri ( ERRFILE_NET | 0x003c0000 ) +#define ERRFILE_rndis ( ERRFILE_NET | 0x003d0000 ) +#define ERRFILE_pccrc ( ERRFILE_NET | 0x003e0000 ) +#define ERRFILE_stp ( ERRFILE_NET | 0x003f0000 ) +#define ERRFILE_pccrd ( ERRFILE_NET | 0x00400000 ) +#define ERRFILE_httpconn ( ERRFILE_NET | 0x00410000 ) +#define ERRFILE_httpauth ( ERRFILE_NET | 0x00420000 ) +#define ERRFILE_httpbasic ( ERRFILE_NET | 0x00430000 ) +#define ERRFILE_httpdigest ( ERRFILE_NET | 0x00440000 ) +#define ERRFILE_peerdisc ( ERRFILE_NET | 0x00450000 ) +#define ERRFILE_peerblk ( ERRFILE_NET | 0x00460000 ) +#define ERRFILE_peermux ( ERRFILE_NET | 0x00470000 ) #define ERRFILE_image ( ERRFILE_IMAGE | 0x00000000 ) #define ERRFILE_elf ( ERRFILE_IMAGE | 0x00010000 ) @@ -245,7 +273,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_imgmgmt ( ERRFILE_OTHER | 0x00050000 ) #define ERRFILE_pxe_tftp ( ERRFILE_OTHER | 0x00060000 ) #define ERRFILE_pxe_udp ( ERRFILE_OTHER | 0x00070000 ) -#define ERRFILE_axtls_aes ( ERRFILE_OTHER | 0x00080000 ) +#define ERRFILE_aes ( ERRFILE_OTHER | 0x00080000 ) #define ERRFILE_cipher ( ERRFILE_OTHER | 0x00090000 ) #define ERRFILE_image_cmd ( ERRFILE_OTHER | 0x000a0000 ) #define ERRFILE_uri_test ( ERRFILE_OTHER | 0x000b0000 ) @@ -308,6 +336,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define ERRFILE_xengrant ( ERRFILE_OTHER | 0x00440000 ) #define ERRFILE_efi_utils ( ERRFILE_OTHER | 0x00450000 ) #define ERRFILE_efi_wrap ( ERRFILE_OTHER | 0x00460000 ) +#define ERRFILE_vmbus ( ERRFILE_OTHER | 0x00470000 ) +#define ERRFILE_efi_time ( ERRFILE_OTHER | 0x00480000 ) +#define ERRFILE_efi_watchdog ( ERRFILE_OTHER | 0x00490000 ) /** @} */ diff --git a/roms/ipxe/src/include/ipxe/errno/efi.h b/roms/ipxe/src/include/ipxe/errno/efi.h index 2d2c50176..9f010f5fb 100644 --- a/roms/ipxe/src/include/ipxe/errno/efi.h +++ b/roms/ipxe/src/include/ipxe/errno/efi.h @@ -21,7 +21,7 @@ * as-is. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/Uefi/UefiBaseType.h> diff --git a/roms/ipxe/src/include/ipxe/errno/linux.h b/roms/ipxe/src/include/ipxe/errno/linux.h index 11309b4ad..99133c816 100644 --- a/roms/ipxe/src/include/ipxe/errno/linux.h +++ b/roms/ipxe/src/include/ipxe/errno/linux.h @@ -10,7 +10,7 @@ * directly as our platform error codes. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Convert platform error code to platform component of iPXE error code diff --git a/roms/ipxe/src/include/ipxe/errortab.h b/roms/ipxe/src/include/ipxe/errortab.h index a2f6a70f5..4fe81a6be 100644 --- a/roms/ipxe/src/include/ipxe/errortab.h +++ b/roms/ipxe/src/include/ipxe/errortab.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/eth_slow.h b/roms/ipxe/src/include/ipxe/eth_slow.h index 00509197d..f6d731b3b 100644 --- a/roms/ipxe/src/include/ipxe/eth_slow.h +++ b/roms/ipxe/src/include/ipxe/eth_slow.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Slow protocols header */ struct eth_slow_header { diff --git a/roms/ipxe/src/include/ipxe/ethernet.h b/roms/ipxe/src/include/ipxe/ethernet.h index d1263d7c3..dd04e00ce 100644 --- a/roms/ipxe/src/include/ipxe/ethernet.h +++ b/roms/ipxe/src/include/ipxe/ethernet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/netdevice.h> diff --git a/roms/ipxe/src/include/ipxe/fakedhcp.h b/roms/ipxe/src/include/ipxe/fakedhcp.h index ea06b06dc..d016b5237 100644 --- a/roms/ipxe/src/include/ipxe/fakedhcp.h +++ b/roms/ipxe/src/include/ipxe/fakedhcp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/fault.h b/roms/ipxe/src/include/ipxe/fault.h new file mode 100644 index 000000000..356296c35 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/fault.h @@ -0,0 +1,53 @@ +#ifndef _IPXE_FAULT_H +#define _IPXE_FAULT_H + +/** @file + * + * Fault injection + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <config/fault.h> + +extern int inject_fault_nonzero ( unsigned int rate ); +extern void inject_corruption_nonzero ( unsigned int rate, const void *data, + size_t len ); + +/** + * Inject fault with a specified probability + * + * @v rate Reciprocal of fault probability (zero for no faults) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +inject_fault ( unsigned int rate ) { + + /* Force dead code elimination in non-fault-injecting builds */ + if ( rate == 0 ) + return 0; + + return inject_fault_nonzero ( rate ); +} + +/** + * Corrupt data with a specified probability + * + * @v rate Reciprocal of fault probability (zero for no faults) + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) void +inject_corruption ( unsigned int rate, const void *data, size_t len ) { + + /* Force dead code elimination in non-fault-injecting builds */ + if ( rate == 0 ) + return; + + return inject_corruption_nonzero ( rate, data, len ); +} + +#endif /* _IPXE_FAULT_H */ diff --git a/roms/ipxe/src/include/ipxe/fbcon.h b/roms/ipxe/src/include/ipxe/fbcon.h index 0538449ac..d442bb918 100644 --- a/roms/ipxe/src/include/ipxe/fbcon.h +++ b/roms/ipxe/src/include/ipxe/fbcon.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/ansiesc.h> diff --git a/roms/ipxe/src/include/ipxe/fc.h b/roms/ipxe/src/include/ipxe/fc.h index 6fdef092d..840d11f62 100644 --- a/roms/ipxe/src/include/ipxe/fc.h +++ b/roms/ipxe/src/include/ipxe/fc.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/fcels.h b/roms/ipxe/src/include/ipxe/fcels.h index 45fa69a4a..02f755115 100644 --- a/roms/ipxe/src/include/ipxe/fcels.h +++ b/roms/ipxe/src/include/ipxe/fcels.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/fc.h> diff --git a/roms/ipxe/src/include/ipxe/fcns.h b/roms/ipxe/src/include/ipxe/fcns.h index e25d9b9d5..9011a7be7 100644 --- a/roms/ipxe/src/include/ipxe/fcns.h +++ b/roms/ipxe/src/include/ipxe/fcns.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/fc.h> diff --git a/roms/ipxe/src/include/ipxe/fcoe.h b/roms/ipxe/src/include/ipxe/fcoe.h index 6ba5b406a..b61e82fea 100644 --- a/roms/ipxe/src/include/ipxe/fcoe.h +++ b/roms/ipxe/src/include/ipxe/fcoe.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/fc.h> diff --git a/roms/ipxe/src/include/ipxe/fcp.h b/roms/ipxe/src/include/ipxe/fcp.h index f6922bc7c..853ca13f6 100644 --- a/roms/ipxe/src/include/ipxe/fcp.h +++ b/roms/ipxe/src/include/ipxe/fcp.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/fc.h> diff --git a/roms/ipxe/src/include/ipxe/features.h b/roms/ipxe/src/include/ipxe/features.h index d8b8b2184..e86a2d226 100644 --- a/roms/ipxe/src/include/ipxe/features.h +++ b/roms/ipxe/src/include/ipxe/features.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @defgroup featurecat Feature categories diff --git a/roms/ipxe/src/include/ipxe/fragment.h b/roms/ipxe/src/include/ipxe/fragment.h index e311ad1e4..0069e5e08 100644 --- a/roms/ipxe/src/include/ipxe/fragment.h +++ b/roms/ipxe/src/include/ipxe/fragment.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/list.h> diff --git a/roms/ipxe/src/include/ipxe/ftp.h b/roms/ipxe/src/include/ipxe/ftp.h index cbab12d2c..3180f1631 100644 --- a/roms/ipxe/src/include/ipxe/ftp.h +++ b/roms/ipxe/src/include/ipxe/ftp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** FTP default port */ #define FTP_PORT 21 diff --git a/roms/ipxe/src/include/ipxe/gdbserial.h b/roms/ipxe/src/include/ipxe/gdbserial.h index a3b56173c..e1040c94e 100644 --- a/roms/ipxe/src/include/ipxe/gdbserial.h +++ b/roms/ipxe/src/include/ipxe/gdbserial.h @@ -7,15 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> struct gdb_transport; -/** - * Set up the serial transport - * - * @ret transport suitable for starting the GDB stub or NULL on error - */ -struct gdb_transport *gdbserial_configure ( void ); +extern struct gdb_transport * gdbserial_configure ( unsigned int port, + unsigned int baud, + uint8_t lcr ); #endif /* _IPXE_GDBSERIAL_H */ diff --git a/roms/ipxe/src/include/ipxe/gdbstub.h b/roms/ipxe/src/include/ipxe/gdbstub.h index 319606747..13ca33ddb 100644 --- a/roms/ipxe/src/include/ipxe/gdbstub.h +++ b/roms/ipxe/src/include/ipxe/gdbstub.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/gdbudp.h b/roms/ipxe/src/include/ipxe/gdbudp.h index db7a451c9..a1c091522 100644 --- a/roms/ipxe/src/include/ipxe/gdbudp.h +++ b/roms/ipxe/src/include/ipxe/gdbudp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct sockaddr_in; struct gdb_transport; diff --git a/roms/ipxe/src/include/ipxe/hash_df.h b/roms/ipxe/src/include/ipxe/hash_df.h index 607a4a610..e57682446 100644 --- a/roms/ipxe/src/include/ipxe/hash_df.h +++ b/roms/ipxe/src/include/ipxe/hash_df.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/include/ipxe/hidemem.h b/roms/ipxe/src/include/ipxe/hidemem.h index ddc9cd8b3..cc8d5ee37 100644 --- a/roms/ipxe/src/include/ipxe/hidemem.h +++ b/roms/ipxe/src/include/ipxe/hidemem.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/hmac.h b/roms/ipxe/src/include/ipxe/hmac.h index d5ec0868d..09d3e273d 100644 --- a/roms/ipxe/src/include/ipxe/hmac.h +++ b/roms/ipxe/src/include/ipxe/hmac.h @@ -6,7 +6,7 @@ * Keyed-Hashing for Message Authentication */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/include/ipxe/hmac_drbg.h b/roms/ipxe/src/include/ipxe/hmac_drbg.h index 8dfd2924f..a0f22da75 100644 --- a/roms/ipxe/src/include/ipxe/hmac_drbg.h +++ b/roms/ipxe/src/include/ipxe/hmac_drbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/include/ipxe/http.h b/roms/ipxe/src/include/ipxe/http.h index cf8c0c7fa..a0dff7d00 100644 --- a/roms/ipxe/src/include/ipxe/http.h +++ b/roms/ipxe/src/include/ipxe/http.h @@ -7,7 +7,26 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/refcnt.h> +#include <ipxe/interface.h> +#include <ipxe/iobuf.h> +#include <ipxe/process.h> +#include <ipxe/retry.h> +#include <ipxe/linebuf.h> +#include <ipxe/pool.h> +#include <ipxe/tables.h> + +struct http_transaction; + +/****************************************************************************** + * + * HTTP URI schemes + * + ****************************************************************************** + */ /** HTTP default port */ #define HTTP_PORT 80 @@ -15,10 +34,469 @@ FILE_LICENCE ( GPL2_OR_LATER ); /** HTTPS default port */ #define HTTPS_PORT 443 -extern int http_open_filter ( struct interface *xfer, struct uri *uri, - unsigned int default_port, - int ( * filter ) ( struct interface *, - const char *, - struct interface ** ) ); +/** An HTTP URI scheme */ +struct http_scheme { + /** Scheme name (e.g. "http" or "https") */ + const char *name; + /** Default port */ + unsigned int port; + /** Transport-layer filter (if any) + * + * @v xfer Data transfer interface + * @v name Host name + * @v next Next interface + * @ret rc Return status code + */ + int ( * filter ) ( struct interface *xfer, const char *name, + struct interface **next ); +}; + +/** HTTP scheme table */ +#define HTTP_SCHEMES __table ( struct http_scheme, "http_schemes" ) + +/** Declare an HTTP scheme */ +#define __http_scheme __table_entry ( HTTP_SCHEMES, 01 ) + +/****************************************************************************** + * + * Connections + * + ****************************************************************************** + */ + +/** An HTTP connection + * + * This represents a potentially reusable connection to an HTTP + * server. + */ +struct http_connection { + /** Reference count */ + struct refcnt refcnt; + /** Connection URI + * + * This encapsulates the server (and protocol) used for the + * connection. This may be the origin server or a proxy + * server. + */ + struct uri *uri; + /** HTTP scheme */ + struct http_scheme *scheme; + /** Transport layer interface */ + struct interface socket; + /** Data transfer interface */ + struct interface xfer; + /** Pooled connection */ + struct pooled_connection pool; +}; + +/****************************************************************************** + * + * HTTP methods + * + ****************************************************************************** + */ + +/** An HTTP method */ +struct http_method { + /** Method name (e.g. "GET" or "POST") */ + const char *name; +}; + +extern struct http_method http_head; +extern struct http_method http_get; +extern struct http_method http_post; + +/****************************************************************************** + * + * Requests + * + ****************************************************************************** + */ + +/** HTTP Digest authentication client nonce count + * + * We choose to generate a new client nonce each time. + */ +#define HTTP_DIGEST_NC "00000001" + +/** HTTP Digest authentication client nonce length + * + * We choose to use a 32-bit hex client nonce. + */ +#define HTTP_DIGEST_CNONCE_LEN 8 + +/** HTTP Digest authentication response length + * + * The Digest authentication response is a Base16-encoded 16-byte MD5 + * checksum. + */ +#define HTTP_DIGEST_RESPONSE_LEN 32 + +/** HTTP request range descriptor */ +struct http_request_range { + /** Range start */ + size_t start; + /** Range length, or zero for no range request */ + size_t len; +}; + +/** HTTP request content descriptor */ +struct http_request_content { + /** Content type (if any) */ + const char *type; + /** Content data (if any) */ + const void *data; + /** Content length */ + size_t len; +}; + +/** HTTP request authentication descriptor */ +struct http_request_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Username */ + const char *username; + /** Password */ + const char *password; + /** Quality of protection */ + const char *qop; + /** Algorithm */ + const char *algorithm; + /** Client nonce */ + char cnonce[ HTTP_DIGEST_CNONCE_LEN + 1 /* NUL */ ]; + /** Response */ + char response[ HTTP_DIGEST_RESPONSE_LEN + 1 /* NUL */ ]; +}; + +/** An HTTP request + * + * This represents a single request to be sent to a server, including + * the values required to construct all headers. + * + * Pointers within this structure must point to storage which is + * guaranteed to remain valid for the lifetime of the containing HTTP + * transaction. + */ +struct http_request { + /** Method */ + struct http_method *method; + /** Request URI string */ + const char *uri; + /** Server host name */ + const char *host; + /** Range descriptor */ + struct http_request_range range; + /** Content descriptor */ + struct http_request_content content; + /** Authentication descriptor */ + struct http_request_auth auth; +}; + +/** An HTTP request header */ +struct http_request_header { + /** Header name (e.g. "User-Agent") */ + const char *name; + /** Construct remaining header line + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Header length if present, or negative error + */ + int ( * format ) ( struct http_transaction *http, char *buf, + size_t len ); +}; + +/** HTTP request header table */ +#define HTTP_REQUEST_HEADERS \ + __table ( struct http_request_header, "http_request_headers" ) + +/** Declare an HTTP request header */ +#define __http_request_header __table_entry ( HTTP_REQUEST_HEADERS, 01 ) + +/****************************************************************************** + * + * Responses + * + ****************************************************************************** + */ + +/** HTTP response transfer descriptor */ +struct http_response_transfer { + /** Transfer encoding */ + struct http_transfer_encoding *encoding; +}; + +/** HTTP response content descriptor */ +struct http_response_content { + /** Content length (may be zero) */ + size_t len; + /** Content encoding */ + struct http_content_encoding *encoding; +}; + +/** HTTP response authorization descriptor */ +struct http_response_auth { + /** Authentication scheme (if any) */ + struct http_authentication *auth; + /** Realm */ + const char *realm; + /** Quality of protection */ + const char *qop; + /** Algorithm */ + const char *algorithm; + /** Nonce */ + const char *nonce; + /** Opaque */ + const char *opaque; +}; + +/** An HTTP response + * + * This represents a single response received from the server, + * including all values parsed from headers. + * + * Pointers within this structure may point into the raw response + * buffer, and so should be invalidated when the response buffer is + * modified or discarded. + */ +struct http_response { + /** Raw response header lines + * + * This is the raw response data received from the server, up + * to and including the terminating empty line. String + * pointers within the response may point into this data + * buffer; NUL terminators will be added (overwriting the + * original terminating characters) as needed. + */ + struct line_buffer headers; + /** Status code + * + * This is the raw HTTP numeric status code (e.g. 404). + */ + unsigned int status; + /** Return status code + * + * This is the iPXE return status code corresponding to the + * HTTP status code (e.g. -ENOENT). + */ + int rc; + /** Redirection location */ + const char *location; + /** Transfer descriptor */ + struct http_response_transfer transfer; + /** Content descriptor */ + struct http_response_content content; + /** Authorization descriptor */ + struct http_response_auth auth; + /** Retry delay (in seconds) */ + unsigned int retry_after; + /** Flags */ + unsigned int flags; +}; + +/** HTTP response flags */ +enum http_response_flags { + /** Keep connection alive after close */ + HTTP_RESPONSE_KEEPALIVE = 0x0001, + /** Content length specified */ + HTTP_RESPONSE_CONTENT_LEN = 0x0002, + /** Transaction may be retried on failure */ + HTTP_RESPONSE_RETRY = 0x0004, +}; + +/** An HTTP response header */ +struct http_response_header { + /** Header name (e.g. "Transfer-Encoding") */ + const char *name; + /** Parse header line + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ + int ( * parse ) ( struct http_transaction *http, char *line ); +}; + +/** HTTP response header table */ +#define HTTP_RESPONSE_HEADERS \ + __table ( struct http_response_header, "http_response_headers" ) + +/** Declare an HTTP response header */ +#define __http_response_header __table_entry ( HTTP_RESPONSE_HEADERS, 01 ) + +/****************************************************************************** + * + * Transactions + * + ****************************************************************************** + */ + +/** HTTP transaction state */ +struct http_state { + /** Transmit data + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * tx ) ( struct http_transaction *http ); + /** Receive data + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ + int ( * rx ) ( struct http_transaction *http, + struct io_buffer **iobuf ); + /** Server connection closed + * + * @v http HTTP transaction + * @v rc Reason for close + */ + void ( * close ) ( struct http_transaction *http, int rc ); +}; + +/** An HTTP transaction */ +struct http_transaction { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Content-decoded interface */ + struct interface content; + /** Transfer-decoded interface */ + struct interface transfer; + /** Server connection */ + struct interface conn; + /** Transmit process */ + struct process process; + /** Reconnection timer */ + struct retry_timer timer; + + /** Request URI */ + struct uri *uri; + /** Request */ + struct http_request request; + /** Response */ + struct http_response response; + /** Temporary line buffer */ + struct line_buffer linebuf; + + /** Transaction state */ + struct http_state *state; + /** Accumulated transfer-decoded length */ + size_t len; + /** Chunk length remaining */ + size_t remaining; +}; + +/****************************************************************************** + * + * Transfer encoding + * + ****************************************************************************** + */ + +/** An HTTP transfer encoding */ +struct http_transfer_encoding { + /** Name */ + const char *name; + /** Initialise transfer encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * init ) ( struct http_transaction *http ); + /** Receive data state */ + struct http_state state; +}; + +/** HTTP transfer encoding table */ +#define HTTP_TRANSFER_ENCODINGS \ + __table ( struct http_transfer_encoding, "http_transfer_encodings" ) + +/** Declare an HTTP transfer encoding */ +#define __http_transfer_encoding __table_entry ( HTTP_TRANSFER_ENCODINGS, 01 ) + +/****************************************************************************** + * + * Content encoding + * + ****************************************************************************** + */ + +/** An HTTP content encoding */ +struct http_content_encoding { + /** Name */ + const char *name; + /** Check if content encoding is supported for this request + * + * @v http HTTP transaction + * @ret supported Content encoding is supported for this request + */ + int ( * supported ) ( struct http_transaction *http ); + /** Initialise content encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * init ) ( struct http_transaction *http ); +}; + +/** HTTP content encoding table */ +#define HTTP_CONTENT_ENCODINGS \ + __table ( struct http_content_encoding, "http_content_encodings" ) + +/** Declare an HTTP content encoding */ +#define __http_content_encoding __table_entry ( HTTP_CONTENT_ENCODINGS, 01 ) + +/****************************************************************************** + * + * Authentication + * + ****************************************************************************** + */ + +/** An HTTP authentication scheme */ +struct http_authentication { + /** Name (e.g. "Digest") */ + const char *name; + /** Perform authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ + int ( * authenticate ) ( struct http_transaction *http ); + /** Construct remaining "Authorization" header line + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Header length if present, or negative error + */ + int ( * format ) ( struct http_transaction *http, char *buf, + size_t len ); +}; + +/** HTTP authentication scheme table */ +#define HTTP_AUTHENTICATIONS \ + __table ( struct http_authentication, "http_authentications" ) + +/** Declare an HTTP authentication scheme */ +#define __http_authentication __table_entry ( HTTP_AUTHENTICATIONS, 01 ) + +/****************************************************************************** + * + * General + * + ****************************************************************************** + */ + +extern char * http_token ( char **line, char **value ); +extern int http_connect ( struct interface *xfer, struct uri *uri ); +extern int http_open ( struct interface *xfer, struct http_method *method, + struct uri *uri, struct http_request_range *range, + struct http_request_content *content ); +extern int http_open_uri ( struct interface *xfer, struct uri *uri ); #endif /* _IPXE_HTTP_H */ diff --git a/roms/ipxe/src/include/ipxe/hyperv.h b/roms/ipxe/src/include/ipxe/hyperv.h new file mode 100644 index 000000000..c61e2a083 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/hyperv.h @@ -0,0 +1,232 @@ +#ifndef _IPXE_HYPERV_H +#define _IPXE_HYPERV_H + +/** @file + * + * Hyper-V interface + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/io.h> + +/** Hyper-V interface identification */ +#define HV_INTERFACE_ID 0x31237648 /* "Hv#1" */ + +/** Guest OS identity for iPXE + * + * This field comprises: + * + * Bit 63 : set to 1 to indicate an open source OS + * Bits 62:56 : OS Type + * Bits 55:48 : OS ID + * Bits 47:16 : Version + * Bits 15:0 : Build number + * + * There appears to be no central registry for the "OS Type". The + * specification states that "Linux is 0x100", and the FreeBSD source + * states that "FreeBSD is 0x200". Both of these statements are + * actually referring to the combined "OS Type" and "OS ID" field. + * + * We choose to use 0x98ae: this is generated by setting bit 63 (to + * indicate an open source OS) and setting the OS Type+ID equal to the + * PnP vendor ID used in romprefix.S. No version information or build + * number is included. + */ +#define HV_GUEST_OS_ID_IPXE ( ( 1ULL << 63 ) | ( 0x18aeULL << 48 ) ) + +/** Enable hypercall page */ +#define HV_HYPERCALL_ENABLE 0x00000001UL + +/** Enable SynIC */ +#define HV_SCONTROL_ENABLE 0x00000001UL + +/** Enable SynIC event flags */ +#define HV_SIEFP_ENABLE 0x00000001UL + +/** Enable SynIC messages */ +#define HV_SIMP_ENABLE 0x00000001UL + +/** Perform implicit EOI upon synthetic interrupt delivery */ +#define HV_SINT_AUTO_EOI 0x00020000UL + +/** Mask synthetic interrupt */ +#define HV_SINT_MASKED 0x00010000UL + +/** Synthetic interrupt vector */ +#define HV_SINT_VECTOR(x) ( (x) << 0 ) + +/** Synthetic interrupt vector mask */ +#define HV_SINT_VECTOR_MASK HV_SINT_VECTOR ( 0xff ) + +/** Post message */ +#define HV_POST_MESSAGE 0x005c + +/** A posted message + * + * This is the input parameter list for the HvPostMessage hypercall. + */ +struct hv_post_message { + /** Connection ID */ + uint32_t id; + /** Padding */ + uint32_t reserved; + /** Type */ + uint32_t type; + /** Length of message */ + uint32_t len; + /** Message */ + uint8_t data[240]; +} __attribute__ (( packed )); + +/** A received message + * + * This is the HV_MESSAGE structure from the Hypervisor Top-Level + * Functional Specification. The field order given in the + * documentation is incorrect. + */ +struct hv_message { + /** Type */ + uint32_t type; + /** Length of message */ + uint8_t len; + /** Flags */ + uint8_t flags; + /** Padding */ + uint16_t reserved; + /** Origin */ + uint64_t origin; + /** Message */ + uint8_t data[240]; +} __attribute__ (( packed )); + +/** Signal event */ +#define HV_SIGNAL_EVENT 0x005d + +/** A signalled event */ +struct hv_signal_event { + /** Connection ID */ + uint32_t id; + /** Flag number */ + uint16_t flag; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A received event */ +struct hv_event { + /** Event flags */ + uint8_t flags[256]; +} __attribute__ (( packed )); + +/** A monitor trigger group + * + * This is the HV_MONITOR_TRIGGER_GROUP structure from the Hypervisor + * Top-Level Functional Specification. + */ +struct hv_monitor_trigger { + /** Pending events */ + uint32_t pending; + /** Armed events */ + uint32_t armed; +} __attribute__ (( packed )); + +/** A monitor parameter set + * + * This is the HV_MONITOR_PARAMETER structure from the Hypervisor + * Top-Level Functional Specification. + */ +struct hv_monitor_parameter { + /** Connection ID */ + uint32_t id; + /** Flag number */ + uint16_t flag; + /** Reserved */ + uint16_t reserved; +} __attribute__ (( packed )); + +/** A monitor page + * + * This is the HV_MONITOR_PAGE structure from the Hypervisor Top-Level + * Functional Specification. + */ +struct hv_monitor { + /** Flags */ + uint32_t flags; + /** Reserved */ + uint8_t reserved_a[4]; + /** Trigger groups */ + struct hv_monitor_trigger trigger[4]; + /** Reserved */ + uint8_t reserved_b[536]; + /** Latencies */ + uint16 latency[4][32]; + /** Reserved */ + uint8_t reserved_c[256]; + /** Parameters */ + struct hv_monitor_parameter param[4][32]; + /** Reserved */ + uint8_t reserved_d[1984]; +} __attribute__ (( packed )); + +/** A synthetic interrupt controller */ +struct hv_synic { + /** Message page */ + struct hv_message *message; + /** Event flag page */ + struct hv_event *event; +}; + +/** A message buffer */ +union hv_message_buffer { + /** Posted message */ + struct hv_post_message posted; + /** Received message */ + struct hv_message received; + /** Signalled event */ + struct hv_signal_event signalled; +}; + +/** A Hyper-V hypervisor */ +struct hv_hypervisor { + /** Hypercall page */ + void *hypercall; + /** Synthetic interrupt controller (SynIC) */ + struct hv_synic synic; + /** Message buffer */ + union hv_message_buffer *message; + /** Virtual machine bus */ + struct vmbus *vmbus; +}; + +#include <bits/hyperv.h> + +/** + * Calculate the number of pages covering an address range + * + * @v data Start of data + * @v len Length of data (must be non-zero) + * @ret pfn_count Number of pages covered + */ +static inline unsigned int hv_pfn_count ( physaddr_t data, size_t len ) { + unsigned int first_pfn = ( data / PAGE_SIZE ); + unsigned int last_pfn = ( ( data + len - 1 ) / PAGE_SIZE ); + + return ( last_pfn - first_pfn + 1 ); +} + +extern __attribute__ (( sentinel )) int +hv_alloc_pages ( struct hv_hypervisor *hv, ... ); +extern __attribute__ (( sentinel )) void +hv_free_pages ( struct hv_hypervisor *hv, ... ); +extern void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ); +extern void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ); +extern int hv_post_message ( struct hv_hypervisor *hv, unsigned int id, + unsigned int type, const void *data, size_t len ); +extern int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ); +extern int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id, + unsigned int flag ); + +#endif /* _IPXE_HYPERV_H */ diff --git a/roms/ipxe/src/include/ipxe/i2c.h b/roms/ipxe/src/include/ipxe/i2c.h index c1f5a9bbd..46970515c 100644 --- a/roms/ipxe/src/include/ipxe/i2c.h +++ b/roms/ipxe/src/include/ipxe/i2c.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/bitbash.h> diff --git a/roms/ipxe/src/include/ipxe/ib_cm.h b/roms/ipxe/src/include/ipxe/ib_cm.h index 7d08cd9b1..4913eebae 100644 --- a/roms/ipxe/src/include/ipxe/ib_cm.h +++ b/roms/ipxe/src/include/ipxe/ib_cm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/infiniband.h> #include <ipxe/retry.h> diff --git a/roms/ipxe/src/include/ipxe/ib_mad.h b/roms/ipxe/src/include/ipxe/ib_mad.h index b8694833e..ae1eea7e4 100644 --- a/roms/ipxe/src/include/ipxe/ib_mad.h +++ b/roms/ipxe/src/include/ipxe/ib_mad.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/ib_packet.h> diff --git a/roms/ipxe/src/include/ipxe/ib_mcast.h b/roms/ipxe/src/include/ipxe/ib_mcast.h index a5c22a03e..564066975 100644 --- a/roms/ipxe/src/include/ipxe/ib_mcast.h +++ b/roms/ipxe/src/include/ipxe/ib_mcast.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/infiniband.h> diff --git a/roms/ipxe/src/include/ipxe/ib_mi.h b/roms/ipxe/src/include/ipxe/ib_mi.h index 5c5415b71..c7c8143ba 100644 --- a/roms/ipxe/src/include/ipxe/ib_mi.h +++ b/roms/ipxe/src/include/ipxe/ib_mi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> #include <ipxe/retry.h> diff --git a/roms/ipxe/src/include/ipxe/ib_packet.h b/roms/ipxe/src/include/ipxe/ib_packet.h index a959967cb..f275fcb09 100644 --- a/roms/ipxe/src/include/ipxe/ib_packet.h +++ b/roms/ipxe/src/include/ipxe/ib_packet.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ib_device; struct ib_queue_pair; diff --git a/roms/ipxe/src/include/ipxe/ib_pathrec.h b/roms/ipxe/src/include/ipxe/ib_pathrec.h index 1fe67f87d..a4e11ebe3 100644 --- a/roms/ipxe/src/include/ipxe/ib_pathrec.h +++ b/roms/ipxe/src/include/ipxe/ib_pathrec.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/infiniband.h> diff --git a/roms/ipxe/src/include/ipxe/ib_sma.h b/roms/ipxe/src/include/ipxe/ib_sma.h index fa355c652..74003d045 100644 --- a/roms/ipxe/src/include/ipxe/ib_sma.h +++ b/roms/ipxe/src/include/ipxe/ib_sma.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct ib_device; struct ib_mad_interface; diff --git a/roms/ipxe/src/include/ipxe/ib_smc.h b/roms/ipxe/src/include/ipxe/ib_smc.h index 259d2cde1..f9b96b1bd 100644 --- a/roms/ipxe/src/include/ipxe/ib_smc.h +++ b/roms/ipxe/src/include/ipxe/ib_smc.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/infiniband.h> diff --git a/roms/ipxe/src/include/ipxe/icmp.h b/roms/ipxe/src/include/ipxe/icmp.h index 0480ddfaf..803f8e019 100644 --- a/roms/ipxe/src/include/ipxe/icmp.h +++ b/roms/ipxe/src/include/ipxe/icmp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/iobuf.h> diff --git a/roms/ipxe/src/include/ipxe/icmpv6.h b/roms/ipxe/src/include/ipxe/icmpv6.h index b5ea54eab..0474ddca8 100644 --- a/roms/ipxe/src/include/ipxe/icmpv6.h +++ b/roms/ipxe/src/include/ipxe/icmpv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/tables.h> @@ -40,6 +40,18 @@ struct icmpv6_handler { /** Declare an ICMPv6 handler */ #define __icmpv6_handler __table_entry ( ICMPV6_HANDLERS, 01 ) +/** ICMPv6 destination unreachable */ +#define ICMPV6_DESTINATION_UNREACHABLE 1 + +/** ICMPv6 packet too big */ +#define ICMPV6_PACKET_TOO_BIG 2 + +/** ICMPv6 time exceeded */ +#define ICMPV6_TIME_EXCEEDED 3 + +/** ICMPv6 parameter problem */ +#define ICMPV6_PARAMETER_PROBLEM 4 + /** ICMPv6 echo request */ #define ICMPV6_ECHO_REQUEST 128 diff --git a/roms/ipxe/src/include/ipxe/if_arp.h b/roms/ipxe/src/include/ipxe/if_arp.h index fd36e9c67..4eb1f80b7 100644 --- a/roms/ipxe/src/include/ipxe/if_arp.h +++ b/roms/ipxe/src/include/ipxe/if_arp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/if_ether.h b/roms/ipxe/src/include/ipxe/if_ether.h index a7e237349..58d91b976 100644 --- a/roms/ipxe/src/include/ipxe/if_ether.h +++ b/roms/ipxe/src/include/ipxe/if_ether.h @@ -1,7 +1,7 @@ #ifndef _IPXE_IF_ETHER_H #define _IPXE_IF_ETHER_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/image.h b/roms/ipxe/src/include/ipxe/image.h index 5d7080a75..6abd7a2d2 100644 --- a/roms/ipxe/src/include/ipxe/image.h +++ b/roms/ipxe/src/include/ipxe/image.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> #include <ipxe/list.h> @@ -163,7 +163,6 @@ extern int image_set_cmdline ( struct image *image, const char *cmdline ); extern int register_image ( struct image *image ); extern void unregister_image ( struct image *image ); struct image * find_image ( const char *name ); -extern int image_probe ( struct image *image ); extern int image_exec ( struct image *image ); extern int image_replace ( struct image *replacement ); extern int image_select ( struct image *image ); diff --git a/roms/ipxe/src/include/ipxe/in.h b/roms/ipxe/src/include/ipxe/in.h index de96ca22a..0ebf441c2 100644 --- a/roms/ipxe/src/include/ipxe/in.h +++ b/roms/ipxe/src/include/ipxe/in.h @@ -1,9 +1,10 @@ #ifndef _IPXE_IN_H #define _IPXE_IN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> +#include <byteswap.h> #include <ipxe/socket.h> /* Protocol numbers */ @@ -15,17 +16,22 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* IP address constants */ -#define INADDR_NONE 0xffffffff +#define INADDR_NONE htonl ( 0xffffffff ) -#define INADDR_BROADCAST 0xffffffff +#define INADDR_BROADCAST htonl ( 0xffffffff ) -#define IN_CLASSA(addr) ( ( (addr) & 0x80000000 ) == 0x00000000 ) -#define IN_CLASSA_NET 0xff000000 -#define IN_CLASSB(addr) ( ( (addr) & 0xc0000000 ) == 0x80000000 ) -#define IN_CLASSB_NET 0xffff0000 -#define IN_CLASSC(addr) ( ( (addr) & 0xe0000000 ) == 0xc0000000 ) -#define IN_CLASSC_NET 0xffffff00 -#define IN_MULTICAST(addr) ( ( (addr) & 0xf0000000 ) == 0xe0000000 ) +#define INADDR_NET_CLASSA htonl ( 0xff000000 ) +#define INADDR_NET_CLASSB htonl ( 0xffff0000 ) +#define INADDR_NET_CLASSC htonl ( 0xffffff00 ) + +#define IN_IS_CLASSA( addr ) \ + ( ( (addr) & htonl ( 0x80000000 ) ) == htonl ( 0x00000000 ) ) +#define IN_IS_CLASSB( addr ) \ + ( ( (addr) & htonl ( 0xc0000000 ) ) == htonl ( 0x80000000 ) ) +#define IN_IS_CLASSC( addr ) \ + ( ( (addr) & htonl ( 0xe0000000 ) ) == htonl ( 0xc0000000 ) ) +#define IN_IS_MULTICAST( addr ) \ + ( ( (addr) & htonl ( 0xf0000000 ) ) == htonl ( 0xe0000000 ) ) /** * IP address structure @@ -63,6 +69,9 @@ struct in6_addr { ( ( *( ( const uint16_t * ) (addr) ) & htons ( 0xffc0 ) ) == \ htons ( 0xfe80 ) ) +#define IN6_IS_ADDR_NONGLOBAL( addr ) \ + ( IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MULTICAST (addr) ) + /** * IPv4 socket address */ @@ -76,6 +85,11 @@ struct sockaddr_in { uint16_t sin_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin_port; + /** Scope ID (part of struct @c sockaddr_tcpip) + * + * For multicast addresses, this is the network device index. + */ + uint16_t sin_scope_id; /** IPv4 address */ struct in_addr sin_addr; /** Padding @@ -87,6 +101,7 @@ struct sockaddr_in { ( sizeof ( sa_family_t ) /* sin_family */ + sizeof ( uint16_t ) /* sin_flags */ + sizeof ( uint16_t ) /* sin_port */ + + sizeof ( uint16_t ) /* sin_scope_id */ + sizeof ( struct in_addr ) /* sin_addr */ ) ]; } __attribute__ (( packed, may_alias )); @@ -103,9 +118,10 @@ struct sockaddr_in6 { uint16_t sin6_flags; /** TCP/IP port (part of struct @c sockaddr_tcpip) */ uint16_t sin6_port; - /** Scope ID + /** Scope ID (part of struct @c sockaddr_tcpip) * - * For link-local addresses, this is the network device index. + * For link-local or multicast addresses, this is the network + * device index. */ uint16_t sin6_scope_id; /** IPv6 address */ diff --git a/roms/ipxe/src/include/ipxe/infiniband.h b/roms/ipxe/src/include/ipxe/infiniband.h index f546ea61b..87cfe5082 100644 --- a/roms/ipxe/src/include/ipxe/infiniband.h +++ b/roms/ipxe/src/include/ipxe/infiniband.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/init.h b/roms/ipxe/src/include/ipxe/init.h index 19c5925bf..025cfaf37 100644 --- a/roms/ipxe/src/include/ipxe/init.h +++ b/roms/ipxe/src/include/ipxe/init.h @@ -1,7 +1,7 @@ #ifndef _IPXE_INIT_H #define _IPXE_INIT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> @@ -26,10 +26,9 @@ struct init_fn { */ #define INIT_EARLY 01 /**< Early initialisation */ -#define INIT_SERIAL 02 /**< Serial driver initialisation */ -#define INIT_CONSOLE 03 /**< Console initialisation */ -#define INIT_NORMAL 04 /**< Normal initialisation */ -#define INIT_LATE 05 /**< Late initialisation */ +#define INIT_CONSOLE 02 /**< Console initialisation */ +#define INIT_NORMAL 03 /**< Normal initialisation */ +#define INIT_LATE 04 /**< Late initialisation */ /** @} */ diff --git a/roms/ipxe/src/include/ipxe/interface.h b/roms/ipxe/src/include/ipxe/interface.h index a474aaad0..a8d823775 100644 --- a/roms/ipxe/src/include/ipxe/interface.h +++ b/roms/ipxe/src/include/ipxe/interface.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/refcnt.h> @@ -145,6 +145,11 @@ extern void intf_close ( struct interface *intf, int rc ); extern void intf_shutdown ( struct interface *intf, int rc ); extern void intf_restart ( struct interface *intf, int rc ); +extern void intf_poke ( struct interface *intf, + void ( type ) ( struct interface *intf ) ); +#define intf_poke_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + extern struct interface_descriptor null_intf_desc; extern struct interface null_intf; diff --git a/roms/ipxe/src/include/ipxe/io.h b/roms/ipxe/src/include/ipxe/io.h index 29ccfd1fa..af767915d 100644 --- a/roms/ipxe/src/include/ipxe/io.h +++ b/roms/ipxe/src/include/ipxe/io.h @@ -16,7 +16,7 @@ * the address parameter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/api.h> diff --git a/roms/ipxe/src/include/ipxe/iobuf.h b/roms/ipxe/src/include/ipxe/iobuf.h index b2b0cb440..27d285d44 100644 --- a/roms/ipxe/src/include/ipxe/iobuf.h +++ b/roms/ipxe/src/include/ipxe/iobuf.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <assert.h> @@ -217,5 +217,6 @@ extern void free_iob ( struct io_buffer *iobuf ); extern void iob_pad ( struct io_buffer *iobuf, size_t min_len ); extern int iob_ensure_headroom ( struct io_buffer *iobuf, size_t len ); extern struct io_buffer * iob_concatenate ( struct list_head *list ); +extern struct io_buffer * iob_split ( struct io_buffer *iobuf, size_t len ); #endif /* _IPXE_IOBUF_H */ diff --git a/roms/ipxe/src/include/ipxe/ip.h b/roms/ipxe/src/include/ipxe/ip.h index 1a93a552e..285be6dcd 100644 --- a/roms/ipxe/src/include/ipxe/ip.h +++ b/roms/ipxe/src/include/ipxe/ip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/in.h> diff --git a/roms/ipxe/src/include/ipxe/ipoib.h b/roms/ipxe/src/include/ipxe/ipoib.h index 68ff8df49..b34dd32d0 100644 --- a/roms/ipxe/src/include/ipxe/ipoib.h +++ b/roms/ipxe/src/include/ipxe/ipoib.h @@ -6,7 +6,7 @@ * IP over Infiniband */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/if_arp.h> #include <ipxe/infiniband.h> diff --git a/roms/ipxe/src/include/ipxe/ipstat.h b/roms/ipxe/src/include/ipxe/ipstat.h index c554c1859..b34ed5fcf 100644 --- a/roms/ipxe/src/include/ipxe/ipstat.h +++ b/roms/ipxe/src/include/ipxe/ipstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/ipv6.h b/roms/ipxe/src/include/ipxe/ipv6.h index 48aaf677e..b500382c1 100644 --- a/roms/ipxe/src/include/ipxe/ipv6.h +++ b/roms/ipxe/src/include/ipxe/ipv6.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/include/ipxe/isa_ids.h b/roms/ipxe/src/include/ipxe/isa_ids.h index 1faf1148d..d815bda34 100644 --- a/roms/ipxe/src/include/ipxe/isa_ids.h +++ b/roms/ipxe/src/include/ipxe/isa_ids.h @@ -19,7 +19,7 @@ * the underlying "meaning" is big-endian. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <byteswap.h> diff --git a/roms/ipxe/src/include/ipxe/isapnp.h b/roms/ipxe/src/include/ipxe/isapnp.h index b0b0e98d6..59beac986 100644 --- a/roms/ipxe/src/include/ipxe/isapnp.h +++ b/roms/ipxe/src/include/ipxe/isapnp.h @@ -17,6 +17,10 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. * * Portions of this code: * Copyright (C) 2001 P.J.H.Fox (fox@roestock.demon.co.uk) @@ -34,7 +38,7 @@ * ***************************************************************************/ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef ISAPNP_H #define ISAPNP_H diff --git a/roms/ipxe/src/include/ipxe/iscsi.h b/roms/ipxe/src/include/ipxe/iscsi.h index be71360a0..c75ff4188 100644 --- a/roms/ipxe/src/include/ipxe/iscsi.h +++ b/roms/ipxe/src/include/ipxe/iscsi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/socket.h> diff --git a/roms/ipxe/src/include/ipxe/iso9660.h b/roms/ipxe/src/include/ipxe/iso9660.h index 02c2ae377..34cb8f0a1 100644 --- a/roms/ipxe/src/include/ipxe/iso9660.h +++ b/roms/ipxe/src/include/ipxe/iso9660.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/isqrt.h b/roms/ipxe/src/include/ipxe/isqrt.h index 58ed42f0c..68255d1bc 100644 --- a/roms/ipxe/src/include/ipxe/isqrt.h +++ b/roms/ipxe/src/include/ipxe/isqrt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern unsigned long isqrt ( unsigned long value ); diff --git a/roms/ipxe/src/include/ipxe/job.h b/roms/ipxe/src/include/ipxe/job.h index a2369f7c2..7e1bd8109 100644 --- a/roms/ipxe/src/include/ipxe/job.h +++ b/roms/ipxe/src/include/ipxe/job.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/interface.h> diff --git a/roms/ipxe/src/include/ipxe/jumpscroll.h b/roms/ipxe/src/include/ipxe/jumpscroll.h new file mode 100644 index 000000000..7a5b111c1 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/jumpscroll.h @@ -0,0 +1,50 @@ +#ifndef _IPXE_JUMPSCROLL_H +#define _IPXE_JUMPSCROLL_H + +/** @file + * + * Jump scrolling + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** A jump scroller */ +struct jump_scroller { + /** Maximum number of visible rows */ + unsigned int rows; + /** Total number of items */ + unsigned int count; + /** Currently selected item */ + unsigned int current; + /** First visible item */ + unsigned int first; +}; + +/** + * Check if jump scroller is currently on first page + * + * @v scroll Jump scroller + * @ret is_first Scroller is currently on first page + */ +static inline int jump_scroll_is_first ( struct jump_scroller *scroll ) { + + return ( scroll->first == 0 ); +} + +/** + * Check if jump scroller is currently on last page + * + * @v scroll Jump scroller + * @ret is_last Scroller is currently on last page + */ +static inline int jump_scroll_is_last ( struct jump_scroller *scroll ) { + + return ( ( scroll->first + scroll->rows ) >= scroll->count ); +} + +extern int jump_scroll_key ( struct jump_scroller *scroll, int key ); +extern int jump_scroll_move ( struct jump_scroller *scroll, int move ); +extern int jump_scroll ( struct jump_scroller *scroll ); + +#endif /* _IPXE_JUMPSCROLL_H */ diff --git a/roms/ipxe/src/include/ipxe/keymap.h b/roms/ipxe/src/include/ipxe/keymap.h index 9ac42a6b1..0f1b0c656 100644 --- a/roms/ipxe/src/include/ipxe/keymap.h +++ b/roms/ipxe/src/include/ipxe/keymap.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/keys.h b/roms/ipxe/src/include/ipxe/keys.h index 8b13550b9..d15267a1f 100644 --- a/roms/ipxe/src/include/ipxe/keys.h +++ b/roms/ipxe/src/include/ipxe/keys.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Symbolic names for some standard ASCII characters @@ -58,6 +58,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ #define KEY_ANSI( n, terminator ) ( 0x100 * ( (n) + 1 ) + (terminator) ) +#define KEY_ANSI_N( key ) ( ( (key) / 0x100 ) - 1 ) +#define KEY_ANSI_TERMINATOR( key ) ( (key) & 0xff ) #define KEY_MIN 0x101 #define KEY_UP KEY_ANSI ( 0, 'A' ) /**< Up arrow */ diff --git a/roms/ipxe/src/include/ipxe/linebuf.h b/roms/ipxe/src/include/ipxe/linebuf.h index 706ef2554..630278a04 100644 --- a/roms/ipxe/src/include/ipxe/linebuf.h +++ b/roms/ipxe/src/include/ipxe/linebuf.h @@ -7,24 +7,24 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> /** A line buffer */ struct line_buffer { - /** Current string in the buffer */ + /** Data buffer */ char *data; - /** Length of current string, excluding the terminating NUL */ + /** Length of buffered data */ size_t len; - /** String is ready to read */ - int ready; + /** Most recently consumed length */ + size_t consumed; }; extern char * buffered_line ( struct line_buffer *linebuf ); -extern ssize_t line_buffer ( struct line_buffer *linebuf, - const char *data, size_t len ); +extern int line_buffer ( struct line_buffer *linebuf, + const char *data, size_t len ); extern void empty_line_buffer ( struct line_buffer *linebuf ); #endif /* _IPXE_LINEBUF_H */ diff --git a/roms/ipxe/src/include/ipxe/lineconsole.h b/roms/ipxe/src/include/ipxe/lineconsole.h index 925c0accc..31117e73c 100644 --- a/roms/ipxe/src/include/ipxe/lineconsole.h +++ b/roms/ipxe/src/include/ipxe/lineconsole.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/ansiesc.h> diff --git a/roms/ipxe/src/include/ipxe/linux/linux_entropy.h b/roms/ipxe/src/include/ipxe/linux/linux_entropy.h index bd89bd52f..afef6fe19 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_entropy.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_entropy.h @@ -3,11 +3,11 @@ /** @file * - * iPXE entropy API for linux + * /dev/random-based entropy source * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef ENTROPY_LINUX #define ENTROPY_PREFIX_linux @@ -23,10 +23,12 @@ FILE_LICENCE(GPL2_OR_LATER); static inline __always_inline double ENTROPY_INLINE ( linux, min_entropy_per_sample ) ( void ) { - /* We read single bytes from /dev/random and assume that each - * contains full entropy. + /* linux_get_noise() reads a single byte from /dev/random, + * which is supposed to block until a sufficient amount of + * entropy is available. We therefore assume that each sample + * contains exactly 8 bits of entropy. */ - return 8; + return 8.0; } #endif /* _IPXE_LINUX_ENTROPY_H */ diff --git a/roms/ipxe/src/include/ipxe/linux/linux_nap.h b/roms/ipxe/src/include/ipxe/linux/linux_nap.h index 5bac7242f..d072886c7 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_nap.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_LINUX #define NAP_PREFIX_linux diff --git a/roms/ipxe/src/include/ipxe/linux/linux_pci.h b/roms/ipxe/src/include/ipxe/linux/linux_pci.h index 439166733..22ae7f1bc 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_pci.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_pci.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef PCIAPI_LINUX #define PCIAPI_PREFIX_linux diff --git a/roms/ipxe/src/include/ipxe/linux/linux_smbios.h b/roms/ipxe/src/include/ipxe/linux/linux_smbios.h index 6d51e13ba..16c6d8acd 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_smbios.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_smbios.h @@ -3,11 +3,11 @@ /** @file * - * iPXE SMBIOS API for linux + * iPXE SMBIOS API for Linux * */ -FILE_LICENCE(GPL2_OR_LATER); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SMBIOS_LINUX #define SMBIOS_PREFIX_linux diff --git a/roms/ipxe/src/include/ipxe/linux/linux_time.h b/roms/ipxe/src/include/ipxe/linux/linux_time.h index 93a257730..872ef5ade 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_time.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_LINUX #define TIME_PREFIX_linux diff --git a/roms/ipxe/src/include/ipxe/linux/linux_timer.h b/roms/ipxe/src/include/ipxe/linux/linux_timer.h index 379507417..7f46e36b2 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_timer.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_timer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIMER_LINUX #define TIMER_PREFIX_linux diff --git a/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h b/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h index e4d16d9e0..acd919a85 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_uaccess.h @@ -1,116 +1,108 @@ -/* - * Copyright (C) 2010 Piotr JaroszyÅ„ski <p.jaroszynski@gmail.com> - * - * 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 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - */ - #ifndef _IPXE_LINUX_UACCESS_H #define _IPXE_LINUX_UACCESS_H -FILE_LICENCE(GPL2_OR_LATER); - /** @file * - * iPXE user access API for linux + * iPXE user access API for Linux + * + * We run with no distinction between internal and external addresses, + * so can use trivial_virt_to_user() et al. * - * In linux userspace virtual == user == phys addresses. - * Physical addresses also being the same is wrong, but there is no general way - * of converting userspace addresses to physical as what appears to be - * contiguous in userspace is physically fragmented. - * Currently only the DMA memory is special-cased, but its conversion to bus - * addresses is done in phys_to_bus. - * This is known to break virtio as it is passing phys addresses to the virtual - * device. + * We have no concept of the underlying physical addresses, since + * these are not exposed to userspace. We provide a stub + * implementation of user_to_phys() since this is required by + * alloc_memblock(). We provide no implementation of phys_to_user(); + * any code attempting to access physical addresses will therefore + * (correctly) fail to link. */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #ifdef UACCESS_LINUX #define UACCESS_PREFIX_linux #else #define UACCESS_PREFIX_linux __linux_ #endif -static inline __always_inline userptr_t -UACCESS_INLINE(linux, phys_to_user)(unsigned long phys_addr) -{ - return phys_addr; -} - +/** + * Convert user buffer to physical address + * + * @v userptr User pointer + * @v offset Offset from user pointer + * @ret phys_addr Physical address + */ static inline __always_inline unsigned long -UACCESS_INLINE(linux, user_to_phys)(userptr_t userptr, off_t offset) -{ - return userptr + offset; +UACCESS_INLINE ( linux, user_to_phys ) ( userptr_t userptr, off_t offset ) { + + /* We do not know the real underlying physical address. We + * provide this stub implementation only because it is + * required by alloc_memblock() (which allocates memory with + * specified physical address alignment). We assume that the + * low-order bits of virtual addresses match the low-order + * bits of physical addresses, and so simply returning the + * virtual address will suffice for the purpose of determining + * alignment. + */ + return ( userptr + offset ); } static inline __always_inline userptr_t -UACCESS_INLINE(linux, virt_to_user)(volatile const void *addr) -{ - return trivial_virt_to_user(addr); +UACCESS_INLINE ( linux, virt_to_user ) ( volatile const void *addr ) { + return trivial_virt_to_user ( addr ); } static inline __always_inline void * -UACCESS_INLINE(linux, user_to_virt)(userptr_t userptr, off_t offset) -{ - return trivial_user_to_virt(userptr, offset); +UACCESS_INLINE ( linux, user_to_virt ) ( userptr_t userptr, off_t offset ) { + return trivial_user_to_virt ( userptr, offset ); } static inline __always_inline userptr_t -UACCESS_INLINE(linux, userptr_add)(userptr_t userptr, off_t offset) -{ - return trivial_userptr_add(userptr, offset); +UACCESS_INLINE ( linux, userptr_add ) ( userptr_t userptr, off_t offset ) { + return trivial_userptr_add ( userptr, offset ); } static inline __always_inline off_t -UACCESS_INLINE(linux, userptr_sub)(userptr_t userptr, userptr_t subtrahend) -{ +UACCESS_INLINE ( linux, userptr_sub ) ( userptr_t userptr, + userptr_t subtrahend ) { return trivial_userptr_sub ( userptr, subtrahend ); } static inline __always_inline void -UACCESS_INLINE(linux, memcpy_user)(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len) -{ - trivial_memcpy_user(dest, dest_off, src, src_off, len); +UACCESS_INLINE ( linux, memcpy_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memcpy_user ( dest, dest_off, src, src_off, len ); } static inline __always_inline void -UACCESS_INLINE(linux, memmove_user)(userptr_t dest, off_t dest_off, userptr_t src, off_t src_off, size_t len) -{ - trivial_memmove_user(dest, dest_off, src, src_off, len); +UACCESS_INLINE ( linux, memmove_user ) ( userptr_t dest, off_t dest_off, + userptr_t src, off_t src_off, + size_t len ) { + trivial_memmove_user ( dest, dest_off, src, src_off, len ); } static inline __always_inline int -UACCESS_INLINE(linux, memcmp_user)(userptr_t first, off_t first_off, userptr_t second, off_t second_off, size_t len) -{ - return trivial_memcmp_user(first, first_off, second, second_off, len); +UACCESS_INLINE ( linux, memcmp_user ) ( userptr_t first, off_t first_off, + userptr_t second, off_t second_off, + size_t len ) { + return trivial_memcmp_user ( first, first_off, second, second_off, len); } static inline __always_inline void -UACCESS_INLINE(linux, memset_user)(userptr_t buffer, off_t offset, int c, size_t len) -{ - trivial_memset_user(buffer, offset, c, len); +UACCESS_INLINE ( linux, memset_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + trivial_memset_user ( buffer, offset, c, len ); } static inline __always_inline size_t -UACCESS_INLINE(linux, strlen_user)(userptr_t buffer, off_t offset) -{ - return trivial_strlen_user(buffer, offset); +UACCESS_INLINE ( linux, strlen_user ) ( userptr_t buffer, off_t offset ) { + return trivial_strlen_user ( buffer, offset ); } static inline __always_inline off_t -UACCESS_INLINE(linux, memchr_user)(userptr_t buffer, off_t offset, int c, size_t len) -{ - return trivial_memchr_user(buffer, offset, c, len); +UACCESS_INLINE ( linux, memchr_user ) ( userptr_t buffer, off_t offset, + int c, size_t len ) { + return trivial_memchr_user ( buffer, offset, c, len ); } #endif /* _IPXE_LINUX_UACCESS_H */ diff --git a/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h b/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h index 4de55ecf3..1811d0bc6 100644 --- a/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h +++ b/roms/ipxe/src/include/ipxe/linux/linux_umalloc.h @@ -1,14 +1,14 @@ #ifndef _IPXE_LINUX_UMALLOC_H #define _IPXE_LINUX_UMALLOC_H -FILE_LICENCE(GPL2_OR_LATER); - /** @file * - * iPXE user memory allocation API for linux + * iPXE user memory allocation API for Linux * */ +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + #ifdef UMALLOC_LINUX #define UMALLOC_PREFIX_linux #else diff --git a/roms/ipxe/src/include/ipxe/linux_compat.h b/roms/ipxe/src/include/ipxe/linux_compat.h index 6f6ed97d7..4704c4817 100644 --- a/roms/ipxe/src/include/ipxe/linux_compat.h +++ b/roms/ipxe/src/include/ipxe/linux_compat.h @@ -10,7 +10,7 @@ * intended to be a substitute for proper porting. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> diff --git a/roms/ipxe/src/include/ipxe/list.h b/roms/ipxe/src/include/ipxe/list.h index 581ec9806..6a9b76f91 100644 --- a/roms/ipxe/src/include/ipxe/list.h +++ b/roms/ipxe/src/include/ipxe/list.h @@ -9,7 +9,7 @@ * list.h. */ -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <assert.h> diff --git a/roms/ipxe/src/include/ipxe/login_ui.h b/roms/ipxe/src/include/ipxe/login_ui.h index 01e5479f7..313e07349 100644 --- a/roms/ipxe/src/include/ipxe/login_ui.h +++ b/roms/ipxe/src/include/ipxe/login_ui.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int login_ui ( void ); diff --git a/roms/ipxe/src/include/ipxe/malloc.h b/roms/ipxe/src/include/ipxe/malloc.h index bbd6cb898..dd158b8e6 100644 --- a/roms/ipxe/src/include/ipxe/malloc.h +++ b/roms/ipxe/src/include/ipxe/malloc.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * Prototypes for the standard functions (malloc() et al) are in @@ -77,8 +77,8 @@ static inline void * __malloc malloc_dma ( size_t size, size_t phys_align ) { * If @c ptr is NULL, no action is taken. */ static inline void free_dma ( void *ptr, size_t size ) { - free_memblock ( ptr, size ); VALGRIND_FREELIKE_BLOCK ( ptr, 0 ); + free_memblock ( ptr, size ); } /** A cache discarder */ diff --git a/roms/ipxe/src/include/ipxe/mca.h b/roms/ipxe/src/include/ipxe/mca.h index d86dab195..11470ec93 100644 --- a/roms/ipxe/src/include/ipxe/mca.h +++ b/roms/ipxe/src/include/ipxe/mca.h @@ -5,7 +5,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifndef MCA_H #define MCA_H diff --git a/roms/ipxe/src/include/ipxe/md5.h b/roms/ipxe/src/include/ipxe/md5.h index 860bc4769..05c3974c8 100644 --- a/roms/ipxe/src/include/ipxe/md5.h +++ b/roms/ipxe/src/include/ipxe/md5.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/include/ipxe/memblock.h b/roms/ipxe/src/include/ipxe/memblock.h index 13af3e433..2bb38c460 100644 --- a/roms/ipxe/src/include/ipxe/memblock.h +++ b/roms/ipxe/src/include/ipxe/memblock.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/include/ipxe/menu.h b/roms/ipxe/src/include/ipxe/menu.h index f2b3caccc..3cc99be48 100644 --- a/roms/ipxe/src/include/ipxe/menu.h +++ b/roms/ipxe/src/include/ipxe/menu.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> diff --git a/roms/ipxe/src/include/ipxe/mii.h b/roms/ipxe/src/include/ipxe/mii.h index f53ad4a62..c2245b49e 100644 --- a/roms/ipxe/src/include/ipxe/mii.h +++ b/roms/ipxe/src/include/ipxe/mii.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <mii.h> #include <ipxe/netdevice.h> @@ -114,5 +114,7 @@ mii_dump ( struct mii_interface *mii ) { extern int mii_restart ( struct mii_interface *mii ); extern int mii_reset ( struct mii_interface *mii ); +extern int mii_check_link ( struct mii_interface *mii, + struct net_device *netdev ); #endif /* _IPXE_MII_H */ diff --git a/roms/ipxe/src/include/ipxe/monojob.h b/roms/ipxe/src/include/ipxe/monojob.h index aedc37eca..1661d91c2 100644 --- a/roms/ipxe/src/include/ipxe/monojob.h +++ b/roms/ipxe/src/include/ipxe/monojob.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct interface; diff --git a/roms/ipxe/src/include/ipxe/mount.h b/roms/ipxe/src/include/ipxe/mount.h index ca958117a..2d42ba080 100644 --- a/roms/ipxe/src/include/ipxe/mount.h +++ b/roms/ipxe/src/include/ipxe/mount.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** NFS MOUNT protocol number */ #define ONCRPC_MOUNT 100005 diff --git a/roms/ipxe/src/include/ipxe/nap.h b/roms/ipxe/src/include/ipxe/nap.h index afc887910..f4de778c4 100644 --- a/roms/ipxe/src/include/ipxe/nap.h +++ b/roms/ipxe/src/include/ipxe/nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/api.h> #include <config/nap.h> diff --git a/roms/ipxe/src/include/ipxe/ndp.h b/roms/ipxe/src/include/ipxe/ndp.h index 7388f938e..1815236f5 100644 --- a/roms/ipxe/src/include/ipxe/ndp.h +++ b/roms/ipxe/src/include/ipxe/ndp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/in.h> diff --git a/roms/ipxe/src/include/ipxe/neighbour.h b/roms/ipxe/src/include/ipxe/neighbour.h index f2a3946f1..1c1d1b6ca 100644 --- a/roms/ipxe/src/include/ipxe/neighbour.h +++ b/roms/ipxe/src/include/ipxe/neighbour.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/net80211_err.h b/roms/ipxe/src/include/ipxe/net80211_err.h index 7df3d0d85..32ccc257f 100644 --- a/roms/ipxe/src/include/ipxe/net80211_err.h +++ b/roms/ipxe/src/include/ipxe/net80211_err.h @@ -10,7 +10,7 @@ * Copyright (c) 2009 Joshua Oreman <oremanj@rwcr.net>. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/include/ipxe/netdevice.h b/roms/ipxe/src/include/ipxe/netdevice.h index 95ad1cf1b..a1d207ffc 100644 --- a/roms/ipxe/src/include/ipxe/netdevice.h +++ b/roms/ipxe/src/include/ipxe/netdevice.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/list.h> @@ -15,6 +15,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/refcnt.h> #include <ipxe/settings.h> #include <ipxe/interface.h> +#include <ipxe/retry.h> struct io_buffer; struct net_device; @@ -36,13 +37,12 @@ struct device; /** Maximum length of a link-layer header * - * The longest currently-supported link-layer header is for 802.11: a - * 24-byte frame header plus an 8-byte 802.3 LLC/SNAP header, plus a - * possible 4-byte VLAN header. (The IPoIB link-layer pseudo-header - * doesn't actually include link-layer addresses; see ipoib.c for - * details.) + * The longest currently-supported link-layer header is for RNDIS: an + * 8-byte RNDIS header, a 32-byte RNDIS packet message header, a + * 14-byte Ethernet header and a possible 4-byte VLAN header. Round + * up to 64 bytes. */ -#define MAX_LL_HEADER_LEN 36 +#define MAX_LL_HEADER_LEN 64 /** Maximum length of a network-layer address */ #define MAX_NET_ADDR_LEN 16 @@ -393,6 +393,8 @@ struct net_device { * indicates the error preventing link-up. */ int link_rc; + /** Link block timer */ + struct retry_timer link_block; /** Maximum packet length * * This length includes any link-layer headers. @@ -428,6 +430,14 @@ struct net_device { /** Network device receive queue processing is frozen */ #define NETDEV_RX_FROZEN 0x0004 +/** Network device interrupts are unsupported + * + * This flag can be used by a network device to indicate that + * interrupts are not supported despite the presence of an irq() + * method. + */ +#define NETDEV_IRQ_UNSUPPORTED 0x0008 + /** Link-layer protocol table */ #define LL_PROTOCOLS __table ( struct ll_protocol, "ll_protocols" ) @@ -615,6 +625,17 @@ netdev_link_ok ( struct net_device *netdev ) { } /** + * Check link block state of network device + * + * @v netdev Network device + * @ret link_blocked Link is blocked + */ +static inline __attribute__ (( always_inline )) int +netdev_link_blocked ( struct net_device *netdev ) { + return ( timer_running ( &netdev->link_block ) ); +} + +/** * Check whether or not network device is open * * @v netdev Network device @@ -633,7 +654,8 @@ netdev_is_open ( struct net_device *netdev ) { */ static inline __attribute__ (( always_inline )) int netdev_irq_supported ( struct net_device *netdev ) { - return ( netdev->op->irq != NULL ); + return ( ( netdev->op->irq != NULL ) && + ! ( netdev->state & NETDEV_IRQ_UNSUPPORTED ) ); } /** @@ -662,6 +684,9 @@ extern void netdev_rx_freeze ( struct net_device *netdev ); extern void netdev_rx_unfreeze ( struct net_device *netdev ); extern void netdev_link_err ( struct net_device *netdev, int rc ); extern void netdev_link_down ( struct net_device *netdev ); +extern void netdev_link_block ( struct net_device *netdev, + unsigned long timeout ); +extern void netdev_link_unblock ( struct net_device *netdev ); extern int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ); extern void netdev_tx_defer ( struct net_device *netdev, struct io_buffer *iobuf ); diff --git a/roms/ipxe/src/include/ipxe/nfs.h b/roms/ipxe/src/include/ipxe/nfs.h index 498ed5a27..69b8b5381 100644 --- a/roms/ipxe/src/include/ipxe/nfs.h +++ b/roms/ipxe/src/include/ipxe/nfs.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** NFS protocol number */ #define ONCRPC_NFS 100003 diff --git a/roms/ipxe/src/include/ipxe/nfs_open.h b/roms/ipxe/src/include/ipxe/nfs_open.h index caba977f7..8572c41b3 100644 --- a/roms/ipxe/src/include/ipxe/nfs_open.h +++ b/roms/ipxe/src/include/ipxe/nfs_open.h @@ -7,6 +7,6 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #endif /* _IPXE_NFS_OPEN_H */ diff --git a/roms/ipxe/src/include/ipxe/nfs_uri.h b/roms/ipxe/src/include/ipxe/nfs_uri.h index d88bd6f65..aaa6d3749 100644 --- a/roms/ipxe/src/include/ipxe/nfs_uri.h +++ b/roms/ipxe/src/include/ipxe/nfs_uri.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uri.h> diff --git a/roms/ipxe/src/include/ipxe/null_entropy.h b/roms/ipxe/src/include/ipxe/null_entropy.h index 646d1a17e..91adefa69 100644 --- a/roms/ipxe/src/include/ipxe/null_entropy.h +++ b/roms/ipxe/src/include/ipxe/null_entropy.h @@ -9,7 +9,7 @@ * security-sensitive environment. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/null_nap.h b/roms/ipxe/src/include/ipxe/null_nap.h index 0c0704bc7..17145b48b 100644 --- a/roms/ipxe/src/include/ipxe/null_nap.h +++ b/roms/ipxe/src/include/ipxe/null_nap.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef NAP_NULL #define NAP_PREFIX_null diff --git a/roms/ipxe/src/include/ipxe/null_reboot.h b/roms/ipxe/src/include/ipxe/null_reboot.h index 3de36c5b3..5de38afc0 100644 --- a/roms/ipxe/src/include/ipxe/null_reboot.h +++ b/roms/ipxe/src/include/ipxe/null_reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef REBOOT_NULL #define REBOOT_PREFIX_null diff --git a/roms/ipxe/src/include/ipxe/null_sanboot.h b/roms/ipxe/src/include/ipxe/null_sanboot.h index 2b3a2c74d..58f03339f 100644 --- a/roms/ipxe/src/include/ipxe/null_sanboot.h +++ b/roms/ipxe/src/include/ipxe/null_sanboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef SANBOOT_NULL #define SANBOOT_PREFIX_null diff --git a/roms/ipxe/src/include/ipxe/null_time.h b/roms/ipxe/src/include/ipxe/null_time.h index 2b72cdf50..d2b15194b 100644 --- a/roms/ipxe/src/include/ipxe/null_time.h +++ b/roms/ipxe/src/include/ipxe/null_time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #ifdef TIME_NULL #define TIME_PREFIX_null diff --git a/roms/ipxe/src/include/ipxe/nvo.h b/roms/ipxe/src/include/ipxe/nvo.h index 1a629da78..7a3c7a3db 100644 --- a/roms/ipxe/src/include/ipxe/nvo.h +++ b/roms/ipxe/src/include/ipxe/nvo.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/dhcpopts.h> diff --git a/roms/ipxe/src/include/ipxe/nvs.h b/roms/ipxe/src/include/ipxe/nvs.h index 4733123cf..5789f4c0d 100644 --- a/roms/ipxe/src/include/ipxe/nvs.h +++ b/roms/ipxe/src/include/ipxe/nvs.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/nvsvpd.h b/roms/ipxe/src/include/ipxe/nvsvpd.h index 3450e5c71..4c50daf85 100644 --- a/roms/ipxe/src/include/ipxe/nvsvpd.h +++ b/roms/ipxe/src/include/ipxe/nvsvpd.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/nvs.h> #include <ipxe/pcivpd.h> diff --git a/roms/ipxe/src/include/ipxe/ocsp.h b/roms/ipxe/src/include/ipxe/ocsp.h index 387e28f81..71fa41dc9 100644 --- a/roms/ipxe/src/include/ipxe/ocsp.h +++ b/roms/ipxe/src/include/ipxe/ocsp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdarg.h> #include <time.h> diff --git a/roms/ipxe/src/include/ipxe/oncrpc.h b/roms/ipxe/src/include/ipxe/oncrpc.h index 76c1260f2..071468711 100644 --- a/roms/ipxe/src/include/ipxe/oncrpc.h +++ b/roms/ipxe/src/include/ipxe/oncrpc.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** ONC RCP Version */ #define ONCRPC_VERS 2 diff --git a/roms/ipxe/src/include/ipxe/oncrpc_iob.h b/roms/ipxe/src/include/ipxe/oncrpc_iob.h index 4858d96b5..b55043770 100644 --- a/roms/ipxe/src/include/ipxe/oncrpc_iob.h +++ b/roms/ipxe/src/include/ipxe/oncrpc_iob.h @@ -13,7 +13,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Add a string to the end of an I/O buffer diff --git a/roms/ipxe/src/include/ipxe/open.h b/roms/ipxe/src/include/ipxe/open.h index a522f0cd1..43d4cdc66 100644 --- a/roms/ipxe/src/include/ipxe/open.h +++ b/roms/ipxe/src/include/ipxe/open.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdarg.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/params.h b/roms/ipxe/src/include/ipxe/params.h index c2d82d9cf..dd3292efc 100644 --- a/roms/ipxe/src/include/ipxe/params.h +++ b/roms/ipxe/src/include/ipxe/params.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/parseopt.h b/roms/ipxe/src/include/ipxe/parseopt.h index 840de7497..829b3431c 100644 --- a/roms/ipxe/src/include/ipxe/parseopt.h +++ b/roms/ipxe/src/include/ipxe/parseopt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> diff --git a/roms/ipxe/src/include/ipxe/pccrc.h b/roms/ipxe/src/include/ipxe/pccrc.h new file mode 100644 index 000000000..7f0963428 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/pccrc.h @@ -0,0 +1,447 @@ +#ifndef _IPXE_PCCRC_H +#define _IPXE_PCCRC_H + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <byteswap.h> +#include <ipxe/uaccess.h> +#include <ipxe/crypto.h> + +/****************************************************************************** + * + * Content Information versioning + * + ****************************************************************************** + * + * Note that version 1 data structures are little-endian, but version + * 2 data structures are big-endian. + */ + +/** Content Information version number */ +union peerdist_info_version { + /** Raw version number + * + * Always little-endian, regardless of whether the + * encompassing structure is version 1 (little-endian) or + * version 2 (big-endian). + */ + uint16_t raw; + /** Major:minor version number */ + struct { + /** Minor version number */ + uint8_t minor; + /** Major version number */ + uint8_t major; + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** Content Information version 1 */ +#define PEERDIST_INFO_V1 0x0100 + +/** Content Information version 2 */ +#define PEERDIST_INFO_V2 0x0200 + +/****************************************************************************** + * + * Content Information version 1 + * + ****************************************************************************** + */ + +/** Content Information version 1 data structure header + * + * All fields are little-endian. + */ +struct peerdist_info_v1 { + /** Version number */ + union peerdist_info_version version; + /** Hash algorithm + * + * This is a @c PEERDIST_INFO_V1_HASH_XXX constant. + */ + uint32_t hash; + /** Length to skip in first segment + * + * Length at the start of the first segment which is not + * included within the content range. + */ + uint32_t first; + /** Length to read in last segment, or zero + * + * Length within the last segment which is included within the + * content range. A zero value indicates that the whole of + * the last segment is included within the content range. + */ + uint32_t last; + /** Number of segments within the content information */ + uint32_t segments; + /* Followed by a variable-length array of segment descriptions + * and a list of variable-length block descriptions: + * + * peerdist_info_v1_segment_t(digestsize) segment[segments]; + * peerdist_info_v1_block_t(digestsize, block0.blocks) block0; + * peerdist_info_v1_block_t(digestsize, block1.blocks) block1; + * ... + * peerdist_info_v1_block_t(digestsize, blockN.blocks) blockN; + */ +} __attribute__ (( packed )); + +/** SHA-256 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA256 0x0000800cUL + +/** SHA-384 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA384 0x0000800dUL + +/** SHA-512 hash algorithm */ +#define PEERDIST_INFO_V1_HASH_SHA512 0x0000800eUL + +/** Content Information version 1 segment description header + * + * All fields are little-endian. + */ +struct peerdist_info_v1_segment { + /** Offset of this segment within the content */ + uint64_t offset; + /** Length of this segment + * + * Should always be 32MB, except for the last segment within + * the content. + */ + uint32_t len; + /** Block size for this segment + * + * Should always be 64kB. Note that the last block within the + * last segment may actually be less than 64kB. + */ + uint32_t blksize; + /* Followed by two variable-length hashes: + * + * uint8_t hash[digestsize]; + * uint8_t secret[digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + * + * Note that the hash is taken over (the hashes of all blocks + * within) the entire segment, even if the blocks do not + * intersect the content range (and so do not appear within + * the block list). It therefore functions only as a segment + * identifier; it cannot be used to verify the content of the + * segment (since we may not download all blocks within the + * segment). + */ +} __attribute__ (( packed )); + +/** Content Information version 1 segment description + * + * @v digestsize Digest size + */ +#define peerdist_info_v1_segment_t( digestsize ) \ + struct { \ + struct peerdist_info_v1_segment segment; \ + uint8_t hash[digestsize]; \ + uint8_t secret[digestsize]; \ + } __attribute__ (( packed )) + +/** Content Information version 1 block description header + * + * All fields are little-endian. + */ +struct peerdist_info_v1_block { + /** Number of blocks within the block description + * + * This is the number of blocks within the segment which + * overlap the content range. It may therefore be less than + * the number of blocks within the segment. + */ + uint32_t blocks; + /* Followed by an array of variable-length hashes: + * + * uint8_t hash[blocks][digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + */ + } __attribute__ (( packed )); + +/** Content Information version 1 block description + * + * @v digestsize Digest size + * @v blocks Number of blocks + */ +#define peerdist_info_v1_block_t( digestsize, blocks ) \ + struct { \ + struct peerdist_info_v1_block block; \ + uint8_t hash[blocks][digestsize]; \ + } __attribute__ (( packed )) + +/****************************************************************************** + * + * Content Information version 2 + * + ****************************************************************************** + */ + +/** Content Information version 2 data structure header + * + * All fields are big-endian. + */ +struct peerdist_info_v2 { + /** Version number */ + union peerdist_info_version version; + /** Hash algorithm + * + * This is a @c PEERDIST_INFO_V2_HASH_XXX constant. + */ + uint8_t hash; + /** Offset of the first segment within the content */ + uint64_t offset; + /** Index of the first segment within the content */ + uint64_t index; + /** Length to skip in first segment + * + * Length at the start of the first segment which is not + * included within the content range. + */ + uint32_t first; + /** Length of content range, or zero + * + * Length of the content range. A zero indicates that + * everything up to the end of the last segment is included in + * the content range. + */ + uint64_t len; + /* Followed by a list of chunk descriptions */ +} __attribute__ (( packed )); + +/** SHA-512 hash algorithm with output truncated to first 256 bits */ +#define PEERDIST_INFO_V2_HASH_SHA512_TRUNC 0x04 + +/** Content Information version 2 chunk description header + * + * All fields are big-endian. + */ +struct peerdist_info_v2_chunk { + /** Chunk type */ + uint8_t type; + /** Chunk data length */ + uint32_t len; + /* Followed by an array of segment descriptions: + * + * peerdist_info_v2_segment_t(digestsize) segment[segments] + * + * where digestsize is the digest size for the selected hash + * algorithm, and segments is equal to @c len divided by the + * size of each segment array entry. + */ +} __attribute__ (( packed )); + +/** Content Information version 2 chunk description + * + * @v digestsize Digest size + */ +#define peerdist_info_v2_chunk_t( digestsize ) \ + struct { \ + struct peerdist_info_v2_chunk chunk; \ + peerdist_info_v2_segment_t ( digestsize ) segment[0]; \ + } __attribute__ (( packed )) + +/** Chunk type */ +#define PEERDIST_INFO_V2_CHUNK_TYPE 0x00 + +/** Content Information version 2 segment description header + * + * All fields are big-endian. + */ +struct peerdist_info_v2_segment { + /** Segment length */ + uint32_t len; + /* Followed by two variable-length hashes: + * + * uint8_t hash[digestsize]; + * uint8_t secret[digestsize]; + * + * where digestsize is the digest size for the selected hash + * algorithm. + */ +} __attribute__ (( packed )); + +/** Content Information version 2 segment description + * + * @v digestsize Digest size + */ +#define peerdist_info_v2_segment_t( digestsize ) \ + struct { \ + struct peerdist_info_v2_segment segment; \ + uint8_t hash[digestsize]; \ + uint8_t secret[digestsize]; \ + } __attribute__ (( packed )) + +/****************************************************************************** + * + * Content Information + * + ****************************************************************************** + */ + +/** Maximum digest size for any supported algorithm + * + * The largest digest size that we support is for SHA-512 at 64 bytes + */ +#define PEERDIST_DIGEST_MAX_SIZE 64 + +/** Raw content information */ +struct peerdist_raw { + /** Data buffer */ + userptr_t data; + /** Length of data buffer */ + size_t len; +}; + +/** A content range */ +struct peerdist_range { + /** Start offset */ + size_t start; + /** End offset */ + size_t end; +}; + +/** Content information */ +struct peerdist_info { + /** Raw content information */ + struct peerdist_raw raw; + + /** Content information operations */ + struct peerdist_info_operations *op; + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Digest size + * + * Note that this may be shorter than the digest size of the + * digest algorithm. The truncation does not always take + * place as soon as a digest is calculated. For example, + * version 2 content information uses SHA-512 with a truncated + * digest size of 32 (256 bits), but the segment identifier + * ("HoHoDk") is calculated by using HMAC with the full + * SHA-512 digest and then truncating the HMAC output, rather + * than by simply using HMAC with the truncated SHA-512 + * digest. This is, of course, totally undocumented. + */ + size_t digestsize; + /** Content range */ + struct peerdist_range range; + /** Trimmed content range */ + struct peerdist_range trim; + /** Number of segments within the content information */ + unsigned int segments; +}; + +/** A content information segment */ +struct peerdist_info_segment { + /** Content information */ + const struct peerdist_info *info; + /** Segment index */ + unsigned int index; + + /** Content range + * + * Note that this range may exceed the overall content range. + */ + struct peerdist_range range; + /** Number of blocks within this segment */ + unsigned int blocks; + /** Block size */ + size_t blksize; + /** Segment hash of data + * + * This is MS-PCCRC's "HoD". + */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment secret + * + * This is MS-PCCRC's "Ke = Kp". + */ + uint8_t secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment identifier + * + * This is MS-PCCRC's "HoHoDk". + */ + uint8_t id[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** Magic string constant used to calculate segment identifier + * + * Note that the MS-PCCRC specification states that this constant is + * + * "the null-terminated ASCII string constant "MS_P2P_CACHING"; + * string literals are all ASCII strings with NULL terminators + * unless otherwise noted." + * + * The specification lies. This constant is a UTF-16LE string, not an + * ASCII string. The terminating wNUL *is* included within the + * constant. + */ +#define PEERDIST_SEGMENT_ID_MAGIC L"MS_P2P_CACHING" + +/** A content information block */ +struct peerdist_info_block { + /** Content information segment */ + const struct peerdist_info_segment *segment; + /** Block index */ + unsigned int index; + + /** Content range + * + * Note that this range may exceed the overall content range. + */ + struct peerdist_range range; + /** Trimmed content range */ + struct peerdist_range trim; + /** Block hash */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** Content information operations */ +struct peerdist_info_operations { + /** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ + int ( * info ) ( struct peerdist_info *info ); + /** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ + int ( * segment ) ( struct peerdist_info_segment *segment ); + /** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ + int ( * block ) ( struct peerdist_info_block *block ); +}; + +extern struct digest_algorithm sha512_trunc_algorithm; + +extern int peerdist_info ( userptr_t data, size_t len, + struct peerdist_info *info ); +extern int peerdist_info_segment ( const struct peerdist_info *info, + struct peerdist_info_segment *segment, + unsigned int index ); +extern int peerdist_info_block ( const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + unsigned int index ); + +#endif /* _IPXE_PCCRC_H */ diff --git a/roms/ipxe/src/include/ipxe/pccrd.h b/roms/ipxe/src/include/ipxe/pccrd.h new file mode 100644 index 000000000..3daa92f29 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/pccrd.h @@ -0,0 +1,47 @@ +#ifndef _IPXE_PCCRD_H +#define _IPXE_PCCRD_H + +/** @file + * + * Peer Content Caching and Retrieval: Discovery Protocol [MS-PCCRD] + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** PeerDist discovery port */ +#define PEERDIST_DISCOVERY_PORT 3702 + +/** PeerDist discovery IPv4 address (239.255.255.250) */ +#define PEERDIST_DISCOVERY_IPV4 \ + ( ( 239 << 24 ) | ( 255 << 16 ) | ( 255 << 8 ) | ( 250 << 0 ) ) + +/** PeerDist discovery IPv6 address (ff02::c) */ +#define PEERDIST_DISCOVERY_IPV6 \ + { 0xff, 0x02, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xc } + +/** A PeerDist discovery reply block count */ +struct peerdist_discovery_block_count { + /** Count (as an eight-digit hex value) */ + char hex[8]; +} __attribute__ (( packed )); + +/** A PeerDist discovery reply */ +struct peerdist_discovery_reply { + /** List of segment ID strings + * + * The list is terminated with a zero-length string. + */ + char *ids; + /** List of peer locations + * + * The list is terminated with a zero-length string. + */ + char *locations; +}; + +extern char * peerdist_discovery_request ( const char *uuid, const char *id ); +extern int peerdist_discovery_reply ( char *data, size_t len, + struct peerdist_discovery_reply *reply ); + +#endif /* _IPXE_PCCRD_H */ diff --git a/roms/ipxe/src/include/ipxe/pccrr.h b/roms/ipxe/src/include/ipxe/pccrr.h new file mode 100644 index 000000000..1ea86c40d --- /dev/null +++ b/roms/ipxe/src/include/ipxe/pccrr.h @@ -0,0 +1,376 @@ +#ifndef _IPXE_PCCRR_H +#define _IPXE_PCCRR_H + +/** @file + * + * Peer Content Caching and Retrieval: Retrieval Protocol [MS-PCCRR] + * + * All fields are in network byte order. + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/uaccess.h> + +/** Magic retrieval URI path */ +#define PEERDIST_MAGIC_PATH "/116B50EB-ECE2-41ac-8429-9F9E963361B7/" + +/** Retrieval protocol version */ +union peerdist_msg_version { + /** Raw version number */ + uint32_t raw; + /** Major:minor version number */ + struct { + /** Minor version number */ + uint16_t minor; + /** Major version number */ + uint16_t major; + } __attribute__ (( packed )); +} __attribute__ (( packed )); + +/** Retrieval protocol version 1.0 */ +#define PEERDIST_MSG_VERSION_1_0 0x00000001UL + +/** Retrieval protocol version 2.0 */ +#define PEERDIST_MSG_VERSION_2_0 0x00000002UL + +/** Retrieval protocol supported versions */ +struct peerdist_msg_versions { + /** Minimum supported protocol version */ + union peerdist_msg_version min; + /** Maximum supported protocol version */ + union peerdist_msg_version max; +} __attribute__ (( packed )); + +/** Retrieval protocol block range */ +struct peerdist_msg_range { + /** First block in range */ + uint32_t first; + /** Number of blocks in range */ + uint32_t count; +} __attribute__ (( packed )); + +/** Retrieval protocol segment ID header */ +struct peerdist_msg_segment { + /** Digest size (i.e. length of segment ID) */ + uint32_t digestsize; + /* Followed by a single variable-length ID and padding: + * + * uint8_t id[digestsize]; + * uint8_t pad[ (-digestsize) & 0x3 ]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol segment ID + * + * @v digestsize Digest size + */ +#define peerdist_msg_segment_t( digestsize ) \ + struct { \ + struct peerdist_msg_segment segment; \ + uint8_t id[digestsize]; \ + uint8_t pad[ ( -(digestsize) ) & 0x3 ]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block range list header */ +struct peerdist_msg_ranges { + /** Number of ranges */ + uint32_t count; + /* Followed by an array of block ranges: + * + * struct peerdist_msg_range range[count]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block range list + * + * @v count Number of ranges + */ +#define peerdist_msg_ranges_t( count ) \ + struct { \ + struct peerdist_msg_ranges ranges; \ + struct peerdist_msg_range range[count]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol data block header */ +struct peerdist_msg_block { + /** Length of data block */ + uint32_t len; + /* Followed by the (encrypted) data block: + * + * uint8_t data[len]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol data block */ +#define peerdist_msg_block_t( len ) \ + struct { \ + struct peerdist_msg_block block; \ + uint8_t data[len]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol initialisation vector header */ +struct peerdist_msg_iv { + /** Cipher block size */ + uint32_t blksize; + /* Followed by the initialisation vector: + * + * uint8_t data[blksize]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol initialisation vector */ +#define peerdist_msg_iv_t( blksize ) \ + struct { \ + struct peerdist_msg_iv iv; \ + uint8_t data[blksize]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol useless VRF data header */ +struct peerdist_msg_useless_vrf { + /** Length of useless VRF data */ + uint32_t len; + /* Followed by a variable-length useless VRF data block and + * padding: + * + * uint8_t data[len]; + * uint8_t pad[ (-len) & 0x3 ]; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol useless VRF data */ +#define peerdist_msg_useless_vrf_t( vrf_len ) \ + struct { \ + struct peerdist_msg_useless_vrf vrf; \ + uint8_t data[vrf_len]; \ + uint8_t pad[ ( -(vrf_len) ) & 0x3 ]; \ + } __attribute__ (( packed )) + +/** Retrieval protocol message header */ +struct peerdist_msg_header { + /** Protocol version + * + * This is the protocol version in which the message type was + * first defined. + */ + union peerdist_msg_version version; + /** Message type */ + uint32_t type; + /** Message size (including this header) */ + uint32_t len; + /** Cryptographic algorithm ID */ + uint32_t algorithm; +} __attribute__ (( packed )); + +/** Retrieval protocol cryptographic algorithm IDs */ +enum peerdist_msg_algorithm { + /** No encryption */ + PEERDIST_MSG_PLAINTEXT = 0x00000000UL, + /** AES-128 in CBC mode */ + PEERDIST_MSG_AES_128_CBC = 0x00000001UL, + /** AES-192 in CBC mode */ + PEERDIST_MSG_AES_192_CBC = 0x00000002UL, + /** AES-256 in CBC mode */ + PEERDIST_MSG_AES_256_CBC = 0x00000003UL, +}; + +/** Retrieval protocol transport response header */ +struct peerdist_msg_transport_header { + /** Length (excluding this header) + * + * This seems to be identical in both purpose and value to the + * length found within the message header, and therefore + * serves no useful purpose. + */ + uint32_t len; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation request */ +struct peerdist_msg_nego_req { + /** Message header */ + struct peerdist_msg_header hdr; + /** Supported versions */ + struct peerdist_msg_versions versions; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation request version */ +#define PEERDIST_MSG_NEGO_REQ_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol negotiation request type */ +#define PEERDIST_MSG_NEGO_REQ_TYPE 0x00000000UL + +/** Retrieval protocol negotiation response */ +struct peerdist_msg_nego_resp { + /** Message header */ + struct peerdist_msg_header hdr; + /** Supported versions */ + struct peerdist_msg_versions versions; +} __attribute__ (( packed )); + +/** Retrieval protocol negotiation response version */ +#define PEERDIST_MSG_NEGO_RESP_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol negotiation response type */ +#define PEERDIST_MSG_NEGO_RESP_TYPE 0x00000001UL + +/** Retrieval protocol block list request header */ +struct peerdist_msg_getblklist { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID and a block range list: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block list request + * + * @v digestsize Digest size + * @v count Block range count + */ +#define peerdist_msg_getblklist_t( digestsize, count ) \ + struct { \ + struct peerdist_msg_getblklist getblklist; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block list request version */ +#define PEERDIST_MSG_GETBLKLIST_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block list request type */ +#define PEERDIST_MSG_GETBLKLIST_TYPE 0x00000002UL + +/** Retrieval protocol block fetch request header */ +struct peerdist_msg_getblks { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block range list, and a useless + * VRF block: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + * peerdist_msg_vrf_t(vrf_len) vrf; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block fetch request + * + * @v digestsize Digest size + * @v count Block range count + * @v vrf_len Length of uselessness + */ +#define peerdist_msg_getblks_t( digestsize, count, vrf_len ) \ + struct { \ + struct peerdist_msg_getblks getblks; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + peerdist_msg_useless_vrf_t ( vrf_len ); \ + } __attribute__ (( packed )) + +/** Retrieval protocol block fetch request version */ +#define PEERDIST_MSG_GETBLKS_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block fetch request type */ +#define PEERDIST_MSG_GETBLKS_TYPE 0x00000003UL + +/** Retrieval protocol block list response header */ +struct peerdist_msg_blklist { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block range list, and a next + * block index: + * + * peerdist_msg_segment_t(digestsize) segment; + * peerdist_msg_ranges_t(count) ranges; + * uint32_t next; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block list response + * + * @v digestsize Digest size + * @v count Block range count + */ +#define peerdist_msg_blklist_t( digestsize, count ) \ + struct { \ + struct peerdist_msg_blklist blklist; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + peerdist_msg_ranges_t ( count ) ranges; \ + uint32_t next; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block list response version */ +#define PEERDIST_MSG_BLKLIST_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block list response type */ +#define PEERDIST_MSG_BLKLIST_TYPE 0x00000004UL + +/** Retrieval protocol block fetch response header */ +struct peerdist_msg_blk { + /** Message header */ + struct peerdist_msg_header hdr; + /* Followed by a segment ID, a block index, a next block + * index, a data block, a useless VRF block, and an + * initialisation vector: + * + * peerdist_msg_segment_t(digestsize) segment; + * uint32_t index; + * uint32_t next; + * peerdist_msg_block_t(len) data; + * peerdist_msg_useless_vrf_t(vrf_len) vrf; + * peerdist_msg_iv_t(blksize) iv; + */ +} __attribute__ (( packed )); + +/** Retrieval protocol block fetch response + * + * @v digestsize Digest size + * @v len Data block length + * @v vrf_len Length of uselessness + * @v blksize Cipher block size + */ +#define peerdist_msg_blk_t( digestsize, len, vrf_len, blksize ) \ + struct { \ + struct peerdist_msg_blk blk; \ + peerdist_msg_segment_t ( digestsize ) segment; \ + uint32_t index; \ + uint32_t next; \ + peerdist_msg_block_t ( len ) block; \ + peerdist_msg_useless_vrf_t ( vrf_len ) vrf; \ + peerdist_msg_iv_t ( blksize ) iv; \ + } __attribute__ (( packed )) + +/** Retrieval protocol block fetch response version */ +#define PEERDIST_MSG_BLK_VERSION PEERDIST_MSG_VERSION_1_0 + +/** Retrieval protocol block fetch response type */ +#define PEERDIST_MSG_BLK_TYPE 0x00000005UL + +/** + * Parse retrieval protocol block fetch response + * + * @v raw Raw data + * @v raw_len Length of raw data + * @v digestsize Digest size + * @v blksize Cipher block size + * @v blk Structure to fill in + * @ret rc Return status code + */ +#define peerdist_msg_blk( raw, raw_len, digestsize, blksize, blk ) ( { \ + assert ( sizeof ( (blk)->segment.id ) == (digestsize) ); \ + assert ( sizeof ( (blk)->block.data ) == 0 ); \ + assert ( sizeof ( (blk)->vrf.data ) == 0 ); \ + assert ( sizeof ( (blk)->iv.data ) == blksize ); \ + peerdist_msg_blk_untyped ( (raw), (raw_len), (digestsize), \ + (blksize), blk ); \ + } ) + +extern int peerdist_msg_blk_untyped ( userptr_t raw, size_t raw_len, + size_t digestsize, size_t blksize, + void *out ); + +#endif /* _IPXE_PCCRR_H */ diff --git a/roms/ipxe/src/include/ipxe/pci.h b/roms/ipxe/src/include/ipxe/pci.h index 692771ebe..a841e00ff 100644 --- a/roms/ipxe/src/include/ipxe/pci.h +++ b/roms/ipxe/src/include/ipxe/pci.h @@ -1,268 +1,132 @@ #ifndef _IPXE_PCI_H #define _IPXE_PCI_H -/* - * Support for NE2000 PCI clones added David Monro June 1997 - * Generalised for other PCI NICs by Ken Yap July 1997 - * PCI support rewritten by Michael Brown 2006 +/** @file + * + * PCI bus * - * Most of this is taken from /usr/src/linux/include/linux/pci.h. - */ - -/* - * 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, or (at - * your option) any later version. */ -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/device.h> #include <ipxe/tables.h> #include <ipxe/pci_io.h> -#include "pci_ids.h" -/* - * PCI constants - * - */ - -#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */ -#define PCI_COMMAND_MEM 0x2 /* Enable response in mem space */ -#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */ - -#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */ -#define PCI_LATENCY_TIMER 0x0d /* 8 bits */ - -#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */ -#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */ -#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */ -#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */ -#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */ -#define PCI_COMMAND_SERR 0x100 /* Enable SERR */ -#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */ -#define PCI_COMMAND_INTX_DISABLE 0x400 /* INTx Emulation Disable */ - - -#define PCI_VENDOR_ID 0x00 /* 16 bits */ -#define PCI_DEVICE_ID 0x02 /* 16 bits */ -#define PCI_COMMAND 0x04 /* 16 bits */ - -#define PCI_STATUS 0x06 /* 16 bits */ -#define PCI_STATUS_CAP_LIST 0x10 /* Support Capability List */ -#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */ -#define PCI_STATUS_UDF 0x40 /* Support User Definable Features [obsolete] */ -#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */ -#define PCI_STATUS_PARITY 0x100 /* Detected parity error */ -#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */ -#define PCI_STATUS_DEVSEL_FAST 0x000 -#define PCI_STATUS_DEVSEL_MEDIUM 0x200 -#define PCI_STATUS_DEVSEL_SLOW 0x400 -#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */ -#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */ -#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */ -#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */ -#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */ - -#define PCI_REVISION 0x08 /* 8 bits */ -#define PCI_REVISION_ID 0x08 /* 8 bits */ -#define PCI_CLASS_REVISION 0x08 /* 32 bits */ -#define PCI_CLASS_CODE 0x0b /* 8 bits */ -#define PCI_SUBCLASS_CODE 0x0a /* 8 bits */ -#define PCI_HEADER_TYPE 0x0e /* 8 bits */ -#define PCI_HEADER_TYPE_NORMAL 0 -#define PCI_HEADER_TYPE_BRIDGE 1 -#define PCI_HEADER_TYPE_CARDBUS 2 - - -/* Header type 0 (normal devices) */ -#define PCI_CARDBUS_CIS 0x28 +/** PCI vendor ID */ +#define PCI_VENDOR_ID 0x00 + +/** PCI device ID */ +#define PCI_DEVICE_ID 0x02 + +/** PCI command */ +#define PCI_COMMAND 0x04 +#define PCI_COMMAND_IO 0x0001 /**< I/O space */ +#define PCI_COMMAND_MEM 0x0002 /**< Memory space */ +#define PCI_COMMAND_MASTER 0x0004 /**< Bus master */ +#define PCI_COMMAND_INVALIDATE 0x0010 /**< Mem. write & invalidate */ +#define PCI_COMMAND_PARITY 0x0040 /**< Parity error response */ +#define PCI_COMMAND_SERR 0x0100 /**< SERR# enable */ +#define PCI_COMMAND_INTX_DISABLE 0x0400 /**< Interrupt disable */ + +/** PCI status */ +#define PCI_STATUS 0x06 +#define PCI_STATUS_CAP_LIST 0x0010 /**< Capabilities list */ +#define PCI_STATUS_PARITY 0x0100 /**< Master data parity error */ +#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /**< Received target abort */ +#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /**< Received master abort */ +#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /**< Signalled system error */ +#define PCI_STATUS_DETECTED_PARITY 0x8000 /**< Detected parity error */ + +/** PCI revision */ +#define PCI_REVISION 0x08 + +/** PCI cache line size */ +#define PCI_CACHE_LINE_SIZE 0x0c + +/** PCI latency timer */ +#define PCI_LATENCY_TIMER 0x0d + +/** PCI header type */ +#define PCI_HEADER_TYPE 0x0e +#define PCI_HEADER_TYPE_NORMAL 0x00 /**< Normal header */ +#define PCI_HEADER_TYPE_BRIDGE 0x01 /**< PCI-to-PCI bridge header */ +#define PCI_HEADER_TYPE_CARDBUS 0x02 /**< CardBus header */ +#define PCI_HEADER_TYPE_MASK 0x7f /**< Header type mask */ +#define PCI_HEADER_TYPE_MULTI 0x80 /**< Multi-function device */ + +/** PCI base address registers */ +#define PCI_BASE_ADDRESS(n) ( 0x10 + ( 4 * (n) ) ) +#define PCI_BASE_ADDRESS_0 PCI_BASE_ADDRESS ( 0 ) +#define PCI_BASE_ADDRESS_1 PCI_BASE_ADDRESS ( 1 ) +#define PCI_BASE_ADDRESS_2 PCI_BASE_ADDRESS ( 2 ) +#define PCI_BASE_ADDRESS_3 PCI_BASE_ADDRESS ( 3 ) +#define PCI_BASE_ADDRESS_4 PCI_BASE_ADDRESS ( 4 ) +#define PCI_BASE_ADDRESS_5 PCI_BASE_ADDRESS ( 5 ) +#define PCI_BASE_ADDRESS_SPACE_IO 0x00000001UL /**< I/O BAR */ +#define PCI_BASE_ADDRESS_IO_MASK 0x00000003UL /**< I/O BAR mask */ +#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x00000004UL /**< 64-bit memory */ +#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x00000006UL /**< Memory type mask */ +#define PCI_BASE_ADDRESS_MEM_MASK 0x0000000fUL /**< Memory BAR mask */ + +/** PCI subsystem vendor ID */ #define PCI_SUBSYSTEM_VENDOR_ID 0x2c + +/** PCI subsystem ID */ #define PCI_SUBSYSTEM_ID 0x2e -#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */ -#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */ -#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */ -#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */ -#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */ -#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */ - -#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */ -#define PCI_BASE_ADDRESS_SPACE_IO 0x01 -#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00 - -#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06 -#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */ -#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M [obsolete] */ -#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */ -#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f) -#define PCI_BASE_ADDRESS_IO_MASK (~0x03) -#define PCI_ROM_ADDRESS 0x30 /* 32 bits */ -#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM, - bits 31..11 are address, - 10..2 are reserved */ - -#define PCI_CAPABILITY_LIST 0x34 /* Offset of first capability list entry */ - -#define PCI_INTERRUPT_LINE 0x3c /* IRQ number (0-15) */ -#define PCI_INTERRUPT_PIN 0x3d /* IRQ pin on PCI bus (A-D) */ - -/* Header type 1 (PCI-to-PCI bridges) */ -#define PCI_PRIMARY_BUS 0x18 /* Primary bus number */ -#define PCI_SECONDARY_BUS 0x19 /* Secondary bus number */ -#define PCI_SUBORDINATE_BUS 0x1a /* Highest bus number behind the bridge */ -#define PCI_SEC_LATENCY_TIMER 0x1b /* Latency timer for secondary interface */ -#define PCI_IO_BASE 0x1c /* I/O range behind the bridge */ -#define PCI_IO_LIMIT 0x1d -#define PCI_IO_RANGE_TYPE_MASK 0x0f /* I/O bridging type */ -#define PCI_IO_RANGE_TYPE_16 0x00 -#define PCI_IO_RANGE_TYPE_32 0x01 -#define PCI_IO_RANGE_MASK ~0x0f -#define PCI_SEC_STATUS 0x1e /* Secondary status register, only bit 14 used */ -#define PCI_MEMORY_BASE 0x20 /* Memory range behind */ -#define PCI_MEMORY_LIMIT 0x22 -#define PCI_MEMORY_RANGE_TYPE_MASK 0x0f -#define PCI_MEMORY_RANGE_MASK ~0x0f -#define PCI_PREF_MEMORY_BASE 0x24 /* Prefetchable memory range behind */ -#define PCI_PREF_MEMORY_LIMIT 0x26 -#define PCI_PREF_RANGE_TYPE_MASK 0x0f -#define PCI_PREF_RANGE_TYPE_32 0x00 -#define PCI_PREF_RANGE_TYPE_64 0x01 -#define PCI_PREF_RANGE_MASK ~0x0f -#define PCI_PREF_BASE_UPPER32 0x28 /* Upper half of prefetchable memory range */ -#define PCI_PREF_LIMIT_UPPER32 0x2c -#define PCI_IO_BASE_UPPER16 0x30 /* Upper half of I/O addresses */ -#define PCI_IO_LIMIT_UPPER16 0x32 -/* 0x34 same as for htype 0 */ -/* 0x35-0x3b is reserved */ -#define PCI_ROM_ADDRESS1 0x38 /* Same as PCI_ROM_ADDRESS, but for htype 1 */ -/* 0x3c-0x3d are same as for htype 0 */ -#define PCI_BRIDGE_CONTROL 0x3e -#define PCI_BRIDGE_CTL_PARITY 0x01 /* Enable parity detection on secondary interface */ -#define PCI_BRIDGE_CTL_SERR 0x02 /* The same for SERR forwarding */ -#define PCI_BRIDGE_CTL_NO_ISA 0x04 /* Disable bridging of ISA ports */ -#define PCI_BRIDGE_CTL_VGA 0x08 /* Forward VGA addresses */ -#define PCI_BRIDGE_CTL_MASTER_ABORT 0x20 /* Report master aborts */ -#define PCI_BRIDGE_CTL_BUS_RESET 0x40 /* Secondary bus reset */ -#define PCI_BRIDGE_CTL_FAST_BACK 0x80 /* Fast Back2Back enabled on secondary interface */ +/** PCI expansion ROM base address */ +#define PCI_ROM_ADDRESS 0x30 +/** PCI capabilities pointer */ +#define PCI_CAPABILITY_LIST 0x34 + +/** CardBus capabilities pointer */ #define PCI_CB_CAPABILITY_LIST 0x14 -/* Capability lists */ - -#define PCI_CAP_LIST_ID 0 /* Capability ID */ -#define PCI_CAP_ID_PM 0x01 /* Power Management */ -#define PCI_CAP_ID_AGP 0x02 /* Accelerated Graphics Port */ -#define PCI_CAP_ID_VPD 0x03 /* Vital Product Data */ -#define PCI_CAP_ID_SLOTID 0x04 /* Slot Identification */ -#define PCI_CAP_ID_MSI 0x05 /* Message Signalled Interrupts */ -#define PCI_CAP_ID_CHSWP 0x06 /* CompactPCI HotSwap */ -#define PCI_CAP_ID_VNDR 0x09 /* Vendor specific */ -#define PCI_CAP_ID_EXP 0x10 /* PCI Express */ -#define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ -#define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ -#define PCI_CAP_SIZEOF 4 - -/* Power Management Registers */ - -#define PCI_PM_PMC 2 /* PM Capabilities Register */ -#define PCI_PM_CAP_VER_MASK 0x0007 /* Version */ -#define PCI_PM_CAP_PME_CLOCK 0x0008 /* PME clock required */ -#define PCI_PM_CAP_RESERVED 0x0010 /* Reserved field */ -#define PCI_PM_CAP_DSI 0x0020 /* Device specific initialization */ -#define PCI_PM_CAP_AUX_POWER 0x01C0 /* Auxiliary power support mask */ -#define PCI_PM_CAP_D1 0x0200 /* D1 power state support */ -#define PCI_PM_CAP_D2 0x0400 /* D2 power state support */ -#define PCI_PM_CAP_PME 0x0800 /* PME pin supported */ -#define PCI_PM_CAP_PME_MASK 0xF800 /* PME Mask of all supported states */ -#define PCI_PM_CAP_PME_D0 0x0800 /* PME# from D0 */ -#define PCI_PM_CAP_PME_D1 0x1000 /* PME# from D1 */ -#define PCI_PM_CAP_PME_D2 0x2000 /* PME# from D2 */ -#define PCI_PM_CAP_PME_D3 0x4000 /* PME# from D3 (hot) */ -#define PCI_PM_CAP_PME_D3cold 0x8000 /* PME# from D3 (cold) */ -#define PCI_PM_CTRL 4 /* PM control and status register */ -#define PCI_PM_CTRL_STATE_MASK 0x0003 /* Current power state (D0 to D3) */ -#define PCI_PM_CTRL_PME_ENABLE 0x0100 /* PME pin enable */ -#define PCI_PM_CTRL_DATA_SEL_MASK 0x1e00 /* Data select (??) */ -#define PCI_PM_CTRL_DATA_SCALE_MASK 0x6000 /* Data scale (??) */ -#define PCI_PM_CTRL_PME_STATUS 0x8000 /* PME pin status */ -#define PCI_PM_PPB_EXTENSIONS 6 /* PPB support extensions (??) */ -#define PCI_PM_PPB_B2_B3 0x40 /* Stop clock when in D3hot (??) */ -#define PCI_PM_BPCC_ENABLE 0x80 /* Bus power/clock control enable (??) */ -#define PCI_PM_DATA_REGISTER 7 /* (??) */ -#define PCI_PM_SIZEOF 8 - -/* AGP registers */ - -#define PCI_AGP_VERSION 2 /* BCD version number */ -#define PCI_AGP_RFU 3 /* Rest of capability flags */ -#define PCI_AGP_STATUS 4 /* Status register */ -#define PCI_AGP_STATUS_RQ_MASK 0xff000000 /* Maximum number of requests - 1 */ -#define PCI_AGP_STATUS_SBA 0x0200 /* Sideband addressing supported */ -#define PCI_AGP_STATUS_64BIT 0x0020 /* 64-bit addressing supported */ -#define PCI_AGP_STATUS_FW 0x0010 /* FW transfers supported */ -#define PCI_AGP_STATUS_RATE4 0x0004 /* 4x transfer rate supported */ -#define PCI_AGP_STATUS_RATE2 0x0002 /* 2x transfer rate supported */ -#define PCI_AGP_STATUS_RATE1 0x0001 /* 1x transfer rate supported */ -#define PCI_AGP_COMMAND 8 /* Control register */ -#define PCI_AGP_COMMAND_RQ_MASK 0xff000000 /* Master: Maximum number of requests */ -#define PCI_AGP_COMMAND_SBA 0x0200 /* Sideband addressing enabled */ -#define PCI_AGP_COMMAND_AGP 0x0100 /* Allow processing of AGP transactions */ -#define PCI_AGP_COMMAND_64BIT 0x0020 /* Allow processing of 64-bit addresses */ -#define PCI_AGP_COMMAND_FW 0x0010 /* Force FW transfers */ -#define PCI_AGP_COMMAND_RATE4 0x0004 /* Use 4x rate */ -#define PCI_AGP_COMMAND_RATE2 0x0002 /* Use 2x rate */ -#define PCI_AGP_COMMAND_RATE1 0x0001 /* Use 1x rate */ -#define PCI_AGP_SIZEOF 12 - -/* Slot Identification */ - -#define PCI_SID_ESR 2 /* Expansion Slot Register */ -#define PCI_SID_ESR_NSLOTS 0x1f /* Number of expansion slots available */ -#define PCI_SID_ESR_FIC 0x20 /* First In Chassis Flag */ -#define PCI_SID_CHASSIS_NR 3 /* Chassis Number */ - -/* Message Signalled Interrupts registers */ - -#define PCI_MSI_FLAGS 2 /* Various flags */ -#define PCI_MSI_FLAGS_64BIT 0x80 /* 64-bit addresses allowed */ -#define PCI_MSI_FLAGS_QSIZE 0x70 /* Message queue size configured */ -#define PCI_MSI_FLAGS_QMASK 0x0e /* Maximum queue size available */ -#define PCI_MSI_FLAGS_ENABLE 0x01 /* MSI feature enabled */ -#define PCI_MSI_RFU 3 /* Rest of capability flags */ -#define PCI_MSI_ADDRESS_LO 4 /* Lower 32 bits */ -#define PCI_MSI_ADDRESS_HI 8 /* Upper 32 bits (if PCI_MSI_FLAGS_64BIT set) */ -#define PCI_MSI_DATA_32 8 /* 16 bits of data for 32-bit devices */ -#define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ - -/* Advanced Error Reporting */ - -#define PCI_ERR_UNCOR_STATUS 4 /* Uncorrectable Error Status */ -#define PCI_ERR_UNC_TRAIN 0x00000001 /* Training */ -#define PCI_ERR_UNC_DLP 0x00000010 /* Data Link Protocol */ -#define PCI_ERR_UNC_POISON_TLP 0x00001000 /* Poisoned TLP */ -#define PCI_ERR_UNC_FCP 0x00002000 /* Flow Control Protocol */ -#define PCI_ERR_UNC_COMP_TIME 0x00004000 /* Completion Timeout */ -#define PCI_ERR_UNC_COMP_ABORT 0x00008000 /* Completer Abort */ -#define PCI_ERR_UNC_UNX_COMP 0x00010000 /* Unexpected Completion */ -#define PCI_ERR_UNC_RX_OVER 0x00020000 /* Receiver Overflow */ -#define PCI_ERR_UNC_MALF_TLP 0x00040000 /* Malformed TLP */ -#define PCI_ERR_UNC_ECRC 0x00080000 /* ECRC Error Status */ -#define PCI_ERR_UNC_UNSUP 0x00100000 /* Unsupported Request */ -#define PCI_ERR_UNCOR_MASK 8 /* Uncorrectable Error Mask */ - /* Same bits as above */ -#define PCI_ERR_UNCOR_SEVER 12 /* Uncorrectable Error Severity */ - /* Same bits as above */ -#define PCI_ERR_COR_STATUS 16 /* Correctable Error Status */ -#define PCI_ERR_COR_RCVR 0x00000001 /* Receiver Error Status */ -#define PCI_ERR_COR_BAD_TLP 0x00000040 /* Bad TLP Status */ -#define PCI_ERR_COR_BAD_DLLP 0x00000080 /* Bad DLLP Status */ -#define PCI_ERR_COR_REP_ROLL 0x00000100 /* REPLAY_NUM Rollover */ -#define PCI_ERR_COR_REP_TIMER 0x00001000 /* Replay Timer Timeout */ -#define PCI_ERR_COR_MASK 20 /* Correctable Error Mask */ - /* Same bits as above */ +/** PCI interrupt line */ +#define PCI_INTERRUPT_LINE 0x3c + +/** Capability ID */ +#define PCI_CAP_ID 0x00 +#define PCI_CAP_ID_PM 0x01 /**< Power management */ +#define PCI_CAP_ID_VPD 0x03 /**< Vital product data */ +#define PCI_CAP_ID_VNDR 0x09 /**< Vendor-specific */ +#define PCI_CAP_ID_EXP 0x10 /**< PCI Express */ + +/** Next capability */ +#define PCI_CAP_NEXT 0x01 + +/** Power management control and status */ +#define PCI_PM_CTRL 0x04 +#define PCI_PM_CTRL_STATE_MASK 0x0003 /**< Current power state */ +#define PCI_PM_CTRL_PME_ENABLE 0x0100 /**< PME pin enable */ +#define PCI_PM_CTRL_PME_STATUS 0x8000 /**< PME pin status */ + +/** Uncorrectable error status */ +#define PCI_ERR_UNCOR_STATUS 0x04 + +/** Network controller */ +#define PCI_CLASS_NETWORK 0x02 + +/** Serial bus controller */ +#define PCI_CLASS_SERIAL 0x0c +#define PCI_CLASS_SERIAL_USB 0x03 /**< USB controller */ +#define PCI_CLASS_SERIAL_USB_UHCI 0x00 /**< UHCI USB controller */ +#define PCI_CLASS_SERIAL_USB_OHCI 0x10 /**< OHCI USB controller */ +#define PCI_CLASS_SERIAL_USB_EHCI 0x20 /**< ECHI USB controller */ +#define PCI_CLASS_SERIAL_USB_XHCI 0x30 /**< xHCI USB controller */ + +/** Construct PCI class + * + * @v base Base class (or PCI_ANY_ID) + * @v sub Subclass (or PCI_ANY_ID) + * @v progif Programming interface (or PCI_ANY_ID) + */ +#define PCI_CLASS( base, sub, progif ) \ + ( ( ( (base) & 0xff ) << 16 ) | ( ( (sub) & 0xff ) << 8 ) | \ + ( ( (progif) & 0xff) << 0 ) ) /** A PCI device ID list entry */ struct pci_device_id { @@ -279,6 +143,27 @@ struct pci_device_id { /** Match-anything ID */ #define PCI_ANY_ID 0xffff +/** A PCI class ID */ +struct pci_class_id { + /** Class */ + uint32_t class; + /** Class mask */ + uint32_t mask; +}; + +/** Construct PCI class ID + * + * @v base Base class (or PCI_ANY_ID) + * @v sub Subclass (or PCI_ANY_ID) + * @v progif Programming interface (or PCI_ANY_ID) + */ +#define PCI_CLASS_ID( base, sub, progif ) { \ + .class = PCI_CLASS ( base, sub, progif ), \ + .mask = ( ( ( ( (base) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 16 ) | \ + ( ( ( (sub) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 8 ) | \ + ( ( ( (progif) == PCI_ANY_ID ) ? 0x00 : 0xff ) << 0 ) ), \ + } + /** A PCI device */ struct pci_device { /** Generic device */ @@ -322,6 +207,8 @@ struct pci_driver { struct pci_device_id *ids; /** Number of entries in PCI ID table */ unsigned int id_count; + /** PCI class ID */ + struct pci_class_id class; /** * Probe device * @@ -352,6 +239,7 @@ struct pci_driver { #define PCI_BUSDEVFN( bus, slot, func ) \ ( ( (bus) << 8 ) | ( (slot) << 3 ) | ( (func) << 0 ) ) #define PCI_FIRST_FUNC( busdevfn ) ( (busdevfn) & ~0x07 ) +#define PCI_LAST_FUNC( busdevfn ) ( (busdevfn) | 0x07 ) #define PCI_BASE_CLASS( class ) ( (class) >> 16 ) #define PCI_SUB_CLASS( class ) ( ( (class) >> 8 ) & 0xff ) diff --git a/roms/ipxe/src/include/ipxe/pci_ids.h b/roms/ipxe/src/include/ipxe/pci_ids.h deleted file mode 100644 index 25c7782bc..000000000 --- a/roms/ipxe/src/include/ipxe/pci_ids.h +++ /dev/null @@ -1,351 +0,0 @@ -#ifndef _IPXE_PCI_IDS_H -#define _IPXE_PCI_IDS_H - -/* - * PCI Class, Vendor and Device IDs - * - * Please keep sorted. - */ - -FILE_LICENCE ( GPL2_ONLY ); - -/* Device classes and subclasses */ - -#define PCI_CLASS_NOT_DEFINED 0x0000 -#define PCI_CLASS_NOT_DEFINED_VGA 0x0001 - -#define PCI_BASE_CLASS_STORAGE 0x01 -#define PCI_CLASS_STORAGE_SCSI 0x0100 -#define PCI_CLASS_STORAGE_IDE 0x0101 -#define PCI_CLASS_STORAGE_FLOPPY 0x0102 -#define PCI_CLASS_STORAGE_IPI 0x0103 -#define PCI_CLASS_STORAGE_RAID 0x0104 -#define PCI_CLASS_STORAGE_OTHER 0x0180 - -#define PCI_BASE_CLASS_NETWORK 0x02 -#define PCI_CLASS_NETWORK_ETHERNET 0x0200 -#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201 -#define PCI_CLASS_NETWORK_FDDI 0x0202 -#define PCI_CLASS_NETWORK_ATM 0x0203 -#define PCI_CLASS_NETWORK_OTHER 0x0280 - -#define PCI_BASE_CLASS_DISPLAY 0x03 -#define PCI_CLASS_DISPLAY_VGA 0x0300 -#define PCI_CLASS_DISPLAY_XGA 0x0301 -#define PCI_CLASS_DISPLAY_3D 0x0302 -#define PCI_CLASS_DISPLAY_OTHER 0x0380 - -#define PCI_BASE_CLASS_MULTIMEDIA 0x04 -#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400 -#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401 -#define PCI_CLASS_MULTIMEDIA_PHONE 0x0402 -#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480 - -#define PCI_BASE_CLASS_MEMORY 0x05 -#define PCI_CLASS_MEMORY_RAM 0x0500 -#define PCI_CLASS_MEMORY_FLASH 0x0501 -#define PCI_CLASS_MEMORY_OTHER 0x0580 - -#define PCI_BASE_CLASS_BRIDGE 0x06 -#define PCI_CLASS_BRIDGE_HOST 0x0600 -#define PCI_CLASS_BRIDGE_ISA 0x0601 -#define PCI_CLASS_BRIDGE_EISA 0x0602 -#define PCI_CLASS_BRIDGE_MC 0x0603 -#define PCI_CLASS_BRIDGE_PCI 0x0604 -#define PCI_CLASS_BRIDGE_PCMCIA 0x0605 -#define PCI_CLASS_BRIDGE_NUBUS 0x0606 -#define PCI_CLASS_BRIDGE_CARDBUS 0x0607 -#define PCI_CLASS_BRIDGE_RACEWAY 0x0608 -#define PCI_CLASS_BRIDGE_OTHER 0x0680 - -#define PCI_BASE_CLASS_COMMUNICATION 0x07 -#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700 -#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701 -#define PCI_CLASS_COMMUNICATION_MULTISERIAL 0x0702 -#define PCI_CLASS_COMMUNICATION_MODEM 0x0703 -#define PCI_CLASS_COMMUNICATION_OTHER 0x0780 - -#define PCI_BASE_CLASS_SYSTEM 0x08 -#define PCI_CLASS_SYSTEM_PIC 0x0800 -#define PCI_CLASS_SYSTEM_DMA 0x0801 -#define PCI_CLASS_SYSTEM_TIMER 0x0802 -#define PCI_CLASS_SYSTEM_RTC 0x0803 -#define PCI_CLASS_SYSTEM_PCI_HOTPLUG 0x0804 -#define PCI_CLASS_SYSTEM_OTHER 0x0880 - -#define PCI_BASE_CLASS_INPUT 0x09 -#define PCI_CLASS_INPUT_KEYBOARD 0x0900 -#define PCI_CLASS_INPUT_PEN 0x0901 -#define PCI_CLASS_INPUT_MOUSE 0x0902 -#define PCI_CLASS_INPUT_SCANNER 0x0903 -#define PCI_CLASS_INPUT_GAMEPORT 0x0904 -#define PCI_CLASS_INPUT_OTHER 0x0980 - -#define PCI_BASE_CLASS_DOCKING 0x0a -#define PCI_CLASS_DOCKING_GENERIC 0x0a00 -#define PCI_CLASS_DOCKING_OTHER 0x0a80 - -#define PCI_BASE_CLASS_PROCESSOR 0x0b -#define PCI_CLASS_PROCESSOR_386 0x0b00 -#define PCI_CLASS_PROCESSOR_486 0x0b01 -#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02 -#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10 -#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20 -#define PCI_CLASS_PROCESSOR_MIPS 0x0b30 -#define PCI_CLASS_PROCESSOR_CO 0x0b40 - -#define PCI_BASE_CLASS_SERIAL 0x0c -#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00 -#define PCI_CLASS_SERIAL_ACCESS 0x0c01 -#define PCI_CLASS_SERIAL_SSA 0x0c02 -#define PCI_CLASS_SERIAL_USB 0x0c03 -#define PCI_CLASS_SERIAL_FIBER 0x0c04 -#define PCI_CLASS_SERIAL_SMBUS 0x0c05 - -#define PCI_BASE_CLASS_INTELLIGENT 0x0e -#define PCI_CLASS_INTELLIGENT_I2O 0x0e00 - -#define PCI_BASE_CLASS_SATELLITE 0x0f -#define PCI_CLASS_SATELLITE_TV 0x0f00 -#define PCI_CLASS_SATELLITE_AUDIO 0x0f01 -#define PCI_CLASS_SATELLITE_VOICE 0x0f03 -#define PCI_CLASS_SATELLITE_DATA 0x0f04 - -#define PCI_BASE_CLASS_CRYPT 0x10 -#define PCI_CLASS_CRYPT_NETWORK 0x1000 -#define PCI_CLASS_CRYPT_ENTERTAINMENT 0x1001 -#define PCI_CLASS_CRYPT_OTHER 0x1080 - -#define PCI_BASE_CLASS_SIGNAL_PROCESSING 0x11 -#define PCI_CLASS_SP_DPIO 0x1100 -#define PCI_CLASS_SP_OTHER 0x1180 - -#define PCI_CLASS_OTHERS 0xff - -/* Vendors */ - -#define PCI_VENDOR_ID_DYNALINK 0x0675 -#define PCI_VENDOR_ID_BERKOM 0x0871 -#define PCI_VENDOR_ID_COMPAQ 0x0e11 -#define PCI_VENDOR_ID_NCR 0x1000 -#define PCI_VENDOR_ID_LSI_LOGIC 0x1000 -#define PCI_VENDOR_ID_ATI 0x1002 -#define PCI_VENDOR_ID_VLSI 0x1004 -#define PCI_VENDOR_ID_ADL 0x1005 -#define PCI_VENDOR_ID_NS 0x100b -#define PCI_VENDOR_ID_TSENG 0x100c -#define PCI_VENDOR_ID_WEITEK 0x100e -#define PCI_VENDOR_ID_DEC 0x1011 -#define PCI_VENDOR_ID_CIRRUS 0x1013 -#define PCI_VENDOR_ID_IBM 0x1014 -#define PCI_VENDOR_ID_COMPEX2 0x101a -/* pci.ids says "AT&T GIS (NCR)" */ -#define PCI_VENDOR_ID_WD 0x101c -#define PCI_VENDOR_ID_AMI 0x101e -#define PCI_VENDOR_ID_AMD 0x1022 -#define PCI_VENDOR_ID_TRIDENT 0x1023 -#define PCI_VENDOR_ID_AI 0x1025 -#define PCI_VENDOR_ID_DELL 0x1028 -#define PCI_VENDOR_ID_MATROX 0x102B -#define PCI_VENDOR_ID_CT 0x102c -#define PCI_VENDOR_ID_MIRO 0x1031 -#define PCI_VENDOR_ID_NEC 0x1033 -#define PCI_VENDOR_ID_FD 0x1036 -#define PCI_VENDOR_ID_SIS 0x1039 -#define PCI_VENDOR_ID_SI 0x1039 -#define PCI_VENDOR_ID_HP 0x103c -#define PCI_VENDOR_ID_PCTECH 0x1042 -#define PCI_VENDOR_ID_ASUSTEK 0x1043 -#define PCI_VENDOR_ID_DPT 0x1044 -#define PCI_VENDOR_ID_OPTI 0x1045 -#define PCI_VENDOR_ID_ELSA 0x1048 -#define PCI_VENDOR_ID_ELSA 0x1048 -#define PCI_VENDOR_ID_SGS 0x104a -#define PCI_VENDOR_ID_BUSLOGIC 0x104B -#define PCI_VENDOR_ID_TI 0x104c -#define PCI_VENDOR_ID_SONY 0x104d -#define PCI_VENDOR_ID_OAK 0x104e -/* Winbond have two vendor IDs! See 0x10ad as well */ -#define PCI_VENDOR_ID_WINBOND2 0x1050 -#define PCI_VENDOR_ID_ANIGMA 0x1051 -#define PCI_VENDOR_ID_EFAR 0x1055 -#define PCI_VENDOR_ID_MOTOROLA 0x1057 -#define PCI_VENDOR_ID_MOTOROLA_OOPS 0x1507 -#define PCI_VENDOR_ID_PROMISE 0x105a -#define PCI_VENDOR_ID_N9 0x105d -#define PCI_VENDOR_ID_UMC 0x1060 -#define PCI_VENDOR_ID_X 0x1061 -#define PCI_VENDOR_ID_MYLEX 0x1069 -#define PCI_VENDOR_ID_PICOP 0x1066 -#define PCI_VENDOR_ID_APPLE 0x106b -#define PCI_VENDOR_ID_YAMAHA 0x1073 -#define PCI_VENDOR_ID_NEXGEN 0x1074 -#define PCI_VENDOR_ID_QLOGIC 0x1077 -#define PCI_VENDOR_ID_CYRIX 0x1078 -#define PCI_VENDOR_ID_LEADTEK 0x107d -#define PCI_VENDOR_ID_INTERPHASE 0x107e -#define PCI_VENDOR_ID_CONTAQ 0x1080 -#define PCI_VENDOR_ID_FOREX 0x1083 -#define PCI_VENDOR_ID_OLICOM 0x108d -#define PCI_VENDOR_ID_SUN 0x108e -#define PCI_VENDOR_ID_CMD 0x1095 -#define PCI_VENDOR_ID_VISION 0x1098 -#define PCI_VENDOR_ID_BROOKTREE 0x109e -#define PCI_VENDOR_ID_SIERRA 0x10a8 -#define PCI_VENDOR_ID_SGI 0x10a9 -#define PCI_VENDOR_ID_ACC 0x10aa -#define PCI_VENDOR_ID_WINBOND 0x10ad -#define PCI_VENDOR_ID_DATABOOK 0x10b3 -#define PCI_VENDOR_ID_PLX 0x10b5 -#define PCI_VENDOR_ID_MADGE 0x10b6 -#define PCI_VENDOR_ID_3COM 0x10b7 -#define PCI_VENDOR_ID_SMC 0x10b8 -#define PCI_VENDOR_ID_SUNDANCE 0x13F0 -#define PCI_VENDOR_ID_AL 0x10b9 -#define PCI_VENDOR_ID_MITSUBISHI 0x10ba -#define PCI_VENDOR_ID_SURECOM 0x10bd -#define PCI_VENDOR_ID_NEOMAGIC 0x10c8 -#define PCI_VENDOR_ID_ASP 0x10cd -#define PCI_VENDOR_ID_MACRONIX 0x10d9 -#define PCI_VENDOR_ID_TCONRAD 0x10da -#define PCI_VENDOR_ID_CERN 0x10dc -#define PCI_VENDOR_ID_NVIDIA 0x10de -#define PCI_VENDOR_ID_IMS 0x10e0 -#define PCI_VENDOR_ID_TEKRAM2 0x10e1 -#define PCI_VENDOR_ID_TUNDRA 0x10e3 -#define PCI_VENDOR_ID_AMCC 0x10e8 -#define PCI_VENDOR_ID_INTERG 0x10ea -#define PCI_VENDOR_ID_REALTEK 0x10ec -#define PCI_VENDOR_ID_XILINX 0x10ee -#define PCI_VENDOR_ID_TRUEVISION 0x10fa -#define PCI_VENDOR_ID_INIT 0x1101 -#define PCI_VENDOR_ID_CREATIVE 0x1102 -/* duplicate: ECTIVA */ -#define PCI_VENDOR_ID_ECTIVA 0x1102 -/* duplicate: CREATIVE */ -#define PCI_VENDOR_ID_TTI 0x1103 -#define PCI_VENDOR_ID_VIA 0x1106 -#define PCI_VENDOR_ID_VIATEC 0x1106 -#define PCI_VENDOR_ID_SIEMENS 0x110A -#define PCI_VENDOR_ID_SMC2 0x1113 -#define PCI_VENDOR_ID_VORTEX 0x1119 -#define PCI_VENDOR_ID_EF 0x111a -#define PCI_VENDOR_ID_IDT 0x111d -#define PCI_VENDOR_ID_FORE 0x1127 -#define PCI_VENDOR_ID_IMAGINGTECH 0x112f -#define PCI_VENDOR_ID_PHILIPS 0x1131 -#define PCI_VENDOR_ID_EICON 0x1133 -#define PCI_VENDOR_ID_CYCLONE 0x113c -#define PCI_VENDOR_ID_ALLIANCE 0x1142 -#define PCI_VENDOR_ID_SYSKONNECT 0x1148 -#define PCI_VENDOR_ID_VMIC 0x114a -#define PCI_VENDOR_ID_DIGI 0x114f -#define PCI_VENDOR_ID_MUTECH 0x1159 -#define PCI_VENDOR_ID_XIRCOM 0x115d -#define PCI_VENDOR_ID_RENDITION 0x1163 -#define PCI_VENDOR_ID_SERVERWORKS 0x1166 -#define PCI_VENDOR_ID_SBE 0x1176 -#define PCI_VENDOR_ID_TOSHIBA 0x1179 -#define PCI_VENDOR_ID_RICOH 0x1180 -#define PCI_VENDOR_ID_DLINK 0x1186 -#define PCI_VENDOR_ID_ARTOP 0x1191 -#define PCI_VENDOR_ID_ZEITNET 0x1193 -#define PCI_VENDOR_ID_OMEGA 0x119b -#define PCI_VENDOR_ID_FUJITSU_ME 0x119e -#define PCI_SUBVENDOR_ID_KEYSPAN 0x11a9 -#define PCI_VENDOR_ID_GALILEO 0x11ab -#define PCI_VENDOR_ID_LINKSYS 0x11ad -#define PCI_VENDOR_ID_LITEON 0x11ad -#define PCI_VENDOR_ID_V3 0x11b0 -#define PCI_VENDOR_ID_NP 0x11bc -#define PCI_VENDOR_ID_ATT 0x11c1 -#define PCI_VENDOR_ID_SPECIALIX 0x11cb -#define PCI_VENDOR_ID_AURAVISION 0x11d1 -#define PCI_VENDOR_ID_ANALOG_DEVICES 0x11d4 -#define PCI_VENDOR_ID_IKON 0x11d5 -#define PCI_VENDOR_ID_ZORAN 0x11de -#define PCI_VENDOR_ID_KINETIC 0x11f4 -#define PCI_VENDOR_ID_COMPEX 0x11f6 -#define PCI_VENDOR_ID_RP 0x11fe -#define PCI_VENDOR_ID_CYCLADES 0x120e -#define PCI_VENDOR_ID_ESSENTIAL 0x120f -#define PCI_VENDOR_ID_O2 0x1217 -#define PCI_VENDOR_ID_3DFX 0x121a -#define PCI_VENDOR_ID_SIGMADES 0x1236 -#define PCI_VENDOR_ID_CCUBE 0x123f -#define PCI_VENDOR_ID_AVM 0x1244 -#define PCI_VENDOR_ID_DIPIX 0x1246 -#define PCI_VENDOR_ID_STALLION 0x124d -#define PCI_VENDOR_ID_OPTIBASE 0x1255 -#define PCI_VENDOR_ID_ESS 0x125d -#define PCI_VENDOR_ID_HARRIS 0x1260 -#define PCI_VENDOR_ID_SATSAGEM 0x1267 -#define PCI_VENDOR_ID_HUGHES 0x1273 -#define PCI_VENDOR_ID_ENSONIQ 0x1274 -#define PCI_VENDOR_ID_ROCKWELL 0x127A -#define PCI_VENDOR_ID_DAVICOM 0x1282 -#define PCI_VENDOR_ID_ITE 0x1283 -/* formerly Platform Tech */ -#define PCI_VENDOR_ID_ESS_OLD 0x1285 -#define PCI_VENDOR_ID_ALTEON 0x12ae -#define PCI_VENDOR_ID_USR 0x12B9 -#define PCI_VENDOR_ID_HOLTEK 0x12c3 -#define PCI_SUBVENDOR_ID_CONNECT_TECH 0x12c4 -#define PCI_VENDOR_ID_PICTUREL 0x12c5 -#define PCI_VENDOR_ID_NVIDIA_SGS 0x12d2 -#define PCI_SUBVENDOR_ID_CHASE_PCIFAST 0x12E0 -#define PCI_SUBVENDOR_ID_CHASE_PCIRAS 0x124D -#define PCI_VENDOR_ID_AUREAL 0x12eb -#define PCI_VENDOR_ID_CBOARDS 0x1307 -#define PCI_VENDOR_ID_SIIG 0x131f -#define PCI_VENDOR_ID_ADMTEK 0x1317 -#define PCI_VENDOR_ID_DOMEX 0x134a -#define PCI_VENDOR_ID_QUATECH 0x135C -#define PCI_VENDOR_ID_SEALEVEL 0x135e -#define PCI_VENDOR_ID_HYPERCOPE 0x1365 -#define PCI_VENDOR_ID_KAWASAKI 0x136b -#define PCI_VENDOR_ID_LMC 0x1376 -#define PCI_VENDOR_ID_NETGEAR 0x1385 -#define PCI_VENDOR_ID_APPLICOM 0x1389 -#define PCI_VENDOR_ID_MOXA 0x1393 -#define PCI_VENDOR_ID_CCD 0x1397 -#define PCI_VENDOR_ID_MICROGATE 0x13c0 -#define PCI_VENDOR_ID_3WARE 0x13C1 -#define PCI_VENDOR_ID_ABOCOM 0x13D1 -#define PCI_VENDOR_ID_CMEDIA 0x13f6 -#define PCI_VENDOR_ID_LAVA 0x1407 -#define PCI_VENDOR_ID_TIMEDIA 0x1409 -#define PCI_VENDOR_ID_OXSEMI 0x1415 -#define PCI_VENDOR_ID_AIRONET 0x14b9 -#define PCI_VENDOR_ID_MYRICOM 0x14c1 -#define PCI_VENDOR_ID_TITAN 0x14D2 -#define PCI_VENDOR_ID_PANACOM 0x14d4 -#define PCI_VENDOR_ID_BROADCOM 0x14e4 -#define PCI_VENDOR_ID_SYBA 0x1592 -#define PCI_VENDOR_ID_MORETON 0x15aa -#define PCI_VENDOR_ID_ZOLTRIX 0x15b0 -#define PCI_VENDOR_ID_PDC 0x15e9 -#define PCI_VENDOR_ID_FSC 0x1734 -#define PCI_VENDOR_ID_SYMPHONY 0x1c1c -#define PCI_VENDOR_ID_TEKRAM 0x1de1 -#define PCI_VENDOR_ID_3DLABS 0x3d3d -#define PCI_VENDOR_ID_AVANCE 0x4005 -#define PCI_VENDOR_ID_AKS 0x416c -#define PCI_VENDOR_ID_NETVIN 0x4a14 -#define PCI_VENDOR_ID_S3 0x5333 -#define PCI_VENDOR_ID_DCI 0x6666 -#define PCI_VENDOR_ID_GENROCO 0x5555 -#define PCI_VENDOR_ID_INTEL 0x8086 -#define PCI_VENDOR_ID_COMPUTONE 0x8e0e -#define PCI_SUBVENDOR_ID_COMPUTONE 0x8e0e -#define PCI_VENDOR_ID_KTI 0x8e2e -#define PCI_VENDOR_ID_ADAPTEC 0x9004 -#define PCI_VENDOR_ID_ADAPTEC2 0x9005 -#define PCI_VENDOR_ID_ATRONICS 0x907f -#define PCI_VENDOR_ID_HOLTEK2 0x9412 -#define PCI_VENDOR_ID_NETMOS 0x9710 -#define PCI_SUBVENDOR_ID_EXSYS 0xd84d -#define PCI_VENDOR_ID_TIGERJET 0xe159 -#define PCI_VENDOR_ID_ARK 0xedd8 - -#endif /* _IPXE_PCI_IDS_H */ diff --git a/roms/ipxe/src/include/ipxe/pci_io.h b/roms/ipxe/src/include/ipxe/pci_io.h index 781b77fe1..10e69763e 100644 --- a/roms/ipxe/src/include/ipxe/pci_io.h +++ b/roms/ipxe/src/include/ipxe/pci_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/api.h> diff --git a/roms/ipxe/src/include/ipxe/pcibackup.h b/roms/ipxe/src/include/ipxe/pcibackup.h index b9f55cf71..159d25392 100644 --- a/roms/ipxe/src/include/ipxe/pcibackup.h +++ b/roms/ipxe/src/include/ipxe/pcibackup.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/pcivpd.h b/roms/ipxe/src/include/ipxe/pcivpd.h index 0abf8a956..fefb69740 100644 --- a/roms/ipxe/src/include/ipxe/pcivpd.h +++ b/roms/ipxe/src/include/ipxe/pcivpd.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <byteswap.h> diff --git a/roms/ipxe/src/include/ipxe/peerblk.h b/roms/ipxe/src/include/ipxe/peerblk.h new file mode 100644 index 000000000..6fc9172f6 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/peerblk.h @@ -0,0 +1,144 @@ +#ifndef _IPXE_PEERBLK_H +#define _IPXE_PEERBLK_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol block downloads + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/refcnt.h> +#include <ipxe/interface.h> +#include <ipxe/crypto.h> +#include <ipxe/aes.h> +#include <ipxe/xferbuf.h> +#include <ipxe/retry.h> +#include <ipxe/process.h> +#include <ipxe/pccrc.h> +#include <ipxe/peerdisc.h> + +/** A PeerDist retrieval protocol decryption buffer descriptor */ +struct peerdist_block_decrypt { + /** Data transfer buffer */ + struct xfer_buffer *xferbuf; + /** Offset within data transfer buffer */ + size_t offset; + /** Length to use from data transfer buffer */ + size_t len; +}; + +/** PeerDist retrieval protocol decryption data transfer buffer indices */ +enum peerdist_block_decrypt_index { + /** Data before the trimmed content */ + PEERBLK_BEFORE = 0, + /** Data within the trimmed content */ + PEERBLK_DURING, + /** Data after the trimmed content */ + PEERBLK_AFTER, + /** Number of decryption buffers */ + PEERBLK_NUM_BUFFERS +}; + +/** A PeerDist block download */ +struct peerdist_block { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Raw data interface */ + struct interface raw; + /** Retrieval protocol interface */ + struct interface retrieval; + + /** Original URI */ + struct uri *uri; + /** Content range of this block */ + struct peerdist_range range; + /** Trimmed range of this block */ + struct peerdist_range trim; + /** Offset of first byte in trimmed range within overall download */ + size_t offset; + + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Digest size + * + * Note that this may be shorter than the digest size of the + * digest algorithm. + */ + size_t digestsize; + /** Digest context (statically allocated at instantiation time) */ + void *digestctx; + + /** Cipher algorithm */ + struct cipher_algorithm *cipher; + /** Cipher context (dynamically allocated as needed) */ + void *cipherctx; + + /** Segment index */ + unsigned int segment; + /** Segment identifier */ + uint8_t id[PEERDIST_DIGEST_MAX_SIZE]; + /** Segment secret */ + uint8_t secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Block index */ + unsigned int block; + /** Block hash */ + uint8_t hash[PEERDIST_DIGEST_MAX_SIZE]; + + /** Current position (relative to incoming data stream) */ + size_t pos; + /** Start of trimmed content (relative to incoming data stream) */ + size_t start; + /** End of trimmed content (relative to incoming data stream) */ + size_t end; + /** Data buffer */ + struct xfer_buffer buffer; + + /** Decryption process */ + struct process process; + /** Decryption data buffer descriptors */ + struct peerdist_block_decrypt decrypt[PEERBLK_NUM_BUFFERS]; + /** Remaining decryption length */ + size_t cipher_remaining; + /** Remaining digest length (excluding AES padding bytes) */ + size_t digest_remaining; + + /** Discovery client */ + struct peerdisc_client discovery; + /** Current position in discovered peer list */ + struct peerdisc_peer *peer; + /** Retry timer */ + struct retry_timer timer; + /** Number of full attempt cycles completed */ + unsigned int cycles; + /** Most recent attempt failure */ + int rc; + + /** Time at which block download was started */ + unsigned long started; + /** Time at which most recent attempt was started */ + unsigned long attempted; +}; + +/** Retrieval protocol block fetch response (including transport header) + * + * @v digestsize Digest size + * @v len Data block length + * @v vrf_len Length of uselessness + * @v blksize Cipher block size + */ +#define peerblk_msg_blk_t( digestsize, len, vrf_len, blksize ) \ + struct { \ + struct peerdist_msg_transport_header hdr; \ + peerdist_msg_blk_t ( digestsize, len, vrf_len, \ + blksize ) msg; \ + } __attribute__ (( packed )) + +extern int peerblk_open ( struct interface *xfer, struct uri *uri, + struct peerdist_info_block *block ); + +#endif /* _IPXE_PEERBLK_H */ diff --git a/roms/ipxe/src/include/ipxe/peerdisc.h b/roms/ipxe/src/include/ipxe/peerdisc.h new file mode 100644 index 000000000..f08ccaae2 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/peerdisc.h @@ -0,0 +1,116 @@ +#ifndef _IPXE_PEERDISC_H +#define _IPXE_PEERDISC_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/refcnt.h> +#include <ipxe/list.h> +#include <ipxe/tables.h> +#include <ipxe/retry.h> +#include <ipxe/socket.h> +#include <ipxe/interface.h> +#include <ipxe/pccrc.h> + +/** A PeerDist discovery socket */ +struct peerdisc_socket { + /** Name */ + const char *name; + /** Data transfer interface */ + struct interface xfer; + /** Socket address */ + union { + struct sockaddr sa; + struct sockaddr_in sin; + struct sockaddr_in6 sin6; + } address; +}; + +/** PeerDist discovery socket table */ +#define PEERDISC_SOCKETS __table ( struct peerdisc_socket, "peerdisc_sockets" ) + +/** Declare a PeerDist discovery socket */ +#define __peerdisc_socket __table_entry ( PEERDISC_SOCKETS, 01 ) + +/** A PeerDist discovery segment */ +struct peerdisc_segment { + /** Reference count */ + struct refcnt refcnt; + /** List of segments */ + struct list_head list; + /** Segment identifier string + * + * This is MS-PCCRC's "HoHoDk", transcribed as an upper-case + * Base16-encoded string. + */ + const char *id; + /** Message UUID string */ + const char *uuid; + /** List of discovered peers + * + * The list of peers may be appended to during the lifetime of + * the discovery segment. Discovered peers will not be + * removed from the list until the last discovery has been + * closed; this allows users to safely maintain a pointer to a + * current position within the list. + */ + struct list_head peers; + /** List of active clients */ + struct list_head clients; + /** Transmission timer */ + struct retry_timer timer; +}; + +/** A PeerDist discovery peer */ +struct peerdisc_peer { + /** List of peers */ + struct list_head list; + /** Peer location */ + char location[0]; +}; + +/** A PeerDist discovery client */ +struct peerdisc_client { + /** Discovery segment */ + struct peerdisc_segment *segment; + /** List of clients */ + struct list_head list; + /** Operations */ + struct peerdisc_client_operations *op; +}; + +/** PeerDist discovery client operations */ +struct peerdisc_client_operations { + /** New peers have been discovered + * + * @v peerdisc PeerDist discovery client + */ + void ( * discovered ) ( struct peerdisc_client *peerdisc ); +}; + +/** + * Initialise PeerDist discovery + * + * @v peerdisc PeerDist discovery client + * @v op Discovery operations + */ +static inline __attribute__ (( always_inline )) void +peerdisc_init ( struct peerdisc_client *peerdisc, + struct peerdisc_client_operations *op ) { + + peerdisc->op = op; +} + +extern unsigned int peerdisc_timeout_secs; + +extern int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, + size_t len ); +extern void peerdisc_close ( struct peerdisc_client *peerdisc ); + +#endif /* _IPXE_PEERDISC_H */ diff --git a/roms/ipxe/src/include/ipxe/peermux.h b/roms/ipxe/src/include/ipxe/peermux.h new file mode 100644 index 000000000..44cbdb9d6 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/peermux.h @@ -0,0 +1,73 @@ +#ifndef _IPXE_PEERMUX_H +#define _IPXE_PEERMUX_H + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/list.h> +#include <ipxe/refcnt.h> +#include <ipxe/interface.h> +#include <ipxe/process.h> +#include <ipxe/uri.h> +#include <ipxe/xferbuf.h> +#include <ipxe/pccrc.h> + +/** Maximum number of concurrent block downloads */ +#define PEERMUX_MAX_BLOCKS 32 + +/** PeerDist download content information cache */ +struct peerdist_info_cache { + /** Content information */ + struct peerdist_info info; + /** Content information segment */ + struct peerdist_info_segment segment; + /** Content information block */ + struct peerdist_info_block block; +}; + +/** A PeerDist multiplexed block download */ +struct peerdist_multiplexed_block { + /** PeerDist download multiplexer */ + struct peerdist_multiplexer *peermux; + /** List of multiplexed blocks */ + struct list_head list; + /** Data transfer interface */ + struct interface xfer; +}; + +/** A PeerDist download multiplexer */ +struct peerdist_multiplexer { + /** Reference count */ + struct refcnt refcnt; + /** Data transfer interface */ + struct interface xfer; + /** Content information interface */ + struct interface info; + /** Original URI */ + struct uri *uri; + + /** Content information data transfer buffer */ + struct xfer_buffer buffer; + /** Content information cache */ + struct peerdist_info_cache cache; + + /** Block download initiation process */ + struct process process; + /** List of busy block downloads */ + struct list_head busy; + /** List of idle block downloads */ + struct list_head idle; + /** Block downloads */ + struct peerdist_multiplexed_block block[PEERMUX_MAX_BLOCKS]; +}; + +extern int peermux_filter ( struct interface *xfer, struct interface *info, + struct uri *uri ); + +#endif /* _IPXE_PEERMUX_H */ diff --git a/roms/ipxe/src/include/ipxe/pending.h b/roms/ipxe/src/include/ipxe/pending.h index e6a369813..be6ed05a1 100644 --- a/roms/ipxe/src/include/ipxe/pending.h +++ b/roms/ipxe/src/include/ipxe/pending.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A pending operation */ struct pending_operation { diff --git a/roms/ipxe/src/include/ipxe/ping.h b/roms/ipxe/src/include/ipxe/ping.h index 6cd376b6f..c55bd1ab2 100644 --- a/roms/ipxe/src/include/ipxe/ping.h +++ b/roms/ipxe/src/include/ipxe/ping.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/iobuf.h> #include <ipxe/tcpip.h> diff --git a/roms/ipxe/src/include/ipxe/pinger.h b/roms/ipxe/src/include/ipxe/pinger.h index 9932df6b0..227f002dc 100644 --- a/roms/ipxe/src/include/ipxe/pinger.h +++ b/roms/ipxe/src/include/ipxe/pinger.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/interface.h> diff --git a/roms/ipxe/src/include/ipxe/pixbuf.h b/roms/ipxe/src/include/ipxe/pixbuf.h index 106b666e6..615744812 100644 --- a/roms/ipxe/src/include/ipxe/pixbuf.h +++ b/roms/ipxe/src/include/ipxe/pixbuf.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/png.h b/roms/ipxe/src/include/ipxe/png.h index f51d1e6fe..3505eefc8 100644 --- a/roms/ipxe/src/include/ipxe/png.h +++ b/roms/ipxe/src/include/ipxe/png.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <byteswap.h> diff --git a/roms/ipxe/src/include/ipxe/pnm.h b/roms/ipxe/src/include/ipxe/pnm.h index 536c14d5f..860968cbc 100644 --- a/roms/ipxe/src/include/ipxe/pnm.h +++ b/roms/ipxe/src/include/ipxe/pnm.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/include/ipxe/pool.h b/roms/ipxe/src/include/ipxe/pool.h new file mode 100644 index 000000000..27066e9b3 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/pool.h @@ -0,0 +1,127 @@ +#ifndef _IPXE_POOL_H +#define _IPXE_POOL_H + +/** @file + * + * Pooled connections + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/interface.h> +#include <ipxe/list.h> +#include <ipxe/retry.h> + +/** A pooled connection */ +struct pooled_connection { + /** List of pooled connections + * + * Note that each connecton in the pool has a running expiry + * timer which holds a reference to the connection. We + * therefore do not require the connection pool list to hold a + * reference for each pooled connection. + */ + struct list_head list; + /** Expiry timer */ + struct retry_timer timer; + /** Close expired pooled connection + * + * @v pool Pooled connection + */ + void ( * expired ) ( struct pooled_connection *pool ); + /** Flags */ + unsigned int flags; +}; + +/** Pooled connection flags */ +enum pooled_connection_flags { + /** Connection should be recycled after closing */ + POOL_RECYCLABLE = 0x0001, + /** Connection has been recycled */ + POOL_RECYCLED = 0x0002, + /** Connection is known to be alive */ + POOL_ALIVE = 0x0004, +}; + +extern void pool_add ( struct pooled_connection *pool, struct list_head *list, + unsigned long expiry ); +extern void pool_del ( struct pooled_connection *pool ); +extern void pool_expired ( struct retry_timer *timer, int over ); + +/** + * Initialise a pooled connection + * + * @v pool Pooled connection + * @v expired Close expired pooled connection method + * @v refcnt Containing object reference counter + */ +static inline __attribute__ (( always_inline )) void +pool_init ( struct pooled_connection *pool, + void ( * expired ) ( struct pooled_connection *pool ), + struct refcnt *refcnt ) { + + INIT_LIST_HEAD ( &pool->list ); + timer_init ( &pool->timer, pool_expired, refcnt ); + pool->expired = expired; +} + +/** + * Mark pooled connection as recyclable + * + * @v pool Pooled connection + */ +static inline __attribute__ (( always_inline )) void +pool_recyclable ( struct pooled_connection *pool ) { + + pool->flags |= POOL_RECYCLABLE; +} + +/** + * Mark pooled connection as alive + * + * @v pool Pooled connection + */ +static inline __attribute__ (( always_inline )) void +pool_alive ( struct pooled_connection *pool ) { + + pool->flags |= POOL_ALIVE; +} + +/** + * Check if pooled connection is recyclable + * + * @v pool Pooled connection + * @ret recyclable Pooled connection is recyclable + */ +static inline __attribute__ (( always_inline )) int +pool_is_recyclable ( struct pooled_connection *pool ) { + + return ( pool->flags & POOL_RECYCLABLE ); +} + +/** + * Check if pooled connection is reopenable + * + * @v pool Pooled connection + * @ret reopenable Pooled connection is reopenable + */ +static inline __attribute__ (( always_inline )) int +pool_is_reopenable ( struct pooled_connection *pool ) { + + /* A connection is reopenable if it has been recycled but is + * not yet known to be alive. + */ + return ( ( pool->flags & POOL_RECYCLED ) & + ( ! ( pool->flags & POOL_ALIVE ) ) ); +} + +extern void pool_recycle ( struct interface *intf ); +#define pool_recycle_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + +extern void pool_reopen ( struct interface *intf ); +#define pool_reopen_TYPE( object_type ) \ + typeof ( void ( object_type ) ) + +#endif /* _IPXE_POOL_H */ diff --git a/roms/ipxe/src/include/ipxe/portmap.h b/roms/ipxe/src/include/ipxe/portmap.h index 9b735bbca..681ca2ec2 100644 --- a/roms/ipxe/src/include/ipxe/portmap.h +++ b/roms/ipxe/src/include/ipxe/portmap.h @@ -10,7 +10,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** PORTMAP default port */ #define PORTMAP_PORT 111 diff --git a/roms/ipxe/src/include/ipxe/posix_io.h b/roms/ipxe/src/include/ipxe/posix_io.h index 11f3bb5c9..1a73b5e86 100644 --- a/roms/ipxe/src/include/ipxe/posix_io.h +++ b/roms/ipxe/src/include/ipxe/posix_io.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/include/ipxe/privkey.h b/roms/ipxe/src/include/ipxe/privkey.h index 39049ac9f..81108b6bf 100644 --- a/roms/ipxe/src/include/ipxe/privkey.h +++ b/roms/ipxe/src/include/ipxe/privkey.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/asn1.h> diff --git a/roms/ipxe/src/include/ipxe/process.h b/roms/ipxe/src/include/ipxe/process.h index 2c76ff260..d600508e7 100644 --- a/roms/ipxe/src/include/ipxe/process.h +++ b/roms/ipxe/src/include/ipxe/process.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/include/ipxe/profile.h b/roms/ipxe/src/include/ipxe/profile.h index 3a745fcfa..b6d2b19e0 100644 --- a/roms/ipxe/src/include/ipxe/profile.h +++ b/roms/ipxe/src/include/ipxe/profile.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <bits/profile.h> #include <ipxe/tables.h> @@ -186,4 +186,18 @@ profile_exclude ( struct profiler *profiler ) { profile_excluded += profile_elapsed ( profiler ); } +/** + * Record profiling sample in custom units + * + * @v profiler Profiler + * @v sample Profiling sample + */ +static inline __attribute__ (( always_inline )) void +profile_custom ( struct profiler *profiler, unsigned long sample ) { + + /* If profiling is active then update stats */ + if ( PROFILING ) + profile_update ( profiler, sample ); +} + #endif /* _IPXE_PROFILE_H */ diff --git a/roms/ipxe/src/include/ipxe/random_nz.h b/roms/ipxe/src/include/ipxe/random_nz.h index 6bb80d2ab..4c433fa38 100644 --- a/roms/ipxe/src/include/ipxe/random_nz.h +++ b/roms/ipxe/src/include/ipxe/random_nz.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/rarp.h b/roms/ipxe/src/include/ipxe/rarp.h index f84301a43..9054db21a 100644 --- a/roms/ipxe/src/include/ipxe/rarp.h +++ b/roms/ipxe/src/include/ipxe/rarp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/netdevice.h> diff --git a/roms/ipxe/src/include/ipxe/rbg.h b/roms/ipxe/src/include/ipxe/rbg.h index 9689142f8..758238a65 100644 --- a/roms/ipxe/src/include/ipxe/rbg.h +++ b/roms/ipxe/src/include/ipxe/rbg.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/drbg.h> diff --git a/roms/ipxe/src/include/ipxe/reboot.h b/roms/ipxe/src/include/ipxe/reboot.h index 97e0d5fb6..33606d9d5 100644 --- a/roms/ipxe/src/include/ipxe/reboot.h +++ b/roms/ipxe/src/include/ipxe/reboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/api.h> #include <config/reboot.h> diff --git a/roms/ipxe/src/include/ipxe/refcnt.h b/roms/ipxe/src/include/ipxe/refcnt.h index 0e8b8658c..7f489abc9 100644 --- a/roms/ipxe/src/include/ipxe/refcnt.h +++ b/roms/ipxe/src/include/ipxe/refcnt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <assert.h> diff --git a/roms/ipxe/src/include/ipxe/resolv.h b/roms/ipxe/src/include/ipxe/resolv.h index d9868a5d7..ff48d35ca 100644 --- a/roms/ipxe/src/include/ipxe/resolv.h +++ b/roms/ipxe/src/include/ipxe/resolv.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/interface.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/retry.h b/roms/ipxe/src/include/ipxe/retry.h index c514822b2..76d45fbd0 100644 --- a/roms/ipxe/src/include/ipxe/retry.h +++ b/roms/ipxe/src/include/ipxe/retry.h @@ -7,14 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/list.h> -/** Default timeout value */ +/** Default minimum timeout value (in ticks) */ #define DEFAULT_MIN_TIMEOUT ( TICKS_PER_SEC / 4 ) -/** Limit after which the timeout will be deemed permanent */ +/** Default maximum timeout value (in ticks) */ #define DEFAULT_MAX_TIMEOUT ( 10 * TICKS_PER_SEC ) /** A retry timer */ @@ -25,16 +25,18 @@ struct retry_timer { unsigned int running; /** Timeout value (in ticks) */ unsigned long timeout; - /** Minimum timeout value (in ticks) + /** Minimum timeout value (in ticks), or zero to use default * - * A value of zero means "use default timeout." + * The timeout will never be reduced below this value. */ - unsigned long min_timeout; - /** Maximum timeout value before failure (in ticks) + unsigned long min; + /** Maximum timeout value (in ticks), or zero to use default * - * A value of zero means "use default timeout." + * The timeout will be deemed permanent (according to the + * failure indicator passed to expired()) when it exceeds this + * value. */ - unsigned long max_timeout; + unsigned long max; /** Start time (in ticks) */ unsigned long start; /** Retry count */ @@ -46,7 +48,7 @@ struct retry_timer { * * The timer will already be stopped when this method is * called. The failure indicator will be True if the retry - * timeout has already exceeded @c MAX_TIMEOUT. + * timeout has already exceeded @c max_timeout. */ void ( * expired ) ( struct retry_timer *timer, int over ); /** Reference counter @@ -109,4 +111,18 @@ timer_running ( struct retry_timer *timer ) { return ( timer->running ); } +/** + * Set minimum and maximum timeouts + * + * @v timer Retry timer + * @v min Minimum timeout (in ticks), or zero to use default + * @v max Maximum timeout (in ticks), or zero to use default + */ +static inline __attribute__ (( always_inline )) void +set_timer_limits ( struct retry_timer *timer, unsigned long min, + unsigned long max ) { + timer->min = min; + timer->max = max; +} + #endif /* _IPXE_RETRY_H */ diff --git a/roms/ipxe/src/include/ipxe/rndis.h b/roms/ipxe/src/include/ipxe/rndis.h new file mode 100644 index 000000000..bcb6d8e6a --- /dev/null +++ b/roms/ipxe/src/include/ipxe/rndis.h @@ -0,0 +1,370 @@ +#ifndef _IPXE_RNDIS_H +#define _IPXE_RNDIS_H + +/** @file + * + * Remote Network Driver Interface Specification + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/netdevice.h> +#include <ipxe/iobuf.h> + +/** Maximum time to wait for a transaction to complete + * + * This is a policy decision. + */ +#define RNDIS_MAX_WAIT_MS 1000 + +/** RNDIS message header */ +struct rndis_header { + /** Message type */ + uint32_t type; + /** Message length */ + uint32_t len; +} __attribute__ (( packed )); + +/** RNDIS initialise message */ +#define RNDIS_INITIALISE_MSG 0x00000002UL + +/** RNDIS initialise message */ +struct rndis_initialise_message { + /** Request ID */ + uint32_t id; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Maximum transfer size */ + uint32_t mtu; +} __attribute__ (( packed )); + +/** Request ID used for initialisation + * + * This is a policy decision. + */ +#define RNDIS_INIT_ID 0xe110e110UL + +/** RNDIS major version */ +#define RNDIS_VERSION_MAJOR 1 + +/** RNDIS minor version */ +#define RNDIS_VERSION_MINOR 0 + +/** RNDIS maximum transfer size + * + * This is a policy decision. + */ +#define RNDIS_MTU 2048 + +/** RNDIS initialise completion */ +#define RNDIS_INITIALISE_CMPLT 0x80000002UL + +/** RNDIS initialise completion */ +struct rndis_initialise_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; + /** Major version */ + uint32_t major; + /** Minor version */ + uint32_t minor; + /** Device flags */ + uint32_t flags; + /** Medium */ + uint32_t medium; + /** Maximum packets per transfer */ + uint32_t max_pkts; + /** Maximum transfer size */ + uint32_t mtu; + /** Packet alignment factor */ + uint32_t align; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS halt message */ +#define RNDIS_HALT_MSG 0x00000003UL + +/** RNDIS halt message */ +struct rndis_halt_message { + /** Request ID */ + uint32_t id; +} __attribute__ (( packed )); + +/** RNDIS query OID message */ +#define RNDIS_QUERY_MSG 0x00000004UL + +/** RNDIS set OID message */ +#define RNDIS_SET_MSG 0x00000005UL + +/** RNDIS query or set OID message */ +struct rndis_oid_message { + /** Request ID */ + uint32_t id; + /** Object ID */ + uint32_t oid; + /** Information buffer length */ + uint32_t len; + /** Information buffer offset */ + uint32_t offset; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS query OID completion */ +#define RNDIS_QUERY_CMPLT 0x80000004UL + +/** RNDIS query OID completion */ +struct rndis_query_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; + /** Information buffer length */ + uint32_t len; + /** Information buffer offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** RNDIS set OID completion */ +#define RNDIS_SET_CMPLT 0x80000005UL + +/** RNDIS set OID completion */ +struct rndis_set_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** RNDIS reset message */ +#define RNDIS_RESET_MSG 0x00000006UL + +/** RNDIS reset message */ +struct rndis_reset_message { + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS reset completion */ +#define RNDIS_RESET_CMPLT 0x80000006UL + +/** RNDIS reset completion */ +struct rndis_reset_completion { + /** Status */ + uint32_t status; + /** Addressing reset */ + uint32_t addr; +} __attribute__ (( packed )); + +/** RNDIS indicate status message */ +#define RNDIS_INDICATE_STATUS_MSG 0x00000007UL + +/** RNDIS diagnostic information */ +struct rndis_diagnostic_info { + /** Status */ + uint32_t status; + /** Error offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** RNDIS indicate status message */ +struct rndis_indicate_status_message { + /** Status */ + uint32_t status; + /** Status buffer length */ + uint32_t len; + /** Status buffer offset */ + uint32_t offset; + /** Diagnostic information (optional) */ + struct rndis_diagnostic_info diag[0]; +} __attribute__ (( packed )); + +/** RNDIS status codes */ +enum rndis_status { + /** Device is connected to a network medium */ + RNDIS_STATUS_MEDIA_CONNECT = 0x4001000bUL, + /** Device is disconnected from the medium */ + RNDIS_STATUS_MEDIA_DISCONNECT = 0x4001000cUL, + /** Unknown start-of-day status code */ + RNDIS_STATUS_WTF_WORLD = 0x40020006UL, +}; + +/** RNDIS keepalive message */ +#define RNDIS_KEEPALIVE_MSG 0x00000008UL + +/** RNDIS keepalive message */ +struct rndis_keepalive_message { + /** Request ID */ + uint32_t id; +} __attribute__ (( packed )); + +/** RNDIS keepalive completion */ +#define RNDIS_KEEPALIVE_CMPLT 0x80000008UL + +/** RNDIS keepalive completion */ +struct rndis_keepalive_completion { + /** Request ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** RNDIS packet message */ +#define RNDIS_PACKET_MSG 0x00000001UL + +/** RNDIS packet field */ +struct rndis_packet_field { + /** Offset */ + uint32_t offset; + /** Length */ + uint32_t len; +} __attribute__ (( packed )); + +/** RNDIS packet message */ +struct rndis_packet_message { + /** Data */ + struct rndis_packet_field data; + /** Out-of-band data records */ + struct rndis_packet_field oob; + /** Number of out-of-band data records */ + uint32_t oob_count; + /** Per-packet information record */ + struct rndis_packet_field ppi; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** RNDIS packet record */ +struct rndis_packet_record { + /** Length */ + uint32_t len; + /** Type */ + uint32_t type; + /** Offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** OID for packet filter */ +#define RNDIS_OID_GEN_CURRENT_PACKET_FILTER 0x0001010eUL + +/** Packet filter bits */ +enum rndis_packet_filter { + /** Unicast packets */ + RNDIS_FILTER_UNICAST = 0x00000001UL, + /** Multicast packets */ + RNDIS_FILTER_MULTICAST = 0x00000002UL, + /** All multicast packets */ + RNDIS_FILTER_ALL_MULTICAST = 0x00000004UL, + /** Broadcast packets */ + RNDIS_FILTER_BROADCAST = 0x00000008UL, + /** All packets */ + RNDIS_FILTER_PROMISCUOUS = 0x00000020UL +}; + +/** OID for media status */ +#define RNDIS_OID_GEN_MEDIA_CONNECT_STATUS 0x00010114UL + +/** OID for permanent MAC address */ +#define RNDIS_OID_802_3_PERMANENT_ADDRESS 0x01010101UL + +/** OID for current MAC address */ +#define RNDIS_OID_802_3_CURRENT_ADDRESS 0x01010102UL + +struct rndis_device; + +/** RNDIS device operations */ +struct rndis_operations { + /** + * Open RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + */ + int ( * open ) ( struct rndis_device *rndis ); + /** + * Close RNDIS device + * + * @v rndis RNDIS device + */ + void ( * close ) ( struct rndis_device *rndis ); + /** + * Transmit packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * If this method returns success then the RNDIS device must + * eventually report completion via rndis_tx_complete(). + */ + int ( * transmit ) ( struct rndis_device *rndis, + struct io_buffer *iobuf ); + /** + * Poll for completed and received packets + * + * @v rndis RNDIS device + */ + void ( * poll ) ( struct rndis_device *rndis ); +}; + +/** An RNDIS device */ +struct rndis_device { + /** Network device */ + struct net_device *netdev; + /** Device name */ + const char *name; + /** RNDIS operations */ + struct rndis_operations *op; + /** Driver private data */ + void *priv; + + /** Request ID for current blocking request */ + unsigned int wait_id; + /** Return status code for current blocking request */ + int wait_rc; +}; + +/** + * Initialise an RNDIS device + * + * @v rndis RNDIS device + * @v op RNDIS device operations + */ +static inline void rndis_init ( struct rndis_device *rndis, + struct rndis_operations *op ) { + + rndis->op = op; +} + +extern void rndis_tx_complete_err ( struct rndis_device *rndis, + struct io_buffer *iobuf, int rc ); +extern int rndis_tx_defer ( struct rndis_device *rndis, + struct io_buffer *iobuf ); +extern void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ); +extern void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf, + int rc ); + +extern struct rndis_device * alloc_rndis ( size_t priv_len ); +extern int register_rndis ( struct rndis_device *rndis ); +extern void unregister_rndis ( struct rndis_device *rndis ); +extern void free_rndis ( struct rndis_device *rndis ); + +/** + * Complete message transmission + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static inline void rndis_tx_complete ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + + rndis_tx_complete_err ( rndis, iobuf, 0 ); +} + +#endif /* _IPXE_RNDIS_H */ diff --git a/roms/ipxe/src/include/ipxe/rootcert.h b/roms/ipxe/src/include/ipxe/rootcert.h index 6525df87a..d4be2e1bc 100644 --- a/roms/ipxe/src/include/ipxe/rootcert.h +++ b/roms/ipxe/src/include/ipxe/rootcert.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/x509.h> diff --git a/roms/ipxe/src/include/ipxe/rotate.h b/roms/ipxe/src/include/ipxe/rotate.h index ba271ca74..b5693e3ca 100644 --- a/roms/ipxe/src/include/ipxe/rotate.h +++ b/roms/ipxe/src/include/ipxe/rotate.h @@ -6,10 +6,30 @@ * Bit operations */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> +static inline __attribute__ (( always_inline )) uint8_t +rol8 ( uint8_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 8 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint8_t +ror8 ( uint8_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 8 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint16_t +rol16 ( uint16_t data, unsigned int rotation ) { + return ( ( data << rotation ) | ( data >> ( 16 - rotation ) ) ); +} + +static inline __attribute__ (( always_inline )) uint16_t +ror16 ( uint16_t data, unsigned int rotation ) { + return ( ( data >> rotation ) | ( data << ( 16 - rotation ) ) ); +} + static inline __attribute__ (( always_inline )) uint32_t rol32 ( uint32_t data, unsigned int rotation ) { return ( ( data << rotation ) | ( data >> ( 32 - rotation ) ) ); diff --git a/roms/ipxe/src/include/ipxe/rsa.h b/roms/ipxe/src/include/ipxe/rsa.h index 1a5ad8bab..d947eec73 100644 --- a/roms/ipxe/src/include/ipxe/rsa.h +++ b/roms/ipxe/src/include/ipxe/rsa.h @@ -6,8 +6,9 @@ * RSA public-key cryptography */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); +#include <stdarg.h> #include <ipxe/crypto.h> #include <ipxe/bigint.h> #include <ipxe/asn1.h> diff --git a/roms/ipxe/src/include/ipxe/sanboot.h b/roms/ipxe/src/include/ipxe/sanboot.h index 14c8a5da4..57025f2c6 100644 --- a/roms/ipxe/src/include/ipxe/sanboot.h +++ b/roms/ipxe/src/include/ipxe/sanboot.h @@ -16,7 +16,7 @@ * the address parameter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/api.h> #include <config/sanboot.h> diff --git a/roms/ipxe/src/include/ipxe/script.h b/roms/ipxe/src/include/ipxe/script.h index 33420dae4..7e7a9a3a4 100644 --- a/roms/ipxe/src/include/ipxe/script.h +++ b/roms/ipxe/src/include/ipxe/script.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/image.h> diff --git a/roms/ipxe/src/include/ipxe/scsi.h b/roms/ipxe/src/include/ipxe/scsi.h index 4428daac3..28b55b2d5 100644 --- a/roms/ipxe/src/include/ipxe/scsi.h +++ b/roms/ipxe/src/include/ipxe/scsi.h @@ -11,7 +11,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Maximum block for READ/WRITE (10) commands */ #define SCSI_MAX_BLOCK_10 0xffffffffULL diff --git a/roms/ipxe/src/include/ipxe/segment.h b/roms/ipxe/src/include/ipxe/segment.h index 37bed0e19..9d5ecbd9b 100644 --- a/roms/ipxe/src/include/ipxe/segment.h +++ b/roms/ipxe/src/include/ipxe/segment.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> diff --git a/roms/ipxe/src/include/ipxe/serial.h b/roms/ipxe/src/include/ipxe/serial.h index b47b1d125..83be59c31 100644 --- a/roms/ipxe/src/include/ipxe/serial.h +++ b/roms/ipxe/src/include/ipxe/serial.h @@ -3,15 +3,14 @@ /** @file * - * Serial driver functions + * Serial console * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -extern void serial_putc ( int ch ); -extern int serial_getc ( void ); -extern int serial_ischar ( void ); -extern int serial_initialized; +#include <ipxe/uart.h> + +extern struct uart serial_console; #endif /* _IPXE_SERIAL_H */ diff --git a/roms/ipxe/src/include/ipxe/settings.h b/roms/ipxe/src/include/ipxe/settings.h index d6929ecd0..95a553cc8 100644 --- a/roms/ipxe/src/include/ipxe/settings.h +++ b/roms/ipxe/src/include/ipxe/settings.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/tables.h> @@ -415,6 +415,7 @@ extern const struct setting_type setting_type_uint32 __setting_type; extern const struct setting_type setting_type_hex __setting_type; extern const struct setting_type setting_type_hexhyp __setting_type; extern const struct setting_type setting_type_hexraw __setting_type; +extern const struct setting_type setting_type_base64 __setting_type; extern const struct setting_type setting_type_uuid __setting_type; extern const struct setting_type setting_type_busdevfn __setting_type; extern const struct setting_type setting_type_dnssl __setting_type; diff --git a/roms/ipxe/src/include/ipxe/settings_ui.h b/roms/ipxe/src/include/ipxe/settings_ui.h index 5f7be30cc..0bf21935d 100644 --- a/roms/ipxe/src/include/ipxe/settings_ui.h +++ b/roms/ipxe/src/include/ipxe/settings_ui.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct settings; diff --git a/roms/ipxe/src/include/ipxe/sha256.h b/roms/ipxe/src/include/ipxe/sha256.h index 9aa9f3e57..e234cce33 100644 --- a/roms/ipxe/src/include/ipxe/sha256.h +++ b/roms/ipxe/src/include/ipxe/sha256.h @@ -7,11 +7,14 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> +/** SHA-256 number of rounds */ +#define SHA256_ROUNDS 64 + /** An SHA-256 digest */ struct sha256_digest { /** Hash output */ @@ -58,6 +61,8 @@ union sha256_digest_data_dwords { struct sha256_context { /** Amount of accumulated data */ size_t len; + /** Digest size */ + size_t digestsize; /** Digest and accumulated data */ union sha256_digest_data_dwords ddd; } __attribute__ (( packed )); @@ -68,6 +73,16 @@ struct sha256_context { /** SHA-256 digest size */ #define SHA256_DIGEST_SIZE sizeof ( struct sha256_digest ) +/** SHA-224 digest size */ +#define SHA224_DIGEST_SIZE ( SHA256_DIGEST_SIZE * 224 / 256 ) + +extern void sha256_family_init ( struct sha256_context *context, + const struct sha256_digest *init, + size_t digestsize ); +extern void sha256_update ( void *ctx, const void *data, size_t len ); +extern void sha256_final ( void *ctx, void *out ); + extern struct digest_algorithm sha256_algorithm; +extern struct digest_algorithm sha224_algorithm; #endif /* _IPXE_SHA256_H */ diff --git a/roms/ipxe/src/include/ipxe/sha512.h b/roms/ipxe/src/include/ipxe/sha512.h new file mode 100644 index 000000000..8e22d8357 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/sha512.h @@ -0,0 +1,98 @@ +#ifndef _IPXE_SHA512_H +#define _IPXE_SHA512_H + +/** @file + * + * SHA-512 algorithm + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/crypto.h> + +/** SHA-512 number of rounds */ +#define SHA512_ROUNDS 80 + +/** An SHA-512 digest */ +struct sha512_digest { + /** Hash output */ + uint64_t h[8]; +}; + +/** An SHA-512 data block */ +union sha512_block { + /** Raw bytes */ + uint8_t byte[128]; + /** Raw qwords */ + uint64_t qword[16]; + /** Final block structure */ + struct { + /** Padding */ + uint8_t pad[112]; + /** High 64 bits of length in bits */ + uint64_t len_hi; + /** Low 64 bits of length in bits */ + uint64_t len_lo; + } final; +}; + +/** SHA-512 digest and data block + * + * The order of fields within this structure is designed to minimise + * code size. + */ +struct sha512_digest_data { + /** Digest of data already processed */ + struct sha512_digest digest; + /** Accumulated data */ + union sha512_block data; +} __attribute__ (( packed )); + +/** SHA-512 digest and data block */ +union sha512_digest_data_qwords { + /** Digest and data block */ + struct sha512_digest_data dd; + /** Raw qwords */ + uint64_t qword[ sizeof ( struct sha512_digest_data ) / + sizeof ( uint64_t ) ]; +}; + +/** An SHA-512 context */ +struct sha512_context { + /** Amount of accumulated data */ + size_t len; + /** Digest size */ + size_t digestsize; + /** Digest and accumulated data */ + union sha512_digest_data_qwords ddq; +} __attribute__ (( packed )); + +/** SHA-512 context size */ +#define SHA512_CTX_SIZE sizeof ( struct sha512_context ) + +/** SHA-512 digest size */ +#define SHA512_DIGEST_SIZE sizeof ( struct sha512_digest ) + +/** SHA-384 digest size */ +#define SHA384_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 384 / 512 ) + +/** SHA-512/256 digest size */ +#define SHA512_256_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 256 / 512 ) + +/** SHA-512/224 digest size */ +#define SHA512_224_DIGEST_SIZE ( SHA512_DIGEST_SIZE * 224 / 512 ) + +extern void sha512_family_init ( struct sha512_context *context, + const struct sha512_digest *init, + size_t digestsize ); +extern void sha512_update ( void *ctx, const void *data, size_t len ); +extern void sha512_final ( void *ctx, void *out ); + +extern struct digest_algorithm sha512_algorithm; +extern struct digest_algorithm sha384_algorithm; +extern struct digest_algorithm sha512_256_algorithm; +extern struct digest_algorithm sha512_224_algorithm; + +#endif /* IPXE_SHA512_H */ diff --git a/roms/ipxe/src/include/ipxe/shell.h b/roms/ipxe/src/include/ipxe/shell.h index faa32f422..0d574e028 100644 --- a/roms/ipxe/src/include/ipxe/shell.h +++ b/roms/ipxe/src/include/ipxe/shell.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** Shell stop states */ enum shell_stop_state { diff --git a/roms/ipxe/src/include/ipxe/smbios.h b/roms/ipxe/src/include/ipxe/smbios.h index ef5892a21..24b05ed62 100644 --- a/roms/ipxe/src/include/ipxe/smbios.h +++ b/roms/ipxe/src/include/ipxe/smbios.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/api.h> diff --git a/roms/ipxe/src/include/ipxe/socket.h b/roms/ipxe/src/include/ipxe/socket.h index 7cb3912f4..8c70ea4c0 100644 --- a/roms/ipxe/src/include/ipxe/socket.h +++ b/roms/ipxe/src/include/ipxe/socket.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/spi.h b/roms/ipxe/src/include/ipxe/spi.h index d92d1aec9..83b53bce3 100644 --- a/roms/ipxe/src/include/ipxe/spi.h +++ b/roms/ipxe/src/include/ipxe/spi.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/nvs.h> diff --git a/roms/ipxe/src/include/ipxe/spi_bit.h b/roms/ipxe/src/include/ipxe/spi_bit.h index 9cfa7b825..049d30a22 100644 --- a/roms/ipxe/src/include/ipxe/spi_bit.h +++ b/roms/ipxe/src/include/ipxe/spi_bit.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/spi.h> #include <ipxe/bitbash.h> diff --git a/roms/ipxe/src/include/ipxe/stp.h b/roms/ipxe/src/include/ipxe/stp.h new file mode 100644 index 000000000..3d85e5ba4 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/stp.h @@ -0,0 +1,76 @@ +#ifndef _IPXE_STP_H +#define _IPXE_STP_H + +/** @file + * + * Spanning Tree Protocol (STP) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/if_ether.h> + +/** "Protocol" value for STP + * + * This is the concatenated {DSAP,SSAP} value used internally by iPXE + * as the network-layer protocol for LLC frames. + */ +#define ETH_P_STP 0x4242 + +/** A switch identifier */ +struct stp_switch { + /** Priotity */ + uint16_t priority; + /** MAC address */ + uint8_t mac[ETH_ALEN]; +} __attribute__ (( packed )); + +/** A Spanning Tree bridge protocol data unit */ +struct stp_bpdu { + /** LLC DSAP */ + uint8_t dsap; + /** LLC SSAP */ + uint8_t ssap; + /** LLC control field */ + uint8_t control; + /** Protocol ID */ + uint16_t protocol; + /** Protocol version */ + uint8_t version; + /** Message type */ + uint8_t type; + /** Flags */ + uint8_t flags; + /** Root switch */ + struct stp_switch root; + /** Root path cost */ + uint32_t cost; + /** Sender switch */ + struct stp_switch sender; + /** Port */ + uint16_t port; + /** Message age */ + uint16_t age; + /** Maximum age */ + uint16_t max; + /** Hello time */ + uint16_t hello; + /** Forward delay */ + uint16_t delay; +} __attribute__ (( packed )); + +/** Spanning Tree protocol ID */ +#define STP_PROTOCOL 0x0000 + +/** Rapid Spanning Tree protocol version */ +#define STP_VERSION_RSTP 0x02 + +/** Rapid Spanning Tree bridge PDU type */ +#define STP_TYPE_RSTP 0x02 + +/** Port is forwarding */ +#define STP_FL_FORWARDING 0x20 + +#endif /* _IPXE_STP_H */ diff --git a/roms/ipxe/src/include/ipxe/string.h b/roms/ipxe/src/include/ipxe/string.h new file mode 100644 index 000000000..a8cbe8faa --- /dev/null +++ b/roms/ipxe/src/include/ipxe/string.h @@ -0,0 +1,14 @@ +#ifndef _IPXE_STRING_H +#define _IPXE_STRING_H + +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +extern unsigned int digit_value ( unsigned int digit ); + +#endif /* _IPXE_STRING_H */ diff --git a/roms/ipxe/src/include/ipxe/syslog.h b/roms/ipxe/src/include/ipxe/syslog.h index 131692654..138440d66 100644 --- a/roms/ipxe/src/include/ipxe/syslog.h +++ b/roms/ipxe/src/include/ipxe/syslog.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <syslog.h> diff --git a/roms/ipxe/src/include/ipxe/tables.h b/roms/ipxe/src/include/ipxe/tables.h index e35ce8220..60f8efdea 100644 --- a/roms/ipxe/src/include/ipxe/tables.h +++ b/roms/ipxe/src/include/ipxe/tables.h @@ -1,7 +1,7 @@ #ifndef _IPXE_TABLES_H #define _IPXE_TABLES_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @page ifdef_harmful #ifdef considered harmful * diff --git a/roms/ipxe/src/include/ipxe/tcp.h b/roms/ipxe/src/include/ipxe/tcp.h index 9baa6391c..063ebaa4b 100644 --- a/roms/ipxe/src/include/ipxe/tcp.h +++ b/roms/ipxe/src/include/ipxe/tcp.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tcpip.h> @@ -79,6 +79,48 @@ struct tcp_window_scale_padded_option { */ #define TCP_RX_WINDOW_SCALE 9 +/** TCP selective acknowledgement permitted option */ +struct tcp_sack_permitted_option { + uint8_t kind; + uint8_t length; +} __attribute__ (( packed )); + +/** Padded TCP selective acknowledgement permitted option (used for sending) */ +struct tcp_sack_permitted_padded_option { + uint8_t nop[2]; + struct tcp_sack_permitted_option spopt; +} __attribute__ (( packed )); + +/** Code for the TCP selective acknowledgement permitted option */ +#define TCP_OPTION_SACK_PERMITTED 4 + +/** TCP selective acknowledgement option */ +struct tcp_sack_option { + uint8_t kind; + uint8_t length; +} __attribute__ (( packed )); + +/** TCP selective acknowledgement block */ +struct tcp_sack_block { + uint32_t left; + uint32_t right; +} __attribute__ (( packed )); + +/** Maximum number of selective acknowledgement blocks + * + * This allows for the presence of the TCP timestamp option. + */ +#define TCP_SACK_MAX 3 + +/** Padded TCP selective acknowledgement option (used for sending) */ +struct tcp_sack_padded_option { + uint8_t nop[2]; + struct tcp_sack_option sackopt; +} __attribute__ (( packed )); + +/** Code for the TCP selective acknowledgement option */ +#define TCP_OPTION_SACK 5 + /** TCP timestamp option */ struct tcp_timestamp_option { uint8_t kind; @@ -102,6 +144,8 @@ struct tcp_options { const struct tcp_mss_option *mssopt; /** Window scale option, if present */ const struct tcp_window_scale_option *wsopt; + /** SACK permitted option, if present */ + const struct tcp_sack_permitted_option *spopt; /** Timestamp option, if present */ const struct tcp_timestamp_option *tsopt; }; @@ -376,6 +420,13 @@ static inline int tcp_in_window ( uint32_t seq, uint32_t start, return ( ( seq - start ) < len ); } +/** TCP finish wait time + * + * Currently set to one second, since we should not allow a slowly + * responding server to substantially delay a call to shutdown(). + */ +#define TCP_FINISH_TIMEOUT ( 1 * TICKS_PER_SEC ) + extern struct tcpip_protocol tcp_protocol __tcpip_protocol; #endif /* _IPXE_TCP_H */ diff --git a/roms/ipxe/src/include/ipxe/tcpip.h b/roms/ipxe/src/include/ipxe/tcpip.h index 200630d6b..3cfc8e3ac 100644 --- a/roms/ipxe/src/include/ipxe/tcpip.h +++ b/roms/ipxe/src/include/ipxe/tcpip.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/socket.h> @@ -48,6 +48,12 @@ struct sockaddr_tcpip { uint16_t st_flags; /** TCP/IP port */ uint16_t st_port; + /** Scope ID + * + * For link-local or multicast addresses, this is the network + * device index. + */ + uint16_t st_scope_id; /** Padding * * This ensures that a struct @c sockaddr_tcpip is large @@ -57,7 +63,8 @@ struct sockaddr_tcpip { char pad[ sizeof ( struct sockaddr ) - ( sizeof ( sa_family_t ) /* st_family */ + sizeof ( uint16_t ) /* st_flags */ + - sizeof ( uint16_t ) /* st_port */ ) ]; + sizeof ( uint16_t ) /* st_port */ + + sizeof ( uint16_t ) /* st_scope_id */ ) ]; } __attribute__ (( packed, may_alias )); /** diff --git a/roms/ipxe/src/include/ipxe/test.h b/roms/ipxe/src/include/ipxe/test.h index 028ee29fb..0b65c299c 100644 --- a/roms/ipxe/src/include/ipxe/test.h +++ b/roms/ipxe/src/include/ipxe/test.h @@ -1,7 +1,7 @@ #ifndef _IPXE_TEST_H #define _IPXE_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/include/ipxe/tftp.h b/roms/ipxe/src/include/ipxe/tftp.h index aecafa2ae..e3661e1ac 100644 --- a/roms/ipxe/src/include/ipxe/tftp.h +++ b/roms/ipxe/src/include/ipxe/tftp.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/ipxe/time.h b/roms/ipxe/src/include/ipxe/time.h index 673fe098a..4c5bb2a00 100644 --- a/roms/ipxe/src/include/ipxe/time.h +++ b/roms/ipxe/src/include/ipxe/time.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <sys/time.h> #include <ipxe/api.h> @@ -44,6 +44,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); /* Include all architecture-independent time API headers */ #include <ipxe/null_time.h> +#include <ipxe/efi/efi_time.h> #include <ipxe/linux/linux_time.h> /* Include all architecture-dependent time API headers */ diff --git a/roms/ipxe/src/include/ipxe/timer.h b/roms/ipxe/src/include/ipxe/timer.h index d0309655d..82fbb6764 100644 --- a/roms/ipxe/src/include/ipxe/timer.h +++ b/roms/ipxe/src/include/ipxe/timer.h @@ -9,7 +9,7 @@ * for a monotonically increasing tick counter. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/api.h> #include <config/timer.h> diff --git a/roms/ipxe/src/include/ipxe/tls.h b/roms/ipxe/src/include/ipxe/tls.h index 586da26ec..7d982c326 100644 --- a/roms/ipxe/src/include/ipxe/tls.h +++ b/roms/ipxe/src/include/ipxe/tls.h @@ -7,7 +7,7 @@ * Transport Layer Security Protocol */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/refcnt.h> @@ -20,6 +20,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/x509.h> #include <ipxe/pending.h> #include <ipxe/iobuf.h> +#include <ipxe/tables.h> /** A TLS header */ struct tls_header { @@ -85,7 +86,10 @@ struct tls_header { /* TLS hash algorithm identifiers */ #define TLS_MD5_ALGORITHM 1 #define TLS_SHA1_ALGORITHM 2 +#define TLS_SHA224_ALGORITHM 3 #define TLS_SHA256_ALGORITHM 4 +#define TLS_SHA384_ALGORITHM 5 +#define TLS_SHA512_ALGORITHM 6 /* TLS signature algorithm identifiers */ #define TLS_RSA_ALGORITHM 1 @@ -101,6 +105,9 @@ struct tls_header { #define TLS_MAX_FRAGMENT_LENGTH_2048 3 #define TLS_MAX_FRAGMENT_LENGTH_4096 4 +/* TLS signature algorithms extension */ +#define TLS_SIGNATURE_ALGORITHMS 13 + /** TLS RX state machine state */ enum tls_rx_state { TLS_RX_HEADER = 0, @@ -131,6 +138,14 @@ struct tls_cipher_suite { uint16_t code; }; +/** TLS cipher suite table */ +#define TLS_CIPHER_SUITES \ + __table ( struct tls_cipher_suite, "tls_cipher_suites" ) + +/** Declare a TLS cipher suite */ +#define __tls_cipher_suite( pref ) \ + __table_entry ( TLS_CIPHER_SUITES, pref ) + /** A TLS cipher specification */ struct tls_cipherspec { /** Cipher suite */ @@ -165,6 +180,19 @@ struct tls_signature_hash_algorithm { struct tls_signature_hash_id code; }; +/** TLS signature hash algorithm table + * + * Note that the default (TLSv1.1 and earlier) algorithm using + * MD5+SHA1 is never explicitly specified. + */ +#define TLS_SIG_HASH_ALGORITHMS \ + __table ( struct tls_signature_hash_algorithm, \ + "tls_sig_hash_algorithms" ) + +/** Declare a TLS signature hash algorithm */ +#define __tls_sig_hash_algorithm \ + __table_entry ( TLS_SIG_HASH_ALGORITHMS, 01 ) + /** TLS pre-master secret */ struct tls_pre_master_secret { /** TLS version */ diff --git a/roms/ipxe/src/include/ipxe/uaccess.h b/roms/ipxe/src/include/ipxe/uaccess.h index 055bb2ca7..a3f78566a 100644 --- a/roms/ipxe/src/include/ipxe/uaccess.h +++ b/roms/ipxe/src/include/ipxe/uaccess.h @@ -19,7 +19,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/include/ipxe/uart.h b/roms/ipxe/src/include/ipxe/uart.h new file mode 100644 index 000000000..c63eae615 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/uart.h @@ -0,0 +1,132 @@ +#ifndef _IPXE_UART_H +#define _IPXE_UART_H + +/** @file + * + * 16550-compatible UART + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> + +/** Transmitter holding register */ +#define UART_THR 0x00 + +/** Receiver buffer register */ +#define UART_RBR 0x00 + +/** Interrupt enable register */ +#define UART_IER 0x01 + +/** FIFO control register */ +#define UART_FCR 0x02 +#define UART_FCR_FE 0x01 /**< FIFO enable */ + +/** Line control register */ +#define UART_LCR 0x03 +#define UART_LCR_WLS0 0x01 /**< Word length select bit 0 */ +#define UART_LCR_WLS1 0x02 /**< Word length select bit 1 */ +#define UART_LCR_STB 0x04 /**< Number of stop bits */ +#define UART_LCR_PEN 0x08 /**< Parity enable */ +#define UART_LCR_EPS 0x10 /**< Even parity select */ +#define UART_LCR_DLAB 0x80 /**< Divisor latch access bit */ + +#define UART_LCR_WORD_LEN(x) ( ( (x) - 5 ) << 0 ) /**< Word length */ +#define UART_LCR_STOP_BITS(x) ( ( (x) - 1 ) << 2 ) /**< Stop bits */ +#define UART_LCR_PARITY(x) ( ( (x) - 0 ) << 3 ) /**< Parity */ + +/** + * Calculate line control register value + * + * @v word_len Word length (5-8) + * @v parity Parity (0=none, 1=odd, 3=even) + * @v stop_bits Stop bits (1-2) + * @ret lcr Line control register value + */ +#define UART_LCR_WPS( word_len, parity, stop_bits ) \ + ( UART_LCR_WORD_LEN ( (word_len) ) | \ + UART_LCR_PARITY ( (parity) ) | \ + UART_LCR_STOP_BITS ( (stop_bits) ) ) + +/** Default LCR value: 8 data bits, no parity, one stop bit */ +#define UART_LCR_8N1 UART_LCR_WPS ( 8, 0, 1 ) + +/** Modem control register */ +#define UART_MCR 0x04 +#define UART_MCR_DTR 0x01 /**< Data terminal ready */ +#define UART_MCR_RTS 0x02 /**< Request to send */ + +/** Line status register */ +#define UART_LSR 0x05 +#define UART_LSR_DR 0x01 /**< Data ready */ +#define UART_LSR_THRE 0x20 /**< Transmitter holding register empty */ +#define UART_LSR_TEMT 0x40 /**< Transmitter empty */ + +/** Scratch register */ +#define UART_SCR 0x07 + +/** Divisor latch (least significant byte) */ +#define UART_DLL 0x00 + +/** Divisor latch (most significant byte) */ +#define UART_DLM 0x01 + +/** Maximum baud rate */ +#define UART_MAX_BAUD 115200 + +/** A 16550-compatible UART */ +struct uart { + /** I/O port base address */ + void *base; + /** Baud rate divisor */ + uint16_t divisor; + /** Line control register */ + uint8_t lcr; +}; + +/** Symbolic names for port indexes */ +enum uart_port { + COM1 = 1, + COM2 = 2, + COM3 = 3, + COM4 = 4, +}; + +#include <bits/uart.h> + +void uart_write ( struct uart *uart, unsigned int addr, uint8_t data ); +uint8_t uart_read ( struct uart *uart, unsigned int addr ); +int uart_select ( struct uart *uart, unsigned int port ); + +/** + * Check if received data is ready + * + * @v uart UART + * @ret ready Data is ready + */ +static inline int uart_data_ready ( struct uart *uart ) { + uint8_t lsr; + + lsr = uart_read ( uart, UART_LSR ); + return ( lsr & UART_LSR_DR ); +} + +/** + * Receive data + * + * @v uart UART + * @ret data Data + */ +static inline uint8_t uart_receive ( struct uart *uart ) { + + return uart_read ( uart, UART_RBR ); +} + +extern void uart_transmit ( struct uart *uart, uint8_t data ); +extern void uart_flush ( struct uart *uart ); +extern int uart_exists ( struct uart *uart ); +extern int uart_init ( struct uart *uart, unsigned int baud, uint8_t lcr ); + +#endif /* _IPXE_UART_H */ diff --git a/roms/ipxe/src/include/ipxe/udp.h b/roms/ipxe/src/include/ipxe/udp.h index 5717ef213..7b0de4dc0 100644 --- a/roms/ipxe/src/include/ipxe/udp.h +++ b/roms/ipxe/src/include/ipxe/udp.h @@ -9,7 +9,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/iobuf.h> diff --git a/roms/ipxe/src/include/ipxe/umalloc.h b/roms/ipxe/src/include/ipxe/umalloc.h index 4b25e182a..3892ef53b 100644 --- a/roms/ipxe/src/include/ipxe/umalloc.h +++ b/roms/ipxe/src/include/ipxe/umalloc.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/api.h> #include <config/umalloc.h> diff --git a/roms/ipxe/src/include/ipxe/uri.h b/roms/ipxe/src/include/ipxe/uri.h index 7613d578d..00e5a24c4 100644 --- a/roms/ipxe/src/include/ipxe/uri.h +++ b/roms/ipxe/src/include/ipxe/uri.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> @@ -203,7 +203,7 @@ extern char * resolve_path ( const char *base_path, const char *relative_path ); extern struct uri * resolve_uri ( const struct uri *base_uri, struct uri *relative_uri ); -extern struct uri * tftp_uri ( struct in_addr next_server, +extern struct uri * tftp_uri ( struct in_addr next_server, unsigned int port, const char *filename ); extern void churi ( struct uri *uri ); diff --git a/roms/ipxe/src/include/ipxe/usb.h b/roms/ipxe/src/include/ipxe/usb.h new file mode 100644 index 000000000..ab060b8f4 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/usb.h @@ -0,0 +1,1319 @@ +#ifndef _IPXE_USB_H +#define _IPXE_USB_H + +/** @file + * + * Universal Serial Bus (USB) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <byteswap.h> +#include <ipxe/list.h> +#include <ipxe/device.h> +#include <ipxe/process.h> +#include <ipxe/iobuf.h> +#include <ipxe/tables.h> + +/** USB protocols */ +enum usb_protocol { + /** USB 2.0 */ + USB_PROTO_2_0 = 0x0200, + /** USB 3.0 */ + USB_PROTO_3_0 = 0x0300, + /** USB 3.1 */ + USB_PROTO_3_1 = 0x0301, +}; + +/** Define a USB speed + * + * @v mantissa Mantissa + * @v exponent Exponent (in engineering terms: 1=k, 2=M, 3=G) + * @ret speed USB speed + */ +#define USB_SPEED( mantissa, exponent ) ( (exponent << 16) | (mantissa) ) + +/** Extract USB speed mantissa */ +#define USB_SPEED_MANTISSA(speed) ( (speed) & 0xffff ) + +/** Extract USB speed exponent */ +#define USB_SPEED_EXPONENT(speed) ( ( (speed) >> 16 ) & 0x3 ) + +/** USB device speeds */ +enum usb_speed { + /** Not connected */ + USB_SPEED_NONE = 0, + /** Low speed (1.5Mbps) */ + USB_SPEED_LOW = USB_SPEED ( 1500, 1 ), + /** Full speed (12Mbps) */ + USB_SPEED_FULL = USB_SPEED ( 12, 2 ), + /** High speed (480Mbps) */ + USB_SPEED_HIGH = USB_SPEED ( 480, 2 ), + /** Super speed (5Gbps) */ + USB_SPEED_SUPER = USB_SPEED ( 5, 3 ), +}; + +/** USB packet IDs */ +enum usb_pid { + /** IN PID */ + USB_PID_IN = 0x69, + /** OUT PID */ + USB_PID_OUT = 0xe1, + /** SETUP PID */ + USB_PID_SETUP = 0x2d, +}; + +/** A USB setup data packet */ +struct usb_setup_packet { + /** Request */ + uint16_t request; + /** Value paramer */ + uint16_t value; + /** Index parameter */ + uint16_t index; + /** Length of data stage */ + uint16_t len; +} __attribute__ (( packed )); + +/** Data transfer is from host to device */ +#define USB_DIR_OUT ( 0 << 7 ) + +/** Data transfer is from device to host */ +#define USB_DIR_IN ( 1 << 7 ) + +/** Standard request type */ +#define USB_TYPE_STANDARD ( 0 << 5 ) + +/** Class-specific request type */ +#define USB_TYPE_CLASS ( 1 << 5 ) + +/** Vendor-specific request type */ +#define USB_TYPE_VENDOR ( 2 << 5 ) + +/** Request recipient is the device */ +#define USB_RECIP_DEVICE ( 0 << 0 ) + +/** Request recipient is an interface */ +#define USB_RECIP_INTERFACE ( 1 << 0 ) + +/** Request recipient is an endpoint */ +#define USB_RECIP_ENDPOINT ( 2 << 0 ) + +/** Construct USB request type */ +#define USB_REQUEST_TYPE(type) ( (type) << 8 ) + +/** Get status */ +#define USB_GET_STATUS ( USB_DIR_IN | USB_REQUEST_TYPE ( 0 ) ) + +/** Clear feature */ +#define USB_CLEAR_FEATURE ( USB_DIR_OUT | USB_REQUEST_TYPE ( 1 ) ) + +/** Set feature */ +#define USB_SET_FEATURE ( USB_DIR_OUT | USB_REQUEST_TYPE ( 3 ) ) + +/** Set address */ +#define USB_SET_ADDRESS ( USB_DIR_OUT | USB_REQUEST_TYPE ( 5 ) ) + +/** Get descriptor */ +#define USB_GET_DESCRIPTOR ( USB_DIR_IN | USB_REQUEST_TYPE ( 6 ) ) + +/** Set descriptor */ +#define USB_SET_DESCRIPTOR ( USB_DIR_OUT | USB_REQUEST_TYPE ( 7 ) ) + +/** Get configuration */ +#define USB_GET_CONFIGURATION ( USB_DIR_IN | USB_REQUEST_TYPE ( 8 ) ) + +/** Set configuration */ +#define USB_SET_CONFIGURATION ( USB_DIR_OUT | USB_REQUEST_TYPE ( 9 ) ) + +/** Get interface */ +#define USB_GET_INTERFACE \ + ( USB_DIR_IN | USB_RECIP_INTERFACE | USB_REQUEST_TYPE ( 10 ) ) + +/** Set interface */ +#define USB_SET_INTERFACE \ + ( USB_DIR_OUT | USB_RECIP_INTERFACE | USB_REQUEST_TYPE ( 11 ) ) + +/** Endpoint halt feature */ +#define USB_ENDPOINT_HALT 0 + +/** A USB class code tuple */ +struct usb_class { + /** Class code */ + uint8_t class; + /** Subclass code */ + uint8_t subclass; + /** Protocol code */ + uint8_t protocol; +} __attribute__ (( packed )); + +/** Class code for USB hubs */ +#define USB_CLASS_HUB 9 + +/** A USB descriptor header */ +struct usb_descriptor_header { + /** Length of descriptor */ + uint8_t len; + /** Descriptor type */ + uint8_t type; +} __attribute__ (( packed )); + +/** A USB device descriptor */ +struct usb_device_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** USB specification release number in BCD */ + uint16_t protocol; + /** Device class */ + struct usb_class class; + /** Maximum packet size for endpoint zero */ + uint8_t mtu; + /** Vendor ID */ + uint16_t vendor; + /** Product ID */ + uint16_t product; + /** Device release number in BCD */ + uint16_t release; + /** Manufacturer string */ + uint8_t manufacturer; + /** Product string */ + uint8_t name; + /** Serial number string */ + uint8_t serial; + /** Number of possible configurations */ + uint8_t configurations; +} __attribute__ (( packed )); + +/** A USB device descriptor */ +#define USB_DEVICE_DESCRIPTOR 1 + +/** A USB configuration descriptor */ +struct usb_configuration_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Total length */ + uint16_t len; + /** Number of interfaces */ + uint8_t interfaces; + /** Configuration value */ + uint8_t config; + /** Configuration string */ + uint8_t name; + /** Attributes */ + uint8_t attributes; + /** Maximum power consumption */ + uint8_t power; +} __attribute__ (( packed )); + +/** A USB configuration descriptor */ +#define USB_CONFIGURATION_DESCRIPTOR 2 + +/** A USB string descriptor */ +struct usb_string_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** String */ + char string[0]; +} __attribute__ (( packed )); + +/** A USB string descriptor */ +#define USB_STRING_DESCRIPTOR 3 + +/** A USB interface descriptor */ +struct usb_interface_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Interface number */ + uint8_t interface; + /** Alternate setting */ + uint8_t alternate; + /** Number of endpoints */ + uint8_t endpoints; + /** Interface class */ + struct usb_class class; + /** Interface name */ + uint8_t name; +} __attribute__ (( packed )); + +/** A USB interface descriptor */ +#define USB_INTERFACE_DESCRIPTOR 4 + +/** A USB endpoint descriptor */ +struct usb_endpoint_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Endpoint address */ + uint8_t endpoint; + /** Attributes */ + uint8_t attributes; + /** Maximum packet size and burst size */ + uint16_t sizes; + /** Polling interval */ + uint8_t interval; +} __attribute__ (( packed )); + +/** A USB endpoint descriptor */ +#define USB_ENDPOINT_DESCRIPTOR 5 + +/** Endpoint attribute transfer type mask */ +#define USB_ENDPOINT_ATTR_TYPE_MASK 0x03 + +/** Endpoint periodic type */ +#define USB_ENDPOINT_ATTR_PERIODIC 0x01 + +/** Control endpoint transfer type */ +#define USB_ENDPOINT_ATTR_CONTROL 0x00 + +/** Bulk endpoint transfer type */ +#define USB_ENDPOINT_ATTR_BULK 0x02 + +/** Interrupt endpoint transfer type */ +#define USB_ENDPOINT_ATTR_INTERRUPT 0x03 + +/** Bulk OUT endpoint (internal) type */ +#define USB_BULK_OUT ( USB_ENDPOINT_ATTR_BULK | USB_DIR_OUT ) + +/** Bulk IN endpoint (internal) type */ +#define USB_BULK_IN ( USB_ENDPOINT_ATTR_BULK | USB_DIR_IN ) + +/** Interrupt IN endpoint (internal) type */ +#define USB_INTERRUPT_IN ( USB_ENDPOINT_ATTR_INTERRUPT | USB_DIR_IN ) + +/** Interrupt OUT endpoint (internal) type */ +#define USB_INTERRUPT_OUT ( USB_ENDPOINT_ATTR_INTERRUPT | USB_DIR_OUT ) + +/** USB endpoint MTU */ +#define USB_ENDPOINT_MTU(sizes) ( ( (sizes) >> 0 ) & 0x07ff ) + +/** USB endpoint maximum burst size */ +#define USB_ENDPOINT_BURST(sizes) ( ( (sizes) >> 11 ) & 0x0003 ) + +/** A USB endpoint companion descriptor */ +struct usb_endpoint_companion_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** Maximum burst size */ + uint8_t burst; + /** Extended attributes */ + uint8_t extended; + /** Number of bytes per service interval */ + uint16_t periodic; +} __attribute__ (( packed )); + +/** A USB endpoint companion descriptor */ +#define USB_ENDPOINT_COMPANION_DESCRIPTOR 48 + +/** A USB interface association descriptor */ +struct usb_interface_association_descriptor { + /** Descriptor header */ + struct usb_descriptor_header header; + /** First interface number */ + uint8_t first; + /** Interface count */ + uint8_t count; + /** Association class */ + struct usb_class class; + /** Association name */ + uint8_t name; +} __attribute__ (( packed )); + +/** A USB interface association descriptor */ +#define USB_INTERFACE_ASSOCIATION_DESCRIPTOR 11 + +/** A class-specific interface descriptor */ +#define USB_CS_INTERFACE_DESCRIPTOR 36 + +/** A class-specific endpoint descriptor */ +#define USB_CS_ENDPOINT_DESCRIPTOR 37 + +/** + * Get next USB descriptor + * + * @v desc USB descriptor header + * @ret next Next USB descriptor header + */ +static inline __attribute__ (( always_inline )) struct usb_descriptor_header * +usb_next_descriptor ( struct usb_descriptor_header *desc ) { + + return ( ( ( void * ) desc ) + desc->len ); +} + +/** + * Check that descriptor lies within a configuration descriptor + * + * @v config Configuration descriptor + * @v desc Descriptor header + * @v is_within Descriptor is within the configuration descriptor + */ +static inline __attribute__ (( always_inline )) int +usb_is_within_config ( struct usb_configuration_descriptor *config, + struct usb_descriptor_header *desc ) { + struct usb_descriptor_header *end = + ( ( ( void * ) config ) + le16_to_cpu ( config->len ) ); + + /* Check that descriptor starts within the configuration + * descriptor, and that the length does not exceed the + * configuration descriptor. This relies on the fact that + * usb_next_descriptor() needs to access only the first byte + * of the descriptor in order to determine the length. + */ + return ( ( desc < end ) && ( usb_next_descriptor ( desc ) <= end ) ); +} + +/** Iterate over all configuration descriptors */ +#define for_each_config_descriptor( desc, config ) \ + for ( desc = container_of ( &(config)->header, \ + typeof ( *desc ), header ) ; \ + usb_is_within_config ( (config), &desc->header ) ; \ + desc = container_of ( usb_next_descriptor ( &desc->header ), \ + typeof ( *desc ), header ) ) + +/** Iterate over all configuration descriptors within an interface descriptor */ +#define for_each_interface_descriptor( desc, config, interface ) \ + for ( desc = container_of ( usb_next_descriptor ( &(interface)-> \ + header ), \ + typeof ( *desc ), header ) ; \ + ( usb_is_within_config ( (config), &desc->header ) && \ + ( desc->header.type != USB_INTERFACE_DESCRIPTOR ) ) ; \ + desc = container_of ( usb_next_descriptor ( &desc->header ), \ + typeof ( *desc ), header ) ) + +/** A USB endpoint */ +struct usb_endpoint { + /** USB device */ + struct usb_device *usb; + /** Endpoint address */ + unsigned int address; + /** Attributes */ + unsigned int attributes; + /** Maximum transfer size */ + size_t mtu; + /** Maximum burst size */ + unsigned int burst; + /** Interval (in microframes) */ + unsigned int interval; + + /** Endpoint is open */ + int open; + /** Buffer fill level */ + unsigned int fill; + + /** List of halted endpoints */ + struct list_head halted; + + /** Host controller operations */ + struct usb_endpoint_host_operations *host; + /** Host controller private data */ + void *priv; + /** Driver operations */ + struct usb_endpoint_driver_operations *driver; + + /** Recycled I/O buffer list */ + struct list_head recycled; + /** Refill buffer length */ + size_t len; + /** Maximum fill level */ + unsigned int max; +}; + +/** USB endpoint host controller operations */ +struct usb_endpoint_host_operations { + /** Open endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * open ) ( struct usb_endpoint *ep ); + /** Close endpoint + * + * @v ep USB endpoint + */ + void ( * close ) ( struct usb_endpoint *ep ); + /** + * Reset endpoint + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * reset ) ( struct usb_endpoint *ep ); + /** Update MTU + * + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * mtu ) ( struct usb_endpoint *ep ); + /** Enqueue message transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @ret rc Return status code + */ + int ( * message ) ( struct usb_endpoint *ep, + struct io_buffer *iobuf ); + /** Enqueue stream transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v terminate Terminate using a short packet + * @ret rc Return status code + */ + int ( * stream ) ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ); +}; + +/** USB endpoint driver operations */ +struct usb_endpoint_driver_operations { + /** Complete transfer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + * @v rc Completion status code + */ + void ( * complete ) ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ); +}; + +/** Control endpoint address */ +#define USB_EP0_ADDRESS 0x00 + +/** Control endpoint attributes */ +#define USB_EP0_ATTRIBUTES 0x00 + +/** Calculate default MTU based on device speed + * + * @v speed Device speed + * @ret mtu Default MTU + */ +#define USB_EP0_DEFAULT_MTU(speed) \ + ( ( (speed) >= USB_SPEED_SUPER ) ? 512 : \ + ( ( (speed) >= USB_SPEED_FULL ) ? 64 : 8 ) ) + +/** Control endpoint maximum burst size */ +#define USB_EP0_BURST 0 + +/** Control endpoint interval */ +#define USB_EP0_INTERVAL 0 + +/** Maximum endpoint number */ +#define USB_ENDPOINT_MAX 0x0f + +/** Endpoint direction is in */ +#define USB_ENDPOINT_IN 0x80 + +/** Construct endpoint index from endpoint address */ +#define USB_ENDPOINT_IDX(address) \ + ( ( (address) & USB_ENDPOINT_MAX ) | \ + ( ( (address) & USB_ENDPOINT_IN ) >> 3 ) ) + +/** + * Initialise USB endpoint + * + * @v ep USB endpoint + * @v usb USB device + * @v driver Driver operations + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_init ( struct usb_endpoint *ep, struct usb_device *usb, + struct usb_endpoint_driver_operations *driver ) { + + ep->usb = usb; + ep->driver = driver; +} + +/** + * Describe USB endpoint + * + * @v ep USB endpoint + * @v address Endpoint address + * @v attributes Attributes + * @v mtu Maximum packet size + * @v burst Maximum burst size + * @v interval Interval (in microframes) + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_describe ( struct usb_endpoint *ep, unsigned int address, + unsigned int attributes, size_t mtu, + unsigned int burst, unsigned int interval ) { + + ep->address = address; + ep->attributes = attributes; + ep->mtu = mtu; + ep->burst = burst; + ep->interval = interval; +} + +/** + * Set USB endpoint host controller private data + * + * @v ep USB endpoint + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_endpoint_set_hostdata ( struct usb_endpoint *ep, void *priv ) { + ep->priv = priv; +} + +/** + * Get USB endpoint host controller private data + * + * @v ep USB endpoint + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_endpoint_get_hostdata ( struct usb_endpoint *ep ) { + return ep->priv; +} + +extern const char * usb_endpoint_name ( struct usb_endpoint *ep ); +extern int +usb_endpoint_described ( struct usb_endpoint *ep, + struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ); +extern int usb_endpoint_open ( struct usb_endpoint *ep ); +extern void usb_endpoint_close ( struct usb_endpoint *ep ); +extern int usb_message ( struct usb_endpoint *ep, unsigned int request, + unsigned int value, unsigned int index, + struct io_buffer *iobuf ); +extern int usb_stream ( struct usb_endpoint *ep, struct io_buffer *iobuf, + int terminate ); +extern void usb_complete_err ( struct usb_endpoint *ep, + struct io_buffer *iobuf, int rc ); + +/** + * Initialise USB endpoint refill + * + * @v ep USB endpoint + * @v len Refill buffer length (or zero to use endpoint's MTU) + * @v max Maximum fill level + */ +static inline __attribute__ (( always_inline )) void +usb_refill_init ( struct usb_endpoint *ep, size_t len, unsigned int max ) { + + INIT_LIST_HEAD ( &ep->recycled ); + ep->len = len; + ep->max = max; +} + +/** + * Recycle I/O buffer + * + * @v ep USB endpoint + * @v iobuf I/O buffer + */ +static inline __attribute__ (( always_inline )) void +usb_recycle ( struct usb_endpoint *ep, struct io_buffer *iobuf ) { + + list_add_tail ( &iobuf->list, &ep->recycled ); +} + +extern int usb_prefill ( struct usb_endpoint *ep ); +extern int usb_refill ( struct usb_endpoint *ep ); +extern void usb_flush ( struct usb_endpoint *ep ); + +/** + * A USB function + * + * A USB function represents an association of interfaces within a USB + * device. + */ +struct usb_function { + /** Name */ + const char *name; + /** USB device */ + struct usb_device *usb; + /** Class */ + struct usb_class class; + /** Number of interfaces */ + unsigned int count; + /** Generic device */ + struct device dev; + /** List of functions within this USB device */ + struct list_head list; + + /** Driver */ + struct usb_driver *driver; + /** Driver private data */ + void *priv; + + /** List of interface numbers + * + * This must be the last field within the structure. + */ + uint8_t interface[0]; +}; + +/** + * Set USB function driver private data + * + * @v func USB function + * @v priv Driver private data + */ +static inline __attribute__ (( always_inline )) void +usb_func_set_drvdata ( struct usb_function *func, void *priv ) { + func->priv = priv; +} + +/** + * Get USB function driver private data + * + * @v function USB function + * @ret priv Driver private data + */ +static inline __attribute__ (( always_inline )) void * +usb_func_get_drvdata ( struct usb_function *func ) { + return func->priv; +} + +/** A USB device */ +struct usb_device { + /** Name */ + char name[32]; + /** USB port */ + struct usb_port *port; + /** List of devices on this bus */ + struct list_head list; + /** Device address, if assigned */ + unsigned int address; + /** Device descriptor */ + struct usb_device_descriptor device; + /** List of functions */ + struct list_head functions; + + /** Host controller operations */ + struct usb_device_host_operations *host; + /** Host controller private data */ + void *priv; + + /** Endpoint list */ + struct usb_endpoint *ep[32]; + + /** Control endpoint */ + struct usb_endpoint control; + /** Completed control transfers */ + struct list_head complete; +}; + +/** USB device host controller operations */ +struct usb_device_host_operations { + /** Open device + * + * @v usb USB device + * @ret rc Return status code + */ + int ( * open ) ( struct usb_device *usb ); + /** Close device + * + * @v usb USB device + */ + void ( * close ) ( struct usb_device *usb ); + /** Assign device address + * + * @v usb USB device + * @ret rc Return status code + */ + int ( * address ) ( struct usb_device *usb ); +}; + +/** + * Set USB device host controller private data + * + * @v usb USB device + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_set_hostdata ( struct usb_device *usb, void *priv ) { + usb->priv = priv; +} + +/** + * Get USB device host controller private data + * + * @v usb USB device + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_get_hostdata ( struct usb_device *usb ) { + return usb->priv; +} + +/** + * Get USB endpoint + * + * @v usb USB device + * @v address Endpoint address + * @ret ep USB endpoint, or NULL if not opened + */ +static inline struct usb_endpoint * usb_endpoint ( struct usb_device *usb, + unsigned int address ) { + + return usb->ep[ USB_ENDPOINT_IDX ( address ) ]; +} + +/** A USB port */ +struct usb_port { + /** USB hub */ + struct usb_hub *hub; + /** Port address */ + unsigned int address; + /** Port protocol */ + unsigned int protocol; + /** Port speed */ + unsigned int speed; + /** Port disconnection has been detected + * + * This should be set whenever the underlying hardware reports + * a connection status change. + */ + int disconnected; + /** Port has an attached device */ + int attached; + /** Currently attached device (if in use) + * + * Note that this field will be NULL if the attached device + * has been freed (e.g. because there were no drivers found). + */ + struct usb_device *usb; + /** List of changed ports */ + struct list_head changed; +}; + +/** A USB hub */ +struct usb_hub { + /** Name */ + const char *name; + /** USB bus */ + struct usb_bus *bus; + /** Underlying USB device, if any */ + struct usb_device *usb; + /** Hub protocol */ + unsigned int protocol; + /** Number of ports */ + unsigned int ports; + + /** List of hubs */ + struct list_head list; + + /** Host controller operations */ + struct usb_hub_host_operations *host; + /** Driver operations */ + struct usb_hub_driver_operations *driver; + /** Driver private data */ + void *priv; + + /** Port list + * + * This must be the last field within the structure. + */ + struct usb_port port[0]; +}; + +/** USB hub host controller operations */ +struct usb_hub_host_operations { + /** Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ + int ( * open ) ( struct usb_hub *hub ); + /** Close hub + * + * @v hub USB hub + */ + void ( * close ) ( struct usb_hub *hub ); +}; + +/** USB hub driver operations */ +struct usb_hub_driver_operations { + /** Open hub + * + * @v hub USB hub + * @ret rc Return status code + */ + int ( * open ) ( struct usb_hub *hub ); + /** Close hub + * + * @v hub USB hub + */ + void ( * close ) ( struct usb_hub *hub ); + /** Enable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * enable ) ( struct usb_hub *hub, struct usb_port *port ); + /** Disable port + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * disable ) ( struct usb_hub *hub, struct usb_port *port ); + /** Update port speed + * + * @v hub USB hub + * @v port USB port + * @ret rc Return status code + */ + int ( * speed ) ( struct usb_hub *hub, struct usb_port *port ); + /** Clear transaction translator buffer + * + * @v hub USB hub + * @v port USB port + * @v ep USB endpoint + * @ret rc Return status code + */ + int ( * clear_tt ) ( struct usb_hub *hub, struct usb_port *port, + struct usb_endpoint *ep ); +}; + +/** + * Set USB hub driver private data + * + * @v hub USB hub + * @v priv Driver private data + */ +static inline __attribute__ (( always_inline )) void +usb_hub_set_drvdata ( struct usb_hub *hub, void *priv ) { + hub->priv = priv; +} + +/** + * Get USB hub driver private data + * + * @v hub USB hub + * @ret priv Driver private data + */ +static inline __attribute__ (( always_inline )) void * +usb_hub_get_drvdata ( struct usb_hub *hub ) { + return hub->priv; +} + +/** + * Get USB port + * + * @v hub USB hub + * @v address Port address + * @ret port USB port + */ +static inline __attribute__ (( always_inline )) struct usb_port * +usb_port ( struct usb_hub *hub, unsigned int address ) { + + return &hub->port[ address - 1 ]; +} + +/** A USB bus */ +struct usb_bus { + /** Name */ + const char *name; + /** Underlying hardware device */ + struct device *dev; + /** Host controller operations set */ + struct usb_host_operations *op; + + /** Largest transfer allowed on the bus */ + size_t mtu; + /** Address in-use mask + * + * This is used only by buses which perform manual address + * assignment. USB allows for addresses in the range [1,127]. + * We use a simple bitmask which restricts us to the range + * [1,64]; this is unlikely to be a problem in practice. For + * comparison: controllers which perform autonomous address + * assignment (such as xHCI) typically allow for only 32 + * devices per bus anyway. + */ + unsigned long long addresses; + + /** Root hub */ + struct usb_hub *hub; + + /** List of USB buses */ + struct list_head list; + /** List of devices */ + struct list_head devices; + /** List of hubs */ + struct list_head hubs; + + /** Host controller operations */ + struct usb_bus_host_operations *host; + /** Host controller private data */ + void *priv; +}; + +/** USB bus host controller operations */ +struct usb_bus_host_operations { + /** Open bus + * + * @v bus USB bus + * @ret rc Return status code + */ + int ( * open ) ( struct usb_bus *bus ); + /** Close bus + * + * @v bus USB bus + */ + void ( * close ) ( struct usb_bus *bus ); + /** Poll bus + * + * @v bus USB bus + */ + void ( * poll ) ( struct usb_bus *bus ); +}; + +/** USB host controller operations */ +struct usb_host_operations { + /** Endpoint operations */ + struct usb_endpoint_host_operations endpoint; + /** Device operations */ + struct usb_device_host_operations device; + /** Bus operations */ + struct usb_bus_host_operations bus; + /** Hub operations */ + struct usb_hub_host_operations hub; + /** Root hub operations */ + struct usb_hub_driver_operations root; +}; + +/** + * Set USB bus host controller private data + * + * @v bus USB bus + * @v priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void +usb_bus_set_hostdata ( struct usb_bus *bus, void *priv ) { + bus->priv = priv; +} + +/** + * Get USB bus host controller private data + * + * @v bus USB bus + * @ret priv Host controller private data + */ +static inline __attribute__ (( always_inline )) void * +usb_bus_get_hostdata ( struct usb_bus *bus ) { + return bus->priv; +} + +/** + * Poll USB bus + * + * @v bus USB bus + */ +static inline __attribute__ (( always_inline )) void +usb_poll ( struct usb_bus *bus ) { + bus->host->poll ( bus ); +} + +/** Iterate over all USB buses */ +#define for_each_usb_bus( bus ) \ + list_for_each_entry ( (bus), &usb_buses, list ) + +/** + * Complete transfer (without error) + * + * @v ep USB endpoint + * @v iobuf I/O buffer + */ +static inline __attribute__ (( always_inline )) void +usb_complete ( struct usb_endpoint *ep, struct io_buffer *iobuf ) { + usb_complete_err ( ep, iobuf, 0 ); +} + +extern int usb_control ( struct usb_device *usb, unsigned int request, + unsigned int value, unsigned int index, void *data, + size_t len ); +extern int usb_get_string_descriptor ( struct usb_device *usb, + unsigned int index, + unsigned int language, + char *buf, size_t len ); + +/** + * Get status + * + * @v usb USB device + * @v type Request type + * @v index Target index + * @v data Status to fill in + * @v len Length of status descriptor + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_status ( struct usb_device *usb, unsigned int type, unsigned int index, + void *data, size_t len ) { + + return usb_control ( usb, ( USB_GET_STATUS | type ), 0, index, + data, len ); +} + +/** + * Clear feature + * + * @v usb USB device + * @v type Request type + * @v feature Feature selector + * @v index Target index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_clear_feature ( struct usb_device *usb, unsigned int type, + unsigned int feature, unsigned int index ) { + + return usb_control ( usb, ( USB_CLEAR_FEATURE | type ), + feature, index, NULL, 0 ); +} + +/** + * Set feature + * + * @v usb USB device + * @v type Request type + * @v feature Feature selector + * @v index Target index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_feature ( struct usb_device *usb, unsigned int type, + unsigned int feature, unsigned int index ) { + + return usb_control ( usb, ( USB_SET_FEATURE | type ), + feature, index, NULL, 0 ); +} + +/** + * Set address + * + * @v usb USB device + * @v address Device address + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_address ( struct usb_device *usb, unsigned int address ) { + + return usb_control ( usb, USB_SET_ADDRESS, address, 0, NULL, 0 ); +} + +/** + * Get USB descriptor + * + * @v usb USB device + * @v type Request type + * @v desc Descriptor type + * @v index Descriptor index + * @v language Language ID (for string descriptors) + * @v data Descriptor to fill in + * @v len Maximum length of descriptor + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_descriptor ( struct usb_device *usb, unsigned int type, + unsigned int desc, unsigned int index, + unsigned int language, struct usb_descriptor_header *data, + size_t len ) { + + return usb_control ( usb, ( USB_GET_DESCRIPTOR | type ), + ( ( desc << 8 ) | index ), language, data, len ); +} + +/** + * Get first part of USB device descriptor (up to and including MTU) + * + * @v usb USB device + * @v data Device descriptor to (partially) fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_mtu ( struct usb_device *usb, struct usb_device_descriptor *data ) { + + return usb_get_descriptor ( usb, 0, USB_DEVICE_DESCRIPTOR, 0, 0, + &data->header, + ( offsetof ( typeof ( *data ), mtu ) + + sizeof ( data->mtu ) ) ); +} + +/** + * Get USB device descriptor + * + * @v usb USB device + * @v data Device descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_get_device_descriptor ( struct usb_device *usb, + struct usb_device_descriptor *data ) { + + return usb_get_descriptor ( usb, 0, USB_DEVICE_DESCRIPTOR, 0, 0, + &data->header, sizeof ( *data ) ); +} + +/** + * Get USB configuration descriptor + * + * @v usb USB device + * @v index Configuration index + * @v data Configuration descriptor to fill in + * @ret rc Return status code + */ +static inline __attribute (( always_inline )) int +usb_get_config_descriptor ( struct usb_device *usb, unsigned int index, + struct usb_configuration_descriptor *data, + size_t len ) { + + return usb_get_descriptor ( usb, 0, USB_CONFIGURATION_DESCRIPTOR, index, + 0, &data->header, len ); +} + +/** + * Set USB configuration + * + * @v usb USB device + * @v index Configuration index + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_configuration ( struct usb_device *usb, unsigned int index ) { + + return usb_control ( usb, USB_SET_CONFIGURATION, index, 0, NULL, 0 ); +} + +/** + * Set USB interface alternate setting + * + * @v usb USB device + * @v interface Interface number + * @v alternate Alternate setting + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usb_set_interface ( struct usb_device *usb, unsigned int interface, + unsigned int alternate ) { + + return usb_control ( usb, USB_SET_INTERFACE, alternate, interface, + NULL, 0 ); +} + +extern struct list_head usb_buses; + +extern struct usb_interface_descriptor * +usb_interface_descriptor ( struct usb_configuration_descriptor *config, + unsigned int interface, unsigned int alternate ); +extern struct usb_endpoint_descriptor * +usb_endpoint_descriptor ( struct usb_configuration_descriptor *config, + struct usb_interface_descriptor *interface, + unsigned int type, unsigned int index ); +extern struct usb_endpoint_companion_descriptor * +usb_endpoint_companion_descriptor ( struct usb_configuration_descriptor *config, + struct usb_endpoint_descriptor *desc ); + +extern struct usb_hub * alloc_usb_hub ( struct usb_bus *bus, + struct usb_device *usb, + unsigned int ports, + struct usb_hub_driver_operations *op ); +extern int register_usb_hub ( struct usb_hub *hub ); +extern void unregister_usb_hub ( struct usb_hub *hub ); +extern void free_usb_hub ( struct usb_hub *hub ); + +extern void usb_port_changed ( struct usb_port *port ); + +extern struct usb_bus * alloc_usb_bus ( struct device *dev, + unsigned int ports, size_t mtu, + struct usb_host_operations *op ); +extern int register_usb_bus ( struct usb_bus *bus ); +extern void unregister_usb_bus ( struct usb_bus *bus ); +extern void free_usb_bus ( struct usb_bus *bus ); +extern struct usb_bus * find_usb_bus_by_location ( unsigned int bus_type, + unsigned int location ); + +extern int usb_alloc_address ( struct usb_bus *bus ); +extern void usb_free_address ( struct usb_bus *bus, unsigned int address ); +extern unsigned int usb_route_string ( struct usb_device *usb ); +extern unsigned int usb_depth ( struct usb_device *usb ); +extern struct usb_port * usb_root_hub_port ( struct usb_device *usb ); +extern struct usb_port * usb_transaction_translator ( struct usb_device *usb ); + +/** Minimum reset time + * + * Section 7.1.7.5 of the USB2 specification states that root hub + * ports should assert reset signalling for at least 50ms. + */ +#define USB_RESET_DELAY_MS 50 + +/** Reset recovery time + * + * Section 9.2.6.2 of the USB2 specification states that the + * "recovery" interval after a port reset is 10ms. + */ +#define USB_RESET_RECOVER_DELAY_MS 10 + +/** Maximum time to wait for a control transaction to complete + * + * Section 9.2.6.1 of the USB2 specification states that the upper + * limit for commands to be processed is 5 seconds. + */ +#define USB_CONTROL_MAX_WAIT_MS 5000 + +/** Set address recovery time + * + * Section 9.2.6.3 of the USB2 specification states that devices are + * allowed a 2ms recovery interval after receiving a new address. + */ +#define USB_SET_ADDRESS_RECOVER_DELAY_MS 2 + +/** Time to wait for ports to stabilise + * + * Section 7.1.7.3 of the USB specification states that we must allow + * 100ms for devices to signal attachment, and an additional 100ms for + * connection debouncing. (This delay is parallelised across all + * ports on a hub; we do not delay separately for each port.) + */ +#define USB_PORT_DELAY_MS 200 + +/** A USB device ID */ +struct usb_device_id { + /** Name */ + const char *name; + /** Vendor ID */ + uint16_t vendor; + /** Product ID */ + uint16_t product; + /** Class */ + struct usb_class class; +}; + +/** Match-anything ID */ +#define USB_ANY_ID 0xffff + +/** A USB driver */ +struct usb_driver { + /** USB ID table */ + struct usb_device_id *ids; + /** Number of entries in ID table */ + unsigned int id_count; + /** + * Probe device + * + * @v func USB function + * @v config Configuration descriptor + * @ret rc Return status code + */ + int ( * probe ) ( struct usb_function *func, + struct usb_configuration_descriptor *config ); + /** + * Remove device + * + * @v func USB function + */ + void ( * remove ) ( struct usb_function *func ); +}; + +/** USB driver table */ +#define USB_DRIVERS __table ( struct usb_driver, "usb_drivers" ) + +/** Declare a USB driver */ +#define __usb_driver __table_entry ( USB_DRIVERS, 01 ) + +#endif /* _IPXE_USB_H */ diff --git a/roms/ipxe/src/include/ipxe/usbhid.h b/roms/ipxe/src/include/ipxe/usbhid.h new file mode 100644 index 000000000..fe9d84455 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/usbhid.h @@ -0,0 +1,106 @@ +#ifndef _IPXE_USBHID_H +#define _IPXE_USBHID_H + +/** @file + * + * USB human interface devices (HID) + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> + +/** Class code for human interface devices */ +#define USB_CLASS_HID 3 + +/** Subclass code for boot devices */ +#define USB_SUBCLASS_HID_BOOT 1 + +/** Set protocol */ +#define USBHID_SET_PROTOCOL \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x0b ) ) + +/** Boot protocol */ +#define USBHID_PROTOCOL_BOOT 0 + +/** Report protocol */ +#define USBHID_PROTOCOL_REPORT 1 + +/** Set idle time */ +#define USBHID_SET_IDLE \ + ( USB_DIR_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE | \ + USB_REQUEST_TYPE ( 0x0a ) ) + +/** A USB human interface device */ +struct usb_hid { + /** USB function */ + struct usb_function *func; + /** Interrupt IN endpoint */ + struct usb_endpoint in; + /** Interrupt OUT endpoint (optional) */ + struct usb_endpoint out; +}; + +/** + * Initialise USB human interface device + * + * @v hid USB human interface device + * @v func USB function + * @v in Interrupt IN endpoint operations + * @v out Interrupt OUT endpoint operations (or NULL) + */ +static inline __attribute__ (( always_inline )) void +usbhid_init ( struct usb_hid *hid, struct usb_function *func, + struct usb_endpoint_driver_operations *in, + struct usb_endpoint_driver_operations *out ) { + struct usb_device *usb = func->usb; + + hid->func = func; + usb_endpoint_init ( &hid->in, usb, in ); + if ( out ) + usb_endpoint_init ( &hid->out, usb, out ); +} + +/** + * Set protocol + * + * @v usb USB device + * @v interface Interface number + * @v protocol HID protocol + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_protocol ( struct usb_device *usb, unsigned int interface, + unsigned int protocol ) { + + return usb_control ( usb, USBHID_SET_PROTOCOL, protocol, interface, + NULL, 0 ); +} + +/** + * Set idle time + * + * @v usb USB device + * @v interface Interface number + * @v report Report ID + * @v duration Duration (in 4ms units) + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +usbhid_set_idle ( struct usb_device *usb, unsigned int interface, + unsigned int report, unsigned int duration ) { + + return usb_control ( usb, USBHID_SET_IDLE, + ( ( duration << 8 ) | report ), + interface, NULL, 0 ); +} + +extern int usbhid_open ( struct usb_hid *hid ); +extern void usbhid_close ( struct usb_hid *hid ); +extern int usbhid_refill ( struct usb_hid *hid ); +extern int usbhid_describe ( struct usb_hid *hid, + struct usb_configuration_descriptor *config ); + +#endif /* _IPXE_USBHID_H */ diff --git a/roms/ipxe/src/include/ipxe/usbnet.h b/roms/ipxe/src/include/ipxe/usbnet.h new file mode 100644 index 000000000..33a8f3f58 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/usbnet.h @@ -0,0 +1,62 @@ +#ifndef _IPXE_USBNET_H +#define _IPXE_USBNET_H + +/** @file + * + * USB network devices + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <ipxe/usb.h> + +/** A USB network device */ +struct usbnet_device { + /** USB function */ + struct usb_function *func; + + /** Communications interface */ + unsigned int comms; + /** Data interface */ + unsigned int data; + /** Alternate setting for data interface */ + unsigned int alternate; + + /** Interrupt endpoint */ + struct usb_endpoint intr; + /** Bulk IN endpoint */ + struct usb_endpoint in; + /** Bulk OUT endpoint */ + struct usb_endpoint out; +}; + +/** + * Initialise USB network device + * + * @v usbnet USB network device + * @v func USB function + * @v intr Interrupt endpoint operations + * @v in Bulk IN endpoint operations + * @v out Bulk OUT endpoint operations + */ +static inline __attribute__ (( always_inline )) void +usbnet_init ( struct usbnet_device *usbnet, struct usb_function *func, + struct usb_endpoint_driver_operations *intr, + struct usb_endpoint_driver_operations *in, + struct usb_endpoint_driver_operations *out ) { + struct usb_device *usb = func->usb; + + usbnet->func = func; + usb_endpoint_init ( &usbnet->intr, usb, intr ); + usb_endpoint_init ( &usbnet->in, usb, in ); + usb_endpoint_init ( &usbnet->out, usb, out ); +} + +extern int usbnet_open ( struct usbnet_device *usbnet ); +extern void usbnet_close ( struct usbnet_device *usbnet ); +extern int usbnet_refill ( struct usbnet_device *usbnet ); +extern int usbnet_describe ( struct usbnet_device *usbnet, + struct usb_configuration_descriptor *config ); + +#endif /* _IPXE_USBNET_H */ diff --git a/roms/ipxe/src/include/ipxe/uuid.h b/roms/ipxe/src/include/ipxe/uuid.h index ad515d0cb..6c45eb9aa 100644 --- a/roms/ipxe/src/include/ipxe/uuid.h +++ b/roms/ipxe/src/include/ipxe/uuid.h @@ -6,7 +6,7 @@ * Universally unique IDs */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <byteswap.h> diff --git a/roms/ipxe/src/include/ipxe/validator.h b/roms/ipxe/src/include/ipxe/validator.h index 23bdab423..0aee56eb0 100644 --- a/roms/ipxe/src/include/ipxe/validator.h +++ b/roms/ipxe/src/include/ipxe/validator.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/interface.h> #include <ipxe/x509.h> diff --git a/roms/ipxe/src/include/ipxe/version.h b/roms/ipxe/src/include/ipxe/version.h index ae4275db1..a43a33425 100644 --- a/roms/ipxe/src/include/ipxe/version.h +++ b/roms/ipxe/src/include/ipxe/version.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <wchar.h> diff --git a/roms/ipxe/src/include/ipxe/vlan.h b/roms/ipxe/src/include/ipxe/vlan.h index 083c21916..439e0c16d 100644 --- a/roms/ipxe/src/include/ipxe/vlan.h +++ b/roms/ipxe/src/include/ipxe/vlan.h @@ -8,7 +8,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A VLAN header */ struct vlan_header { diff --git a/roms/ipxe/src/include/ipxe/vmbus.h b/roms/ipxe/src/include/ipxe/vmbus.h new file mode 100644 index 000000000..26fc578c6 --- /dev/null +++ b/roms/ipxe/src/include/ipxe/vmbus.h @@ -0,0 +1,634 @@ +#ifndef _IPXE_VMBUS_H +#define _IPXE_VMBUS_H + +/** @file + * + * Hyper-V virtual machine bus + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <byteswap.h> +#include <ipxe/uuid.h> +#include <ipxe/device.h> +#include <ipxe/tables.h> +#include <ipxe/uaccess.h> +#include <ipxe/iobuf.h> +#include <ipxe/hyperv.h> + +/** VMBus message connection ID */ +#define VMBUS_MESSAGE_ID 1 + +/** VMBus event connection ID */ +#define VMBUS_EVENT_ID 2 + +/** VMBus message type */ +#define VMBUS_MESSAGE_TYPE 1 + +/** VMBus message synthetic interrupt */ +#define VMBUS_MESSAGE_SINT 2 + +/** VMBus version number */ +union vmbus_version { + /** Raw version */ + uint32_t raw; + /** Major/minor version */ + struct { + /** Minor version */ + uint16_t minor; + /** Major version */ + uint16_t major; + }; +} __attribute__ (( packed )); + +/** Known VMBus protocol versions */ +enum vmbus_raw_version { + /** Windows Server 2008 */ + VMBUS_VERSION_WS2008 = ( ( 0 << 16 ) | ( 13 << 0 ) ), + /** Windows 7 */ + VMBUS_VERSION_WIN7 = ( ( 1 << 16 ) | ( 1 << 0 ) ), + /** Windows 8 */ + VMBUS_VERSION_WIN8 = ( ( 2 << 16 ) | ( 4 << 0 ) ), + /** Windows 8.1 */ + VMBUS_VERSION_WIN8_1 = ( ( 3 << 16 ) | ( 0 << 0 ) ), +}; + +/** Guest physical address range descriptor */ +struct vmbus_gpa_range { + /** Byte count */ + uint32_t len; + /** Starting byte offset */ + uint32_t offset; + /** Page frame numbers + * + * The length of this array is implied by the byte count and + * starting offset. + */ + uint64_t pfn[0]; +} __attribute__ (( packed )); + +/** VMBus message header */ +struct vmbus_message_header { + /** Message type */ + uint32_t type; + /** Reserved */ + uint32_t reserved; +} __attribute__ (( packed )); + +/** VMBus message types */ +enum vmbus_message_type { + VMBUS_OFFER_CHANNEL = 1, + VMBUS_REQUEST_OFFERS = 3, + VMBUS_ALL_OFFERS_DELIVERED = 4, + VMBUS_OPEN_CHANNEL = 5, + VMBUS_OPEN_CHANNEL_RESULT = 6, + VMBUS_CLOSE_CHANNEL = 7, + VMBUS_GPADL_HEADER = 8, + VMBUS_GPADL_CREATED = 10, + VMBUS_GPADL_TEARDOWN = 11, + VMBUS_GPADL_TORNDOWN = 12, + VMBUS_INITIATE_CONTACT = 14, + VMBUS_VERSION_RESPONSE = 15, + VMBUS_UNLOAD = 16, + VMBUS_UNLOAD_RESPONSE = 17, +}; + +/** VMBus "offer channel" message */ +struct vmbus_offer_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel type */ + union uuid type; + /** Channel instance */ + union uuid instance; + /** Reserved */ + uint8_t reserved_a[16]; + /** Flags */ + uint16_t flags; + /** Reserved */ + uint8_t reserved_b[2]; + /** User data */ + uint8_t data[120]; + /** Reserved */ + uint8_t reserved_c[4]; + /** Channel ID */ + uint32_t channel; + /** Monitor ID */ + uint8_t monitor; + /** Monitor exists */ + uint8_t monitored; + /** Reserved */ + uint8_t reserved[2]; + /** Connection ID */ + uint32_t connection; +} __attribute__ (( packed )); + +/** VMBus "open channel" message */ +struct vmbus_open_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** Open ID */ + uint32_t id; + /** Ring buffer GPADL ID */ + uint32_t gpadl; + /** Reserved */ + uint32_t reserved; + /** Outbound ring buffer size (in pages) */ + uint32_t out_pages; + /** User-specific data */ + uint8_t data[120]; +} __attribute__ (( packed )); + +/** VMBus "open channel result" message */ +struct vmbus_open_channel_result { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** Open ID */ + uint32_t id; + /** Status */ + uint32_t status; +} __attribute__ (( packed )); + +/** VMBus "close channel" message */ +struct vmbus_close_channel { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; +} __attribute__ (( packed )); + +/** VMBus "GPADL header" message */ +struct vmbus_gpadl_header { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; + /** Length of range descriptors */ + uint16_t range_len; + /** Number of range descriptors */ + uint16_t range_count; + /** Range descriptors */ + struct vmbus_gpa_range range[0]; +} __attribute__ (( packed )); + +/** VMBus "GPADL created" message */ +struct vmbus_gpadl_created { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; + /** Creation status */ + uint32_t status; +} __attribute__ (( packed )); + +/** VMBus "GPADL teardown" message */ +struct vmbus_gpadl_teardown { + /** Message header */ + struct vmbus_message_header header; + /** Channel ID */ + uint32_t channel; + /** GPADL ID */ + uint32_t gpadl; +} __attribute__ (( packed )); + +/** VMBus "GPADL torndown" message */ +struct vmbus_gpadl_torndown { + /** Message header */ + struct vmbus_message_header header; + /** GPADL ID */ + uint32_t gpadl; +} __attribute__ (( packed )); + +/** VMBus "initiate contact" message */ +struct vmbus_initiate_contact { + /** Message header */ + struct vmbus_message_header header; + /** Requested version */ + union vmbus_version version; + /** Target virtual CPU */ + uint32_t vcpu; + /** Interrupt page base address */ + uint64_t intr; + /** Parent to child monitor page base address */ + uint64_t monitor_in; + /** Child to parent monitor page base address */ + uint64_t monitor_out; +} __attribute__ (( packed )); + +/** VMBus "version response" message */ +struct vmbus_version_response { + /** Message header */ + struct vmbus_message_header header; + /** Version is supported */ + uint8_t supported; + /** Reserved */ + uint8_t reserved[3]; + /** Version */ + union vmbus_version version; +} __attribute__ (( packed )); + +/** VMBus message */ +union vmbus_message { + /** Common message header */ + struct vmbus_message_header header; + /** "Offer channel" message */ + struct vmbus_offer_channel offer; + /** "Open channel" message */ + struct vmbus_open_channel open; + /** "Open channel result" message */ + struct vmbus_open_channel_result opened; + /** "Close channel" message */ + struct vmbus_close_channel close; + /** "GPADL header" message */ + struct vmbus_gpadl_header gpadlhdr; + /** "GPADL created" message */ + struct vmbus_gpadl_created created; + /** "GPADL teardown" message */ + struct vmbus_gpadl_teardown teardown; + /** "GPADL torndown" message */ + struct vmbus_gpadl_torndown torndown; + /** "Initiate contact" message */ + struct vmbus_initiate_contact initiate; + /** "Version response" message */ + struct vmbus_version_response version; +}; + +/** VMBus packet header */ +struct vmbus_packet_header { + /** Type */ + uint16_t type; + /** Length of packet header (in quadwords) */ + uint16_t hdr_qlen; + /** Length of packet (in quadwords) */ + uint16_t qlen; + /** Flags */ + uint16_t flags; + /** Transaction ID + * + * This is an opaque token: we therefore treat it as + * native-endian and don't worry about byte-swapping. + */ + uint64_t xid; +} __attribute__ (( packed )); + +/** VMBus packet types */ +enum vmbus_packet_type { + VMBUS_DATA_INBAND = 6, + VMBUS_DATA_XFER_PAGES = 7, + VMBUS_DATA_GPA_DIRECT = 9, + VMBUS_CANCELLATION = 10, + VMBUS_COMPLETION = 11, +}; + +/** VMBus packet flags */ +enum vmbus_packet_flags { + VMBUS_COMPLETION_REQUESTED = 0x0001, +}; + +/** VMBus GPA direct header */ +struct vmbus_gpa_direct_header { + /** Packet header */ + struct vmbus_packet_header header; + /** Reserved */ + uint32_t reserved; + /** Number of range descriptors */ + uint32_t range_count; + /** Range descriptors */ + struct vmbus_gpa_range range[0]; +} __attribute__ (( packed )); + +/** VMBus transfer page range */ +struct vmbus_xfer_page_range { + /** Length */ + uint32_t len; + /** Offset */ + uint32_t offset; +} __attribute__ (( packed )); + +/** VMBus transfer page header */ +struct vmbus_xfer_page_header { + /** Packet header */ + struct vmbus_packet_header header; + /** Page set ID */ + uint16_t pageset; + /** Sender owns page set */ + uint8_t owner; + /** Reserved */ + uint8_t reserved; + /** Number of range descriptors */ + uint32_t range_count; + /** Range descriptors */ + struct vmbus_xfer_page_range range[0]; +} __attribute__ (( packed )); + +/** Maximum expected size of VMBus packet header */ +#define VMBUS_PACKET_MAX_HEADER_LEN 64 + +/** VMBus maximum-sized packet header */ +union vmbus_packet_header_max { + /** Common header */ + struct vmbus_packet_header header; + /** GPA direct header */ + struct vmbus_gpa_direct_header gpa; + /** Transfer page header */ + struct vmbus_xfer_page_header xfer; + /** Padding to maximum supported size */ + uint8_t padding[VMBUS_PACKET_MAX_HEADER_LEN]; +} __attribute__ (( packed )); + +/** VMBus packet footer */ +struct vmbus_packet_footer { + /** Reserved */ + uint32_t reserved; + /** Producer index of the first byte of the packet */ + uint32_t prod; +} __attribute__ (( packed )); + +/** VMBus ring buffer + * + * This is the structure of the each of the ring buffers created when + * a VMBus channel is opened. + */ +struct vmbus_ring { + /** Producer index (modulo ring length) */ + uint32_t prod; + /** Consumer index (modulo ring length) */ + uint32_t cons; + /** Interrupt mask */ + uint32_t intr_mask; + /** Reserved */ + uint8_t reserved[4084]; + /** Ring buffer contents */ + uint8_t data[0]; +} __attribute__ (( packed )); + +/** VMBus interrupt page */ +struct vmbus_interrupt { + /** Inbound interrupts */ + uint8_t in[ PAGE_SIZE / 2 ]; + /** Outbound interrupts */ + uint8_t out[ PAGE_SIZE / 2 ]; +} __attribute__ (( packed )); + +/** A virtual machine bus */ +struct vmbus { + /** Interrupt page */ + struct vmbus_interrupt *intr; + /** Inbound notifications */ + struct hv_monitor *monitor_in; + /** Outbound notifications */ + struct hv_monitor *monitor_out; + /** Received message buffer */ + const union vmbus_message *message; +}; + +struct vmbus_device; + +/** VMBus channel operations */ +struct vmbus_channel_operations { + /** + * Handle received control packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ + int ( * recv_control ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); + /** + * Handle received data packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v list List of I/O buffers + * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. It should + * eventually call vmbus_send_completion() to indicate to the + * host that the buffer can be reused. + */ + int ( * recv_data ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct list_head *list ); + /** + * Handle received completion packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + */ + int ( * recv_completion ) ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); + /** + * Handle received cancellation packet + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + */ + int ( * recv_cancellation ) ( struct vmbus_device *vmdev, + uint64_t xid ); +}; + +struct vmbus_xfer_pages; + +/** VMBus transfer page set operations */ +struct vmbus_xfer_pages_operations { + /** + * Copy data from transfer page + * + * @v pages Transfer page set + * @v data Data buffer + * @v offset Offset within page set + * @v len Length within page set + * @ret rc Return status code + */ + int ( * copy ) ( struct vmbus_xfer_pages *pages, void *data, + size_t offset, size_t len ); +}; + +/** VMBus transfer page set */ +struct vmbus_xfer_pages { + /** List of all transfer page sets */ + struct list_head list; + /** Page set ID (in protocol byte order) */ + uint16_t pageset; + /** Page set operations */ + struct vmbus_xfer_pages_operations *op; +}; + +/** A VMBus device */ +struct vmbus_device { + /** Generic iPXE device */ + struct device dev; + /** Hyper-V hypervisor */ + struct hv_hypervisor *hv; + + /** Channel ID */ + unsigned int channel; + /** Monitor ID */ + unsigned int monitor; + /** Signal channel + * + * @v vmdev VMBus device + */ + void ( * signal ) ( struct vmbus_device *vmdev ); + + /** Outbound ring buffer length */ + uint32_t out_len; + /** Inbound ring buffer length */ + uint32_t in_len; + /** Outbound ring buffer */ + struct vmbus_ring *out; + /** Inbound ring buffer */ + struct vmbus_ring *in; + /** Ring buffer GPADL ID */ + unsigned int gpadl; + + /** Channel operations */ + struct vmbus_channel_operations *op; + /** Maximum expected data packet length */ + size_t mtu; + /** Packet buffer */ + void *packet; + /** List of transfer page sets */ + struct list_head pages; + + /** Driver */ + struct vmbus_driver *driver; + /** Driver-private data */ + void *priv; +}; + +/** A VMBus device driver */ +struct vmbus_driver { + /** Name */ + const char *name; + /** Device type */ + union uuid type; + /** Probe device + * + * @v vmdev VMBus device + * @ret rc Return status code + */ + int ( * probe ) ( struct vmbus_device *vmdev ); + /** Remove device + * + * @v vmdev VMBus device + */ + void ( * remove ) ( struct vmbus_device *vmdev ); +}; + +/** VMBus device driver table */ +#define VMBUS_DRIVERS __table ( struct vmbus_driver, "vmbus_drivers" ) + +/** Declare a VMBus device driver */ +#define __vmbus_driver __table_entry ( VMBUS_DRIVERS, 01 ) + +/** + * Set VMBus device driver-private data + * + * @v vmdev VMBus device + * @v priv Private data + */ +static inline void vmbus_set_drvdata ( struct vmbus_device *vmdev, void *priv ){ + vmdev->priv = priv; +} + +/** + * Get VMBus device driver-private data + * + * @v vmdev VMBus device + * @ret priv Private data + */ +static inline void * vmbus_get_drvdata ( struct vmbus_device *vmdev ) { + return vmdev->priv; +} + +/** Construct VMBus type */ +#define VMBUS_TYPE( a, b, c, d, e0, e1, e2, e3, e4, e5 ) { \ + .canonical = { \ + cpu_to_le32 ( a ), cpu_to_le16 ( b ), \ + cpu_to_le16 ( c ), cpu_to_be16 ( d ), \ + { e0, e1, e2, e3, e4, e5 } \ + } } + +/** + * Check if data is present in ring buffer + * + * @v vmdev VMBus device + * @v has_data Data is present + */ +static inline __attribute__ (( always_inline )) int +vmbus_has_data ( struct vmbus_device *vmdev ) { + + return ( vmdev->in->prod != vmdev->in->cons ); +} + +/** + * Register transfer page set + * + * @v vmdev VMBus device + * @v pages Transfer page set + * @ret rc Return status code + */ +static inline __attribute__ (( always_inline )) int +vmbus_register_pages ( struct vmbus_device *vmdev, + struct vmbus_xfer_pages *pages ) { + + list_add ( &pages->list, &vmdev->pages ); + return 0; +} + +/** + * Unregister transfer page set + * + * @v vmdev VMBus device + * @v pages Transfer page set + */ +static inline __attribute__ (( always_inline )) void +vmbus_unregister_pages ( struct vmbus_device *vmdev, + struct vmbus_xfer_pages *pages ) { + + list_check_contains_entry ( pages, &vmdev->pages, list ); + list_del ( &pages->list ); +} + +extern int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, + size_t len ); +extern int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, + unsigned int gpadl ); +extern int vmbus_open ( struct vmbus_device *vmdev, + struct vmbus_channel_operations *op, + size_t out_len, size_t in_len, size_t mtu ); +extern void vmbus_close ( struct vmbus_device *vmdev ); +extern int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); +extern int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, + struct io_buffer *iobuf ); +extern int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ); +extern int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid ); +extern int vmbus_poll ( struct vmbus_device *vmdev ); +extern void vmbus_dump_channel ( struct vmbus_device *vmdev ); + +extern int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ); +extern void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ); + +#endif /* _IPXE_VMBUS_H */ diff --git a/roms/ipxe/src/include/ipxe/vsprintf.h b/roms/ipxe/src/include/ipxe/vsprintf.h index c48c97a87..9e6297715 100644 --- a/roms/ipxe/src/include/ipxe/vsprintf.h +++ b/roms/ipxe/src/include/ipxe/vsprintf.h @@ -31,7 +31,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdarg.h> diff --git a/roms/ipxe/src/include/ipxe/x509.h b/roms/ipxe/src/include/ipxe/x509.h index 802480e54..0daaf5e59 100644 --- a/roms/ipxe/src/include/ipxe/x509.h +++ b/roms/ipxe/src/include/ipxe/x509.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stddef.h> diff --git a/roms/ipxe/src/include/ipxe/xen.h b/roms/ipxe/src/include/ipxe/xen.h index 60aabe03e..eac1145ad 100644 --- a/roms/ipxe/src/include/ipxe/xen.h +++ b/roms/ipxe/src/include/ipxe/xen.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Define Xen interface version before including any Xen header files */ #define __XEN_INTERFACE_VERSION__ 0x00040400 diff --git a/roms/ipxe/src/include/ipxe/xenbus.h b/roms/ipxe/src/include/ipxe/xenbus.h index ef2b5496f..ec5782eed 100644 --- a/roms/ipxe/src/include/ipxe/xenbus.h +++ b/roms/ipxe/src/include/ipxe/xenbus.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/device.h> #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/ipxe/xenevent.h b/roms/ipxe/src/include/ipxe/xenevent.h index 1dd6a0c0b..f0bd3465e 100644 --- a/roms/ipxe/src/include/ipxe/xenevent.h +++ b/roms/ipxe/src/include/ipxe/xenevent.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/xen.h> #include <xen/event_channel.h> diff --git a/roms/ipxe/src/include/ipxe/xengrant.h b/roms/ipxe/src/include/ipxe/xengrant.h index f9b3beb21..451a3ceee 100644 --- a/roms/ipxe/src/include/ipxe/xengrant.h +++ b/roms/ipxe/src/include/ipxe/xengrant.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/include/ipxe/xenmem.h b/roms/ipxe/src/include/ipxe/xenmem.h index 9b9aeda9c..dcc38d460 100644 --- a/roms/ipxe/src/include/ipxe/xenmem.h +++ b/roms/ipxe/src/include/ipxe/xenmem.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/xen.h> #include <xen/memory.h> diff --git a/roms/ipxe/src/include/ipxe/xenstore.h b/roms/ipxe/src/include/ipxe/xenstore.h index f25f15704..892640755 100644 --- a/roms/ipxe/src/include/ipxe/xenstore.h +++ b/roms/ipxe/src/include/ipxe/xenstore.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/xen.h> diff --git a/roms/ipxe/src/include/ipxe/xenver.h b/roms/ipxe/src/include/ipxe/xenver.h index 5d678c5a3..b29dfb321 100644 --- a/roms/ipxe/src/include/ipxe/xenver.h +++ b/roms/ipxe/src/include/ipxe/xenver.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/xen.h> #include <xen/version.h> diff --git a/roms/ipxe/src/include/ipxe/xfer.h b/roms/ipxe/src/include/ipxe/xfer.h index 1167e5cba..3a35fa924 100644 --- a/roms/ipxe/src/include/ipxe/xfer.h +++ b/roms/ipxe/src/include/ipxe/xfer.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdarg.h> @@ -103,5 +103,7 @@ extern int xfer_vprintf ( struct interface *intf, extern int __attribute__ (( format ( printf, 2, 3 ) )) xfer_printf ( struct interface *intf, const char *format, ... ); extern int xfer_seek ( struct interface *intf, off_t offset ); +extern int xfer_check_order ( struct xfer_metadata *meta, size_t *pos, + size_t len ); #endif /* _IPXE_XFER_H */ diff --git a/roms/ipxe/src/include/ipxe/xferbuf.h b/roms/ipxe/src/include/ipxe/xferbuf.h index 2ca871e59..cb0b1a0e8 100644 --- a/roms/ipxe/src/include/ipxe/xferbuf.h +++ b/roms/ipxe/src/include/ipxe/xferbuf.h @@ -7,10 +7,12 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/iobuf.h> +#include <ipxe/uaccess.h> +#include <ipxe/interface.h> #include <ipxe/xfer.h> /** A data transfer buffer */ @@ -21,11 +23,83 @@ struct xfer_buffer { size_t len; /** Current offset within data */ size_t pos; + /** Data transfer buffer operations */ + struct xfer_buffer_operations *op; }; -extern void xferbuf_done ( struct xfer_buffer *xferbuf ); +/** Data transfer buffer operations */ +struct xfer_buffer_operations { + /** Reallocate data buffer + * + * @v xferbuf Data transfer buffer + * @v len New length (or zero to free buffer) + * @ret rc Return status code + */ + int ( * realloc ) ( struct xfer_buffer *xferbuf, size_t len ); + /** Write data to buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to write + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the write does not exceed the buffer length. + */ + void ( * write ) ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); + /** Read data from buffer + * + * @v xferbuf Data transfer buffer + * @v offset Starting offset + * @v data Data to read + * @v len Length of data + * + * This call is simply a wrapper for the appropriate + * memcpy()-like operation: the caller is responsible for + * ensuring that the read does not exceed the buffer length. + */ + void ( * read ) ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); +}; + +extern struct xfer_buffer_operations xferbuf_malloc_operations; +extern struct xfer_buffer_operations xferbuf_umalloc_operations; + +/** + * Initialise malloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_malloc_init ( struct xfer_buffer *xferbuf ) { + xferbuf->op = &xferbuf_malloc_operations; +} + +/** + * Initialise umalloc()-based data transfer buffer + * + * @v xferbuf Data transfer buffer + * @v data User pointer + */ +static inline __attribute__ (( always_inline )) void +xferbuf_umalloc_init ( struct xfer_buffer *xferbuf, userptr_t *data ) { + xferbuf->data = data; + xferbuf->op = &xferbuf_umalloc_operations; +} + +extern void xferbuf_free ( struct xfer_buffer *xferbuf ); +extern int xferbuf_write ( struct xfer_buffer *xferbuf, size_t offset, + const void *data, size_t len ); +extern int xferbuf_read ( struct xfer_buffer *xferbuf, size_t offset, + void *data, size_t len ); extern int xferbuf_deliver ( struct xfer_buffer *xferbuf, struct io_buffer *iobuf, struct xfer_metadata *meta ); +extern struct xfer_buffer * xfer_buffer ( struct interface *intf ); +#define xfer_buffer_TYPE( object_type ) \ + typeof ( struct xfer_buffer * ( object_type ) ) + #endif /* _IPXE_XFERBUF_H */ diff --git a/roms/ipxe/src/include/libgen.h b/roms/ipxe/src/include/libgen.h index 7e94881a9..ae0861270 100644 --- a/roms/ipxe/src/include/libgen.h +++ b/roms/ipxe/src/include/libgen.h @@ -1,7 +1,7 @@ #ifndef _LIBGEN_H #define _LIBGEN_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern char * basename ( char *path ); extern char * dirname ( char *path ); diff --git a/roms/ipxe/src/include/little_bswap.h b/roms/ipxe/src/include/little_bswap.h deleted file mode 100644 index 92dd26ba1..000000000 --- a/roms/ipxe/src/include/little_bswap.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef ETHERBOOT_LITTLE_BSWAP_H -#define ETHERBOOT_LITTLE_BSWAP_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#define htonll(x) __bswap_64(x) -#define ntohll(x) __bswap_64(x) -#define ntohl(x) __bswap_32(x) -#define htonl(x) __bswap_32(x) -#define ntohs(x) __bswap_16(x) -#define htons(x) __bswap_16(x) -#define cpu_to_le64(x) (x) -#define cpu_to_le32(x) (x) -#define cpu_to_le16(x) (x) -#define cpu_to_be64(x) __bswap_64(x) -#define cpu_to_be32(x) __bswap_32(x) -#define cpu_to_be16(x) __bswap_16(x) -#define le64_to_cpu(x) (x) -#define le32_to_cpu(x) (x) -#define le16_to_cpu(x) (x) -#define be64_to_cpu(x) __bswap_64(x) -#define be32_to_cpu(x) __bswap_32(x) -#define be16_to_cpu(x) __bswap_16(x) -#define cpu_to_le64s(x) do {} while (0) -#define cpu_to_le32s(x) do {} while (0) -#define cpu_to_le16s(x) do {} while (0) -#define cpu_to_be64s(x) __bswap_64s(x) -#define cpu_to_be32s(x) __bswap_32s(x) -#define cpu_to_be16s(x) __bswap_16s(x) -#define le64_to_cpus(x) do {} while (0) -#define le32_to_cpus(x) do {} while (0) -#define le16_to_cpus(x) do {} while (0) -#define be64_to_cpus(x) __bswap_64s(x) -#define be32_to_cpus(x) __bswap_32s(x) -#define be16_to_cpus(x) __bswap_16s(x) - -#endif /* ETHERBOOT_LITTLE_BSWAP_H */ diff --git a/roms/ipxe/src/include/nic.h b/roms/ipxe/src/include/nic.h index 9aaede8a7..4c91f57a6 100644 --- a/roms/ipxe/src/include/nic.h +++ b/roms/ipxe/src/include/nic.h @@ -1,8 +1,18 @@ - /* +/* * 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, or (at - * your option) any later version. + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. */ FILE_LICENCE ( GPL2_OR_LATER ); @@ -266,6 +276,7 @@ static inline void * legacy_isa_get_drvdata ( void *hwdev ) { _name ## _isa_legacy_remove ( struct isa_device *isa ) { \ return legacy_remove ( isa, legacy_isa_get_drvdata, \ _name ## _disable ); \ - } + } \ + PROVIDE_REQUIRING_SYMBOL() #endif /* NIC_H */ diff --git a/roms/ipxe/src/include/readline/readline.h b/roms/ipxe/src/include/readline/readline.h index 0449a3f98..afafbbdf5 100644 --- a/roms/ipxe/src/include/readline/readline.h +++ b/roms/ipxe/src/include/readline/readline.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A readline history entry */ struct readline_history_entry { diff --git a/roms/ipxe/src/include/stdarg.h b/roms/ipxe/src/include/stdarg.h index f317238a9..89e94ce22 100644 --- a/roms/ipxe/src/include/stdarg.h +++ b/roms/ipxe/src/include/stdarg.h @@ -1,7 +1,7 @@ #ifndef _STDARG_H #define _STDARG_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); typedef __builtin_va_list va_list; #define va_start( ap, last ) __builtin_va_start ( ap, last ) diff --git a/roms/ipxe/src/include/stddef.h b/roms/ipxe/src/include/stddef.h index bf792771f..3c056294f 100644 --- a/roms/ipxe/src/include/stddef.h +++ b/roms/ipxe/src/include/stddef.h @@ -1,25 +1,43 @@ #ifndef STDDEF_H #define STDDEF_H -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -/* for size_t */ #include <stdint.h> +/** EFI headers also define NULL */ #undef NULL -#define NULL ((void *)0) -#undef offsetof -#if ( defined ( __GNUC__ ) && ( __GNUC__ > 3 ) ) -#define offsetof(TYPE, MEMBER) __builtin_offsetof(TYPE, MEMBER) +/** Null pointer */ +#define NULL ( ( void * ) 0 ) + +/** + * Get offset of a field within a structure + * + * @v type Structure type + * @v field Field within structure + * @ret offset Offset within structure + */ +#if defined ( __GNUC__ ) && ( __GNUC__ > 3 ) +#define offsetof( type, field ) __builtin_offsetof ( type, field ) #else -#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) +#define offsetof( type, field ) ( ( size_t ) &( ( ( type * ) NULL )->field ) ) #endif -#undef container_of -#define container_of(ptr, type, member) ({ \ - const typeof( ((type *)0)->member ) *__mptr = (ptr); \ - (type *)( (char *)__mptr - offsetof(type,member) );}) +/** + * Get containing structure + * + * @v ptr Pointer to contained field + * @v type Containing structure type + * @v field Field within containing structure + * @ret container Pointer to containing structure + */ +#define container_of( ptr, type, field ) ( { \ + type *__container; \ + const typeof ( __container->field ) *__field = (ptr); \ + __container = ( ( ( void * ) __field ) - \ + offsetof ( type, field ) ); \ + __container; } ) /* __WCHAR_TYPE__ is defined by gcc and will change if -fshort-wchar is used */ #ifndef __WCHAR_TYPE__ diff --git a/roms/ipxe/src/include/stdint.h b/roms/ipxe/src/include/stdint.h index 8cc9b84a5..0a239a517 100644 --- a/roms/ipxe/src/include/stdint.h +++ b/roms/ipxe/src/include/stdint.h @@ -1,7 +1,7 @@ #ifndef _STDINT_H #define _STDINT_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* * This is a standard predefined macro on all gcc's I've seen. It's diff --git a/roms/ipxe/src/include/stdio.h b/roms/ipxe/src/include/stdio.h index 91840af5b..a618482ce 100644 --- a/roms/ipxe/src/include/stdio.h +++ b/roms/ipxe/src/include/stdio.h @@ -1,7 +1,7 @@ #ifndef _STDIO_H #define _STDIO_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdarg.h> diff --git a/roms/ipxe/src/include/stdlib.h b/roms/ipxe/src/include/stdlib.h index 2951522b8..d7748a07e 100644 --- a/roms/ipxe/src/include/stdlib.h +++ b/roms/ipxe/src/include/stdlib.h @@ -1,7 +1,7 @@ #ifndef STDLIB_H #define STDLIB_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <assert.h> @@ -13,31 +13,9 @@ FILE_LICENCE ( GPL2_OR_LATER ); **************************************************************************** */ -static inline int strtoul_base ( const char **pp, int base ) -{ - const char *p = *pp; - - if ( base == 0 ) { - base = 10; - if ( *p == '0' ) { - p++; - base = 8; - if ( ( *p | 0x20 ) == 'x' ) { - p++; - base = 16; - } - } - } - - *pp = p; - - return base; -} - -extern unsigned int strtoul_charval ( unsigned int charval ); -extern unsigned long strtoul ( const char *p, char **endp, int base ); -extern unsigned long long strtoull ( const char *p, char **endp, int base ); - +extern unsigned long strtoul ( const char *string, char **endp, int base ); +extern unsigned long long strtoull ( const char *string, char **endp, + int base ); /***************************************************************************** * diff --git a/roms/ipxe/src/include/string.h b/roms/ipxe/src/include/string.h index 3482e1b22..0fab6c74b 100644 --- a/roms/ipxe/src/include/string.h +++ b/roms/ipxe/src/include/string.h @@ -1,52 +1,53 @@ -/* - * Copyright (C) 1991, 1992 Linus Torvalds - * Copyright (C) 2004 Tobias Lorenz +#ifndef _STRING_H +#define _STRING_H + +/** @file * - * string handling functions - * based on linux/include/linux/ctype.h - * and linux/include/linux/string.h + * String functions * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. */ -FILE_LICENCE ( GPL2_ONLY ); - -#ifndef ETHERBOOT_STRING_H -#define ETHERBOOT_STRING_H +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <bits/string.h> -int __pure strnicmp(const char *s1, const char *s2, size_t len) __nonnull; -char * strcpy(char * dest,const char *src) __nonnull; -char * strncpy(char * dest,const char *src,size_t count) __nonnull; -char * strcat(char * dest, const char * src) __nonnull; -char * strncat(char *dest, const char *src, size_t count) __nonnull; -int __pure strcmp(const char * cs,const char * ct) __nonnull; -int __pure strncmp(const char * cs,const char * ct, - size_t count) __nonnull; -char * __pure strchr(const char * s, int c) __nonnull; -char * __pure strrchr(const char * s, int c) __nonnull; -size_t __pure strlen(const char * s) __nonnull; -size_t __pure strnlen(const char * s, size_t count) __nonnull; -size_t __pure strspn(const char *s, const char *accept) __nonnull; -size_t __pure strcspn(const char *s, const char *reject) __nonnull; -char * __pure strpbrk(const char * cs,const char * ct) __nonnull; -char * strtok(char * s,const char * ct) __nonnull; -char * strsep(char **s, const char *ct) __nonnull; -void * memset(void * s,int c,size_t count) __nonnull; +/* Architecture-specific code is expected to provide these functions, + * but may instead explicitly choose to use the generic versions. + */ +void * memset ( void *dest, int character, size_t len ) __nonnull; void * memcpy ( void *dest, const void *src, size_t len ) __nonnull; -void * memmove(void * dest,const void *src,size_t count) __nonnull; -int __pure memcmp(const void * cs,const void * ct, - size_t count) __nonnull; -void * __pure memscan(const void * addr, int c, size_t size) __nonnull; -char * __pure strstr(const char * s1,const char * s2) __nonnull; -void * __pure memchr(const void *s, int c, size_t n) __nonnull; -char * __malloc strdup(const char *s) __nonnull; -char * __malloc strndup(const char *s, size_t n) __nonnull; +void * memmove ( void *dest, const void *src, size_t len ) __nonnull; +extern void * generic_memset ( void *dest, int character, + size_t len ) __nonnull; +extern void * generic_memcpy ( void *dest, const void *src, + size_t len ) __nonnull; +extern void * generic_memmove ( void *dest, const void *src, + size_t len ) __nonnull; + +extern int __pure memcmp ( const void *first, const void *second, + size_t len ) __nonnull; +extern void * __pure memchr ( const void *src, int character, + size_t len ) __nonnull; +extern void * memswap ( void *dest, void *src, size_t len ) __nonnull; +extern int __pure strcmp ( const char *first, const char *second ) __nonnull; +extern int __pure strncmp ( const char *first, const char *second, + size_t max ) __nonnull; +extern size_t __pure strlen ( const char *src ) __nonnull; +extern size_t __pure strnlen ( const char *src, size_t max ) __nonnull; +extern char * __pure strchr ( const char *src, int character ) __nonnull; +extern char * __pure strrchr ( const char *src, int character ) __nonnull; +extern char * __pure strstr ( const char *haystack, + const char *needle ) __nonnull; +extern char * strcpy ( char *dest, const char *src ) __nonnull; +extern char * strncpy ( char *dest, const char *src, size_t max ) __nonnull; +extern char * strcat ( char *dest, const char *src ) __nonnull; +extern char * __malloc strdup ( const char *src ) __nonnull; +extern char * __malloc strndup ( const char *src, size_t max ) __nonnull; +extern char * __pure strpbrk ( const char *string, + const char *delim ) __nonnull; +extern char * strsep ( char **string, const char *delim ) __nonnull; -extern const char * __pure strerror ( int errno ); +extern char * __pure strerror ( int errno ); -#endif /* ETHERBOOT_STRING */ +#endif /* _STRING_H */ diff --git a/roms/ipxe/src/include/strings.h b/roms/ipxe/src/include/strings.h index 6912a1e45..fab26dc28 100644 --- a/roms/ipxe/src/include/strings.h +++ b/roms/ipxe/src/include/strings.h @@ -1,12 +1,71 @@ #ifndef _STRINGS_H #define _STRINGS_H -FILE_LICENCE ( GPL2_OR_LATER ); +/** @file + * + * String functions + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); -#include <limits.h> #include <string.h> #include <bits/strings.h> +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int +__constant_ffsll ( unsigned long long x ) { + int r = 0; + + if ( ! ( x & 0x00000000ffffffffULL ) ) { + x >>= 32; + r += 32; + } + if ( ! ( x & 0x0000ffffUL ) ) { + x >>= 16; + r += 16; + } + if ( ! ( x & 0x00ff ) ) { + x >>= 8; + r += 8; + } + if ( ! ( x & 0x0f ) ) { + x >>= 4; + r += 4; + } + if ( ! ( x & 0x3 ) ) { + x >>= 2; + r += 2; + } + if ( ! ( x & 0x1 ) ) { + x >>= 1; + r += 1; + } + return ( x ? ( r + 1 ) : 0 ); +} + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +static inline __attribute__ (( always_inline )) int +__constant_ffsl ( unsigned long x ) { + return __constant_ffsll ( x ); +} + +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ static inline __attribute__ (( always_inline )) int __constant_flsll ( unsigned long long x ) { int r = 0; @@ -35,38 +94,100 @@ __constant_flsll ( unsigned long long x ) { x >>= 1; r += 1; } - if ( x & 0x1 ) { - r += 1; - } - return r; + return ( x ? ( r + 1 ) : 0 ); } +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ static inline __attribute__ (( always_inline )) int __constant_flsl ( unsigned long x ) { return __constant_flsll ( x ); } +int __ffsll ( long long x ); +int __ffsl ( long x ); int __flsll ( long long x ); int __flsl ( long x ); +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffsll( x ) \ + ( __builtin_constant_p ( x ) ? __constant_ffsll ( x ) : __ffsll ( x ) ) + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffsl( x ) \ + ( __builtin_constant_p ( x ) ? __constant_ffsl ( x ) : __ffsl ( x ) ) + +/** + * Find first (i.e. least significant) set bit + * + * @v x Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +#define ffs( x ) ffsl ( x ) + +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define flsll( x ) \ ( __builtin_constant_p ( x ) ? __constant_flsll ( x ) : __flsll ( x ) ) +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define flsl( x ) \ ( __builtin_constant_p ( x ) ? __constant_flsl ( x ) : __flsl ( x ) ) +/** + * Find last (i.e. most significant) set bit + * + * @v x Value + * @ret msb Most significant bit set in value (LSB=1), or zero + */ #define fls( x ) flsl ( x ) -extern int strcasecmp ( const char *s1, const char *s2 ); - +/** + * Copy memory + * + * @v src Source + * @v dest Destination + * @v len Length + */ static inline __attribute__ (( always_inline )) void -bcopy ( const void *src, void *dest, size_t n ) { - memmove ( dest, src, n ); +bcopy ( const void *src, void *dest, size_t len ) { + memmove ( dest, src, len ); } +/** + * Zero memory + * + * @v dest Destination + * @v len Length + */ static inline __attribute__ (( always_inline )) void -bzero ( void *s, size_t n ) { - memset ( s, 0, n ); +bzero ( void *dest, size_t len ) { + memset ( dest, 0, len ); } +int __pure strcasecmp ( const char *first, const char *second ) __nonnull; + #endif /* _STRINGS_H */ diff --git a/roms/ipxe/src/include/sys/time.h b/roms/ipxe/src/include/sys/time.h index 2647d3588..6e2a24447 100644 --- a/roms/ipxe/src/include/sys/time.h +++ b/roms/ipxe/src/include/sys/time.h @@ -6,7 +6,7 @@ * Date and time */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/syslog.h b/roms/ipxe/src/include/syslog.h index 93f32f867..748a4faec 100644 --- a/roms/ipxe/src/include/syslog.h +++ b/roms/ipxe/src/include/syslog.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdarg.h> #include <ipxe/ansiesc.h> diff --git a/roms/ipxe/src/include/time.h b/roms/ipxe/src/include/time.h index 452a544bb..462ac6999 100644 --- a/roms/ipxe/src/include/time.h +++ b/roms/ipxe/src/include/time.h @@ -6,7 +6,7 @@ * Date and time */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <sys/time.h> #include <ipxe/time.h> diff --git a/roms/ipxe/src/include/unistd.h b/roms/ipxe/src/include/unistd.h index 3a50a2521..d09e1ae30 100644 --- a/roms/ipxe/src/include/unistd.h +++ b/roms/ipxe/src/include/unistd.h @@ -1,7 +1,7 @@ #ifndef _UNISTD_H #define _UNISTD_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdarg.h> diff --git a/roms/ipxe/src/include/usr/autoboot.h b/roms/ipxe/src/include/usr/autoboot.h index bc51aae79..4db226b9c 100644 --- a/roms/ipxe/src/include/usr/autoboot.h +++ b/roms/ipxe/src/include/usr/autoboot.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/device.h> @@ -35,7 +35,7 @@ extern int uriboot ( struct uri *filename, struct uri *root_path, int drive, extern struct uri * fetch_next_server_and_filename ( struct settings *settings ); extern int netboot ( struct net_device *netdev ); -extern void ipxe ( struct net_device *netdev ); +extern int ipxe ( struct net_device *netdev ); extern int pxe_menu_boot ( struct net_device *netdev ); diff --git a/roms/ipxe/src/include/usr/dhcpmgmt.h b/roms/ipxe/src/include/usr/dhcpmgmt.h index af1eceb17..ed669eb9d 100644 --- a/roms/ipxe/src/include/usr/dhcpmgmt.h +++ b/roms/ipxe/src/include/usr/dhcpmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct net_device; diff --git a/roms/ipxe/src/include/usr/fcmgmt.h b/roms/ipxe/src/include/usr/fcmgmt.h index 9441cefb4..eb568fd20 100644 --- a/roms/ipxe/src/include/usr/fcmgmt.h +++ b/roms/ipxe/src/include/usr/fcmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct fc_port; struct fc_peer; diff --git a/roms/ipxe/src/include/usr/ifmgmt.h b/roms/ipxe/src/include/usr/ifmgmt.h index db77f1f1b..5c386327b 100644 --- a/roms/ipxe/src/include/usr/ifmgmt.h +++ b/roms/ipxe/src/include/usr/ifmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); struct net_device; struct net_device_configurator; diff --git a/roms/ipxe/src/include/usr/imgmgmt.h b/roms/ipxe/src/include/usr/imgmgmt.h index 5e25c562b..806df0bfb 100644 --- a/roms/ipxe/src/include/usr/imgmgmt.h +++ b/roms/ipxe/src/include/usr/imgmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/image.h> diff --git a/roms/ipxe/src/include/usr/imgtrust.h b/roms/ipxe/src/include/usr/imgtrust.h index f47105af0..414e07a80 100644 --- a/roms/ipxe/src/include/usr/imgtrust.h +++ b/roms/ipxe/src/include/usr/imgtrust.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/image.h> diff --git a/roms/ipxe/src/include/usr/ipstat.h b/roms/ipxe/src/include/usr/ipstat.h index 5ff8b40c3..803254bcb 100644 --- a/roms/ipxe/src/include/usr/ipstat.h +++ b/roms/ipxe/src/include/usr/ipstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void ipstat ( void ); diff --git a/roms/ipxe/src/include/usr/lotest.h b/roms/ipxe/src/include/usr/lotest.h index aa4bbac4d..ce0fe5eda 100644 --- a/roms/ipxe/src/include/usr/lotest.h +++ b/roms/ipxe/src/include/usr/lotest.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int loopback_test ( struct net_device *sender, struct net_device *receiver, size_t mtu ); diff --git a/roms/ipxe/src/include/usr/neighmgmt.h b/roms/ipxe/src/include/usr/neighmgmt.h index 3c2b704af..06f03716e 100644 --- a/roms/ipxe/src/include/usr/neighmgmt.h +++ b/roms/ipxe/src/include/usr/neighmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void nstat ( void ); diff --git a/roms/ipxe/src/include/usr/pingmgmt.h b/roms/ipxe/src/include/usr/pingmgmt.h index d4c2d6cd5..c7a8434be 100644 --- a/roms/ipxe/src/include/usr/pingmgmt.h +++ b/roms/ipxe/src/include/usr/pingmgmt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> diff --git a/roms/ipxe/src/include/usr/profstat.h b/roms/ipxe/src/include/usr/profstat.h index 06ea251a0..b7812ca7f 100644 --- a/roms/ipxe/src/include/usr/profstat.h +++ b/roms/ipxe/src/include/usr/profstat.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern void profstat ( void ); diff --git a/roms/ipxe/src/include/usr/prompt.h b/roms/ipxe/src/include/usr/prompt.h index 57e43d2dc..8d3eeee3c 100644 --- a/roms/ipxe/src/include/usr/prompt.h +++ b/roms/ipxe/src/include/usr/prompt.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int prompt ( const char *text, unsigned long timeout, int key ); diff --git a/roms/ipxe/src/include/usr/route.h b/roms/ipxe/src/include/usr/route.h index b914f4b84..7ec4a3509 100644 --- a/roms/ipxe/src/include/usr/route.h +++ b/roms/ipxe/src/include/usr/route.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/tables.h> diff --git a/roms/ipxe/src/include/usr/sync.h b/roms/ipxe/src/include/usr/sync.h index 0047d4ed9..b6f12ad6e 100644 --- a/roms/ipxe/src/include/usr/sync.h +++ b/roms/ipxe/src/include/usr/sync.h @@ -7,7 +7,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); extern int sync ( unsigned long timeout ); diff --git a/roms/ipxe/src/arch/x86/include/valgrind/memcheck.h b/roms/ipxe/src/include/valgrind/memcheck.h index 7d4b56d31..7d4b56d31 100644 --- a/roms/ipxe/src/arch/x86/include/valgrind/memcheck.h +++ b/roms/ipxe/src/include/valgrind/memcheck.h diff --git a/roms/ipxe/src/arch/x86/include/valgrind/valgrind.h b/roms/ipxe/src/include/valgrind/valgrind.h index d48bbccae..d48bbccae 100644 --- a/roms/ipxe/src/arch/x86/include/valgrind/valgrind.h +++ b/roms/ipxe/src/include/valgrind/valgrind.h diff --git a/roms/ipxe/src/include/wchar.h b/roms/ipxe/src/include/wchar.h index ba349aae8..d054b8d5b 100644 --- a/roms/ipxe/src/include/wchar.h +++ b/roms/ipxe/src/include/wchar.h @@ -1,7 +1,7 @@ #ifndef WCHAR_H #define WCHAR_H -FILE_LICENCE ( GPL2_ONLY ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> diff --git a/roms/ipxe/src/interface/bofm/bofm.c b/roms/ipxe/src/interface/bofm/bofm.c index b0e92b27c..545088dc6 100644 --- a/roms/ipxe/src/interface/bofm/bofm.c +++ b/roms/ipxe/src/interface/bofm/bofm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/interface/efi/efi_autoboot.c b/roms/ipxe/src/interface/efi/efi_autoboot.c index ab0f36541..a9e807e23 100644 --- a/roms/ipxe/src/interface/efi/efi_autoboot.c +++ b/roms/ipxe/src/interface/efi/efi_autoboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/efi_autoboot.h> diff --git a/roms/ipxe/src/interface/efi/efi_bofm.c b/roms/ipxe/src/interface/efi/efi_bofm.c index bdb705196..ea0e15f7f 100644 --- a/roms/ipxe/src/interface/efi/efi_bofm.c +++ b/roms/ipxe/src/interface/efi/efi_bofm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <errno.h> #include <ipxe/bofm.h> diff --git a/roms/ipxe/src/interface/efi/efi_debug.c b/roms/ipxe/src/interface/efi/efi_debug.c index d23960140..473803951 100644 --- a/roms/ipxe/src/interface/efi/efi_debug.c +++ b/roms/ipxe/src/interface/efi/efi_debug.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -326,7 +330,7 @@ const char * efi_devpath_text ( EFI_DEVICE_PATH_PROTOCOL *path ) { max_len = ( ( sizeof ( text ) - 1 /* NUL */ ) / 2 /* "xx" */ ); if ( len > max_len ) len = max_len; - base16_encode ( start, len, text ); + base16_encode ( start, len, text, sizeof ( text ) ); return text; } diff --git a/roms/ipxe/src/interface/efi/efi_file.c b/roms/ipxe/src/interface/efi/efi_file.c index 2ef3c5734..3715b70bf 100644 --- a/roms/ipxe/src/interface/efi/efi_file.c +++ b/roms/ipxe/src/interface/efi/efi_file.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/interface/efi/efi_guid.c b/roms/ipxe/src/interface/efi/efi_guid.c index 52ba58ae4..ab1c91e9f 100644 --- a/roms/ipxe/src/interface/efi/efi_guid.c +++ b/roms/ipxe/src/interface/efi/efi_guid.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/efi/efi.h> #include <ipxe/efi/Protocol/Arp.h> diff --git a/roms/ipxe/src/interface/efi/efi_hii.c b/roms/ipxe/src/interface/efi/efi_hii.c index 834060b54..0ea970e67 100644 --- a/roms/ipxe/src/interface/efi/efi_hii.c +++ b/roms/ipxe/src/interface/efi/efi_hii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stddef.h> diff --git a/roms/ipxe/src/interface/efi/efi_pci.c b/roms/ipxe/src/interface/efi/efi_pci.c index 86c781d82..97ea72bb9 100644 --- a/roms/ipxe/src/interface/efi/efi_pci.c +++ b/roms/ipxe/src/interface/efi/efi_pci.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <errno.h> diff --git a/roms/ipxe/src/interface/efi/efi_reboot.c b/roms/ipxe/src/interface/efi/efi_reboot.c index 96638c48e..35919221e 100644 --- a/roms/ipxe/src/interface/efi/efi_reboot.c +++ b/roms/ipxe/src/interface/efi/efi_reboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/interface/efi/efi_snp.c b/roms/ipxe/src/interface/efi/efi_snp.c index 67fba342e..3dfcc5e16 100644 --- a/roms/ipxe/src/interface/efi/efi_snp.c +++ b/roms/ipxe/src/interface/efi/efi_snp.c @@ -32,8 +32,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi_driver.h> #include <ipxe/efi/efi_strings.h> #include <ipxe/efi/efi_utils.h> +#include <ipxe/efi/efi_watchdog.h> #include <ipxe/efi/efi_snp.h> #include <usr/autoboot.h> +#include <config/general.h> /** List of SNP devices */ static LIST_HEAD ( efi_snp_devices ); @@ -41,6 +43,39 @@ static LIST_HEAD ( efi_snp_devices ); /** Network devices are currently claimed for use by iPXE */ static int efi_snp_claimed; +/* Downgrade user experience if configured to do so + * + * The default UEFI user experience for network boot is somewhat + * excremental: only TFTP is available as a download protocol, and if + * anything goes wrong the user will be shown just a dot on an + * otherwise blank screen. (Some programmer was clearly determined to + * win a bet that they could outshine Apple at producing uninformative + * error messages.) + * + * For comparison, the default iPXE user experience provides the + * option to use protocols designed more recently than 1980 (such as + * HTTP and iSCSI), and if anything goes wrong the the user will be + * shown one of over 1200 different error messages, complete with a + * link to a wiki page describing that specific error. + * + * We default to upgrading the user experience to match that available + * in a "legacy" BIOS environment, by installing our own instance of + * EFI_LOAD_FILE_PROTOCOL. + * + * Note that unfortunately we can't sensibly provide the choice of + * both options to the user in the same build, because the UEFI boot + * menu ignores the multitude of ways in which a network device handle + * can be described and opaquely labels both menu entries as just "EFI + * Network". + */ +#ifdef EFI_DOWNGRADE_UX +static EFI_GUID dummy_load_file_protocol_guid = { + 0x6f6c7323, 0x2077, 0x7523, + { 0x6e, 0x68, 0x65, 0x6c, 0x70, 0x66, 0x75, 0x6c } +}; +#define efi_load_file_protocol_guid dummy_load_file_protocol_guid +#endif + /** * Set EFI SNP mode state * @@ -98,28 +133,43 @@ static void efi_snp_set_mode ( struct efi_snp_device *snpdev ) { } /** + * Flush transmit ring and receive queue + * + * @v snpdev SNP device + */ +static void efi_snp_flush ( struct efi_snp_device *snpdev ) { + struct io_buffer *iobuf; + struct io_buffer *tmp; + + /* Reset transmit completion ring */ + snpdev->tx_prod = 0; + snpdev->tx_cons = 0; + + /* Discard any queued receive buffers */ + list_for_each_entry_safe ( iobuf, tmp, &snpdev->rx, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } +} + +/** * Poll net device and count received packets * * @v snpdev SNP device */ static void efi_snp_poll ( struct efi_snp_device *snpdev ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct io_buffer *iobuf; - unsigned int before = 0; - unsigned int after = 0; - unsigned int arrived; - /* We have to report packet arrivals, and this is the easiest - * way to fake it. - */ - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - before++; + /* Poll network device */ netdev_poll ( snpdev->netdev ); - list_for_each_entry ( iobuf, &snpdev->netdev->rx_queue, list ) - after++; - arrived = ( after - before ); - snpdev->rx_count_interrupts += arrived; - snpdev->rx_count_events += arrived; + /* Retrieve any received packets */ + while ( ( iobuf = netdev_rx_dequeue ( snpdev->netdev ) ) ) { + list_add_tail ( &iobuf->list, &snpdev->rx ); + snpdev->interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; + bs->SignalEvent ( &snpdev->snp.WaitForPacket ); + } } /** @@ -221,6 +271,7 @@ efi_snp_reset ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN ext_verify ) { netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); + efi_snp_flush ( snpdev ); if ( ( rc = netdev_open ( snpdev->netdev ) ) != 0 ) { DBGC ( snpdev, "SNPDEV %p could not reopen %s: %s\n", @@ -251,6 +302,7 @@ efi_snp_shutdown ( EFI_SIMPLE_NETWORK_PROTOCOL *snp ) { netdev_close ( snpdev->netdev ); efi_snp_set_state ( snpdev ); + efi_snp_flush ( snpdev ); return 0; } @@ -446,20 +498,22 @@ efi_snp_nvdata ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, BOOLEAN read, * * @v snp SNP interface * @v interrupts Interrupt status, or NULL - * @v txbufs Recycled transmit buffer address, or NULL + * @v txbuf Recycled transmit buffer address, or NULL * @ret efirc EFI status code */ static EFI_STATUS EFIAPI efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, - UINT32 *interrupts, VOID **txbufs ) { + UINT32 *interrupts, VOID **txbuf ) { struct efi_snp_device *snpdev = container_of ( snp, struct efi_snp_device, snp ); DBGC2 ( snpdev, "SNPDEV %p GET_STATUS", snpdev ); /* Fail if net device is currently claimed for use by iPXE */ - if ( efi_snp_claimed ) + if ( efi_snp_claimed ) { + DBGC2 ( snpdev, "\n" ); return EFI_NOT_READY; + } /* Poll the network device */ efi_snp_poll ( snpdev ); @@ -468,47 +522,19 @@ efi_snp_get_status ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, * to detect TX completions. */ if ( interrupts ) { - *interrupts = 0; - /* Report TX completions once queue is empty; this - * avoids having to add hooks in the net device layer. - */ - if ( snpdev->tx_count_interrupts && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; - snpdev->tx_count_interrupts--; - } - /* Report RX */ - if ( snpdev->rx_count_interrupts ) { - *interrupts |= EFI_SIMPLE_NETWORK_RECEIVE_INTERRUPT; - snpdev->rx_count_interrupts--; - } + *interrupts = snpdev->interrupts; DBGC2 ( snpdev, " INTS:%02x", *interrupts ); + snpdev->interrupts = 0; } - /* TX completions. It would be possible to design a more - * idiotic scheme for this, but it would be a challenge. - * According to the UEFI header file, txbufs will be filled in - * with a list of "recycled transmit buffers" (i.e. completed - * TX buffers). Observant readers may care to note that - * *txbufs is a void pointer. Precisely how a list of - * completed transmit buffers is meant to be represented as an - * array of voids is left as an exercise for the reader. - * - * The only users of this interface (MnpDxe/MnpIo.c and - * PxeBcDxe/Bc.c within the EFI dev kit) both just poll until - * seeing a non-NULL result return in txbufs. This is valid - * provided that they do not ever attempt to transmit more - * than one packet concurrently (and that TX never times out). - */ - if ( txbufs ) { - if ( snpdev->tx_count_txbufs && - list_empty ( &snpdev->netdev->tx_queue ) ) { - *txbufs = "Which idiot designed this API?"; - snpdev->tx_count_txbufs--; + /* TX completions */ + if ( txbuf ) { + if ( snpdev->tx_prod != snpdev->tx_cons ) { + *txbuf = snpdev->tx[snpdev->tx_cons++ % EFI_SNP_NUM_TX]; } else { - *txbufs = NULL; + *txbuf = NULL; } - DBGC2 ( snpdev, " TX:%s", ( *txbufs ? "some" : "none" ) ); + DBGC2 ( snpdev, " TX:%p", *txbuf ); } DBGC2 ( snpdev, "\n" ); @@ -537,6 +563,7 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, struct ll_protocol *ll_protocol = snpdev->netdev->ll_protocol; struct io_buffer *iobuf; size_t payload_len; + unsigned int tx_fill; int rc; DBGC2 ( snpdev, "SNPDEV %p TRANSMIT %p+%lx", snpdev, data, @@ -624,12 +651,27 @@ efi_snp_transmit ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, goto err_tx; } - /* Record transmission as outstanding */ - snpdev->tx_count_interrupts++; - snpdev->tx_count_txbufs++; + /* Record in transmit completion ring. If we run out of + * space, report the failure even though we have already + * transmitted the packet. + * + * This allows us to report completions only for packets for + * which we had reported successfully initiating transmission, + * while continuing to support clients that never poll for + * transmit completions. + */ + tx_fill = ( snpdev->tx_prod - snpdev->tx_cons ); + if ( tx_fill >= EFI_SNP_NUM_TX ) { + DBGC ( snpdev, "SNPDEV %p TX completion ring full\n", snpdev ); + rc = -ENOBUFS; + goto err_ring_full; + } + snpdev->tx[ snpdev->tx_prod++ % EFI_SNP_NUM_TX ] = data; + snpdev->interrupts |= EFI_SIMPLE_NETWORK_TRANSMIT_INTERRUPT; return 0; + err_ring_full: err_tx: err_ll_push: free_iob ( iobuf ); @@ -676,12 +718,13 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, efi_snp_poll ( snpdev ); /* Dequeue a packet, if one is available */ - iobuf = netdev_rx_dequeue ( snpdev->netdev ); + iobuf = list_first_entry ( &snpdev->rx, struct io_buffer, list ); if ( ! iobuf ) { DBGC2 ( snpdev, "\n" ); rc = -EAGAIN; goto out_no_packet; } + list_del ( &iobuf->list ); DBGC2 ( snpdev, "+%zx\n", iob_len ( iobuf ) ); /* Return packet to caller */ @@ -721,9 +764,8 @@ efi_snp_receive ( EFI_SIMPLE_NETWORK_PROTOCOL *snp, * @v event Event * @v context Event context */ -static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, +static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event __unused, VOID *context ) { - EFI_BOOT_SERVICES *bs = efi_systab->BootServices; struct efi_snp_device *snpdev = context; DBGCP ( snpdev, "SNPDEV %p WAIT_FOR_PACKET\n", snpdev ); @@ -738,14 +780,6 @@ static VOID EFIAPI efi_snp_wait_for_packet ( EFI_EVENT event, /* Poll the network device */ efi_snp_poll ( snpdev ); - - /* Fire event if packets have been received */ - if ( snpdev->rx_count_events != 0 ) { - DBGC2 ( snpdev, "SNPDEV %p firing WaitForPacket event\n", - snpdev ); - bs->SignalEvent ( event ); - snpdev->rx_count_events--; - } } /** SNP interface */ @@ -837,6 +871,7 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file, struct efi_snp_device *snpdev = container_of ( load_file, struct efi_snp_device, load_file ); struct net_device *netdev = snpdev->netdev; + int rc; /* Fail unless this is a boot attempt */ if ( ! booting ) { @@ -848,14 +883,17 @@ efi_snp_load_file ( EFI_LOAD_FILE_PROTOCOL *load_file, /* Claim network devices for use by iPXE */ efi_snp_claim(); + /* Start watchdog holdoff timer */ + efi_watchdog_start(); + /* Boot from network device */ - ipxe ( netdev ); + if ( ( rc = ipxe ( netdev ) ) != 0 ) + goto err_ipxe; - /* Release network devices for use via SNP */ + err_ipxe: + efi_watchdog_stop(); efi_snp_release(); - - /* Assume boot process was aborted */ - return EFI_ABORTED; + return EFIRC ( rc ); } /** Load file protocol */ @@ -922,6 +960,7 @@ static int efi_snp_probe ( struct net_device *netdev ) { } snpdev->netdev = netdev_get ( netdev ); snpdev->efidev = efidev; + INIT_LIST_HEAD ( &snpdev->rx ); /* Sanity check */ if ( netdev->ll_protocol->ll_addr_len > sizeof ( EFI_MAC_ADDRESS ) ) { diff --git a/roms/ipxe/src/interface/efi/efi_snp_hii.c b/roms/ipxe/src/interface/efi/efi_snp_hii.c index c49c76a32..720402bdb 100644 --- a/roms/ipxe/src/interface/efi/efi_snp_hii.c +++ b/roms/ipxe/src/interface/efi/efi_snp_hii.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -59,6 +63,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/efi/efi_hii.h> #include <ipxe/efi/efi_snp.h> #include <ipxe/efi/efi_strings.h> +#include <config/branding.h> /** EFI platform setup formset GUID */ static EFI_GUID efi_hii_platform_setup_formset_guid @@ -136,7 +141,7 @@ static void efi_snp_hii_questions ( struct efi_snp_device *snpdev, previous = setting; name_id = efi_ifr_string ( ifr, "%s", setting->name ); prompt_id = efi_ifr_string ( ifr, "%s", setting->description ); - help_id = efi_ifr_string ( ifr, "http://ipxe.org/cfg/%s", + help_id = efi_ifr_string ( ifr, PRODUCT_SETTING_URI, setting->name ); question_id = setting->tag; efi_ifr_string_op ( ifr, prompt_id, help_id, diff --git a/roms/ipxe/src/interface/efi/efi_strings.c b/roms/ipxe/src/interface/efi/efi_strings.c index 751589b46..aa3afc64f 100644 --- a/roms/ipxe/src/interface/efi/efi_strings.c +++ b/roms/ipxe/src/interface/efi/efi_strings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdarg.h> diff --git a/roms/ipxe/src/interface/efi/efi_time.c b/roms/ipxe/src/interface/efi/efi_time.c new file mode 100644 index 000000000..983a0ef5c --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_time.c @@ -0,0 +1,75 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <string.h> +#include <errno.h> +#include <time.h> +#include <ipxe/time.h> +#include <ipxe/efi/efi.h> + +/** @file + * + * EFI time source + * + */ + +/** + * Get current time in seconds + * + * @ret time Time, in seconds + */ +static time_t efi_get_time ( void ) { + EFI_RUNTIME_SERVICES *rs = efi_systab->RuntimeServices; + EFI_TIME time; + struct tm tm; + EFI_STATUS efirc; + int rc; + + /* Get current time and date */ + if ( ( efirc = rs->GetTime ( &time, NULL ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( rs, "EFITIME could not get system time: %s\n", + strerror ( rc ) ); + /* Nothing meaningful we can return */ + return 0; + } + + /* Construct broken-down time */ + memset ( &tm, 0, sizeof ( tm ) ); + tm.tm_sec = time.Second; + tm.tm_min = time.Minute; + tm.tm_hour = time.Hour; + tm.tm_mday = time.Day; + tm.tm_mon = ( time.Month - 1 ); + tm.tm_year = ( time.Year - 1900 ); + DBGC ( rs, "EFITIME is %04d-%02d-%02d %02d:%02d:%02d\n", + ( tm.tm_year + 1900 ), ( tm.tm_mon + 1 ), + tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ); + + /* Convert to seconds since the Epoch */ + return mktime ( &tm ); +} + +PROVIDE_TIME ( efi, time_now, efi_get_time ); diff --git a/roms/ipxe/src/interface/efi/efi_timer.c b/roms/ipxe/src/interface/efi/efi_timer.c index 7a1ff7869..81620c92c 100644 --- a/roms/ipxe/src/interface/efi/efi_timer.c +++ b/roms/ipxe/src/interface/efi/efi_timer.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/interface/efi/efi_uaccess.c b/roms/ipxe/src/interface/efi/efi_uaccess.c index 8b429b9ee..e058be66b 100644 --- a/roms/ipxe/src/interface/efi/efi_uaccess.c +++ b/roms/ipxe/src/interface/efi/efi_uaccess.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/uaccess.h> #include <ipxe/efi/efi.h> diff --git a/roms/ipxe/src/interface/efi/efi_umalloc.c b/roms/ipxe/src/interface/efi/efi_umalloc.c index 356efaa6f..e3f1dacc2 100644 --- a/roms/ipxe/src/interface/efi/efi_umalloc.c +++ b/roms/ipxe/src/interface/efi/efi_umalloc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/interface/efi/efi_watchdog.c b/roms/ipxe/src/interface/efi/efi_watchdog.c new file mode 100644 index 000000000..7061f81d8 --- /dev/null +++ b/roms/ipxe/src/interface/efi/efi_watchdog.c @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * EFI watchdog holdoff timer + * + */ + +#include <errno.h> +#include <string.h> +#include <ipxe/retry.h> +#include <ipxe/timer.h> +#include <ipxe/efi/efi.h> +#include <ipxe/efi/efi_watchdog.h> + +/** Watchdog holdoff interval (in seconds) */ +#define WATCHDOG_HOLDOFF_SECS 10 + +/** Watchdog timeout (in seconds) */ +#define WATCHDOG_TIMEOUT_SECS ( 5 * 60 ) + +/** Watchdog code (to be logged on watchdog timeout) */ +#define WATCHDOG_CODE 0x6950584544454144ULL + +/** Watchdog data (to be logged on watchdog timeout) */ +#define WATCHDOG_DATA L"iPXE"; + +/** + * Hold off watchdog timer + * + * @v retry Retry timer + * @v over Failure indicator + */ +static void efi_watchdog_expired ( struct retry_timer *timer, + int over __unused ) { + EFI_BOOT_SERVICES *bs = efi_systab->BootServices; + static CHAR16 data[] = WATCHDOG_DATA; + EFI_STATUS efirc; + int rc; + + DBGC2 ( timer, "EFI holding off watchdog timer\n" ); + + /* Restart this holdoff timer */ + start_timer_fixed ( timer, ( WATCHDOG_HOLDOFF_SECS * TICKS_PER_SEC ) ); + + /* Reset watchdog timer */ + if ( ( efirc = bs->SetWatchdogTimer ( WATCHDOG_TIMEOUT_SECS, + WATCHDOG_CODE, sizeof ( data ), + data ) ) != 0 ) { + rc = -EEFI ( efirc ); + DBGC ( timer, "EFI could not set watchdog timer: %s\n", + strerror ( rc ) ); + return; + } +} + +/** Watchdog holdoff timer */ +struct retry_timer efi_watchdog = TIMER_INIT ( efi_watchdog_expired ); diff --git a/roms/ipxe/src/interface/efi/efi_wrap.c b/roms/ipxe/src/interface/efi/efi_wrap.c index ff46b76ed..2ea184e97 100644 --- a/roms/ipxe/src/interface/efi/efi_wrap.c +++ b/roms/ipxe/src/interface/efi/efi_wrap.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/interface/hyperv/vmbus.c b/roms/ipxe/src/interface/hyperv/vmbus.c new file mode 100644 index 000000000..795929eae --- /dev/null +++ b/roms/ipxe/src/interface/hyperv/vmbus.c @@ -0,0 +1,1333 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Hyper-V virtual machine bus + * + */ + +#include <stdint.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <byteswap.h> +#include <ipxe/nap.h> +#include <ipxe/malloc.h> +#include <ipxe/iobuf.h> +#include <ipxe/hyperv.h> +#include <ipxe/vmbus.h> + +/** VMBus initial GPADL ID + * + * This is an opaque value with no meaning. The Linux kernel uses + * 0xe1e10. + */ +#define VMBUS_GPADL_MAGIC 0x18ae0000 + +/** + * Post message + * + * @v hv Hyper-V hypervisor + * @v header Message header + * @v len Length of message (including header) + * @ret rc Return status code + */ +static int vmbus_post_message ( struct hv_hypervisor *hv, + const struct vmbus_message_header *header, + size_t len ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Post message */ + if ( ( rc = hv_post_message ( hv, VMBUS_MESSAGE_ID, VMBUS_MESSAGE_TYPE, + header, len ) ) != 0 ) { + DBGC ( vmbus, "VMBUS %p could not post message: %s\n", + vmbus, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Post empty message + * + * @v hv Hyper-V hypervisor + * @v type Message type + * @ret rc Return status code + */ +static int vmbus_post_empty_message ( struct hv_hypervisor *hv, + unsigned int type ) { + struct vmbus_message_header header = { .type = cpu_to_le32 ( type ) }; + + return vmbus_post_message ( hv, &header, sizeof ( header ) ); +} + +/** + * Wait for received message + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_wait_for_message ( struct hv_hypervisor *hv ) { + struct vmbus *vmbus = hv->vmbus; + int rc; + + /* Wait for message */ + if ( ( rc = hv_wait_for_message ( hv, VMBUS_MESSAGE_SINT ) ) != 0 ) { + DBGC ( vmbus, "VMBUS %p failed waiting for message: %s\n", + vmbus, strerror ( rc ) ); + return rc; + } + + /* Sanity check */ + if ( hv->message->received.type != cpu_to_le32 ( VMBUS_MESSAGE_TYPE ) ){ + DBGC ( vmbus, "VMBUS %p invalid message type %d\n", + vmbus, le32_to_cpu ( hv->message->received.type ) ); + return -EINVAL; + } + + return 0; +} + +/** + * Initiate contact + * + * @v hv Hyper-V hypervisor + * @v raw VMBus protocol (raw) version + * @ret rc Return status code + */ +static int vmbus_initiate_contact ( struct hv_hypervisor *hv, + unsigned int raw ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_version_response *version = &vmbus->message->version; + struct vmbus_initiate_contact initiate; + int rc; + + /* Construct message */ + memset ( &initiate, 0, sizeof ( initiate ) ); + initiate.header.type = cpu_to_le32 ( VMBUS_INITIATE_CONTACT ); + initiate.version.raw = cpu_to_le32 ( raw ); + initiate.intr = virt_to_phys ( vmbus->intr ); + initiate.monitor_in = virt_to_phys ( vmbus->monitor_in ); + initiate.monitor_out = virt_to_phys ( vmbus->monitor_out ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &initiate.header, + sizeof ( initiate ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( version->header.type != cpu_to_le32 ( VMBUS_VERSION_RESPONSE ) ) { + DBGC ( vmbus, "VMBUS %p unexpected version response type %d\n", + vmbus, le32_to_cpu ( version->header.type ) ); + return -EPROTO; + } + if ( ! version->supported ) { + DBGC ( vmbus, "VMBUS %p requested version not supported\n", + vmbus ); + return -ENOTSUP; + } + if ( version->version.raw != cpu_to_le32 ( raw ) ) { + DBGC ( vmbus, "VMBUS %p unexpected version %d.%d\n", + vmbus, le16_to_cpu ( version->version.major ), + le16_to_cpu ( version->version.minor ) ); + return -EPROTO; + } + + DBGC ( vmbus, "VMBUS %p initiated contact using version %d.%d\n", + vmbus, le16_to_cpu ( version->version.major ), + le16_to_cpu ( version->version.minor ) ); + return 0; +} + +/** + * Terminate contact + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_unload ( struct hv_hypervisor *hv ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_UNLOAD ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( header->type != cpu_to_le32 ( VMBUS_UNLOAD_RESPONSE ) ) { + DBGC ( vmbus, "VMBUS %p unexpected unload response type %d\n", + vmbus, le32_to_cpu ( header->type ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Negotiate protocol version + * + * @v hv Hyper-V hypervisor + * @ret rc Return status code + */ +static int vmbus_negotiate_version ( struct hv_hypervisor *hv ) { + int rc; + + /* We require the ability to disconnect from and reconnect to + * VMBus; if we don't have this then there is no (viable) way + * for a loaded operating system to continue to use any VMBus + * devices. (There is also a small but non-zero risk that the + * host will continue to write to our interrupt and monitor + * pages, since the VMBUS_UNLOAD message in earlier versions + * is essentially a no-op.) + * + * This requires us to ensure that the host supports protocol + * version 3.0 (VMBUS_VERSION_WIN8_1). However, we can't + * actually _use_ protocol version 3.0, since doing so causes + * an iSCSI-booted Windows Server 2012 R2 VM to crash due to a + * NULL pointer dereference in vmbus.sys. + * + * To work around this problem, we first ensure that we can + * connect using protocol v3.0, then disconnect and reconnect + * using the oldest known protocol. + */ + + /* Initiate contact to check for required protocol support */ + if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WIN8_1 ) ) != 0 ) + return rc; + + /* Terminate contact */ + if ( ( rc = vmbus_unload ( hv ) ) != 0 ) + return rc; + + /* Reinitiate contact using the oldest known protocol version */ + if ( ( rc = vmbus_initiate_contact ( hv, VMBUS_VERSION_WS2008 ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Establish GPA descriptor list + * + * @v vmdev VMBus device + * @v data Data buffer + * @v len Length of data buffer + * @ret gpadl GPADL ID, or negative error + */ +int vmbus_establish_gpadl ( struct vmbus_device *vmdev, userptr_t data, + size_t len ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + physaddr_t addr = user_to_phys ( data, 0 ); + unsigned int pfn_count = hv_pfn_count ( addr, len ); + struct { + struct vmbus_gpadl_header gpadlhdr; + struct vmbus_gpa_range range; + uint64_t pfn[pfn_count]; + } __attribute__ (( packed )) gpadlhdr; + const struct vmbus_gpadl_created *created = &vmbus->message->created; + static unsigned int gpadl = VMBUS_GPADL_MAGIC; + unsigned int i; + int rc; + + /* Allocate GPADL ID */ + gpadl++; + + /* Construct message */ + memset ( &gpadlhdr, 0, sizeof ( gpadlhdr ) ); + gpadlhdr.gpadlhdr.header.type = cpu_to_le32 ( VMBUS_GPADL_HEADER ); + gpadlhdr.gpadlhdr.channel = cpu_to_le32 ( vmdev->channel ); + gpadlhdr.gpadlhdr.gpadl = cpu_to_le32 ( gpadl ); + gpadlhdr.gpadlhdr.range_len = + cpu_to_le16 ( ( sizeof ( gpadlhdr.range ) + + sizeof ( gpadlhdr.pfn ) ) ); + gpadlhdr.gpadlhdr.range_count = cpu_to_le16 ( 1 ); + gpadlhdr.range.len = cpu_to_le32 ( len ); + gpadlhdr.range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) ); + for ( i = 0 ; i < pfn_count ; i++ ) + gpadlhdr.pfn[i] = ( ( addr / PAGE_SIZE ) + i ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &gpadlhdr.gpadlhdr.header, + sizeof ( gpadlhdr ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( created->header.type != cpu_to_le32 ( VMBUS_GPADL_CREATED ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", + vmdev->dev.name, le32_to_cpu ( created->header.type ) ); + return -EPROTO; + } + if ( created->channel != cpu_to_le32 ( vmdev->channel ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL channel %d\n", + vmdev->dev.name, le32_to_cpu ( created->channel ) ); + return -EPROTO; + } + if ( created->gpadl != cpu_to_le32 ( gpadl ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( created->gpadl ) ); + return -EPROTO; + } + if ( created->status != 0 ) { + DBGC ( vmdev, "VMBUS %s GPADL creation failed: %#08x\n", + vmdev->dev.name, le32_to_cpu ( created->status ) ); + return -EPROTO; + } + + DBGC ( vmdev, "VMBUS %s GPADL %#08x is [%08lx,%08lx)\n", + vmdev->dev.name, gpadl, addr, ( addr + len ) ); + return gpadl; +} + +/** + * Tear down GPA descriptor list + * + * @v vmdev VMBus device + * @v gpadl GPADL ID + * @ret rc Return status code + */ +int vmbus_gpadl_teardown ( struct vmbus_device *vmdev, unsigned int gpadl ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct vmbus_gpadl_teardown teardown; + const struct vmbus_gpadl_torndown *torndown = &vmbus->message->torndown; + int rc; + + /* Construct message */ + memset ( &teardown, 0, sizeof ( teardown ) ); + teardown.header.type = cpu_to_le32 ( VMBUS_GPADL_TEARDOWN ); + teardown.channel = cpu_to_le32 ( vmdev->channel ); + teardown.gpadl = cpu_to_le32 ( gpadl ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &teardown.header, + sizeof ( teardown ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( torndown->header.type != cpu_to_le32 ( VMBUS_GPADL_TORNDOWN ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL response type %d\n", + vmdev->dev.name, le32_to_cpu ( torndown->header.type ) ); + return -EPROTO; + } + if ( torndown->gpadl != cpu_to_le32 ( gpadl ) ) { + DBGC ( vmdev, "VMBUS %s unexpected GPADL ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( torndown->gpadl ) ); + return -EPROTO; + } + + return 0; +} + +/** + * Open VMBus channel + * + * @v vmdev VMBus device + * @v op Channel operations + * @v out_len Outbound ring buffer length + * @v in_len Inbound ring buffer length + * @v mtu Maximum expected data packet length (including headers) + * @ret rc Return status code + * + * Both outbound and inbound ring buffer lengths must be a power of + * two and a multiple of PAGE_SIZE. The requirement to be a power of + * two is a policy decision taken to simplify the ring buffer indexing + * logic. + */ +int vmbus_open ( struct vmbus_device *vmdev, + struct vmbus_channel_operations *op, + size_t out_len, size_t in_len, size_t mtu ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct vmbus_open_channel open; + const struct vmbus_open_channel_result *opened = + &vmbus->message->opened; + size_t len; + void *ring; + void *packet; + int gpadl; + uint32_t open_id; + int rc; + + /* Sanity checks */ + assert ( ( out_len % PAGE_SIZE ) == 0 ); + assert ( ( out_len & ( out_len - 1 ) ) == 0 ); + assert ( ( in_len % PAGE_SIZE ) == 0 ); + assert ( ( in_len & ( in_len - 1 ) ) == 0 ); + assert ( mtu >= ( sizeof ( struct vmbus_packet_header ) + + sizeof ( struct vmbus_packet_footer ) ) ); + + /* Allocate packet buffer */ + packet = malloc ( mtu ); + if ( ! packet ) { + rc = -ENOMEM; + goto err_alloc_packet; + } + + /* Allocate ring buffer */ + len = ( sizeof ( *vmdev->out ) + out_len + + sizeof ( *vmdev->in ) + in_len ); + assert ( ( len % PAGE_SIZE ) == 0 ); + ring = malloc_dma ( len, PAGE_SIZE ); + if ( ! ring ) { + rc = -ENOMEM; + goto err_alloc_ring; + } + memset ( ring, 0, len ); + + /* Establish GPADL for ring buffer */ + gpadl = vmbus_establish_gpadl ( vmdev, virt_to_user ( ring ), len ); + if ( gpadl < 0 ) { + rc = gpadl; + goto err_establish; + } + + /* Construct message */ + memset ( &open, 0, sizeof ( open ) ); + open.header.type = cpu_to_le32 ( VMBUS_OPEN_CHANNEL ); + open.channel = cpu_to_le32 ( vmdev->channel ); + open_id = random(); + open.id = open_id; /* Opaque random value: endianness irrelevant */ + open.gpadl = cpu_to_le32 ( gpadl ); + open.out_pages = ( ( sizeof ( *vmdev->out ) / PAGE_SIZE ) + + ( out_len / PAGE_SIZE ) ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &open.header, + sizeof ( open ) ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + return rc; + + /* Check response */ + if ( opened->header.type != cpu_to_le32 ( VMBUS_OPEN_CHANNEL_RESULT ) ){ + DBGC ( vmdev, "VMBUS %s unexpected open response type %d\n", + vmdev->dev.name, le32_to_cpu ( opened->header.type ) ); + return -EPROTO; + } + if ( opened->channel != cpu_to_le32 ( vmdev->channel ) ) { + DBGC ( vmdev, "VMBUS %s unexpected opened channel %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->channel ) ); + return -EPROTO; + } + if ( opened->id != open_id /* Non-endian */ ) { + DBGC ( vmdev, "VMBUS %s unexpected open ID %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->id ) ); + return -EPROTO; + } + if ( opened->status != 0 ) { + DBGC ( vmdev, "VMBUS %s open failed: %#08x\n", + vmdev->dev.name, le32_to_cpu ( opened->status ) ); + return -EPROTO; + } + + /* Store channel parameters */ + vmdev->out_len = out_len; + vmdev->in_len = in_len; + vmdev->out = ring; + vmdev->in = ( ring + sizeof ( *vmdev->out ) + out_len ); + vmdev->gpadl = gpadl; + vmdev->op = op; + vmdev->mtu = mtu; + vmdev->packet = packet; + + DBGC ( vmdev, "VMBUS %s channel GPADL %#08x ring " + "[%#08lx,%#08lx,%#08lx)\n", vmdev->dev.name, vmdev->gpadl, + virt_to_phys ( vmdev->out ), virt_to_phys ( vmdev->in ), + ( virt_to_phys ( vmdev->out ) + len ) ); + return 0; + + vmbus_gpadl_teardown ( vmdev, vmdev->gpadl ); + err_establish: + free_dma ( ring, len ); + err_alloc_ring: + free ( packet ); + err_alloc_packet: + return rc; +} + +/** + * Close VMBus channel + * + * @v vmdev VMBus device + */ +void vmbus_close ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus_close_channel close; + size_t len; + int rc; + + /* Construct message */ + memset ( &close, 0, sizeof ( close ) ); + close.header.type = cpu_to_le32 ( VMBUS_CLOSE_CHANNEL ); + close.channel = cpu_to_le32 ( vmdev->channel ); + + /* Post message */ + if ( ( rc = vmbus_post_message ( hv, &close.header, + sizeof ( close ) ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s failed to close: %s\n", + vmdev->dev.name, strerror ( rc ) ); + /* Continue to attempt to tear down GPADL, so that our + * memory is no longer accessible by the remote VM. + */ + } + + /* Tear down GPADL */ + if ( ( rc = vmbus_gpadl_teardown ( vmdev, + vmdev->gpadl ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s failed to tear down channel GPADL: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + /* We can't prevent the remote VM from continuing to + * access this memory, so leak it. + */ + return; + } + + /* Free ring buffer */ + len = ( sizeof ( *vmdev->out ) + vmdev->out_len + + sizeof ( *vmdev->in ) + vmdev->in_len ); + free_dma ( vmdev->out, len ); + vmdev->out = NULL; + vmdev->in = NULL; + + /* Free packet buffer */ + free ( vmdev->packet ); + vmdev->packet = NULL; + + DBGC ( vmdev, "VMBUS %s closed\n", vmdev->dev.name ); +} + +/** + * Signal channel via monitor page + * + * @v vmdev VMBus device + */ +static void vmbus_signal_monitor ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + struct hv_monitor_trigger *trigger; + unsigned int group; + unsigned int bit; + + /* Set bit in monitor trigger group */ + group = ( vmdev->monitor / ( 8 * sizeof ( trigger->pending ) )); + bit = ( vmdev->monitor % ( 8 * sizeof ( trigger->pending ) ) ); + trigger = &vmbus->monitor_out->trigger[group]; + hv_set_bit ( trigger, bit ); +} + +/** + * Signal channel via hypervisor event + * + * @v vmdev VMBus device + */ +static void vmbus_signal_event ( struct vmbus_device *vmdev ) { + struct hv_hypervisor *hv = vmdev->hv; + int rc; + + /* Signal hypervisor event */ + if ( ( rc = hv_signal_event ( hv, VMBUS_EVENT_ID, 0 ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not signal event: %s\n", + vmdev->dev.name, strerror ( rc ) ); + return; + } +} + +/** + * Fill outbound ring buffer + * + * @v vmdev VMBus device + * @v prod Producer index + * @v data Data + * @v len Length + * @ret prod New producer index + * + * The caller must ensure that there is sufficient space in the ring + * buffer. + */ +static size_t vmbus_produce ( struct vmbus_device *vmdev, size_t prod, + const void *data, size_t len ) { + size_t first; + size_t second; + + /* Determine fragment lengths */ + first = ( vmdev->out_len - prod ); + if ( first > len ) + first = len; + second = ( len - first ); + + /* Copy fragment(s) */ + memcpy ( &vmdev->out->data[prod], data, first ); + if ( second ) + memcpy ( &vmdev->out->data[0], ( data + first ), second ); + + return ( ( prod + len ) & ( vmdev->out_len - 1 ) ); +} + +/** + * Consume inbound ring buffer + * + * @v vmdev VMBus device + * @v cons Consumer index + * @v data Data buffer, or NULL + * @v len Length to consume + * @ret cons New consumer index + */ +static size_t vmbus_consume ( struct vmbus_device *vmdev, size_t cons, + void *data, size_t len ) { + size_t first; + size_t second; + + /* Determine fragment lengths */ + first = ( vmdev->in_len - cons ); + if ( first > len ) + first = len; + second = ( len - first ); + + /* Copy fragment(s) */ + memcpy ( data, &vmdev->in->data[cons], first ); + if ( second ) + memcpy ( ( data + first ), &vmdev->in->data[0], second ); + + return ( ( cons + len ) & ( vmdev->in_len - 1 ) ); +} + +/** + * Send packet via ring buffer + * + * @v vmdev VMBus device + * @v header Packet header + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send a packet via the outbound ring buffer. All fields in the + * packet header must be filled in, with the exception of the total + * packet length. + */ +static int vmbus_send ( struct vmbus_device *vmdev, + struct vmbus_packet_header *header, + const void *data, size_t len ) { + struct hv_hypervisor *hv = vmdev->hv; + struct vmbus *vmbus = hv->vmbus; + static uint8_t padding[ 8 - 1 ]; + struct vmbus_packet_footer footer; + size_t header_len; + size_t pad_len; + size_t footer_len; + size_t ring_len; + size_t cons; + size_t prod; + size_t old_prod; + size_t fill; + + /* Sanity check */ + assert ( vmdev->out != NULL ); + + /* Calculate lengths */ + header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 ); + pad_len = ( ( -len ) & ( 8 - 1 ) ); + footer_len = sizeof ( footer ); + ring_len = ( header_len + len + pad_len + footer_len ); + + /* Check that we have enough room in the outbound ring buffer */ + cons = le32_to_cpu ( vmdev->out->cons ); + prod = le32_to_cpu ( vmdev->out->prod ); + old_prod = prod; + fill = ( ( prod - cons ) & ( vmdev->out_len - 1 ) ); + if ( ( fill + ring_len ) >= vmdev->out_len ) { + DBGC ( vmdev, "VMBUS %s ring buffer full\n", vmdev->dev.name ); + return -ENOBUFS; + } + + /* Complete header */ + header->qlen = cpu_to_le16 ( ( ring_len - footer_len ) / 8 ); + + /* Construct footer */ + footer.reserved = 0; + footer.prod = vmdev->out->prod; + + /* Copy packet to buffer */ + DBGC2 ( vmdev, "VMBUS %s sending:\n", vmdev->dev.name ); + DBGC2_HDA ( vmdev, prod, header, header_len ); + prod = vmbus_produce ( vmdev, prod, header, header_len ); + DBGC2_HDA ( vmdev, prod, data, len ); + prod = vmbus_produce ( vmdev, prod, data, len ); + prod = vmbus_produce ( vmdev, prod, padding, pad_len ); + DBGC2_HDA ( vmdev, prod, &footer, sizeof ( footer ) ); + prod = vmbus_produce ( vmdev, prod, &footer, sizeof ( footer ) ); + assert ( ( ( prod - old_prod ) & ( vmdev->out_len - 1 ) ) == ring_len ); + + /* Update producer index */ + wmb(); + vmdev->out->prod = cpu_to_le32 ( prod ); + + /* Return if we do not need to signal the host. This follows + * the logic of hv_need_to_signal() in the Linux driver. + */ + mb(); + if ( vmdev->out->intr_mask ) + return 0; + rmb(); + cons = le32_to_cpu ( vmdev->out->cons ); + if ( cons != old_prod ) + return 0; + + /* Set channel bit in interrupt page */ + hv_set_bit ( vmbus->intr->out, vmdev->channel ); + + /* Signal the host */ + vmdev->signal ( vmdev ); + + return 0; +} + +/** + * Send control packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID (or zero to not request completion) + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send data using a VMBUS_DATA_INBAND packet. + */ +int vmbus_send_control ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_DATA_INBAND ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = ( xid ? + cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED ) : 0 ); + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, data, len ); +} + +/** + * Send data packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @v iobuf I/O buffer + * @ret rc Return status code + * + * Send data using a VMBUS_DATA_GPA_DIRECT packet. The caller is + * responsible for ensuring that the I/O buffer remains untouched + * until the corresponding completion has been received. + */ +int vmbus_send_data ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len, struct io_buffer *iobuf ) { + physaddr_t addr = virt_to_phys ( iobuf->data ); + unsigned int pfn_count = hv_pfn_count ( addr, iob_len ( iobuf ) ); + struct { + struct vmbus_gpa_direct_header gpa; + struct vmbus_gpa_range range; + uint64_t pfn[pfn_count]; + } __attribute__ (( packed )) *header = vmdev->packet; + unsigned int i; + + /* Sanity check */ + assert ( header != NULL ); + assert ( sizeof ( *header ) <= vmdev->mtu ); + + /* Construct header in packet buffer */ + header->gpa.header.type = cpu_to_le16 ( VMBUS_DATA_GPA_DIRECT ); + header->gpa.header.hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->gpa.header.flags = cpu_to_le16 ( VMBUS_COMPLETION_REQUESTED ); + header->gpa.header.xid = xid; /* Non-endian */ + header->gpa.range_count = 1; + header->range.len = cpu_to_le32 ( iob_len ( iobuf ) ); + header->range.offset = cpu_to_le32 ( addr & ( PAGE_SIZE - 1 ) ); + for ( i = 0 ; i < pfn_count ; i++ ) + header->pfn[i] = ( ( addr / PAGE_SIZE ) + i ); + + return vmbus_send ( vmdev, &header->gpa.header, data, len ); +} + +/** + * Send completion packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @v data Data + * @v len Length of data + * @ret rc Return status code + * + * Send data using a VMBUS_COMPLETION packet. + */ +int vmbus_send_completion ( struct vmbus_device *vmdev, uint64_t xid, + const void *data, size_t len ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_COMPLETION ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = 0; + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, data, len ); +} + +/** + * Send cancellation packet via ring buffer + * + * @v vmdev VMBus device + * @v xid Transaction ID + * @ret rc Return status code + * + * Send data using a VMBUS_CANCELLATION packet. + */ +int vmbus_send_cancellation ( struct vmbus_device *vmdev, uint64_t xid ) { + struct vmbus_packet_header *header = vmdev->packet; + + /* Construct header in packet buffer */ + assert ( header != NULL ); + header->type = cpu_to_le16 ( VMBUS_CANCELLATION ); + header->hdr_qlen = cpu_to_le16 ( sizeof ( *header ) / 8 ); + header->flags = 0; + header->xid = xid; /* Non-endian */ + + return vmbus_send ( vmdev, header, NULL, 0 ); +} + +/** + * Get transfer page set from pageset ID + * + * @v vmdev VMBus device + * @v pageset Page set ID (in protocol byte order) + * @ret pages Page set, or NULL if not found + */ +static struct vmbus_xfer_pages * vmbus_xfer_pages ( struct vmbus_device *vmdev, + uint16_t pageset ) { + struct vmbus_xfer_pages *pages; + + /* Locate page set */ + list_for_each_entry ( pages, &vmdev->pages, list ) { + if ( pages->pageset == pageset ) + return pages; + } + + DBGC ( vmdev, "VMBUS %s unrecognised page set ID %#04x\n", + vmdev->dev.name, le16_to_cpu ( pageset ) ); + return NULL; +} + +/** + * Construct I/O buffer list from transfer pages + * + * @v vmdev VMBus device + * @v header Transfer page header + * @v list I/O buffer list to populate + * @ret rc Return status code + */ +static int vmbus_xfer_page_iobufs ( struct vmbus_device *vmdev, + struct vmbus_packet_header *header, + struct list_head *list ) { + struct vmbus_xfer_page_header *page_header = + container_of ( header, struct vmbus_xfer_page_header, header ); + struct vmbus_xfer_pages *pages; + struct io_buffer *iobuf; + struct io_buffer *tmp; + size_t len; + size_t offset; + unsigned int range_count; + unsigned int i; + int rc; + + /* Sanity check */ + assert ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ); + + /* Locate page set */ + pages = vmbus_xfer_pages ( vmdev, page_header->pageset ); + if ( ! pages ) { + rc = -ENOENT; + goto err_pages; + } + + /* Allocate and populate I/O buffers */ + range_count = le32_to_cpu ( page_header->range_count ); + for ( i = 0 ; i < range_count ; i++ ) { + + /* Parse header */ + len = le32_to_cpu ( page_header->range[i].len ); + offset = le32_to_cpu ( page_header->range[i].offset ); + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len ); + if ( ! iobuf ) { + DBGC ( vmdev, "VMBUS %s could not allocate %zd-byte " + "I/O buffer\n", vmdev->dev.name, len ); + rc = -ENOMEM; + goto err_alloc; + } + + /* Add I/O buffer to list */ + list_add ( &iobuf->list, list ); + + /* Populate I/O buffer */ + if ( ( rc = pages->op->copy ( pages, iob_put ( iobuf, len ), + offset, len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not populate I/O buffer " + "range [%zd,%zd): %s\n", + vmdev->dev.name, offset, len, strerror ( rc ) ); + goto err_copy; + } + } + + return 0; + + err_copy: + err_alloc: + list_for_each_entry_safe ( iobuf, tmp, list, list ) { + list_del ( &iobuf->list ); + free_iob ( iobuf ); + } + err_pages: + return rc; +} + +/** + * Poll ring buffer + * + * @v vmdev VMBus device + * @ret rc Return status code + */ +int vmbus_poll ( struct vmbus_device *vmdev ) { + struct vmbus_packet_header *header = vmdev->packet; + struct list_head list; + void *data; + size_t header_len; + size_t len; + size_t footer_len; + size_t ring_len; + size_t cons; + size_t old_cons; + uint64_t xid; + int rc; + + /* Sanity checks */ + assert ( vmdev->packet != NULL ); + assert ( vmdev->in != NULL ); + + /* Return immediately if buffer is empty */ + if ( ! vmbus_has_data ( vmdev ) ) + return 0; + cons = le32_to_cpu ( vmdev->in->cons ); + old_cons = cons; + + /* Consume (start of) header */ + cons = vmbus_consume ( vmdev, cons, header, sizeof ( *header ) ); + + /* Parse and sanity check header */ + header_len = ( le16_to_cpu ( header->hdr_qlen ) * 8 ); + if ( header_len < sizeof ( *header ) ) { + DBGC ( vmdev, "VMBUS %s received underlength header (%zd " + "bytes)\n", vmdev->dev.name, header_len ); + return -EINVAL; + } + len = ( ( le16_to_cpu ( header->qlen ) * 8 ) - header_len ); + footer_len = sizeof ( struct vmbus_packet_footer ); + ring_len = ( header_len + len + footer_len ); + if ( ring_len > vmdev->mtu ) { + DBGC ( vmdev, "VMBUS %s received overlength packet (%zd " + "bytes)\n", vmdev->dev.name, ring_len ); + return -ERANGE; + } + xid = le64_to_cpu ( header->xid ); + + /* Consume remainder of packet */ + cons = vmbus_consume ( vmdev, cons, + ( ( ( void * ) header ) + sizeof ( *header ) ), + ( ring_len - sizeof ( *header ) ) ); + DBGC2 ( vmdev, "VMBUS %s received:\n", vmdev->dev.name ); + DBGC2_HDA ( vmdev, old_cons, header, ring_len ); + assert ( ( ( cons - old_cons ) & ( vmdev->in_len - 1 ) ) == ring_len ); + + /* Allocate I/O buffers, if applicable */ + INIT_LIST_HEAD ( &list ); + if ( header->type == cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) ) { + if ( ( rc = vmbus_xfer_page_iobufs ( vmdev, header, + &list ) ) != 0 ) + return rc; + } + + /* Update producer index */ + rmb(); + vmdev->in->cons = cpu_to_le32 ( cons ); + + /* Handle packet */ + data = ( ( ( void * ) header ) + header_len ); + switch ( header->type ) { + + case cpu_to_le16 ( VMBUS_DATA_INBAND ) : + if ( ( rc = vmdev->op->recv_control ( vmdev, xid, data, + len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle control " + "packet: %s\n", + vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_DATA_XFER_PAGES ) : + if ( ( rc = vmdev->op->recv_data ( vmdev, xid, data, len, + &list ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle data packet: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_COMPLETION ) : + if ( ( rc = vmdev->op->recv_completion ( vmdev, xid, data, + len ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not handle completion: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + case cpu_to_le16 ( VMBUS_CANCELLATION ) : + if ( ( rc = vmdev->op->recv_cancellation ( vmdev, xid ) ) != 0){ + DBGC ( vmdev, "VMBUS %s could not handle cancellation: " + "%s\n", vmdev->dev.name, strerror ( rc ) ); + return rc; + } + break; + + default: + DBGC ( vmdev, "VMBUS %s unknown packet type %d\n", + vmdev->dev.name, le16_to_cpu ( header->type ) ); + return -ENOTSUP; + } + + return 0; +} + +/** + * Dump channel status (for debugging) + * + * @v vmdev VMBus device + */ +void vmbus_dump_channel ( struct vmbus_device *vmdev ) { + size_t out_prod = le32_to_cpu ( vmdev->out->prod ); + size_t out_cons = le32_to_cpu ( vmdev->out->cons ); + size_t in_prod = le32_to_cpu ( vmdev->in->prod ); + size_t in_cons = le32_to_cpu ( vmdev->in->cons ); + size_t in_len; + size_t first; + size_t second; + + /* Dump ring status */ + DBGC ( vmdev, "VMBUS %s out %03zx:%03zx%s in %03zx:%03zx%s\n", + vmdev->dev.name, out_prod, out_cons, + ( vmdev->out->intr_mask ? "(m)" : "" ), in_prod, in_cons, + ( vmdev->in->intr_mask ? "(m)" : "" ) ); + + /* Dump inbound ring contents, if any */ + if ( in_prod != in_cons ) { + in_len = ( ( in_prod - in_cons ) & + ( vmdev->in_len - 1 ) ); + first = ( vmdev->in_len - in_cons ); + if ( first > in_len ) + first = in_len; + second = ( in_len - first ); + DBGC_HDA ( vmdev, in_cons, &vmdev->in->data[in_cons], first ); + DBGC_HDA ( vmdev, 0, &vmdev->in->data[0], second ); + } +} + +/** + * Find driver for VMBus device + * + * @v vmdev VMBus device + * @ret driver Driver, or NULL + */ +static struct vmbus_driver * vmbus_find_driver ( const union uuid *type ) { + struct vmbus_driver *vmdrv; + + for_each_table_entry ( vmdrv, VMBUS_DRIVERS ) { + if ( memcmp ( &vmdrv->type, type, sizeof ( *type ) ) == 0 ) + return vmdrv; + } + return NULL; +} + +/** + * Probe channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +static int vmbus_probe_channels ( struct hv_hypervisor *hv, + struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + const struct vmbus_message_header *header = &vmbus->message->header; + const struct vmbus_offer_channel *offer = &vmbus->message->offer; + const union uuid *type; + struct vmbus_driver *driver; + struct vmbus_device *vmdev; + struct vmbus_device *tmp; + unsigned int channel; + int rc; + + /* Post message */ + if ( ( rc = vmbus_post_empty_message ( hv, VMBUS_REQUEST_OFFERS ) ) !=0) + goto err_post_message; + + /* Collect responses */ + while ( 1 ) { + + /* Wait for response */ + if ( ( rc = vmbus_wait_for_message ( hv ) ) != 0 ) + goto err_wait_for_message; + + /* Handle response */ + if ( header->type == cpu_to_le32 ( VMBUS_OFFER_CHANNEL ) ) { + + /* Parse offer */ + type = &offer->type; + channel = le32_to_cpu ( offer->channel ); + DBGC2 ( vmbus, "VMBUS %p offer %d type %s", + vmbus, channel, uuid_ntoa ( type ) ); + if ( offer->monitored ) + DBGC2 ( vmbus, " monitor %d", offer->monitor ); + DBGC2 ( vmbus, "\n" ); + + /* Look for a driver */ + driver = vmbus_find_driver ( type ); + if ( ! driver ) { + DBGC2 ( vmbus, "VMBUS %p has no driver for " + "type %s\n", vmbus, uuid_ntoa ( type )); + /* Not a fatal error */ + continue; + } + + /* Allocate and initialise device */ + vmdev = zalloc ( sizeof ( *vmdev ) ); + if ( ! vmdev ) { + rc = -ENOMEM; + goto err_alloc_vmdev; + } + snprintf ( vmdev->dev.name, sizeof ( vmdev->dev.name ), + "vmbus:%02x", channel ); + vmdev->dev.desc.bus_type = BUS_TYPE_HV; + INIT_LIST_HEAD ( &vmdev->dev.children ); + list_add_tail ( &vmdev->dev.siblings, + &parent->children ); + vmdev->dev.parent = parent; + vmdev->hv = hv; + vmdev->channel = channel; + vmdev->monitor = offer->monitor; + vmdev->signal = ( offer->monitored ? + vmbus_signal_monitor : + vmbus_signal_event ); + INIT_LIST_HEAD ( &vmdev->pages ); + vmdev->driver = driver; + vmdev->dev.driver_name = driver->name; + DBGC ( vmdev, "VMBUS %s has driver \"%s\"\n", + vmdev->dev.name, vmdev->driver->name ); + + } else if ( header->type == + cpu_to_le32 ( VMBUS_ALL_OFFERS_DELIVERED ) ) { + + break; + + } else { + DBGC ( vmbus, "VMBUS %p unexpected offer response type " + "%d\n", vmbus, le32_to_cpu ( header->type ) ); + rc = -EPROTO; + goto err_unexpected_offer; + } + } + + /* Probe all devices. We do this only after completing + * enumeration since devices will need to send and receive + * VMBus messages. + */ + list_for_each_entry ( vmdev, &parent->children, dev.siblings ) { + if ( ( rc = vmdev->driver->probe ( vmdev ) ) != 0 ) { + DBGC ( vmdev, "VMBUS %s could not probe: %s\n", + vmdev->dev.name, strerror ( rc ) ); + goto err_probe; + } + } + + return 0; + + err_probe: + /* Remove driver from each device that was already probed */ + list_for_each_entry_continue_reverse ( vmdev, &parent->children, + dev.siblings ) { + vmdev->driver->remove ( vmdev ); + } + err_unexpected_offer: + err_alloc_vmdev: + err_wait_for_message: + /* Free any devices allocated (but potentially not yet probed) */ + list_for_each_entry_safe ( vmdev, tmp, &parent->children, + dev.siblings ) { + list_del ( &vmdev->dev.siblings ); + free ( vmdev ); + } + err_post_message: + return rc; +} + +/** + * Remove channels + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + */ +static void vmbus_remove_channels ( struct hv_hypervisor *hv __unused, + struct device *parent ) { + struct vmbus_device *vmdev; + struct vmbus_device *tmp; + + /* Remove devices */ + list_for_each_entry_safe ( vmdev, tmp, &parent->children, + dev.siblings ) { + vmdev->driver->remove ( vmdev ); + assert ( list_empty ( &vmdev->dev.children ) ); + assert ( vmdev->out == NULL ); + assert ( vmdev->in == NULL ); + assert ( vmdev->packet == NULL ); + assert ( list_empty ( &vmdev->pages ) ); + list_del ( &vmdev->dev.siblings ); + free ( vmdev ); + } +} + +/** + * Probe Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + * @ret rc Return status code + */ +int vmbus_probe ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus; + int rc; + + /* Allocate and initialise structure */ + vmbus = zalloc ( sizeof ( *vmbus ) ); + if ( ! vmbus ) { + rc = -ENOMEM; + goto err_alloc; + } + hv->vmbus = vmbus; + + /* Initialise message buffer pointer + * + * We use a pointer to the fixed-size Hyper-V received message + * buffer. This allows us to access fields within received + * messages without first checking the message size: any + * fields beyond the end of the message will read as zero. + */ + vmbus->message = ( ( void * ) hv->message->received.data ); + assert ( sizeof ( *vmbus->message ) <= + sizeof ( hv->message->received.data ) ); + + /* Allocate interrupt and monitor pages */ + if ( ( rc = hv_alloc_pages ( hv, &vmbus->intr, &vmbus->monitor_in, + &vmbus->monitor_out, NULL ) ) != 0 ) + goto err_alloc_pages; + + /* Enable message interrupt */ + hv_enable_sint ( hv, VMBUS_MESSAGE_SINT ); + + /* Negotiate protocol version */ + if ( ( rc = vmbus_negotiate_version ( hv ) ) != 0 ) + goto err_negotiate_version; + + /* Enumerate channels */ + if ( ( rc = vmbus_probe_channels ( hv, parent ) ) != 0 ) + goto err_probe_channels; + + return 0; + + vmbus_remove_channels ( hv, parent ); + err_probe_channels: + vmbus_unload ( hv ); + err_negotiate_version: + hv_disable_sint ( hv, VMBUS_MESSAGE_SINT ); + hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out, + NULL ); + err_alloc_pages: + free ( vmbus ); + err_alloc: + return rc; +} + +/** + * Remove Hyper-V virtual machine bus + * + * @v hv Hyper-V hypervisor + * @v parent Parent device + */ +void vmbus_remove ( struct hv_hypervisor *hv, struct device *parent ) { + struct vmbus *vmbus = hv->vmbus; + + vmbus_remove_channels ( hv, parent ); + vmbus_unload ( hv ); + hv_disable_sint ( hv, VMBUS_MESSAGE_SINT ); + hv_free_pages ( hv, vmbus->intr, vmbus->monitor_in, vmbus->monitor_out, + NULL ); + free ( vmbus ); +} diff --git a/roms/ipxe/src/interface/linux/linux_entropy.c b/roms/ipxe/src/interface/linux/linux_entropy.c index 4671a48da..0f8e45d36 100644 --- a/roms/ipxe/src/interface/linux/linux_entropy.c +++ b/roms/ipxe/src/interface/linux/linux_entropy.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/interface/linux/linux_pci.c b/roms/ipxe/src/interface/linux/linux_pci.c index cbd825c18..0c140cb89 100644 --- a/roms/ipxe/src/interface/linux/linux_pci.c +++ b/roms/ipxe/src/interface/linux/linux_pci.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/interface/linux/linux_time.c b/roms/ipxe/src/interface/linux/linux_time.c index e3cbafec6..9e99fe9cd 100644 --- a/roms/ipxe/src/interface/linux/linux_time.c +++ b/roms/ipxe/src/interface/linux/linux_time.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/interface/linux/linux_uaccess.c b/roms/ipxe/src/interface/linux/linux_uaccess.c index 5ab0b6b65..ea2d8057c 100644 --- a/roms/ipxe/src/interface/linux/linux_uaccess.c +++ b/roms/ipxe/src/interface/linux/linux_uaccess.c @@ -27,7 +27,6 @@ FILE_LICENCE(GPL2_OR_LATER); * */ -PROVIDE_UACCESS_INLINE(linux, phys_to_user); PROVIDE_UACCESS_INLINE(linux, user_to_phys); PROVIDE_UACCESS_INLINE(linux, virt_to_user); PROVIDE_UACCESS_INLINE(linux, user_to_virt); diff --git a/roms/ipxe/src/interface/smbios/smbios.c b/roms/ipxe/src/interface/smbios/smbios.c index 856943428..1dcf819c2 100644 --- a/roms/ipxe/src/interface/smbios/smbios.c +++ b/roms/ipxe/src/interface/smbios/smbios.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/interface/smbios/smbios_settings.c b/roms/ipxe/src/interface/smbios/smbios_settings.c index 83e4320e9..5eadfa081 100644 --- a/roms/ipxe/src/interface/smbios/smbios_settings.c +++ b/roms/ipxe/src/interface/smbios/smbios_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> diff --git a/roms/ipxe/src/interface/xen/xenbus.c b/roms/ipxe/src/interface/xen/xenbus.c index ffc8aba3e..c328af443 100644 --- a/roms/ipxe/src/interface/xen/xenbus.c +++ b/roms/ipxe/src/interface/xen/xenbus.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <errno.h> diff --git a/roms/ipxe/src/interface/xen/xengrant.c b/roms/ipxe/src/interface/xen/xengrant.c index be12b23dc..269cd5836 100644 --- a/roms/ipxe/src/interface/xen/xengrant.c +++ b/roms/ipxe/src/interface/xen/xengrant.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <strings.h> diff --git a/roms/ipxe/src/interface/xen/xenstore.c b/roms/ipxe/src/interface/xen/xenstore.c index b96982927..23424a926 100644 --- a/roms/ipxe/src/interface/xen/xenstore.c +++ b/roms/ipxe/src/interface/xen/xenstore.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdarg.h> @@ -238,6 +242,10 @@ static int xenstore_response ( struct xen_hypervisor *xen, uint32_t req_id, char *string; int rc; + /* Wait for response to become available */ + while ( ! xenevent_pending ( xen, xen->store.port ) ) + cpu_nap(); + /* Receive message header */ xenstore_recv ( xen, &msg, sizeof ( msg ) ); *len = msg.len; diff --git a/roms/ipxe/src/net/80211/net80211.c b/roms/ipxe/src/net/80211/net80211.c index 434944523..d4970ad5c 100644 --- a/roms/ipxe/src/net/80211/net80211.c +++ b/roms/ipxe/src/net/80211/net80211.c @@ -805,6 +805,10 @@ int net80211_register ( struct net80211_device *dev, NET80211_MAX_CHANNELS * sizeof ( dev->channels[0] ) ); dev->channel = 0; + /* Mark device as not supporting interrupts, if applicable */ + if ( ! ops->irq ) + dev->netdev->state |= NETDEV_IRQ_UNSUPPORTED; + list_add_tail ( &dev->list, &net80211_devices ); return register_netdev ( dev->netdev ); } @@ -2826,3 +2830,9 @@ struct errortab common_wireless_errors[] __errortab = { __einfo_errortab ( EINFO_ECONNREFUSED_ASSOC_DENIED ), __einfo_errortab ( EINFO_ECONNREFUSED_AUTH_ALGO_UNSUPP ), }; + +/* Drag in objects via net80211_ll_protocol */ +REQUIRING_SYMBOL ( net80211_ll_protocol ); + +/* Drag in 802.11 configuration */ +REQUIRE_OBJECT ( config_net80211 ); diff --git a/roms/ipxe/src/net/80211/wpa.c b/roms/ipxe/src/net/80211/wpa.c index e2c4945f9..77f66d825 100644 --- a/roms/ipxe/src/net/80211/wpa.c +++ b/roms/ipxe/src/net/80211/wpa.c @@ -912,4 +912,5 @@ struct eapol_handler eapol_key_handler __eapol_handler = { }; /* WPA always needs EAPOL in order to be useful */ +REQUIRING_SYMBOL ( eapol_key_handler ); REQUIRE_OBJECT ( eapol ); diff --git a/roms/ipxe/src/net/80211/wpa_ccmp.c b/roms/ipxe/src/net/80211/wpa_ccmp.c index f98ebea26..a073c6a3c 100644 --- a/roms/ipxe/src/net/80211/wpa_ccmp.c +++ b/roms/ipxe/src/net/80211/wpa_ccmp.c @@ -480,7 +480,7 @@ static void ccmp_kie_mic ( const void *kck, const void *msg, size_t len, { u8 sha1_ctx[SHA1_CTX_SIZE]; u8 kckb[16]; - u8 hash[SHA1_SIZE]; + u8 hash[SHA1_DIGEST_SIZE]; size_t kck_len = 16; memcpy ( kckb, kck, kck_len ); diff --git a/roms/ipxe/src/net/80211/wpa_tkip.c b/roms/ipxe/src/net/80211/wpa_tkip.c index fa3e0763b..3b1934b59 100644 --- a/roms/ipxe/src/net/80211/wpa_tkip.c +++ b/roms/ipxe/src/net/80211/wpa_tkip.c @@ -136,7 +136,7 @@ static const u16 Sbox[256] = { */ static inline u16 S ( u16 v ) { - return Sbox[v & 0xFF] ^ swap16 ( Sbox[v >> 8] ); + return Sbox[v & 0xFF] ^ bswap_16 ( Sbox[v >> 8] ); } /** diff --git a/roms/ipxe/src/net/aoe.c b/roms/ipxe/src/net/aoe.c index a6d7b3e7b..2da8655b4 100644 --- a/roms/ipxe/src/net/aoe.c +++ b/roms/ipxe/src/net/aoe.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <string.h> diff --git a/roms/ipxe/src/net/arp.c b/roms/ipxe/src/net/arp.c index 261e681e1..1e27c44e7 100644 --- a/roms/ipxe/src/net/arp.c +++ b/roms/ipxe/src/net/arp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -52,9 +56,9 @@ struct net_protocol arp_protocol __net_protocol; * @v net_source Source network-layer address * @ret rc Return status code */ -static int arp_tx_request ( struct net_device *netdev, - struct net_protocol *net_protocol, - const void *net_dest, const void *net_source ) { +int arp_tx_request ( struct net_device *netdev, + struct net_protocol *net_protocol, + const void *net_dest, const void *net_source ) { struct ll_protocol *ll_protocol = netdev->ll_protocol; struct io_buffer *iobuf; struct arphdr *arphdr; diff --git a/roms/ipxe/src/net/dhcpopts.c b/roms/ipxe/src/net/dhcpopts.c index 8cd19cf80..cdb632b46 100644 --- a/roms/ipxe/src/net/dhcpopts.c +++ b/roms/ipxe/src/net/dhcpopts.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/dhcppkt.c b/roms/ipxe/src/net/dhcppkt.c index a9a6d3a94..4e64f85e4 100644 --- a/roms/ipxe/src/net/dhcppkt.c +++ b/roms/ipxe/src/net/dhcppkt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/eth_slow.c b/roms/ipxe/src/net/eth_slow.c index db54b55a4..049c26cb3 100644 --- a/roms/ipxe/src/net/eth_slow.c +++ b/roms/ipxe/src/net/eth_slow.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> diff --git a/roms/ipxe/src/net/ethernet.c b/roms/ipxe/src/net/ethernet.c index 03978c2a8..6ddf05344 100644 --- a/roms/ipxe/src/net/ethernet.c +++ b/roms/ipxe/src/net/ethernet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -43,6 +47,24 @@ FILE_LICENCE ( GPL2_OR_LATER ); uint8_t eth_broadcast[ETH_ALEN] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff }; /** + * Check if Ethernet packet has an 802.3 LLC header + * + * @v ethhdr Ethernet header + * @ret is_llc Packet has 802.3 LLC header + */ +static inline int eth_is_llc_packet ( struct ethhdr *ethhdr ) { + uint8_t len_msb; + + /* Check if the protocol field contains a value short enough + * to be a frame length. The slightly convoluted form of the + * comparison is designed to reduce to a single x86 + * instruction. + */ + len_msb = *( ( uint8_t * ) ðhdr->h_protocol ); + return ( len_msb < 0x06 ); +} + +/** * Add Ethernet link-layer header * * @v netdev Network device @@ -80,9 +102,14 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, const void **ll_dest, const void **ll_source, uint16_t *net_proto, unsigned int *flags ) { struct ethhdr *ethhdr = iobuf->data; + uint16_t *llc_proto; - /* Sanity check */ - if ( iob_len ( iobuf ) < sizeof ( *ethhdr ) ) { + /* Sanity check. While in theory we could receive a one-byte + * packet, this will never happen in practice and performing + * the combined length check here avoids the need for an + * additional comparison if we detect an LLC frame. + */ + if ( iob_len ( iobuf ) < ( sizeof ( *ethhdr ) + sizeof ( *llc_proto ))){ DBG ( "Ethernet packet too short (%zd bytes)\n", iob_len ( iobuf ) ); return -EINVAL; @@ -100,6 +127,17 @@ int eth_pull ( struct net_device *netdev __unused, struct io_buffer *iobuf, ( is_broadcast_ether_addr ( ethhdr->h_dest ) ? LL_BROADCAST : 0 ) ); + /* If this is an LLC frame (with a length in place of the + * protocol field), then use the next two bytes (which happen + * to be the LLC DSAP and SSAP) as the protocol. This allows + * for minimal-overhead support for receiving (rare) LLC + * frames, without requiring a full LLC protocol layer. + */ + if ( eth_is_llc_packet ( ethhdr ) ) { + llc_proto = ( ðhdr->h_protocol + 1 ); + *net_proto = *llc_proto; + } + return 0; } @@ -235,5 +273,11 @@ struct net_device * alloc_etherdev ( size_t priv_size ) { return netdev; } +/* Drag in objects via ethernet_protocol */ +REQUIRING_SYMBOL ( ethernet_protocol ); + +/* Drag in Ethernet configuration */ +REQUIRE_OBJECT ( config_ethernet ); + /* Drag in Ethernet slow protocols */ REQUIRE_OBJECT ( eth_slow ); diff --git a/roms/ipxe/src/net/fakedhcp.c b/roms/ipxe/src/net/fakedhcp.c index 3dec88b11..b6c456a59 100644 --- a/roms/ipxe/src/net/fakedhcp.c +++ b/roms/ipxe/src/net/fakedhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/fc.c b/roms/ipxe/src/net/fc.c index 58008995c..2e8070272 100644 --- a/roms/ipxe/src/net/fc.c +++ b/roms/ipxe/src/net/fc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> @@ -1935,3 +1939,9 @@ struct fc_ulp * fc_ulp_get_port_id_type ( struct fc_port *port, err_peer_get_wwn: return NULL; } + +/* Drag in objects via fc_ports */ +REQUIRING_SYMBOL ( fc_ports ); + +/* Drag in Fibre Channel configuration */ +REQUIRE_OBJECT ( config_fc ); diff --git a/roms/ipxe/src/net/fcels.c b/roms/ipxe/src/net/fcels.c index 1cfe90727..5fc27cef4 100644 --- a/roms/ipxe/src/net/fcels.c +++ b/roms/ipxe/src/net/fcels.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/fcns.c b/roms/ipxe/src/net/fcns.c index 3ca4ad557..be4dfea24 100644 --- a/roms/ipxe/src/net/fcns.c +++ b/roms/ipxe/src/net/fcns.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/fcoe.c b/roms/ipxe/src/net/fcoe.c index e9e404ec3..c3258f15e 100644 --- a/roms/ipxe/src/net/fcoe.c +++ b/roms/ipxe/src/net/fcoe.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/fcp.c b/roms/ipxe/src/net/fcp.c index 9c36a4c72..930bf7dd4 100644 --- a/roms/ipxe/src/net/fcp.c +++ b/roms/ipxe/src/net/fcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <stdint.h> diff --git a/roms/ipxe/src/net/fragment.c b/roms/ipxe/src/net/fragment.c index 410915b3b..781b9bc60 100644 --- a/roms/ipxe/src/net/fragment.c +++ b/roms/ipxe/src/net/fragment.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/icmp.c b/roms/ipxe/src/net/icmp.c index 1bbf8bd30..5371277e4 100644 --- a/roms/ipxe/src/net/icmp.c +++ b/roms/ipxe/src/net/icmp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <byteswap.h> diff --git a/roms/ipxe/src/net/icmpv4.c b/roms/ipxe/src/net/icmpv4.c index 996ba1490..0858ff37f 100644 --- a/roms/ipxe/src/net/icmpv4.c +++ b/roms/ipxe/src/net/icmpv4.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> diff --git a/roms/ipxe/src/net/icmpv6.c b/roms/ipxe/src/net/icmpv6.c index 479800e7d..8555aaf0b 100644 --- a/roms/ipxe/src/net/icmpv6.c +++ b/roms/ipxe/src/net/icmpv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> @@ -34,6 +38,65 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ +/* Disambiguate the various error causes */ +#define EHOSTUNREACH_ROUTE \ + __einfo_error ( EINFO_EHOSTUNREACH_ROUTE ) +#define EINFO_EHOSTUNREACH_ROUTE \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 0, \ + "No route to destination" ) +#define EHOSTUNREACH_PROHIBITED \ + __einfo_error ( EINFO_EHOSTUNREACH_PROHIBITED ) +#define EINFO_EHOSTUNREACH_PROHIBITED \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 1, \ + "Communication administratively prohibited" ) +#define EHOSTUNREACH_ADDRESS \ + __einfo_error ( EINFO_EHOSTUNREACH_ADDRESS ) +#define EINFO_EHOSTUNREACH_ADDRESS \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 3, \ + "Address unreachable" ) +#define EHOSTUNREACH_PORT \ + __einfo_error ( EINFO_EHOSTUNREACH_PORT ) +#define EINFO_EHOSTUNREACH_PORT \ + __einfo_uniqify ( EINFO_EHOSTUNREACH, 4, \ + "Port unreachable" ) +#define EHOSTUNREACH_CODE( code ) \ + EUNIQ ( EINFO_EHOSTUNREACH, ( (code) & 0x1f ), \ + EHOSTUNREACH_ROUTE, EHOSTUNREACH_PROHIBITED, \ + EHOSTUNREACH_ADDRESS, EHOSTUNREACH_PORT ) + +#define ETIMEDOUT_HOP \ + __einfo_error ( EINFO_ETIMEDOUT_HOP ) +#define EINFO_ETIMEDOUT_HOP \ + __einfo_uniqify ( EINFO_ETIMEDOUT, 0, \ + "Hop limit exceeded in transit" ) +#define ETIMEDOUT_REASSEMBLY \ + __einfo_error ( EINFO_ETIMEDOUT_REASSEMBLY ) +#define EINFO_ETIMEDOUT_REASSEMBLY \ + __einfo_uniqify ( EINFO_ETIMEDOUT, 1, \ + "Fragment reassembly time exceeded" ) +#define ETIMEDOUT_CODE( code ) \ + EUNIQ ( EINFO_ETIMEDOUT, ( (code) & 0x1f ), \ + ETIMEDOUT_HOP, ETIMEDOUT_REASSEMBLY ) + +#define EPROTO_BAD_HEADER \ + __einfo_error ( EINFO_EPROTO_BAD_HEADER ) +#define EINFO_EPROTO_BAD_HEADER \ + __einfo_uniqify ( EINFO_EPROTO, 0, \ + "Erroneous header field" ) +#define EPROTO_NEXT_HEADER \ + __einfo_error ( EINFO_EPROTO_NEXT_HEADER ) +#define EINFO_EPROTO_NEXT_HEADER \ + __einfo_uniqify ( EINFO_EPROTO, 1, \ + "Unrecognised next header type" ) +#define EPROTO_OPTION \ + __einfo_error ( EINFO_EPROTO_OPTION ) +#define EINFO_EPROTO_OPTION \ + __einfo_uniqify ( EINFO_EPROTO, 2, \ + "Unrecognised IPv6 option" ) +#define EPROTO_CODE( code ) \ + EUNIQ ( EINFO_EPROTO, ( (code) & 0x1f ), \ + EPROTO_BAD_HEADER, EPROTO_NEXT_HEADER, EPROTO_OPTION ) + struct icmp_echo_protocol icmpv6_echo_protocol __icmp_echo_protocol; /** @@ -144,8 +207,25 @@ static int icmpv6_rx ( struct io_buffer *iobuf, struct net_device *netdev, /* Identify handler */ handler = icmpv6_handler ( icmp->type ); if ( ! handler ) { - DBGC ( netdev, "ICMPv6 unrecognised type %d\n", icmp->type ); - rc = -ENOTSUP; + switch ( icmp->type ) { + case ICMPV6_DESTINATION_UNREACHABLE: + rc = -EHOSTUNREACH_CODE ( icmp->code ); + break; + case ICMPV6_PACKET_TOO_BIG: + rc = -ERANGE; + break; + case ICMPV6_TIME_EXCEEDED: + rc = -ETIMEDOUT_CODE ( icmp->code ); + break; + case ICMPV6_PARAMETER_PROBLEM: + rc = -EPROTO_CODE ( icmp->code ); + break; + default: + DBGC ( netdev, "ICMPv6 unrecognised type %d code %d\n", + icmp->type, icmp->code ); + rc = -ENOTSUP; + break; + }; goto done; } diff --git a/roms/ipxe/src/net/infiniband.c b/roms/ipxe/src/net/infiniband.c index 12d1d83ce..2e3d76d54 100644 --- a/roms/ipxe/src/net/infiniband.c +++ b/roms/ipxe/src/net/infiniband.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -714,6 +718,9 @@ int ib_mcast_attach ( struct ib_device *ibdev, struct ib_queue_pair *qp, struct ib_multicast_gid *mgid; int rc; + /* Sanity check */ + assert ( qp != NULL ); + /* Add to software multicast GID list */ mgid = zalloc ( sizeof ( *mgid ) ); if ( ! mgid ) { @@ -747,6 +754,9 @@ void ib_mcast_detach ( struct ib_device *ibdev, struct ib_queue_pair *qp, union ib_gid *gid ) { struct ib_multicast_gid *mgid; + /* Sanity check */ + assert ( qp != NULL ); + /* Remove from hardware multicast GID list */ ibdev->op->mcast_detach ( ibdev, qp, gid ); @@ -995,5 +1005,11 @@ struct ib_device * last_opened_ibdev ( void ) { return ibdev; } +/* Drag in objects via register_ibdev() */ +REQUIRING_SYMBOL ( register_ibdev ); + +/* Drag in Infiniband configuration */ +REQUIRE_OBJECT ( config_infiniband ); + /* Drag in IPoIB */ REQUIRE_OBJECT ( ipoib ); diff --git a/roms/ipxe/src/net/infiniband/ib_cm.c b/roms/ipxe/src/net/infiniband/ib_cm.c index 797639bc8..85982f09d 100644 --- a/roms/ipxe/src/net/infiniband/ib_cm.c +++ b/roms/ipxe/src/net/infiniband/ib_cm.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_mcast.c b/roms/ipxe/src/net/infiniband/ib_mcast.c index 0a5e72a37..fc4ff7f0a 100644 --- a/roms/ipxe/src/net/infiniband/ib_mcast.c +++ b/roms/ipxe/src/net/infiniband/ib_mcast.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -146,6 +150,9 @@ int ib_mcast_join ( struct ib_device *ibdev, struct ib_queue_pair *qp, DBGC ( ibdev, "IBDEV %p QPN %lx joining " IB_GID_FMT "\n", ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + /* Sanity check */ + assert ( qp != NULL ); + /* Initialise structure */ membership->qp = qp; memcpy ( &membership->gid, gid, sizeof ( membership->gid ) ); @@ -195,6 +202,9 @@ void ib_mcast_leave ( struct ib_device *ibdev, struct ib_queue_pair *qp, DBGC ( ibdev, "IBDEV %p QPN %lx leaving " IB_GID_FMT "\n", ibdev, qp->qpn, IB_GID_ARGS ( gid ) ); + /* Sanity check */ + assert ( qp != NULL ); + /* Detach from multicast GID */ ib_mcast_detach ( ibdev, qp, &membership->gid ); diff --git a/roms/ipxe/src/net/infiniband/ib_mi.c b/roms/ipxe/src/net/infiniband/ib_mi.c index ef6d539f1..b43212974 100644 --- a/roms/ipxe/src/net/infiniband/ib_mi.c +++ b/roms/ipxe/src/net/infiniband/ib_mi.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_packet.c b/roms/ipxe/src/net/infiniband/ib_packet.c index 6c850e39b..d3a22d309 100644 --- a/roms/ipxe/src/net/infiniband/ib_packet.c +++ b/roms/ipxe/src/net/infiniband/ib_packet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_pathrec.c b/roms/ipxe/src/net/infiniband/ib_pathrec.c index 1b95cbfa8..f9cbab87f 100644 --- a/roms/ipxe/src/net/infiniband/ib_pathrec.c +++ b/roms/ipxe/src/net/infiniband/ib_pathrec.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_sma.c b/roms/ipxe/src/net/infiniband/ib_sma.c index 86553732a..a05d7c924 100644 --- a/roms/ipxe/src/net/infiniband/ib_sma.c +++ b/roms/ipxe/src/net/infiniband/ib_sma.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_smc.c b/roms/ipxe/src/net/infiniband/ib_smc.c index 4d947d568..c1741b26c 100644 --- a/roms/ipxe/src/net/infiniband/ib_smc.c +++ b/roms/ipxe/src/net/infiniband/ib_smc.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/infiniband/ib_srp.c b/roms/ipxe/src/net/infiniband/ib_srp.c index 7b2b2b4ea..3700184c0 100644 --- a/roms/ipxe/src/net/infiniband/ib_srp.c +++ b/roms/ipxe/src/net/infiniband/ib_srp.c @@ -291,7 +291,7 @@ static int ib_srp_parse_byte_string ( const char *rp_comp, uint8_t *bytes, return -EINVAL_BYTE_STRING_LEN; /* Parse byte string */ - decoded_size = base16_decode ( rp_comp, bytes ); + decoded_size = base16_decode ( rp_comp, bytes, size ); if ( decoded_size < 0 ) return decoded_size; diff --git a/roms/ipxe/src/net/iobpad.c b/roms/ipxe/src/net/iobpad.c index 9cc8328e9..936b4bde4 100644 --- a/roms/ipxe/src/net/iobpad.c +++ b/roms/ipxe/src/net/iobpad.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file diff --git a/roms/ipxe/src/net/ipv4.c b/roms/ipxe/src/net/ipv4.c index 9c5cf2eb4..a54784049 100644 --- a/roms/ipxe/src/net/ipv4.c +++ b/roms/ipxe/src/net/ipv4.c @@ -1,3 +1,27 @@ +/* + * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>. + * Copyright (C) 2006 Nikhil Chandru Rao + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + #include <string.h> #include <stdint.h> #include <stdlib.h> @@ -24,7 +48,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /* Unique IP datagram identification number (high byte) */ static uint8_t next_ident_high = 0; @@ -115,6 +139,7 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { /** * Perform IPv4 routing * + * @v scope_id Destination address scope ID * @v dest Final destination address * @ret dest Next hop destination address * @ret miniroute Routing table entry to use, or NULL if no route @@ -122,22 +147,42 @@ static void del_ipv4_miniroute ( struct ipv4_miniroute *miniroute ) { * If the route requires use of a gateway, the next hop destination * address will be overwritten with the gateway address. */ -static struct ipv4_miniroute * ipv4_route ( struct in_addr *dest ) { +static struct ipv4_miniroute * ipv4_route ( unsigned int scope_id, + struct in_addr *dest ) { struct ipv4_miniroute *miniroute; - int local; - int has_gw; /* Find first usable route in routing table */ list_for_each_entry ( miniroute, &ipv4_miniroutes, list ) { + + /* Skip closed network devices */ if ( ! netdev_is_open ( miniroute->netdev ) ) continue; - local = ( ( ( dest->s_addr ^ miniroute->address.s_addr ) - & miniroute->netmask.s_addr ) == 0 ); - has_gw = ( miniroute->gateway.s_addr ); - if ( local || has_gw ) { - if ( ! local ) + + if ( IN_IS_MULTICAST ( dest->s_addr ) ) { + + /* If destination is non-global, and the scope ID + * matches this network device, then use this route. + */ + if ( miniroute->netdev->index == scope_id ) + return miniroute; + + } else { + + /* If destination is an on-link global + * address, then use this route. + */ + if ( ( ( dest->s_addr ^ miniroute->address.s_addr ) + & miniroute->netmask.s_addr ) == 0 ) + return miniroute; + + /* If destination is an off-link global + * address, and we have a default gateway, + * then use this route. + */ + if ( miniroute->gateway.s_addr ) { *dest = miniroute->gateway; - return miniroute; + return miniroute; + } } } @@ -156,7 +201,7 @@ static struct net_device * ipv4_netdev ( struct sockaddr_tcpip *st_dest ) { struct ipv4_miniroute *miniroute; /* Find routing table entry */ - miniroute = ipv4_route ( &dest ); + miniroute = ipv4_route ( sin_dest->sin_scope_id, &dest ); if ( ! miniroute ) return NULL; @@ -290,8 +335,8 @@ static int ipv4_tx ( struct io_buffer *iobuf, if ( sin_src ) iphdr->src = sin_src->sin_addr; if ( ( next_hop.s_addr != INADDR_BROADCAST ) && - ( ! IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) && - ( ( miniroute = ipv4_route ( &next_hop ) ) != NULL ) ) { + ( ( miniroute = ipv4_route ( sin_dest->sin_scope_id, + &next_hop ) ) != NULL ) ) { iphdr->src = miniroute->address; netmask = miniroute->netmask; netdev = miniroute->netdev; @@ -329,7 +374,7 @@ static int ipv4_tx ( struct io_buffer *iobuf, /* Broadcast address */ ipv4_stats.out_bcast_pkts++; ll_dest = netdev->ll_broadcast; - } else if ( IN_MULTICAST ( ntohl ( next_hop.s_addr ) ) ) { + } else if ( IN_IS_MULTICAST ( next_hop.s_addr ) ) { /* Multicast address */ ipv4_stats.out_mcast_pkts++; if ( ( rc = netdev->ll_protocol->mc_hash ( AF_INET, &next_hop, @@ -569,10 +614,42 @@ static int ipv4_arp_check ( struct net_device *netdev, const void *net_addr ) { } /** + * Parse IPv4 address + * + * @v string IPv4 address string + * @ret in IPv4 address to fill in + * @ret ok IPv4 address is valid + * + * Note that this function returns nonzero iff the address is valid, + * to match the standard BSD API function of the same name. Unlike + * most other iPXE functions, a zero therefore indicates failure. + */ +int inet_aton ( const char *string, struct in_addr *in ) { + const char *separator = "..."; + uint8_t *byte = ( ( uint8_t * ) in ); + char *endp; + unsigned long value; + + while ( 1 ) { + value = strtoul ( string, &endp, 0 ); + if ( string == endp ) + return 0; + if ( value > 0xff ) + return 0; + *(byte++) = value; + if ( *endp != *separator ) + return 0; + if ( ! *(separator++) ) + return 1; + string = ( endp + 1 ); + } +} + +/** * Convert IPv4 address to dotted-quad notation * - * @v in IP address - * @ret string IP address in dotted-quad notation + * @v in IPv4 address + * @ret string IPv4 address in dotted-quad notation */ char * inet_ntoa ( struct in_addr in ) { static char buf[16]; /* "xxx.xxx.xxx.xxx" */ @@ -583,10 +660,10 @@ char * inet_ntoa ( struct in_addr in ) { } /** - * Transcribe IP address + * Transcribe IPv4 address * - * @v net_addr IP address - * @ret string IP address in dotted-quad notation + * @v net_addr IPv4 address + * @ret string IPv4 address in dotted-quad notation * */ static const char * ipv4_ntoa ( const void *net_addr ) { @@ -760,12 +837,12 @@ static int ipv4_create_routes ( void ) { fetch_ipv4_setting ( settings, &netmask_setting, &netmask ); /* Calculate default netmask, if necessary */ if ( ! netmask.s_addr ) { - if ( IN_CLASSA ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSA_NET ); - } else if ( IN_CLASSB ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSB_NET ); - } else if ( IN_CLASSC ( ntohl ( address.s_addr ) ) ) { - netmask.s_addr = htonl ( IN_CLASSC_NET ); + if ( IN_IS_CLASSA ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSA; + } else if ( IN_IS_CLASSB ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSB; + } else if ( IN_IS_CLASSC ( address.s_addr ) ) { + netmask.s_addr = INADDR_NET_CLASSC; } } /* Get default gateway, if present */ @@ -785,5 +862,8 @@ struct settings_applicator ipv4_settings_applicator __settings_applicator = { .apply = ipv4_create_routes, }; +/* Drag in objects via ipv4_protocol */ +REQUIRING_SYMBOL ( ipv4_protocol ); + /* Drag in ICMPv4 */ REQUIRE_OBJECT ( icmpv4 ); diff --git a/roms/ipxe/src/net/ipv6.c b/roms/ipxe/src/net/ipv6.c index 3c374168c..a75e72ddb 100644 --- a/roms/ipxe/src/net/ipv6.c +++ b/roms/ipxe/src/net/ipv6.c @@ -290,8 +290,7 @@ static struct ipv6_miniroute * ipv6_route ( unsigned int scope_id, if ( ! ( miniroute->flags & IPV6_HAS_ADDRESS ) ) continue; - if ( IN6_IS_ADDR_LINKLOCAL ( *dest ) || - IN6_IS_ADDR_MULTICAST ( *dest ) ) { + if ( IN6_IS_ADDR_NONGLOBAL ( *dest ) ) { /* If destination is non-global, and the scope ID * matches this network device, then use this route. @@ -901,7 +900,7 @@ static const char * ipv6_sock_ntoa ( struct sockaddr *sa ) { const char *netdev_name; /* Identify network device, if applicable */ - if ( IN6_IS_ADDR_LINKLOCAL ( in ) || IN6_IS_ADDR_MULTICAST ( in ) ) { + if ( IN6_IS_ADDR_NONGLOBAL ( in ) ) { netdev = find_netdev_by_index ( sin6->sin6_scope_id ); netdev_name = ( netdev ? netdev->name : "UNKNOWN" ); } else { @@ -956,14 +955,26 @@ static int ipv6_sock_aton ( const char *string, struct sockaddr *sa ) { if ( ( rc = inet6_aton ( in_string, &in ) ) != 0 ) goto err_inet6_aton; - /* Parse network device name, if present */ + /* Parse scope ID, if applicable */ if ( netdev_string ) { + + /* Parse explicit network device name, if present */ netdev = find_netdev ( netdev_string ); if ( ! netdev ) { rc = -ENODEV; goto err_find_netdev; } sin6->sin6_scope_id = netdev->index; + + } else if ( IN6_IS_ADDR_NONGLOBAL ( &in ) ) { + + /* If no network device is explicitly specified for a + * link-local or multicast address, default to using + * "netX" (if existent). + */ + netdev = last_opened_netdev(); + if ( netdev ) + sin6->sin6_scope_id = netdev->index; } /* Copy IPv6 address portion to socket address */ @@ -1104,6 +1115,9 @@ struct net_driver ipv6_driver __net_driver = { .remove = ipv6_remove, }; +/* Drag in objects via ipv6_protocol */ +REQUIRING_SYMBOL ( ipv6_protocol ); + /* Drag in ICMPv6 */ REQUIRE_OBJECT ( icmpv6 ); diff --git a/roms/ipxe/src/net/neighbour.c b/roms/ipxe/src/net/neighbour.c index e3026ce46..7f66d9992 100644 --- a/roms/ipxe/src/net/neighbour.c +++ b/roms/ipxe/src/net/neighbour.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -91,8 +95,8 @@ static struct neighbour * neighbour_create ( struct net_device *netdev, memcpy ( neighbour->net_dest, net_dest, net_protocol->net_addr_len ); timer_init ( &neighbour->timer, neighbour_expired, &neighbour->refcnt ); - neighbour->timer.min_timeout = NEIGHBOUR_MIN_TIMEOUT; - neighbour->timer.max_timeout = NEIGHBOUR_MAX_TIMEOUT; + set_timer_limits ( &neighbour->timer, NEIGHBOUR_MIN_TIMEOUT, + NEIGHBOUR_MAX_TIMEOUT ); INIT_LIST_HEAD ( &neighbour->tx_queue ); /* Transfer ownership to cache */ @@ -318,7 +322,7 @@ int neighbour_tx ( struct io_buffer *iobuf, struct net_device *netdev, netdev->name, net_protocol->name, net_protocol->ntoa ( net_dest ) ); list_add_tail ( &iobuf->list, &neighbour->tx_queue ); - return -EAGAIN; + return 0; } } diff --git a/roms/ipxe/src/net/netdev_settings.c b/roms/ipxe/src/net/netdev_settings.c index b3b2e68d8..edd4c4b9f 100644 --- a/roms/ipxe/src/net/netdev_settings.c +++ b/roms/ipxe/src/net/netdev_settings.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <errno.h> @@ -121,6 +125,10 @@ static int netdev_fetch_bustype ( struct net_device *netdev, void *data, [BUS_TYPE_MCA] = "MCA", [BUS_TYPE_ISA] = "ISA", [BUS_TYPE_TAP] = "TAP", + [BUS_TYPE_EFI] = "EFI", + [BUS_TYPE_XEN] = "XEN", + [BUS_TYPE_HV] = "HV", + [BUS_TYPE_USB] = "USB", }; struct device_description *desc = &netdev->dev->desc; const char *bustype; diff --git a/roms/ipxe/src/net/netdevice.c b/roms/ipxe/src/net/netdevice.c index a55e6b7d7..7c40a2ac8 100644 --- a/roms/ipxe/src/net/netdevice.c +++ b/roms/ipxe/src/net/netdevice.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -35,6 +39,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/device.h> #include <ipxe/errortab.h> #include <ipxe/profile.h> +#include <ipxe/fault.h> #include <ipxe/vlan.h> #include <ipxe/netdevice.h> @@ -157,6 +162,9 @@ void netdev_rx_unfreeze ( struct net_device *netdev ) { */ void netdev_link_err ( struct net_device *netdev, int rc ) { + /* Stop link block timer */ + stop_timer ( &netdev->link_block ); + /* Record link state */ netdev->link_rc = rc; if ( netdev->link_rc == 0 ) { @@ -187,6 +195,50 @@ void netdev_link_down ( struct net_device *netdev ) { } /** + * Mark network device link as being blocked + * + * @v netdev Network device + * @v timeout Timeout (in ticks) + */ +void netdev_link_block ( struct net_device *netdev, unsigned long timeout ) { + + /* Start link block timer */ + if ( ! netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "NETDEV %s link blocked for %ld ticks\n", + netdev->name, timeout ); + } + start_timer_fixed ( &netdev->link_block, timeout ); +} + +/** + * Mark network device link as being unblocked + * + * @v netdev Network device + */ +void netdev_link_unblock ( struct net_device *netdev ) { + + /* Stop link block timer */ + if ( netdev_link_blocked ( netdev ) ) + DBGC ( netdev, "NETDEV %s link unblocked\n", netdev->name ); + stop_timer ( &netdev->link_block ); +} + +/** + * Handle network device link block timer expiry + * + * @v timer Link block timer + * @v fail Failure indicator + */ +static void netdev_link_block_expired ( struct retry_timer *timer, + int fail __unused ) { + struct net_device *netdev = + container_of ( timer, struct net_device, link_block ); + + /* Assume link is no longer blocked */ + DBGC ( netdev, "NETDEV %s link block expired\n", netdev->name ); +} + +/** * Record network device statistic * * @v stats Network device statistics @@ -252,11 +304,8 @@ int netdev_tx ( struct net_device *netdev, struct io_buffer *iobuf ) { } /* Discard packet (for test purposes) if applicable */ - if ( ( NETDEV_DISCARD_RATE > 0 ) && - ( ( random() % NETDEV_DISCARD_RATE ) == 0 ) ) { - rc = -EAGAIN; + if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) goto err; - } /* Transmit packet */ if ( ( rc = netdev->op->transmit ( netdev, iobuf ) ) != 0 ) @@ -406,14 +455,14 @@ static void netdev_tx_flush ( struct net_device *netdev ) { * function takes ownership of the I/O buffer. */ void netdev_rx ( struct net_device *netdev, struct io_buffer *iobuf ) { + int rc; DBGC2 ( netdev, "NETDEV %s received %p (%p+%zx)\n", netdev->name, iobuf, iobuf->data, iob_len ( iobuf ) ); /* Discard packet (for test purposes) if applicable */ - if ( ( NETDEV_DISCARD_RATE > 0 ) && - ( ( random() % NETDEV_DISCARD_RATE ) == 0 ) ) { - netdev_rx_err ( netdev, iobuf, -EAGAIN ); + if ( ( rc = inject_fault ( NETDEV_DISCARD_RATE ) ) != 0 ) { + netdev_rx_err ( netdev, iobuf, rc ); return; } @@ -541,7 +590,8 @@ static struct interface_descriptor netdev_config_desc = static void free_netdev ( struct refcnt *refcnt ) { struct net_device *netdev = container_of ( refcnt, struct net_device, refcnt ); - + + stop_timer ( &netdev->link_block ); netdev_tx_flush ( netdev ); netdev_rx_flush ( netdev ); clear_settings ( netdev_settings ( netdev ) ); @@ -571,6 +621,8 @@ struct net_device * alloc_netdev ( size_t priv_len ) { if ( netdev ) { ref_init ( &netdev->refcnt, free_netdev ); netdev->link_rc = -EUNKNOWN_LINK_STATUS; + timer_init ( &netdev->link_block, netdev_link_block_expired, + &netdev->refcnt ); INIT_LIST_HEAD ( &netdev->tx_queue ); INIT_LIST_HEAD ( &netdev->tx_deferred ); INIT_LIST_HEAD ( &netdev->rx_queue ); @@ -624,11 +676,11 @@ int register_netdev ( struct net_device *netdev ) { } /* Record device index and create device name */ - netdev->index = netdev_index++; if ( netdev->name[0] == '\0' ) { snprintf ( netdev->name, sizeof ( netdev->name ), "net%d", - netdev->index ); + netdev_index ); } + netdev->index = ++netdev_index; /* Use least significant bits of the link-layer address to * improve the randomness of the (non-cryptographic) random diff --git a/roms/ipxe/src/net/nullnet.c b/roms/ipxe/src/net/nullnet.c index 4ac50f64b..2948b38c0 100644 --- a/roms/ipxe/src/net/nullnet.c +++ b/roms/ipxe/src/net/nullnet.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <errno.h> diff --git a/roms/ipxe/src/net/pccrc.c b/roms/ipxe/src/net/pccrc.c new file mode 100644 index 000000000..4cd82cd1c --- /dev/null +++ b/roms/ipxe/src/net/pccrc.c @@ -0,0 +1,818 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <errno.h> +#include <assert.h> +#include <ipxe/uaccess.h> +#include <ipxe/sha256.h> +#include <ipxe/sha512.h> +#include <ipxe/hmac.h> +#include <ipxe/base16.h> +#include <ipxe/pccrc.h> + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] + * + */ + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + +/** + * Transcribe hash value (for debugging) + * + * @v info Content information + * @v hash Hash value + * @ret string Hash value string + */ +static inline const char * +peerdist_info_hash_ntoa ( const struct peerdist_info *info, const void *hash ) { + static char buf[ ( 2 * PEERDIST_DIGEST_MAX_SIZE ) + 1 /* NUL */ ]; + size_t digestsize = info->digestsize; + + /* Sanity check */ + assert ( info != NULL ); + assert ( digestsize != 0 ); + assert ( base16_encoded_len ( digestsize ) < sizeof ( buf ) ); + + /* Transcribe hash value */ + base16_encode ( hash, digestsize, buf, sizeof ( buf ) ); + return buf; +} + +/** + * Get raw data + * + * @v info Content information + * @v data Data buffer + * @v offset Starting offset + * @v len Length + * @ret rc Return status code + */ +static int peerdist_info_get ( const struct peerdist_info *info, void *data, + size_t offset, size_t len ) { + + /* Sanity check */ + if ( ( offset > info->raw.len ) || + ( len > ( info->raw.len - offset ) ) ) { + DBGC ( info, "PCCRC %p data underrun at [%zx,%zx) of %zx\n", + info, offset, ( offset + len ), info->raw.len ); + return -ERANGE; + } + + /* Copy data */ + copy_from_user ( data, info->raw.data, offset, len ); + + return 0; +} + +/** + * Populate segment hashes + * + * @v segment Content information segment to fill in + * @v hash Segment hash of data + * @v secret Segment secret + */ +static void peerdist_info_segment_hash ( struct peerdist_info_segment *segment, + const void *hash, const void *secret ){ + const struct peerdist_info *info = segment->info; + struct digest_algorithm *digest = info->digest; + uint8_t ctx[digest->ctxsize]; + size_t digestsize = info->digestsize; + size_t secretsize = digestsize; + static const uint16_t magic[] = PEERDIST_SEGMENT_ID_MAGIC; + + /* Sanity check */ + assert ( digestsize <= sizeof ( segment->hash ) ); + assert ( digestsize <= sizeof ( segment->secret ) ); + assert ( digestsize <= sizeof ( segment->id ) ); + + /* Get segment hash of data */ + memcpy ( segment->hash, hash, digestsize ); + + /* Get segment secret */ + memcpy ( segment->secret, secret, digestsize ); + + /* Calculate segment identifier */ + hmac_init ( digest, ctx, segment->secret, &secretsize ); + assert ( secretsize == digestsize ); + hmac_update ( digest, ctx, segment->hash, digestsize ); + hmac_update ( digest, ctx, magic, sizeof ( magic ) ); + hmac_final ( digest, ctx, segment->secret, &secretsize, segment->id ); + assert ( secretsize == digestsize ); +} + +/****************************************************************************** + * + * Content Information version 1 + * + ****************************************************************************** + */ + +/** + * Get number of blocks within a block description + * + * @v info Content information + * @v offset Block description offset + * @ret blocks Number of blocks, or negative error + */ +static int peerdist_info_v1_blocks ( const struct peerdist_info *info, + size_t offset ) { + struct peerdist_info_v1_block raw; + unsigned int blocks; + int rc; + + /* Get block description header */ + if ( ( rc = peerdist_info_get ( info, &raw, offset, + sizeof ( raw ) ) ) != 0 ) + return rc; + + /* Calculate number of blocks */ + blocks = le32_to_cpu ( raw.blocks ); + + return blocks; +} + +/** + * Locate block description + * + * @v info Content information + * @v index Segment index + * @ret offset Block description offset, or negative error + */ +static ssize_t peerdist_info_v1_block_offset ( const struct peerdist_info *info, + unsigned int index ) { + size_t digestsize = info->digestsize; + unsigned int i; + size_t offset; + int blocks; + int rc; + + /* Sanity check */ + assert ( index < info->segments ); + + /* Calculate offset of first block description */ + offset = ( sizeof ( struct peerdist_info_v1 ) + + ( info->segments * + sizeof ( peerdist_info_v1_segment_t ( digestsize ) ) ) ); + + /* Iterate over block descriptions until we find this segment */ + for ( i = 0 ; i < index ; i++ ) { + + /* Get number of blocks */ + blocks = peerdist_info_v1_blocks ( info, offset ); + if ( blocks < 0 ) { + rc = blocks; + DBGC ( info, "PCCRC %p segment %d could not get number " + "of blocks: %s\n", info, i, strerror ( rc ) ); + return rc; + } + + /* Move to next block description */ + offset += sizeof ( peerdist_info_v1_block_t ( digestsize, + blocks ) ); + } + + return offset; +} + +/** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1 ( struct peerdist_info *info ) { + struct peerdist_info_v1 raw; + struct peerdist_info_segment first; + struct peerdist_info_segment last; + size_t first_skip; + size_t last_skip; + size_t last_read; + int rc; + + /* Get raw header */ + if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ + DBGC ( info, "PCCRC %p could not get V1 content information: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V1 ) ); + + /* Determine hash algorithm */ + switch ( raw.hash ) { + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA256 ) : + info->digest = &sha256_algorithm; + break; + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA384 ) : + info->digest = &sha384_algorithm; + break; + case cpu_to_le32 ( PEERDIST_INFO_V1_HASH_SHA512 ) : + info->digest = &sha512_algorithm; + break; + default: + DBGC ( info, "PCCRC %p unsupported hash algorithm %#08x\n", + info, le32_to_cpu ( raw.hash ) ); + return -ENOTSUP; + } + info->digestsize = info->digest->digestsize; + assert ( info->digest != NULL ); + DBGC2 ( info, "PCCRC %p using %s[%zd]\n", + info, info->digest->name, ( info->digestsize * 8 ) ); + + /* Calculate number of segments */ + info->segments = le32_to_cpu ( raw.segments ); + + /* Get first segment */ + if ( ( rc = peerdist_info_segment ( info, &first, 0 ) ) != 0 ) + return rc; + + /* Calculate range start offset */ + info->range.start = first.range.start; + + /* Calculate trimmed range start offset */ + first_skip = le32_to_cpu ( raw.first ); + info->trim.start = ( first.range.start + first_skip ); + + /* Get last segment */ + if ( ( rc = peerdist_info_segment ( info, &last, + ( info->segments - 1 ) ) ) != 0 ) + return rc; + + /* Calculate range end offset */ + info->range.end = last.range.end; + + /* Calculate trimmed range end offset */ + if ( raw.last ) { + /* Explicit length to include from last segment is given */ + last_read = le32_to_cpu ( raw.last ); + last_skip = ( last.index ? 0 : first_skip ); + info->trim.end = ( last.range.start + last_skip + last_read ); + } else { + /* No explicit length given: range extends to end of segment */ + info->trim.end = last.range.end; + } + + return 0; +} + +/** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1_segment ( struct peerdist_info_segment *segment ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v1_segment_t ( digestsize ) raw; + ssize_t raw_offset; + int blocks; + int rc; + + /* Sanity checks */ + assert ( segment->index < info->segments ); + + /* Get raw description */ + raw_offset = ( sizeof ( struct peerdist_info_v1 ) + + ( segment->index * sizeof ( raw ) ) ); + if ( ( rc = peerdist_info_get ( info, &raw, raw_offset, + sizeof ( raw ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not get segment " + "description: %s\n", info, segment->index, + strerror ( rc ) ); + return rc; + } + + /* Calculate start offset of this segment */ + segment->range.start = le64_to_cpu ( raw.segment.offset ); + + /* Calculate end offset of this segment */ + segment->range.end = ( segment->range.start + + le32_to_cpu ( raw.segment.len ) ); + + /* Calculate block size of this segment */ + segment->blksize = le32_to_cpu ( raw.segment.blksize ); + + /* Locate block description for this segment */ + raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); + if ( raw_offset < 0 ) { + rc = raw_offset; + return rc; + } + + /* Get number of blocks */ + blocks = peerdist_info_v1_blocks ( info, raw_offset ); + if ( blocks < 0 ) { + rc = blocks; + DBGC ( info, "PCCRC %p segment %d could not get number of " + "blocks: %s\n", info, segment->index, strerror ( rc ) ); + return rc; + } + segment->blocks = blocks; + + /* Calculate segment hashes */ + peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); + + return 0; +} + +/** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ +static int peerdist_info_v1_block ( struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v1_block_t ( digestsize, segment->blocks ) raw; + ssize_t raw_offset; + int rc; + + /* Sanity checks */ + assert ( block->index < segment->blocks ); + + /* Calculate start offset of this block */ + block->range.start = ( segment->range.start + + ( block->index * segment->blksize ) ); + + /* Calculate end offset of this block */ + block->range.end = ( block->range.start + segment->blksize ); + if ( block->range.end > segment->range.end ) + block->range.end = segment->range.end; + + /* Locate block description */ + raw_offset = peerdist_info_v1_block_offset ( info, segment->index ); + if ( raw_offset < 0 ) { + rc = raw_offset; + return rc; + } + + /* Get block hash */ + raw_offset += offsetof ( typeof ( raw ), hash[block->index] ); + if ( ( rc = peerdist_info_get ( info, block->hash, raw_offset, + digestsize ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d block %d could not get " + "hash: %s\n", info, segment->index, block->index, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** Content information version 1 operations */ +static struct peerdist_info_operations peerdist_info_v1_operations = { + .info = peerdist_info_v1, + .segment = peerdist_info_v1_segment, + .block = peerdist_info_v1_block, +}; + +/****************************************************************************** + * + * Content Information version 2 + * + ****************************************************************************** + */ + +/** A segment cursor */ +struct peerdist_info_v2_cursor { + /** Raw data offset */ + size_t offset; + /** Number of segments remaining within this chunk */ + unsigned int remaining; + /** Accumulated segment length */ + size_t len; +}; + +/** + * Initialise segment cursor + * + * @v cursor Segment cursor + */ +static inline void +peerdist_info_v2_cursor_init ( struct peerdist_info_v2_cursor *cursor ) { + + /* Initialise cursor */ + cursor->offset = ( sizeof ( struct peerdist_info_v2 ) + + sizeof ( struct peerdist_info_v2_chunk ) ); + cursor->remaining = 0; + cursor->len = 0; +} + +/** + * Update segment cursor to next segment description + * + * @v info Content information + * @v offset Current offset + * @v remaining Number of segments remaining within this chunk + * @ret rc Return status code + */ +static int +peerdist_info_v2_cursor_next ( const struct peerdist_info *info, + struct peerdist_info_v2_cursor *cursor ) { + size_t digestsize = info->digestsize; + peerdist_info_v2_segment_t ( digestsize ) raw; + struct peerdist_info_v2_chunk chunk; + int rc; + + /* Get chunk description if applicable */ + if ( ! cursor->remaining ) { + + /* Get chunk description */ + if ( ( rc = peerdist_info_get ( info, &chunk, + ( cursor->offset - + sizeof ( chunk ) ), + sizeof ( chunk ) ) ) != 0 ) + return rc; + + /* Update number of segments remaining */ + cursor->remaining = ( be32_to_cpu ( chunk.len ) / + sizeof ( raw ) ); + } + + /* Get segment description header */ + if ( ( rc = peerdist_info_get ( info, &raw.segment, cursor->offset, + sizeof ( raw.segment ) ) ) != 0 ) + return rc; + + /* Update cursor */ + cursor->offset += sizeof ( raw ); + cursor->remaining--; + if ( ! cursor->remaining ) + cursor->offset += sizeof ( chunk ); + cursor->len += be32_to_cpu ( raw.segment.len ); + + return 0; +} + +/** + * Get number of segments and total length + * + * @v info Content information + * @v len Length to fill in + * @ret rc Number of segments, or negative error + */ +static int peerdist_info_v2_segments ( const struct peerdist_info *info, + size_t *len ) { + struct peerdist_info_v2_cursor cursor; + unsigned int segments; + int rc; + + /* Iterate over all segments */ + for ( peerdist_info_v2_cursor_init ( &cursor ), segments = 0 ; + cursor.offset < info->raw.len ; segments++ ) { + + /* Update segment cursor */ + if ( ( rc = peerdist_info_v2_cursor_next ( info, + &cursor ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not update " + "segment cursor: %s\n", + info, segments, strerror ( rc ) ); + return rc; + } + } + + /* Record accumulated length */ + *len = cursor.len; + + return segments; +} + +/** + * Populate content information + * + * @v info Content information to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2 ( struct peerdist_info *info ) { + struct peerdist_info_v2 raw; + size_t len = 0; + int segments; + int rc; + + /* Get raw header */ + if ( ( rc = peerdist_info_get ( info, &raw, 0, sizeof ( raw ) ) ) != 0){ + DBGC ( info, "PCCRC %p could not get V2 content information: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + assert ( raw.version.raw == cpu_to_le16 ( PEERDIST_INFO_V2 ) ); + + /* Determine hash algorithm */ + switch ( raw.hash ) { + case PEERDIST_INFO_V2_HASH_SHA512_TRUNC : + info->digest = &sha512_algorithm; + info->digestsize = ( 256 / 8 ); + break; + default: + DBGC ( info, "PCCRC %p unsupported hash algorithm %#02x\n", + info, raw.hash ); + return -ENOTSUP; + } + assert ( info->digest != NULL ); + DBGC2 ( info, "PCCRC %p using %s[%zd]\n", + info, info->digest->name, ( info->digestsize * 8 ) ); + + /* Calculate number of segments and total length */ + segments = peerdist_info_v2_segments ( info, &len ); + if ( segments < 0 ) { + rc = segments; + DBGC ( info, "PCCRC %p could not get segment count and length: " + "%s\n", info, strerror ( rc ) ); + return rc; + } + info->segments = segments; + + /* Calculate range start offset */ + info->range.start = be64_to_cpu ( raw.offset ); + + /* Calculate trimmed range start offset */ + info->trim.start = ( info->range.start + be32_to_cpu ( raw.first ) ); + + /* Calculate range end offset */ + info->range.end = ( info->range.start + len ); + + /* Calculate trimmed range end offset */ + info->trim.end = ( raw.len ? be64_to_cpu ( raw.len ) : + info->range.end ); + + return 0; +} + +/** + * Populate content information segment + * + * @v segment Content information segment to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2_segment ( struct peerdist_info_segment *segment ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + peerdist_info_v2_segment_t ( digestsize ) raw; + struct peerdist_info_v2_cursor cursor; + unsigned int index; + size_t len; + int rc; + + /* Sanity checks */ + assert ( segment->index < info->segments ); + + /* Iterate over all segments before the target segment */ + for ( peerdist_info_v2_cursor_init ( &cursor ), index = 0 ; + index < segment->index ; index++ ) { + + /* Update segment cursor */ + if ( ( rc = peerdist_info_v2_cursor_next ( info, + &cursor ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not update " + "segment cursor: %s\n", + info, index, strerror ( rc ) ); + return rc; + } + } + + /* Get raw description */ + if ( ( rc = peerdist_info_get ( info, &raw, cursor.offset, + sizeof ( raw ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p segment %d could not get segment " + "description: %s\n", + info, segment->index, strerror ( rc ) ); + return rc; + } + + /* Calculate start offset of this segment */ + segment->range.start = ( info->range.start + cursor.len ); + + /* Calculate end offset of this segment */ + len = be32_to_cpu ( raw.segment.len ); + segment->range.end = ( segment->range.start + len ); + + /* Model as a segment containing a single block */ + segment->blocks = 1; + segment->blksize = len; + + /* Calculate segment hashes */ + peerdist_info_segment_hash ( segment, raw.hash, raw.secret ); + + return 0; +} + +/** + * Populate content information block + * + * @v block Content information block to fill in + * @ret rc Return status code + */ +static int peerdist_info_v2_block ( struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + + /* Sanity checks */ + assert ( block->index < segment->blocks ); + + /* Model as a block covering the whole segment */ + memcpy ( &block->range, &segment->range, sizeof ( block->range ) ); + memcpy ( block->hash, segment->hash, digestsize ); + + return 0; +} + +/** Content information version 2 operations */ +static struct peerdist_info_operations peerdist_info_v2_operations = { + .block = peerdist_info_v2_block, + .segment = peerdist_info_v2_segment, + .info = peerdist_info_v2, +}; + +/****************************************************************************** + * + * Content Information + * + ****************************************************************************** + */ + +/** + * Populate content information + * + * @v data Raw data + * @v len Length of raw data + * @v info Content information to fill in + * @ret rc Return status code + */ +int peerdist_info ( userptr_t data, size_t len, struct peerdist_info *info ) { + union peerdist_info_version version; + int rc; + + /* Initialise structure */ + memset ( info, 0, sizeof ( *info ) ); + info->raw.data = data; + info->raw.len = len; + + /* Get version */ + if ( ( rc = peerdist_info_get ( info, &version, 0, + sizeof ( version ) ) ) != 0 ) { + DBGC ( info, "PCCRC %p could not get version: %s\n", + info, strerror ( rc ) ); + return rc; + } + DBGC2 ( info, "PCCRC %p version %d.%d\n", + info, version.major, version.minor ); + + /* Determine version */ + switch ( version.raw ) { + case cpu_to_le16 ( PEERDIST_INFO_V1 ) : + info->op = &peerdist_info_v1_operations; + break; + case cpu_to_le16 ( PEERDIST_INFO_V2 ) : + info->op = &peerdist_info_v2_operations; + break; + default: + DBGC ( info, "PCCRC %p unsupported version %d.%d\n", + info, version.major, version.minor ); + return -ENOTSUP; + } + assert ( info->op != NULL ); + assert ( info->op->info != NULL ); + + /* Populate content information */ + if ( ( rc = info->op->info ( info ) ) != 0 ) + return rc; + + DBGC2 ( info, "PCCRC %p range [%08zx,%08zx) covers [%08zx,%08zx) with " + "%d segments\n", info, info->range.start, info->range.end, + info->trim.start, info->trim.end, info->segments ); + return 0; +} + +/** + * Populate content information segment + * + * @v info Content information + * @v segment Content information segment to fill in + * @v index Segment index + * @ret rc Return status code + */ +int peerdist_info_segment ( const struct peerdist_info *info, + struct peerdist_info_segment *segment, + unsigned int index ) { + int rc; + + /* Sanity checks */ + assert ( info != NULL ); + assert ( info->op != NULL ); + assert ( info->op->segment != NULL ); + if ( index >= info->segments ) { + DBGC ( info, "PCCRC %p segment %d of [0,%d) out of range\n", + info, index, info->segments ); + return -ERANGE; + } + + /* Initialise structure */ + memset ( segment, 0, sizeof ( *segment ) ); + segment->info = info; + segment->index = index; + + /* Populate content information segment */ + if ( ( rc = info->op->segment ( segment ) ) != 0 ) + return rc; + + DBGC2 ( info, "PCCRC %p segment %d range [%08zx,%08zx) with %d " + "blocks\n", info, segment->index, segment->range.start, + segment->range.end, segment->blocks ); + DBGC2 ( info, "PCCRC %p segment %d digest %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->hash ) ); + DBGC2 ( info, "PCCRC %p segment %d secret %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->secret ) ); + DBGC2 ( info, "PCCRC %p segment %d identf %s\n", info, segment->index, + peerdist_info_hash_ntoa ( info, segment->id ) ); + return 0; +} + +/** + * Populate content information block + * + * @v segment Content information segment + * @v block Content information block to fill in + * @v index Block index + * @ret rc Return status code + */ +int peerdist_info_block ( const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + unsigned int index ) { + const struct peerdist_info *info = segment->info; + size_t start; + size_t end; + int rc; + + /* Sanity checks */ + assert ( segment != NULL ); + assert ( info != NULL ); + assert ( info->op != NULL ); + assert ( info->op->block != NULL ); + if ( index >= segment->blocks ) { + DBGC ( info, "PCCRC %p segment %d block %d of [0,%d) out of " + "range\n", info, segment->index, index, segment->blocks); + return -ERANGE; + } + + /* Initialise structure */ + memset ( block, 0, sizeof ( *block ) ); + block->segment = segment; + block->index = index; + + /* Populate content information block */ + if ( ( rc = info->op->block ( block ) ) != 0 ) + return rc; + + /* Calculate trimmed range */ + start = block->range.start; + if ( start < info->trim.start ) + start = info->trim.start; + end = block->range.end; + if ( end > info->trim.end ) + end = info->trim.end; + if ( end < start ) + end = start; + block->trim.start = start; + block->trim.end = end; + + DBGC2 ( info, "PCCRC %p segment %d block %d hash %s\n", + info, segment->index, block->index, + peerdist_info_hash_ntoa ( info, block->hash ) ); + DBGC2 ( info, "PCCRC %p segment %d block %d range [%08zx,%08zx) covers " + "[%08zx,%08zx)\n", info, segment->index, block->index, + block->range.start, block->range.end, block->trim.start, + block->trim.end ); + return 0; +} diff --git a/roms/ipxe/src/net/pccrd.c b/roms/ipxe/src/net/pccrd.c new file mode 100644 index 000000000..04b5dd86c --- /dev/null +++ b/roms/ipxe/src/net/pccrd.c @@ -0,0 +1,286 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stddef.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/pccrd.h> + +/** @file + * + * Peer Content Caching and Retrieval: Discovery Protocol [MS-PCCRD] + * + * This protocol manages to ingeniously combine the excessive + * verbosity of XML with a paucity of actual information. For + * example: even in version 2.0 of the protocol it is still not + * possible to discover which peers hold a specific block within a + * given segment. + * + * For added bonus points, version 1.0 of the protocol is specified to + * use a case-sensitive string comparison (for SHA2 digest values) but + * nothing specifies whether the strings in question should be in + * upper or lower case. There are example strings given in the + * specification, but the author skilfully manages to leave the issue + * unresolved by using the somewhat implausible digest value of + * "0200000000000000000000000000000000000000000000000000000000000000". + * + * Just in case you were thinking that the silver lining of the choice + * to use an XML-based protocol would be the ability to generate and + * process messages with standard tools, version 2.0 of the protocol + * places most of the critical information inside a Base64-encoded + * custom binary data structure. Within an XML element, naturally. + * + * I hereby announce this specification to be the 2015 winner of the + * prestigious "UEFI HII API" award for incompetent design. + */ + +/** Discovery request format */ +#define PEERDIST_DISCOVERY_REQUEST \ + "<?xml version=\"1.0\" encoding=\"utf-8\"?>" \ + "<soap:Envelope " \ + "xmlns:soap=\"http://www.w3.org/2003/05/soap-envelope\" " \ + "xmlns:wsa=\"http://schemas.xmlsoap.org/ws/2004/08/addressing\" " \ + "xmlns:wsd=\"http://schemas.xmlsoap.org/ws/2005/04/discovery\" " \ + "xmlns:PeerDist=\"http://schemas.microsoft.com/p2p/" \ + "2007/09/PeerDistributionDiscovery\">" \ + "<soap:Header>" \ + "<wsa:To>" \ + "urn:schemas-xmlsoap-org:ws:2005:04:discovery" \ + "</wsa:To>" \ + "<wsa:Action>" \ + "http://schemas.xmlsoap.org/ws/2005/04/discovery/Probe" \ + "</wsa:Action>" \ + "<wsa:MessageID>" \ + "urn:uuid:%s" \ + "</wsa:MessageID>" \ + "</soap:Header>" \ + "<soap:Body>" \ + "<wsd:Probe>" \ + "<wsd:Types>" \ + "PeerDist:PeerDistData" \ + "</wsd:Types>" \ + "<wsd:Scopes MatchBy=\"http://schemas.xmlsoap.org/ws/" \ + "2005/04/discovery/strcmp0\">" \ + "%s" \ + "</wsd:Scopes>" \ + "</wsd:Probe>" \ + "</soap:Body>" \ + "</soap:Envelope>" + +/** + * Construct discovery request + * + * @v uuid Message UUID string + * @v id Segment identifier string + * @ret request Discovery request, or NULL on failure + * + * The request is dynamically allocated; the caller must eventually + * free() the request. + */ +char * peerdist_discovery_request ( const char *uuid, const char *id ) { + char *request; + int len; + + /* Construct request */ + len = asprintf ( &request, PEERDIST_DISCOVERY_REQUEST, uuid, id ); + if ( len < 0 ) + return NULL; + + return request; +} + +/** + * Locate discovery reply tag + * + * @v data Reply data (not NUL-terminated) + * @v len Length of reply data + * @v tag XML tag + * @ret found Found tag (or NULL if not found) + */ +static char * peerdist_discovery_reply_tag ( char *data, size_t len, + const char *tag ) { + size_t tag_len = strlen ( tag ); + + /* Search, allowing for the fact that the reply data is not + * cleanly NUL-terminated and may contain embedded NULs due to + * earlier parsing. + */ + for ( ; len >= tag_len ; data++, len-- ) { + if ( strncmp ( data, tag, tag_len ) == 0 ) + return data; + } + return NULL; +} + +/** + * Locate discovery reply values + * + * @v data Reply data (not NUL-terminated, will be modified) + * @v len Length of reply data + * @v name XML tag name + * @ret values Tag values (or NULL if not found) + * + * The reply data is modified by adding NULs and moving characters as + * needed to produce a NUL-separated list of values, terminated with a + * zero-length string. + * + * This is not supposed to be a full XML parser; it's supposed to + * include just enough functionality to allow PeerDist discovery to + * work with existing implementations. + */ +static char * peerdist_discovery_reply_values ( char *data, size_t len, + const char *name ) { + char buf[ 2 /* "</" */ + strlen ( name ) + 1 /* ">" */ + 1 /* NUL */ ]; + char *open; + char *close; + char *start; + char *end; + char *in; + char *out; + char c; + + /* Locate opening tag */ + snprintf ( buf, sizeof ( buf ), "<%s>", name ); + open = peerdist_discovery_reply_tag ( data, len, buf ); + if ( ! open ) + return NULL; + start = ( open + strlen ( buf ) ); + len -= ( start - data ); + data = start; + + /* Locate closing tag */ + snprintf ( buf, sizeof ( buf ), "</%s>", name ); + close = peerdist_discovery_reply_tag ( data, len, buf ); + if ( ! close ) + return NULL; + assert ( close >= open ); + end = close; + + /* Strip initial whitespace, convert other whitespace + * sequences to single NULs, add terminating pair of NULs. + * This will probably overwrite part of the closing tag. + */ + for ( in = start, out = start ; in < end ; in++ ) { + c = *in; + if ( isspace ( c ) ) { + if ( ( out > start ) && ( out[-1] != '\0' ) ) + *(out++) = '\0'; + } else { + *(out++) = c; + } + } + *(out++) = '\0'; + *(out++) = '\0'; + assert ( out < ( close + strlen ( buf ) ) ); + + return start; +} + +/** + * Parse discovery reply + * + * @v data Reply data (not NUL-terminated, will be modified) + * @v len Length of reply data + * @v reply Discovery reply to fill in + * @ret rc Return status code + * + * The discovery reply includes pointers to strings within the + * modified reply data. + */ +int peerdist_discovery_reply ( char *data, size_t len, + struct peerdist_discovery_reply *reply ) { + static const struct peerdist_discovery_block_count zcount = { + .hex = "00000000", + }; + struct peerdist_discovery_block_count *count; + unsigned int max; + unsigned int i; + char *scopes; + char *xaddrs; + char *blockcount; + char *in; + char *out; + size_t skip; + + /* Find <wsd:Scopes> tag */ + scopes = peerdist_discovery_reply_values ( data, len, "wsd:Scopes" ); + if ( ! scopes ) { + DBGC ( reply, "PCCRD %p missing <wsd:Scopes> tag\n", reply ); + return -ENOENT; + } + + /* Find <wsd:XAddrs> tag */ + xaddrs = peerdist_discovery_reply_values ( data, len, "wsd:XAddrs" ); + if ( ! xaddrs ) { + DBGC ( reply, "PCCRD %p missing <wsd:XAddrs> tag\n", reply ); + return -ENOENT; + } + + /* Find <PeerDist:BlockCount> tag */ + blockcount = peerdist_discovery_reply_values ( data, len, + "PeerDist:BlockCount" ); + if ( ! blockcount ) { + DBGC ( reply, "PCCRD %p missing <PeerDist:BlockCount> tag\n", + reply ); + return -ENOENT; + } + + /* Determine maximum number of segments (according to number + * of entries in the block count list). + */ + max = ( strlen ( blockcount ) / sizeof ( *count ) ); + count = container_of ( blockcount, + struct peerdist_discovery_block_count, hex[0] ); + + /* Eliminate any segments with a zero block count */ + for ( i = 0, in = scopes, out = scopes ; *in ; i++, in += skip ) { + + /* Fail if we have overrun the maximum number of segments */ + if ( i >= max ) { + DBGC ( reply, "PCCRD %p too many segment IDs\n", + reply ); + return -EPROTO; + } + + /* Delete segment if block count is zero */ + skip = ( strlen ( in ) + 1 /* NUL */ ); + if ( memcmp ( count[i].hex, zcount.hex, + sizeof ( zcount.hex ) ) == 0 ) + continue; + strcpy ( out, in ); + out += skip; + } + out[0] = '\0'; /* Ensure list is terminated with a zero-length string */ + + /* Fill in discovery reply */ + reply->ids = scopes; + reply->locations = xaddrs; + + return 0; +} diff --git a/roms/ipxe/src/net/peerblk.c b/roms/ipxe/src/net/peerblk.c new file mode 100644 index 000000000..fd7ea0893 --- /dev/null +++ b/roms/ipxe/src/net/peerblk.c @@ -0,0 +1,1366 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <ipxe/http.h> +#include <ipxe/iobuf.h> +#include <ipxe/xfer.h> +#include <ipxe/uri.h> +#include <ipxe/timer.h> +#include <ipxe/profile.h> +#include <ipxe/fault.h> +#include <ipxe/pccrr.h> +#include <ipxe/peerblk.h> + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol block downloads + * + */ + +/** PeerDist decryption chunksize + * + * This is a policy decision. + */ +#define PEERBLK_DECRYPT_CHUNKSIZE 2048 + +/** PeerDist raw block download attempt initial progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RAW_OPEN_TIMEOUT ( 10 * TICKS_PER_SEC ) + +/** PeerDist raw block download attempt ongoing progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RAW_RX_TIMEOUT ( 15 * TICKS_PER_SEC ) + +/** PeerDist retrieval protocol block download attempt initial progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RETRIEVAL_OPEN_TIMEOUT ( 3 * TICKS_PER_SEC ) + +/** PeerDist retrieval protocol block download attempt ongoing progress timeout + * + * This is a policy decision. + */ +#define PEERBLK_RETRIEVAL_RX_TIMEOUT ( 5 * TICKS_PER_SEC ) + +/** PeerDist maximum number of full download attempt cycles + * + * This is the maximum number of times that we will try a full cycle + * of download attempts (i.e. a retrieval protocol download attempt + * from each discovered peer plus a raw download attempt from the + * origin server). + * + * This is a policy decision. + */ +#define PEERBLK_MAX_ATTEMPT_CYCLES 4 + +/** PeerDist block download profiler */ +static struct profiler peerblk_download_profiler __profiler = + { .name = "peerblk.download" }; + +/** PeerDist block download attempt success profiler */ +static struct profiler peerblk_attempt_success_profiler __profiler = + { .name = "peerblk.attempt.success" }; + +/** PeerDist block download attempt failure profiler */ +static struct profiler peerblk_attempt_failure_profiler __profiler = + { .name = "peerblk.attempt.failure" }; + +/** PeerDist block download attempt timeout profiler */ +static struct profiler peerblk_attempt_timeout_profiler __profiler = + { .name = "peerblk.attempt.timeout" }; + +/** PeerDist block download discovery success profiler */ +static struct profiler peerblk_discovery_success_profiler __profiler = + { .name = "peerblk.discovery.success" }; + +/** PeerDist block download discovery timeout profiler */ +static struct profiler peerblk_discovery_timeout_profiler __profiler = + { .name = "peerblk.discovery.timeout" }; + +/** + * Get profiling timestamp + * + * @ret timestamp Timestamp + */ +static inline __attribute__ (( always_inline )) unsigned long +peerblk_timestamp ( void ) { + + if ( PROFILING ) { + return currticks(); + } else { + return 0; + } +} + +/** + * Free PeerDist block download + * + * @v refcnt Reference count + */ +static void peerblk_free ( struct refcnt *refcnt ) { + struct peerdist_block *peerblk = + container_of ( refcnt, struct peerdist_block, refcnt ); + + uri_put ( peerblk->uri ); + free ( peerblk->cipherctx ); + free ( peerblk ); +} + +/** + * Reset PeerDist block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for reset + */ +static void peerblk_reset ( struct peerdist_block *peerblk, int rc ) { + + /* Stop decryption process */ + process_del ( &peerblk->process ); + + /* Stop timer */ + stop_timer ( &peerblk->timer ); + + /* Abort any current download attempt */ + intf_restart ( &peerblk->raw, rc ); + intf_restart ( &peerblk->retrieval, rc ); + + /* Empty received data buffer */ + xferbuf_free ( &peerblk->buffer ); + peerblk->pos = 0; + + /* Reset digest and free cipher context */ + digest_init ( peerblk->digest, peerblk->digestctx ); + free ( peerblk->cipherctx ); + peerblk->cipherctx = NULL; + peerblk->cipher = NULL; + + /* Reset trim thresholds */ + peerblk->start = ( peerblk->trim.start - peerblk->range.start ); + peerblk->end = ( peerblk->trim.end - peerblk->range.start ); + assert ( peerblk->start <= peerblk->end ); +} + +/** + * Close PeerDist block download + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_close ( struct peerdist_block *peerblk, int rc ) { + unsigned long now = peerblk_timestamp(); + + /* Profile overall block download */ + profile_custom ( &peerblk_download_profiler, + ( now - peerblk->started ) ); + + /* Reset download attempt */ + peerblk_reset ( peerblk, rc ); + + /* Close discovery */ + peerdisc_close ( &peerblk->discovery ); + + /* Shut down all interfaces */ + intf_shutdown ( &peerblk->retrieval, rc ); + intf_shutdown ( &peerblk->raw, rc ); + intf_shutdown ( &peerblk->xfer, rc ); +} + +/** + * Calculate offset within overall download + * + * @v peerblk PeerDist block download + * @v pos Position within incoming data stream + * @ret offset Offset within overall download + */ +static inline __attribute__ (( always_inline )) size_t +peerblk_offset ( struct peerdist_block *peerblk, size_t pos ) { + + return ( ( pos - peerblk->start ) + peerblk->offset ); +} + +/** + * Deliver download attempt data block + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Original data transfer metadata + * @v pos Position within incoming data stream + * @ret rc Return status code + */ +static int peerblk_deliver ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta, size_t pos ) { + struct xfer_metadata xfer_meta; + size_t len = iob_len ( iobuf ); + size_t start = pos; + size_t end = ( pos + len ); + int rc; + + /* Discard zero-length packets and packets which lie entirely + * outside the trimmed range. + */ + if ( ( start >= peerblk->end ) || ( end <= peerblk->start ) || + ( len == 0 ) ) { + free_iob ( iobuf ); + return 0; + } + + /* Truncate data to within trimmed range */ + if ( start < peerblk->start ) { + iob_pull ( iobuf, ( peerblk->start - start ) ); + start = peerblk->start; + } + if ( end > peerblk->end ) { + iob_unput ( iobuf, ( end - peerblk->end ) ); + end = peerblk->end; + } + + /* Construct metadata */ + memcpy ( &xfer_meta, meta, sizeof ( xfer_meta ) ); + xfer_meta.flags |= XFER_FL_ABS_OFFSET; + xfer_meta.offset = peerblk_offset ( peerblk, start ); + + /* Deliver data */ + if ( ( rc = xfer_deliver ( &peerblk->xfer, iob_disown ( iobuf ), + &xfer_meta ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not deliver data: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Finish PeerDist block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_done ( struct peerdist_block *peerblk, int rc ) { + struct digest_algorithm *digest = peerblk->digest; + uint8_t hash[digest->digestsize]; + unsigned long now = peerblk_timestamp(); + + /* Check for errors on completion */ + if ( rc != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d attempt failed: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err; + } + + /* Check digest */ + digest_final ( digest, peerblk->digestctx, hash ); + if ( memcmp ( hash, peerblk->hash, peerblk->digestsize ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d digest mismatch:\n", + peerblk, peerblk->segment, peerblk->block ); + DBGC_HDA ( peerblk, 0, hash, peerblk->digestsize ); + DBGC_HDA ( peerblk, 0, peerblk->hash, peerblk->digestsize ); + rc = -EIO; + goto err; + } + + /* Profile successful attempt */ + profile_custom ( &peerblk_attempt_success_profiler, + ( now - peerblk->attempted ) ); + + /* Close download */ + peerblk_close ( peerblk, 0 ); + return; + + err: + /* Record failure reason and schedule a retry attempt */ + profile_custom ( &peerblk_attempt_failure_profiler, + ( now - peerblk->attempted ) ); + peerblk_reset ( peerblk, rc ); + peerblk->rc = rc; + start_timer_nodelay ( &peerblk->timer ); +} + +/****************************************************************************** + * + * Raw block download attempts (using an HTTP range request) + * + ****************************************************************************** + */ + +/** + * Open PeerDist raw block download attempt + * + * @v peerblk PeerDist block download + * @ret rc Return status code + */ +static int peerblk_raw_open ( struct peerdist_block *peerblk ) { + struct http_request_range range; + int rc; + + DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting raw range request\n", + peerblk, peerblk->segment, peerblk->block ); + + /* Construct HTTP range */ + memset ( &range, 0, sizeof ( range ) ); + range.start = peerblk->range.start; + range.len = ( peerblk->range.end - peerblk->range.start ); + + /* Initiate range request to retrieve block */ + if ( ( rc = http_open ( &peerblk->raw, &http_get, peerblk->uri, + &range, NULL ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not create range " + "request: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + return rc; + } + + /* Annul HTTP connection (for testing) if applicable. Do not + * report as an immediate error, in order to test our ability + * to recover from a totally unresponsive HTTP server. + */ + if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) + intf_restart ( &peerblk->raw, 0 ); + + return 0; +} + +/** + * Receive PeerDist raw data + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerblk_raw_rx ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + size_t pos = peerblk->pos; + size_t mid = ( ( peerblk->range.end - peerblk->range.start ) / 2 ); + int rc; + + /* Corrupt received data (for testing) if applicable */ + inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); + + /* Fail if data is delivered out of order, since the streaming + * digest requires strict ordering. + */ + if ( ( rc = xfer_check_order ( meta, &peerblk->pos, len ) ) != 0 ) + goto err; + + /* Add data to digest */ + digest_update ( peerblk->digest, peerblk->digestctx, iobuf->data, len ); + + /* Deliver data */ + if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, + pos ) ) != 0 ) + goto err; + + /* Extend download attempt timer */ + start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_RX_TIMEOUT ); + + /* Stall download attempt (for testing) if applicable */ + if ( ( pos < mid ) && ( ( pos + len ) >= mid ) && + ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { + intf_restart ( &peerblk->raw, rc ); + } + + return 0; + + err: + free_iob ( iobuf ); + peerblk_done ( peerblk, rc ); + return rc; +} + +/** + * Close PeerDist raw block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_raw_close ( struct peerdist_block *peerblk, int rc ) { + + /* Restart interface */ + intf_restart ( &peerblk->raw, rc ); + + /* Fail immediately if we have an error */ + if ( rc != 0 ) + goto done; + + /* Abort download attempt (for testing) if applicable */ + if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) + goto done; + + done: + /* Complete download attempt */ + peerblk_done ( peerblk, rc ); +} + +/****************************************************************************** + * + * Retrieval protocol block download attempts (using HTTP POST) + * + ****************************************************************************** + */ + +/** + * Construct PeerDist retrieval protocol URI + * + * @v location Peer location + * @ret uri Retrieval URI, or NULL on error + */ +static struct uri * peerblk_retrieval_uri ( const char *location ) { + char uri_string[ 7 /* "http://" */ + strlen ( location ) + + sizeof ( PEERDIST_MAGIC_PATH /* includes NUL */ ) ]; + + /* Construct URI string */ + snprintf ( uri_string, sizeof ( uri_string ), + ( "http://%s" PEERDIST_MAGIC_PATH ), location ); + + /* Parse URI string */ + return parse_uri ( uri_string ); +} + +/** + * Open PeerDist retrieval protocol block download attempt + * + * @v peerblk PeerDist block download + * @v location Peer location + * @ret rc Return status code + */ +static int peerblk_retrieval_open ( struct peerdist_block *peerblk, + const char *location ) { + size_t digestsize = peerblk->digestsize; + peerdist_msg_getblks_t ( digestsize, 1, 0 ) req; + peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *rsp; + struct http_request_content content; + struct uri *uri; + int rc; + + DBGC2 ( peerblk, "PEERBLK %p %d.%d attempting retrieval from %s\n", + peerblk, peerblk->segment, peerblk->block, location ); + + /* Construct block fetch request */ + memset ( &req, 0, sizeof ( req ) ); + req.getblks.hdr.version.raw = htonl ( PEERDIST_MSG_GETBLKS_VERSION ); + req.getblks.hdr.type = htonl ( PEERDIST_MSG_GETBLKS_TYPE ); + req.getblks.hdr.len = htonl ( sizeof ( req ) ); + req.getblks.hdr.algorithm = htonl ( PEERDIST_MSG_AES_128_CBC ); + req.segment.segment.digestsize = htonl ( digestsize ); + memcpy ( req.segment.id, peerblk->id, digestsize ); + req.ranges.ranges.count = htonl ( 1 ); + req.ranges.range[0].first = htonl ( peerblk->block ); + req.ranges.range[0].count = htonl ( 1 ); + + /* Construct POST request content */ + memset ( &content, 0, sizeof ( content ) ); + content.data = &req; + content.len = sizeof ( req ); + + /* Construct URI */ + if ( ( uri = peerblk_retrieval_uri ( location ) ) == NULL ) { + rc = -ENOMEM; + goto err_uri; + } + + /* Update trim thresholds */ + peerblk->start += offsetof ( typeof ( *rsp ), msg.vrf ); + peerblk->end += offsetof ( typeof ( *rsp ), msg.vrf ); + + /* Initiate HTTP POST to retrieve block */ + if ( ( rc = http_open ( &peerblk->retrieval, &http_post, uri, + NULL, &content ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not create retrieval " + "request: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err_open; + } + + /* Annul HTTP connection (for testing) if applicable. Do not + * report as an immediate error, in order to test our ability + * to recover from a totally unresponsive HTTP server. + */ + if ( inject_fault ( PEERBLK_ANNUL_RATE ) ) + intf_restart ( &peerblk->retrieval, 0 ); + + err_open: + uri_put ( uri ); + err_uri: + return rc; +} + +/** + * Receive PeerDist retrieval protocol data + * + * @v peerblk PeerDist block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerblk_retrieval_rx ( struct peerdist_block *peerblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + size_t len = iob_len ( iobuf ); + size_t start; + size_t end; + size_t before; + size_t after; + size_t cut; + int rc; + + /* Some genius at Microsoft thought it would be a great idea + * to place the AES-CBC initialisation vector *after* the + * encrypted data, thereby making it logically impossible to + * decrypt each packet as it arrives. + * + * To work around this mindless stupidity, we deliver the + * ciphertext as-is and later use xfer_buffer() to obtain + * access to the underlying data transfer buffer in order to + * perform the decryption. + * + * There will be some data both before and after the bytes + * corresponding to the trimmed plaintext: a MSG_BLK + * header/footer, some block padding for the AES-CBC cipher, + * and a possibly large quantity of unwanted ciphertext which + * is excluded from the trimmed content range. We store this + * data in a local data transfer buffer. If the amount of + * data to be stored is too large, we will fail allocation and + * so eventually fall back to using a range request (which + * does not require this kind of temporary storage + * allocation). + */ + + /* Corrupt received data (for testing) if applicable */ + inject_corruption ( PEERBLK_CORRUPT_RATE, iobuf->data, len ); + + /* Calculate start and end positions of this buffer */ + start = peerblk->pos; + if ( meta->flags & XFER_FL_ABS_OFFSET ) + start = 0; + start += meta->offset; + end = ( start + len ); + + /* Buffer any data before the trimmed content */ + if ( ( start < peerblk->start ) && ( len > 0 ) ) { + + /* Calculate length of data before the trimmed content */ + before = ( peerblk->start - start ); + if ( before > len ) + before = len; + + /* Buffer data before the trimmed content */ + if ( ( rc = xferbuf_write ( &peerblk->buffer, start, + iobuf->data, before ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " + "data: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err; + } + } + + /* Buffer any data after the trimmed content */ + if ( ( end > peerblk->end ) && ( len > 0 ) ) { + + /* Calculate length of data after the trimmed content */ + after = ( end - peerblk->end ); + if ( after > len ) + after = len; + + /* Buffer data after the trimmed content */ + cut = ( peerblk->end - peerblk->start ); + if ( ( rc = xferbuf_write ( &peerblk->buffer, + ( end - after - cut ), + ( iobuf->data + len - after ), + after ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not buffer " + "data: %s\n", peerblk, peerblk->segment, + peerblk->block, strerror ( rc ) ); + goto err; + } + } + + /* Deliver any remaining data */ + if ( ( rc = peerblk_deliver ( peerblk, iob_disown ( iobuf ), meta, + start ) ) != 0 ) + goto err; + + /* Update position */ + peerblk->pos = end; + + /* Extend download attempt timer */ + start_timer_fixed ( &peerblk->timer, PEERBLK_RETRIEVAL_RX_TIMEOUT ); + + /* Stall download attempt (for testing) if applicable */ + if ( ( start < peerblk->end ) && ( end >= peerblk->end ) && + ( ( rc = inject_fault ( PEERBLK_STALL_RATE ) ) != 0 ) ) { + intf_restart ( &peerblk->retrieval, rc ); + } + + return 0; + + err: + free_iob ( iobuf ); + peerblk_done ( peerblk, rc ); + return rc; +} + +/** + * Parse retrieval protocol message header + * + * @v peerblk PeerDist block download + * @ret rc Return status code + */ +static int peerblk_parse_header ( struct peerdist_block *peerblk ) { + struct { + struct peerdist_msg_transport_header hdr; + struct peerdist_msg_header msg; + } __attribute__ (( packed )) *msg = peerblk->buffer.data; + struct cipher_algorithm *cipher; + size_t len = peerblk->buffer.len; + size_t keylen = 0; + int rc; + + /* Check message length */ + if ( len < sizeof ( *msg ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for header " + "(%zd bytes)\n", peerblk, peerblk->segment, + peerblk->block, len ); + return -ERANGE; + } + + /* Check message type */ + if ( msg->msg.type != htonl ( PEERDIST_MSG_BLK_TYPE ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d unexpected message type " + "%#08x\n", peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.type ) ); + return -EPROTO; + } + + /* Determine cipher algorithm and key length */ + cipher = &aes_cbc_algorithm; + switch ( msg->msg.algorithm ) { + case htonl ( PEERDIST_MSG_PLAINTEXT ) : + cipher = NULL; + break; + case htonl ( PEERDIST_MSG_AES_128_CBC ) : + keylen = ( 128 / 8 ); + break; + case htonl ( PEERDIST_MSG_AES_192_CBC ) : + keylen = ( 192 / 8 ); + break; + case htonl ( PEERDIST_MSG_AES_256_CBC ) : + keylen = ( 256 / 8 ); + break; + default: + DBGC ( peerblk, "PEERBLK %p %d.%d unrecognised algorithm " + "%#08x\n", peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.algorithm ) ); + return -ENOTSUP; + } + DBGC2 ( peerblk, "PEERBLK %p %d.%d using %s with %zd-bit key\n", + peerblk, peerblk->segment, peerblk->block, + ( cipher ? cipher->name : "plaintext" ), ( 8 * keylen ) ); + + /* Sanity check key length against maximum secret length */ + if ( keylen > peerblk->digestsize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d %zd-byte secret too short " + "for %zd-bit key\n", peerblk, peerblk->segment, + peerblk->block, peerblk->digestsize, ( 8 * keylen ) ); + return -EPROTO; + } + + /* Allocate cipher context. Freeing the cipher context (on + * error or otherwise) is handled by peerblk_reset(). + */ + peerblk->cipher = cipher; + assert ( peerblk->cipherctx == NULL ); + peerblk->cipherctx = malloc ( cipher->ctxsize ); + if ( ! peerblk->cipherctx ) + return -ENOMEM; + + /* Initialise cipher */ + if ( ( rc = cipher_setkey ( cipher, peerblk->cipherctx, peerblk->secret, + keylen ) ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d could not set key: %s\n", + peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Parse retrieval protocol message segment and block details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data to fill in + * @ret rc Return status code + */ +static int peerblk_parse_block ( struct peerdist_block *peerblk, + size_t *buf_len ) { + size_t digestsize = peerblk->digestsize; + peerblk_msg_blk_t ( digestsize, 0, 0, 0 ) *msg = peerblk->buffer.data; + size_t len = peerblk->buffer.len; + size_t data_len; + size_t total; + + /* Check message length */ + if ( len < offsetof ( typeof ( *msg ), msg.block.data ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "zero-length data (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Check digest size */ + if ( ntohl ( msg->msg.segment.segment.digestsize ) != digestsize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d incorrect digest size %d\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.segment.segment.digestsize ) ); + return -EPROTO; + } + + /* Check segment ID */ + if ( memcmp ( msg->msg.segment.id, peerblk->id, digestsize ) != 0 ) { + DBGC ( peerblk, "PEERBLK %p %d.%d segment ID mismatch\n", + peerblk, peerblk->segment, peerblk->block ); + return -EPROTO; + } + + /* Check block ID */ + if ( ntohl ( msg->msg.index ) != peerblk->block ) { + DBGC ( peerblk, "PEERBLK %p %d.%d block ID mismatch (got %d)\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.index ) ); + return -EPROTO; + } + + /* Check for missing blocks */ + data_len = be32_to_cpu ( msg->msg.block.block.len ); + if ( ! data_len ) { + DBGC ( peerblk, "PEERBLK %p %d.%d block not found\n", + peerblk, peerblk->segment, peerblk->block ); + return -ENOENT; + } + + /* Check for underlength blocks */ + if ( data_len < ( peerblk->range.end - peerblk->range.start ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d underlength block (%zd " + "bytes)\n", peerblk, peerblk->segment, peerblk->block, + data_len ); + return -ERANGE; + } + + /* Calculate buffered data length (i.e. excluding data which + * was delivered to the final data transfer buffer). + */ + *buf_len = ( data_len - ( peerblk->end - peerblk->start ) ); + + /* Describe data before the trimmed content */ + peerblk->decrypt[PEERBLK_BEFORE].xferbuf = &peerblk->buffer; + peerblk->decrypt[PEERBLK_BEFORE].offset = + offsetof ( typeof ( *msg ), msg.block.data ); + peerblk->decrypt[PEERBLK_BEFORE].len = + ( peerblk->start - + offsetof ( typeof ( *msg ), msg.block.data ) ); + total = peerblk->decrypt[PEERBLK_BEFORE].len; + + /* Describe data within the trimmed content */ + peerblk->decrypt[PEERBLK_DURING].offset = + peerblk_offset ( peerblk, peerblk->start ); + peerblk->decrypt[PEERBLK_DURING].len = + ( peerblk->end - peerblk->start ); + total += peerblk->decrypt[PEERBLK_DURING].len; + + /* Describe data after the trimmed content */ + peerblk->decrypt[PEERBLK_AFTER].xferbuf = &peerblk->buffer; + peerblk->decrypt[PEERBLK_AFTER].offset = peerblk->start; + peerblk->decrypt[PEERBLK_AFTER].len = + ( offsetof ( typeof ( *msg ), msg.block.data ) + + *buf_len - peerblk->start ); + total += peerblk->decrypt[PEERBLK_AFTER].len; + + /* Sanity check */ + assert ( total == be32_to_cpu ( msg->msg.block.block.len ) ); + + /* Initialise cipher and digest lengths */ + peerblk->cipher_remaining = total; + peerblk->digest_remaining = + ( peerblk->range.end - peerblk->range.start ); + assert ( peerblk->cipher_remaining >= peerblk->digest_remaining ); + + return 0; +} + +/** + * Parse retrieval protocol message useless details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data + * @v vrf_len Length of uselessness to fill in + * @ret rc Return status code + */ +static int peerblk_parse_useless ( struct peerdist_block *peerblk, + size_t buf_len, size_t *vrf_len ) { + size_t digestsize = peerblk->digestsize; + peerblk_msg_blk_t ( digestsize, buf_len, 0, 0 ) *msg = + peerblk->buffer.data; + size_t len = peerblk->buffer.len; + + /* Check message length */ + if ( len < offsetof ( typeof ( *msg ), msg.vrf.data ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "zero-length uselessness (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Extract length of uselessness */ + *vrf_len = be32_to_cpu ( msg->msg.vrf.vrf.len ); + + return 0; +} + +/** + * Parse retrieval protocol message initialisation vector details + * + * @v peerblk PeerDist block download + * @v buf_len Length of buffered data + * @v vrf_len Length of uselessness + * @ret rc Return status code + */ +static int peerblk_parse_iv ( struct peerdist_block *peerblk, size_t buf_len, + size_t vrf_len ) { + size_t digestsize = peerblk->digestsize; + size_t blksize = peerblk->cipher->blocksize; + peerblk_msg_blk_t ( digestsize, buf_len, vrf_len, blksize ) *msg = + peerblk->buffer.data; + size_t len = peerblk->buffer.len; + + /* Check message length */ + if ( len < sizeof ( *msg ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d message too short for " + "initialisation vector (%zd bytes)\n", peerblk, + peerblk->segment, peerblk->block, len ); + return -ERANGE; + } + + /* Check initialisation vector size */ + if ( ntohl ( msg->msg.iv.iv.blksize ) != blksize ) { + DBGC ( peerblk, "PEERBLK %p %d.%d incorrect IV size %d\n", + peerblk, peerblk->segment, peerblk->block, + ntohl ( msg->msg.iv.iv.blksize ) ); + return -EPROTO; + } + + /* Set initialisation vector */ + cipher_setiv ( peerblk->cipher, peerblk->cipherctx, msg->msg.iv.data ); + + return 0; +} + +/** + * Read from decryption buffers + * + * @v peerblk PeerDist block download + * @v data Data buffer + * @v len Length to read + * @ret rc Return status code + */ +static int peerblk_decrypt_read ( struct peerdist_block *peerblk, + void *data, size_t len ) { + struct peerdist_block_decrypt *decrypt = peerblk->decrypt; + size_t frag_len; + int rc; + + /* Read from each decryption buffer in turn */ + for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { + + /* Calculate length to use from this buffer */ + frag_len = decrypt->len; + if ( frag_len > len ) + frag_len = len; + if ( ! frag_len ) + continue; + + /* Read from this buffer */ + if ( ( rc = xferbuf_read ( decrypt->xferbuf, decrypt->offset, + data, frag_len ) ) != 0 ) + return rc; + } + + return 0; +} + +/** + * Write to decryption buffers and update offsets and lengths + * + * @v peerblk PeerDist block download + * @v data Data buffer + * @v len Length to read + * @ret rc Return status code + */ +static int peerblk_decrypt_write ( struct peerdist_block *peerblk, + const void *data, size_t len ) { + struct peerdist_block_decrypt *decrypt = peerblk->decrypt; + size_t frag_len; + int rc; + + /* Write to each decryption buffer in turn */ + for ( ; len ; decrypt++, data += frag_len, len -= frag_len ) { + + /* Calculate length to use from this buffer */ + frag_len = decrypt->len; + if ( frag_len > len ) + frag_len = len; + if ( ! frag_len ) + continue; + + /* Write to this buffer */ + if ( ( rc = xferbuf_write ( decrypt->xferbuf, decrypt->offset, + data, frag_len ) ) != 0 ) + return rc; + + /* Update offset and length */ + decrypt->offset += frag_len; + decrypt->len -= frag_len; + } + + return 0; +} + +/** + * Decrypt one chunk of PeerDist retrieval protocol data + * + * @v peerblk PeerDist block download + */ +static void peerblk_decrypt ( struct peerdist_block *peerblk ) { + struct cipher_algorithm *cipher = peerblk->cipher; + struct digest_algorithm *digest = peerblk->digest; + struct xfer_buffer *xferbuf; + size_t cipher_len; + size_t digest_len; + void *data; + int rc; + + /* Sanity check */ + assert ( ( PEERBLK_DECRYPT_CHUNKSIZE % cipher->blocksize ) == 0 ); + + /* Get the underlying data transfer buffer */ + xferbuf = xfer_buffer ( &peerblk->xfer ); + if ( ! xferbuf ) { + DBGC ( peerblk, "PEERBLK %p %d.%d has no underlying data " + "transfer buffer\n", peerblk, peerblk->segment, + peerblk->block ); + rc = -ENOTSUP; + goto err_xfer_buffer; + } + peerblk->decrypt[PEERBLK_DURING].xferbuf = xferbuf; + + /* Calculate cipher and digest lengths */ + cipher_len = PEERBLK_DECRYPT_CHUNKSIZE; + if ( cipher_len > peerblk->cipher_remaining ) + cipher_len = peerblk->cipher_remaining; + digest_len = cipher_len; + if ( digest_len > peerblk->digest_remaining ) + digest_len = peerblk->digest_remaining; + assert ( ( cipher_len & ( cipher->blocksize - 1 ) ) == 0 ); + + /* Allocate temporary data buffer */ + data = malloc ( cipher_len ); + if ( ! data ) { + rc = -ENOMEM; + goto err_alloc_data; + } + + /* Read ciphertext */ + if ( ( rc = peerblk_decrypt_read ( peerblk, data, cipher_len ) ) != 0 ){ + DBGC ( peerblk, "PEERBLK %p %d.%d could not read ciphertext: " + "%s\n", peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err_read; + } + + /* Decrypt data */ + cipher_decrypt ( cipher, peerblk->cipherctx, data, data, cipher_len ); + + /* Add data to digest */ + digest_update ( digest, peerblk->digestctx, data, digest_len ); + + /* Write plaintext */ + if ( ( rc = peerblk_decrypt_write ( peerblk, data, cipher_len ) ) != 0){ + DBGC ( peerblk, "PEERBLK %p %d.%d could not write plaintext: " + "%s\n", peerblk, peerblk->segment, peerblk->block, + strerror ( rc ) ); + goto err_write; + } + + /* Consume input */ + peerblk->cipher_remaining -= cipher_len; + peerblk->digest_remaining -= digest_len; + + /* Free temporary data buffer */ + free ( data ); + + /* Continue processing until all input is consumed */ + if ( peerblk->cipher_remaining ) + return; + + /* Complete download attempt */ + peerblk_done ( peerblk, 0 ); + return; + + err_write: + err_read: + free ( data ); + err_alloc_data: + err_xfer_buffer: + peerblk_done ( peerblk, rc ); +} + +/** + * Close PeerDist retrieval protocol block download attempt + * + * @v peerblk PeerDist block download + * @v rc Reason for close + */ +static void peerblk_retrieval_close ( struct peerdist_block *peerblk, int rc ) { + size_t buf_len; + size_t vrf_len; + + /* Restart interface */ + intf_restart ( &peerblk->retrieval, rc ); + + /* Fail immediately if we have an error */ + if ( rc != 0 ) + goto done; + + /* Abort download attempt (for testing) if applicable */ + if ( ( rc = inject_fault ( PEERBLK_ABORT_RATE ) ) != 0 ) + goto done; + + /* Parse message header */ + if ( ( rc = peerblk_parse_header ( peerblk ) ) != 0 ) + goto done; + + /* Parse message segment and block details */ + if ( ( rc = peerblk_parse_block ( peerblk, &buf_len ) ) != 0 ) + goto done; + + /* If the block was plaintext, then there is nothing more to do */ + if ( ! peerblk->cipher ) + goto done; + + /* Parse message useless details */ + if ( ( rc = peerblk_parse_useless ( peerblk, buf_len, &vrf_len ) ) != 0) + goto done; + + /* Parse message initialisation vector details */ + if ( ( rc = peerblk_parse_iv ( peerblk, buf_len, vrf_len ) ) != 0 ) + goto done; + + /* Fail if decryption length is not aligned to the cipher block size */ + if ( peerblk->cipher_remaining & ( peerblk->cipher->blocksize - 1 ) ) { + DBGC ( peerblk, "PEERBLK %p %d.%d unaligned data length %zd\n", + peerblk, peerblk->segment, peerblk->block, + peerblk->cipher_remaining ); + rc = -EPROTO; + goto done; + } + + /* Stop the download attempt timer: there is no point in + * timing out while decrypting. + */ + stop_timer ( &peerblk->timer ); + + /* Start decryption process */ + process_add ( &peerblk->process ); + return; + + done: + /* Complete download attempt */ + peerblk_done ( peerblk, rc ); +} + +/****************************************************************************** + * + * Retry policy + * + ****************************************************************************** + */ + +/** + * Handle PeerDist retry timer expiry + * + * @v timer Retry timer + * @v over Failure indicator + */ +static void peerblk_expired ( struct retry_timer *timer, int over __unused ) { + struct peerdist_block *peerblk = + container_of ( timer, struct peerdist_block, timer ); + struct peerdisc_segment *segment = peerblk->discovery.segment; + struct peerdisc_peer *head; + unsigned long now = peerblk_timestamp(); + const char *location; + int rc; + + /* Profile discovery timeout, if applicable */ + if ( ( peerblk->peer == NULL ) && ( timer->timeout != 0 ) ) { + profile_custom ( &peerblk_discovery_timeout_profiler, + ( now - peerblk->started ) ); + DBGC ( peerblk, "PEERBLK %p %d.%d discovery timed out after " + "%ld ticks\n", peerblk, peerblk->segment, + peerblk->block, timer->timeout ); + } + + /* Profile download timeout, if applicable */ + if ( ( peerblk->peer != NULL ) && ( timer->timeout != 0 ) ) { + profile_custom ( &peerblk_attempt_timeout_profiler, + ( now - peerblk->attempted ) ); + DBGC ( peerblk, "PEERBLK %p %d.%d timed out after %ld ticks\n", + peerblk, peerblk->segment, peerblk->block, + timer->timeout ); + } + + /* Abort any current download attempt */ + peerblk_reset ( peerblk, -ETIMEDOUT ); + + /* Record attempt start time */ + peerblk->attempted = now; + + /* If we have exceeded our maximum number of attempt cycles + * (each cycle comprising a retrieval protocol download from + * each peer in the list followed by a raw download from the + * origin server), then abort the overall download. + */ + head = list_entry ( &segment->peers, struct peerdisc_peer, list ); + if ( ( peerblk->peer == head ) && + ( ++peerblk->cycles >= PEERBLK_MAX_ATTEMPT_CYCLES ) ) { + rc = peerblk->rc; + assert ( rc != 0 ); + goto err; + } + + /* If we have not yet made any download attempts, then move to + * the start of the peer list. + */ + if ( peerblk->peer == NULL ) + peerblk->peer = head; + + /* Attempt retrieval protocol download from next usable peer */ + list_for_each_entry_continue ( peerblk->peer, &segment->peers, list ) { + + /* Attempt retrieval protocol download from this peer */ + location = peerblk->peer->location; + if ( ( rc = peerblk_retrieval_open ( peerblk, + location ) ) != 0 ) { + /* Non-fatal: continue to try next peer */ + continue; + } + + /* Start download attempt timer */ + peerblk->rc = -ETIMEDOUT; + start_timer_fixed ( &peerblk->timer, + PEERBLK_RETRIEVAL_OPEN_TIMEOUT ); + return; + } + + /* Attempt raw download */ + if ( ( rc = peerblk_raw_open ( peerblk ) ) != 0 ) + goto err; + + /* Start download attempt timer */ + peerblk->rc = -ETIMEDOUT; + start_timer_fixed ( &peerblk->timer, PEERBLK_RAW_OPEN_TIMEOUT ); + return; + + err: + peerblk_close ( peerblk, rc ); +} + +/** + * Handle PeerDist peer discovery + * + * @v discovery PeerDist discovery client + */ +static void peerblk_discovered ( struct peerdisc_client *discovery ) { + struct peerdist_block *peerblk = + container_of ( discovery, struct peerdist_block, discovery ); + unsigned long now = peerblk_timestamp(); + + /* Do nothing unless we are still waiting for the initial + * discovery timeout. + */ + if ( ( peerblk->peer != NULL ) || ( peerblk->timer.timeout == 0 ) ) + return; + + /* Schedule an immediate retry */ + start_timer_nodelay ( &peerblk->timer ); + + /* Profile discovery success */ + profile_custom ( &peerblk_discovery_success_profiler, + ( now - peerblk->started ) ); +} + +/****************************************************************************** + * + * Opener + * + ****************************************************************************** + */ + +/** PeerDist block download data transfer interface operations */ +static struct interface_operation peerblk_xfer_operations[] = { + INTF_OP ( intf_close, struct peerdist_block *, peerblk_close ), +}; + +/** PeerDist block download data transfer interface descriptor */ +static struct interface_descriptor peerblk_xfer_desc = + INTF_DESC ( struct peerdist_block, xfer, peerblk_xfer_operations ); + +/** PeerDist block download raw data interface operations */ +static struct interface_operation peerblk_raw_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_raw_rx ), + INTF_OP ( intf_close, struct peerdist_block *, peerblk_raw_close ), +}; + +/** PeerDist block download raw data interface descriptor */ +static struct interface_descriptor peerblk_raw_desc = + INTF_DESC ( struct peerdist_block, raw, peerblk_raw_operations ); + +/** PeerDist block download retrieval protocol interface operations */ +static struct interface_operation peerblk_retrieval_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_block *, peerblk_retrieval_rx ), + INTF_OP ( intf_close, struct peerdist_block *, peerblk_retrieval_close), +}; + +/** PeerDist block download retrieval protocol interface descriptor */ +static struct interface_descriptor peerblk_retrieval_desc = + INTF_DESC ( struct peerdist_block, retrieval, + peerblk_retrieval_operations ); + +/** PeerDist block download decryption process descriptor */ +static struct process_descriptor peerblk_process_desc = + PROC_DESC ( struct peerdist_block, process, peerblk_decrypt ); + +/** PeerDist block download discovery operations */ +static struct peerdisc_client_operations peerblk_discovery_operations = { + .discovered = peerblk_discovered, +}; + +/** + * Open PeerDist block download + * + * @v xfer Data transfer interface + * @v uri Original URI + * @v info Content information block + * @ret rc Return status code + */ +int peerblk_open ( struct interface *xfer, struct uri *uri, + struct peerdist_info_block *block ) { + const struct peerdist_info_segment *segment = block->segment; + const struct peerdist_info *info = segment->info; + struct digest_algorithm *digest = info->digest; + struct peerdist_block *peerblk; + unsigned long timeout; + size_t digestsize; + int rc; + + /* Allocate and initialise structure */ + peerblk = zalloc ( sizeof ( *peerblk ) + digest->ctxsize ); + if ( ! peerblk ) { + rc = -ENOMEM; + goto err_alloc; + } + ref_init ( &peerblk->refcnt, peerblk_free ); + intf_init ( &peerblk->xfer, &peerblk_xfer_desc, &peerblk->refcnt ); + intf_init ( &peerblk->raw, &peerblk_raw_desc, &peerblk->refcnt ); + intf_init ( &peerblk->retrieval, &peerblk_retrieval_desc, + &peerblk->refcnt ); + peerblk->uri = uri_get ( uri ); + memcpy ( &peerblk->range, &block->range, sizeof ( peerblk->range ) ); + memcpy ( &peerblk->trim, &block->trim, sizeof ( peerblk->trim ) ); + peerblk->offset = ( block->trim.start - info->trim.start ); + peerblk->digest = info->digest; + peerblk->digestsize = digestsize = info->digestsize; + peerblk->digestctx = ( ( ( void * ) peerblk ) + sizeof ( *peerblk ) ); + peerblk->segment = segment->index; + memcpy ( peerblk->id, segment->id, sizeof ( peerblk->id ) ); + memcpy ( peerblk->secret, segment->secret, sizeof ( peerblk->secret ) ); + peerblk->block = block->index; + memcpy ( peerblk->hash, block->hash, sizeof ( peerblk->hash ) ); + xferbuf_malloc_init ( &peerblk->buffer ); + process_init_stopped ( &peerblk->process, &peerblk_process_desc, + &peerblk->refcnt ); + peerdisc_init ( &peerblk->discovery, &peerblk_discovery_operations ); + timer_init ( &peerblk->timer, peerblk_expired, &peerblk->refcnt ); + DBGC2 ( peerblk, "PEERBLK %p %d.%d id %02x%02x%02x%02x%02x..." + "%02x%02x%02x [%08zx,%08zx)", peerblk, peerblk->segment, + peerblk->block, peerblk->id[0], peerblk->id[1], peerblk->id[2], + peerblk->id[3], peerblk->id[4], peerblk->id[ digestsize - 3 ], + peerblk->id[ digestsize - 2 ], peerblk->id[ digestsize - 1 ], + peerblk->range.start, peerblk->range.end ); + if ( ( peerblk->trim.start != peerblk->range.start ) || + ( peerblk->trim.end != peerblk->range.end ) ) { + DBGC2 ( peerblk, " covers [%08zx,%08zx)", + peerblk->trim.start, peerblk->trim.end ); + } + DBGC2 ( peerblk, "\n" ); + + /* Open discovery */ + if ( ( rc = peerdisc_open ( &peerblk->discovery, peerblk->id, + peerblk->digestsize ) ) != 0 ) + goto err_open_discovery; + + /* Schedule a retry attempt either immediately (if we already + * have some peers) or after the discovery timeout. + */ + timeout = ( list_empty ( &peerblk->discovery.segment->peers ) ? + ( peerdisc_timeout_secs * TICKS_PER_SEC ) : 0 ); + start_timer_fixed ( &peerblk->timer, timeout ); + + /* Record start time */ + peerblk->started = peerblk_timestamp(); + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( xfer, &peerblk->xfer ); + ref_put ( &peerblk->refcnt ); + return 0; + + err_open_discovery: + peerblk_close ( peerblk, rc ); + err_alloc: + return rc; +} diff --git a/roms/ipxe/src/net/peerdisc.c b/roms/ipxe/src/net/peerdisc.c new file mode 100644 index 000000000..5b0e98911 --- /dev/null +++ b/roms/ipxe/src/net/peerdisc.c @@ -0,0 +1,551 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <errno.h> +#include <assert.h> +#include <ipxe/xfer.h> +#include <ipxe/iobuf.h> +#include <ipxe/open.h> +#include <ipxe/tcpip.h> +#include <ipxe/uuid.h> +#include <ipxe/base16.h> +#include <ipxe/netdevice.h> +#include <ipxe/timer.h> +#include <ipxe/fault.h> +#include <ipxe/pccrd.h> +#include <ipxe/peerdisc.h> + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol peer discovery + * + */ + +/** List of discovery segments */ +static LIST_HEAD ( peerdisc_segments ); + +/** Number of repeated discovery attempts */ +#define PEERDISC_REPEAT_COUNT 2 + +/** Time between repeated discovery attempts */ +#define PEERDISC_REPEAT_TIMEOUT ( 1 * TICKS_PER_SEC ) + +/** Default discovery timeout (in seconds) */ +#define PEERDISC_DEFAULT_TIMEOUT_SECS 2 + +/** Recommended discovery timeout (in seconds) + * + * We reduce the recommended discovery timeout whenever a segment + * fails to discover any peers, and restore the default value whenever + * a valid discovery reply is received. We continue to send discovery + * requests even if the recommended timeout is reduced to zero. + * + * This strategy is intended to minimise discovery delays when no + * peers are available on the network, while allowing downloads to + * quickly switch back to using PeerDist acceleration if new peers + * become available. + */ +unsigned int peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; + +static struct peerdisc_segment * peerdisc_find ( const char *id ); +static int peerdisc_discovered ( struct peerdisc_segment *segment, + const char *location ); + +/****************************************************************************** + * + * Discovery sockets + * + ****************************************************************************** + */ + +/** + * Open all PeerDist discovery sockets + * + * @ret rc Return status code + */ +static int peerdisc_socket_open ( void ) { + struct peerdisc_socket *socket; + int rc; + + /* Open each socket */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) { + if ( ( rc = xfer_open_socket ( &socket->xfer, SOCK_DGRAM, + &socket->address.sa, + NULL ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not open socket: " + "%s\n", socket->name, strerror ( rc ) ); + goto err; + } + } + + return 0; + + err: + for_each_table_entry_continue_reverse ( socket, PEERDISC_SOCKETS ) + intf_restart ( &socket->xfer, rc ); + return rc; +} + +/** + * Attempt to transmit PeerDist discovery requests on all sockets + * + * @v uuid Message UUID string + * @v id Segment identifier string + */ +static void peerdisc_socket_tx ( const char *uuid, const char *id ) { + struct peerdisc_socket *socket; + struct net_device *netdev; + struct xfer_metadata meta; + union { + struct sockaddr sa; + struct sockaddr_tcpip st; + } address; + char *request; + size_t len; + int rc; + + /* Construct discovery request */ + request = peerdist_discovery_request ( uuid, id ); + if ( ! request ) + goto err_request; + len = strlen ( request ); + + /* Initialise data transfer metadata */ + memset ( &meta, 0, sizeof ( meta ) ); + meta.dest = &address.sa; + + /* Send message on each socket */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) { + + /* Initialise socket address */ + memcpy ( &address.sa, &socket->address.sa, + sizeof ( address.sa ) ); + + /* Send message on each open network device */ + for_each_netdev ( netdev ) { + + /* Skip unopened network devices */ + if ( ! netdev_is_open ( netdev ) ) + continue; + address.st.st_scope_id = netdev->index; + + /* Discard request (for test purposes) if applicable */ + if ( inject_fault ( PEERDISC_DISCARD_RATE ) ) + continue; + + /* Transmit request */ + if ( ( rc = xfer_deliver_raw_meta ( &socket->xfer, + request, len, + &meta ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not transmit " + "via %s: %s\n", socket->name, + netdev->name, strerror ( rc ) ); + /* Contine to try other net devices/sockets */ + continue; + } + } + } + + free ( request ); + err_request: + return; +} + +/** + * Handle received PeerDist discovery reply + * + * @v socket PeerDist discovery socket + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peerdisc_socket_rx ( struct peerdisc_socket *socket, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + struct peerdist_discovery_reply reply; + struct peerdisc_segment *segment; + char *id; + char *location; + int rc; + + /* Discard reply (for test purposes) if applicable */ + if ( ( rc = inject_fault ( PEERDISC_DISCARD_RATE ) ) != 0 ) + goto err; + + /* Parse reply */ + if ( ( rc = peerdist_discovery_reply ( iobuf->data, iob_len ( iobuf ), + &reply ) ) != 0 ) { + DBGC ( socket, "PEERDISC %s could not parse reply: %s\n", + socket->name, strerror ( rc ) ); + DBGC_HDA ( socket, 0, iobuf->data, iob_len ( iobuf ) ); + goto err; + } + + /* Any kind of discovery reply indicates that there are active + * peers on a local network, so restore the recommended + * discovery timeout to its default value for future requests. + */ + if ( peerdisc_timeout_secs != PEERDISC_DEFAULT_TIMEOUT_SECS ) { + DBGC ( socket, "PEERDISC %s restoring timeout to %d seconds\n", + socket->name, PEERDISC_DEFAULT_TIMEOUT_SECS ); + } + peerdisc_timeout_secs = PEERDISC_DEFAULT_TIMEOUT_SECS; + + /* Iterate over segment IDs */ + for ( id = reply.ids ; *id ; id += ( strlen ( id ) + 1 /* NUL */ ) ) { + + /* Find corresponding segment */ + segment = peerdisc_find ( id ); + if ( ! segment ) { + DBGC ( socket, "PEERDISC %s ignoring reply for %s\n", + socket->name, id ); + continue; + } + + /* Report all discovered peer locations */ + for ( location = reply.locations ; *location ; + location += ( strlen ( location ) + 1 /* NUL */ ) ) { + + /* Report discovered peer location */ + if ( ( rc = peerdisc_discovered ( segment, + location ) ) != 0 ) + goto err; + } + } + + err: + free_iob ( iobuf ); + return rc; +} + +/** + * Close all PeerDist discovery sockets + * + * @v rc Reason for close + */ +static void peerdisc_socket_close ( int rc ) { + struct peerdisc_socket *socket; + + /* Close all sockets */ + for_each_table_entry ( socket, PEERDISC_SOCKETS ) + intf_restart ( &socket->xfer, rc ); +} + +/** PeerDist discovery socket interface operations */ +static struct interface_operation peerdisc_socket_operations[] = { + INTF_OP ( xfer_deliver, struct peerdisc_socket *, peerdisc_socket_rx ), +}; + +/** PeerDist discovery socket interface descriptor */ +static struct interface_descriptor peerdisc_socket_desc = + INTF_DESC ( struct peerdisc_socket, xfer, peerdisc_socket_operations ); + +/** PeerDist discovery IPv4 socket */ +struct peerdisc_socket peerdisc_socket_ipv4 __peerdisc_socket = { + .name = "IPv4", + .address = { + .sin = { + .sin_family = AF_INET, + .sin_port = htons ( PEERDIST_DISCOVERY_PORT ), + .sin_addr.s_addr = htonl ( PEERDIST_DISCOVERY_IPV4 ), + }, + }, + .xfer = INTF_INIT ( peerdisc_socket_desc ), +}; + +/** PeerDist discovery IPv6 socket */ +struct peerdisc_socket peerdisc_socket_ipv6 __peerdisc_socket = { + .name = "IPv6", + .address = { + .sin6 = { + .sin6_family = AF_INET6, + .sin6_port = htons ( PEERDIST_DISCOVERY_PORT ), + .sin6_addr.s6_addr = PEERDIST_DISCOVERY_IPV6, + }, + }, + .xfer = INTF_INIT ( peerdisc_socket_desc ), +}; + +/****************************************************************************** + * + * Discovery segments + * + ****************************************************************************** + */ + +/** + * Free PeerDist discovery segment + * + * @v refcnt Reference count + */ +static void peerdisc_free ( struct refcnt *refcnt ) { + struct peerdisc_segment *segment = + container_of ( refcnt, struct peerdisc_segment, refcnt ); + struct peerdisc_peer *peer; + struct peerdisc_peer *tmp; + + /* Free all discovered peers */ + list_for_each_entry_safe ( peer, tmp, &segment->peers, list ) { + list_del ( &peer->list ); + free ( peer ); + } + + /* Free segment */ + free ( segment ); +} + +/** + * Find PeerDist discovery segment + * + * @v id Segment ID + * @ret segment PeerDist discovery segment, or NULL if not found + */ +static struct peerdisc_segment * peerdisc_find ( const char *id ) { + struct peerdisc_segment *segment; + + /* Look for a matching segment */ + list_for_each_entry ( segment, &peerdisc_segments, list ) { + if ( strcmp ( id, segment->id ) == 0 ) + return segment; + } + + return NULL; +} + +/** + * Add discovered PeerDist peer + * + * @v segment PeerDist discovery segment + * @v location Peer location + * @ret rc Return status code + */ +static int peerdisc_discovered ( struct peerdisc_segment *segment, + const char *location ) { + struct peerdisc_peer *peer; + struct peerdisc_client *peerdisc; + struct peerdisc_client *tmp; + + /* Ignore duplicate peers */ + list_for_each_entry ( peer, &segment->peers, list ) { + if ( strcmp ( peer->location, location ) == 0 ) { + DBGC2 ( segment, "PEERDISC %p duplicate %s\n", + segment, location ); + return 0; + } + } + DBGC2 ( segment, "PEERDISC %p discovered %s\n", segment, location ); + + /* Allocate and initialise structure */ + peer = zalloc ( sizeof ( *peer ) + strlen ( location ) + 1 /* NUL */ ); + if ( ! peer ) + return -ENOMEM; + strcpy ( peer->location, location ); + + /* Add to end of list of peers */ + list_add_tail ( &peer->list, &segment->peers ); + + /* Notify all clients */ + list_for_each_entry_safe ( peerdisc, tmp, &segment->clients, list ) + peerdisc->op->discovered ( peerdisc ); + + return 0; +} + +/** + * Handle discovery timer expiry + * + * @v timer Discovery timer + * @v over Failure indicator + */ +static void peerdisc_expired ( struct retry_timer *timer, int over __unused ) { + struct peerdisc_segment *segment = + container_of ( timer, struct peerdisc_segment, timer ); + + /* Attempt to transmit discovery requests */ + peerdisc_socket_tx ( segment->uuid, segment->id ); + + /* Schedule next transmission, if applicable */ + if ( timer->count < PEERDISC_REPEAT_COUNT ) + start_timer_fixed ( &segment->timer, PEERDISC_REPEAT_TIMEOUT ); +} + +/** + * Create PeerDist discovery segment + * + * @v id Segment ID + * @ret segment PeerDist discovery segment, or NULL on error + */ +static struct peerdisc_segment * peerdisc_create ( const char *id ) { + struct peerdisc_segment *segment; + union { + union uuid uuid; + uint32_t dword[ sizeof ( union uuid ) / sizeof ( uint32_t ) ]; + } random_uuid; + size_t uuid_len; + size_t id_len; + char *uuid; + char *uuid_copy; + char *id_copy; + unsigned int i; + + /* Generate a random message UUID. This does not require high + * quality randomness. + */ + for ( i = 0 ; i < ( sizeof ( random_uuid.dword ) / + sizeof ( random_uuid.dword[0] ) ) ; i++ ) + random_uuid.dword[i] = random(); + uuid = uuid_ntoa ( &random_uuid.uuid ); + + /* Calculate string lengths */ + id_len = ( strlen ( id ) + 1 /* NUL */ ); + uuid_len = ( strlen ( uuid ) + 1 /* NUL */ ); + + /* Allocate and initialise structure */ + segment = zalloc ( sizeof ( *segment ) + id_len + uuid_len ); + if ( ! segment ) + return NULL; + id_copy = ( ( ( void * ) segment ) + sizeof ( *segment ) ); + memcpy ( id_copy, id, id_len ); + uuid_copy = ( ( ( void * ) id_copy ) + id_len ); + memcpy ( uuid_copy, uuid, uuid_len ); + ref_init ( &segment->refcnt, peerdisc_free ); + segment->id = id_copy; + segment->uuid = uuid_copy; + INIT_LIST_HEAD ( &segment->peers ); + INIT_LIST_HEAD ( &segment->clients ); + timer_init ( &segment->timer, peerdisc_expired, &segment->refcnt ); + DBGC2 ( segment, "PEERDISC %p discovering %s\n", segment, segment->id ); + + /* Start discovery timer */ + start_timer_nodelay ( &segment->timer ); + + /* Add to list of segments, transfer reference to list, and return */ + list_add_tail ( &segment->list, &peerdisc_segments ); + return segment; +} + +/** + * Destroy PeerDist discovery segment + * + * @v segment PeerDist discovery segment + */ +static void peerdisc_destroy ( struct peerdisc_segment *segment ) { + + /* Sanity check */ + assert ( list_empty ( &segment->clients ) ); + + /* Stop timer */ + stop_timer ( &segment->timer ); + + /* Remove from list of segments and drop list's reference */ + list_del ( &segment->list ); + ref_put ( &segment->refcnt ); +} + +/****************************************************************************** + * + * Discovery clients + * + ****************************************************************************** + */ + +/** + * Open PeerDist discovery client + * + * @v peerdisc PeerDist discovery client + * @v id Segment ID + * @v len Length of segment ID + * @ret rc Return status code + */ +int peerdisc_open ( struct peerdisc_client *peerdisc, const void *id, + size_t len ) { + struct peerdisc_segment *segment; + char id_string[ base16_encoded_len ( len ) + 1 /* NUL */ ]; + char *id_chr; + int rc; + + /* Construct ID string */ + base16_encode ( id, len, id_string, sizeof ( id_string ) ); + for ( id_chr = id_string ; *id_chr ; id_chr++ ) + *id_chr = toupper ( *id_chr ); + + /* Sanity check */ + assert ( peerdisc->segment == NULL ); + + /* Open socket if this is the first segment */ + if ( list_empty ( &peerdisc_segments ) && + ( ( rc = peerdisc_socket_open() ) != 0 ) ) + return rc; + + /* Find or create segment */ + if ( ! ( ( segment = peerdisc_find ( id_string ) ) || + ( segment = peerdisc_create ( id_string ) ) ) ) + return -ENOMEM; + + /* Add to list of clients */ + ref_get ( &segment->refcnt ); + peerdisc->segment = segment; + list_add_tail ( &peerdisc->list, &segment->clients ); + + return 0; +} + +/** + * Close PeerDist discovery client + * + * @v peerdisc PeerDist discovery client + */ +void peerdisc_close ( struct peerdisc_client *peerdisc ) { + struct peerdisc_segment *segment = peerdisc->segment; + + /* Ignore if discovery is already closed */ + if ( ! segment ) + return; + + /* If no peers were discovered, reduce the recommended + * discovery timeout to minimise delays on future requests. + */ + if ( list_empty ( &segment->peers ) && peerdisc_timeout_secs ) { + peerdisc_timeout_secs--; + DBGC ( segment, "PEERDISC %p reducing timeout to %d " + "seconds\n", peerdisc, peerdisc_timeout_secs ); + } + + /* Remove from list of clients */ + peerdisc->segment = NULL; + list_del ( &peerdisc->list ); + ref_put ( &segment->refcnt ); + + /* If this was the last clients, destroy the segment */ + if ( list_empty ( &segment->clients ) ) + peerdisc_destroy ( segment ); + + /* If there are no more segments, close the socket */ + if ( list_empty ( &peerdisc_segments ) ) + peerdisc_socket_close ( 0 ); +} diff --git a/roms/ipxe/src/net/peerdist.c b/roms/ipxe/src/net/peerdist.c new file mode 100644 index 000000000..48933f951 --- /dev/null +++ b/roms/ipxe/src/net/peerdist.c @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdio.h> +#include <ipxe/http.h> +#include <ipxe/peermux.h> + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol + * + * This is quite possibly the ugliest protocol I have ever had the + * misfortune to encounter, and I've encountered multicast TFTP. + */ + +/** + * Check whether or not to support PeerDist encoding for this request + * + * @v http HTTP transaction + * @ret supported PeerDist encoding is supported for this request + */ +static int http_peerdist_supported ( struct http_transaction *http ) { + + /* Support PeerDist encoding only if we can directly access an + * underlying data transfer buffer. Direct access is required + * in order to support decryption of data received via the + * retrieval protocol (which provides the AES initialisation + * vector only after all of the encrypted data has been + * received). + * + * This test simultaneously ensures that we do not attempt to + * use PeerDist encoding on a request which is itself a + * PeerDist individual block download, since the individual + * block downloads do not themselves provide direct access to + * an underlying data transfer buffer. + */ + return ( xfer_buffer ( &http->xfer ) != NULL ); +} + +/** + * Format HTTP "X-P2P-PeerDist" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_p2p_peerdist ( struct http_transaction *http, + char *buf, size_t len ) { + int supported = http_peerdist_supported ( http ); + int missing; + + /* PeerDist wants us to inform the server whenever we make a + * request for data that was missing from local peers + * (presumably for statistical purposes only). We use the + * heuristic of assuming that the combination of "this request + * may not itself use PeerDist content encoding" and "this is + * a range request" probably indicates that we are making a + * PeerDist block raw range request for missing data. + */ + missing = ( http->request.range.len && ( ! supported ) ); + + /* Omit header if PeerDist encoding is not supported and we + * are not reporting a missing data request. + */ + if ( ! ( supported || missing ) ) + return 0; + + /* Construct header */ + return snprintf ( buf, len, "Version=1.1%s", + ( missing ? ", MissingDataRequest=true" : "" ) ); +} + +/** HTTP "X-P2P-PeerDist" header */ +struct http_request_header http_request_p2p_peerdist __http_request_header = { + .name = "X-P2P-PeerDist", + .format = http_format_p2p_peerdist, +}; + +/** + * Format HTTP "X-P2P-PeerDistEx" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_p2p_peerdistex ( struct http_transaction *http, + char *buf, size_t len ) { + int supported = http_peerdist_supported ( http ); + + /* Omit header if PeerDist encoding is not supported */ + if ( ! supported ) + return 0; + + /* Construct header */ + return snprintf ( buf, len, ( "MinContentInformation=1.0, " + "MaxContentInformation=2.0" ) ); +} + +/** HTTP "X-P2P-PeerDist" header */ +struct http_request_header http_request_p2p_peerdistex __http_request_header = { + .name = "X-P2P-PeerDistEx", + .format = http_format_p2p_peerdistex, +}; + +/** + * Initialise PeerDist content encoding + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_peerdist_init ( struct http_transaction *http ) { + + return peermux_filter ( &http->content, &http->transfer, http->uri ); +} + +/** PeerDist HTTP content encoding */ +struct http_content_encoding peerdist_encoding __http_content_encoding = { + .name = "peerdist", + .supported = http_peerdist_supported, + .init = http_peerdist_init, +}; diff --git a/roms/ipxe/src/net/peermux.c b/roms/ipxe/src/net/peermux.c new file mode 100644 index 000000000..634c69992 --- /dev/null +++ b/roms/ipxe/src/net/peermux.c @@ -0,0 +1,387 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdlib.h> +#include <errno.h> +#include <ipxe/uri.h> +#include <ipxe/xferbuf.h> +#include <ipxe/peerblk.h> +#include <ipxe/peermux.h> + +/** @file + * + * Peer Content Caching and Retrieval (PeerDist) protocol multiplexer + * + */ + +/** + * Free PeerDist download multiplexer + * + * @v refcnt Reference count + */ +static void peermux_free ( struct refcnt *refcnt ) { + struct peerdist_multiplexer *peermux = + container_of ( refcnt, struct peerdist_multiplexer, refcnt ); + + uri_put ( peermux->uri ); + xferbuf_free ( &peermux->buffer ); + free ( peermux ); +} + +/** + * Close PeerDist download multiplexer + * + * @v peermux PeerDist download multiplexer + * @v rc Reason for close + */ +static void peermux_close ( struct peerdist_multiplexer *peermux, int rc ) { + unsigned int i; + + /* Stop block download initiation process */ + process_del ( &peermux->process ); + + /* Shut down all block downloads */ + for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) + intf_shutdown ( &peermux->block[i].xfer, rc ); + + /* Shut down all other interfaces (which may be connected to + * the same object). + */ + intf_nullify ( &peermux->info ); /* avoid potential loops */ + intf_shutdown ( &peermux->xfer, rc ); + intf_shutdown ( &peermux->info, rc ); +} + +/** + * Receive content information + * + * @v peermux PeerDist download multiplexer + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peermux_info_deliver ( struct peerdist_multiplexer *peermux, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Add data to buffer */ + if ( ( rc = xferbuf_deliver ( &peermux->buffer, iobuf, meta ) ) != 0 ) + goto err; + + return 0; + + err: + peermux_close ( peermux, rc ); + return rc; +} + +/** + * Close content information interface + * + * @v peermux PeerDist download multiplexer + * @v rc Reason for close + */ +static void peermux_info_close ( struct peerdist_multiplexer *peermux, int rc ){ + struct peerdist_info *info = &peermux->cache.info; + size_t len; + + /* Terminate download on error */ + if ( rc != 0 ) + goto err; + + /* Successfully closing the content information interface + * indicates that the content information has been fully + * received, and initiates the actual PeerDist download. + */ + + /* Shut down content information interface */ + intf_shutdown ( &peermux->info, rc ); + + /* Parse content information */ + if ( ( rc = peerdist_info ( info->raw.data, peermux->buffer.len, + info ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not parse content info: %s\n", + peermux, strerror ( rc ) ); + goto err; + } + + /* Notify recipient of total download size */ + len = ( info->trim.end - info->trim.start ); + if ( ( rc = xfer_seek ( &peermux->xfer, len ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not presize buffer: %s\n", + peermux, strerror ( rc ) ); + goto err; + } + xfer_seek ( &peermux->xfer, 0 ); + + /* Start block download process */ + process_add ( &peermux->process ); + + return; + + err: + peermux_close ( peermux, rc ); +} + +/** + * Initiate multiplexed block download + * + * @v peermux PeerDist download multiplexer + */ +static void peermux_step ( struct peerdist_multiplexer *peermux ) { + struct peerdist_info *info = &peermux->cache.info; + struct peerdist_info_segment *segment = &peermux->cache.segment; + struct peerdist_info_block *block = &peermux->cache.block; + struct peerdist_multiplexed_block *peermblk; + unsigned int next_segment; + unsigned int next_block; + int rc; + + /* Stop initiation process if all block downloads are busy */ + peermblk = list_first_entry ( &peermux->idle, + struct peerdist_multiplexed_block, list ); + if ( ! peermblk ) { + process_del ( &peermux->process ); + return; + } + + /* Increment block index */ + next_block = ( block->index + 1 ); + + /* Move to first/next segment, if applicable */ + if ( next_block >= segment->blocks ) { + + /* Reset block index */ + next_block = 0; + + /* Calculate segment index */ + next_segment = ( segment->info ? ( segment->index + 1 ) : 0 ); + + /* If we have finished all segments and have no + * remaining block downloads, then we are finished. + */ + if ( next_segment >= info->segments ) { + process_del ( &peermux->process ); + if ( list_empty ( &peermux->busy ) ) + peermux_close ( peermux, 0 ); + return; + } + + /* Get content information segment */ + if ( ( rc = peerdist_info_segment ( info, segment, + next_segment ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not get segment %d " + "information: %s\n", peermux, next_segment, + strerror ( rc ) ); + goto err; + } + } + + /* Get content information block */ + if ( ( rc = peerdist_info_block ( segment, block, next_block ) ) != 0 ){ + DBGC ( peermux, "PEERMUX %p could not get segment %d block " + "%d information: %s\n", peermux, segment->index, + next_block, strerror ( rc ) ); + goto err; + } + + /* Ignore block if it lies entirely outside the trimmed range */ + if ( block->trim.start == block->trim.end ) { + DBGC ( peermux, "PEERMUX %p skipping segment %d block %d\n", + peermux, segment->index, block->index ); + return; + } + + /* Start downloading this block */ + if ( ( rc = peerblk_open ( &peermblk->xfer, peermux->uri, + block ) ) != 0 ) { + DBGC ( peermux, "PEERMUX %p could not start download for " + "segment %d block %d: %s\n", peermux, segment->index, + block->index, strerror ( rc ) ); + goto err; + } + + /* Move to list of busy block downloads */ + list_del ( &peermblk->list ); + list_add_tail ( &peermblk->list, &peermux->busy ); + + return; + + err: + peermux_close ( peermux, rc ); +} + +/** + * Receive data from multiplexed block download + * + * @v peermblk PeerDist multiplexed block download + * @v iobuf I/O buffer + * @v meta Data transfer metadata + * @ret rc Return status code + */ +static int peermux_block_deliver ( struct peerdist_multiplexed_block *peermblk, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* Sanity check: all block downloads must use absolute + * positions for all deliveries, since they run concurrently. + */ + assert ( meta->flags & XFER_FL_ABS_OFFSET ); + + /* We can't use a simple passthrough interface descriptor, + * since there are multiple block download interfaces. + */ + return xfer_deliver ( &peermux->xfer, iob_disown ( iobuf ), meta ); +} + +/** + * Get multiplexed block download underlying data transfer buffer + * + * @v peermblk PeerDist multiplexed download block + * @ret xferbuf Data transfer buffer, or NULL on error + */ +static struct xfer_buffer * +peermux_block_buffer ( struct peerdist_multiplexed_block *peermblk ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* We can't use a simple passthrough interface descriptor, + * since there are multiple block download interfaces. + */ + return xfer_buffer ( &peermux->xfer ); +} + +/** + * Close multiplexed block download + * + * @v peermblk PeerDist multiplexed block download + * @v rc Reason for close + */ +static void peermux_block_close ( struct peerdist_multiplexed_block *peermblk, + int rc ) { + struct peerdist_multiplexer *peermux = peermblk->peermux; + + /* Move to list of idle downloads */ + list_del ( &peermblk->list ); + list_add_tail ( &peermblk->list, &peermux->idle ); + + /* If any error occurred, terminate the whole multiplexer */ + if ( rc != 0 ) { + peermux_close ( peermux, rc ); + return; + } + + /* Restart data transfer interface */ + intf_restart ( &peermblk->xfer, rc ); + + /* Restart block download initiation process */ + process_add ( &peermux->process ); +} + +/** Data transfer interface operations */ +static struct interface_operation peermux_xfer_operations[] = { + INTF_OP ( intf_close, struct peerdist_multiplexer *, peermux_close ), +}; + +/** Data transfer interface descriptor */ +static struct interface_descriptor peermux_xfer_desc = + INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, xfer, + peermux_xfer_operations, info ); + +/** Content information interface operations */ +static struct interface_operation peermux_info_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_multiplexer *, + peermux_info_deliver ), + INTF_OP ( intf_close, struct peerdist_multiplexer *, + peermux_info_close ), +}; + +/** Content information interface descriptor */ +static struct interface_descriptor peermux_info_desc = + INTF_DESC_PASSTHRU ( struct peerdist_multiplexer, info, + peermux_info_operations, xfer ); + +/** Block download data transfer interface operations */ +static struct interface_operation peermux_block_operations[] = { + INTF_OP ( xfer_deliver, struct peerdist_multiplexed_block *, + peermux_block_deliver ), + INTF_OP ( xfer_buffer, struct peerdist_multiplexed_block *, + peermux_block_buffer ), + INTF_OP ( intf_close, struct peerdist_multiplexed_block *, + peermux_block_close ), +}; + +/** Block download data transfer interface descriptor */ +static struct interface_descriptor peermux_block_desc = + INTF_DESC ( struct peerdist_multiplexed_block, xfer, + peermux_block_operations ); + +/** Block download initiation process descriptor */ +static struct process_descriptor peermux_process_desc = + PROC_DESC ( struct peerdist_multiplexer, process, peermux_step ); + +/** + * Add PeerDist content-encoding filter + * + * @v xfer Data transfer interface + * @v info Content information interface + * @v uri Original URI + * @ret rc Return status code + */ +int peermux_filter ( struct interface *xfer, struct interface *info, + struct uri *uri ) { + struct peerdist_multiplexer *peermux; + struct peerdist_multiplexed_block *peermblk; + unsigned int i; + + /* Allocate and initialise structure */ + peermux = zalloc ( sizeof ( *peermux ) ); + if ( ! peermux ) + return -ENOMEM; + ref_init ( &peermux->refcnt, peermux_free ); + intf_init ( &peermux->xfer, &peermux_xfer_desc, &peermux->refcnt ); + intf_init ( &peermux->info, &peermux_info_desc, &peermux->refcnt ); + peermux->uri = uri_get ( uri ); + xferbuf_umalloc_init ( &peermux->buffer, + &peermux->cache.info.raw.data ); + process_init_stopped ( &peermux->process, &peermux_process_desc, + &peermux->refcnt ); + INIT_LIST_HEAD ( &peermux->busy ); + INIT_LIST_HEAD ( &peermux->idle ); + for ( i = 0 ; i < PEERMUX_MAX_BLOCKS ; i++ ) { + peermblk = &peermux->block[i]; + peermblk->peermux = peermux; + list_add_tail ( &peermblk->list, &peermux->idle ); + intf_init ( &peermblk->xfer, &peermux_block_desc, + &peermux->refcnt ); + } + + /* Attach to parent interfaces, mortalise self, and return */ + intf_plug_plug ( &peermux->xfer, xfer ); + intf_plug_plug ( &peermux->info, info ); + ref_put ( &peermux->refcnt ); + return 0; +} diff --git a/roms/ipxe/src/net/ping.c b/roms/ipxe/src/net/ping.c index d9da87ade..3f4fa5c11 100644 --- a/roms/ipxe/src/net/ping.c +++ b/roms/ipxe/src/net/ping.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <string.h> diff --git a/roms/ipxe/src/net/rarp.c b/roms/ipxe/src/net/rarp.c index 371145015..c194a404f 100644 --- a/roms/ipxe/src/net/rarp.c +++ b/roms/ipxe/src/net/rarp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <byteswap.h> diff --git a/roms/ipxe/src/net/retry.c b/roms/ipxe/src/net/retry.c index 8f210bdcc..734567be5 100644 --- a/roms/ipxe/src/net/retry.c +++ b/roms/ipxe/src/net/retry.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/timer.h> @@ -35,7 +39,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * * This implementation of the timer is designed to satisfy RFC 2988 * and therefore be usable as a TCP retransmission timer. - * * */ @@ -49,47 +52,59 @@ FILE_LICENCE ( GPL2_OR_LATER ); static LIST_HEAD ( timers ); /** - * Start timer + * Start timer with a specified timeout * * @v timer Retry timer + * @v timeout Timeout, in ticks * - * This starts the timer running with the current timeout value. If + * This starts the timer running with the specified timeout value. If * stop_timer() is not called before the timer expires, the timer will * be stopped and the timer's callback function will be called. */ -void start_timer ( struct retry_timer *timer ) { +void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { + + /* Add to list of running timers (if applicable) */ if ( ! timer->running ) { list_add ( &timer->list, &timers ); ref_get ( timer->refcnt ); + timer->running = 1; } + + /* Record start time */ timer->start = currticks(); - timer->running = 1; - - /* 0 means "use default timeout" */ - if ( timer->min_timeout == 0 ) - timer->min_timeout = DEFAULT_MIN_TIMEOUT; - /* We must never be less than MIN_TIMEOUT under any circumstances */ - if ( timer->min_timeout < MIN_TIMEOUT ) - timer->min_timeout = MIN_TIMEOUT; - /* Honor user-specified minimum timeout */ - if ( timer->timeout < timer->min_timeout ) - timer->timeout = timer->min_timeout; - - DBG2 ( "Timer %p started at time %ld (expires at %ld)\n", - timer, timer->start, ( timer->start + timer->timeout ) ); + + /* Record timeout */ + timer->timeout = timeout; + + DBGC2 ( timer, "Timer %p started at time %ld (expires at %ld)\n", + timer, timer->start, ( timer->start + timer->timeout ) ); } /** - * Start timer with a specified fixed timeout + * Start timer * * @v timer Retry timer - * @v timeout Timeout, in ticks + * + * This starts the timer running with the current timeout value + * (rounded up to the minimum timeout value). If stop_timer() is not + * called before the timer expires, the timer will be stopped and the + * timer's callback function will be called. */ -void start_timer_fixed ( struct retry_timer *timer, unsigned long timeout ) { - start_timer ( timer ); - timer->timeout = timeout; - DBG2 ( "Timer %p expiry time changed to %ld\n", - timer, ( timer->start + timer->timeout ) ); +void start_timer ( struct retry_timer *timer ) { + unsigned long timeout = timer->timeout; + unsigned long min; + + /* Calculate minimum timeout */ + min = ( timer->min ? timer->min : DEFAULT_MIN_TIMEOUT ); + if ( min < MIN_TIMEOUT ) + min = MIN_TIMEOUT; + + /* Ensure timeout is at least the minimum */ + if ( timeout < min ) + timeout = min; + + /* Start timer with this timeout */ + start_timer_fixed ( timer, timeout ); } /** @@ -111,8 +126,8 @@ void stop_timer ( struct retry_timer *timer ) { list_del ( &timer->list ); runtime = ( now - timer->start ); timer->running = 0; - DBG2 ( "Timer %p stopped at time %ld (ran for %ld)\n", - timer, now, runtime ); + DBGC2 ( timer, "Timer %p stopped at time %ld (ran for %ld)\n", + timer, now, runtime ); /* Update timer. Variables are: * @@ -135,8 +150,8 @@ void stop_timer ( struct retry_timer *timer ) { timer->timeout -= ( timer->timeout >> 3 ); timer->timeout += ( runtime >> 1 ); if ( timer->timeout != old_timeout ) { - DBG ( "Timer %p timeout updated to %ld\n", - timer, timer->timeout ); + DBGC ( timer, "Timer %p timeout updated to %ld\n", + timer, timer->timeout ); } } @@ -150,11 +165,12 @@ void stop_timer ( struct retry_timer *timer ) { */ static void timer_expired ( struct retry_timer *timer ) { struct refcnt *refcnt = timer->refcnt; + unsigned long max = ( timer->max ? timer->max : DEFAULT_MAX_TIMEOUT ); int fail; /* Stop timer without performing RTT calculations */ - DBG2 ( "Timer %p stopped at time %ld on expiry\n", - timer, currticks() ); + DBGC2 ( timer, "Timer %p stopped at time %ld on expiry\n", + timer, currticks() ); assert ( timer->running ); list_del ( &timer->list ); timer->running = 0; @@ -162,12 +178,10 @@ static void timer_expired ( struct retry_timer *timer ) { /* Back off the timeout value */ timer->timeout <<= 1; - if ( timer->max_timeout == 0 ) /* 0 means "use default timeout" */ - timer->max_timeout = DEFAULT_MAX_TIMEOUT; - if ( ( fail = ( timer->timeout > timer->max_timeout ) ) ) - timer->timeout = timer->max_timeout; - DBG ( "Timer %p timeout backed off to %ld\n", - timer, timer->timeout ); + if ( ( fail = ( timer->timeout > max ) ) ) + timer->timeout = max; + DBGC ( timer, "Timer %p timeout backed off to %ld\n", + timer, timer->timeout ); /* Call expiry callback */ timer->expired ( timer, fail ); diff --git a/roms/ipxe/src/net/rndis.c b/roms/ipxe/src/net/rndis.c new file mode 100644 index 000000000..8c4fe8b30 --- /dev/null +++ b/roms/ipxe/src/net/rndis.c @@ -0,0 +1,1052 @@ +/* + * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Remote Network Driver Interface Specification + * + */ + +#include <unistd.h> +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/iobuf.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/device.h> +#include <ipxe/rndis.h> + +/** + * Allocate I/O buffer + * + * @v len Length + * @ret iobuf I/O buffer, or NULL + */ +static struct io_buffer * rndis_alloc_iob ( size_t len ) { + struct rndis_header *header; + struct io_buffer *iobuf; + + /* Allocate I/O buffer and reserve space */ + iobuf = alloc_iob ( sizeof ( *header ) + len ); + if ( iobuf ) + iob_reserve ( iobuf, sizeof ( *header ) ); + + return iobuf; +} + +/** + * Wait for completion + * + * @v rndis RNDIS device + * @v wait_id Request ID + * @ret rc Return status code + */ +static int rndis_wait ( struct rndis_device *rndis, unsigned int wait_id ) { + unsigned int i; + + /* Record query ID */ + rndis->wait_id = wait_id; + + /* Wait for operation to complete */ + for ( i = 0 ; i < RNDIS_MAX_WAIT_MS ; i++ ) { + + /* Check for completion */ + if ( ! rndis->wait_id ) + return rndis->wait_rc; + + /* Poll RNDIS device */ + rndis->op->poll ( rndis ); + + /* Delay for 1ms */ + mdelay ( 1 ); + } + + DBGC ( rndis, "RNDIS %s timed out waiting for ID %#08x\n", + rndis->name, wait_id ); + return -ETIMEDOUT; +} + +/** + * Transmit message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v type Message type + * @ret rc Return status code + */ +static int rndis_tx_message ( struct rndis_device *rndis, + struct io_buffer *iobuf, unsigned int type ) { + struct rndis_header *header; + int rc; + + /* Prepend RNDIS header */ + header = iob_push ( iobuf, sizeof ( *header ) ); + header->type = cpu_to_le32 ( type ); + header->len = cpu_to_le32 ( iob_len ( iobuf ) ); + + /* Transmit message */ + if ( ( rc = rndis->op->transmit ( rndis, iobuf ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not transmit: %s\n", + rndis->name, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Complete message transmission + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v rc Packet status code + */ +void rndis_tx_complete_err ( struct rndis_device *rndis, + struct io_buffer *iobuf, int rc ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + size_t len = iob_len ( iobuf ); + + /* Sanity check */ + if ( len < sizeof ( *header ) ) { + DBGC ( rndis, "RNDIS %s completed underlength transmission:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + netdev_tx_err ( netdev, NULL, -EINVAL ); + return; + } + header = iobuf->data; + + /* Complete buffer */ + if ( header->type == cpu_to_le32 ( RNDIS_PACKET_MSG ) ) { + netdev_tx_complete_err ( netdev, iobuf, rc ); + } else { + free_iob ( iobuf ); + } +} + +/** + * Transmit data packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rndis_tx_data ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_packet_message *msg; + size_t len = iob_len ( iobuf ); + int rc; + + /* Prepend packet message header */ + msg = iob_push ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->data.offset = cpu_to_le32 ( sizeof ( *msg ) ); + msg->data.len = cpu_to_le32 ( len ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_PACKET_MSG ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Defer transmitted packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @ret rc Return status code + * + * As with netdev_tx_defer(), the caller must ensure that space in the + * transmit descriptor ring is freed up before calling + * rndis_tx_complete(). + * + * Unlike netdev_tx_defer(), this call may fail. + */ +int rndis_tx_defer ( struct rndis_device *rndis, struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + struct rndis_packet_message *msg; + + /* Fail unless this was a packet message. Only packet + * messages correspond to I/O buffers in the network device's + * TX queue; other messages cannot be deferred in this way. + */ + assert ( iob_len ( iobuf ) >= sizeof ( *header ) ); + header = iobuf->data; + if ( header->type != cpu_to_le32 ( RNDIS_PACKET_MSG ) ) + return -ENOTSUP; + + /* Strip RNDIS header and packet message header, to return + * this packet to the state in which we received it. + */ + iob_pull ( iobuf, ( sizeof ( *header ) + sizeof ( *msg ) ) ); + + /* Defer packet */ + netdev_tx_defer ( netdev, iobuf ); + + return 0; +} + +/** + * Receive data packet + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_data ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_packet_message *msg; + size_t len = iob_len ( iobuf ); + size_t data_offset; + size_t data_len; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( rndis, "RNDIS %s received underlength data packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + msg = iobuf->data; + + /* Locate and sanity check data buffer */ + data_offset = le32_to_cpu ( msg->data.offset ); + data_len = le32_to_cpu ( msg->data.len ); + if ( ( data_offset > len ) || ( data_len > ( len - data_offset ) ) ) { + DBGC ( rndis, "RNDIS %s data packet data exceeds packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_data; + } + + /* Strip non-data portions */ + iob_pull ( iobuf, data_offset ); + iob_unput ( iobuf, ( iob_len ( iobuf ) - data_len ) ); + + /* Hand off to network stack */ + netdev_rx ( netdev, iob_disown ( iobuf ) ); + + return; + + err_data: + err_len: + /* Report error to network stack */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Transmit initialisation message + * + * @v rndis RNDIS device + * @v id Request ID + * @ret rc Return status code + */ +static int rndis_tx_initialise ( struct rndis_device *rndis, unsigned int id ) { + struct io_buffer *iobuf; + struct rndis_initialise_message *msg; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = id; /* Non-endian */ + msg->major = cpu_to_le32 ( RNDIS_VERSION_MAJOR ); + msg->minor = cpu_to_le32 ( RNDIS_VERSION_MINOR ); + msg->mtu = cpu_to_le32 ( RNDIS_MTU ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, + RNDIS_INITIALISE_MSG ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Receive initialisation completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_initialise ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_initialise_completion *cmplt; + size_t len = iob_len ( iobuf ); + unsigned int id; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength initialisation " + "completion:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received initialisation completion " + "failure %#08x\n", rndis->name, + le32_to_cpu ( cmplt->status ) ); + rc = -EIO; + goto err_status; + } + + /* Success */ + rc = 0; + + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + free_iob ( iobuf ); +} + +/** + * Initialise RNDIS + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_initialise ( struct rndis_device *rndis ) { + int rc; + + /* Transmit initialisation message */ + if ( ( rc = rndis_tx_initialise ( rndis, RNDIS_INIT_ID ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = rndis_wait ( rndis, RNDIS_INIT_ID ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit halt message + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_tx_halt ( struct rndis_device *rndis ) { + struct io_buffer *iobuf; + struct rndis_halt_message *msg; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + + /* Transmit message */ + if ( ( rc = rndis_tx_message ( rndis, iobuf, RNDIS_HALT_MSG ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Halt RNDIS + * + * @v rndis RNDIS device + * @ret rc Return status code + */ +static int rndis_halt ( struct rndis_device *rndis ) { + int rc; + + /* Transmit halt message */ + if ( ( rc = rndis_tx_halt ( rndis ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Transmit OID message + * + * @v rndis RNDIS device + * @v oid Object ID + * @v data New OID value (or NULL to query current value) + * @v len Length of new OID value + * @ret rc Return status code + */ +static int rndis_tx_oid ( struct rndis_device *rndis, unsigned int oid, + const void *data, size_t len ) { + struct io_buffer *iobuf; + struct rndis_oid_message *msg; + unsigned int type; + int rc; + + /* Allocate I/O buffer */ + iobuf = rndis_alloc_iob ( sizeof ( *msg ) + len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } + + /* Construct message. We use the OID as the request ID. */ + msg = iob_put ( iobuf, sizeof ( *msg ) ); + memset ( msg, 0, sizeof ( *msg ) ); + msg->id = oid; /* Non-endian */ + msg->oid = cpu_to_le32 ( oid ); + msg->offset = cpu_to_le32 ( sizeof ( *msg ) ); + msg->len = cpu_to_le32 ( len ); + memcpy ( iob_put ( iobuf, len ), data, len ); + + /* Transmit message */ + type = ( data ? RNDIS_SET_MSG : RNDIS_QUERY_MSG ); + if ( ( rc = rndis_tx_message ( rndis, iobuf, type ) ) != 0 ) + goto err_tx; + + return 0; + + err_tx: + free_iob ( iobuf ); + err_alloc: + return rc; +} + +/** + * Receive query OID completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_query_oid ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_query_completion *cmplt; + size_t len = iob_len ( iobuf ); + size_t info_offset; + size_t info_len; + unsigned int id; + void *info; + uint32_t *link_status; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength query " + "completion:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received query completion failure " + "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EIO; + goto err_status; + } + + /* Locate and sanity check information buffer */ + info_offset = le32_to_cpu ( cmplt->offset ); + info_len = le32_to_cpu ( cmplt->len ); + if ( ( info_offset > len ) || ( info_len > ( len - info_offset ) ) ) { + DBGC ( rndis, "RNDIS %s query completion information exceeds " + "packet:\n", rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_info; + } + info = ( ( ( void * ) cmplt ) + info_offset ); + + /* Handle OID */ + switch ( id ) { + + case RNDIS_OID_802_3_PERMANENT_ADDRESS: + if ( info_len > sizeof ( netdev->hw_addr ) ) + info_len = sizeof ( netdev->hw_addr ); + memcpy ( netdev->hw_addr, info, info_len ); + break; + + case RNDIS_OID_802_3_CURRENT_ADDRESS: + if ( info_len > sizeof ( netdev->ll_addr ) ) + info_len = sizeof ( netdev->ll_addr ); + memcpy ( netdev->ll_addr, info, info_len ); + break; + + case RNDIS_OID_GEN_MEDIA_CONNECT_STATUS: + if ( info_len != sizeof ( *link_status ) ) { + DBGC ( rndis, "RNDIS %s invalid link status:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EPROTO; + goto err_link_status; + } + link_status = info; + if ( *link_status == 0 ) { + DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); + netdev_link_up ( netdev ); + } else { + DBGC ( rndis, "RNDIS %s link is down: %#08x\n", + rndis->name, le32_to_cpu ( *link_status ) ); + netdev_link_down ( netdev ); + } + break; + + default: + DBGC ( rndis, "RNDIS %s unexpected query completion ID %#08x\n", + rndis->name, id ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EPROTO; + goto err_id; + } + + /* Success */ + rc = 0; + + err_id: + err_link_status: + err_info: + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** + * Receive set OID completion + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_set_oid ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct rndis_set_completion *cmplt; + size_t len = iob_len ( iobuf ); + unsigned int id; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *cmplt ) ) { + DBGC ( rndis, "RNDIS %s received underlength set completion:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + cmplt = iobuf->data; + + /* Extract request ID */ + id = cmplt->id; /* Non-endian */ + + /* Check status */ + if ( cmplt->status ) { + DBGC ( rndis, "RNDIS %s received set completion failure " + "%#08x\n", rndis->name, le32_to_cpu ( cmplt->status ) ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EIO; + goto err_status; + } + + /* Success */ + rc = 0; + + err_status: + /* Record completion result if applicable */ + if ( id == rndis->wait_id ) { + rndis->wait_id = 0; + rndis->wait_rc = rc; + } + err_len: + /* Free I/O buffer */ + free_iob ( iobuf ); +} + +/** + * Query or set OID + * + * @v rndis RNDIS device + * @v oid Object ID + * @v data New OID value (or NULL to query current value) + * @v len Length of new OID value + * @ret rc Return status code + */ +static int rndis_oid ( struct rndis_device *rndis, unsigned int oid, + const void *data, size_t len ) { + int rc; + + /* Transmit query */ + if ( ( rc = rndis_tx_oid ( rndis, oid, data, len ) ) != 0 ) + return rc; + + /* Wait for response */ + if ( ( rc = rndis_wait ( rndis, oid ) ) != 0 ) + return rc; + + return 0; +} + +/** + * Receive indicate status message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +static void rndis_rx_status ( struct rndis_device *rndis, + struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_indicate_status_message *msg; + size_t len = iob_len ( iobuf ); + unsigned int status; + int rc; + + /* Sanity check */ + if ( len < sizeof ( *msg ) ) { + DBGC ( rndis, "RNDIS %s received underlength status message:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -EINVAL; + goto err_len; + } + msg = iobuf->data; + + /* Extract status */ + status = le32_to_cpu ( msg->status ); + + /* Handle status */ + switch ( msg->status ) { + + case RNDIS_STATUS_MEDIA_CONNECT: + DBGC ( rndis, "RNDIS %s link is up\n", rndis->name ); + netdev_link_up ( netdev ); + break; + + case RNDIS_STATUS_MEDIA_DISCONNECT: + DBGC ( rndis, "RNDIS %s link is down\n", rndis->name ); + netdev_link_down ( netdev ); + break; + + case RNDIS_STATUS_WTF_WORLD: + /* Ignore */ + break; + + default: + DBGC ( rndis, "RNDIS %s unexpected status %#08x:\n", + rndis->name, status ); + DBGC_HDA ( rndis, 0, iobuf->data, len ); + rc = -ENOTSUP; + goto err_status; + } + + /* Free I/O buffer */ + free_iob ( iobuf ); + + return; + + err_status: + err_len: + /* Report error via network device statistics */ + netdev_rx_err ( netdev, iobuf, rc ); +} + +/** + * Receive RNDIS message + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v type Message type + */ +static void rndis_rx_message ( struct rndis_device *rndis, + struct io_buffer *iobuf, unsigned int type ) { + struct net_device *netdev = rndis->netdev; + int rc; + + /* Handle packet */ + switch ( type ) { + + case RNDIS_PACKET_MSG: + rndis_rx_data ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_INITIALISE_CMPLT: + rndis_rx_initialise ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_QUERY_CMPLT: + rndis_rx_query_oid ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_SET_CMPLT: + rndis_rx_set_oid ( rndis, iob_disown ( iobuf ) ); + break; + + case RNDIS_INDICATE_STATUS_MSG: + rndis_rx_status ( rndis, iob_disown ( iobuf ) ); + break; + + default: + DBGC ( rndis, "RNDIS %s received unexpected type %#08x\n", + rndis->name, type ); + DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EPROTO; + goto err_type; + } + + return; + + err_type: + /* Report error via network device statistics */ + netdev_rx_err ( netdev, iobuf, rc ); +} + +/** + * Receive packet from underlying transport layer + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + */ +void rndis_rx ( struct rndis_device *rndis, struct io_buffer *iobuf ) { + struct net_device *netdev = rndis->netdev; + struct rndis_header *header; + unsigned int type; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *header ) ) { + DBGC ( rndis, "RNDIS %s received underlength packet:\n", + rndis->name ); + DBGC_HDA ( rndis, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto drop; + } + header = iobuf->data; + + /* Parse and strip header */ + type = le32_to_cpu ( header->type ); + iob_pull ( iobuf, sizeof ( *header ) ); + + /* Handle message */ + rndis_rx_message ( rndis, iob_disown ( iobuf ), type ); + + return; + + drop: + /* Record error */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Discard packet from underlying transport layer + * + * @v rndis RNDIS device + * @v iobuf I/O buffer + * @v rc Packet status code + */ +void rndis_rx_err ( struct rndis_device *rndis, struct io_buffer *iobuf, + int rc ) { + struct net_device *netdev = rndis->netdev; + + /* Record error */ + netdev_rx_err ( netdev, iob_disown ( iobuf ), rc ); +} + +/** + * Set receive filter + * + * @v rndis RNDIS device + * @v filter Receive filter + * @ret rc Return status code + */ +static int rndis_filter ( struct rndis_device *rndis, unsigned int filter ) { + uint32_t value = cpu_to_le32 ( filter ); + int rc; + + /* Set receive filter */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_CURRENT_PACKET_FILTER, + &value, sizeof ( value ) ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not set receive filter to %#08x: " + "%s\n", rndis->name, filter, strerror ( rc ) ); + return rc; + } + + return 0; +} + +/** + * Open network device + * + * @v netdev Network device + * @ret rc Return status code + */ +static int rndis_open ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + int rc; + + /* Open RNDIS device */ + if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not open: %s\n", + rndis->name, strerror ( rc ) ); + goto err_open; + } + + /* Initialise RNDIS */ + if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) + goto err_initialise; + + /* Set receive filter */ + if ( ( rc = rndis_filter ( rndis, ( RNDIS_FILTER_UNICAST | + RNDIS_FILTER_MULTICAST | + RNDIS_FILTER_ALL_MULTICAST | + RNDIS_FILTER_BROADCAST | + RNDIS_FILTER_PROMISCUOUS ) ) ) != 0) + goto err_set_filter; + + /* Update link status */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + NULL, 0 ) ) != 0 ) + goto err_query_link; + + return 0; + + err_query_link: + err_set_filter: + rndis_halt ( rndis ); + err_initialise: + rndis->op->close ( rndis ); + err_open: + return rc; +} + +/** + * Close network device + * + * @v netdev Network device + */ +static void rndis_close ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + + /* Clear receive filter */ + rndis_filter ( rndis, 0 ); + + /* Halt RNDIS device */ + rndis_halt ( rndis ); + + /* Close RNDIS device */ + rndis->op->close ( rndis ); +} + +/** + * Transmit packet + * + * @v netdev Network device + * @v iobuf I/O buffer + * @ret rc Return status code + */ +static int rndis_transmit ( struct net_device *netdev, + struct io_buffer *iobuf ) { + struct rndis_device *rndis = netdev->priv; + + /* Transmit data packet */ + return rndis_tx_data ( rndis, iobuf ); +} + +/** + * Poll for completed and received packets + * + * @v netdev Network device + */ +static void rndis_poll ( struct net_device *netdev ) { + struct rndis_device *rndis = netdev->priv; + + /* Poll RNDIS device */ + rndis->op->poll ( rndis ); +} + +/** Network device operations */ +static struct net_device_operations rndis_operations = { + .open = rndis_open, + .close = rndis_close, + .transmit = rndis_transmit, + .poll = rndis_poll, +}; + +/** + * Allocate RNDIS device + * + * @v priv_len Length of private data + * @ret rndis RNDIS device, or NULL on allocation failure + */ +struct rndis_device * alloc_rndis ( size_t priv_len ) { + struct net_device *netdev; + struct rndis_device *rndis; + + /* Allocate and initialise structure */ + netdev = alloc_etherdev ( sizeof ( *rndis ) + priv_len ); + if ( ! netdev ) + return NULL; + netdev_init ( netdev, &rndis_operations ); + rndis = netdev->priv; + rndis->netdev = netdev; + rndis->priv = ( ( ( void * ) rndis ) + sizeof ( *rndis ) ); + + return rndis; +} + +/** + * Register RNDIS device + * + * @v rndis RNDIS device + * @ret rc Return status code + * + * Note that this routine will open and use the RNDIS device in order + * to query the MAC address. The device must be immediately ready for + * use prior to registration. + */ +int register_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + int rc; + + /* Assign device name (for debugging) */ + rndis->name = netdev->dev->name; + + /* Register network device */ + if ( ( rc = register_netdev ( netdev ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not register: %s\n", + rndis->name, strerror ( rc ) ); + goto err_register; + } + + /* Open RNDIS device to read MAC addresses */ + if ( ( rc = rndis->op->open ( rndis ) ) != 0 ) { + DBGC ( rndis, "RNDIS %s could not open: %s\n", + rndis->name, strerror ( rc ) ); + goto err_open; + } + + /* Initialise RNDIS */ + if ( ( rc = rndis_initialise ( rndis ) ) != 0 ) + goto err_initialise; + + /* Query permanent MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_PERMANENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_permanent; + + /* Query current MAC address */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_802_3_CURRENT_ADDRESS, + NULL, 0 ) ) != 0 ) + goto err_query_current; + + /* Get link status */ + if ( ( rc = rndis_oid ( rndis, RNDIS_OID_GEN_MEDIA_CONNECT_STATUS, + NULL, 0 ) ) != 0 ) + goto err_query_link; + + /* Halt RNDIS device */ + rndis_halt ( rndis ); + + /* Close RNDIS device */ + rndis->op->close ( rndis ); + + return 0; + + err_query_link: + err_query_current: + err_query_permanent: + rndis_halt ( rndis ); + err_initialise: + rndis->op->close ( rndis ); + err_open: + unregister_netdev ( netdev ); + err_register: + return rc; +} + +/** + * Unregister RNDIS device + * + * @v rndis RNDIS device + */ +void unregister_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + + /* Unregister network device */ + unregister_netdev ( netdev ); +} + +/** + * Free RNDIS device + * + * @v rndis RNDIS device + */ +void free_rndis ( struct rndis_device *rndis ) { + struct net_device *netdev = rndis->netdev; + + /* Free network device */ + netdev_nullify ( netdev ); + netdev_put ( netdev ); +} diff --git a/roms/ipxe/src/net/socket.c b/roms/ipxe/src/net/socket.c index 24f6a0892..2009ab237 100644 --- a/roms/ipxe/src/net/socket.c +++ b/roms/ipxe/src/net/socket.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <errno.h> diff --git a/roms/ipxe/src/net/stp.c b/roms/ipxe/src/net/stp.c new file mode 100644 index 000000000..d4e65a1a2 --- /dev/null +++ b/roms/ipxe/src/net/stp.c @@ -0,0 +1,152 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <errno.h> +#include <byteswap.h> +#include <ipxe/netdevice.h> +#include <ipxe/ethernet.h> +#include <ipxe/iobuf.h> +#include <ipxe/timer.h> +#include <ipxe/stp.h> + +/** @file + * + * Spanning Tree Protocol (STP) + * + */ + +/* Disambiguate the various error causes */ +#define ENOTSUP_PROTOCOL __einfo_error ( EINFO_ENOTSUP_PROTOCOL ) +#define EINFO_ENOTSUP_PROTOCOL \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Non-STP packet received" ) +#define ENOTSUP_VERSION __einfo_error ( EINFO_ENOTSUP_VERSION ) +#define EINFO_ENOTSUP_VERSION \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Legacy STP packet received" ) +#define ENOTSUP_TYPE __einfo_error ( EINFO_ENOTSUP_TYPE ) +#define EINFO_ENOTSUP_TYPE \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, \ + "Non-RSTP packet received" ) + +/** + * Process incoming STP packets + * + * @v iobuf I/O buffer + * @v netdev Network device + * @v ll_source Link-layer source address + * @v flags Packet flags + * @ret rc Return status code + */ +static int stp_rx ( struct io_buffer *iobuf, struct net_device *netdev, + const void *ll_dest __unused, + const void *ll_source __unused, + unsigned int flags __unused ) { + struct stp_bpdu *stp; + unsigned int hello; + int rc; + + /* Sanity check */ + if ( iob_len ( iobuf ) < sizeof ( *stp ) ) { + DBGC ( netdev, "STP %s received underlength packet (%zd " + "bytes):\n", netdev->name, iob_len ( iobuf ) ); + DBGC_HDA ( netdev, 0, iobuf->data, iob_len ( iobuf ) ); + rc = -EINVAL; + goto done; + } + stp = iobuf->data; + + /* Ignore non-RSTP packets */ + if ( stp->protocol != htons ( STP_PROTOCOL ) ) { + DBGC ( netdev, "STP %s ignoring non-STP packet (protocol " + "%#04x)\n", netdev->name, ntohs ( stp->protocol ) ); + rc = -ENOTSUP_PROTOCOL; + goto done; + } + if ( stp->version < STP_VERSION_RSTP ) { + DBGC ( netdev, "STP %s received legacy STP packet (version " + "%#02x)\n", netdev->name, stp->version ); + rc = -ENOTSUP_VERSION; + goto done; + } + if ( stp->type != STP_TYPE_RSTP ) { + DBGC ( netdev, "STP %s received non-RSTP packet (type %#02x)\n", + netdev->name, stp->type ); + rc = -ENOTSUP_TYPE; + goto done; + } + + /* Dump information */ + DBGC2 ( netdev, "STP %s %s port %#04x flags %#02x hello %d delay %d\n", + netdev->name, eth_ntoa ( stp->sender.mac ), ntohs ( stp->port ), + stp->flags, ntohs ( stp->hello ), ntohs ( stp->delay ) ); + + /* Check if port is forwarding */ + if ( ! ( stp->flags & STP_FL_FORWARDING ) ) { + /* Port is not forwarding: block link for two hello times */ + DBGC ( netdev, "STP %s %s port %#04x flags %#02x is not " + "forwarding\n", + netdev->name, eth_ntoa ( stp->sender.mac ), + ntohs ( stp->port ), stp->flags ); + hello = ( ( ntohs ( stp->hello ) * TICKS_PER_SEC ) / 256 ); + netdev_link_block ( netdev, ( hello * 2 ) ); + rc = -ENETUNREACH; + goto done; + } + + /* Success */ + if ( netdev_link_blocked ( netdev ) ) { + DBGC ( netdev, "STP %s %s port %#04x flags %#02x is " + "forwarding\n", + netdev->name, eth_ntoa ( stp->sender.mac ), + ntohs ( stp->port ), stp->flags ); + } + netdev_link_unblock ( netdev ); + rc = 0; + + done: + free_iob ( iobuf ); + return rc; +} + +/** + * Transcribe STP address + * + * @v net_addr STP address + * @ret string "<STP>" + * + * This operation is meaningless for the STP protocol. + */ +static const char * stp_ntoa ( const void *net_addr __unused ) { + return "<STP>"; +} + +/** STP network protocol */ +struct net_protocol stp_protocol __net_protocol = { + .name = "STP", + .net_proto = htons ( ETH_P_STP ), + .rx = stp_rx, + .ntoa = stp_ntoa, +}; diff --git a/roms/ipxe/src/net/tcp.c b/roms/ipxe/src/net/tcp.c index 987cb63e1..c69c83b85 100644 --- a/roms/ipxe/src/net/tcp.c +++ b/roms/ipxe/src/net/tcp.c @@ -26,7 +26,7 @@ * */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** A TCP connection */ struct tcp_connection { @@ -101,8 +101,9 @@ struct tcp_connection { * Equivalent to Rcv.Wind.Scale in RFC 1323 terminology */ uint8_t rcv_win_scale; - /** Maximum receive window */ - uint32_t max_rcv_win; + + /** Selective acknowledgement list (in host-endian order) */ + struct tcp_sack_block sack[TCP_SACK_MAX]; /** Transmit queue */ struct list_head tx_queue; @@ -129,6 +130,8 @@ enum tcp_flags { TCP_TS_ENABLED = 0x0002, /** TCP acknowledgement is pending */ TCP_ACK_PENDING = 0x0004, + /** TCP selective acknowledgement is enabled */ + TCP_SACK_ENABLED = 0x0008, }; /** TCP internal header @@ -143,6 +146,8 @@ struct tcp_rx_queued_header { * enqueued, and so excludes the SYN, if present. */ uint32_t seq; + /** Next SEQ value, in host-endian order */ + uint32_t nxt; /** Flags * * Only FIN is valid within this flags byte; all other flags @@ -284,7 +289,6 @@ static int tcp_open ( struct interface *xfer, struct sockaddr *peer, tcp->tcp_state = TCP_STATE_SENT ( TCP_SYN ); tcp_dump_state ( tcp ); tcp->snd_seq = random(); - tcp->max_rcv_win = TCP_MAX_WINDOW_SIZE; INIT_LIST_HEAD ( &tcp->tx_queue ); INIT_LIST_HEAD ( &tcp->rx_queue ); memcpy ( &tcp->peer, st_peer, sizeof ( tcp->peer ) ); @@ -396,6 +400,7 @@ static void tcp_close ( struct tcp_connection *tcp, int rc ) { tcp->tcp_state |= TCP_STATE_SENT ( TCP_FIN ); tcp_dump_state ( tcp ); + process_add ( &tcp->process ); /* Add a pending operation for the FIN */ pending_get ( &tcp->pending_flags ); @@ -450,6 +455,94 @@ static size_t tcp_xfer_window ( struct tcp_connection *tcp ) { } /** + * Find selective acknowledgement block + * + * @v tcp TCP connection + * @v seq SEQ value in SACK block (in host-endian order) + * @v sack SACK block to fill in (in host-endian order) + * @ret len Length of SACK block + */ +static uint32_t tcp_sack_block ( struct tcp_connection *tcp, uint32_t seq, + struct tcp_sack_block *sack ) { + struct io_buffer *iobuf; + struct tcp_rx_queued_header *tcpqhdr; + uint32_t left = tcp->rcv_ack; + uint32_t right = left; + + /* Find highest block which does not start after SEQ */ + list_for_each_entry ( iobuf, &tcp->rx_queue, list ) { + tcpqhdr = iobuf->data; + if ( tcp_cmp ( tcpqhdr->seq, right ) > 0 ) { + if ( tcp_cmp ( tcpqhdr->seq, seq ) > 0 ) + break; + left = tcpqhdr->seq; + } + if ( tcp_cmp ( tcpqhdr->nxt, right ) > 0 ) + right = tcpqhdr->nxt; + } + + /* Fail if this block does not contain SEQ */ + if ( tcp_cmp ( right, seq ) < 0 ) + return 0; + + /* Populate SACK block */ + sack->left = left; + sack->right = right; + return ( right - left ); +} + +/** + * Update TCP selective acknowledgement list + * + * @v tcp TCP connection + * @v seq SEQ value in first SACK block (in host-endian order) + * @ret count Number of SACK blocks + */ +static unsigned int tcp_sack ( struct tcp_connection *tcp, uint32_t seq ) { + struct tcp_sack_block sack[TCP_SACK_MAX]; + unsigned int old = 0; + unsigned int new = 0; + unsigned int i; + uint32_t len; + + /* Populate first new SACK block */ + len = tcp_sack_block ( tcp, seq, &sack[0] ); + if ( len ) + new++; + + /* Populate remaining new SACK blocks based on old SACK blocks */ + for ( old = 0 ; old < TCP_SACK_MAX ; old++ ) { + + /* Stop if we run out of space in the new list */ + if ( new == TCP_SACK_MAX ) + break; + + /* Skip empty old SACK blocks */ + if ( tcp->sack[old].left == tcp->sack[old].right ) + continue; + + /* Populate new SACK block */ + len = tcp_sack_block ( tcp, tcp->sack[old].left, &sack[new] ); + if ( len == 0 ) + continue; + + /* Eliminate duplicates */ + for ( i = 0 ; i < new ; i++ ) { + if ( sack[i].left == sack[new].left ) { + new--; + break; + } + } + new++; + } + + /* Update SACK list */ + memset ( tcp->sack, 0, sizeof ( tcp->sack ) ); + memcpy ( tcp->sack, sack, ( new * sizeof ( tcp->sack[0] ) ) ); + return new; +} + +/** * Process TCP transmit queue * * @v tcp TCP connection @@ -493,9 +586,10 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, } /** - * Transmit any outstanding data + * Transmit any outstanding data (with selective acknowledgement) * * @v tcp TCP connection + * @v sack_seq SEQ for first selective acknowledgement (if any) * * Transmits any outstanding data on the connection. * @@ -503,17 +597,22 @@ static size_t tcp_process_tx_queue ( struct tcp_connection *tcp, size_t max_len, * will have been started if necessary, and so the stack will * eventually attempt to retransmit the failed packet. */ -static void tcp_xmit ( struct tcp_connection *tcp ) { +static void tcp_xmit_sack ( struct tcp_connection *tcp, uint32_t sack_seq ) { struct io_buffer *iobuf; struct tcp_header *tcphdr; struct tcp_mss_option *mssopt; struct tcp_window_scale_padded_option *wsopt; struct tcp_timestamp_padded_option *tsopt; + struct tcp_sack_permitted_padded_option *spopt; + struct tcp_sack_padded_option *sackopt; + struct tcp_sack_block *sack; void *payload; unsigned int flags; + unsigned int sack_count; + unsigned int i; size_t len = 0; + size_t sack_len; uint32_t seq_len; - uint32_t app_win; uint32_t max_rcv_win; uint32_t max_representable_win; int rc; @@ -567,10 +666,9 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { tcp_process_tx_queue ( tcp, len, iobuf, 0 ); /* Expand receive window if possible */ - max_rcv_win = tcp->max_rcv_win; - app_win = xfer_window ( &tcp->xfer ); - if ( max_rcv_win > app_win ) - max_rcv_win = app_win; + max_rcv_win = xfer_window ( &tcp->xfer ); + if ( max_rcv_win > TCP_MAX_WINDOW_SIZE ) + max_rcv_win = TCP_MAX_WINDOW_SIZE; max_representable_win = ( 0xffff << tcp->rcv_win_scale ); if ( max_rcv_win > max_representable_win ) max_rcv_win = max_representable_win; @@ -590,6 +688,10 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { wsopt->wsopt.kind = TCP_OPTION_WS; wsopt->wsopt.length = sizeof ( wsopt->wsopt ); wsopt->wsopt.scale = TCP_RX_WINDOW_SCALE; + spopt = iob_push ( iobuf, sizeof ( *spopt ) ); + memset ( spopt->nop, TCP_OPTION_NOP, sizeof ( spopt ) ); + spopt->spopt.kind = TCP_OPTION_SACK_PERMITTED; + spopt->spopt.length = sizeof ( spopt->spopt ); } if ( ( flags & TCP_SYN ) || ( tcp->flags & TCP_TS_ENABLED ) ) { tsopt = iob_push ( iobuf, sizeof ( *tsopt ) ); @@ -599,6 +701,21 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { tsopt->tsopt.tsval = htonl ( currticks() ); tsopt->tsopt.tsecr = htonl ( tcp->ts_recent ); } + if ( ( tcp->flags & TCP_SACK_ENABLED ) && + ( ! list_empty ( &tcp->rx_queue ) ) && + ( ( sack_count = tcp_sack ( tcp, sack_seq ) ) != 0 ) ) { + sack_len = ( sack_count * sizeof ( *sack ) ); + sackopt = iob_push ( iobuf, ( sizeof ( *sackopt ) + sack_len )); + memset ( sackopt->nop, TCP_OPTION_NOP, sizeof ( sackopt->nop )); + sackopt->sackopt.kind = TCP_OPTION_SACK; + sackopt->sackopt.length = + ( sizeof ( sackopt->sackopt ) + sack_len ); + sack = ( ( ( void * ) sackopt ) + sizeof ( *sackopt ) ); + for ( i = 0 ; i < sack_count ; i++, sack++ ) { + sack->left = htonl ( tcp->sack[i].left ); + sack->right = htonl ( tcp->sack[i].right ); + } + } if ( len != 0 ) flags |= TCP_PSH; tcphdr = iob_push ( iobuf, sizeof ( *tcphdr ) ); @@ -635,6 +752,17 @@ static void tcp_xmit ( struct tcp_connection *tcp ) { profile_stop ( &tcp_tx_profiler ); } +/** + * Transmit any outstanding data + * + * @v tcp TCP connection + */ +static void tcp_xmit ( struct tcp_connection *tcp ) { + + /* Transmit without an explicit first SACK */ + tcp_xmit_sack ( tcp, tcp->rcv_ack ); +} + /** TCP process descriptor */ static struct process_descriptor tcp_process_desc = PROC_DESC_ONCE ( struct tcp_connection, process, tcp_xmit ); @@ -804,6 +932,12 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, case TCP_OPTION_WS: options->wsopt = data; break; + case TCP_OPTION_SACK_PERMITTED: + options->spopt = data; + break; + case TCP_OPTION_SACK: + /* Ignore received SACKs */ + break; case TCP_OPTION_TS: options->tsopt = data; break; @@ -823,6 +957,7 @@ static void tcp_rx_opts ( struct tcp_connection *tcp, const void *data, * @v seq_len Sequence space length to consume */ static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { + unsigned int sack; /* Sanity check */ assert ( seq_len > 0 ); @@ -840,6 +975,16 @@ static void tcp_rx_seq ( struct tcp_connection *tcp, uint32_t seq_len ) { /* Update timestamp */ tcp->ts_recent = tcp->ts_val; + /* Update SACK list */ + for ( sack = 0 ; sack < TCP_SACK_MAX ; sack++ ) { + if ( tcp->sack[sack].left == tcp->sack[sack].right ) + continue; + if ( tcp_cmp ( tcp->sack[sack].left, tcp->rcv_ack ) < 0 ) + tcp->sack[sack].left = tcp->rcv_ack; + if ( tcp_cmp ( tcp->sack[sack].right, tcp->rcv_ack ) < 0 ) + tcp->sack[sack].right = tcp->rcv_ack; + } + /* Mark ACK as pending */ tcp->flags |= TCP_ACK_PENDING; } @@ -860,6 +1005,8 @@ static int tcp_rx_syn ( struct tcp_connection *tcp, uint32_t seq, tcp->rcv_ack = seq; if ( options->tsopt ) tcp->flags |= TCP_TS_ENABLED; + if ( options->spopt ) + tcp->flags |= TCP_SACK_ENABLED; if ( options->wsopt ) { tcp->snd_win_scale = options->wsopt->scale; tcp->rcv_win_scale = TCP_RX_WINDOW_SCALE; @@ -1070,6 +1217,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, struct io_buffer *queued; size_t len; uint32_t seq_len; + uint32_t nxt; /* Calculate remaining flags and sequence length. Note that * SYN, if present, has already been processed by this point. @@ -1077,6 +1225,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, flags &= TCP_FIN; len = iob_len ( iobuf ); seq_len = ( len + ( flags ? 1 : 0 ) ); + nxt = ( seq + seq_len ); /* Discard immediately (to save memory) if: * @@ -1087,7 +1236,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, */ if ( ( ! ( tcp->tcp_state & TCP_STATE_RCVD ( TCP_SYN ) ) ) || ( tcp_cmp ( seq, tcp->rcv_ack + tcp->rcv_win ) >= 0 ) || - ( tcp_cmp ( seq + seq_len, tcp->rcv_ack ) < 0 ) || + ( tcp_cmp ( nxt, tcp->rcv_ack ) < 0 ) || ( seq_len == 0 ) ) { free_iob ( iobuf ); return; @@ -1096,6 +1245,7 @@ static void tcp_rx_enqueue ( struct tcp_connection *tcp, uint32_t seq, /* Add internal header */ tcpqhdr = iob_push ( iobuf, sizeof ( *tcpqhdr ) ); tcpqhdr->seq = seq; + tcpqhdr->nxt = nxt; tcpqhdr->flags = flags; /* Add to RX queue */ @@ -1289,7 +1439,7 @@ static int tcp_rx ( struct io_buffer *iobuf, if ( list_empty ( &tcp->rx_queue ) ) { process_add ( &tcp->process ); } else { - tcp_xmit ( tcp ); + tcp_xmit_sack ( tcp, seq ); } /* If this packet was the last we expect to receive, set up @@ -1328,24 +1478,12 @@ struct tcpip_protocol tcp_protocol __tcpip_protocol = { static unsigned int tcp_discard ( void ) { struct tcp_connection *tcp; struct io_buffer *iobuf; - struct tcp_rx_queued_header *tcpqhdr; - uint32_t max_win; unsigned int discarded = 0; /* Try to drop one queued RX packet from each connection */ list_for_each_entry ( tcp, &tcp_conns, list ) { list_for_each_entry_reverse ( iobuf, &tcp->rx_queue, list ) { - /* Limit window to prevent future discards */ - tcpqhdr = iobuf->data; - max_win = ( tcpqhdr->seq - tcp->rcv_ack ); - if ( max_win < tcp->max_rcv_win ) { - DBGC ( tcp, "TCP %p reducing maximum window " - "from %d to %d\n", - tcp, tcp->max_rcv_win, max_win ); - tcp->max_rcv_win = max_win; - } - /* Remove packet from queue */ list_del ( &iobuf->list ); free_iob ( iobuf ); @@ -1365,12 +1503,67 @@ struct cache_discarder tcp_discarder __cache_discarder ( CACHE_NORMAL ) = { }; /** + * Find first TCP connection that has not yet been closed + * + * @ret tcp First unclosed connection, or NULL + */ +static struct tcp_connection * tcp_first_unclosed ( void ) { + struct tcp_connection *tcp; + + /* Find first connection which has not yet been closed */ + list_for_each_entry ( tcp, &tcp_conns, list ) { + if ( ! ( tcp->flags & TCP_XFER_CLOSED ) ) + return tcp; + } + return NULL; +} + +/** + * Find first TCP connection that has not yet finished all operations + * + * @ret tcp First unfinished connection, or NULL + */ +static struct tcp_connection * tcp_first_unfinished ( void ) { + struct tcp_connection *tcp; + + /* Find first connection which has not yet closed gracefully, + * or which still has a pending transmission (e.g. to ACK the + * received FIN). + */ + list_for_each_entry ( tcp, &tcp_conns, list ) { + if ( ( ! TCP_CLOSED_GRACEFULLY ( tcp->tcp_state ) ) || + process_running ( &tcp->process ) ) { + return tcp; + } + } + return NULL; +} + +/** * Shut down all TCP connections * */ static void tcp_shutdown ( int booting __unused ) { struct tcp_connection *tcp; + unsigned long start; + unsigned long elapsed; + + /* Initiate a graceful close of all connections, allowing for + * the fact that the connection list may change as we do so. + */ + while ( ( tcp = tcp_first_unclosed() ) ) { + DBGC ( tcp, "TCP %p closing for shutdown\n", tcp ); + tcp_close ( tcp, -ECANCELED ); + } + + /* Wait for all connections to finish closing gracefully */ + start = currticks(); + while ( ( tcp = tcp_first_unfinished() ) && + ( ( elapsed = ( currticks() - start ) ) < TCP_FINISH_TIMEOUT )){ + step(); + } + /* Forcibly close any remaining connections */ while ( ( tcp = list_first_entry ( &tcp_conns, struct tcp_connection, list ) ) != NULL ) { tcp->tcp_state = TCP_CLOSED; @@ -1380,7 +1573,7 @@ static void tcp_shutdown ( int booting __unused ) { } /** TCP shutdown function */ -struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_EARLY ) = { +struct startup_fn tcp_startup_fn __startup_fn ( STARTUP_LATE ) = { .shutdown = tcp_shutdown, }; diff --git a/roms/ipxe/src/net/tcp/http.c b/roms/ipxe/src/net/tcp/http.c index 90bae9d7a..b000ed80f 100644 --- a/roms/ipxe/src/net/tcp/http.c +++ b/roms/ipxe/src/net/tcp/http.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -26,26 +30,20 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#include <stddef.h> #include <ipxe/open.h> #include <ipxe/http.h> #include <ipxe/features.h> FEATURE ( FEATURE_PROTOCOL, "HTTP", DHCP_EB_FEATURE_HTTP, 1 ); -/** - * Initiate an HTTP connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int http_open ( struct interface *xfer, struct uri *uri ) { - return http_open_filter ( xfer, uri, HTTP_PORT, NULL ); -} - /** HTTP URI opener */ struct uri_opener http_uri_opener __uri_opener = { .scheme = "http", - .open = http_open, + .open = http_open_uri, +}; + +/** HTTP URI scheme */ +struct http_scheme http_scheme __http_scheme = { + .name = "http", + .port = HTTP_PORT, }; diff --git a/roms/ipxe/src/net/tcp/httpauth.c b/roms/ipxe/src/net/tcp/httpauth.c new file mode 100644 index 000000000..fb6dcd035 --- /dev/null +++ b/roms/ipxe/src/net/tcp/httpauth.c @@ -0,0 +1,190 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) authentication + * + */ + +#include <stdio.h> +#include <strings.h> +#include <errno.h> +#include <ipxe/http.h> + +/** + * Identify authentication scheme + * + * @v http HTTP transaction + * @v name Scheme name + * @ret auth Authentication scheme, or NULL + */ +static struct http_authentication * http_authentication ( const char *name ) { + struct http_authentication *auth; + + /* Identify authentication scheme */ + for_each_table_entry ( auth, HTTP_AUTHENTICATIONS ) { + if ( strcasecmp ( name, auth->name ) == 0 ) + return auth; + } + + return NULL; +} + +/** An HTTP "WWW-Authenticate" response field */ +struct http_www_authenticate_field { + /** Name */ + const char *name; + /** Offset */ + size_t offset; +}; + +/** Define an HTTP "WWW-Authenticate" response field */ +#define HTTP_WWW_AUTHENTICATE_FIELD( _name ) { \ + .name = #_name, \ + .offset = offsetof ( struct http_transaction, \ + response.auth._name ), \ + } + +/** + * Set HTTP "WWW-Authenticate" response field value + * + * @v http HTTP transaction + * @v field Response field + * @v value Field value + */ +static inline void +http_www_auth_field ( struct http_transaction *http, + struct http_www_authenticate_field *field, char *value ) { + char **ptr; + + ptr = ( ( ( void * ) http ) + field->offset ); + *ptr = value; +} + +/** HTTP "WWW-Authenticate" fields */ +static struct http_www_authenticate_field http_www_auth_fields[] = { + HTTP_WWW_AUTHENTICATE_FIELD ( realm ), + HTTP_WWW_AUTHENTICATE_FIELD ( qop ), + HTTP_WWW_AUTHENTICATE_FIELD ( algorithm ), + HTTP_WWW_AUTHENTICATE_FIELD ( nonce ), + HTTP_WWW_AUTHENTICATE_FIELD ( opaque ), +}; + +/** + * Parse HTTP "WWW-Authenticate" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_www_authenticate ( struct http_transaction *http, + char *line ) { + struct http_www_authenticate_field *field; + char *name; + char *key; + char *value; + unsigned int i; + + /* Get scheme name */ + name = http_token ( &line, NULL ); + if ( ! name ) { + DBGC ( http, "HTTP %p malformed WWW-Authenticate \"%s\"\n", + http, value ); + return -EPROTO; + } + + /* Identify scheme */ + http->response.auth.auth = http_authentication ( name ); + if ( ! http->response.auth.auth ) { + DBGC ( http, "HTTP %p unrecognised authentication scheme " + "\"%s\"\n", http, name ); + return -ENOTSUP; + } + + /* Process fields */ + while ( ( key = http_token ( &line, &value ) ) ) { + for ( i = 0 ; i < ( sizeof ( http_www_auth_fields ) / + sizeof ( http_www_auth_fields[0] ) ) ; i++){ + field = &http_www_auth_fields[i]; + if ( strcasecmp ( key, field->name ) == 0 ) + http_www_auth_field ( http, field, value ); + } + } + + /* Allow HTTP request to be retried if the request had not + * already tried authentication. + */ + if ( ! http->request.auth.auth ) + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + +/** HTTP "WWW-Authenticate" header */ +struct http_response_header +http_response_www_authenticate __http_response_header = { + .name = "WWW-Authenticate", + .parse = http_parse_www_authenticate, +}; + +/** + * Construct HTTP "Authorization" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_authorization ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_authentication *auth = http->request.auth.auth; + size_t used; + int auth_len; + int rc; + + /* Do nothing unless we have an authentication scheme */ + if ( ! auth ) + return 0; + + /* Construct header */ + used = snprintf ( buf, len, "%s ", auth->name ); + auth_len = auth->format ( http, ( buf + used ), + ( ( used < len ) ? ( len - used ) : 0 ) ); + if ( auth_len < 0 ) { + rc = auth_len; + return rc; + } + used += auth_len; + + return used; +} + +/** HTTP "Authorization" header */ +struct http_request_header http_request_authorization __http_request_header = { + .name = "Authorization", + .format = http_format_authorization, +}; diff --git a/roms/ipxe/src/net/tcp/httpbasic.c b/roms/ipxe/src/net/tcp/httpbasic.c new file mode 100644 index 000000000..7ed7de9e7 --- /dev/null +++ b/roms/ipxe/src/net/tcp/httpbasic.c @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) Basic authentication + * + */ + +#include <stdio.h> +#include <errno.h> +#include <ipxe/uri.h> +#include <ipxe/base64.h> +#include <ipxe/http.h> + +/* Disambiguate the various error causes */ +#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) +#define EINFO_EACCES_USERNAME \ + __einfo_uniqify ( EINFO_EACCES, 0x01, \ + "No username available for Basic authentication" ) + +/** + * Perform HTTP Basic authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_basic_authenticate ( struct http_transaction *http ) { + struct http_request_auth *req = &http->request.auth; + + /* Record username and password */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for Basic " + "authentication\n", http ); + return -EACCES_USERNAME; + } + req->username = http->uri->user; + req->password = ( http->uri->password ? http->uri->password : "" ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for Basic authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_basic_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth *req = &http->request.auth; + size_t user_pw_len = ( strlen ( req->username ) + 1 /* ":" */ + + strlen ( req->password ) ); + char user_pw[ user_pw_len + 1 /* NUL */ ]; + + /* Sanity checks */ + assert ( req->username != NULL ); + assert ( req->password != NULL ); + + /* Construct "user:password" string */ + snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", + req->username, req->password ); + + /* Construct response */ + return base64_encode ( user_pw, user_pw_len, buf, len ); +} + +/** HTTP Basic authentication scheme */ +struct http_authentication http_basic_auth __http_authentication = { + .name = "Basic", + .authenticate = http_basic_authenticate, + .format = http_format_basic_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_basic_auth ); +REQUIRE_OBJECT ( httpauth ); diff --git a/roms/ipxe/src/net/tcp/httpblock.c b/roms/ipxe/src/net/tcp/httpblock.c new file mode 100644 index 000000000..e124ad2d6 --- /dev/null +++ b/roms/ipxe/src/net/tcp/httpblock.c @@ -0,0 +1,134 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) block device + * + */ + +#include <stdint.h> +#include <ipxe/uaccess.h> +#include <ipxe/blocktrans.h> +#include <ipxe/blockdev.h> +#include <ipxe/acpi.h> +#include <ipxe/http.h> + +/** Block size used for HTTP block device requests */ +#define HTTP_BLKSIZE 512 + +/** + * Read from block device + * + * @v http HTTP transaction + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer + * @ret rc Return status code + */ +int http_block_read ( struct http_transaction *http, struct interface *data, + uint64_t lba, unsigned int count, userptr_t buffer, + size_t len ) { + struct http_request_range range; + int rc; + + /* Sanity check */ + assert ( len == ( count * HTTP_BLKSIZE ) ); + + /* Construct request range descriptor */ + range.start = ( lba * HTTP_BLKSIZE ); + range.len = len; + + /* Start a range request to retrieve the block(s) */ + if ( ( rc = http_open ( data, &http_get, http->uri, &range, + NULL ) ) != 0 ) + goto err_open; + + /* Insert block device translator */ + if ( ( rc = block_translate ( data, buffer, len ) ) != 0 ) { + DBGC ( http, "HTTP %p could not insert block translator: %s\n", + http, strerror ( rc ) ); + goto err_translate; + } + + return 0; + + err_translate: + intf_restart ( data, rc ); + err_open: + return rc; +} + +/** + * Read block device capacity + * + * @v control Control interface + * @v data Data interface + * @ret rc Return status code + */ +int http_block_read_capacity ( struct http_transaction *http, + struct interface *data ) { + int rc; + + /* Start a HEAD request to retrieve the capacity */ + if ( ( rc = http_open ( data, &http_head, http->uri, NULL, + NULL ) ) != 0 ) + goto err_open; + + /* Insert block device translator */ + if ( ( rc = block_translate ( data, UNULL, HTTP_BLKSIZE ) ) != 0 ) { + DBGC ( http, "HTTP %p could not insert block translator: %s\n", + http, strerror ( rc ) ); + goto err_translate; + } + + return 0; + + err_translate: + intf_restart ( data, rc ); + err_open: + return rc; +} + +/** + * Describe device in ACPI table + * + * @v http HTTP transaction + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +int http_acpi_describe ( struct http_transaction *http, + struct acpi_description_header *acpi, size_t len ) { + + DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", + http ); + ( void ) acpi; + ( void ) len; + return 0; +} diff --git a/roms/ipxe/src/net/tcp/httpconn.c b/roms/ipxe/src/net/tcp/httpconn.c new file mode 100644 index 000000000..7e4877b7b --- /dev/null +++ b/roms/ipxe/src/net/tcp/httpconn.c @@ -0,0 +1,309 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) connection management + * + */ + +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <byteswap.h> +#include <ipxe/tcpip.h> +#include <ipxe/uri.h> +#include <ipxe/timer.h> +#include <ipxe/xfer.h> +#include <ipxe/open.h> +#include <ipxe/pool.h> +#include <ipxe/http.h> + +/** HTTP pooled connection expiry time */ +#define HTTP_CONN_EXPIRY ( 10 * TICKS_PER_SEC ) + +/** HTTP connection pool */ +static LIST_HEAD ( http_connection_pool ); + +/** + * Identify HTTP scheme + * + * @v uri URI + * @ret scheme HTTP scheme, or NULL + */ +static struct http_scheme * http_scheme ( struct uri *uri ) { + struct http_scheme *scheme; + + /* Sanity check */ + if ( ! uri->scheme ) + return NULL; + + /* Identify scheme */ + for_each_table_entry ( scheme, HTTP_SCHEMES ) { + if ( strcmp ( uri->scheme, scheme->name ) == 0 ) + return scheme; + } + + return NULL; +} + +/** + * Free HTTP connection + * + * @v refcnt Reference count + */ +static void http_conn_free ( struct refcnt *refcnt ) { + struct http_connection *conn = + container_of ( refcnt, struct http_connection, refcnt ); + + /* Free connection */ + uri_put ( conn->uri ); + free ( conn ); +} + +/** + * Close HTTP connection + * + * @v conn HTTP connection + * @v rc Reason for close + */ +static void http_conn_close ( struct http_connection *conn, int rc ) { + + /* Remove from connection pool, if applicable */ + pool_del ( &conn->pool ); + + /* Shut down interfaces */ + intf_shutdown ( &conn->socket, rc ); + intf_shutdown ( &conn->xfer, rc ); + if ( rc == 0 ) { + DBGC2 ( conn, "HTTPCONN %p closed %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + } else { + DBGC ( conn, "HTTPCONN %p closed %s://%s: %s\n", + conn, conn->scheme->name, conn->uri->host, + strerror ( rc ) ); + } +} + +/** + * Disconnect idle HTTP connection + * + * @v pool Pooled connection + */ +static void http_conn_expired ( struct pooled_connection *pool ) { + struct http_connection *conn = + container_of ( pool, struct http_connection, pool ); + + /* Close connection */ + http_conn_close ( conn, 0 /* Not an error to close idle connection */ ); +} + +/** + * Receive data from transport layer interface + * + * @v http HTTP connection + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + */ +static int http_conn_socket_deliver ( struct http_connection *conn, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + + /* Mark connection as alive */ + pool_alive ( &conn->pool ); + + /* Pass on to data transfer interface */ + return xfer_deliver ( &conn->xfer, iobuf, meta ); +} + +/** + * Close HTTP connection transport layer interface + * + * @v http HTTP connection + * @v rc Reason for close + */ +static void http_conn_socket_close ( struct http_connection *conn, int rc ) { + + /* If we are reopenable (i.e. we are a recycled connection + * from the connection pool, and we have received no data from + * the underlying socket since we were pooled), then suggest + * that the client should reopen the connection. + */ + if ( pool_is_reopenable ( &conn->pool ) ) + pool_reopen ( &conn->xfer ); + + /* Close the connection */ + http_conn_close ( conn, rc ); +} + +/** + * Recycle this connection after closing + * + * @v http HTTP connection + */ +static void http_conn_xfer_recycle ( struct http_connection *conn ) { + + /* Mark connection as recyclable */ + pool_recyclable ( &conn->pool ); + DBGC2 ( conn, "HTTPCONN %p keepalive enabled\n", conn ); +} + +/** + * Close HTTP connection data transfer interface + * + * @v conn HTTP connection + * @v rc Reason for close + */ +static void http_conn_xfer_close ( struct http_connection *conn, int rc ) { + + /* Add to the connection pool if keepalive is enabled and no + * error occurred. + */ + if ( ( rc == 0 ) && pool_is_recyclable ( &conn->pool ) ) { + intf_restart ( &conn->xfer, rc ); + pool_add ( &conn->pool, &http_connection_pool, + HTTP_CONN_EXPIRY ); + DBGC2 ( conn, "HTTPCONN %p pooled %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + return; + } + + /* Otherwise, close the connection */ + http_conn_close ( conn, rc ); +} + +/** HTTP connection socket interface operations */ +static struct interface_operation http_conn_socket_operations[] = { + INTF_OP ( xfer_deliver, struct http_connection *, + http_conn_socket_deliver ), + INTF_OP ( intf_close, struct http_connection *, + http_conn_socket_close ), +}; + +/** HTTP connection socket interface descriptor */ +static struct interface_descriptor http_conn_socket_desc = + INTF_DESC_PASSTHRU ( struct http_connection, socket, + http_conn_socket_operations, xfer ); + +/** HTTP connection data transfer interface operations */ +static struct interface_operation http_conn_xfer_operations[] = { + INTF_OP ( pool_recycle, struct http_connection *, + http_conn_xfer_recycle ), + INTF_OP ( intf_close, struct http_connection *, + http_conn_xfer_close ), +}; + +/** HTTP connection data transfer interface descriptor */ +static struct interface_descriptor http_conn_xfer_desc = + INTF_DESC_PASSTHRU ( struct http_connection, xfer, + http_conn_xfer_operations, socket ); + +/** + * Connect to an HTTP server + * + * @v xfer Data transfer interface + * @v uri Connection URI + * @ret rc Return status code + * + * HTTP connections are pooled. The caller should be prepared to + * receive a pool_reopen() message. + */ +int http_connect ( struct interface *xfer, struct uri *uri ) { + struct http_connection *conn; + struct http_scheme *scheme; + struct sockaddr_tcpip server; + struct interface *socket; + int rc; + + /* Identify scheme */ + scheme = http_scheme ( uri ); + if ( ! scheme ) + return -ENOTSUP; + + /* Sanity check */ + if ( ! uri->host ) + return -EINVAL; + + /* Look for a reusable connection in the pool */ + list_for_each_entry ( conn, &http_connection_pool, pool.list ) { + + /* Sanity checks */ + assert ( conn->uri != NULL ); + assert ( conn->uri->host != NULL ); + + /* Reuse connection, if possible */ + if ( ( scheme == conn->scheme ) && + ( strcmp ( uri->host, conn->uri->host ) == 0 ) ) { + + /* Remove from connection pool, stop timer, + * attach to parent interface, and return. + */ + pool_del ( &conn->pool ); + intf_plug_plug ( &conn->xfer, xfer ); + DBGC2 ( conn, "HTTPCONN %p reused %s://%s\n", + conn, conn->scheme->name, conn->uri->host ); + return 0; + } + } + + /* Allocate and initialise structure */ + conn = zalloc ( sizeof ( *conn ) ); + ref_init ( &conn->refcnt, http_conn_free ); + conn->uri = uri_get ( uri ); + conn->scheme = scheme; + intf_init ( &conn->socket, &http_conn_socket_desc, &conn->refcnt ); + intf_init ( &conn->xfer, &http_conn_xfer_desc, &conn->refcnt ); + pool_init ( &conn->pool, http_conn_expired, &conn->refcnt ); + + /* Open socket */ + memset ( &server, 0, sizeof ( server ) ); + server.st_port = htons ( uri_port ( uri, scheme->port ) ); + socket = &conn->socket; + if ( scheme->filter && + ( ( rc = scheme->filter ( socket, uri->host, &socket ) ) != 0 ) ) + goto err_filter; + if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, + ( struct sockaddr * ) &server, + uri->host, NULL ) ) != 0 ) + goto err_open; + + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &conn->xfer, xfer ); + ref_put ( &conn->refcnt ); + + DBGC2 ( conn, "HTTPCONN %p created %s://%s:%d\n", conn, + conn->scheme->name, conn->uri->host, ntohs ( server.st_port ) ); + return 0; + + err_open: + err_filter: + DBGC2 ( conn, "HTTPCONN %p could not create %s://%s: %s\n", + conn, conn->scheme->name, conn->uri->host, strerror ( rc ) ); + http_conn_close ( conn, rc ); + ref_put ( &conn->refcnt ); + return rc; +} diff --git a/roms/ipxe/src/net/tcp/httpcore.c b/roms/ipxe/src/net/tcp/httpcore.c index 1d1953e61..af3ca9780 100644 --- a/roms/ipxe/src/net/tcp/httpcore.c +++ b/roms/ipxe/src/net/tcp/httpcore.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>. + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -40,35 +44,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/iobuf.h> #include <ipxe/xfer.h> #include <ipxe/open.h> -#include <ipxe/socket.h> -#include <ipxe/tcpip.h> #include <ipxe/process.h> #include <ipxe/retry.h> #include <ipxe/timer.h> #include <ipxe/linebuf.h> -#include <ipxe/base64.h> -#include <ipxe/base16.h> -#include <ipxe/md5.h> +#include <ipxe/xferbuf.h> #include <ipxe/blockdev.h> #include <ipxe/acpi.h> #include <ipxe/version.h> #include <ipxe/params.h> #include <ipxe/profile.h> +#include <ipxe/vsprintf.h> #include <ipxe/http.h> /* Disambiguate the various error causes */ #define EACCES_401 __einfo_error ( EINFO_EACCES_401 ) #define EINFO_EACCES_401 \ __einfo_uniqify ( EINFO_EACCES, 0x01, "HTTP 401 Unauthorized" ) -#define EIO_OTHER __einfo_error ( EINFO_EIO_OTHER ) -#define EINFO_EIO_OTHER \ - __einfo_uniqify ( EINFO_EIO, 0x01, "Unrecognised HTTP response code" ) -#define EIO_CONTENT_LENGTH __einfo_error ( EINFO_EIO_CONTENT_LENGTH ) -#define EINFO_EIO_CONTENT_LENGTH \ - __einfo_uniqify ( EINFO_EIO, 0x02, "Content length mismatch" ) -#define EINVAL_RESPONSE __einfo_error ( EINFO_EINVAL_RESPONSE ) -#define EINFO_EINVAL_RESPONSE \ - __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid content length" ) +#define EINVAL_STATUS __einfo_error ( EINFO_EINVAL_STATUS ) +#define EINFO_EINVAL_STATUS \ + __einfo_uniqify ( EINFO_EINVAL, 0x01, "Invalid status line" ) #define EINVAL_HEADER __einfo_error ( EINFO_EINVAL_HEADER ) #define EINFO_EINVAL_HEADER \ __einfo_uniqify ( EINFO_EINVAL, 0x02, "Invalid header" ) @@ -78,9 +73,27 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINVAL_CHUNK_LENGTH __einfo_error ( EINFO_EINVAL_CHUNK_LENGTH ) #define EINFO_EINVAL_CHUNK_LENGTH \ __einfo_uniqify ( EINFO_EINVAL, 0x04, "Invalid chunk length" ) +#define EIO_OTHER __einfo_error ( EINFO_EIO_OTHER ) +#define EINFO_EIO_OTHER \ + __einfo_uniqify ( EINFO_EIO, 0x01, "Unrecognised HTTP response code" ) +#define EIO_CONTENT_LENGTH __einfo_error ( EINFO_EIO_CONTENT_LENGTH ) +#define EINFO_EIO_CONTENT_LENGTH \ + __einfo_uniqify ( EINFO_EIO, 0x02, "Content length mismatch" ) +#define EIO_4XX __einfo_error ( EINFO_EIO_4XX ) +#define EINFO_EIO_4XX \ + __einfo_uniqify ( EINFO_EIO, 0x04, "HTTP 4xx Client Error" ) +#define EIO_5XX __einfo_error ( EINFO_EIO_5XX ) +#define EINFO_EIO_5XX \ + __einfo_uniqify ( EINFO_EIO, 0x05, "HTTP 5xx Server Error" ) #define ENOENT_404 __einfo_error ( EINFO_ENOENT_404 ) #define EINFO_ENOENT_404 \ __einfo_uniqify ( EINFO_ENOENT, 0x01, "HTTP 404 Not Found" ) +#define ENOTSUP_CONNECTION __einfo_error ( EINFO_ENOTSUP_CONNECTION ) +#define EINFO_ENOTSUP_CONNECTION \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported connection header" ) +#define ENOTSUP_TRANSFER __einfo_error ( EINFO_ENOTSUP_TRANSFER ) +#define EINFO_ENOTSUP_TRANSFER \ + __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported transfer encoding" ) #define EPERM_403 __einfo_error ( EINFO_EPERM_403 ) #define EINFO_EPERM_403 \ __einfo_uniqify ( EINFO_EPERM, 0x01, "HTTP 403 Forbidden" ) @@ -88,9 +101,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); #define EINFO_EPROTO_UNSOLICITED \ __einfo_uniqify ( EINFO_EPROTO, 0x01, "Unsolicited data" ) -/** Block size used for HTTP block device request */ -#define HTTP_BLKSIZE 512 - /** Retry delay used when we cannot understand the Retry-After header */ #define HTTP_RETRY_SECONDS 5 @@ -100,1069 +110,1711 @@ static struct profiler http_rx_profiler __profiler = { .name = "http.rx" }; /** Data transfer profiler */ static struct profiler http_xfer_profiler __profiler = { .name = "http.xfer" }; -/** HTTP flags */ -enum http_flags { - /** Request is waiting to be transmitted */ - HTTP_TX_PENDING = 0x0001, - /** Fetch header only */ - HTTP_HEAD_ONLY = 0x0002, - /** Client would like to keep connection alive */ - HTTP_CLIENT_KEEPALIVE = 0x0004, - /** Server will keep connection alive */ - HTTP_SERVER_KEEPALIVE = 0x0008, - /** Discard the current request and try again */ - HTTP_TRY_AGAIN = 0x0010, - /** Provide Basic authentication details */ - HTTP_BASIC_AUTH = 0x0020, - /** Provide Digest authentication details */ - HTTP_DIGEST_AUTH = 0x0040, - /** Socket must be reopened */ - HTTP_REOPEN_SOCKET = 0x0080, +static struct http_state http_request; +static struct http_state http_headers; +static struct http_state http_trailers; +static struct http_transfer_encoding http_transfer_identity; + +/****************************************************************************** + * + * Methods + * + ****************************************************************************** + */ + +/** HTTP HEAD method */ +struct http_method http_head = { + .name = "HEAD", }; -/** HTTP receive state */ -enum http_rx_state { - HTTP_RX_RESPONSE = 0, - HTTP_RX_HEADER, - HTTP_RX_CHUNK_LEN, - /* In HTTP_RX_DATA, it is acceptable for the server to close - * the connection (unless we are in the middle of a chunked - * transfer). - */ - HTTP_RX_DATA, - /* In the following states, it is acceptable for the server to - * close the connection. - */ - HTTP_RX_TRAILER, - HTTP_RX_IDLE, - HTTP_RX_DEAD, +/** HTTP GET method */ +struct http_method http_get = { + .name = "GET", }; +/** HTTP POST method */ +struct http_method http_post = { + .name = "POST", +}; + +/****************************************************************************** + * + * Utility functions + * + ****************************************************************************** + */ + /** - * An HTTP request + * Handle received HTTP line-buffered data * + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v linebuf Line buffer + * @ret rc Return status code */ -struct http_request { - /** Reference count */ - struct refcnt refcnt; - /** Data transfer interface */ - struct interface xfer; - /** Partial transfer interface */ - struct interface partial; - - /** URI being fetched */ - struct uri *uri; - /** Default port */ - unsigned int default_port; - /** Filter (if any) */ - int ( * filter ) ( struct interface *xfer, - const char *name, - struct interface **next ); - /** Transport layer interface */ - struct interface socket; - - /** Flags */ - unsigned int flags; - /** Starting offset of partial transfer (if applicable) */ - size_t partial_start; - /** Length of partial transfer (if applicable) */ - size_t partial_len; - - /** TX process */ - struct process process; - - /** RX state */ - enum http_rx_state rx_state; - /** Response code */ - unsigned int code; - /** Received length */ - size_t rx_len; - /** Length remaining (or 0 if unknown) */ - size_t remaining; - /** HTTP is using Transfer-Encoding: chunked */ - int chunked; - /** Current chunk length remaining (if applicable) */ - size_t chunk_remaining; - /** Line buffer for received header lines */ - struct line_buffer linebuf; - /** Receive data buffer (if applicable) */ - userptr_t rx_buffer; - - /** Authentication realm (if any) */ - char *auth_realm; - /** Authentication nonce (if any) */ - char *auth_nonce; - /** Authentication opaque string (if any) */ - char *auth_opaque; - - /** Request retry timer */ - struct retry_timer timer; - /** Retry delay (in timer ticks) */ - unsigned long retry_delay; -}; +static int http_rx_linebuf ( struct http_transaction *http, + struct io_buffer *iobuf, + struct line_buffer *linebuf ) { + int consumed; + int rc; + + /* Buffer received line */ + consumed = line_buffer ( linebuf, iobuf->data, iob_len ( iobuf ) ); + if ( consumed < 0 ) { + rc = consumed; + DBGC ( http, "HTTP %p could not buffer line: %s\n", + http, strerror ( rc ) ); + return rc; + } + + /* Consume line */ + iob_pull ( iobuf, consumed ); + + return 0; +} /** - * Free HTTP request + * Get HTTP response token * - * @v refcnt Reference counter + * @v line Line position + * @v value Token value to fill in (if any) + * @ret token Token, or NULL + */ +char * http_token ( char **line, char **value ) { + char *token; + char quote = '\0'; + char c; + + /* Avoid returning uninitialised data */ + if ( value ) + *value = NULL; + + /* Skip any initial whitespace */ + while ( isspace ( **line ) ) + (*line)++; + + /* Check for end of line and record token position */ + if ( ! **line ) + return NULL; + token = *line; + + /* Scan for end of token */ + while ( ( c = **line ) ) { + + /* Terminate if we hit an unquoted whitespace */ + if ( isspace ( c ) && ! quote ) + break; + + /* Terminate if we hit a closing quote */ + if ( c == quote ) + break; + + /* Check for value separator */ + if ( value && ( ! *value ) && ( c == '=' ) ) { + + /* Terminate key portion of token */ + *((*line)++) = '\0'; + + /* Check for quote character */ + c = **line; + if ( ( c == '"' ) || ( c == '\'' ) ) { + quote = c; + (*line)++; + } + + /* Record value portion of token */ + *value = *line; + + } else { + + /* Move to next character */ + (*line)++; + } + } + + /* Terminate token, if applicable */ + if ( c ) + *((*line)++) = '\0'; + + return token; +} + +/****************************************************************************** + * + * Transactions + * + ****************************************************************************** + */ + +/** + * Free HTTP transaction + * + * @v refcnt Reference count */ static void http_free ( struct refcnt *refcnt ) { - struct http_request *http = - container_of ( refcnt, struct http_request, refcnt ); + struct http_transaction *http = + container_of ( refcnt, struct http_transaction, refcnt ); - uri_put ( http->uri ); + empty_line_buffer ( &http->response.headers ); empty_line_buffer ( &http->linebuf ); - free ( http->auth_realm ); - free ( http->auth_nonce ); - free ( http->auth_opaque ); + uri_put ( http->uri ); free ( http ); -}; +} /** - * Close HTTP request + * Close HTTP transaction * - * @v http HTTP request - * @v rc Return status code + * @v http HTTP transaction + * @v rc Reason for close */ -static void http_close ( struct http_request *http, int rc ) { - - /* Prevent further processing of any current packet */ - http->rx_state = HTTP_RX_DEAD; +static void http_close ( struct http_transaction *http, int rc ) { - /* Prevent reconnection */ - http->flags &= ~HTTP_CLIENT_KEEPALIVE; - - /* Remove process */ + /* Stop process */ process_del ( &http->process ); - /* Close all data transfer interfaces */ - intf_shutdown ( &http->socket, rc ); - intf_shutdown ( &http->partial, rc ); + /* Stop timer */ + stop_timer ( &http->timer ); + + /* Close all interfaces, allowing for the fact that the + * content-decoded and transfer-decoded interfaces may be + * connected to the same object. + */ + intf_shutdown ( &http->conn, rc ); + intf_nullify ( &http->transfer ); + intf_shutdown ( &http->content, rc ); + intf_shutdown ( &http->transfer, rc ); intf_shutdown ( &http->xfer, rc ); } /** - * Open HTTP socket + * Close HTTP transaction with error (even if none specified) * - * @v http HTTP request - * @ret rc Return status code + * @v http HTTP transaction + * @v rc Reason for close */ -static int http_socket_open ( struct http_request *http ) { - struct uri *uri = http->uri; - struct sockaddr_tcpip server; - struct interface *socket; +static void http_close_error ( struct http_transaction *http, int rc ) { + + /* Treat any close as an error */ + http_close ( http, ( rc ? rc : -EPIPE ) ); +} + +/** + * Reopen stale HTTP connection + * + * @v http HTTP transaction + */ +static void http_reopen ( struct http_transaction *http ) { int rc; - /* Open socket */ - memset ( &server, 0, sizeof ( server ) ); - server.st_port = htons ( uri_port ( uri, http->default_port ) ); - socket = &http->socket; - if ( http->filter ) { - if ( ( rc = http->filter ( socket, uri->host, &socket ) ) != 0 ) - return rc; + /* Close existing connection */ + intf_restart ( &http->conn, -ECANCELED ); + + /* Reopen connection */ + if ( ( rc = http_connect ( &http->conn, http->uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not reconnect: %s\n", + http, strerror ( rc ) ); + goto err_connect; } - if ( ( rc = xfer_open_named_socket ( socket, SOCK_STREAM, - ( struct sockaddr * ) &server, - uri->host, NULL ) ) != 0 ) - return rc; - return 0; + /* Reset state */ + http->state = &http_request; + + /* Reschedule transmission process */ + process_add ( &http->process ); + + return; + + err_connect: + http_close ( http, rc ); } /** - * Retry HTTP request + * Handle retry timer expiry * * @v timer Retry timer - * @v fail Failure indicator + * @v over Failure indicator + */ +static void http_expired ( struct retry_timer *timer, int over __unused ) { + struct http_transaction *http = + container_of ( timer, struct http_transaction, timer ); + + /* Reopen connection */ + http_reopen ( http ); +} + +/** + * HTTP transmit process + * + * @v http HTTP transaction */ -static void http_retry ( struct retry_timer *timer, int fail __unused ) { - struct http_request *http = - container_of ( timer, struct http_request, timer ); +static void http_step ( struct http_transaction *http ) { int rc; - /* Reopen socket if required */ - if ( http->flags & HTTP_REOPEN_SOCKET ) { - http->flags &= ~HTTP_REOPEN_SOCKET; - DBGC ( http, "HTTP %p reopening connection\n", http ); - if ( ( rc = http_socket_open ( http ) ) != 0 ) { - http_close ( http, rc ); - return; + /* Do nothing if we have nothing to transmit */ + if ( ! http->state->tx ) + return; + + /* Do nothing until connection is ready */ + if ( ! xfer_window ( &http->conn ) ) + return; + + /* Do nothing until data transfer interface is ready */ + if ( ! xfer_window ( &http->xfer ) ) + return; + + /* Transmit data */ + if ( ( rc = http->state->tx ( http ) ) != 0 ) + goto err; + + return; + + err: + http_close ( http, rc ); +} + +/** + * Handle received HTTP data + * + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v meta Transfer metadata + * @ret rc Return status code + * + * This function takes ownership of the I/O buffer. + */ +static int http_conn_deliver ( struct http_transaction *http, + struct io_buffer *iobuf, + struct xfer_metadata *meta __unused ) { + int rc; + + /* Handle received data */ + profile_start ( &http_rx_profiler ); + while ( iobuf && iob_len ( iobuf ) ) { + + /* Sanity check */ + if ( ( ! http->state ) || ( ! http->state->rx ) ) { + DBGC ( http, "HTTP %p unexpected data\n", http ); + rc = -EPROTO_UNSOLICITED; + goto err; } - } - /* Retry the request if applicable */ - if ( http->flags & HTTP_TRY_AGAIN ) { - http->flags &= ~HTTP_TRY_AGAIN; - DBGC ( http, "HTTP %p retrying request\n", http ); - http->flags |= HTTP_TX_PENDING; - http->rx_state = HTTP_RX_RESPONSE; - process_add ( &http->process ); + /* Receive (some) data */ + if ( ( rc = http->state->rx ( http, &iobuf ) ) != 0 ) + goto err; } + + /* Free I/O buffer, if applicable */ + free_iob ( iobuf ); + + profile_stop ( &http_rx_profiler ); + return 0; + + err: + free_iob ( iobuf ); + http_close ( http, rc ); + return rc; } /** - * Mark HTTP request as completed successfully + * Handle server connection close * - * @v http HTTP request + * @v http HTTP transaction + * @v rc Reason for close */ -static void http_done ( struct http_request *http ) { +static void http_conn_close ( struct http_transaction *http, int rc ) { - /* If we are not at an appropriate stage of the protocol - * (including being in the middle of a chunked transfer), - * force an error. - */ - if ( ( http->rx_state < HTTP_RX_DATA ) || ( http->chunked != 0 ) ) { - DBGC ( http, "HTTP %p connection closed unexpectedly in state " - "%d\n", http, http->rx_state ); - http_close ( http, -ECONNRESET ); - return; - } + /* Sanity checks */ + assert ( http->state != NULL ); + assert ( http->state->close != NULL ); + + /* Restart server connection interface */ + intf_restart ( &http->conn, rc ); + + /* Hand off to state-specific method */ + http->state->close ( http, rc ); +} - /* If we had a Content-Length, and the received content length - * isn't correct, force an error +/** + * Handle received content-decoded data + * + * @v http HTTP transaction + * @v iobuf I/O buffer + * @v meta Data transfer metadata + */ +static int http_content_deliver ( struct http_transaction *http, + struct io_buffer *iobuf, + struct xfer_metadata *meta ) { + int rc; + + /* Ignore content if this is anything other than a successful + * transfer. */ - if ( http->remaining != 0 ) { - DBGC ( http, "HTTP %p incorrect length %zd, should be %zd\n", - http, http->rx_len, ( http->rx_len + http->remaining ) ); - http_close ( http, -EIO_CONTENT_LENGTH ); - return; + if ( http->response.rc != 0 ) { + free_iob ( iobuf ); + return 0; } - /* Enter idle state */ - http->rx_state = HTTP_RX_IDLE; - http->rx_len = 0; - assert ( http->remaining == 0 ); - assert ( http->chunked == 0 ); - assert ( http->chunk_remaining == 0 ); + /* Deliver to data transfer interface */ + profile_start ( &http_xfer_profiler ); + if ( ( rc = xfer_deliver ( &http->xfer, iob_disown ( iobuf ), + meta ) ) != 0 ) + return rc; + profile_stop ( &http_xfer_profiler ); - /* Close partial transfer interface */ - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) - intf_restart ( &http->partial, 0 ); + return 0; +} - /* Close everything unless we want to keep the connection alive */ - if ( ! ( http->flags & ( HTTP_CLIENT_KEEPALIVE | HTTP_TRY_AGAIN ) ) ) { - http_close ( http, 0 ); - return; - } +/** + * Get underlying data transfer buffer + * + * @v http HTTP transaction + * @ret xferbuf Data transfer buffer, or NULL on error + */ +static struct xfer_buffer * +http_content_buffer ( struct http_transaction *http ) { - /* If the server is not intending to keep the connection - * alive, then close the socket and mark it as requiring - * reopening. + /* Deny access to the data transfer buffer if this is anything + * other than a successful transfer. */ - if ( ! ( http->flags & HTTP_SERVER_KEEPALIVE ) ) { - intf_restart ( &http->socket, 0 ); - http->flags &= ~HTTP_SERVER_KEEPALIVE; - http->flags |= HTTP_REOPEN_SOCKET; - } + if ( http->response.rc != 0 ) + return NULL; - /* Start request retry timer */ - start_timer_fixed ( &http->timer, http->retry_delay ); - http->retry_delay = 0; + /* Hand off to data transfer interface */ + return xfer_buffer ( &http->xfer ); } /** - * Convert HTTP response code to return status code + * Read from block device (when HTTP block device support is not present) * - * @v response HTTP response code + * @v http HTTP transaction + * @v data Data interface + * @v lba Starting logical block address + * @v count Number of logical blocks + * @v buffer Data buffer + * @v len Length of data buffer * @ret rc Return status code */ -static int http_response_to_rc ( unsigned int response ) { - switch ( response ) { - case 200: - case 206: - case 301: - case 302: - case 303: - return 0; - case 404: - return -ENOENT_404; - case 403: - return -EPERM_403; - case 401: - return -EACCES_401; - default: - return -EIO_OTHER; - } +__weak int http_block_read ( struct http_transaction *http __unused, + struct interface *data __unused, + uint64_t lba __unused, unsigned int count __unused, + userptr_t buffer __unused, size_t len __unused ) { + + return -ENOTSUP; } /** - * Handle HTTP response + * Read block device capacity (when HTTP block device support is not present) * - * @v http HTTP request - * @v response HTTP response + * @v control Control interface + * @v data Data interface * @ret rc Return status code */ -static int http_rx_response ( struct http_request *http, char *response ) { - char *spc; - - DBGC ( http, "HTTP %p response \"%s\"\n", http, response ); +__weak int http_block_read_capacity ( struct http_transaction *http __unused, + struct interface *data __unused ) { - /* Check response starts with "HTTP/" */ - if ( strncmp ( response, "HTTP/", 5 ) != 0 ) - return -EINVAL_RESPONSE; + return -ENOTSUP; +} - /* Locate and store response code */ - spc = strchr ( response, ' ' ); - if ( ! spc ) - return -EINVAL_RESPONSE; - http->code = strtoul ( spc, NULL, 10 ); +/** + * Describe device in ACPI table (when HTTP block device support is not present) + * + * @v http HTTP transaction + * @v acpi ACPI table + * @v len Length of ACPI table + * @ret rc Return status code + */ +__weak int http_acpi_describe ( struct http_transaction *http __unused, + struct acpi_description_header *acpi __unused, + size_t len __unused ) { - /* Move to receive headers */ - http->rx_state = ( ( http->flags & HTTP_HEAD_ONLY ) ? - HTTP_RX_TRAILER : HTTP_RX_HEADER ); - return 0; + return -ENOTSUP; } +/** HTTP data transfer interface operations */ +static struct interface_operation http_xfer_operations[] = { + INTF_OP ( block_read, struct http_transaction *, http_block_read ), + INTF_OP ( block_read_capacity, struct http_transaction *, + http_block_read_capacity ), + INTF_OP ( acpi_describe, struct http_transaction *, + http_acpi_describe ), + INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), + INTF_OP ( intf_close, struct http_transaction *, http_close ), +}; + +/** HTTP data transfer interface descriptor */ +static struct interface_descriptor http_xfer_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, xfer, + http_xfer_operations, content ); + +/** HTTP content-decoded interface operations */ +static struct interface_operation http_content_operations[] = { + INTF_OP ( xfer_deliver, struct http_transaction *, + http_content_deliver ), + INTF_OP ( xfer_buffer, struct http_transaction *, http_content_buffer ), + INTF_OP ( intf_close, struct http_transaction *, http_close ), +}; + +/** HTTP content-decoded interface descriptor */ +static struct interface_descriptor http_content_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, content, + http_content_operations, xfer ); + +/** HTTP transfer-decoded interface operations */ +static struct interface_operation http_transfer_operations[] = { + INTF_OP ( intf_close, struct http_transaction *, http_close ), +}; + +/** HTTP transfer-decoded interface descriptor */ +static struct interface_descriptor http_transfer_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, transfer, + http_transfer_operations, conn ); + +/** HTTP server connection interface operations */ +static struct interface_operation http_conn_operations[] = { + INTF_OP ( xfer_deliver, struct http_transaction *, http_conn_deliver ), + INTF_OP ( xfer_window_changed, struct http_transaction *, http_step ), + INTF_OP ( pool_reopen, struct http_transaction *, http_reopen ), + INTF_OP ( intf_close, struct http_transaction *, http_conn_close ), +}; + +/** HTTP server connection interface descriptor */ +static struct interface_descriptor http_conn_desc = + INTF_DESC_PASSTHRU ( struct http_transaction, conn, + http_conn_operations, transfer ); + +/** HTTP process descriptor */ +static struct process_descriptor http_process_desc = + PROC_DESC_ONCE ( struct http_transaction, process, http_step ); + /** - * Handle HTTP Location header + * Open HTTP transaction * - * @v http HTTP request - * @v value HTTP header value + * @v xfer Data transfer interface + * @v method Request method + * @v uri Request URI + * @v range Content range (if any) + * @v content Request content (if any) * @ret rc Return status code */ -static int http_rx_location ( struct http_request *http, char *value ) { +int http_open ( struct interface *xfer, struct http_method *method, + struct uri *uri, struct http_request_range *range, + struct http_request_content *content ) { + struct http_transaction *http; + struct uri request_uri; + struct uri request_host; + size_t request_uri_len; + size_t request_host_len; + size_t content_len; + char *request_uri_string; + char *request_host_string; + void *content_data; int rc; - /* Redirect to new location */ - DBGC ( http, "HTTP %p redirecting to %s\n", http, value ); - if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, - value ) ) != 0 ) { - DBGC ( http, "HTTP %p could not redirect: %s\n", + /* Calculate request URI length */ + memset ( &request_uri, 0, sizeof ( request_uri ) ); + request_uri.path = ( uri->path ? uri->path : "/" ); + request_uri.query = uri->query; + request_uri_len = + ( format_uri ( &request_uri, NULL, 0 ) + 1 /* NUL */); + + /* Calculate host name length */ + memset ( &request_host, 0, sizeof ( request_host ) ); + request_host.host = uri->host; + request_host.port = uri->port; + request_host_len = + ( format_uri ( &request_host, NULL, 0 ) + 1 /* NUL */ ); + + /* Calculate request content length */ + content_len = ( content ? content->len : 0 ); + + /* Allocate and initialise structure */ + http = zalloc ( sizeof ( *http ) + request_uri_len + request_host_len + + content_len ); + if ( ! http ) { + rc = -ENOMEM; + goto err_alloc; + } + request_uri_string = ( ( ( void * ) http ) + sizeof ( *http ) ); + request_host_string = ( request_uri_string + request_uri_len ); + content_data = ( request_host_string + request_host_len ); + format_uri ( &request_uri, request_uri_string, request_uri_len ); + format_uri ( &request_host, request_host_string, request_host_len ); + ref_init ( &http->refcnt, http_free ); + intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); + intf_init ( &http->content, &http_content_desc, &http->refcnt ); + intf_init ( &http->transfer, &http_transfer_desc, &http->refcnt ); + intf_init ( &http->conn, &http_conn_desc, &http->refcnt ); + intf_plug_plug ( &http->transfer, &http->content ); + process_init ( &http->process, &http_process_desc, &http->refcnt ); + timer_init ( &http->timer, http_expired, &http->refcnt ); + http->uri = uri_get ( uri ); + http->request.method = method; + http->request.uri = request_uri_string; + http->request.host = request_host_string; + if ( range ) { + memcpy ( &http->request.range, range, + sizeof ( http->request.range ) ); + } + if ( content ) { + http->request.content.type = content->type; + http->request.content.data = content_data; + http->request.content.len = content_len; + memcpy ( content_data, content->data, content_len ); + } + http->state = &http_request; + DBGC2 ( http, "HTTP %p %s://%s%s\n", http, http->uri->scheme, + http->request.host, http->request.uri ); + + /* Open connection */ + if ( ( rc = http_connect ( &http->conn, uri ) ) != 0 ) { + DBGC ( http, "HTTP %p could not connect: %s\n", http, strerror ( rc ) ); - return rc; + goto err_connect; } + /* Attach to parent interface, mortalise self, and return */ + intf_plug_plug ( &http->xfer, xfer ); + ref_put ( &http->refcnt ); return 0; + + err_connect: + http_close ( http, rc ); + ref_put ( &http->refcnt ); + err_alloc: + return rc; } /** - * Handle HTTP Content-Length header + * Handle successful transfer completion * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction * @ret rc Return status code */ -static int http_rx_content_length ( struct http_request *http, char *value ) { - struct block_device_capacity capacity; - size_t content_len; - char *endp; +static int http_transfer_complete ( struct http_transaction *http ) { + struct http_authentication *auth; + const char *location; + int rc; - /* Parse content length */ - content_len = strtoul ( value, &endp, 10 ); - if ( ! ( ( *endp == '\0' ) || isspace ( *endp ) ) ) { - DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", - http, value ); - return -EINVAL_CONTENT_LENGTH; + /* Keep connection alive if applicable */ + if ( http->response.flags & HTTP_RESPONSE_KEEPALIVE ) + pool_recycle ( &http->conn ); + + /* Restart server connection interface */ + intf_restart ( &http->conn, 0 ); + + /* No more data is expected */ + http->state = NULL; + + /* If transaction is successful, then close the + * transfer-decoded interface. The content encoding may + * choose whether or not to immediately terminate the + * transaction. + */ + if ( http->response.rc == 0 ) { + intf_shutdown ( &http->transfer, 0 ); + return 0; } - /* If we already have an expected content length, and this - * isn't it, then complain + /* Perform redirection, if applicable */ + if ( ( location = http->response.location ) ) { + DBGC2 ( http, "HTTP %p redirecting to \"%s\"\n", + http, location ); + if ( ( rc = xfer_redirect ( &http->xfer, LOCATION_URI_STRING, + location ) ) != 0 ) { + DBGC ( http, "HTTP %p could not redirect: %s\n", + http, strerror ( rc ) ); + return rc; + } + http_close ( http, 0 ); + return 0; + } + + /* Fail unless a retry is permitted */ + if ( ! ( http->response.flags & HTTP_RESPONSE_RETRY ) ) + return http->response.rc; + + /* Perform authentication, if applicable */ + if ( ( auth = http->response.auth.auth ) ) { + http->request.auth.auth = auth; + DBGC2 ( http, "HTTP %p performing %s authentication\n", + http, auth->name ); + if ( ( rc = auth->authenticate ( http ) ) != 0 ) { + DBGC ( http, "HTTP %p could not authenticate: %s\n", + http, strerror ( rc ) ); + return rc; + } + } + + /* Restart content decoding interfaces (which may be attached + * to the same object). */ - if ( http->remaining && ( http->remaining != content_len ) ) { - DBGC ( http, "HTTP %p incorrect Content-Length %zd (expected " - "%zd)\n", http, content_len, http->remaining ); - return -EIO_CONTENT_LENGTH; + intf_nullify ( &http->content ); + intf_nullify ( &http->transfer ); + intf_restart ( &http->content, http->response.rc ); + intf_restart ( &http->transfer, http->response.rc ); + http->content.desc = &http_content_desc; + http->transfer.desc = &http_transfer_desc; + intf_plug_plug ( &http->transfer, &http->content ); + http->len = 0; + assert ( http->remaining == 0 ); + + /* Start timer to initiate retry */ + DBGC2 ( http, "HTTP %p retrying after %d seconds\n", + http, http->response.retry_after ); + start_timer_fixed ( &http->timer, + ( http->response.retry_after * TICKS_PER_SEC ) ); + return 0; +} + +/****************************************************************************** + * + * Requests + * + ****************************************************************************** + */ + +/** + * Construct HTTP request headers + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length, or negative error + */ +static int http_format_headers ( struct http_transaction *http, char *buf, + size_t len ) { + struct http_request_header *header; + size_t used; + size_t remaining; + char *line; + int value_len; + int rc; + + /* Construct request line */ + used = ssnprintf ( buf, len, "%s %s HTTP/1.1", + http->request.method->name, http->request.uri ); + if ( used < len ) + DBGC2 ( http, "HTTP %p TX %s\n", http, buf ); + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); + + /* Construct all headers */ + for_each_table_entry ( header, HTTP_REQUEST_HEADERS ) { + + /* Determine header value length */ + value_len = header->format ( http, NULL, 0 ); + if ( value_len < 0 ) { + rc = value_len; + return rc; + } + + /* Skip zero-length headers */ + if ( ! value_len ) + continue; + + /* Construct header */ + line = ( buf + used ); + used += ssnprintf ( ( buf + used ), ( len - used ), "%s: ", + header->name ); + remaining = ( ( used < len ) ? ( len - used ) : 0 ); + used += header->format ( http, ( buf + used ), remaining ); + if ( used < len ) + DBGC2 ( http, "HTTP %p TX %s\n", http, line ); + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); } - if ( ! ( http->flags & HTTP_HEAD_ONLY ) ) - http->remaining = content_len; - /* Do nothing more if we are retrying the request */ - if ( http->flags & HTTP_TRY_AGAIN ) + /* Construct terminating newline */ + used += ssnprintf ( ( buf + used ), ( len - used ), "\r\n" ); + + return used; +} + +/** + * Construct HTTP "Host" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_host ( struct http_transaction *http, char *buf, + size_t len ) { + + /* Construct host URI */ + return snprintf ( buf, len, "%s", http->request.host ); +} + +/** HTTP "Host" header "*/ +struct http_request_header http_request_host __http_request_header = { + .name = "Host", + .format = http_format_host, +}; + +/** + * Construct HTTP "User-Agent" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_user_agent ( struct http_transaction *http __unused, + char *buf, size_t len ) { + + /* Construct user agent */ + return snprintf ( buf, len, "iPXE/%s", product_version ); +} + +/** HTTP "User-Agent" header */ +struct http_request_header http_request_user_agent __http_request_header = { + .name = "User-Agent", + .format = http_format_user_agent, +}; + +/** + * Construct HTTP "Connection" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_connection ( struct http_transaction *http __unused, + char *buf, size_t len ) { + + /* Always request keep-alive */ + return snprintf ( buf, len, "keep-alive" ); +} + +/** HTTP "Connection" header */ +struct http_request_header http_request_connection __http_request_header = { + .name = "Connection", + .format = http_format_connection, +}; + +/** + * Construct HTTP "Range" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_range ( struct http_transaction *http, + char *buf, size_t len ) { + + /* Construct range, if applicable */ + if ( http->request.range.len ) { + return snprintf ( buf, len, "bytes=%zd-%zd", + http->request.range.start, + ( http->request.range.start + + http->request.range.len - 1 ) ); + } else { return 0; + } +} + +/** HTTP "Range" header */ +struct http_request_header http_request_range __http_request_header = { + .name = "Range", + .format = http_format_range, +}; - /* Use seek() to notify recipient of filesize */ - xfer_seek ( &http->xfer, http->remaining ); - xfer_seek ( &http->xfer, 0 ); +/** + * Construct HTTP "Content-Type" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_content_type ( struct http_transaction *http, + char *buf, size_t len ) { - /* Report block device capacity if applicable */ - if ( http->flags & HTTP_HEAD_ONLY ) { - capacity.blocks = ( content_len / HTTP_BLKSIZE ); - capacity.blksize = HTTP_BLKSIZE; - capacity.max_count = -1U; - block_capacity ( &http->partial, &capacity ); + /* Construct content type, if applicable */ + if ( http->request.content.type ) { + return snprintf ( buf, len, "%s", http->request.content.type ); + } else { + return 0; } - return 0; } +/** HTTP "Content-Type" header */ +struct http_request_header http_request_content_type __http_request_header = { + .name = "Content-Type", + .format = http_format_content_type, +}; + /** - * Handle HTTP Transfer-Encoding header + * Construct HTTP "Content-Length" header * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error */ -static int http_rx_transfer_encoding ( struct http_request *http, char *value ){ +static int http_format_content_length ( struct http_transaction *http, + char *buf, size_t len ) { - if ( strcasecmp ( value, "chunked" ) == 0 ) { - /* Mark connection as using chunked transfer encoding */ - http->chunked = 1; + /* Construct content length, if applicable */ + if ( http->request.content.len ) { + return snprintf ( buf, len, "%zd", http->request.content.len ); + } else { + return 0; } +} - return 0; +/** HTTP "Content-Length" header */ +struct http_request_header http_request_content_length __http_request_header = { + .name = "Content-Length", + .format = http_format_content_length, +}; + +/** + * Construct HTTP "Accept-Encoding" header + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_accept_encoding ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_content_encoding *encoding; + const char *sep = ""; + size_t used = 0; + + /* Construct list of content encodings */ + for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { + if ( encoding->supported && ( ! encoding->supported ( http ) ) ) + continue; + used += ssnprintf ( ( buf + used ), ( len - used ), + "%s%s", sep, encoding->name ); + sep = ", "; + } + + return used; } +/** HTTP "Accept-Encoding" header */ +struct http_request_header http_request_accept_encoding __http_request_header ={ + .name = "Accept-Encoding", + .format = http_format_accept_encoding, +}; + /** - * Handle HTTP Connection header + * Transmit request * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction * @ret rc Return status code */ -static int http_rx_connection ( struct http_request *http, char *value ) { +static int http_tx_request ( struct http_transaction *http ) { + struct io_buffer *iobuf; + int len; + int check_len; + int rc; + + /* Calculate request length */ + len = http_format_headers ( http, NULL, 0 ); + if ( len < 0 ) { + rc = len; + DBGC ( http, "HTTP %p could not construct request: %s\n", + http, strerror ( rc ) ); + goto err_len; + } + + /* Allocate I/O buffer */ + iobuf = alloc_iob ( len + 1 /* NUL */ + http->request.content.len ); + if ( ! iobuf ) { + rc = -ENOMEM; + goto err_alloc; + } - if ( strcasecmp ( value, "keep-alive" ) == 0 ) { - /* Mark connection as being kept alive by the server */ - http->flags |= HTTP_SERVER_KEEPALIVE; + /* Construct request */ + check_len = http_format_headers ( http, iob_put ( iobuf, len ), + ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); + memcpy ( iob_put ( iobuf, http->request.content.len ), + http->request.content.data, http->request.content.len ); + + /* Deliver request */ + if ( ( rc = xfer_deliver_iob ( &http->conn, + iob_disown ( iobuf ) ) ) != 0 ) { + DBGC ( http, "HTTP %p could not deliver request: %s\n", + http, strerror ( rc ) ); + goto err_deliver; } + /* Clear any previous response */ + empty_line_buffer ( &http->response.headers ); + memset ( &http->response, 0, sizeof ( http->response ) ); + + /* Move to response headers state */ + http->state = &http_headers; + return 0; + + err_deliver: + free_iob ( iobuf ); + err_alloc: + err_len: + return rc; } +/** HTTP request state */ +static struct http_state http_request = { + .tx = http_tx_request, + .close = http_close_error, +}; + +/****************************************************************************** + * + * Response headers + * + ****************************************************************************** + */ + /** - * Handle WWW-Authenticate Basic header + * Parse HTTP status line * - * @v http HTTP request - * @v params Parameters + * @v http HTTP transaction + * @v line Status line * @ret rc Return status code */ -static int http_rx_basic_auth ( struct http_request *http, char *params ) { +static int http_parse_status ( struct http_transaction *http, char *line ) { + char *endp; + char *version; + char *vernum; + char *status; + int response_rc; + + DBGC2 ( http, "HTTP %p RX %s\n", http, line ); + + /* Parse HTTP version */ + version = http_token ( &line, NULL ); + if ( ( ! version ) || ( strncmp ( version, "HTTP/", 5 ) != 0 ) ) { + DBGC ( http, "HTTP %p malformed version \"%s\"\n", http, line ); + return -EINVAL_STATUS; + } - DBGC ( http, "HTTP %p Basic authentication required (%s)\n", - http, params ); + /* Keepalive is enabled by default for anything newer than HTTP/1.0 */ + vernum = ( version + 5 /* "HTTP/" (presence already checked) */ ); + if ( vernum[0] == '0' ) { + /* HTTP/0.x : keepalive not enabled by default */ + } else if ( strncmp ( vernum, "1.0", 3 ) == 0 ) { + /* HTTP/1.0 : keepalive not enabled by default */ + } else { + /* HTTP/1.1 or newer: keepalive enabled by default */ + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + } - /* If we received a 401 Unauthorized response, then retry - * using Basic authentication - */ - if ( ( http->code == 401 ) && - ( ! ( http->flags & HTTP_BASIC_AUTH ) ) && - ( http->uri->user != NULL ) ) { - http->flags |= ( HTTP_TRY_AGAIN | HTTP_BASIC_AUTH ); + /* Parse status code */ + status = line; + http->response.status = strtoul ( status, &endp, 10 ); + if ( *endp != ' ' ) { + DBGC ( http, "HTTP %p malformed status code \"%s\"\n", + http, status ); + return -EINVAL_STATUS; + } + + /* Convert HTTP status code to iPXE return status code */ + if ( status[0] == '2' ) { + /* 2xx Success */ + response_rc = 0; + } else if ( status[0] == '3' ) { + /* 3xx Redirection */ + response_rc = -EXDEV; + } else if ( http->response.status == 401 ) { + /* 401 Unauthorized */ + response_rc = -EACCES_401; + } else if ( http->response.status == 403 ) { + /* 403 Forbidden */ + response_rc = -EPERM_403; + } else if ( http->response.status == 404 ) { + /* 404 Not Found */ + response_rc = -ENOENT_404; + } else if ( status[0] == '4' ) { + /* 4xx Client Error (not already specified) */ + response_rc = -EIO_4XX; + } else if ( status[0] == '5' ) { + /* 5xx Server Error */ + response_rc = -EIO_5XX; + } else { + /* Unrecognised */ + response_rc = -EIO_OTHER; } + http->response.rc = response_rc; return 0; } /** - * Parse Digest authentication parameter + * Parse HTTP header * - * @v params Parameters - * @v name Parameter name (including trailing "=\"") - * @ret value Parameter value, or NULL + * @v http HTTP transaction + * @v line Header line + * @ret rc Return status code */ -static char * http_digest_param ( char *params, const char *name ) { - char *key; - char *value; - char *terminator; - - /* Locate parameter */ - key = strstr ( params, name ); - if ( ! key ) - return NULL; +static int http_parse_header ( struct http_transaction *http, char *line ) { + struct http_response_header *header; + char *name = line; + char *sep; - /* Extract value */ - value = ( key + strlen ( name ) ); - terminator = strchr ( value, '"' ); - if ( ! terminator ) - return NULL; - return strndup ( value, ( terminator - value ) ); + DBGC2 ( http, "HTTP %p RX %s\n", http, line ); + + /* Extract header name */ + sep = strstr ( line, ": " ); + if ( ! sep ) { + DBGC ( http, "HTTP %p malformed header \"%s\"\n", http, line ); + return -EINVAL_HEADER; + } + *sep = '\0'; + line = ( sep + 2 /* ": " */ ); + + /* Process header, if recognised */ + for_each_table_entry ( header, HTTP_RESPONSE_HEADERS ) { + if ( strcasecmp ( name, header->name ) == 0 ) + return header->parse ( http, line ); + } + + /* Unrecognised headers should be ignored */ + return 0; } /** - * Handle WWW-Authenticate Digest header + * Parse HTTP response headers * - * @v http HTTP request - * @v params Parameters + * @v http HTTP transaction * @ret rc Return status code */ -static int http_rx_digest_auth ( struct http_request *http, char *params ) { +static int http_parse_headers ( struct http_transaction *http ) { + char *line; + char *next; + int rc; - DBGC ( http, "HTTP %p Digest authentication required (%s)\n", - http, params ); + /* Get status line */ + line = http->response.headers.data; + assert ( line != NULL ); + next = ( line + strlen ( line ) + 1 /* NUL */ ); - /* If we received a 401 Unauthorized response, then retry - * using Digest authentication - */ - if ( ( http->code == 401 ) && - ( ! ( http->flags & HTTP_DIGEST_AUTH ) ) && - ( http->uri->user != NULL ) ) { - - /* Extract realm */ - free ( http->auth_realm ); - http->auth_realm = http_digest_param ( params, "realm=\"" ); - if ( ! http->auth_realm ) { - DBGC ( http, "HTTP %p Digest prompt missing realm\n", - http ); - return -EINVAL_HEADER; - } + /* Parse status line */ + if ( ( rc = http_parse_status ( http, line ) ) != 0 ) + return rc; - /* Extract nonce */ - free ( http->auth_nonce ); - http->auth_nonce = http_digest_param ( params, "nonce=\"" ); - if ( ! http->auth_nonce ) { - DBGC ( http, "HTTP %p Digest prompt missing nonce\n", - http ); - return -EINVAL_HEADER; - } + /* Process header lines */ + while ( 1 ) { - /* Extract opaque */ - free ( http->auth_opaque ); - http->auth_opaque = http_digest_param ( params, "opaque=\"" ); - if ( ! http->auth_opaque ) { - /* Not an error; "opaque" is optional */ - } + /* Move to next line */ + line = next; + next = ( line + strlen ( line ) + 1 /* NUL */ ); + + /* Stop on terminating blank line */ + if ( ! line[0] ) + return 0; - http->flags |= ( HTTP_TRY_AGAIN | HTTP_DIGEST_AUTH ); + /* Process header line */ + if ( ( rc = http_parse_header ( http, line ) ) != 0 ) + return rc; } +} +/** + * Parse HTTP "Location" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_location ( struct http_transaction *http, char *line ) { + + /* Store location */ + http->response.location = line; return 0; } -/** An HTTP WWW-Authenticate header handler */ -struct http_auth_header_handler { - /** Scheme (e.g. "Basic") */ - const char *scheme; - /** Handle received parameters - * - * @v http HTTP request - * @v params Parameters - * @ret rc Return status code - */ - int ( * rx ) ( struct http_request *http, char *params ); +/** HTTP "Location" header */ +struct http_response_header http_response_location __http_response_header = { + .name = "Location", + .parse = http_parse_location, }; -/** List of HTTP WWW-Authenticate header handlers */ -static struct http_auth_header_handler http_auth_header_handlers[] = { - { - .scheme = "Basic", - .rx = http_rx_basic_auth, - }, - { - .scheme = "Digest", - .rx = http_rx_digest_auth, - }, - { NULL, NULL }, +/** + * Parse HTTP "Transfer-Encoding" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_transfer_encoding ( struct http_transaction *http, + char *line ) { + struct http_transfer_encoding *encoding; + + /* Check for known transfer encodings */ + for_each_table_entry ( encoding, HTTP_TRANSFER_ENCODINGS ) { + if ( strcasecmp ( line, encoding->name ) == 0 ) { + http->response.transfer.encoding = encoding; + return 0; + } + } + + DBGC ( http, "HTTP %p unrecognised Transfer-Encoding \"%s\"\n", + http, line ); + return -ENOTSUP_TRANSFER; +} + +/** HTTP "Transfer-Encoding" header */ +struct http_response_header +http_response_transfer_encoding __http_response_header = { + .name = "Transfer-Encoding", + .parse = http_parse_transfer_encoding, }; /** - * Handle HTTP WWW-Authenticate header + * Parse HTTP "Connection" header * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction + * @v line Remaining header line * @ret rc Return status code */ -static int http_rx_www_authenticate ( struct http_request *http, char *value ) { - struct http_auth_header_handler *handler; - char *separator; - char *scheme; - char *params; - int rc; +static int http_parse_connection ( struct http_transaction *http, char *line ) { - /* Extract scheme */ - separator = strchr ( value, ' ' ); - if ( ! separator ) { - DBGC ( http, "HTTP %p malformed WWW-Authenticate header\n", - http ); - return -EINVAL_HEADER; + /* Check for known connection intentions */ + if ( strcasecmp ( line, "keep-alive" ) == 0 ) { + http->response.flags |= HTTP_RESPONSE_KEEPALIVE; + return 0; } - *separator = '\0'; - scheme = value; - params = ( separator + 1 ); - - /* Hand off to header handler, if one exists */ - for ( handler = http_auth_header_handlers; handler->scheme; handler++ ){ - if ( strcasecmp ( scheme, handler->scheme ) == 0 ) { - if ( ( rc = handler->rx ( http, params ) ) != 0 ) - return rc; - break; - } + if ( strcasecmp ( line, "close" ) == 0 ) { + http->response.flags &= ~HTTP_RESPONSE_KEEPALIVE; + return 0; } - return 0; + + DBGC ( http, "HTTP %p unrecognised Connection \"%s\"\n", http, line ); + return -ENOTSUP_CONNECTION; } +/** HTTP "Connection" header */ +struct http_response_header http_response_connection __http_response_header = { + .name = "Connection", + .parse = http_parse_connection, +}; + /** - * Handle HTTP Retry-After header + * Parse HTTP "Content-Length" header * - * @v http HTTP request - * @v value HTTP header value + * @v http HTTP transaction + * @v line Remaining header line * @ret rc Return status code */ -static int http_rx_retry_after ( struct http_request *http, char *value ) { - unsigned long seconds; +static int http_parse_content_length ( struct http_transaction *http, + char *line ) { char *endp; - DBGC ( http, "HTTP %p retry requested (%s)\n", http, value ); + /* Parse length */ + http->response.content.len = strtoul ( line, &endp, 10 ); + if ( *endp != '\0' ) { + DBGC ( http, "HTTP %p invalid Content-Length \"%s\"\n", + http, line ); + return -EINVAL_CONTENT_LENGTH; + } - /* If we received a 503 Service Unavailable response, then - * retry after the specified number of seconds. If the value - * is not a simple number of seconds (e.g. a full HTTP date), - * then retry after a fixed delay, since we don't have code - * able to parse full HTTP dates. - */ - if ( http->code == 503 ) { - seconds = strtoul ( value, &endp, 10 ); - if ( *endp != '\0' ) { - seconds = HTTP_RETRY_SECONDS; - DBGC ( http, "HTTP %p cannot understand \"%s\"; " - "using %ld seconds\n", http, value, seconds ); + /* Record that we have a content length (since it may be zero) */ + http->response.flags |= HTTP_RESPONSE_CONTENT_LEN; + + return 0; +} + +/** HTTP "Content-Length" header */ +struct http_response_header +http_response_content_length __http_response_header = { + .name = "Content-Length", + .parse = http_parse_content_length, +}; + +/** + * Parse HTTP "Content-Encoding" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_content_encoding ( struct http_transaction *http, + char *line ) { + struct http_content_encoding *encoding; + + /* Check for known content encodings */ + for_each_table_entry ( encoding, HTTP_CONTENT_ENCODINGS ) { + if ( encoding->supported && ( ! encoding->supported ( http ) ) ) + continue; + if ( strcasecmp ( line, encoding->name ) == 0 ) { + http->response.content.encoding = encoding; + return 0; } - http->flags |= HTTP_TRY_AGAIN; - http->retry_delay = ( seconds * TICKS_PER_SEC ); } + /* Some servers (e.g. Apache) have a habit of specifying + * unwarranted content encodings. For example, if Apache + * detects (via /etc/httpd/conf/magic) that a file's contents + * are gzip-compressed, it will set "Content-Encoding: x-gzip" + * regardless of the client's Accept-Encoding header. The + * only viable way to handle such servers is to treat unknown + * content encodings as equivalent to "identity". + */ + DBGC ( http, "HTTP %p unrecognised Content-Encoding \"%s\"\n", + http, line ); return 0; } -/** An HTTP header handler */ -struct http_header_handler { - /** Name (e.g. "Content-Length") */ - const char *header; - /** Handle received header - * - * @v http HTTP request - * @v value HTTP header value - * @ret rc Return status code - * - * If an error is returned, the download will be aborted. - */ - int ( * rx ) ( struct http_request *http, char *value ); +/** HTTP "Content-Encoding" header */ +struct http_response_header +http_response_content_encoding __http_response_header = { + .name = "Content-Encoding", + .parse = http_parse_content_encoding, }; -/** List of HTTP header handlers */ -static struct http_header_handler http_header_handlers[] = { - { - .header = "Location", - .rx = http_rx_location, - }, - { - .header = "Content-Length", - .rx = http_rx_content_length, - }, - { - .header = "Transfer-Encoding", - .rx = http_rx_transfer_encoding, - }, - { - .header = "Connection", - .rx = http_rx_connection, - }, - { - .header = "WWW-Authenticate", - .rx = http_rx_www_authenticate, - }, - { - .header = "Retry-After", - .rx = http_rx_retry_after, - }, - { NULL, NULL } +/** + * Parse HTTP "Retry-After" header + * + * @v http HTTP transaction + * @v line Remaining header line + * @ret rc Return status code + */ +static int http_parse_retry_after ( struct http_transaction *http, + char *line ) { + char *endp; + + /* Try to parse value as a simple number of seconds */ + http->response.retry_after = strtoul ( line, &endp, 10 ); + if ( *endp != '\0' ) { + /* For any value which is not a simple number of + * seconds (e.g. a full HTTP date), just retry after a + * fixed delay, since we don't have code able to parse + * full HTTP dates. + */ + http->response.retry_after = HTTP_RETRY_SECONDS; + DBGC ( http, "HTTP %p cannot understand Retry-After \"%s\"; " + "using %d seconds\n", http, line, HTTP_RETRY_SECONDS ); + } + + /* Allow HTTP request to be retried after specified delay */ + http->response.flags |= HTTP_RESPONSE_RETRY; + + return 0; +} + +/** HTTP "Retry-After" header */ +struct http_response_header http_response_retry_after __http_response_header = { + .name = "Retry-After", + .parse = http_parse_retry_after, }; /** - * Handle HTTP header + * Handle received HTTP headers * - * @v http HTTP request - * @v header HTTP header + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) * @ret rc Return status code */ -static int http_rx_header ( struct http_request *http, char *header ) { - struct http_header_handler *handler; - char *separator; - char *value; +static int http_rx_headers ( struct http_transaction *http, + struct io_buffer **iobuf ) { + struct http_transfer_encoding *transfer; + struct http_content_encoding *content; + char *line; int rc; - /* An empty header line marks the end of this phase */ - if ( ! header[0] ) { - empty_line_buffer ( &http->linebuf ); + /* Buffer header line */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, + &http->response.headers ) ) != 0 ) + return rc; - /* Handle response code */ - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) { - if ( ( rc = http_response_to_rc ( http->code ) ) != 0 ) - return rc; - } + /* Wait until we see the empty line marking end of headers */ + line = buffered_line ( &http->response.headers ); + if ( ( line == NULL ) || ( line[0] != '\0' ) ) + return 0; - /* Move to next state */ - if ( http->rx_state == HTTP_RX_HEADER ) { - DBGC ( http, "HTTP %p start of data\n", http ); - http->rx_state = ( http->chunked ? - HTTP_RX_CHUNK_LEN : HTTP_RX_DATA ); - if ( ( http->partial_len != 0 ) && - ( ! ( http->flags & HTTP_TRY_AGAIN ) ) ) { - http->remaining = http->partial_len; - } - return 0; - } else { - DBGC ( http, "HTTP %p end of trailer\n", http ); - http_done ( http ); - return 0; - } + /* Process headers */ + if ( ( rc = http_parse_headers ( http ) ) != 0 ) + return rc; + + /* Initialise content encoding, if applicable */ + if ( ( content = http->response.content.encoding ) && + ( ( rc = content->init ( http ) ) != 0 ) ) { + DBGC ( http, "HTTP %p could not initialise %s content " + "encoding: %s\n", http, content->name, strerror ( rc ) ); + return rc; } - DBGC ( http, "HTTP %p header \"%s\"\n", http, header ); + /* Presize receive buffer, if we have a content length */ + if ( http->response.content.len ) { + xfer_seek ( &http->transfer, http->response.content.len ); + xfer_seek ( &http->transfer, 0 ); + } - /* Split header at the ": " */ - separator = strstr ( header, ": " ); - if ( ! separator ) { - DBGC ( http, "HTTP %p malformed header\n", http ); - return -EINVAL_HEADER; + /* Complete transfer if this is a HEAD request */ + if ( http->request.method == &http_head ) { + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + return rc; + return 0; } - *separator = '\0'; - value = ( separator + 2 ); - - /* Hand off to header handler, if one exists */ - for ( handler = http_header_handlers ; handler->header ; handler++ ) { - if ( strcasecmp ( header, handler->header ) == 0 ) { - if ( ( rc = handler->rx ( http, value ) ) != 0 ) - return rc; - break; - } + + /* Default to identity transfer encoding, if none specified */ + if ( ! http->response.transfer.encoding ) + http->response.transfer.encoding = &http_transfer_identity; + + /* Move to transfer encoding-specific data state */ + transfer = http->response.transfer.encoding; + http->state = &transfer->state; + + /* Initialise transfer encoding */ + if ( ( rc = transfer->init ( http ) ) != 0 ) { + DBGC ( http, "HTTP %p could not initialise %s transfer " + "encoding: %s\n", http, transfer->name, strerror ( rc )); + return rc; } + return 0; } +/** HTTP response headers state */ +static struct http_state http_headers = { + .rx = http_rx_headers, + .close = http_close_error, +}; + +/****************************************************************************** + * + * Identity transfer encoding + * + ****************************************************************************** + */ + /** - * Handle HTTP chunk length + * Initialise transfer encoding * - * @v http HTTP request - * @v length HTTP chunk length + * @v http HTTP transaction * @ret rc Return status code */ -static int http_rx_chunk_len ( struct http_request *http, char *length ) { - char *endp; +static int http_init_transfer_identity ( struct http_transaction *http ) { + int rc; - /* Skip blank lines between chunks */ - if ( length[0] == '\0' ) - return 0; + /* Complete transfer immediately if we have a zero content length */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->response.content.len == 0 ) && + ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) + return rc; - /* Parse chunk length */ - http->chunk_remaining = strtoul ( length, &endp, 16 ); - if ( *endp != '\0' ) { - DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", - http, length ); - return -EINVAL_CHUNK_LENGTH; - } + return 0; +} - /* Terminate chunked encoding if applicable */ - if ( http->chunk_remaining == 0 ) { - DBGC ( http, "HTTP %p end of chunks\n", http ); - http->chunked = 0; - http->rx_state = HTTP_RX_TRAILER; - return 0; - } +/** + * Handle received data + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ +static int http_rx_transfer_identity ( struct http_transaction *http, + struct io_buffer **iobuf ) { + size_t len = iob_len ( *iobuf ); + int rc; + + /* Update lengths */ + http->len += len; - /* Use seek() to notify recipient of new filesize */ - DBGC ( http, "HTTP %p start of chunk of length %zd\n", - http, http->chunk_remaining ); - if ( ! ( http->flags & HTTP_TRY_AGAIN ) ) { - xfer_seek ( &http->xfer, - ( http->rx_len + http->chunk_remaining ) ); - xfer_seek ( &http->xfer, http->rx_len ); + /* Fail if this transfer would overrun the expected content + * length (if any). + */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->len > http->response.content.len ) ) { + DBGC ( http, "HTTP %p content length overrun\n", http ); + return -EIO_CONTENT_LENGTH; } - /* Start receiving data */ - http->rx_state = HTTP_RX_DATA; + /* Hand off to content encoding */ + if ( ( rc = xfer_deliver_iob ( &http->transfer, + iob_disown ( *iobuf ) ) ) != 0 ) + return rc; + + /* Complete transfer if we have received the expected content + * length (if any). + */ + if ( ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) && + ( http->len == http->response.content.len ) && + ( ( rc = http_transfer_complete ( http ) ) != 0 ) ) + return rc; return 0; } -/** An HTTP line-based data handler */ -struct http_line_handler { - /** Handle line - * - * @v http HTTP request - * @v line Line to handle - * @ret rc Return status code +/** + * Handle server connection close + * + * @v http HTTP transaction + * @v rc Reason for close + */ +static void http_close_transfer_identity ( struct http_transaction *http, + int rc ) { + + /* Fail if any error occurred */ + if ( rc != 0 ) + goto err; + + /* Fail if we have a content length (since we would have + * already closed the connection if we had received the + * correct content length). */ - int ( * rx ) ( struct http_request *http, char *line ); -}; + if ( http->response.flags & HTTP_RESPONSE_CONTENT_LEN ) { + DBGC ( http, "HTTP %p content length underrun\n", http ); + rc = EIO_CONTENT_LENGTH; + goto err; + } + + /* Indicate that transfer is complete */ + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + goto err; -/** List of HTTP line-based data handlers */ -static struct http_line_handler http_line_handlers[] = { - [HTTP_RX_RESPONSE] = { .rx = http_rx_response }, - [HTTP_RX_HEADER] = { .rx = http_rx_header }, - [HTTP_RX_CHUNK_LEN] = { .rx = http_rx_chunk_len }, - [HTTP_RX_TRAILER] = { .rx = http_rx_header }, + return; + + err: + http_close ( http, rc ); +} + +/** Identity transfer encoding */ +static struct http_transfer_encoding http_transfer_identity = { + .name = "identity", + .init = http_init_transfer_identity, + .state = { + .rx = http_rx_transfer_identity, + .close = http_close_transfer_identity, + }, }; +/****************************************************************************** + * + * Chunked transfer encoding + * + ****************************************************************************** + */ + /** - * Handle new data arriving via HTTP connection + * Initialise transfer encoding * - * @v http HTTP request - * @v iobuf I/O buffer - * @v meta Data transfer metadata + * @v http HTTP transaction * @ret rc Return status code */ -static int http_socket_deliver ( struct http_request *http, - struct io_buffer *iobuf, - struct xfer_metadata *meta __unused ) { - struct http_line_handler *lh; +static int http_init_transfer_chunked ( struct http_transaction *http ) { + + /* Sanity checks */ + assert ( http->remaining == 0 ); + assert ( http->linebuf.len == 0 ); + + return 0; +} + +/** + * Handle received chunk length + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ +static int http_rx_chunk_len ( struct http_transaction *http, + struct io_buffer **iobuf ) { char *line; - size_t data_len; - ssize_t line_len; - int rc = 0; + char *endp; + size_t len; + int rc; - profile_start ( &http_rx_profiler ); - while ( iobuf && iob_len ( iobuf ) ) { + /* Receive into temporary line buffer */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) + return rc; - switch ( http->rx_state ) { - case HTTP_RX_IDLE: - /* Receiving any data in this state is an error */ - DBGC ( http, "HTTP %p received %zd bytes while %s\n", - http, iob_len ( iobuf ), - ( ( http->rx_state == HTTP_RX_IDLE ) ? - "idle" : "dead" ) ); - rc = -EPROTO_UNSOLICITED; - goto done; - case HTTP_RX_DEAD: - /* Do no further processing */ - goto done; - case HTTP_RX_DATA: - /* Pass received data to caller */ - data_len = iob_len ( iobuf ); - if ( http->chunk_remaining && - ( http->chunk_remaining < data_len ) ) { - data_len = http->chunk_remaining; - } - if ( http->remaining && - ( http->remaining < data_len ) ) { - data_len = http->remaining; - } - if ( http->flags & HTTP_TRY_AGAIN ) { - /* Discard all received data */ - iob_pull ( iobuf, data_len ); - } else if ( http->rx_buffer != UNULL ) { - /* Copy to partial transfer buffer */ - copy_to_user ( http->rx_buffer, http->rx_len, - iobuf->data, data_len ); - iob_pull ( iobuf, data_len ); - } else if ( data_len < iob_len ( iobuf ) ) { - /* Deliver partial buffer as raw data */ - profile_start ( &http_xfer_profiler ); - rc = xfer_deliver_raw ( &http->xfer, - iobuf->data, data_len ); - iob_pull ( iobuf, data_len ); - if ( rc != 0 ) - goto done; - profile_stop ( &http_xfer_profiler ); - } else { - /* Deliver whole I/O buffer */ - profile_start ( &http_xfer_profiler ); - if ( ( rc = xfer_deliver_iob ( &http->xfer, - iob_disown ( iobuf ) ) ) != 0 ) - goto done; - profile_stop ( &http_xfer_profiler ); - } - http->rx_len += data_len; - if ( http->chunk_remaining ) { - http->chunk_remaining -= data_len; - if ( http->chunk_remaining == 0 ) - http->rx_state = HTTP_RX_CHUNK_LEN; - } - if ( http->remaining ) { - http->remaining -= data_len; - if ( ( http->remaining == 0 ) && - ( http->rx_state == HTTP_RX_DATA ) ) { - http_done ( http ); - } - } - break; - case HTTP_RX_RESPONSE: - case HTTP_RX_HEADER: - case HTTP_RX_CHUNK_LEN: - case HTTP_RX_TRAILER: - /* In the other phases, buffer and process a - * line at a time - */ - line_len = line_buffer ( &http->linebuf, iobuf->data, - iob_len ( iobuf ) ); - if ( line_len < 0 ) { - rc = line_len; - DBGC ( http, "HTTP %p could not buffer line: " - "%s\n", http, strerror ( rc ) ); - goto done; - } - iob_pull ( iobuf, line_len ); - line = buffered_line ( &http->linebuf ); - if ( line ) { - lh = &http_line_handlers[http->rx_state]; - if ( ( rc = lh->rx ( http, line ) ) != 0 ) - goto done; - } - break; - default: - assert ( 0 ); - break; - } + /* Wait until we receive a non-empty line */ + line = buffered_line ( &http->linebuf ); + if ( ( line == NULL ) || ( line[0] == '\0' ) ) + return 0; + + /* Parse chunk length */ + http->remaining = strtoul ( line, &endp, 16 ); + if ( *endp != '\0' ) { + DBGC ( http, "HTTP %p invalid chunk length \"%s\"\n", + http, line ); + return -EINVAL_CHUNK_LENGTH; } - done: - if ( rc ) - http_close ( http, rc ); - free_iob ( iobuf ); - profile_stop ( &http_rx_profiler ); - return rc; + /* Empty line buffer */ + empty_line_buffer ( &http->linebuf ); + + /* Update expected length */ + len = ( http->len + http->remaining ); + xfer_seek ( &http->transfer, len ); + xfer_seek ( &http->transfer, http->len ); + + /* If chunk length is zero, then move to response trailers state */ + if ( ! http->remaining ) + http->state = &http_trailers; + + return 0; } /** - * Check HTTP socket flow control window + * Handle received chunk data * - * @v http HTTP request - * @ret len Length of window + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code */ -static size_t http_socket_window ( struct http_request *http __unused ) { +static int http_rx_chunk_data ( struct http_transaction *http, + struct io_buffer **iobuf ) { + struct io_buffer *payload; + uint8_t *crlf; + size_t len; + int rc; - /* Window is always open. This is to prevent TCP from - * stalling if our parent window is not currently open. + /* In the common case of a final chunk in a packet which also + * includes the terminating CRLF, strip the terminating CRLF + * (which we would ignore anyway) and hence avoid + * unnecessarily copying the data. */ - return ( ~( ( size_t ) 0 ) ); + if ( iob_len ( *iobuf ) == ( http->remaining + 2 /* CRLF */ ) ) { + crlf = ( (*iobuf)->data + http->remaining ); + if ( ( crlf[0] == '\r' ) && ( crlf[1] == '\n' ) ) + iob_unput ( (*iobuf), 2 /* CRLF */ ); + } + len = iob_len ( *iobuf ); + + /* Use whole/partial buffer as applicable */ + if ( len <= http->remaining ) { + + /* Whole buffer is to be consumed: decrease remaining + * length and use original I/O buffer as payload. + */ + payload = iob_disown ( *iobuf ); + http->len += len; + http->remaining -= len; + + } else { + + /* Partial buffer is to be consumed: copy data to a + * temporary I/O buffer. + */ + payload = alloc_iob ( http->remaining ); + if ( ! payload ) { + rc = -ENOMEM; + goto err; + } + memcpy ( iob_put ( payload, http->remaining ), (*iobuf)->data, + http->remaining ); + iob_pull ( *iobuf, http->remaining ); + http->len += http->remaining; + http->remaining = 0; + } + + /* Hand off to content encoding */ + if ( ( rc = xfer_deliver_iob ( &http->transfer, + iob_disown ( payload ) ) ) != 0 ) + goto err; + + return 0; + + err: + assert ( payload == NULL ); + return rc; } /** - * Close HTTP socket + * Handle received chunked data * - * @v http HTTP request - * @v rc Reason for close + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code */ -static void http_socket_close ( struct http_request *http, int rc ) { +static int http_rx_transfer_chunked ( struct http_transaction *http, + struct io_buffer **iobuf ) { - /* If we have an error, terminate */ - if ( rc != 0 ) { - http_close ( http, rc ); - return; + /* Handle as chunk length or chunk data as appropriate */ + if ( http->remaining ) { + return http_rx_chunk_data ( http, iobuf ); + } else { + return http_rx_chunk_len ( http, iobuf ); } - - /* Mark HTTP request as complete */ - http_done ( http ); } -/** - * Generate HTTP Basic authorisation string +/** Chunked transfer encoding */ +struct http_transfer_encoding http_transfer_chunked __http_transfer_encoding = { + .name = "chunked", + .init = http_init_transfer_chunked, + .state = { + .rx = http_rx_transfer_chunked, + .close = http_close_error, + }, +}; + +/****************************************************************************** * - * @v http HTTP request - * @ret auth Authorisation string, or NULL on error + * Response trailers * - * The authorisation string is dynamically allocated, and must be - * freed by the caller. + ****************************************************************************** */ -static char * http_basic_auth ( struct http_request *http ) { - const char *user = http->uri->user; - const char *password = ( http->uri->password ? - http->uri->password : "" ); - size_t user_pw_len = - ( strlen ( user ) + 1 /* ":" */ + strlen ( password ) ); - char user_pw[ user_pw_len + 1 /* NUL */ ]; - size_t user_pw_base64_len = base64_encoded_len ( user_pw_len ); - char user_pw_base64[ user_pw_base64_len + 1 /* NUL */ ]; - char *auth; - int len; - /* Sanity check */ - assert ( user != NULL ); +/** + * Handle received HTTP trailer + * + * @v http HTTP transaction + * @v iobuf I/O buffer (may be claimed) + * @ret rc Return status code + */ +static int http_rx_trailers ( struct http_transaction *http, + struct io_buffer **iobuf ) { + char *line; + int rc; - /* Make "user:password" string from decoded fields */ - snprintf ( user_pw, sizeof ( user_pw ), "%s:%s", user, password ); + /* Buffer trailer line */ + if ( ( rc = http_rx_linebuf ( http, *iobuf, &http->linebuf ) ) != 0 ) + return rc; - /* Base64-encode the "user:password" string */ - base64_encode ( ( void * ) user_pw, user_pw_len, user_pw_base64 ); + /* Wait until we see the empty line marking end of trailers */ + line = buffered_line ( &http->linebuf ); + if ( ( line == NULL ) || ( line[0] != '\0' ) ) + return 0; - /* Generate the authorisation string */ - len = asprintf ( &auth, "Authorization: Basic %s\r\n", - user_pw_base64 ); - if ( len < 0 ) - return NULL; + /* Empty line buffer */ + empty_line_buffer ( &http->linebuf ); - return auth; + /* Transfer is complete */ + if ( ( rc = http_transfer_complete ( http ) ) != 0 ) + return rc; + + return 0; } -/** - * Generate HTTP Digest authorisation string +/** HTTP response trailers state */ +static struct http_state http_trailers = { + .rx = http_rx_trailers, + .close = http_close_error, +}; + +/****************************************************************************** * - * @v http HTTP request - * @v method HTTP method (e.g. "GET") - * @v uri HTTP request URI (e.g. "/index.html") - * @ret auth Authorisation string, or NULL on error + * Simple URI openers * - * The authorisation string is dynamically allocated, and must be - * freed by the caller. + ****************************************************************************** */ -static char * http_digest_auth ( struct http_request *http, - const char *method, const char *uri ) { - const char *user = http->uri->user; - const char *password = ( http->uri->password ? - http->uri->password : "" ); - const char *realm = http->auth_realm; - const char *nonce = http->auth_nonce; - const char *opaque = http->auth_opaque; - static const char colon = ':'; - uint8_t ctx[MD5_CTX_SIZE]; - uint8_t digest[MD5_DIGEST_SIZE]; - char ha1[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char ha2[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char response[ base16_encoded_len ( sizeof ( digest ) ) + 1 /* NUL */ ]; - char *auth; - int len; - - /* Sanity checks */ - assert ( user != NULL ); - assert ( realm != NULL ); - assert ( nonce != NULL ); - - /* Generate HA1 */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, user, strlen ( user ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, realm, strlen ( realm ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, password, strlen ( password ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), ha1 ); - - /* Generate HA2 */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, method, strlen ( method ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, uri, strlen ( uri ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), ha2 ); - - /* Generate response */ - digest_init ( &md5_algorithm, ctx ); - digest_update ( &md5_algorithm, ctx, ha1, strlen ( ha1 ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, nonce, strlen ( nonce ) ); - digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); - digest_update ( &md5_algorithm, ctx, ha2, strlen ( ha2 ) ); - digest_final ( &md5_algorithm, ctx, digest ); - base16_encode ( digest, sizeof ( digest ), response ); - - /* Generate the authorisation string */ - len = asprintf ( &auth, "Authorization: Digest username=\"%s\", " - "realm=\"%s\", nonce=\"%s\", uri=\"%s\", " - "%s%s%sresponse=\"%s\"\r\n", user, realm, nonce, uri, - ( opaque ? "opaque=\"" : "" ), - ( opaque ? opaque : "" ), - ( opaque ? "\", " : "" ), response ); - if ( len < 0 ) - return NULL; - - return auth; -} /** - * Generate HTTP POST parameter list + * Construct HTTP parameter list * - * @v http HTTP request + * @v params Parameter list * @v buf Buffer to contain HTTP POST parameters * @v len Length of buffer * @ret len Length of parameter list (excluding terminating NUL) */ -static size_t http_post_params ( struct http_request *http, - char *buf, size_t len ) { +static size_t http_params ( struct parameters *params, char *buf, size_t len ) { struct parameter *param; ssize_t remaining = len; size_t frag_len; /* Add each parameter in the form "key=value", joined with "&" */ len = 0; - for_each_param ( param, http->uri->params ) { + for_each_param ( param, params ) { /* Add the "&", if applicable */ if ( len ) { @@ -1201,374 +1853,78 @@ static size_t http_post_params ( struct http_request *http, } /** - * Generate HTTP POST body + * Open HTTP transaction for simple GET URI * - * @v http HTTP request - * @ret post I/O buffer containing POST body, or NULL on error + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code */ -static struct io_buffer * http_post ( struct http_request *http ) { - struct io_buffer *post; - size_t len; - size_t check_len; +static int http_open_get_uri ( struct interface *xfer, struct uri *uri ) { - /* Calculate length of parameter list */ - len = http_post_params ( http, NULL, 0 ); - - /* Allocate parameter list */ - post = alloc_iob ( len + 1 /* NUL */ ); - if ( ! post ) - return NULL; - - /* Fill parameter list */ - check_len = http_post_params ( http, iob_put ( post, len ), - ( len + 1 /* NUL */ ) ); - assert ( len == check_len ); - DBGC ( http, "HTTP %p POST %s\n", http, ( ( char * ) post->data ) ); - - return post; + return http_open ( xfer, &http_get, uri, NULL, NULL ); } /** - * HTTP process + * Open HTTP transaction for simple POST URI * - * @v http HTTP request + * @v xfer Data transfer interface + * @v uri Request URI + * @ret rc Return status code */ -static void http_step ( struct http_request *http ) { - struct io_buffer *post; - struct uri host_uri; - struct uri path_uri; - char *host_uri_string; - char *path_uri_string; - char *method; - char *range; - char *auth; - char *content; - int len; +static int http_open_post_uri ( struct interface *xfer, struct uri *uri ) { + struct parameters *params = uri->params; + struct http_request_content content; + void *data; + size_t len; + size_t check_len; int rc; - /* Do nothing if we have already transmitted the request */ - if ( ! ( http->flags & HTTP_TX_PENDING ) ) - return; - - /* Do nothing until socket is ready */ - if ( ! xfer_window ( &http->socket ) ) - return; - - /* Force a HEAD request if we have nowhere to send any received data */ - if ( ( xfer_window ( &http->xfer ) == 0 ) && - ( http->rx_buffer == UNULL ) ) { - http->flags |= ( HTTP_HEAD_ONLY | HTTP_CLIENT_KEEPALIVE ); - } - - /* Determine method */ - method = ( ( http->flags & HTTP_HEAD_ONLY ) ? "HEAD" : - ( http->uri->params ? "POST" : "GET" ) ); + /* Calculate length of parameter list */ + len = http_params ( params, NULL, 0 ); - /* Construct host URI */ - memset ( &host_uri, 0, sizeof ( host_uri ) ); - host_uri.host = http->uri->host; - host_uri.port = http->uri->port; - host_uri_string = format_uri_alloc ( &host_uri ); - if ( ! host_uri_string ) { + /* Allocate temporary parameter list */ + data = zalloc ( len + 1 /* NUL */ ); + if ( ! data ) { rc = -ENOMEM; - goto err_host_uri; + goto err_alloc; } - /* Construct path URI */ - memset ( &path_uri, 0, sizeof ( path_uri ) ); - path_uri.path = ( http->uri->path ? http->uri->path : "/" ); - path_uri.query = http->uri->query; - path_uri_string = format_uri_alloc ( &path_uri ); - if ( ! path_uri_string ) { - rc = -ENOMEM; - goto err_path_uri; - } + /* Construct temporary parameter list */ + check_len = http_params ( params, data, ( len + 1 /* NUL */ ) ); + assert ( check_len == len ); - /* Calculate range request parameters if applicable */ - if ( http->partial_len ) { - len = asprintf ( &range, "Range: bytes=%zd-%zd\r\n", - http->partial_start, - ( http->partial_start + http->partial_len - - 1 ) ); - if ( len < 0 ) { - rc = len; - goto err_range; - } - } else { - range = NULL; - } + /* Construct request content */ + content.type = "application/x-www-form-urlencoded"; + content.data = data; + content.len = len; - /* Construct authorisation, if applicable */ - if ( http->flags & HTTP_BASIC_AUTH ) { - auth = http_basic_auth ( http ); - if ( ! auth ) { - rc = -ENOMEM; - goto err_auth; - } - } else if ( http->flags & HTTP_DIGEST_AUTH ) { - auth = http_digest_auth ( http, method, path_uri_string ); - if ( ! auth ) { - rc = -ENOMEM; - goto err_auth; - } - } else { - auth = NULL; - } + /* Open HTTP transaction */ + if ( ( rc = http_open ( xfer, &http_post, uri, NULL, &content ) ) != 0 ) + goto err_open; - /* Construct POST content, if applicable */ - if ( http->uri->params ) { - post = http_post ( http ); - if ( ! post ) { - rc = -ENOMEM; - goto err_post; - } - len = asprintf ( &content, "Content-Type: " - "application/x-www-form-urlencoded\r\n" - "Content-Length: %zd\r\n", iob_len ( post ) ); - if ( len < 0 ) { - rc = len; - goto err_content; - } - } else { - post = NULL; - content = NULL; - } - - /* Mark request as transmitted */ - http->flags &= ~HTTP_TX_PENDING; - - /* Send request */ - if ( ( rc = xfer_printf ( &http->socket, - "%s %s HTTP/1.1\r\n" - "User-Agent: iPXE/%s\r\n" - "Host: %s\r\n" - "%s%s%s%s" - "\r\n", - method, path_uri_string, product_version, - host_uri_string, - ( ( http->flags & HTTP_CLIENT_KEEPALIVE ) ? - "Connection: keep-alive\r\n" : "" ), - ( range ? range : "" ), - ( auth ? auth : "" ), - ( content ? content : "" ) ) ) != 0 ) { - goto err_xfer; - } - - /* Send POST content, if applicable */ - if ( post ) { - if ( ( rc = xfer_deliver_iob ( &http->socket, - iob_disown ( post ) ) ) != 0 ) - goto err_xfer_post; - } - - err_xfer_post: - err_xfer: - free ( content ); - err_content: - free ( post ); - err_post: - free ( auth ); - err_auth: - free ( range ); - err_range: - free ( path_uri_string ); - err_path_uri: - free ( host_uri_string ); - err_host_uri: - if ( rc != 0 ) - http_close ( http, rc ); -} - -/** - * Check HTTP data transfer flow control window - * - * @v http HTTP request - * @ret len Length of window - */ -static size_t http_xfer_window ( struct http_request *http ) { - - /* New block commands may be issued only when we are idle */ - return ( ( http->rx_state == HTTP_RX_IDLE ) ? 1 : 0 ); -} - -/** - * Initiate HTTP partial read - * - * @v http HTTP request - * @v partial Partial transfer interface - * @v offset Starting offset - * @v buffer Data buffer - * @v len Length - * @ret rc Return status code - */ -static int http_partial_read ( struct http_request *http, - struct interface *partial, - size_t offset, userptr_t buffer, size_t len ) { - - /* Sanity check */ - if ( http_xfer_window ( http ) == 0 ) - return -EBUSY; - - /* Initialise partial transfer parameters */ - http->rx_buffer = buffer; - http->partial_start = offset; - http->partial_len = len; - - /* Schedule request */ - http->rx_state = HTTP_RX_RESPONSE; - http->flags = ( HTTP_TX_PENDING | HTTP_CLIENT_KEEPALIVE ); - if ( ! len ) - http->flags |= HTTP_HEAD_ONLY; - process_add ( &http->process ); - - /* Attach to parent interface and return */ - intf_plug_plug ( &http->partial, partial ); - - return 0; -} - -/** - * Issue HTTP block device read - * - * @v http HTTP request - * @v block Block data interface - * @v lba Starting logical block address - * @v count Number of blocks to transfer - * @v buffer Data buffer - * @v len Length of data buffer - * @ret rc Return status code - */ -static int http_block_read ( struct http_request *http, - struct interface *block, - uint64_t lba, unsigned int count, - userptr_t buffer, size_t len __unused ) { - - return http_partial_read ( http, block, ( lba * HTTP_BLKSIZE ), - buffer, ( count * HTTP_BLKSIZE ) ); -} - -/** - * Read HTTP block device capacity - * - * @v http HTTP request - * @v block Block data interface - * @ret rc Return status code - */ -static int http_block_read_capacity ( struct http_request *http, - struct interface *block ) { - - return http_partial_read ( http, block, 0, 0, 0 ); -} - -/** - * Describe HTTP device in an ACPI table - * - * @v http HTTP request - * @v acpi ACPI table - * @v len Length of ACPI table - * @ret rc Return status code - */ -static int http_acpi_describe ( struct http_request *http, - struct acpi_description_header *acpi, - size_t len ) { - - DBGC ( http, "HTTP %p cannot yet describe device in an ACPI table\n", - http ); - ( void ) acpi; - ( void ) len; - return 0; + err_open: + free ( data ); + err_alloc: + return rc; } -/** HTTP socket interface operations */ -static struct interface_operation http_socket_operations[] = { - INTF_OP ( xfer_window, struct http_request *, http_socket_window ), - INTF_OP ( xfer_deliver, struct http_request *, http_socket_deliver ), - INTF_OP ( xfer_window_changed, struct http_request *, http_step ), - INTF_OP ( intf_close, struct http_request *, http_socket_close ), -}; - -/** HTTP socket interface descriptor */ -static struct interface_descriptor http_socket_desc = - INTF_DESC_PASSTHRU ( struct http_request, socket, - http_socket_operations, xfer ); - -/** HTTP partial transfer interface operations */ -static struct interface_operation http_partial_operations[] = { - INTF_OP ( intf_close, struct http_request *, http_close ), -}; - -/** HTTP partial transfer interface descriptor */ -static struct interface_descriptor http_partial_desc = - INTF_DESC ( struct http_request, partial, http_partial_operations ); - -/** HTTP data transfer interface operations */ -static struct interface_operation http_xfer_operations[] = { - INTF_OP ( xfer_window, struct http_request *, http_xfer_window ), - INTF_OP ( block_read, struct http_request *, http_block_read ), - INTF_OP ( block_read_capacity, struct http_request *, - http_block_read_capacity ), - INTF_OP ( intf_close, struct http_request *, http_close ), - INTF_OP ( acpi_describe, struct http_request *, http_acpi_describe ), -}; - -/** HTTP data transfer interface descriptor */ -static struct interface_descriptor http_xfer_desc = - INTF_DESC_PASSTHRU ( struct http_request, xfer, - http_xfer_operations, socket ); - -/** HTTP process descriptor */ -static struct process_descriptor http_process_desc = - PROC_DESC_ONCE ( struct http_request, process, http_step ); - /** - * Initiate an HTTP connection, with optional filter + * Open HTTP transaction for simple URI * * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @v default_port Default port number - * @v filter Filter to apply to socket, or NULL + * @v uri Request URI * @ret rc Return status code */ -int http_open_filter ( struct interface *xfer, struct uri *uri, - unsigned int default_port, - int ( * filter ) ( struct interface *xfer, - const char *name, - struct interface **next ) ) { - struct http_request *http; - int rc; - - /* Sanity checks */ - if ( ! uri->host ) - return -EINVAL; - - /* Allocate and populate HTTP structure */ - http = zalloc ( sizeof ( *http ) ); - if ( ! http ) - return -ENOMEM; - ref_init ( &http->refcnt, http_free ); - intf_init ( &http->xfer, &http_xfer_desc, &http->refcnt ); - intf_init ( &http->partial, &http_partial_desc, &http->refcnt ); - http->uri = uri_get ( uri ); - http->default_port = default_port; - http->filter = filter; - intf_init ( &http->socket, &http_socket_desc, &http->refcnt ); - process_init ( &http->process, &http_process_desc, &http->refcnt ); - timer_init ( &http->timer, http_retry, &http->refcnt ); - http->flags = HTTP_TX_PENDING; +int http_open_uri ( struct interface *xfer, struct uri *uri ) { - /* Open socket */ - if ( ( rc = http_socket_open ( http ) ) != 0 ) - goto err; - - /* Attach to parent interface, mortalise self, and return */ - intf_plug_plug ( &http->xfer, xfer ); - ref_put ( &http->refcnt ); - return 0; - - err: - DBGC ( http, "HTTP %p could not create request: %s\n", - http, strerror ( rc ) ); - http_close ( http, rc ); - ref_put ( &http->refcnt ); - return rc; + /* Open GET/POST URI as applicable */ + if ( uri->params ) { + return http_open_post_uri ( xfer, uri ); + } else { + return http_open_get_uri ( xfer, uri ); + } } + +/* Drag in HTTP extensions */ +REQUIRING_SYMBOL ( http_open ); +REQUIRE_OBJECT ( config_http ); diff --git a/roms/ipxe/src/net/tcp/httpdigest.c b/roms/ipxe/src/net/tcp/httpdigest.c new file mode 100644 index 000000000..626dd7e9d --- /dev/null +++ b/roms/ipxe/src/net/tcp/httpdigest.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** + * @file + * + * Hyper Text Transfer Protocol (HTTP) Digest authentication + * + */ + +#include <stdio.h> +#include <errno.h> +#include <strings.h> +#include <ipxe/uri.h> +#include <ipxe/md5.h> +#include <ipxe/base16.h> +#include <ipxe/vsprintf.h> +#include <ipxe/http.h> + +/* Disambiguate the various error causes */ +#define EACCES_USERNAME __einfo_error ( EINFO_EACCES_USERNAME ) +#define EINFO_EACCES_USERNAME \ + __einfo_uniqify ( EINFO_EACCES, 0x01, \ + "No username available for Digest authentication" ) + +/** + * Initialise HTTP Digest + * + * @v ctx Digest context + * @v string Initial string + */ +static void http_digest_init ( struct md5_context *ctx ) { + + /* Initialise MD5 digest */ + digest_init ( &md5_algorithm, ctx ); +} + +/** + * Update HTTP Digest with new data + * + * @v ctx Digest context + * @v string String to append + */ +static void http_digest_update ( struct md5_context *ctx, const char *string ) { + static const char colon = ':'; + + /* Add (possibly colon-separated) field to MD5 digest */ + if ( ctx->len ) + digest_update ( &md5_algorithm, ctx, &colon, sizeof ( colon ) ); + digest_update ( &md5_algorithm, ctx, string, strlen ( string ) ); +} + +/** + * Finalise HTTP Digest + * + * @v ctx Digest context + * @v out Buffer for digest output + * @v len Buffer length + */ +static void http_digest_final ( struct md5_context *ctx, char *out, + size_t len ) { + uint8_t digest[MD5_DIGEST_SIZE]; + + /* Finalise and base16-encode MD5 digest */ + digest_final ( &md5_algorithm, ctx, digest ); + base16_encode ( digest, sizeof ( digest ), out, len ); +} + +/** + * Perform HTTP Digest authentication + * + * @v http HTTP transaction + * @ret rc Return status code + */ +static int http_digest_authenticate ( struct http_transaction *http ) { + struct http_request_auth *req = &http->request.auth; + struct http_response_auth *rsp = &http->response.auth; + char ha1[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; + char ha2[ base16_encoded_len ( MD5_DIGEST_SIZE ) + 1 /* NUL */ ]; + static const char md5sess[] = "MD5-sess"; + static const char md5[] = "MD5"; + struct md5_context ctx; + + /* Check for required response parameters */ + if ( ! rsp->realm ) { + DBGC ( http, "HTTP %p has no realm for Digest authentication\n", + http ); + return -EINVAL; + } + if ( ! rsp->nonce ) { + DBGC ( http, "HTTP %p has no nonce for Digest authentication\n", + http ); + return -EINVAL; + } + + /* Record username and password */ + if ( ! http->uri->user ) { + DBGC ( http, "HTTP %p has no username for Digest " + "authentication\n", http ); + return -EACCES_USERNAME; + } + req->username = http->uri->user; + req->password = ( http->uri->password ? http->uri->password : "" ); + + /* Handle quality of protection */ + if ( rsp->qop ) { + + /* Use "auth" in subsequent request */ + req->qop = "auth"; + + /* Generate a client nonce */ + snprintf ( req->cnonce, sizeof ( req->cnonce ), + "%08lx", random() ); + + /* Determine algorithm */ + req->algorithm = md5; + if ( rsp->algorithm && + ( strcasecmp ( rsp->algorithm, md5sess ) == 0 ) ) { + req->algorithm = md5sess; + } + } + + /* Generate HA1 */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, req->username ); + http_digest_update ( &ctx, rsp->realm ); + http_digest_update ( &ctx, req->password ); + http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); + if ( req->algorithm == md5sess ) { + http_digest_init ( &ctx ); + http_digest_update ( &ctx, ha1 ); + http_digest_update ( &ctx, rsp->nonce ); + http_digest_update ( &ctx, req->cnonce ); + http_digest_final ( &ctx, ha1, sizeof ( ha1 ) ); + } + + /* Generate HA2 */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, http->request.method->name ); + http_digest_update ( &ctx, http->request.uri ); + http_digest_final ( &ctx, ha2, sizeof ( ha2 ) ); + + /* Generate response */ + http_digest_init ( &ctx ); + http_digest_update ( &ctx, ha1 ); + http_digest_update ( &ctx, rsp->nonce ); + if ( req->qop ) { + http_digest_update ( &ctx, HTTP_DIGEST_NC ); + http_digest_update ( &ctx, req->cnonce ); + http_digest_update ( &ctx, req->qop ); + } + http_digest_update ( &ctx, ha2 ); + http_digest_final ( &ctx, req->response, sizeof ( req->response ) ); + + return 0; +} + +/** + * Construct HTTP "Authorization" header for Digest authentication + * + * @v http HTTP transaction + * @v buf Buffer + * @v len Length of buffer + * @ret len Length of header value, or negative error + */ +static int http_format_digest_auth ( struct http_transaction *http, + char *buf, size_t len ) { + struct http_request_auth *req = &http->request.auth; + struct http_response_auth *rsp = &http->response.auth; + size_t used = 0; + + /* Sanity checks */ + assert ( rsp->realm != NULL ); + assert ( rsp->nonce != NULL ); + assert ( req->username != NULL ); + if ( req->qop ) { + assert ( req->algorithm != NULL ); + assert ( req->cnonce[0] != '\0' ); + } + assert ( req->response[0] != '\0' ); + + /* Construct response */ + used += ssnprintf ( ( buf + used ), ( len - used ), + "realm=\"%s\", nonce=\"%s\", uri=\"%s\", " + "username=\"%s\"", rsp->realm, rsp->nonce, + http->request.uri, req->username ); + if ( rsp->opaque ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + ", opaque=\"%s\"", rsp->opaque ); + } + if ( req->qop ) { + used += ssnprintf ( ( buf + used ), ( len - used ), + ", qop=%s, algorithm=%s, cnonce=\"%s\", " + "nc=" HTTP_DIGEST_NC, req->qop, + req->algorithm, req->cnonce ); + } + used += ssnprintf ( ( buf + used ), ( len - used ), + ", response=\"%s\"", req->response ); + + return used; +} + +/** HTTP Digest authentication scheme */ +struct http_authentication http_digest_auth __http_authentication = { + .name = "Digest", + .authenticate = http_digest_authenticate, + .format = http_format_digest_auth, +}; + +/* Drag in HTTP authentication support */ +REQUIRING_SYMBOL ( http_digest_auth ); +REQUIRE_OBJECT ( httpauth ); diff --git a/roms/ipxe/src/net/tcp/https.c b/roms/ipxe/src/net/tcp/https.c index 6112acdae..e91000322 100644 --- a/roms/ipxe/src/net/tcp/https.c +++ b/roms/ipxe/src/net/tcp/https.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * @file @@ -26,7 +30,6 @@ FILE_LICENCE ( GPL2_OR_LATER ); * */ -#include <stddef.h> #include <ipxe/open.h> #include <ipxe/tls.h> #include <ipxe/http.h> @@ -34,19 +37,15 @@ FILE_LICENCE ( GPL2_OR_LATER ); FEATURE ( FEATURE_PROTOCOL, "HTTPS", DHCP_EB_FEATURE_HTTPS, 1 ); -/** - * Initiate an HTTPS connection - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int https_open ( struct interface *xfer, struct uri *uri ) { - return http_open_filter ( xfer, uri, HTTPS_PORT, add_tls ); -} - /** HTTPS URI opener */ struct uri_opener https_uri_opener __uri_opener = { .scheme = "https", - .open = https_open, + .open = http_open_uri, +}; + +/** HTTP URI scheme */ +struct http_scheme https_scheme __http_scheme = { + .name = "https", + .port = HTTPS_PORT, + .filter = add_tls, }; diff --git a/roms/ipxe/src/net/tcp/iscsi.c b/roms/ipxe/src/net/tcp/iscsi.c index 03c6d0f23..019a4c14e 100644 --- a/roms/ipxe/src/net/tcp/iscsi.c +++ b/roms/ipxe/src/net/tcp/iscsi.c @@ -15,14 +15,19 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <string.h> #include <stdlib.h> #include <stdio.h> +#include <ctype.h> #include <errno.h> #include <assert.h> #include <byteswap.h> @@ -127,7 +132,7 @@ FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 ); #define EPROTO_INVALID_LARGE_BINARY \ __einfo_error ( EINFO_EPROTO_INVALID_LARGE_BINARY ) #define EINFO_EPROTO_INVALID_LARGE_BINARY \ - __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary" ) + __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" ) #define EPROTO_INVALID_CHAP_RESPONSE \ __einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE ) #define EINFO_EPROTO_INVALID_CHAP_RESPONSE \ @@ -704,7 +709,7 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, char buf[ base16_encoded_len ( iscsi->chap.response_len ) + 1 ]; assert ( iscsi->initiator_username != NULL ); base16_encode ( iscsi->chap.response, iscsi->chap.response_len, - buf ); + buf, sizeof ( buf ) ); used += ssnprintf ( data + used, len - used, "CHAP_N=%s%cCHAP_R=0x%s%c", iscsi->initiator_username, 0, buf, 0 ); @@ -714,7 +719,7 @@ static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi, size_t challenge_len = ( sizeof ( iscsi->chap_challenge ) - 1 ); char buf[ base16_encoded_len ( challenge_len ) + 1 ]; base16_encode ( ( iscsi->chap_challenge + 1 ), challenge_len, - buf ); + buf, sizeof ( buf ) ); used += ssnprintf ( data + used, len - used, "CHAP_I=%d%cCHAP_C=0x%s%c", iscsi->chap_challenge[0], 0, buf, 0 ); @@ -824,38 +829,27 @@ static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) { } /** - * Calculate maximum length of decoded large binary value - * - * @v encoded Encoded large binary value - * @v max_raw_len Maximum length of raw data - */ -static inline size_t -iscsi_large_binary_decoded_max_len ( const char *encoded ) { - return ( strlen ( encoded ) ); /* Decoding never expands data */ -} - -/** * Decode large binary value * * @v encoded Encoded large binary value * @v raw Raw data + * @v len Length of data buffer * @ret len Length of raw data, or negative error */ -static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw ) { - - if ( encoded[0] != '0' ) - return -EPROTO_INVALID_LARGE_BINARY; - - switch ( encoded[1] ) { - case 'x' : - case 'X' : - return base16_decode ( ( encoded + 2 ), raw ); - case 'b' : - case 'B' : - return base64_decode ( ( encoded + 2 ), raw ); - default: - return -EPROTO_INVALID_LARGE_BINARY; +static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw, + size_t len ) { + + /* Check for initial '0x' or '0b' and decode as appropriate */ + if ( *(encoded++) == '0' ) { + switch ( tolower ( *(encoded++) ) ) { + case 'x' : + return base16_decode ( encoded, raw, len ); + case 'b' : + return base64_decode ( encoded, raw, len ); + } } + + return -EPROTO_INVALID_LARGE_BINARY; } /** @@ -982,19 +976,19 @@ static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi, */ static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi, const char *value ) { - uint8_t buf[ iscsi_large_binary_decoded_max_len ( value ) ]; + uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ unsigned int i; - size_t len; + int len; int rc; /* Process challenge */ - rc = iscsi_large_binary_decode ( value, buf ); - if ( rc < 0 ) { + len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; DBGC ( iscsi, "iSCSI %p invalid CHAP challenge \"%s\": %s\n", iscsi, value, strerror ( rc ) ); return rc; } - len = rc; chap_update ( &iscsi->chap, buf, len ); /* Build CHAP response */ @@ -1052,8 +1046,8 @@ static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi, */ static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, const char *value ) { - uint8_t buf[ iscsi_large_binary_decoded_max_len ( value ) ]; - size_t len; + uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */ + int len; int rc; /* Generate CHAP response for verification */ @@ -1073,16 +1067,16 @@ static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi, chap_respond ( &iscsi->chap ); /* Process response */ - rc = iscsi_large_binary_decode ( value, buf ); - if ( rc < 0 ) { + len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) ); + if ( len < 0 ) { + rc = len; DBGC ( iscsi, "iSCSI %p invalid CHAP response \"%s\": %s\n", iscsi, value, strerror ( rc ) ); return rc; } - len = rc; /* Check CHAP response */ - if ( len != iscsi->chap.response_len ) { + if ( len != ( int ) iscsi->chap.response_len ) { DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n", iscsi ); return -EPROTO_INVALID_CHAP_RESPONSE; @@ -1445,8 +1439,10 @@ static void iscsi_tx_done ( struct iscsi_session *iscsi ) { switch ( common->opcode & ISCSI_OPCODE_MASK ) { case ISCSI_OPCODE_DATA_OUT: iscsi_data_out_done ( iscsi ); + break; case ISCSI_OPCODE_LOGIN_REQUEST: iscsi_login_request_done ( iscsi ); + break; default: /* No action */ break; diff --git a/roms/ipxe/src/net/tcp/syslogs.c b/roms/ipxe/src/net/tcp/syslogs.c index 095afc543..0c07f86d5 100644 --- a/roms/ipxe/src/net/tcp/syslogs.c +++ b/roms/ipxe/src/net/tcp/syslogs.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/net/tcpip.c b/roms/ipxe/src/net/tcpip.c index 4bcbe64bb..5ad982fd1 100644 --- a/roms/ipxe/src/net/tcpip.c +++ b/roms/ipxe/src/net/tcpip.c @@ -17,7 +17,7 @@ * TCP/IP transport-network layer interface */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * Process a received TCP/IP packet @@ -235,7 +235,7 @@ int tcpip_bind ( struct sockaddr_tcpip *st_local, /* Otherwise, find an available port in the range [1,1023] or * [1025,65535] as appropriate. */ - min_port = ( ( ( ! flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); + min_port = ( ( ( ~flags ) & TCPIP_BIND_PRIVILEGED ) + 1 ); max_port = ( ( flags & TCPIP_BIND_PRIVILEGED ) - 1 ); offset = random(); for ( i = 0 ; i <= max_port ; i++ ) { diff --git a/roms/ipxe/src/net/tls.c b/roms/ipxe/src/net/tls.c index 30ccc932e..db01fb291 100644 --- a/roms/ipxe/src/net/tls.c +++ b/roms/ipxe/src/net/tls.c @@ -179,20 +179,29 @@ static void tls_clear_cipher ( struct tls_session *tls, ****************************************************************************** */ +/** A TLS 24-bit integer + * + * TLS uses 24-bit integers in several places, which are awkward to + * parse in C. + */ +typedef struct { + /** High byte */ + uint8_t high; + /** Low word */ + uint16_t low; +} __attribute__ (( packed )) tls24_t; + /** * Extract 24-bit field value * * @v field24 24-bit field * @ret value Field value * - * TLS uses 24-bit integers in several places, which are awkward to - * parse in C. */ static inline __attribute__ (( always_inline )) unsigned long -tls_uint24 ( const uint8_t field24[3] ) { - const uint32_t *field32 __attribute__ (( may_alias )) = - ( ( const void * ) field24 ); - return ( be32_to_cpu ( *field32 ) >> 8 ); +tls_uint24 ( const tls24_t *field24 ) { + + return ( ( field24->high << 16 ) | be16_to_cpu ( field24->low ) ); } /** @@ -200,13 +209,11 @@ tls_uint24 ( const uint8_t field24[3] ) { * * @v field24 24-bit field * @v value Field value - * - * The field must be pre-zeroed. */ -static void tls_set_uint24 ( uint8_t field24[3], unsigned long value ) { - uint32_t *field32 __attribute__ (( may_alias )) = - ( ( void * ) field24 ); - *field32 |= cpu_to_be32 ( value << 8 ); +static void tls_set_uint24 ( tls24_t *field24, unsigned long value ) { + + field24->high = ( value >> 16 ); + field24->low = cpu_to_be16 ( value ); } /** @@ -659,41 +666,8 @@ struct tls_cipher_suite tls_cipher_suite_null = { .digest = &digest_null, }; -/** Supported cipher suites, in order of preference */ -struct tls_cipher_suite tls_cipher_suites[] = { - { - .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA256 ), - .key_len = ( 256 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha256_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA256 ), - .key_len = ( 128 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha256_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_256_CBC_SHA ), - .key_len = ( 256 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha1_algorithm, - }, - { - .code = htons ( TLS_RSA_WITH_AES_128_CBC_SHA ), - .key_len = ( 128 / 8 ), - .pubkey = &rsa_algorithm, - .cipher = &aes_cbc_algorithm, - .digest = &sha1_algorithm, - }, -}; - /** Number of supported cipher suites */ -#define TLS_NUM_CIPHER_SUITES \ - ( sizeof ( tls_cipher_suites ) / sizeof ( tls_cipher_suites[0] ) ) +#define TLS_NUM_CIPHER_SUITES table_num_entries ( TLS_CIPHER_SUITES ) /** * Identify cipher suite @@ -704,11 +678,9 @@ struct tls_cipher_suite tls_cipher_suites[] = { static struct tls_cipher_suite * tls_find_cipher_suite ( unsigned int cipher_suite ) { struct tls_cipher_suite *suite; - unsigned int i; /* Identify cipher suite */ - for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ ) { - suite = &tls_cipher_suites[i]; + for_each_table_entry ( suite, TLS_CIPHER_SUITES ) { if ( suite->code == cipher_suite ) return suite; } @@ -841,26 +813,9 @@ static int tls_change_cipher ( struct tls_session *tls, ****************************************************************************** */ -/** Supported signature and hash algorithms - * - * Note that the default (TLSv1.1 and earlier) algorithm using - * MD5+SHA1 is never explicitly specified. - */ -struct tls_signature_hash_algorithm tls_signature_hash_algorithms[] = { - { - .code = { - .signature = TLS_RSA_ALGORITHM, - .hash = TLS_SHA256_ALGORITHM, - }, - .pubkey = &rsa_algorithm, - .digest = &sha256_algorithm, - }, -}; - /** Number of supported signature and hash algorithms */ -#define TLS_NUM_SIG_HASH_ALGORITHMS \ - ( sizeof ( tls_signature_hash_algorithms ) / \ - sizeof ( tls_signature_hash_algorithms[0] ) ) +#define TLS_NUM_SIG_HASH_ALGORITHMS \ + table_num_entries ( TLS_SIG_HASH_ALGORITHMS ) /** * Find TLS signature and hash algorithm @@ -873,11 +828,9 @@ static struct tls_signature_hash_algorithm * tls_signature_hash_algorithm ( struct pubkey_algorithm *pubkey, struct digest_algorithm *digest ) { struct tls_signature_hash_algorithm *sig_hash; - unsigned int i; /* Identify signature and hash algorithm */ - for ( i = 0 ; i < TLS_NUM_SIG_HASH_ALGORITHMS ; i++ ) { - sig_hash = &tls_signature_hash_algorithms[i]; + for_each_table_entry ( sig_hash, TLS_SIG_HASH_ALGORITHMS ) { if ( ( sig_hash->pubkey == pubkey ) && ( sig_hash->digest == digest ) ) { return sig_hash; @@ -994,8 +947,17 @@ static int tls_send_client_hello ( struct tls_session *tls ) { struct { uint8_t max; } __attribute__ (( packed )) max_fragment_length; + uint16_t signature_algorithms_type; + uint16_t signature_algorithms_len; + struct { + uint16_t len; + struct tls_signature_hash_id + code[TLS_NUM_SIG_HASH_ALGORITHMS]; + } __attribute__ (( packed )) signature_algorithms; } __attribute__ (( packed )) extensions; } __attribute__ (( packed )) hello; + struct tls_cipher_suite *suite; + struct tls_signature_hash_algorithm *sighash; unsigned int i; memset ( &hello, 0, sizeof ( hello ) ); @@ -1005,8 +967,8 @@ static int tls_send_client_hello ( struct tls_session *tls ) { hello.version = htons ( tls->version ); memcpy ( &hello.random, &tls->client_random, sizeof ( hello.random ) ); hello.cipher_suite_len = htons ( sizeof ( hello.cipher_suites ) ); - for ( i = 0 ; i < TLS_NUM_CIPHER_SUITES ; i++ ) - hello.cipher_suites[i] = tls_cipher_suites[i].code; + i = 0 ; for_each_table_entry ( suite, TLS_CIPHER_SUITES ) + hello.cipher_suites[i++] = suite->code; hello.compression_methods_len = sizeof ( hello.compression_methods ); hello.extensions_len = htons ( sizeof ( hello.extensions ) ); hello.extensions.server_name_type = htons ( TLS_SERVER_NAME ); @@ -1025,6 +987,14 @@ static int tls_send_client_hello ( struct tls_session *tls ) { = htons ( sizeof ( hello.extensions.max_fragment_length ) ); hello.extensions.max_fragment_length.max = TLS_MAX_FRAGMENT_LENGTH_4096; + hello.extensions.signature_algorithms_type + = htons ( TLS_SIGNATURE_ALGORITHMS ); + hello.extensions.signature_algorithms_len + = htons ( sizeof ( hello.extensions.signature_algorithms ) ); + hello.extensions.signature_algorithms.len + = htons ( sizeof ( hello.extensions.signature_algorithms.code)); + i = 0 ; for_each_table_entry ( sighash, TLS_SIG_HASH_ALGORITHMS ) + hello.extensions.signature_algorithms.code[i++] = sighash->code; return tls_send_handshake ( tls, &hello, sizeof ( hello ) ); } @@ -1038,9 +1008,9 @@ static int tls_send_client_hello ( struct tls_session *tls ) { static int tls_send_certificate ( struct tls_session *tls ) { struct { uint32_t type_length; - uint8_t length[3]; + tls24_t length; struct { - uint8_t length[3]; + tls24_t length; uint8_t data[ tls->cert->raw.len ]; } __attribute__ (( packed )) certificates[1]; } __attribute__ (( packed )) *certificate; @@ -1058,9 +1028,9 @@ static int tls_send_certificate ( struct tls_session *tls ) { ( cpu_to_le32 ( TLS_CERTIFICATE ) | htonl ( sizeof ( *certificate ) - sizeof ( certificate->type_length ) ) ); - tls_set_uint24 ( certificate->length, + tls_set_uint24 ( &certificate->length, sizeof ( certificate->certificates ) ); - tls_set_uint24 ( certificate->certificates[0].length, + tls_set_uint24 ( &certificate->certificates[0].length, sizeof ( certificate->certificates[0].data ) ); memcpy ( certificate->certificates[0].data, tls->cert->raw.data, @@ -1412,7 +1382,7 @@ static int tls_parse_chain ( struct tls_session *tls, const void *data, size_t len ) { const void *end = ( data + len ); const struct { - uint8_t length[3]; + tls24_t length; uint8_t data[0]; } __attribute__ (( packed )) *certificate; size_t certificate_len; @@ -1436,7 +1406,7 @@ static int tls_parse_chain ( struct tls_session *tls, /* Extract raw certificate data */ certificate = data; - certificate_len = tls_uint24 ( certificate->length ); + certificate_len = tls_uint24 ( &certificate->length ); next = ( certificate->data + certificate_len ); if ( next > end ) { DBGC ( tls, "TLS %p overlength certificate:\n", tls ); @@ -1482,10 +1452,10 @@ static int tls_parse_chain ( struct tls_session *tls, static int tls_new_certificate ( struct tls_session *tls, const void *data, size_t len ) { const struct { - uint8_t length[3]; + tls24_t length; uint8_t certificates[0]; } __attribute__ (( packed )) *certificate = data; - size_t certificates_len = tls_uint24 ( certificate->length ); + size_t certificates_len = tls_uint24 ( &certificate->length ); const void *end = ( certificate->certificates + certificates_len ); int rc; @@ -1634,12 +1604,12 @@ static int tls_new_handshake ( struct tls_session *tls, while ( data != end ) { const struct { uint8_t type; - uint8_t length[3]; + tls24_t length; uint8_t payload[0]; } __attribute__ (( packed )) *handshake = data; - void *payload = &handshake->payload; - size_t payload_len = tls_uint24 ( handshake->length ); - void *next = ( payload + payload_len ); + const void *payload = &handshake->payload; + size_t payload_len = tls_uint24 ( &handshake->length ); + const void *next = ( payload + payload_len ); /* Sanity check */ if ( next > end ) { @@ -2637,3 +2607,9 @@ int add_tls ( struct interface *xfer, const char *name, err_alloc: return rc; } + +/* Drag in objects via add_tls() */ +REQUIRING_SYMBOL ( add_tls ); + +/* Drag in crypto configuration */ +REQUIRE_OBJECT ( config_crypto ); diff --git a/roms/ipxe/src/net/udp.c b/roms/ipxe/src/net/udp.c index 76da67ecf..0f7dfb24a 100644 --- a/roms/ipxe/src/net/udp.c +++ b/roms/ipxe/src/net/udp.c @@ -17,7 +17,7 @@ * UDP protocol */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** * A UDP connection diff --git a/roms/ipxe/src/net/udp/dhcp.c b/roms/ipxe/src/net/udp/dhcp.c index 04fad04c2..aed5ee360 100644 --- a/roms/ipxe/src/net/udp/dhcp.c +++ b/roms/ipxe/src/net/udp/dhcp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdlib.h> @@ -44,6 +48,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/dhcppkt.h> #include <ipxe/dhcp_arch.h> #include <ipxe/features.h> +#include <config/dhcp.h> /** @file * @@ -149,30 +154,32 @@ struct dhcp_session_state { * @v dhcppkt DHCP packet * @v peer Destination address */ - int ( * tx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, + int ( * tx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer ); - /** Handle received packet + /** + * Handle received packet * * @v dhcp DHCP session * @v dhcppkt DHCP packet * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ - void ( * rx ) ( struct dhcp_session *dhcp, - struct dhcp_packet *dhcppkt, - struct sockaddr_in *peer, - uint8_t msgtype, struct in_addr server_id ); - /** Handle timer expiry + void ( * rx ) ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, + struct sockaddr_in *peer, uint8_t msgtype, + struct in_addr server_id, struct in_addr pseudo_id ); + /** + * Handle timer expiry * * @v dhcp DHCP session */ void ( * expired ) ( struct dhcp_session *dhcp ); /** Transmitted message type */ uint8_t tx_msgtype; - /** Apply minimum timeout */ - uint8_t apply_min_timeout; + /** Timeout parameters */ + uint8_t min_timeout_sec; + uint8_t max_timeout_sec; }; static struct dhcp_session_state dhcp_state_discover; @@ -272,9 +279,9 @@ static void dhcp_set_state ( struct dhcp_session *dhcp, dhcp->state = state; dhcp->start = currticks(); stop_timer ( &dhcp->timer ); - dhcp->timer.min_timeout = - ( state->apply_min_timeout ? DHCP_MIN_TIMEOUT : 0 ); - dhcp->timer.max_timeout = DHCP_MAX_TIMEOUT; + set_timer_limits ( &dhcp->timer, + ( state->min_timeout_sec * TICKS_PER_SEC ), + ( state->max_timeout_sec * TICKS_PER_SEC ) ); start_timer_nodelay ( &dhcp->timer ); } @@ -334,11 +341,13 @@ static int dhcp_discovery_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; char vci[9]; /* "PXEClient" */ int vci_len; @@ -350,8 +359,11 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify offered IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -392,10 +404,10 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, } /* Select as ProxyDHCP offer, if applicable */ - if ( server_id.s_addr && has_pxeclient && + if ( pseudo_id.s_addr && has_pxeclient && ( priority >= dhcp->proxy_priority ) ) { dhcppkt_put ( dhcp->proxy_offer ); - dhcp->proxy_server = server_id; + dhcp->proxy_server = pseudo_id; dhcp->proxy_offer = dhcppkt_get ( dhcppkt ); dhcp->proxy_priority = priority; } @@ -415,7 +427,7 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, /* If we can't yet transition to DHCPREQUEST, do nothing */ elapsed = ( currticks() - dhcp->start ); if ( ! ( dhcp->no_pxedhcp || dhcp->proxy_offer || - ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) ) + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) ) return; /* Transition to DHCPREQUEST */ @@ -430,8 +442,18 @@ static void dhcp_discovery_rx ( struct dhcp_session *dhcp, static void dhcp_discovery_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); + /* If link is blocked, defer DHCP discovery (and reset timeout) */ + if ( netdev_link_blocked ( dhcp->netdev ) ) { + DBGC ( dhcp, "DHCP %p deferring discovery\n", dhcp ); + start_timer_fixed ( &dhcp->timer, + ( DHCP_DISC_START_TIMEOUT_SEC * + TICKS_PER_SEC ) ); + return; + } + /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( dhcp->offer.s_addr && ( elapsed > PROXYDHCP_MAX_TIMEOUT ) ) { + if ( dhcp->offer.s_addr && + ( elapsed > DHCP_DISC_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) ) { dhcp_set_state ( dhcp, &dhcp_state_request ); return; } @@ -447,7 +469,8 @@ static struct dhcp_session_state dhcp_state_discover = { .rx = dhcp_discovery_rx, .expired = dhcp_discovery_expired, .tx_msgtype = DHCPDISCOVER, - .apply_min_timeout = 1, + .min_timeout_sec = DHCP_DISC_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_DISC_END_TIMEOUT_SEC, }; /** @@ -493,11 +516,13 @@ static int dhcp_request_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_request_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct in_addr ip; struct settings *parent; struct settings *settings; @@ -506,8 +531,11 @@ static void dhcp_request_rx ( struct dhcp_session *dhcp, DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify leased IP address */ ip = dhcppkt->dhcphdr->yiaddr; @@ -584,7 +612,8 @@ static struct dhcp_session_state dhcp_state_request = { .rx = dhcp_request_rx, .expired = dhcp_request_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_REQ_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_REQ_END_TIMEOUT_SEC, }; /** @@ -623,19 +652,26 @@ static int dhcp_proxy_tx ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct settings *settings = &dhcppkt->settings; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } + if ( dhcp_has_pxeopts ( dhcppkt ) ) + DBGC ( dhcp, " pxe" ); DBGC ( dhcp, "\n" ); /* Filter out unacceptable responses */ @@ -643,8 +679,9 @@ static void dhcp_proxy_rx ( struct dhcp_session *dhcp, return; if ( ( msgtype != DHCPOFFER ) && ( msgtype != DHCPACK ) ) return; - if ( server_id.s_addr /* Linux PXE server omits server ID */ && - ( server_id.s_addr != dhcp->proxy_server.s_addr ) ) + if ( ( pseudo_id.s_addr != dhcp->proxy_server.s_addr ) ) + return; + if ( ! dhcp_has_pxeopts ( dhcppkt ) ) return; /* Register settings */ @@ -669,7 +706,7 @@ static void dhcp_proxy_expired ( struct dhcp_session *dhcp ) { unsigned long elapsed = ( currticks() - dhcp->start ); /* Give up waiting for ProxyDHCP before we reach the failure point */ - if ( elapsed > PROXYDHCP_MAX_TIMEOUT ) { + if ( elapsed > DHCP_REQ_PROXY_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp_finished ( dhcp, 0 ); return; } @@ -685,7 +722,8 @@ static struct dhcp_session_state dhcp_state_proxy = { .rx = dhcp_proxy_rx, .expired = dhcp_proxy_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 0, + .min_timeout_sec = DHCP_PROXY_START_TIMEOUT_SEC, + .max_timeout_sec = DHCP_PROXY_END_TIMEOUT_SEC, }; /** @@ -753,19 +791,24 @@ static int dhcp_pxebs_accept ( struct dhcp_session *dhcp, * @v peer DHCP server address * @v msgtype DHCP message type * @v server_id DHCP server ID + * @v pseudo_id DHCP server pseudo-ID */ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, struct dhcp_packet *dhcppkt, struct sockaddr_in *peer, uint8_t msgtype, - struct in_addr server_id ) { + struct in_addr server_id, + struct in_addr pseudo_id ) { struct dhcp_pxe_boot_menu_item menu_item = { 0, 0 }; int rc; DBGC ( dhcp, "DHCP %p %s from %s:%d", dhcp, dhcp_msgtype_name ( msgtype ), inet_ntoa ( peer->sin_addr ), ntohs ( peer->sin_port ) ); - if ( server_id.s_addr != peer->sin_addr.s_addr ) - DBGC ( dhcp, " (%s)", inet_ntoa ( server_id ) ); + if ( ( server_id.s_addr != peer->sin_addr.s_addr ) || + ( pseudo_id.s_addr != peer->sin_addr.s_addr ) ) { + DBGC ( dhcp, " (%s/", inet_ntoa ( server_id ) ); + DBGC ( dhcp, "%s)", inet_ntoa ( pseudo_id ) ); + } /* Identify boot menu item */ dhcppkt_fetch ( dhcppkt, DHCP_PXE_BOOT_MENU_ITEM, @@ -782,8 +825,7 @@ static void dhcp_pxebs_rx ( struct dhcp_session *dhcp, return; if ( menu_item.type != dhcp->pxe_type ) return; - if ( ! dhcp_pxebs_accept ( dhcp, ( server_id.s_addr ? - server_id : peer->sin_addr ) ) ) + if ( ! dhcp_pxebs_accept ( dhcp, pseudo_id ) ) return; /* Register settings */ @@ -810,7 +852,7 @@ static void dhcp_pxebs_expired ( struct dhcp_session *dhcp ) { /* Give up waiting before we reach the failure point, and fail * over to the next server in the attempt list */ - if ( elapsed > PXEBS_MAX_TIMEOUT ) { + if ( elapsed > PXEBS_MAX_TIMEOUT_SEC * TICKS_PER_SEC ) { dhcp->pxe_attempt++; if ( dhcp->pxe_attempt->s_addr ) { dhcp_set_state ( dhcp, &dhcp_state_pxebs ); @@ -832,7 +874,8 @@ static struct dhcp_session_state dhcp_state_pxebs = { .rx = dhcp_pxebs_rx, .expired = dhcp_pxebs_expired, .tx_msgtype = DHCPREQUEST, - .apply_min_timeout = 1, + .min_timeout_sec = PXEBS_START_TIMEOUT_SEC, + .max_timeout_sec = PXEBS_END_TIMEOUT_SEC, }; /**************************************************************************** @@ -1114,6 +1157,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, struct dhcphdr *dhcphdr; uint8_t msgtype = 0; struct in_addr server_id = { 0 }; + struct in_addr pseudo_id; int rc = 0; /* Sanity checks */ @@ -1148,6 +1192,13 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, dhcppkt_fetch ( dhcppkt, DHCP_SERVER_IDENTIFIER, &server_id, sizeof ( server_id ) ); + /* Identify server pseudo-ID */ + pseudo_id = server_id; + if ( ! pseudo_id.s_addr ) + pseudo_id = dhcppkt->dhcphdr->siaddr; + if ( ! pseudo_id.s_addr ) + pseudo_id = peer->sin_addr; + /* Check for matching transaction ID */ if ( dhcphdr->xid != dhcp->xid ) { DBGC ( dhcp, "DHCP %p %s from %s:%d has bad transaction " @@ -1170,7 +1221,7 @@ static int dhcp_deliver ( struct dhcp_session *dhcp, } /* Handle packet based on current state */ - dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id ); + dhcp->state->rx ( dhcp, dhcppkt, peer, msgtype, server_id, pseudo_id ); err_chaddr: err_xid: diff --git a/roms/ipxe/src/net/udp/dhcpv6.c b/roms/ipxe/src/net/udp/dhcpv6.c index f7736d08e..a63543775 100644 --- a/roms/ipxe/src/net/udp/dhcpv6.c +++ b/roms/ipxe/src/net/udp/dhcpv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <stdio.h> diff --git a/roms/ipxe/src/net/udp/dns.c b/roms/ipxe/src/net/udp/dns.c index fffe6e697..2d77477f6 100644 --- a/roms/ipxe/src/net/udp/dns.c +++ b/roms/ipxe/src/net/udp/dns.c @@ -18,9 +18,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/udp/slam.c b/roms/ipxe/src/net/udp/slam.c index 3cb492d73..8b26bfb3c 100644 --- a/roms/ipxe/src/net/udp/slam.c +++ b/roms/ipxe/src/net/udp/slam.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/net/udp/syslog.c b/roms/ipxe/src/net/udp/syslog.c index d65d19ab8..b6eee6036 100644 --- a/roms/ipxe/src/net/udp/syslog.c +++ b/roms/ipxe/src/net/udp/syslog.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/net/udp/tftp.c b/roms/ipxe/src/net/udp/tftp.c index ee827ae3d..953bcb46a 100644 --- a/roms/ipxe/src/net/udp/tftp.c +++ b/roms/ipxe/src/net/udp/tftp.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> @@ -149,8 +153,6 @@ enum { TFTP_FL_RRQ_MULTICAST = 0x0004, /** Perform MTFTP recovery on timeout */ TFTP_FL_MTFTP_RECOVERY = 0x0008, - /** Only get filesize and then abort the transfer */ - TFTP_FL_SIZEONLY = 0x0010, }; /** Maximum number of MTFTP open requests before falling back to TFTP */ @@ -759,14 +761,6 @@ static int tftp_rx_oack ( struct tftp_request *tftp, void *buf, size_t len ) { goto done; } - /* Abort request if only trying to determine file size */ - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - rc = 0; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - tftp_done ( tftp, rc ); - return rc; - } - /* Request next data block */ tftp_send_packet ( tftp ); @@ -794,13 +788,6 @@ static int tftp_rx_data ( struct tftp_request *tftp, size_t data_len; int rc; - if ( tftp->flags & TFTP_FL_SIZEONLY ) { - /* If we get here then server doesn't support SIZE option */ - rc = -ENOTSUP; - tftp_send_error ( tftp, 0, "TFTP Aborted" ); - goto done; - } - /* Sanity check */ if ( iob_len ( iobuf ) < sizeof ( *data ) ) { DBGC ( tftp, "TFTP %p received underlength DATA packet " @@ -1036,10 +1023,25 @@ static size_t tftp_xfer_window ( struct tftp_request *tftp ) { return tftp->blksize; } +/** + * Terminate download + * + * @v tftp TFTP connection + * @v rc Reason for close + */ +static void tftp_close ( struct tftp_request *tftp, int rc ) { + + /* Abort download */ + tftp_send_error ( tftp, 0, "TFTP Aborted" ); + + /* Close TFTP request */ + tftp_done ( tftp, rc ); +} + /** TFTP data transfer interface operations */ static struct interface_operation tftp_xfer_operations[] = { INTF_OP ( xfer_window, struct tftp_request *, tftp_xfer_window ), - INTF_OP ( intf_close, struct tftp_request *, tftp_done ), + INTF_OP ( intf_close, struct tftp_request *, tftp_close ), }; /** TFTP data transfer interface descriptor */ @@ -1126,26 +1128,6 @@ struct uri_opener tftp_uri_opener __uri_opener = { }; /** - * Initiate TFTP-size request - * - * @v xfer Data transfer interface - * @v uri Uniform Resource Identifier - * @ret rc Return status code - */ -static int tftpsize_open ( struct interface *xfer, struct uri *uri ) { - return tftp_core_open ( xfer, uri, TFTP_PORT, NULL, - ( TFTP_FL_RRQ_SIZES | - TFTP_FL_SIZEONLY ) ); - -} - -/** TFTP URI opener */ -struct uri_opener tftpsize_uri_opener __uri_opener = { - .scheme = "tftpsize", - .open = tftpsize_open, -}; - -/** * Initiate TFTM download * * @v xfer Data transfer interface diff --git a/roms/ipxe/src/net/validator.c b/roms/ipxe/src/net/validator.c index 74d70e312..db968398a 100644 --- a/roms/ipxe/src/net/validator.c +++ b/roms/ipxe/src/net/validator.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> @@ -79,7 +83,7 @@ static void validator_free ( struct refcnt *refcnt ) { DBGC2 ( validator, "VALIDATOR %p freed\n", validator ); x509_chain_put ( validator->chain ); ocsp_put ( validator->ocsp ); - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); free ( validator ); } @@ -250,7 +254,8 @@ static int validator_start_download ( struct validator *validator, /* Generate URI string */ len = snprintf ( uri_string, uri_string_len, "%s/%08x.der?subject=", crosscert, crc ); - base64_encode ( issuer->data, issuer->len, ( uri_string + len ) ); + base64_encode ( issuer->data, issuer->len, ( uri_string + len ), + ( uri_string_len - len ) ); DBGC ( validator, "VALIDATOR %p downloading cross-signed certificate " "from %s\n", validator, uri_string ); @@ -387,7 +392,7 @@ static void validator_xfer_close ( struct validator *validator, int rc ) { goto err_append; /* Free downloaded data */ - xferbuf_done ( &validator->buffer ); + xferbuf_free ( &validator->buffer ); /* Resume validation process */ process_add ( &validator->process ); @@ -552,6 +557,7 @@ int create_validator ( struct interface *job, struct x509_chain *chain ) { process_init ( &validator->process, &validator_process_desc, &validator->refcnt ); validator->chain = x509_chain_get ( chain ); + xferbuf_malloc_init ( &validator->buffer ); /* Attach parent interface, mortalise self, and return */ intf_plug_plug ( &validator->job, job ); diff --git a/roms/ipxe/src/net/vlan.c b/roms/ipxe/src/net/vlan.c index b4ddde42d..f515c2dc9 100644 --- a/roms/ipxe/src/net/vlan.c +++ b/roms/ipxe/src/net/vlan.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <string.h> @@ -385,6 +389,10 @@ int vlan_create ( struct net_device *trunk, unsigned int tag, snprintf ( netdev->name, sizeof ( netdev->name ), "%s-%d", trunk->name, vlan->tag ); + /* Mark device as not supporting interrupts, if applicable */ + if ( ! netdev_irq_supported ( trunk ) ) + netdev->state |= NETDEV_IRQ_UNSUPPORTED; + /* Register VLAN device */ if ( ( rc = register_netdev ( netdev ) ) != 0 ) { DBGC ( netdev, "VLAN %s could not register: %s\n", diff --git a/roms/ipxe/src/tests/aes_cbc_test.c b/roms/ipxe/src/tests/aes_cbc_test.c deleted file mode 100644 index 4ae3a92e5..000000000 --- a/roms/ipxe/src/tests/aes_cbc_test.c +++ /dev/null @@ -1,193 +0,0 @@ -/* - * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. - * - * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA - * 02110-1301, USA. - */ - -FILE_LICENCE ( GPL2_OR_LATER ); - -/** @file - * - * AES-in-CBC-mode tests - * - * These test vectors are provided by NIST as part of the - * Cryptographic Toolkit Examples, downloadable from: - * - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_CBC.pdf - * - */ - -/* Forcibly enable assertions */ -#undef NDEBUG - -#include <assert.h> -#include <string.h> -#include <ipxe/aes.h> -#include <ipxe/test.h> -#include "cbc_test.h" - -/** Define inline key */ -#define KEY(...) { __VA_ARGS__ } - -/** Define inline initialisation vector */ -#define IV(...) { __VA_ARGS__ } - -/** Define inline plaintext data */ -#define PLAINTEXT(...) { __VA_ARGS__ } - -/** Define inline ciphertext data */ -#define CIPHERTEXT(...) { __VA_ARGS__ } - -/** An AES-in-CBC-mode test */ -struct aes_cbc_test { - /** Key */ - const void *key; - /** Length of key */ - size_t key_len; - /** Initialisation vector */ - const void *iv; - /** Length of initialisation vector */ - size_t iv_len; - /** Plaintext */ - const void *plaintext; - /** Length of plaintext */ - size_t plaintext_len; - /** Ciphertext */ - const void *ciphertext; - /** Length of ciphertext */ - size_t ciphertext_len; -}; - -/** - * Define an AES-in-CBC-mode test - * - * @v name Test name - * @v key_array Key - * @v iv_array Initialisation vector - * @v plaintext_array Plaintext - * @v ciphertext_array Ciphertext - * @ret test AES-in-CBC-mode test - */ -#define AES_CBC_TEST( name, key_array, iv_array, plaintext_array, \ - ciphertext_array ) \ - static const uint8_t name ## _key [] = key_array; \ - static const uint8_t name ## _iv [] = iv_array; \ - static const uint8_t name ## _plaintext [] = plaintext_array; \ - static const uint8_t name ## _ciphertext [] = ciphertext_array; \ - static struct aes_cbc_test name = { \ - .key = name ## _key, \ - .key_len = sizeof ( name ## _key ), \ - .iv = name ## _iv, \ - .iv_len = sizeof ( name ## _iv ), \ - .plaintext = name ## _plaintext, \ - .plaintext_len = sizeof ( name ## _plaintext ), \ - .ciphertext = name ## _ciphertext, \ - .ciphertext_len = sizeof ( name ## _ciphertext ), \ - } - -/** - * Report AES-in-CBC-mode - * - * @v state HMAC_DRBG internal state - * @v test Instantiation test - */ -#define aes_cbc_ok( test ) do { \ - struct cipher_algorithm *cipher = &aes_cbc_algorithm; \ - \ - assert ( (test)->iv_len == cipher->blocksize ); \ - assert ( (test)->plaintext_len == (test)->ciphertext_len ); \ - cbc_encrypt_ok ( cipher, (test)->key, (test)->key_len, \ - (test)->iv, (test)->plaintext, \ - (test)->ciphertext, (test)->plaintext_len ); \ - cbc_decrypt_ok ( cipher, (test)->key, (test)->key_len, \ - (test)->iv, (test)->ciphertext, \ - (test)->plaintext, (test)->ciphertext_len ); \ - } while ( 0 ) - -/** CBC_AES128 */ -AES_CBC_TEST ( test_128, - KEY ( 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7, 0x15, - 0x88, 0x09, 0xcf, 0x4f, 0x3c ), - IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ), - PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ), - CIPHERTEXT ( 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, - 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, - 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, - 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, - 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, - 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, - 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, - 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ) ); - -/** CBC_AES256 */ -AES_CBC_TEST ( test_256, - KEY ( 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73, 0xae, - 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, - 0x08, 0xd7, 0x2d, 0x98, 0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4 ), - IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, - 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ), - PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, - 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, - 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, - 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, - 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, - 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, - 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, - 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ), - CIPHERTEXT ( 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, - 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, - 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, - 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, - 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, - 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, - 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, - 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ) ); - -/** - * Perform AES-in-CBC-mode self-test - * - */ -static void aes_cbc_test_exec ( void ) { - struct cipher_algorithm *cipher = &aes_cbc_algorithm; - - /* Correctness tests */ - aes_cbc_ok ( &test_128 ); - aes_cbc_ok ( &test_256 ); - - /* Speed tests */ - DBG ( "AES128 encryption required %ld cycles per byte\n", - cbc_cost_encrypt ( cipher, test_128.key_len ) ); - DBG ( "AES128 decryption required %ld cycles per byte\n", - cbc_cost_decrypt ( cipher, test_128.key_len ) ); - DBG ( "AES256 encryption required %ld cycles per byte\n", - cbc_cost_encrypt ( cipher, test_256.key_len ) ); - DBG ( "AES256 decryption required %ld cycles per byte\n", - cbc_cost_decrypt ( cipher, test_256.key_len ) ); -} - -/** AES-in-CBC-mode self-test */ -struct self_test aes_cbc_test __self_test = { - .name = "aes_cbc", - .exec = aes_cbc_test_exec, -}; diff --git a/roms/ipxe/src/tests/aes_test.c b/roms/ipxe/src/tests/aes_test.c new file mode 100644 index 000000000..ad66c734c --- /dev/null +++ b/roms/ipxe/src/tests/aes_test.c @@ -0,0 +1,193 @@ +/* + * Copyright (C) 2012 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * AES tests + * + * These test vectors are provided by NIST as part of the + * Cryptographic Toolkit Examples, downloadable from: + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_Core_All.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_ECB.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/AES_CBC.pdf + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <assert.h> +#include <string.h> +#include <ipxe/aes.h> +#include <ipxe/test.h> +#include "cipher_test.h" + +/** Key used for NIST 128-bit test vectors */ +#define AES_KEY_NIST_128 \ + KEY ( 0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, \ + 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c ) + +/** Key used for NIST 192-bit test vectors */ +#define AES_KEY_NIST_192 \ + KEY ( 0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, \ + 0x10, 0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5, 0x62, 0xf8, \ + 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b ) + +/** Key used for NIST 256-bit test vectors */ +#define AES_KEY_NIST_256 \ + KEY ( 0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, \ + 0x73, 0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81, 0x1f, 0x35, \ + 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98, 0x10, \ + 0xa3, 0x09, 0x14, 0xdf, 0xf4 ) + +/** Dummy initialisation vector used for NIST ECB-mode test vectors */ +#define AES_IV_NIST_DUMMY \ + IV ( 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 ) + +/** Initialisation vector used for NIST CBC-mode test vectors */ +#define AES_IV_NIST_CBC \ + IV ( 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, \ + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f ) + +/** Plaintext used for NIST test vectors */ +#define AES_PLAINTEXT_NIST \ + PLAINTEXT ( 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, \ + 0xe9, 0x3d, 0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a, \ + 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, \ + 0x9e, 0xb7, 0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51, \ + 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, \ + 0xe5, 0xfb, 0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef, \ + 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, \ + 0xad, 0x2b, 0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10 ) + +/** AES-128-ECB (same test as AES-128-Core) */ +CIPHER_TEST ( aes_128_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_128, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, + 0xa8, 0x9e, 0xca, 0xf3, 0x24, 0x66, 0xef, 0x97, + 0xf5, 0xd3, 0xd5, 0x85, 0x03, 0xb9, 0x69, 0x9d, + 0xe7, 0x85, 0x89, 0x5a, 0x96, 0xfd, 0xba, 0xaf, + 0x43, 0xb1, 0xcd, 0x7f, 0x59, 0x8e, 0xce, 0x23, + 0x88, 0x1b, 0x00, 0xe3, 0xed, 0x03, 0x06, 0x88, + 0x7b, 0x0c, 0x78, 0x5e, 0x27, 0xe8, 0xad, 0x3f, + 0x82, 0x23, 0x20, 0x71, 0x04, 0x72, 0x5d, 0xd4 ) ); + +/** AES-128-CBC */ +CIPHER_TEST ( aes_128_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_128, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, + 0xce, 0xe9, 0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d, + 0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, + 0x95, 0xdb, 0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2, + 0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, + 0x71, 0x16, 0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16, + 0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, + 0x12, 0x0e, 0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7 ) ); + +/** AES-192-ECB (same test as AES-192-Core) */ +CIPHER_TEST ( aes_192_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_192, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, + 0xf7, 0x12, 0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc, + 0x97, 0x41, 0x04, 0x84, 0x6d, 0x0a, 0xd3, 0xad, + 0x77, 0x34, 0xec, 0xb3, 0xec, 0xee, 0x4e, 0xef, + 0xef, 0x7a, 0xfd, 0x22, 0x70, 0xe2, 0xe6, 0x0a, + 0xdc, 0xe0, 0xba, 0x2f, 0xac, 0xe6, 0x44, 0x4e, + 0x9a, 0x4b, 0x41, 0xba, 0x73, 0x8d, 0x6c, 0x72, + 0xfb, 0x16, 0x69, 0x16, 0x03, 0xc1, 0x8e, 0x0e ) ); + +/** AES-192-CBC */ +CIPHER_TEST ( aes_192_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_192, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, + 0x71, 0x78, 0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8, + 0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, + 0xe5, 0xe7, 0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a, + 0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, + 0x7f, 0xa9, 0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0, + 0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, + 0xd9, 0x20, 0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd ) ); + +/** AES-256-ECB (same test as AES-256-Core) */ +CIPHER_TEST ( aes_256_ecb, &aes_ecb_algorithm, + AES_KEY_NIST_256, AES_IV_NIST_DUMMY, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, + 0x06, 0x4b, 0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8, + 0x59, 0x1c, 0xcb, 0x10, 0xd4, 0x10, 0xed, 0x26, + 0xdc, 0x5b, 0xa7, 0x4a, 0x31, 0x36, 0x28, 0x70, + 0xb6, 0xed, 0x21, 0xb9, 0x9c, 0xa6, 0xf4, 0xf9, + 0xf1, 0x53, 0xe7, 0xb1, 0xbe, 0xaf, 0xed, 0x1d, + 0x23, 0x30, 0x4b, 0x7a, 0x39, 0xf9, 0xf3, 0xff, + 0x06, 0x7d, 0x8d, 0x8f, 0x9e, 0x24, 0xec, 0xc7 ) ); + +/** AES-256-CBC */ +CIPHER_TEST ( aes_256_cbc, &aes_cbc_algorithm, + AES_KEY_NIST_256, AES_IV_NIST_CBC, AES_PLAINTEXT_NIST, + CIPHERTEXT ( 0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, + 0x77, 0x9e, 0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6, + 0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, + 0x67, 0x9f, 0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d, + 0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, + 0xa5, 0x30, 0xe2, 0x63, 0x04, 0x23, 0x14, 0x61, + 0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, + 0xda, 0x6c, 0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b ) ); + +/** + * Perform AES self-test + * + */ +static void aes_test_exec ( void ) { + struct cipher_algorithm *ecb = &aes_ecb_algorithm; + struct cipher_algorithm *cbc = &aes_cbc_algorithm; + unsigned int keylen; + + /* Correctness tests */ + cipher_ok ( &aes_128_ecb ); + cipher_ok ( &aes_128_cbc ); + cipher_ok ( &aes_192_ecb ); + cipher_ok ( &aes_192_cbc ); + cipher_ok ( &aes_256_ecb ); + cipher_ok ( &aes_256_cbc ); + + /* Speed tests */ + for ( keylen = 128 ; keylen <= 256 ; keylen += 64 ) { + DBG ( "AES-%d-ECB encryption required %ld cycles per byte\n", + keylen, cipher_cost_encrypt ( ecb, ( keylen / 8 ) ) ); + DBG ( "AES-%d-ECB decryption required %ld cycles per byte\n", + keylen, cipher_cost_decrypt ( ecb, ( keylen / 8 ) ) ); + DBG ( "AES-%d-CBC encryption required %ld cycles per byte\n", + keylen, cipher_cost_encrypt ( cbc, ( keylen / 8 ) ) ); + DBG ( "AES-%d-CBC decryption required %ld cycles per byte\n", + keylen, cipher_cost_decrypt ( cbc, ( keylen / 8 ) ) ); + } +} + +/** AES self-test */ +struct self_test aes_test __self_test = { + .name = "aes", + .exec = aes_test_exec, +}; diff --git a/roms/ipxe/src/tests/base16_test.c b/roms/ipxe/src/tests/base16_test.c index 9b047b74c..46884aef7 100644 --- a/roms/ipxe/src/tests/base16_test.c +++ b/roms/ipxe/src/tests/base16_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -73,30 +77,42 @@ BASE16 ( random_test, * Report a base16 encoding test result * * @v test Base16 test + * @v file Test code file + * @v line Test code line */ -#define base16_encode_ok( test ) do { \ - size_t len = base16_encoded_len ( (test)->len ); \ - char buf[ len + 1 /* NUL */ ]; \ - ok ( len == strlen ( (test)->encoded ) ); \ - base16_encode ( (test)->data, (test)->len, buf ); \ - ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ - } while ( 0 ) +static void base16_encode_okx ( struct base16_test *test, const char *file, + unsigned int line ) { + size_t len = base16_encoded_len ( test->len ); + char buf[ len + 1 /* NUL */ ]; + size_t check_len; + + okx ( len == strlen ( test->encoded ), file, line ); + check_len = base16_encode ( test->data, test->len, buf, sizeof ( buf )); + okx ( check_len == len, file, line ); + okx ( strcmp ( test->encoded, buf ) == 0, file, line ); +} +#define base16_encode_ok( test ) base16_encode_okx ( test, __FILE__, __LINE__ ) /** * Report a base16 decoding test result * * @v test Base16 test + * @v file Test code file + * @v line Test code line */ -#define base16_decode_ok( test ) do { \ - size_t max_len = base16_decoded_max_len ( (test)->encoded ); \ - uint8_t buf[max_len]; \ - int len; \ - len = base16_decode ( (test)->encoded, buf ); \ - ok ( len >= 0 ); \ - ok ( ( size_t ) len <= max_len ); \ - ok ( ( size_t ) len == (test)->len ); \ - ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ - } while ( 0 ) +static void base16_decode_okx ( struct base16_test *test, const char *file, + unsigned int line ) { + size_t max_len = base16_decoded_max_len ( test->encoded ); + uint8_t buf[max_len]; + int len; + + len = base16_decode ( test->encoded, buf, sizeof ( buf ) ); + okx ( len >= 0, file, line ); + okx ( ( size_t ) len <= max_len, file, line ); + okx ( ( size_t ) len == test->len, file, line ); + okx ( memcmp ( test->data, buf, len ) == 0, file, line ); +} +#define base16_decode_ok( test ) base16_decode_okx ( test, __FILE__, __LINE__ ) /** * Perform Base16 self-tests diff --git a/roms/ipxe/src/tests/base64_test.c b/roms/ipxe/src/tests/base64_test.c index c088298ca..0fc595d90 100644 --- a/roms/ipxe/src/tests/base64_test.c +++ b/roms/ipxe/src/tests/base64_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -76,30 +80,42 @@ BASE64 ( random_test, * Report a base64 encoding test result * * @v test Base64 test + * @v file Test code file + * @v line Test code line */ -#define base64_encode_ok( test ) do { \ - size_t len = base64_encoded_len ( (test)->len ); \ - char buf[ len + 1 /* NUL */ ]; \ - ok ( len == strlen ( (test)->encoded ) ); \ - base64_encode ( (test)->data, (test)->len, buf ); \ - ok ( strcmp ( (test)->encoded, buf ) == 0 ); \ - } while ( 0 ) +static void base64_encode_okx ( struct base64_test *test, const char *file, + unsigned int line ) { + size_t len = base64_encoded_len ( test->len ); + char buf[ len + 1 /* NUL */ ]; + size_t check_len; + + okx ( len == strlen ( test->encoded ), file, line ); + check_len = base64_encode ( test->data, test->len, buf, sizeof ( buf )); + okx ( check_len == len, file, line ); + okx ( strcmp ( test->encoded, buf ) == 0, file, line ); +} +#define base64_encode_ok( test ) base64_encode_okx ( test, __FILE__, __LINE__ ) /** * Report a base64 decoding test result * * @v test Base64 test + * @v file Test code file + * @v line Test code line */ -#define base64_decode_ok( test ) do { \ - size_t max_len = base64_decoded_max_len ( (test)->encoded ); \ - uint8_t buf[max_len]; \ - int len; \ - len = base64_decode ( (test)->encoded, buf ); \ - ok ( len >= 0 ); \ - ok ( ( size_t ) len <= max_len ); \ - ok ( ( size_t ) len == (test)->len ); \ - ok ( memcmp ( (test)->data, buf, len ) == 0 ); \ - } while ( 0 ) +static void base64_decode_okx ( struct base64_test *test, const char *file, + unsigned int line ) { + size_t max_len = base64_decoded_max_len ( test->encoded ); + uint8_t buf[max_len]; + int len; + + len = base64_decode ( test->encoded, buf, sizeof ( buf ) ); + okx ( len >= 0, file, line ); + okx ( ( size_t ) len <= max_len, file, line ); + okx ( ( size_t ) len == test->len, file, line ); + okx ( memcmp ( test->data, buf, len ) == 0, file, line ); +} +#define base64_decode_ok( test ) base64_decode_okx ( test, __FILE__, __LINE__ ) /** * Perform Base64 self-tests diff --git a/roms/ipxe/src/tests/bigint_test.c b/roms/ipxe/src/tests/bigint_test.c index 75a80622f..8d40c3188 100644 --- a/roms/ipxe/src/tests/bigint_test.c +++ b/roms/ipxe/src/tests/bigint_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/bofm_test.c b/roms/ipxe/src/tests/bofm_test.c index e430d12d4..829924887 100644 --- a/roms/ipxe/src/tests/bofm_test.c +++ b/roms/ipxe/src/tests/bofm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/tests/byteswap_test.c b/roms/ipxe/src/tests/byteswap_test.c index a500218be..92bdb1d59 100644 --- a/roms/ipxe/src/tests/byteswap_test.c +++ b/roms/ipxe/src/tests/byteswap_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/cbc_test.h b/roms/ipxe/src/tests/cbc_test.h deleted file mode 100644 index ad9e6f341..000000000 --- a/roms/ipxe/src/tests/cbc_test.h +++ /dev/null @@ -1,57 +0,0 @@ -#ifndef _CBC_TEST_H -#define _CBC_TEST_H - -FILE_LICENCE ( GPL2_OR_LATER ); - -#include <stdint.h> -#include <ipxe/crypto.h> -#include <ipxe/test.h> - -extern int cbc_test_encrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, - const void *plaintext, - const void *expected_ciphertext, size_t len ); -extern int cbc_test_decrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, - const void *ciphertext, - const void *expected_plaintext, size_t len ); -extern unsigned long cbc_cost_encrypt ( struct cipher_algorithm *cipher, - size_t key_len ); -extern unsigned long cbc_cost_decrypt ( struct cipher_algorithm *cipher, - size_t key_len ); - -/** - * Report CBC encryption test result - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v plaintext Plaintext data - * @v expected_ciphertext Expected ciphertext data - * @v len Length of data - */ -#define cbc_encrypt_ok( cipher, key, key_len, iv, plaintext, \ - expected_ciphertext, len ) do { \ - ok ( cbc_test_encrypt ( cipher, key, key_len, iv, plaintext, \ - expected_ciphertext, len ) ); \ - } while ( 0 ) - -/** - * Report CBC decryption test result - * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v ciphertext Ciphertext data - * @v expected_plaintext Expected plaintext data - * @v len Length of data - */ -#define cbc_decrypt_ok( cipher, key, key_len, iv, ciphertext, \ - expected_plaintext, len ) do { \ - ok ( cbc_test_decrypt ( cipher, key, key_len, iv, ciphertext, \ - expected_plaintext, len ) ); \ - } while ( 0 ) - -#endif /* _CBC_TEST_H */ diff --git a/roms/ipxe/src/tests/cbc_test.c b/roms/ipxe/src/tests/cipher_test.c index cb0f7bdea..800d6c138 100644 --- a/roms/ipxe/src/tests/cbc_test.c +++ b/roms/ipxe/src/tests/cipher_test.c @@ -15,13 +15,17 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * - * CBC self-tests + * Cipher self-tests * */ @@ -34,86 +38,90 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <assert.h> #include <ipxe/crypto.h> #include <ipxe/profile.h> -#include "cbc_test.h" +#include <ipxe/test.h> +#include "cipher_test.h" /** Number of sample iterations for profiling */ #define PROFILE_COUNT 16 /** - * Test CBC encryption + * Report a cipher encryption test result * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v plaintext Plaintext data - * @v expected_ciphertext Expected ciphertext data - * @v len Length of data - * @ret ok Ciphertext is as expected + * @v test Cipher test + * @v file Test code file + * @v line Test code line */ -int cbc_test_encrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, const void *plaintext, - const void *expected_ciphertext, size_t len ) { +void cipher_encrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { + struct cipher_algorithm *cipher = test->cipher; + size_t len = test->len; uint8_t ctx[cipher->ctxsize]; uint8_t ciphertext[len]; - int rc; /* Initialise cipher */ - rc = cipher_setkey ( cipher, ctx, key, key_len ); - assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); + okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, + file, line ); + cipher_setiv ( cipher, ctx, test->iv ); /* Perform encryption */ - cipher_encrypt ( cipher, ctx, plaintext, ciphertext, len ); + cipher_encrypt ( cipher, ctx, test->plaintext, ciphertext, len ); - /* Verify result */ - return ( memcmp ( ciphertext, expected_ciphertext, len ) == 0 ); + /* Compare against expected ciphertext */ + okx ( memcmp ( ciphertext, test->ciphertext, len ) == 0, file, line ); } /** - * Test CBC decryption + * Report a cipher decryption test result * - * @v cipher Cipher algorithm - * @v key Key - * @v key_len Length of key - * @v iv Initialisation vector - * @v ciphertext Ciphertext data - * @v expected_plaintext Expected plaintext data - * @v len Length of data - * @ret ok Plaintext is as expected + * @v test Cipher test + * @v file Test code file + * @v line Test code line */ -int cbc_test_decrypt ( struct cipher_algorithm *cipher, const void *key, - size_t key_len, const void *iv, const void *ciphertext, - const void *expected_plaintext, size_t len ) { +void cipher_decrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { + struct cipher_algorithm *cipher = test->cipher; + size_t len = test->len; uint8_t ctx[cipher->ctxsize]; uint8_t plaintext[len]; - int rc; /* Initialise cipher */ - rc = cipher_setkey ( cipher, ctx, key, key_len ); - assert ( rc == 0 ); - cipher_setiv ( cipher, ctx, iv ); + okx ( cipher_setkey ( cipher, ctx, test->key, test->key_len ) == 0, + file, line ); + cipher_setiv ( cipher, ctx, test->iv ); /* Perform encryption */ - cipher_decrypt ( cipher, ctx, ciphertext, plaintext, len ); + cipher_decrypt ( cipher, ctx, test->ciphertext, plaintext, len ); + + /* Compare against expected plaintext */ + okx ( memcmp ( plaintext, test->plaintext, len ) == 0, file, line ); +} + +/** + * Report a cipher encryption and decryption test result + * + * @v test Cipher test + * @v file Test code file + * @v line Test code line + */ +void cipher_okx ( struct cipher_test *test, const char *file, + unsigned int line ) { - /* Verify result */ - return ( memcmp ( plaintext, expected_plaintext, len ) == 0 ); + cipher_encrypt_okx ( test, file, line ); + cipher_decrypt_okx ( test, file, line ); } /** - * Calculate CBC encryption or decryption cost + * Calculate cipher encryption or decryption cost * * @v cipher Cipher algorithm * @v key_len Length of key * @v op Encryption or decryption operation * @ret cost Cost (in cycles per byte) */ -static unsigned long cbc_cost ( struct cipher_algorithm *cipher, - size_t key_len, - void ( * op ) ( struct cipher_algorithm *cipher, - void *ctx, const void *src, - void *dst, size_t len ) ) { +static unsigned long +cipher_cost ( struct cipher_algorithm *cipher, size_t key_len, + void ( * op ) ( struct cipher_algorithm *cipher, void *ctx, + const void *src, void *dst, size_t len ) ) { static uint8_t random[8192]; /* Too large for stack */ uint8_t key[key_len]; uint8_t iv[cipher->blocksize]; @@ -153,25 +161,25 @@ static unsigned long cbc_cost ( struct cipher_algorithm *cipher, } /** - * Calculate CBC encryption cost + * Calculate cipher encryption cost * * @v cipher Cipher algorithm * @v key_len Length of key * @ret cost Cost (in cycles per byte) */ -unsigned long cbc_cost_encrypt ( struct cipher_algorithm *cipher, - size_t key_len ) { - return cbc_cost ( cipher, key_len, cipher_encrypt ); +unsigned long cipher_cost_encrypt ( struct cipher_algorithm *cipher, + size_t key_len ) { + return cipher_cost ( cipher, key_len, cipher_encrypt ); } /** - * Calculate CBC decryption cost + * Calculate cipher decryption cost * * @v cipher Cipher algorithm * @v key_len Length of key * @ret cost Cost (in cycles per byte) */ -unsigned long cbc_cost_decrypt ( struct cipher_algorithm *cipher, - size_t key_len ) { - return cbc_cost ( cipher, key_len, cipher_decrypt ); +unsigned long cipher_cost_decrypt ( struct cipher_algorithm *cipher, + size_t key_len ) { + return cipher_cost ( cipher, key_len, cipher_decrypt ); } diff --git a/roms/ipxe/src/tests/cipher_test.h b/roms/ipxe/src/tests/cipher_test.h new file mode 100644 index 000000000..d7c5aef8f --- /dev/null +++ b/roms/ipxe/src/tests/cipher_test.h @@ -0,0 +1,111 @@ +#ifndef _CIPHER_TEST_H +#define _CIPHER_TEST_H + +/** @file + * + * Cipher self-tests + * + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +#include <stdint.h> +#include <ipxe/crypto.h> +#include <ipxe/test.h> + +/** A cipher test */ +struct cipher_test { + /** Cipher algorithm */ + struct cipher_algorithm *cipher; + /** Key */ + const void *key; + /** Length of key */ + size_t key_len; + /** Initialisation vector */ + const void *iv; + /** Length of initialisation vector */ + size_t iv_len; + /** Plaintext */ + const void *plaintext; + /** Ciphertext */ + const void *ciphertext; + /** Length of text */ + size_t len; +}; + +/** Define inline key */ +#define KEY(...) { __VA_ARGS__ } + +/** Define inline initialisation vector */ +#define IV(...) { __VA_ARGS__ } + +/** Define inline plaintext data */ +#define PLAINTEXT(...) { __VA_ARGS__ } + +/** Define inline ciphertext data */ +#define CIPHERTEXT(...) { __VA_ARGS__ } + +/** + * Define a cipher test + * + * @v name Test name + * @v CIPHER Cipher algorithm + * @v KEY Key + * @v IV Initialisation vector + * @v PLAINTEXT Plaintext + * @v CIPHERTEXT Ciphertext + * @ret test Cipher test + */ +#define CIPHER_TEST( name, CIPHER, KEY, IV, PLAINTEXT, CIPHERTEXT ) \ + static const uint8_t name ## _key [] = KEY; \ + static const uint8_t name ## _iv [] = IV; \ + static const uint8_t name ## _plaintext [] = PLAINTEXT; \ + static const uint8_t name ## _ciphertext \ + [ sizeof ( name ## _plaintext ) ] = CIPHERTEXT; \ + static struct cipher_test name = { \ + .cipher = CIPHER, \ + .key = name ## _key, \ + .key_len = sizeof ( name ## _key ), \ + .iv = name ## _iv, \ + .iv_len = sizeof ( name ## _iv ), \ + .plaintext = name ## _plaintext, \ + .ciphertext = name ## _ciphertext, \ + .len = sizeof ( name ## _plaintext ), \ + } + +extern void cipher_encrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern void cipher_decrypt_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern void cipher_okx ( struct cipher_test *test, const char *file, + unsigned int line ); +extern unsigned long cipher_cost_encrypt ( struct cipher_algorithm *cipher, + size_t key_len ); +extern unsigned long cipher_cost_decrypt ( struct cipher_algorithm *cipher, + size_t key_len ); + +/** + * Report a cipher encryption test result + * + * @v test Cipher test + */ +#define cipher_encrypt_ok( test ) \ + cipher_encrypt_okx ( test, __FILE__, __LINE__ ) + +/** + * Report a cipher decryption test result + * + * @v test Cipher test + */ +#define cipher_decrypt_ok( test ) \ + cipher_decrypt_okx ( test, __FILE__, __LINE__ ) + +/** + * Report a cipher encryption and decryption test result + * + * @v test Cipher test + */ +#define cipher_ok( test ) \ + cipher_okx ( test, __FILE__, __LINE__ ) + +#endif /* _CIPHER_TEST_H */ diff --git a/roms/ipxe/src/tests/cms_test.c b/roms/ipxe/src/tests/cms_test.c index 8767504c0..b805a9974 100644 --- a/roms/ipxe/src/tests/cms_test.c +++ b/roms/ipxe/src/tests/cms_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1470,6 +1474,7 @@ struct self_test cms_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( cms_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( md5 ); REQUIRE_OBJECT ( sha1 ); diff --git a/roms/ipxe/src/tests/crc32_test.c b/roms/ipxe/src/tests/crc32_test.c index 873f633a5..46cde0f7b 100644 --- a/roms/ipxe/src/tests/crc32_test.c +++ b/roms/ipxe/src/tests/crc32_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/deflate_test.c b/roms/ipxe/src/tests/deflate_test.c index 68c1aad96..20ff5b9a2 100644 --- a/roms/ipxe/src/tests/deflate_test.c +++ b/roms/ipxe/src/tests/deflate_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/digest_test.c b/roms/ipxe/src/tests/digest_test.c index 4df26c099..c3a128853 100644 --- a/roms/ipxe/src/tests/digest_test.c +++ b/roms/ipxe/src/tests/digest_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -34,27 +38,47 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/profile.h> #include "digest_test.h" +/** Maximum number of digest test fragments */ +#define NUM_DIGEST_TEST_FRAG 8 + +/** A digest test fragment list */ +struct digest_test_fragments { + /** Fragment lengths */ + size_t len[NUM_DIGEST_TEST_FRAG]; +}; + +/** Digest test fragment lists */ +static struct digest_test_fragments digest_test_fragments[] = { + { { 0, -1UL, } }, + { { 1, 1, 1, 1, 1, 1, 1, 1 } }, + { { 2, 0, 23, 4, 6, 1, 0 } }, +}; + /** Number of sample iterations for profiling */ #define PROFILE_COUNT 16 /** - * Test digest algorithm + * Report a digest fragmented test result * - * @v digest Digest algorithm - * @v fragments Digest test fragment list, or NULL - * @v data Test data - * @v len Length of test data - * @v expected Expected digest value - * @ret ok Digest value is as expected + * @v test Digest test + * @v fragments Fragment list + * @v file Test code file + * @v line Test code line */ -int digest_test ( struct digest_algorithm *digest, - struct digest_test_fragments *fragments, - void *data, size_t len, void *expected ) { +void digest_frag_okx ( struct digest_test *test, + struct digest_test_fragments *fragments, + const char *file, unsigned int line ) { + struct digest_algorithm *digest = test->digest; uint8_t ctx[digest->ctxsize]; uint8_t out[digest->digestsize]; + const void *data = test->data; + size_t len = test->len; size_t frag_len = 0; unsigned int i; + /* Sanity check */ + okx ( test->expected_len == sizeof ( out ), file, line ); + /* Initialise digest */ digest_init ( digest, ctx ); @@ -74,7 +98,28 @@ int digest_test ( struct digest_algorithm *digest, digest_final ( digest, ctx, out ); /* Compare against expected output */ - return ( memcmp ( expected, out, sizeof ( out ) ) == 0 ); + okx ( memcmp ( test->expected, out, sizeof ( out ) ) == 0, file, line ); +} + +/** + * Report a digest test result + * + * @v test Digest test + * @v file Test code file + * @v line Test code line + */ +void digest_okx ( struct digest_test *test, const char *file, + unsigned int line ) { + unsigned int i; + + /* Test with a single pass */ + digest_frag_okx ( test, NULL, file, line ); + + /* Test with fragment lists */ + for ( i = 0 ; i < ( sizeof ( digest_test_fragments ) / + sizeof ( digest_test_fragments[0] ) ) ; i++ ) { + digest_frag_okx ( test, &digest_test_fragments[i], file, line ); + } } /** diff --git a/roms/ipxe/src/tests/digest_test.h b/roms/ipxe/src/tests/digest_test.h index 49e06d1cb..abf1b834f 100644 --- a/roms/ipxe/src/tests/digest_test.h +++ b/roms/ipxe/src/tests/digest_test.h @@ -1,37 +1,115 @@ #ifndef _DIGEST_TEST_H #define _DIGEST_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> #include <ipxe/test.h> -/** Maximum number of digest test fragments */ -#define NUM_DIGEST_TEST_FRAG 8 - -/** A digest test fragment list */ -struct digest_test_fragments { - /** Fragment lengths */ - size_t len[NUM_DIGEST_TEST_FRAG]; +/** A digest test */ +struct digest_test { + /** Digest algorithm */ + struct digest_algorithm *digest; + /** Test data */ + const void *data; + /** Length of test data */ + size_t len; + /** Expected digest value */ + const void *expected; + /** Expected digest length */ + size_t expected_len; }; -extern int digest_test ( struct digest_algorithm *digest, - struct digest_test_fragments *fragments, - void *data, size_t len, void *expected ); -extern unsigned long digest_cost ( struct digest_algorithm *digest ); +/** Define inline test data */ +#define DATA(...) { __VA_ARGS__ } + +/** Define inline expected digest value */ +#define DIGEST(...) { __VA_ARGS__ } + +/** + * Define a digest test + * + * @v name Test name + * @v DIGEST Digest algorithm + * @v DATA Test data + * @v EXPECTED Expected digest value + * @ret test Digest test + */ +#define DIGEST_TEST( name, DIGEST, DATA, EXPECTED ) \ + static const uint8_t name ## _data[] = DATA; \ + static const uint8_t name ## _expected[] = EXPECTED; \ + static struct digest_test name = { \ + .digest = DIGEST, \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .expected = name ## _expected, \ + .expected_len = sizeof ( name ## _expected ), \ + }; + +/** Standard test vector: empty data */ +#define DIGEST_EMPTY DATA() + +/** Standard test vector: NIST string "abc" + * + * The NIST Cryptographic Toolkit examples for all digest algorithms + * include a test vector which is the unterminated string + * + * "abc" + */ +#define DIGEST_NIST_ABC \ + DATA ( 0x61, 0x62, 0x63 ) + +/** Standard test vector: NIST string "abc...opq" + * + * The NIST Cryptographic Toolkit examples for all 32-bit digest + * algorithms (SHA-1 and the SHA-256 family) include a test vector + * which is the unterminated string + * + * "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq" + */ +#define DIGEST_NIST_ABC_OPQ \ + DATA ( 0x61, 0x62, 0x63, 0x64, 0x62, 0x63, 0x64, 0x65, 0x63, \ + 0x64, 0x65, 0x66, 0x64, 0x65, 0x66, 0x67, 0x65, 0x66, \ + 0x67, 0x68, 0x66, 0x67, 0x68, 0x69, 0x67, 0x68, 0x69, \ + 0x6a, 0x68, 0x69, 0x6a, 0x6b, 0x69, 0x6a, 0x6b, 0x6c, \ + 0x6a, 0x6b, 0x6c, 0x6d, 0x6b, 0x6c, 0x6d, 0x6e, 0x6c, \ + 0x6d, 0x6e, 0x6f, 0x6d, 0x6e, 0x6f, 0x70, 0x6e, 0x6f, \ + 0x70, 0x71 ) + +/** Standard test vector: NIST string "abc...stu" + * + * The NIST Cryptographic Toolkit examples for all 64-bit digest + * algorithms (SHA-512 family) include a test vector which is the + * unterminated string + * + * "abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmn" + * "hijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu" + */ +#define DIGEST_NIST_ABC_STU \ + DATA ( 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x62, \ + 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x63, 0x64, \ + 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a, 0x64, 0x65, 0x66, \ + 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x65, 0x66, 0x67, 0x68, \ + 0x69, 0x6a, 0x6b, 0x6c, 0x66, 0x67, 0x68, 0x69, 0x6a, \ + 0x6b, 0x6c, 0x6d, 0x67, 0x68, 0x69, 0x6a, 0x6b, 0x6c, \ + 0x6d, 0x6e, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, \ + 0x6f, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, \ + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x6b, \ + 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x6c, 0x6d, \ + 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, 0x6d, 0x6e, 0x6f, \ + 0x70, 0x71, 0x72, 0x73, 0x74, 0x6e, 0x6f, 0x70, 0x71, \ + 0x72, 0x73, 0x74, 0x75 ) /** - * Report digest test result + * Report a digest test result * - * @v digest Digest algorithm - * @v fragments Digest test fragment list, or NULL - * @v data Test data - * @v len Length of test data - * @v expected Expected digest value + * @v test Digest test */ -#define digest_ok( digest, fragments, data, len, expected ) do { \ - ok ( digest_test ( digest, fragments, data, len, expected ) ); \ - } while ( 0 ) +#define digest_ok(test) digest_okx ( test, __FILE__, __LINE__ ) + +extern void digest_okx ( struct digest_test *test, const char *file, + unsigned int line ); +extern unsigned long digest_cost ( struct digest_algorithm *digest ); #endif /* _DIGEST_TEST_H */ diff --git a/roms/ipxe/src/tests/dns_test.c b/roms/ipxe/src/tests/dns_test.c index 52f5f19f2..f08e7810f 100644 --- a/roms/ipxe/src/tests/dns_test.c +++ b/roms/ipxe/src/tests/dns_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/entropy_sample.c b/roms/ipxe/src/tests/entropy_sample.c index 95a662e3e..b45648c11 100644 --- a/roms/ipxe/src/tests/entropy_sample.c +++ b/roms/ipxe/src/tests/entropy_sample.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/hash_df_test.c b/roms/ipxe/src/tests/hash_df_test.c index 74c8d0f4d..0b7d56ad7 100644 --- a/roms/ipxe/src/tests/hash_df_test.c +++ b/roms/ipxe/src/tests/hash_df_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/hmac_drbg_test.c b/roms/ipxe/src/tests/hmac_drbg_test.c index 8cbf1cc8b..ddf9db2c5 100644 --- a/roms/ipxe/src/tests/hmac_drbg_test.c +++ b/roms/ipxe/src/tests/hmac_drbg_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/ipv4_test.c b/roms/ipxe/src/tests/ipv4_test.c new file mode 100644 index 000000000..f84a8b81f --- /dev/null +++ b/roms/ipxe/src/tests/ipv4_test.c @@ -0,0 +1,154 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * IPv4 tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stdint.h> +#include <string.h> +#include <byteswap.h> +#include <ipxe/in.h> +#include <ipxe/test.h> + +/** Define inline IPv4 address */ +#define IPV4(a,b,c,d) \ + htonl ( ( (a) << 24 ) | ( (b) << 16 ) | ( (c) << 8 ) | (d) ) + +/** + * Report an inet_ntoa() test result + * + * @v addr IPv4 address + * @v text Expected textual representation + * @v file Test code file + * @v line Test code line + */ +static void inet_ntoa_okx ( uint32_t addr, const char *text, const char *file, + unsigned int line ) { + struct in_addr in = { .s_addr = addr }; + char *actual; + + /* Format address */ + actual = inet_ntoa ( in ); + DBG ( "inet_ntoa ( %d.%d.%d.%d ) = %s\n", + ( ( ntohl ( addr ) >> 24 ) & 0xff ), + ( ( ntohl ( addr ) >> 16 ) & 0xff ), + ( ( ntohl ( addr ) >> 8 ) & 0xff ), + ( ( ntohl ( addr ) >> 0 ) & 0xff ), actual ); + okx ( strcmp ( actual, text ) == 0, file, line ); +} +#define inet_ntoa_ok( addr, text ) \ + inet_ntoa_okx ( addr, text, __FILE__, __LINE__ ) + +/** + * Report an inet_aton() test result + * + * @v text Textual representation + * @v addr Expected IPv4 address + * @v file Test code file + * @v line Test code line + */ +static void inet_aton_okx ( const char *text, uint32_t addr, const char *file, + unsigned int line ) { + struct in_addr actual; + + /* Parse address */ + okx ( inet_aton ( text, &actual ) != 0, file, line ); + DBG ( "inet_aton ( \"%s\" ) = %s\n", text, inet_ntoa ( actual ) ); + okx ( actual.s_addr == addr, file, line ); +}; +#define inet_aton_ok( text, addr ) \ + inet_aton_okx ( text, addr, __FILE__, __LINE__ ) + +/** + * Report an inet_aton() failure test result + * + * @v text Textual representation + * @v file Test code file + * @v line Test code line + */ +static void inet_aton_fail_okx ( const char *text, const char *file, + unsigned int line ) { + struct in_addr actual; + + /* Attempt to parse address */ + okx ( inet_aton ( text, &actual ) == 0, file, line ); +} +#define inet_aton_fail_ok( text ) \ + inet_aton_fail_okx ( text, __FILE__, __LINE__ ) + +/** + * Perform IPv4 self-tests + * + */ +static void ipv4_test_exec ( void ) { + + /* Address testing macros */ + ok ( IN_IS_CLASSA ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSB ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSC ( IPV4 ( 10, 0, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSA ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( IN_IS_CLASSB ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSC ( IPV4 ( 172, 16, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSA ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( ! IN_IS_CLASSB ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( IN_IS_CLASSC ( IPV4 ( 192, 168, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 127, 0, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 8, 8, 8, 8 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 0, 0, 0, 0 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 223, 0, 0, 1 ) ) ); + ok ( ! IN_IS_MULTICAST ( IPV4 ( 240, 0, 0, 1 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 224, 0, 0, 1 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 231, 89, 0, 2 ) ) ); + ok ( IN_IS_MULTICAST ( IPV4 ( 239, 6, 1, 17 ) ) ); + + /* inet_ntoa() tests */ + inet_ntoa_ok ( IPV4 ( 127, 0, 0, 1 ), "127.0.0.1" ); + inet_ntoa_ok ( IPV4 ( 0, 0, 0, 0 ), "0.0.0.0" ); + inet_ntoa_ok ( IPV4 ( 255, 255, 255, 255 ), "255.255.255.255" ); + inet_ntoa_ok ( IPV4 ( 212, 13, 204, 60 ), "212.13.204.60" ); + + /* inet_aton() tests */ + inet_aton_ok ( "212.13.204.60", IPV4 ( 212, 13, 204, 60 ) ); + inet_aton_ok ( "127.0.0.1", IPV4 ( 127, 0, 0, 1 ) ); + + /* inet_aton() failure tests */ + inet_aton_fail_ok ( "256.0.0.1" ); /* Byte out of range */ + inet_aton_fail_ok ( "212.13.204.60.1" ); /* Too long */ + inet_aton_fail_ok ( "127.0.0" ); /* Too short */ + inet_aton_fail_ok ( "1.2.3.a" ); /* Invalid characters */ + inet_aton_fail_ok ( "127.0..1" ); /* Missing bytes */ +} + +/** IPv4 self-test */ +struct self_test ipv4_test __self_test = { + .name = "ipv4", + .exec = ipv4_test_exec, +}; diff --git a/roms/ipxe/src/tests/ipv6_test.c b/roms/ipxe/src/tests/ipv6_test.c index e16fc7c3d..772eb1b82 100644 --- a/roms/ipxe/src/tests/ipv6_test.c +++ b/roms/ipxe/src/tests/ipv6_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/linebuf_test.c b/roms/ipxe/src/tests/linebuf_test.c index e06ac7d86..0dd486e9d 100644 --- a/roms/ipxe/src/tests/linebuf_test.c +++ b/roms/ipxe/src/tests/linebuf_test.c @@ -1,35 +1,320 @@ -#include <stdint.h> +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Line buffer self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + #include <string.h> -#include <stdio.h> +#include <assert.h> #include <ipxe/linebuf.h> +#include <ipxe/test.h> -static const char data1[] = -"Hello world\r\n" -"This is a reasonably nice set of lines\n" -"with not many different terminators\r\n\r\n" -"There should be exactly one blank line above\n" -"and this line should never appear at all since it has no terminator"; +/** Define inline raw data */ +#define DATA(...) { __VA_ARGS__ } -void linebuf_test ( void ) { - struct line_buffer linebuf; - const char *data = data1; - size_t len = ( sizeof ( data1 ) - 1 /* be mean; strip the NUL */ ); - ssize_t frag_len; - char *line; - - memset ( &linebuf, 0, sizeof ( linebuf ) ); - while ( len ) { - frag_len = line_buffer ( &linebuf, data, len ); - if ( frag_len < 0 ) { - printf ( "line_buffer() failed: %s\n", - strerror ( frag_len ) ); +/** Define inline lines */ +#define LINES(...) { __VA_ARGS__ } + +/** A line buffer test */ +struct linebuf_test { + /** Raw data */ + const void *data; + /** Length of raw data */ + size_t len; + /** Expected sequence of lines */ + const char **lines; + /** Number of expected lines */ + unsigned int count; +}; + +/** Line buffer test expected failure indicator */ +static const char linebuf_failure[1]; + +/** + * Define a line buffer test + * + * @v name Test name + * @v DATA Raw data + * @v LINES Expected sequence of lines + * @ret test Line buffer test + */ +#define LINEBUF_TEST( name, DATA, LINES ) \ + static const char name ## _data[] = DATA; \ + static const char * name ## _lines[] = LINES; \ + static struct linebuf_test name = { \ + .data = name ## _data, \ + .len = ( sizeof ( name ## _data ) - 1 /* NUL */ ), \ + .lines = name ## _lines, \ + .count = ( sizeof ( name ## _lines ) / \ + sizeof ( name ## _lines[0] ) ), \ + } + +/** Simple line buffer test */ +LINEBUF_TEST ( simple, + ( "HTTP/1.1 200 OK\r\n" + "Content-Length: 123\r\n" + "Content-Type: text/plain\r\n" + "\r\n" ), + LINES ( "HTTP/1.1 200 OK", + "Content-Length: 123", + "Content-Type: text/plain", + "" ) ); + +/** Mixed line terminators */ +LINEBUF_TEST ( mixed, + ( "LF only\n" "CRLF\r\n" "\n" "\n" "\r\n" "\r\n" "CR only\r" ), + LINES ( "LF only", "CRLF", "", "", "", "", + NULL /* \r should not be treated as a terminator */ ) ); + +/** Split consumption: part 1 */ +LINEBUF_TEST ( split_1, + ( "This line was" ), + LINES ( NULL ) ); + +/** Split consumption: part 2 */ +LINEBUF_TEST ( split_2, + ( " split across" ), + LINES ( NULL ) ); + +/** Split consumption: part 3 */ +LINEBUF_TEST ( split_3, + ( " multiple calls\r\nand so was this one\r" ), + LINES ( "This line was split across multiple calls", NULL ) ); + +/** Split consumption: part 4 */ +LINEBUF_TEST ( split_4, + ( "\nbut not this one\r\n" ), + LINES ( "and so was this one", "but not this one" ) ); + +/** Split consumption: part 5 */ +LINEBUF_TEST ( split_5, + ( "" ), + LINES ( NULL ) ); + +/** Split consumption: part 6 */ +LINEBUF_TEST ( split_6, + ( "This line came after a zero-length call\r\n" ), + LINES ( "This line came after a zero-length call" ) ); + +/** Embedded NULs */ +LINEBUF_TEST ( embedded_nuls, + ( "This\r\ntest\r\nincludes\r\n\r\nsome\0binary\0data\r\n" ), + LINES ( "This", "test", "includes", "", linebuf_failure ) ); + +/** + * Report line buffer initialisation test result + * + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_init_okx ( struct line_buffer *linebuf, + const char *file, unsigned int line ) { + + /* Initialise line buffer */ + memset ( linebuf, 0, sizeof ( *linebuf ) ); + okx ( buffered_line ( linebuf ) == NULL, file, line ); +} +#define linebuf_init_ok( linebuf ) \ + linebuf_init_okx ( linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer consumption test result + * + * @v test Line buffer test + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_consume_okx ( struct linebuf_test *test, + struct line_buffer *linebuf, + const char *file, unsigned int line ) { + const char *data = test->data; + size_t remaining = test->len; + int len; + unsigned int i; + const char *expected; + char *actual; + int rc; + + DBGC ( test, "LINEBUF %p:\n", test ); + DBGC_HDA ( test, 0, data, remaining ); + + /* Consume data one line at a time */ + for ( i = 0 ; i < test->count ; i++ ) { + + /* Add data to line buffer */ + len = line_buffer ( linebuf, data, remaining ); + + /* Get buffered line, if any */ + actual = buffered_line ( linebuf ); + if ( len < 0 ) { + rc = len; + DBGC ( test, "LINEBUF %p %s\n", test, strerror ( rc ) ); + } else if ( actual != NULL ) { + DBGC ( test, "LINEBUF %p \"%s\" (consumed %d)\n", + test, actual, len ); + } else { + DBGC ( test, "LINEBUF %p unterminated (consumed %d)\n", + test, len ); + } + + /* Check for success/failure */ + expected = test->lines[i]; + if ( expected == linebuf_failure ) { + rc = len; + okx ( rc < 0, file, line ); + okx ( remaining > 0, file, line ); return; } - data += frag_len; - len -= frag_len; - if ( ( line = buffered_line ( &linebuf ) ) ) - printf ( "\"%s\"\n", line ); + okx ( len >= 0, file, line ); + okx ( ( ( size_t ) len ) <= remaining, file, line ); + + /* Check expected result */ + if ( expected == NULL ) { + okx ( actual == NULL, file, line ); + } else { + okx ( actual != NULL, file, line ); + okx ( strcmp ( actual, expected ) == 0, file, line ); + } + + /* Consume data */ + data += len; + remaining -= len; + } + + /* Check that all data was consumed */ + okx ( remaining == 0, file, line ); +} +#define linebuf_consume_ok( test, linebuf ) \ + linebuf_consume_okx ( test, linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer accumulation test result + * + * @v test Line buffer test + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_accumulated_okx ( struct linebuf_test *test, + struct line_buffer *linebuf, + const char *file, unsigned int line ) { + const char *actual; + const char *expected; + unsigned int i; + + /* Check each accumulated line */ + actual = linebuf->data; + for ( i = 0 ; i < test->count ; i++ ) { + + /* Check accumulated line */ + okx ( actual != NULL, file, line ); + okx ( actual >= linebuf->data, file, line ); + expected = test->lines[i]; + if ( ( expected == NULL ) || ( expected == linebuf_failure ) ) + return; + okx ( strcmp ( actual, expected ) == 0, file, line ); + + /* Move to next line */ + actual += ( strlen ( actual ) + 1 /* NUL */ ); + okx ( actual <= ( linebuf->data + linebuf->len ), file, line ); } +} +#define linebuf_accumulated_ok( test, linebuf ) \ + linebuf_accumulated_okx ( test, linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer emptying test result + * + * @v linebuf Line buffer + * @v file Test code file + * @v line Test code line + */ +static void linebuf_empty_okx ( struct line_buffer *linebuf, + const char *file, unsigned int line ) { - empty_line_buffer ( &linebuf ); + /* Empty line buffer */ + empty_line_buffer ( linebuf ); + okx ( buffered_line ( linebuf ) == NULL, file, line ); } +#define linebuf_empty_ok( linebuf ) \ + linebuf_empty_okx ( linebuf, __FILE__, __LINE__ ) + +/** + * Report line buffer combined test result + * + * @v test Line buffer test + * @v file Test code file + * @v line Test code line + */ +static void linebuf_okx ( struct linebuf_test *test, const char *file, + unsigned int line ) { + struct line_buffer linebuf; + + linebuf_init_okx ( &linebuf, file, line ); + linebuf_consume_okx ( test, &linebuf, file, line ); + linebuf_accumulated_okx ( test, &linebuf, file, line ); + linebuf_empty_okx ( &linebuf, file, line ); +} +#define linebuf_ok( test ) \ + linebuf_okx ( test, __FILE__, __LINE__ ) + +/** + * Perform line buffer self-tests + * + */ +static void linebuf_test_exec ( void ) { + struct line_buffer linebuf; + + /* Basic tests */ + linebuf_ok ( &simple ); + linebuf_ok ( &mixed ); + + /* Split consumption test */ + linebuf_init_ok ( &linebuf ); + linebuf_consume_ok ( &split_1, &linebuf ); + linebuf_consume_ok ( &split_2, &linebuf ); + linebuf_consume_ok ( &split_3, &linebuf ); + linebuf_consume_ok ( &split_4, &linebuf ); + linebuf_consume_ok ( &split_5, &linebuf ); + linebuf_consume_ok ( &split_6, &linebuf ); + linebuf_empty_ok ( &linebuf ); + + /* Embedded NULs */ + linebuf_ok ( &embedded_nuls ); +} + +/** Line buffer self-test */ +struct self_test linebuf_test __self_test = { + .name = "linebuf", + .exec = linebuf_test_exec, +}; diff --git a/roms/ipxe/src/tests/list_test.c b/roms/ipxe/src/tests/list_test.c index 35cbd5e5f..352c87da0 100644 --- a/roms/ipxe/src/tests/list_test.c +++ b/roms/ipxe/src/tests/list_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/math_test.c b/roms/ipxe/src/tests/math_test.c index e12b7939d..1a244f1eb 100644 --- a/roms/ipxe/src/tests/math_test.c +++ b/roms/ipxe/src/tests/math_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -35,6 +39,26 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <ipxe/isqrt.h> /** + * Force a call to the non-constant implementation of ffsl() + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +__attribute__ (( noinline )) int ffsl_var ( long value ) { + return ffsl ( value ); +} + +/** + * Force a call to the non-constant implementation of ffsll() + * + * @v value Value + * @ret lsb Least significant bit set in value (LSB=1), or zero + */ +__attribute__ (( noinline )) int ffsll_var ( long long value ) { + return ffsll ( value ); +} + +/** * Force a call to the non-constant implementation of flsl() * * @v value Value @@ -173,6 +197,44 @@ __attribute__ (( noinline )) int64_t s64mod_var ( int64_t dividend, } /** + * Report a ffsl() test result + * + * @v value Value + * @v lsb Expected LSB + * @v file Test code file + * @v line Test code line + */ +static inline __attribute__ (( always_inline )) void +ffsl_okx ( long value, int lsb, const char *file, unsigned int line ) { + + /* Verify as a constant (requires to be inlined) */ + okx ( ffsl ( value ) == lsb, file, line ); + + /* Verify as a non-constant */ + okx ( ffsl_var ( value ) == lsb, file, line ); +} +#define ffsl_ok( value, lsb ) ffsl_okx ( value, lsb, __FILE__, __LINE__ ) + +/** + * Report a ffsll() test result + * + * @v value Value + * @v lsb Expected LSB + * @v file Test code file + * @v line Test code line + */ +static inline __attribute__ (( always_inline )) void +ffsll_okx ( long long value, int lsb, const char *file, unsigned int line ) { + + /* Verify as a constant (requires to be inlined) */ + okx ( ffsll ( value ) == lsb, file, line ); + + /* Verify as a non-constant */ + okx ( ffsll_var ( value ) == lsb, file, line ); +} +#define ffsll_ok( value, lsb ) ffsll_okx ( value, lsb, __FILE__, __LINE__ ) + +/** * Report a flsl() test result * * @v value Value @@ -270,6 +332,22 @@ static void s64divmod_okx ( int64_t dividend, int64_t divisor, */ static void math_test_exec ( void ) { + /* Test ffsl() */ + ffsl_ok ( 0, 0 ); + ffsl_ok ( 1, 1 ); + ffsl_ok ( 255, 1 ); + ffsl_ok ( 256, 9 ); + ffsl_ok ( 257, 1 ); + ffsl_ok ( 0x54850596, 2 ); + ffsl_ok ( 0x80000000, 32 ); + + /* Test ffsll() */ + ffsll_ok ( 0, 0 ); + ffsll_ok ( 1, 1 ); + ffsll_ok ( 0x6d63623330ULL, 5 ); + ffsll_ok ( 0x80000000UL, 32 ); + ffsll_ok ( 0x8000000000000000ULL, 64 ); + /* Test flsl() */ flsl_ok ( 0, 0 ); flsl_ok ( 1, 1 ); diff --git a/roms/ipxe/src/tests/md5_test.c b/roms/ipxe/src/tests/md5_test.c index ba5f24c3e..e9ed2716a 100644 --- a/roms/ipxe/src/tests/md5_test.c +++ b/roms/ipxe/src/tests/md5_test.c @@ -15,82 +15,58 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * MD5 tests * + * Test inputs borrowed from NIST SHA-1 tests, with results calculated + * using md5sum. */ -#include <stdint.h> +/* Forcibly enable assertions */ +#undef NDEBUG + #include <ipxe/md5.h> #include <ipxe/test.h> #include "digest_test.h" -/** An MD5 test vector */ -struct md5_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[MD5_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "md5sum /dev/null") */ +DIGEST_TEST ( md5_empty, &md5_algorithm, DIGEST_EMPTY, + DIGEST ( 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, 0xe9, + 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e ) ); -/** MD5 test vectors */ -static struct md5_test_vector md5_test_vectors[] = { - /* Test inputs borrowed from SHA-1 tests, with results - * calculated using md5sum. - */ - { NULL, 0, - { 0xd4, 0x1d, 0x8c, 0xd9, 0x8f, 0x00, 0xb2, 0x04, - 0xe9, 0x80, 0x09, 0x98, 0xec, 0xf8, 0x42, 0x7e } }, - { "abc", 3, - { 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, - 0xd6, 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x82, 0x15, 0xef, 0x07, 0x96, 0xa2, 0x0b, 0xca, - 0xaa, 0xe1, 0x16, 0xd3, 0x87, 0x6c, 0x66, 0x4a } }, -}; +/* NIST test vector "abc" (digest obtained from "md5sum <data>") */ +DIGEST_TEST ( md5_nist_abc, &md5_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x90, 0x01, 0x50, 0x98, 0x3c, 0xd2, 0x4f, 0xb0, 0xd6, + 0x96, 0x3f, 0x7d, 0x28, 0xe1, 0x7f, 0x72 ) ); -/** MD5 test fragment lists */ -static struct digest_test_fragments md5_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" (digest obtained from "md5sum <data>") */ +DIGEST_TEST ( md5_nist_abc_opq, &md5_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x82, 0x15, 0xef, 0x07, 0x96, 0xa2, 0x0b, 0xca, 0xaa, + 0xe1, 0x16, 0xd3, 0x87, 0x6c, 0x66, 0x4a ) ); /** * Perform MD5 self-test * */ static void md5_test_exec ( void ) { - struct digest_algorithm *digest = &md5_algorithm; - struct md5_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( md5_test_vectors ) / - sizeof ( md5_test_vectors[0] ) ) ; i++ ) { - test = &md5_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( md5_test_fragments ) / - sizeof ( md5_test_fragments[0] ) ) ; j++ ){ - digest_ok ( digest, &md5_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &md5_empty ); + digest_ok ( &md5_nist_abc ); + digest_ok ( &md5_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "MD5 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "MD5 required %ld cycles per byte\n", + digest_cost ( &md5_algorithm ) ); } /** MD5 self-test */ diff --git a/roms/ipxe/src/tests/memcpy_test.c b/roms/ipxe/src/tests/memcpy_test.c index f1e5503a6..0247c71d4 100644 --- a/roms/ipxe/src/tests/memcpy_test.c +++ b/roms/ipxe/src/tests/memcpy_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/memset_test.c b/roms/ipxe/src/tests/memset_test.c new file mode 100644 index 000000000..d96f83fa6 --- /dev/null +++ b/roms/ipxe/src/tests/memset_test.c @@ -0,0 +1,157 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * memset() self-tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <string.h> +#include <ipxe/test.h> + +/* Provide global functions to allow inspection of generated code */ + +void memset_zero_0 ( void *dest ) { memset ( dest, 0, 0 ); } +void memset_zero_1 ( void *dest ) { memset ( dest, 0, 1 ); } +void memset_zero_2 ( void *dest ) { memset ( dest, 0, 2 ); } +void memset_zero_3 ( void *dest ) { memset ( dest, 0, 3 ); } +void memset_zero_4 ( void *dest ) { memset ( dest, 0, 4 ); } +void memset_zero_5 ( void *dest ) { memset ( dest, 0, 5 ); } +void memset_zero_6 ( void *dest ) { memset ( dest, 0, 6 ); } +void memset_zero_7 ( void *dest ) { memset ( dest, 0, 7 ); } +void memset_zero_8 ( void *dest ) { memset ( dest, 0, 8 ); } +void memset_zero_9 ( void *dest ) { memset ( dest, 0, 9 ); } +void memset_zero_10 ( void *dest ) { memset ( dest, 0, 10 ); } +void memset_zero_11 ( void *dest ) { memset ( dest, 0, 11 ); } +void memset_zero_12 ( void *dest ) { memset ( dest, 0, 12 ); } +void memset_zero_13 ( void *dest ) { memset ( dest, 0, 13 ); } +void memset_zero_14 ( void *dest ) { memset ( dest, 0, 14 ); } +void memset_zero_15 ( void *dest ) { memset ( dest, 0, 15 ); } +void memset_zero_16 ( void *dest ) { memset ( dest, 0, 16 ); } +void memset_zero_17 ( void *dest ) { memset ( dest, 0, 17 ); } +void memset_zero_18 ( void *dest ) { memset ( dest, 0, 18 ); } +void memset_zero_19 ( void *dest ) { memset ( dest, 0, 19 ); } +void memset_zero_20 ( void *dest ) { memset ( dest, 0, 20 ); } +void memset_zero_21 ( void *dest ) { memset ( dest, 0, 21 ); } +void memset_zero_22 ( void *dest ) { memset ( dest, 0, 22 ); } +void memset_zero_23 ( void *dest ) { memset ( dest, 0, 23 ); } +void memset_zero_24 ( void *dest ) { memset ( dest, 0, 24 ); } +void memset_zero_25 ( void *dest ) { memset ( dest, 0, 25 ); } +void memset_zero_26 ( void *dest ) { memset ( dest, 0, 26 ); } +void memset_zero_27 ( void *dest ) { memset ( dest, 0, 27 ); } +void memset_zero_28 ( void *dest ) { memset ( dest, 0, 28 ); } +void memset_zero_29 ( void *dest ) { memset ( dest, 0, 29 ); } +void memset_zero_30 ( void *dest ) { memset ( dest, 0, 30 ); } +void memset_zero_31 ( void *dest ) { memset ( dest, 0, 31 ); } + +/** + * Force a call to the variable-length implementation of memset() + * + * @v dest Destination address + * @v fill Fill pattern + * @v len Length of data + * @ret dest Destination address + */ +__attribute__ (( noinline )) void * memset_var ( void *dest, unsigned int fill, + size_t len ) { + return memset ( dest, fill, len ); +} + +/** + * Perform a constant-length memset() test + * + * @v len Length of data + */ +#define MEMSET_TEST_CONSTANT( len ) do { \ + uint8_t dest_const[ 1 + len + 1 ]; \ + uint8_t dest_var[ 1 + len + 1 ]; \ + static uint8_t zero[len]; \ + unsigned int i; \ + \ + for ( i = 0 ; i < sizeof ( dest_const ) ; i++ ) \ + dest_const[i] = 0xaa; \ + memset ( ( dest_const + 1 ), 0, len ); \ + ok ( dest_const[0] == 0xaa ); \ + ok ( dest_const[ sizeof ( dest_const ) - 1 ] == 0xaa ); \ + ok ( memcmp ( ( dest_const + 1 ), zero, len ) == 0 ); \ + \ + for ( i = 0 ; i < sizeof ( dest_var ) ; i++ ) \ + dest_var[i] = 0xbb; \ + memset_var ( ( dest_var + 1 ), 0, len ); \ + ok ( dest_var[0] == 0xbb ); \ + ok ( dest_var[ sizeof ( dest_var ) - 1 ] == 0xbb ); \ + ok ( memcmp ( ( dest_var + 1 ), zero, len ) == 0 ); \ + } while ( 0 ) + +/** + * Perform memset() self-tests + * + */ +static void memset_test_exec ( void ) { + + /* Constant-length tests */ + MEMSET_TEST_CONSTANT ( 0 ); + MEMSET_TEST_CONSTANT ( 1 ); + MEMSET_TEST_CONSTANT ( 2 ); + MEMSET_TEST_CONSTANT ( 3 ); + MEMSET_TEST_CONSTANT ( 4 ); + MEMSET_TEST_CONSTANT ( 5 ); + MEMSET_TEST_CONSTANT ( 6 ); + MEMSET_TEST_CONSTANT ( 7 ); + MEMSET_TEST_CONSTANT ( 8 ); + MEMSET_TEST_CONSTANT ( 9 ); + MEMSET_TEST_CONSTANT ( 10 ); + MEMSET_TEST_CONSTANT ( 11 ); + MEMSET_TEST_CONSTANT ( 12 ); + MEMSET_TEST_CONSTANT ( 13 ); + MEMSET_TEST_CONSTANT ( 14 ); + MEMSET_TEST_CONSTANT ( 15 ); + MEMSET_TEST_CONSTANT ( 16 ); + MEMSET_TEST_CONSTANT ( 17 ); + MEMSET_TEST_CONSTANT ( 18 ); + MEMSET_TEST_CONSTANT ( 19 ); + MEMSET_TEST_CONSTANT ( 20 ); + MEMSET_TEST_CONSTANT ( 21 ); + MEMSET_TEST_CONSTANT ( 22 ); + MEMSET_TEST_CONSTANT ( 23 ); + MEMSET_TEST_CONSTANT ( 24 ); + MEMSET_TEST_CONSTANT ( 25 ); + MEMSET_TEST_CONSTANT ( 26 ); + MEMSET_TEST_CONSTANT ( 27 ); + MEMSET_TEST_CONSTANT ( 28 ); + MEMSET_TEST_CONSTANT ( 29 ); + MEMSET_TEST_CONSTANT ( 30 ); + MEMSET_TEST_CONSTANT ( 31 ); +} + +/** memset() self-test */ +struct self_test memset_test __self_test = { + .name = "memset", + .exec = memset_test_exec, +}; diff --git a/roms/ipxe/src/tests/ocsp_test.c b/roms/ipxe/src/tests/ocsp_test.c index a318c185a..c6d458596 100644 --- a/roms/ipxe/src/tests/ocsp_test.c +++ b/roms/ipxe/src/tests/ocsp_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1857,5 +1861,6 @@ struct self_test ocsp_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( ocsp_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); diff --git a/roms/ipxe/src/tests/pccrc_test.c b/roms/ipxe/src/tests/pccrc_test.c new file mode 100644 index 000000000..f4ab573ac --- /dev/null +++ b/roms/ipxe/src/tests/pccrc_test.c @@ -0,0 +1,529 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * Peer Content Caching and Retrieval: Content Identification [MS-PCCRC] tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stdint.h> +#include <string.h> +#include <assert.h> +#include <ipxe/uaccess.h> +#include <ipxe/pccrc.h> +#include <ipxe/sha256.h> +#include <ipxe/sha512.h> +#include <ipxe/hmac.h> +#include <ipxe/test.h> + +/** Define inline raw data */ +#define DATA(...) { __VA_ARGS__ } + +/** + * Define an inline content range + * + * @v START Start offset + * @v END End offset + * @ret range Content range + */ +#define RANGE( START, END ) { .start = START, .end = END } + +/** + * Define an inline trimmed content range + * + * @v START Start offset + * @v END End offset + * @ret trim Trimmed content range + */ +#define TRIM( START, END ) { .start = START, .end = END } + +/** A content information test */ +struct peerdist_info_test { + /** Raw content information */ + const void *data; + /** Length of raw content information */ + size_t len; + /** Expected digest algorithm */ + struct digest_algorithm *expected_digest; + /** Expected digest size */ + size_t expected_digestsize; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected trimmed content range */ + struct peerdist_range expected_trim; + /** Expected number of segments */ + unsigned int expected_segments; +}; + +/** + * Define a content information test + * + * @v name Test name + * @v DATA Raw content information + * @v DIGEST Expected digest algorithm + * @v DIGESTSIZE Expected digest size + * @v RANGE Expected content range + * @v TRIM Expected trimmer content range + * @v SEGMENTS Expected number of segments + * @ret test Content information test + * + * Raw content information can be obtained from PeerDist-capable web + * servers using wget's "--header" option to inject the relevant + * PeerDist headers. For example: + * + * wget --header "Accept-Encoding: peerdist" \ + * --header "X-P2P-PeerDist: Version=1.0" \ + * http://peerdist.server.address/test.url -O - | xxd -i -c 11 + * + * Version 1 content information can be retrieved using the headers: + * + * Accept-Encoding: peerdist + * X-P2P-PeerDist: Version=1.0 + * + * Version 2 content information can be retrieved (from compatible + * servers) using the headers: + * + * Accept-Encoding: peerdist + * X-P2P-PeerDist: Version=1.1 + * X-P2P-PeerDistEx: MinContentInformation=2.0, MaxContentInformation=2.0 + */ +#define PEERDIST_INFO_TEST( name, DATA, DIGEST, DIGESTSIZE, RANGE, \ + TRIM, SEGMENTS ) \ + static const uint8_t name ## _data[] = DATA; \ + static struct peerdist_info_test name = { \ + .data = name ## _data, \ + .len = sizeof ( name ## _data ), \ + .expected_digest = DIGEST, \ + .expected_digestsize = DIGESTSIZE, \ + .expected_range = RANGE, \ + .expected_trim = TRIM, \ + .expected_segments = SEGMENTS, \ + } + +/** A content information segment test */ +struct peerdist_info_segment_test { + /** Segment index */ + unsigned int index; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected number of blocks */ + unsigned int expected_blocks; + /** Expected block size */ + size_t expected_blksize; + /** Expected segment hash of data */ + uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE]; + /** Expected segment secret */ + uint8_t expected_secret[PEERDIST_DIGEST_MAX_SIZE]; + /** Expected segment identifier */ + uint8_t expected_id[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** + * Define a content information segment test + * + * @v name Test name + * @v INDEX Segment index + * @v RANGE Expected content range + * @v BLOCKS Expected number of blocks + * @v BLKSIZE Expected block size + * @v HASH Expected segment hash of data + * @v SECRET Expected segment secret + * @v ID Expected segment identifier + * @ret test Content information segment test + */ +#define PEERDIST_INFO_SEGMENT_TEST( name, INDEX, RANGE, BLOCKS, \ + BLKSIZE, HASH, SECRET, ID ) \ + static struct peerdist_info_segment_test name = { \ + .index = INDEX, \ + .expected_range = RANGE, \ + .expected_blocks = BLOCKS, \ + .expected_blksize = BLKSIZE, \ + .expected_hash = HASH, \ + .expected_secret = SECRET, \ + .expected_id = ID, \ + } + +/** A content information block test */ +struct peerdist_info_block_test { + /** Block index */ + unsigned int index; + /** Expected content range */ + struct peerdist_range expected_range; + /** Expected trimmed content range */ + struct peerdist_range expected_trim; + /** Expected hash of data */ + uint8_t expected_hash[PEERDIST_DIGEST_MAX_SIZE]; +}; + +/** + * Define a content information block test + * + * @v name Test name + * @v INDEX Block index + * @v RANGE Expected content range + * @v TRIM Expected trimmed content range + * @v HASH Expected hash of data + * @ret test Content information block test + */ +#define PEERDIST_INFO_BLOCK_TEST( name, INDEX, RANGE, TRIM, HASH ) \ + static struct peerdist_info_block_test name = { \ + .index = INDEX, \ + .expected_range = RANGE, \ + .expected_trim = TRIM, \ + .expected_hash = HASH, \ + } + +/** + * Define a server passphrase + * + * @v name Server passphrase name + * @v DATA Raw server passphrase + * + * The server passphrase can be exported from a Windows BranchCache + * server using the command: + * + * netsh branchcache exportkey exported.key somepassword + * + * and this encrypted exported key can be decrypted using the + * oSSL_key_dx or mcrypt_key_dx utilities found in the (prototype) + * Prequel project at https://fedorahosted.org/prequel/ : + * + * oSSL_key_dx exported.key somepassword + * or + * mcrypt_key_dx exported.key somepassword + * + * Either command will display both the server passphrase and the + * "Server Secret". Note that this latter is the version 1 server + * secret (i.e. the SHA-256 of the server passphrase); the + * corresponding version 2 server secret can be obtained by + * calculating the truncated SHA-512 of the server passphrase. + * + * We do not know the server passphrase during normal operation. We + * use it in the self-tests only to check for typos and other errors + * in the test vectors, by checking that the segment secret defined in + * a content information segment test is as expected. + */ +#define SERVER_PASSPHRASE( name, DATA ) \ + static uint8_t name[] = DATA + +/** Server passphrase used for these test vectors */ +SERVER_PASSPHRASE ( passphrase, + DATA ( 0x2a, 0x3d, 0x73, 0xeb, 0x43, 0x5e, 0x9f, 0x2b, 0x8a, 0x34, 0x42, + 0x67, 0xe7, 0x46, 0x7a, 0x3c, 0x73, 0x85, 0xc6, 0xe0, 0x55, 0xe2, + 0xb4, 0xd3, 0x0d, 0xfe, 0xc7, 0xc3, 0x8b, 0x0e, 0xd7, 0x2c ) ); + +/** IIS logo (iis-85.png) content information version 1 */ +PEERDIST_INFO_TEST ( iis_85_png_v1, + DATA ( 0x00, 0x01, 0x0c, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x7e, 0x85, 0x01, 0x00, 0x00, 0x00, 0x01, + 0x00, 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, + 0x18, 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, + 0x63, 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba, + 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f, + 0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2, + 0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2, 0x02, + 0x00, 0x00, 0x00, 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, + 0xe9, 0x0e, 0x71, 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, + 0xf4, 0x92, 0x94, 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, + 0x80, 0x0b, 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, + 0xaf, 0xe4, 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, + 0xb1, 0x88, 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, + 0xcc ), + &sha256_algorithm, 32, RANGE ( 0, 99710 ), TRIM ( 0, 99710 ), 1 ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v1_s0, 0, + RANGE ( 0, 99710 ), 2, 65536, + DATA ( 0xd8, 0xd9, 0x76, 0x35, 0x4a, 0x48, 0x72, 0xe9, 0x25, 0x76, 0x18, + 0x03, 0xf4, 0x58, 0xd9, 0xda, 0xaa, 0x67, 0xf8, 0xe3, 0x1c, 0x63, + 0x0f, 0xb7, 0x4e, 0x6a, 0x31, 0x2e, 0xf8, 0xa2, 0x5a, 0xba ), + DATA ( 0x11, 0xaf, 0xc0, 0xd7, 0x94, 0x92, 0x43, 0xf9, 0x4f, 0x9c, 0x1f, + 0xab, 0x35, 0xd9, 0xfd, 0x1e, 0x33, 0x1f, 0xcf, 0x78, 0x11, 0xa2, + 0xe0, 0x1d, 0x35, 0x87, 0xb3, 0x8d, 0x77, 0x0a, 0x29, 0xe2 ), + DATA ( 0x49, 0x1b, 0x21, 0x7d, 0xbe, 0xe2, 0xb5, 0xf1, 0x2c, 0xa7, 0x9b, + 0x01, 0x5e, 0x06, 0xf4, 0xbb, 0xe6, 0x4f, 0x97, 0x45, 0xba, 0xd7, + 0x86, 0x7a, 0xef, 0x17, 0xde, 0x59, 0x92, 0x7e, 0xdc, 0xe9 ) ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b0, 0, + RANGE ( 0, 65536 ), + TRIM ( 0, 65536 ), + DATA ( 0x73, 0xc1, 0x8a, 0xb8, 0x54, 0x91, 0x10, 0xf8, 0xe9, 0x0e, 0x71, + 0xbb, 0xc3, 0xab, 0x2a, 0xa8, 0xc4, 0x4d, 0x13, 0xf4, 0x92, 0x94, + 0x99, 0x25, 0x5b, 0x66, 0x0f, 0x24, 0xec, 0x77, 0x80, 0x0b ) ); + +/** IIS logo (iis-85.png) content information version 1 segment 0 block 1 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v1_s0_b1, 1, + RANGE ( 65536, 99710 ), + TRIM ( 65536, 99710 ), + DATA ( 0x97, 0x4b, 0xdd, 0x65, 0x56, 0x7f, 0xde, 0xec, 0xcd, 0xaf, 0xe4, + 0x57, 0xa9, 0x50, 0x3b, 0x45, 0x48, 0xf6, 0x6e, 0xd3, 0xb1, 0x88, + 0xdc, 0xfd, 0xa0, 0xac, 0x38, 0x2b, 0x09, 0x71, 0x1a, 0xcc ) ); + +/** IIS logo (iis-85.png) content information version 2 */ +PEERDIST_INFO_TEST ( iis_85_png_v2, + DATA ( 0x00, 0x02, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x88, 0x00, 0x00, 0x99, 0xde, 0xe0, 0xd0, 0xc3, 0x58, + 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, 0xb5, 0xf1, 0x97, 0x87, + 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, 0x78, 0x1f, 0xae, 0x71, + 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4, 0x58, 0x03, 0x7e, 0xd4, 0x04, + 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, 0x41, 0x16, 0x08, 0x85, 0x20, + 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, 0xa3, 0xfa, 0xe1, 0x88, 0xa9, + 0x8e, 0xa2, 0x2d, 0xf3, 0xc0, 0x00, 0x00, 0xeb, 0xa0, 0x33, 0x81, + 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, 0x0f, 0x37, + 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, 0xa1, 0x30, + 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc, 0xb8, 0xb6, 0xeb, + 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, 0xf1, 0x46, 0xb5, + 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, 0xa1, 0x1a, 0xca, + 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ), + &sha512_algorithm, 32, RANGE ( 0, 99710 ), TRIM ( 0, 99710 ), 2 ); + +/** IIS logo (iis-85.png) content information version 2 segment 0 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s0, 0, + RANGE ( 0, 39390 ), 1, 39390, + DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, + 0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, + 0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ), + DATA ( 0x58, 0x03, 0x7e, 0xd4, 0x04, 0x11, 0x6b, 0xb6, 0x16, 0xd9, 0xb1, + 0x41, 0x16, 0x08, 0x85, 0x20, 0xc4, 0x7c, 0xdc, 0x50, 0xab, 0xce, + 0xa3, 0xfa, 0xe1, 0x88, 0xa9, 0x8e, 0xa2, 0x2d, 0xf3, 0xc0 ), + DATA ( 0x33, 0x71, 0xbb, 0xea, 0xdd, 0xb6, 0x23, 0x53, 0xad, 0xce, 0xf9, + 0x70, 0xa0, 0x6f, 0xdf, 0x65, 0x00, 0x1e, 0x04, 0x21, 0xf4, 0xc7, + 0x10, 0x82, 0x76, 0xb0, 0xc3, 0x7a, 0x9f, 0x9e, 0xc1, 0x0f ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 0 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s0_b0, 0, + RANGE ( 0, 39390 ), + TRIM ( 0, 39390 ), + DATA ( 0xe0, 0xd0, 0xc3, 0x58, 0xe2, 0x68, 0x4b, 0x62, 0x33, 0x0d, 0x32, + 0xb5, 0xf1, 0x97, 0x87, 0x24, 0xa0, 0xd0, 0xa5, 0x2b, 0xdc, 0x5e, + 0x78, 0x1f, 0xae, 0x71, 0xff, 0x57, 0xa8, 0xbe, 0x3d, 0xd4 ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 1 */ +PEERDIST_INFO_SEGMENT_TEST ( iis_85_png_v2_s1, 1, + RANGE ( 39390, 99710 ), 1, 60320, + DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, + 0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, + 0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ), + DATA ( 0xb8, 0xb6, 0xeb, 0x77, 0x83, 0xe4, 0xf8, 0x07, 0x64, 0x7b, 0x63, + 0xf1, 0x46, 0xb5, 0x2f, 0x4a, 0xc8, 0x9c, 0xcc, 0x7a, 0xbf, 0x5f, + 0xa1, 0x1a, 0xca, 0xfc, 0x2a, 0xcf, 0x50, 0x28, 0x58, 0x6c ), + DATA ( 0xd7, 0xe9, 0x24, 0x42, 0x5e, 0x8f, 0x4f, 0x88, 0xf0, 0x1d, 0xc6, + 0xa9, 0xbb, 0x1b, 0xc3, 0x7b, 0xe1, 0x13, 0xec, 0x79, 0x17, 0xc7, + 0x45, 0xd4, 0x96, 0x5c, 0x2b, 0x55, 0xfa, 0x16, 0x3a, 0x6e ) ); + +/** IIS logo (iis-85.png) content information version 2 segment 1 block 0 */ +PEERDIST_INFO_BLOCK_TEST ( iis_85_png_v2_s1_b0, 0, + RANGE ( 39390, 99710 ), + TRIM ( 39390, 99710 ), + DATA ( 0x33, 0x81, 0xd0, 0xd0, 0xcb, 0x74, 0xf4, 0xb6, 0x13, 0xd8, 0x21, + 0x0f, 0x37, 0xf0, 0x02, 0xa0, 0x6f, 0x39, 0x10, 0x58, 0x60, 0x96, + 0xa1, 0x30, 0xd3, 0x43, 0x98, 0xc0, 0x8e, 0x66, 0xd7, 0xbc ) ); + +/** + * Report content information test result + * + * @v test Content information test + * @v info Content information to fill in + * @v file Test code file + * @v line Test code line + */ +static void peerdist_info_okx ( struct peerdist_info_test *test, + struct peerdist_info *info, + const char *file, unsigned int line ) { + + /* Parse content information */ + okx ( peerdist_info ( virt_to_user ( test->data ), test->len, + info ) == 0, file, line ); + + /* Verify content information */ + okx ( info->raw.data == virt_to_user ( test->data ), file, line ); + okx ( info->raw.len == test->len, file, line ); + okx ( info->digest == test->expected_digest, file, line ); + okx ( info->digestsize == test->expected_digestsize, file, line ); + okx ( info->range.start == test->expected_range.start, file, line ); + okx ( info->range.end == test->expected_range.end, file, line ); + okx ( info->trim.start == test->expected_trim.start, file, line ); + okx ( info->trim.end == test->expected_trim.end, file, line ); + okx ( info->trim.start >= info->range.start, file, line ); + okx ( info->trim.end <= info->range.end, file, line ); + okx ( info->segments == test->expected_segments, file, line ); +} +#define peerdist_info_ok( test, info ) \ + peerdist_info_okx ( test, info, __FILE__, __LINE__ ) + +/** + * Report content information segment test result + * + * @v test Content information segment test + * @v info Content information + * @v segment Segment information to fill in + * @v file Test code file + * @v line Test code line + */ +static void peerdist_info_segment_okx ( struct peerdist_info_segment_test *test, + const struct peerdist_info *info, + struct peerdist_info_segment *segment, + const char *file, unsigned int line ) { + size_t digestsize = info->digestsize; + + /* Parse content information segment */ + okx ( peerdist_info_segment ( info, segment, test->index ) == 0, + file, line ); + + /* Verify content information segment */ + okx ( segment->info == info, file, line ); + okx ( segment->index == test->index, file, line ); + okx ( segment->range.start == test->expected_range.start, file, line ); + okx ( segment->range.end == test->expected_range.end, file, line ); + okx ( segment->blocks == test->expected_blocks, file, line ); + okx ( segment->blksize == test->expected_blksize, file, line ); + okx ( memcmp ( segment->hash, test->expected_hash, + digestsize ) == 0, file, line ); + okx ( memcmp ( segment->secret, test->expected_secret, + digestsize ) == 0, file, line ); + okx ( memcmp ( segment->id, test->expected_id, + digestsize ) == 0, file, line ); +} +#define peerdist_info_segment_ok( test, info, segment ) \ + peerdist_info_segment_okx ( test, info, segment, __FILE__, __LINE__ ) + +/** + * Report content information block test result + * + * @v test Content information block test + * @v segment Segment information + * @v block Block information to fill in + * @v file Test code file + * @v line Test code line + */ +static void +peerdist_info_block_okx ( struct peerdist_info_block_test *test, + const struct peerdist_info_segment *segment, + struct peerdist_info_block *block, + const char *file, unsigned int line ) { + const struct peerdist_info *info = segment->info; + size_t digestsize = info->digestsize; + + /* Parse content information block */ + okx ( peerdist_info_block ( segment, block, test->index ) == 0, + file, line ); + + /* Verify content information block */ + okx ( block->segment == segment, file, line ); + okx ( block->index == test->index, file, line ); + okx ( block->range.start == test->expected_range.start, file, line ); + okx ( block->range.end == test->expected_range.end, file, line ); + okx ( block->trim.start == test->expected_trim.start, file, line ); + okx ( block->trim.end == test->expected_trim.end, file, line ); + okx ( memcmp ( block->hash, test->expected_hash, + digestsize ) == 0, file, line ); +} +#define peerdist_info_block_ok( test, segment, block ) \ + peerdist_info_block_okx ( test, segment, block, __FILE__, __LINE__ ) + +/** + * Report server passphrase test result + * + * @v test Content information segment test + * @v info Content information + * @v pass Server passphrase + * @v pass_len Length of server passphrase + * @v file Test code file + * @v line Test code line + */ +static void +peerdist_info_passphrase_okx ( struct peerdist_info_segment_test *test, + const struct peerdist_info *info, + uint8_t *pass, size_t pass_len, + const char *file, unsigned int line ) { + struct digest_algorithm *digest = info->digest; + uint8_t ctx[digest->ctxsize]; + uint8_t secret[digest->digestsize]; + uint8_t expected[digest->digestsize]; + size_t digestsize = info->digestsize; + size_t secretsize = digestsize; + + /* Calculate server secret */ + digest_init ( digest, ctx ); + digest_update ( digest, ctx, pass, pass_len ); + digest_final ( digest, ctx, secret ); + + /* Calculate expected segment secret */ + hmac_init ( digest, ctx, secret, &secretsize ); + assert ( secretsize == digestsize ); + hmac_update ( digest, ctx, test->expected_hash, digestsize ); + hmac_final ( digest, ctx, secret, &secretsize, expected ); + assert ( secretsize == digestsize ); + + /* Verify segment secret */ + okx ( memcmp ( test->expected_secret, expected, digestsize ) == 0, + file, line ); +} +#define peerdist_info_passphrase_ok( test, info, pass, pass_len ) \ + peerdist_info_passphrase_okx ( test, info, pass, pass_len, \ + __FILE__, __LINE__ ) + +/** + * Perform content information self-tests + * + */ +static void peerdist_info_test_exec ( void ) { + struct peerdist_info info; + struct peerdist_info_segment segment; + struct peerdist_info_block block; + + /* IIS logo (iis-85.png) content information version 1 */ + peerdist_info_ok ( &iis_85_png_v1, &info ); + peerdist_info_passphrase_ok ( &iis_85_png_v1_s0, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v1_s0, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v1_s0_b0, &segment, &block ); + peerdist_info_block_ok ( &iis_85_png_v1_s0_b1, &segment, &block ); + + /* IIS logo (iis-85.png) content information version 2 */ + peerdist_info_ok ( &iis_85_png_v2, &info ); + peerdist_info_passphrase_ok ( &iis_85_png_v2_s0, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v2_s0, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v2_s0_b0, &segment, &block ); + peerdist_info_passphrase_ok ( &iis_85_png_v2_s1, &info, + passphrase, sizeof ( passphrase ) ); + peerdist_info_segment_ok ( &iis_85_png_v2_s1, &info, &segment ); + peerdist_info_block_ok ( &iis_85_png_v2_s1_b0, &segment, &block ); +} + +/** Content information self-test */ +struct self_test peerdist_info_test __self_test = { + .name = "pccrc", + .exec = peerdist_info_test_exec, +}; diff --git a/roms/ipxe/src/tests/pixbuf_test.c b/roms/ipxe/src/tests/pixbuf_test.c index 15cd33dfd..aaa516bb2 100644 --- a/roms/ipxe/src/tests/pixbuf_test.c +++ b/roms/ipxe/src/tests/pixbuf_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -53,8 +57,8 @@ void pixbuf_okx ( struct pixel_buffer_test *test, const char *file, /* Correct image data pointer */ test->image->data = virt_to_user ( ( void * ) test->image->data ); - /* Check that image is detected as PNM */ - okx ( image_probe ( test->image ) == 0, file, line ); + /* Check that image is detected as correct type */ + okx ( register_image ( test->image ) == 0, file, line ); okx ( test->image->type == test->type, file, line ); /* Check that a pixel buffer can be created from the image */ @@ -73,4 +77,7 @@ void pixbuf_okx ( struct pixel_buffer_test *test, const char *file, pixbuf_put ( pixbuf ); } + + /* Unregister image */ + unregister_image ( test->image ); } diff --git a/roms/ipxe/src/tests/pixbuf_test.h b/roms/ipxe/src/tests/pixbuf_test.h index 394f7f5fa..d12829d89 100644 --- a/roms/ipxe/src/tests/pixbuf_test.h +++ b/roms/ipxe/src/tests/pixbuf_test.h @@ -1,7 +1,7 @@ #ifndef _PIXBUF_TEST_H #define _PIXBUF_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/refcnt.h> diff --git a/roms/ipxe/src/tests/png_test.c b/roms/ipxe/src/tests/png_test.c index cf32f2034..e921aa2a6 100644 --- a/roms/ipxe/src/tests/png_test.c +++ b/roms/ipxe/src/tests/png_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/pnm_test.c b/roms/ipxe/src/tests/pnm_test.c index 26b0c0726..d57fdaaef 100644 --- a/roms/ipxe/src/tests/pnm_test.c +++ b/roms/ipxe/src/tests/pnm_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/profile_test.c b/roms/ipxe/src/tests/profile_test.c index 9d682bf2b..d2f8df211 100644 --- a/roms/ipxe/src/tests/profile_test.c +++ b/roms/ipxe/src/tests/profile_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/pubkey_test.h b/roms/ipxe/src/tests/pubkey_test.h index 7678453a9..cd65b8703 100644 --- a/roms/ipxe/src/tests/pubkey_test.h +++ b/roms/ipxe/src/tests/pubkey_test.h @@ -1,7 +1,7 @@ #ifndef _PUBKEY_TEST_H #define _PUBKEY_TEST_H -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <ipxe/crypto.h> diff --git a/roms/ipxe/src/tests/rsa_test.c b/roms/ipxe/src/tests/rsa_test.c index 3b32c74bc..c0d05d263 100644 --- a/roms/ipxe/src/tests/rsa_test.c +++ b/roms/ipxe/src/tests/rsa_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/setjmp_test.c b/roms/ipxe/src/tests/setjmp_test.c new file mode 100644 index 000000000..50ad13f3c --- /dev/null +++ b/roms/ipxe/src/tests/setjmp_test.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * setjmp()/longjmp() tests + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <stddef.h> +#include <assert.h> +#include <setjmp.h> +#include <ipxe/test.h> + +/** A setjmp()/longjmp() test */ +struct setjmp_test { + /** Jump buffer */ + jmp_buf env; + /** Expected value */ + int expected; + /** Test code file */ + const char *file; + /** Test code line */ + unsigned int line; +}; + +/** Expected jump */ +static struct setjmp_test *jumped; + +/** + * Report a setjmp() test result + * + * @v test setjmp()/longjmp() test + * + * This has to be implemented as a macro since if it were a function + * then the context saved by setjmp() would be invalidated when the + * function returned. + */ +#define setjmp_ok( test ) do { \ + int value; \ + /* Sanity check */ \ + assert ( jumped == NULL ); \ + /* Initialise test */ \ + (test)->expected = 0; \ + (test)->file = __FILE__; \ + (test)->line = __LINE__; \ + /* Perform setjmp() */ \ + value = setjmp ( (test)->env ); \ + /* Report setjmp()/longjmp() result */ \ + setjmp_return_ok ( (test), value ); \ + } while ( 0 ) + +/** + * Report a setjmp()/longjmp() test result + * + * @v test setjmp()/longjmp() test + * @v value Value returned from setjmp() + * + * This function ends up reporting results from either setjmp() or + * longjmp() tests (since calls to longjmp() will return via the + * corresponding setjmp()). It therefore uses the test code file and + * line stored in the test structure, which will represent the line + * from which either setjmp() or longjmp() was called. + */ +static void setjmp_return_ok ( struct setjmp_test *test, int value ) { + + /* Determine whether this was reached via setjmp() or longjmp() */ + if ( value == 0 ) { + /* This is the initial call to setjmp() */ + okx ( test->expected == 0, test->file, test->line ); + okx ( jumped == NULL, test->file, test->line ); + } else { + /* This is reached via a call to longjmp() */ + okx ( value == test->expected, test->file, test->line ); + okx ( jumped == test, test->file, test->line ); + } + + /* Clear expected jump */ + jumped = NULL; +} + +/** + * Report a longjmp() test result + * + * @v test setjmp()/longjmp() test + * @v file Test code file + * @v line Test code line + */ +static void longjmp_okx ( struct setjmp_test *test, int value, + const char *file, unsigned int line ) { + + /* Record expected value. A zero passed to longjmp() should + * result in setjmp() returning a value of one. + */ + test->expected = ( value ? value : 1 ); + + /* Record test code file and line */ + test->file = file; + test->line = line; + + /* Record expected jump */ + jumped = test; + + /* Perform longjmp(). Should return via setjmp_okx() */ + longjmp ( test->env, value ); + + /* longjmp() should never return */ + assert ( 0 ); +} +#define longjmp_ok( test, value ) \ + longjmp_okx ( test, value, __FILE__, __LINE__ ) + +/** + * Perform setjmp()/longjmp() self-tests + * + */ +static void setjmp_test_exec ( void ) { + static struct setjmp_test alpha; + static struct setjmp_test beta; + static int iteration; + + /* This is one of the very few situations in which the + * "for-case" pattern is justified. + */ + for ( iteration = 0 ; iteration < 10 ; iteration++ ) { + DBGC ( jumped, "SETJMP test iteration %d\n", iteration ); + switch ( iteration ) { + case 0: setjmp_ok ( &alpha ); break; + case 1: setjmp_ok ( &beta ); break; + case 2: longjmp_ok ( &alpha, 0 ); + case 3: longjmp_ok ( &alpha, 1 ); + case 4: longjmp_ok ( &alpha, 2 ); + case 5: longjmp_ok ( &beta, 17 ); + case 6: longjmp_ok ( &beta, 29 ); + case 7: longjmp_ok ( &alpha, -1 ); + case 8: longjmp_ok ( &beta, 0 ); + case 9: longjmp_ok ( &beta, 42 ); + } + } +} + +/** setjmp()/longjmp() self-test */ +struct self_test setjmp_test __self_test = { + .name = "setjmp", + .exec = setjmp_test_exec, +}; diff --git a/roms/ipxe/src/tests/settings_test.c b/roms/ipxe/src/tests/settings_test.c index 4ee6a10fa..f7fb35d0d 100644 --- a/roms/ipxe/src/tests/settings_test.c +++ b/roms/ipxe/src/tests/settings_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -228,6 +232,12 @@ static struct setting test_hexraw_setting = { .type = &setting_type_hexraw, }; +/** Test Base64 setting type */ +static struct setting test_base64_setting = { + .name = "test_base64", + .type = &setting_type_base64, +}; + /** Test UUID setting type */ static struct setting test_uuid_setting = { .name = "test_uuid", @@ -379,6 +389,15 @@ static void settings_test_exec ( void ) { 0x17, 0x06, 0x39, 0x6b, 0xf4, 0x48, 0x4e ), "9e4b6eef36b646fe8f1706396bf4484e" ); + /* "base64" setting type */ + storef_ok ( &test_settings, &test_base64_setting, + "cGFzc6\nNwaHJhc2U= ", + RAW ( 0x70, 0x61, 0x73, 0x73, 0xa3, 0x70, 0x68, 0x72, 0x61, + 0x73, 0x65 ) ); + fetchf_ok ( &test_settings, &test_base64_setting, + RAW ( 0x80, 0x81, 0x82, 0x83, 0x84, 0x00, 0xff ), + "gIGCg4QA/w==" ); + /* "uuid" setting type (no store capability) */ fetchf_ok ( &test_settings, &test_uuid_setting, RAW ( 0x1a, 0x6a, 0x74, 0x9d, 0x0e, 0xda, 0x46, 0x1a,0xa8, @@ -399,3 +418,7 @@ struct self_test settings_test __self_test = { .name = "settings", .exec = settings_test_exec, }; + +/* Include real IPv6 setting type */ +REQUIRING_SYMBOL ( settings_test ); +REQUIRE_OBJECT ( ipv6 ); diff --git a/roms/ipxe/src/tests/sha1_test.c b/roms/ipxe/src/tests/sha1_test.c index bcf761bdd..9f1d75686 100644 --- a/roms/ipxe/src/tests/sha1_test.c +++ b/roms/ipxe/src/tests/sha1_test.c @@ -15,87 +15,63 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * * SHA-1 tests * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf + * */ -#include <stdint.h> +/* Forcibly enable assertions */ +#undef NDEBUG + #include <ipxe/sha1.h> #include <ipxe/test.h> #include "digest_test.h" -/** An SHA-1 test vector */ -struct sha1_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[SHA1_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "sha1sum /dev/null") */ +DIGEST_TEST ( sha1_empty, &sha1_algorithm, DIGEST_EMPTY, + DIGEST ( 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, + 0x55, 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, + 0x07, 0x09 ) ); -/** SHA-1 test vectors */ -static struct sha1_test_vector sha1_test_vectors[] = { - /* Empty test data - * - * Expected digest value obtained from "sha1sum /dev/null" - */ - { NULL, 0, - { 0xda, 0x39, 0xa3, 0xee, 0x5e, 0x6b, 0x4b, 0x0d, 0x32, 0x55, - 0xbf, 0xef, 0x95, 0x60, 0x18, 0x90, 0xaf, 0xd8, 0x07, 0x09 } }, - /* Test data and expected digests taken from the NIST - * Cryptographic Toolkit Algorithm Examples at - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA1.pdf - */ - { "abc", 3, - { 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, 0x3e, - 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, 0xd8, 0x9d } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, 0xae, - 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, 0x70, 0xf1 } }, -}; +/* NIST test vector "abc" */ +DIGEST_TEST ( sha1_nist_abc, &sha1_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xa9, 0x99, 0x3e, 0x36, 0x47, 0x06, 0x81, 0x6a, 0xba, + 0x3e, 0x25, 0x71, 0x78, 0x50, 0xc2, 0x6c, 0x9c, 0xd0, + 0xd8, 0x9d ) ); -/** SHA-1 test fragment lists */ -static struct digest_test_fragments sha1_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha1_nist_abc_opq, &sha1_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x84, 0x98, 0x3e, 0x44, 0x1c, 0x3b, 0xd2, 0x6e, 0xba, + 0xae, 0x4a, 0xa1, 0xf9, 0x51, 0x29, 0xe5, 0xe5, 0x46, + 0x70, 0xf1 ) ); /** * Perform SHA-1 self-test * */ static void sha1_test_exec ( void ) { - struct digest_algorithm *digest = &sha1_algorithm; - struct sha1_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( sha1_test_vectors ) / - sizeof ( sha1_test_vectors[0] ) ) ; i++ ) { - test = &sha1_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( sha1_test_fragments ) / - sizeof ( sha1_test_fragments[0] ) ) ; j++ ){ - digest_ok ( digest, &sha1_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &sha1_empty ); + digest_ok ( &sha1_nist_abc ); + digest_ok ( &sha1_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "SHA1 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "SHA1 required %ld cycles per byte\n", + digest_cost ( &sha1_algorithm ) ); } /** SHA-1 self-test */ diff --git a/roms/ipxe/src/tests/sha256_test.c b/roms/ipxe/src/tests/sha256_test.c index 06a8cae25..3b4c423fd 100644 --- a/roms/ipxe/src/tests/sha256_test.c +++ b/roms/ipxe/src/tests/sha256_test.c @@ -15,93 +15,96 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * - * SHA-256 tests + * SHA-256 family tests + * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA224.pdf * */ -#include <stdint.h> +/* Forcibly enable assertions */ +#undef NDEBUG + #include <ipxe/sha256.h> #include <ipxe/test.h> #include "digest_test.h" -/** An SHA-256 test vector */ -struct sha256_test_vector { - /** Test data */ - void *data; - /** Test data length */ - size_t len; - /** Expected digest */ - uint8_t digest[SHA256_DIGEST_SIZE]; -}; +/* Empty test vector (digest obtained from "sha256sum /dev/null") */ +DIGEST_TEST ( sha256_empty, &sha256_algorithm, DIGEST_EMPTY, + DIGEST ( 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, + 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, + 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, + 0x1b, 0x78, 0x52, 0xb8, 0x55 ) ); -/** SHA-256 test vectors */ -static struct sha256_test_vector sha256_test_vectors[] = { - /* Empty test data - * - * Expected digest value obtained from "sha256sum /dev/null" - */ - { NULL, 0, - { 0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, - 0xc8, 0x99, 0x6f, 0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, - 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55 } }, - /* Test data and expected digests taken from the NIST - * Cryptographic Toolkit Algorithm Examples at - * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA256.pdf - */ - { "abc", 3, - { 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, - 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, - 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad } }, - { "abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq", 56, - { 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, 0xc0, 0x26, - 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, 0xe4, 0x59, 0x64, 0xff, - 0x21, 0x67, 0xf6, 0xec, 0xed, 0xd4, 0x19, 0xdb, 0x06, 0xc1 } }, -}; +/* NIST test vector "abc" */ +DIGEST_TEST ( sha256_nist_abc, &sha256_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, + 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23, 0xb0, 0x03, + 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, + 0x61, 0xf2, 0x00, 0x15, 0xad ) ); -/** SHA-256 test fragment lists */ -static struct digest_test_fragments sha256_test_fragments[] = { - { { 0, -1UL, } }, - { { 1, 1, 1, 1, 1, 1, 1, 1 } }, - { { 2, 0, 23, 4, 6, 1, 0 } }, -}; +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha256_nist_abc_opq, &sha256_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x24, 0x8d, 0x6a, 0x61, 0xd2, 0x06, 0x38, 0xb8, 0xe5, + 0xc0, 0x26, 0x93, 0x0c, 0x3e, 0x60, 0x39, 0xa3, 0x3c, + 0xe4, 0x59, 0x64, 0xff, 0x21, 0x67, 0xf6, 0xec, 0xed, + 0xd4, 0x19, 0xdb, 0x06, 0xc1 ) ); + +/* Empty test vector (digest obtained from "sha224sum /dev/null") */ +DIGEST_TEST ( sha224_empty, &sha224_algorithm, DIGEST_EMPTY, + DIGEST ( 0xd1, 0x4a, 0x02, 0x8c, 0x2a, 0x3a, 0x2b, 0xc9, 0x47, + 0x61, 0x02, 0xbb, 0x28, 0x82, 0x34, 0xc4, 0x15, 0xa2, + 0xb0, 0x1f, 0x82, 0x8e, 0xa6, 0x2a, 0xc5, 0xb3, 0xe4, + 0x2f ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha224_nist_abc, &sha224_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x23, 0x09, 0x7d, 0x22, 0x34, 0x05, 0xd8, 0x22, 0x86, + 0x42, 0xa4, 0x77, 0xbd, 0xa2, 0x55, 0xb3, 0x2a, 0xad, + 0xbc, 0xe4, 0xbd, 0xa0, 0xb3, 0xf7, 0xe3, 0x6c, 0x9d, + 0xa7 ) ); + +/* NIST test vector "abc...opq" */ +DIGEST_TEST ( sha224_nist_abc_opq, &sha224_algorithm, DIGEST_NIST_ABC_OPQ, + DIGEST ( 0x75, 0x38, 0x8b, 0x16, 0x51, 0x27, 0x76, 0xcc, 0x5d, + 0xba, 0x5d, 0xa1, 0xfd, 0x89, 0x01, 0x50, 0xb0, 0xc6, + 0x45, 0x5c, 0xb4, 0xf5, 0x8b, 0x19, 0x52, 0x52, 0x25, + 0x25 ) ); /** - * Perform SHA-256 self-test + * Perform SHA-256 family self-test * */ static void sha256_test_exec ( void ) { - struct digest_algorithm *digest = &sha256_algorithm; - struct sha256_test_vector *test; - unsigned long cost; - unsigned int i; - unsigned int j; - /* Correctness test */ - for ( i = 0 ; i < ( sizeof ( sha256_test_vectors ) / - sizeof ( sha256_test_vectors[0] ) ) ; i++ ) { - test = &sha256_test_vectors[i]; - /* Test with a single pass */ - digest_ok ( digest, NULL, test->data, test->len, test->digest ); - /* Test with fragment lists */ - for ( j = 0 ; j < ( sizeof ( sha256_test_fragments ) / - sizeof ( sha256_test_fragments[0] )); j++ ){ - digest_ok ( digest, &sha256_test_fragments[j], - test->data, test->len, test->digest ); - } - } + /* Correctness tests */ + digest_ok ( &sha256_empty ); + digest_ok ( &sha256_nist_abc ); + digest_ok ( &sha256_nist_abc_opq ); + digest_ok ( &sha224_empty ); + digest_ok ( &sha224_nist_abc ); + digest_ok ( &sha224_nist_abc_opq ); - /* Speed test */ - cost = digest_cost ( digest ); - DBG ( "SHA256 required %ld cycles per byte\n", cost ); + /* Speed tests */ + DBG ( "SHA256 required %ld cycles per byte\n", + digest_cost ( &sha256_algorithm ) ); + DBG ( "SHA224 required %ld cycles per byte\n", + digest_cost ( &sha224_algorithm ) ); } -/** SHA-256 self-test */ +/** SHA-256 family self-test */ struct self_test sha256_test __self_test = { .name = "sha256", .exec = sha256_test_exec, diff --git a/roms/ipxe/src/tests/sha512_test.c b/roms/ipxe/src/tests/sha512_test.c new file mode 100644 index 000000000..be530ebad --- /dev/null +++ b/roms/ipxe/src/tests/sha512_test.c @@ -0,0 +1,185 @@ +/* + * Copyright (C) 2015 Michael Brown <mbrown@fensystems.co.uk>. + * + * 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 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., 51 Franklin Street, Fifth Floor, Boston, MA + * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. + */ + +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); + +/** @file + * + * SHA-512 family tests + * + * NIST test vectors are taken from + * + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA384.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_256.pdf + * http://csrc.nist.gov/groups/ST/toolkit/documents/Examples/SHA512_224.pdf + * + */ + +/* Forcibly enable assertions */ +#undef NDEBUG + +#include <ipxe/sha512.h> +#include <ipxe/test.h> +#include "digest_test.h" + +/* Empty test vector (digest obtained from "sha512sum /dev/null") */ +DIGEST_TEST ( sha512_empty, &sha512_algorithm, DIGEST_EMPTY, + DIGEST ( 0xcf, 0x83, 0xe1, 0x35, 0x7e, 0xef, 0xb8, 0xbd, 0xf1, + 0x54, 0x28, 0x50, 0xd6, 0x6d, 0x80, 0x07, 0xd6, 0x20, + 0xe4, 0x05, 0x0b, 0x57, 0x15, 0xdc, 0x83, 0xf4, 0xa9, + 0x21, 0xd3, 0x6c, 0xe9, 0xce, 0x47, 0xd0, 0xd1, 0x3c, + 0x5d, 0x85, 0xf2, 0xb0, 0xff, 0x83, 0x18, 0xd2, 0x87, + 0x7e, 0xec, 0x2f, 0x63, 0xb9, 0x31, 0xbd, 0x47, 0x41, + 0x7a, 0x81, 0xa5, 0x38, 0x32, 0x7a, 0xf9, 0x27, 0xda, + 0x3e ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_nist_abc, &sha512_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xdd, 0xaf, 0x35, 0xa1, 0x93, 0x61, 0x7a, 0xba, 0xcc, + 0x41, 0x73, 0x49, 0xae, 0x20, 0x41, 0x31, 0x12, 0xe6, + 0xfa, 0x4e, 0x89, 0xa9, 0x7e, 0xa2, 0x0a, 0x9e, 0xee, + 0xe6, 0x4b, 0x55, 0xd3, 0x9a, 0x21, 0x92, 0x99, 0x2a, + 0x27, 0x4f, 0xc1, 0xa8, 0x36, 0xba, 0x3c, 0x23, 0xa3, + 0xfe, 0xeb, 0xbd, 0x45, 0x4d, 0x44, 0x23, 0x64, 0x3c, + 0xe8, 0x0e, 0x2a, 0x9a, 0xc9, 0x4f, 0xa5, 0x4c, 0xa4, + 0x9f ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_nist_abc_stu, &sha512_algorithm, DIGEST_NIST_ABC_STU, + DIGEST ( 0x8e, 0x95, 0x9b, 0x75, 0xda, 0xe3, 0x13, 0xda, 0x8c, + 0xf4, 0xf7, 0x28, 0x14, 0xfc, 0x14, 0x3f, 0x8f, 0x77, + 0x79, 0xc6, 0xeb, 0x9f, 0x7f, 0xa1, 0x72, 0x99, 0xae, + 0xad, 0xb6, 0x88, 0x90, 0x18, 0x50, 0x1d, 0x28, 0x9e, + 0x49, 0x00, 0xf7, 0xe4, 0x33, 0x1b, 0x99, 0xde, 0xc4, + 0xb5, 0x43, 0x3a, 0xc7, 0xd3, 0x29, 0xee, 0xb6, 0xdd, + 0x26, 0x54, 0x5e, 0x96, 0xe5, 0x5b, 0x87, 0x4b, 0xe9, + 0x09 ) ); + +/* Empty test vector (digest obtained from "sha384sum /dev/null") */ +DIGEST_TEST ( sha384_empty, &sha384_algorithm, DIGEST_EMPTY, + DIGEST ( 0x38, 0xb0, 0x60, 0xa7, 0x51, 0xac, 0x96, 0x38, 0x4c, + 0xd9, 0x32, 0x7e, 0xb1, 0xb1, 0xe3, 0x6a, 0x21, 0xfd, + 0xb7, 0x11, 0x14, 0xbe, 0x07, 0x43, 0x4c, 0x0c, 0xc7, + 0xbf, 0x63, 0xf6, 0xe1, 0xda, 0x27, 0x4e, 0xde, 0xbf, + 0xe7, 0x6f, 0x65, 0xfb, 0xd5, 0x1a, 0xd2, 0xf1, 0x48, + 0x98, 0xb9, 0x5b ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha384_nist_abc, &sha384_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0xcb, 0x00, 0x75, 0x3f, 0x45, 0xa3, 0x5e, 0x8b, 0xb5, + 0xa0, 0x3d, 0x69, 0x9a, 0xc6, 0x50, 0x07, 0x27, 0x2c, + 0x32, 0xab, 0x0e, 0xde, 0xd1, 0x63, 0x1a, 0x8b, 0x60, + 0x5a, 0x43, 0xff, 0x5b, 0xed, 0x80, 0x86, 0x07, 0x2b, + 0xa1, 0xe7, 0xcc, 0x23, 0x58, 0xba, 0xec, 0xa1, 0x34, + 0xc8, 0x25, 0xa7 ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha384_nist_abc_stu, &sha384_algorithm, DIGEST_NIST_ABC_STU, + DIGEST ( 0x09, 0x33, 0x0c, 0x33, 0xf7, 0x11, 0x47, 0xe8, 0x3d, + 0x19, 0x2f, 0xc7, 0x82, 0xcd, 0x1b, 0x47, 0x53, 0x11, + 0x1b, 0x17, 0x3b, 0x3b, 0x05, 0xd2, 0x2f, 0xa0, 0x80, + 0x86, 0xe3, 0xb0, 0xf7, 0x12, 0xfc, 0xc7, 0xc7, 0x1a, + 0x55, 0x7e, 0x2d, 0xb9, 0x66, 0xc3, 0xe9, 0xfa, 0x91, + 0x74, 0x60, 0x39 ) ); + +/* Empty test vector (digest obtained from "shasum -a 512256 /dev/null") */ +DIGEST_TEST ( sha512_256_empty, &sha512_256_algorithm, DIGEST_EMPTY, + DIGEST ( 0xc6, 0x72, 0xb8, 0xd1, 0xef, 0x56, 0xed, 0x28, 0xab, + 0x87, 0xc3, 0x62, 0x2c, 0x51, 0x14, 0x06, 0x9b, 0xdd, + 0x3a, 0xd7, 0xb8, 0xf9, 0x73, 0x74, 0x98, 0xd0, 0xc0, + 0x1e, 0xce, 0xf0, 0x96, 0x7a ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_256_nist_abc, &sha512_256_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x53, 0x04, 0x8e, 0x26, 0x81, 0x94, 0x1e, 0xf9, 0x9b, + 0x2e, 0x29, 0xb7, 0x6b, 0x4c, 0x7d, 0xab, 0xe4, 0xc2, + 0xd0, 0xc6, 0x34, 0xfc, 0x6d, 0x46, 0xe0, 0xe2, 0xf1, + 0x31, 0x07, 0xe7, 0xaf, 0x23 ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_256_nist_abc_stu, &sha512_256_algorithm, + DIGEST_NIST_ABC_STU, + DIGEST ( 0x39, 0x28, 0xe1, 0x84, 0xfb, 0x86, 0x90, 0xf8, 0x40, + 0xda, 0x39, 0x88, 0x12, 0x1d, 0x31, 0xbe, 0x65, 0xcb, + 0x9d, 0x3e, 0xf8, 0x3e, 0xe6, 0x14, 0x6f, 0xea, 0xc8, + 0x61, 0xe1, 0x9b, 0x56, 0x3a ) ); + +/* Empty test vector (digest obtained from "shasum -a 512224 /dev/null") */ +DIGEST_TEST ( sha512_224_empty, &sha512_224_algorithm, DIGEST_EMPTY, + DIGEST ( 0x6e, 0xd0, 0xdd, 0x02, 0x80, 0x6f, 0xa8, 0x9e, 0x25, + 0xde, 0x06, 0x0c, 0x19, 0xd3, 0xac, 0x86, 0xca, 0xbb, + 0x87, 0xd6, 0xa0, 0xdd, 0xd0, 0x5c, 0x33, 0x3b, 0x84, + 0xf4 ) ); + +/* NIST test vector "abc" */ +DIGEST_TEST ( sha512_224_nist_abc, &sha512_224_algorithm, DIGEST_NIST_ABC, + DIGEST ( 0x46, 0x34, 0x27, 0x0f, 0x70, 0x7b, 0x6a, 0x54, 0xda, + 0xae, 0x75, 0x30, 0x46, 0x08, 0x42, 0xe2, 0x0e, 0x37, + 0xed, 0x26, 0x5c, 0xee, 0xe9, 0xa4, 0x3e, 0x89, 0x24, + 0xaa ) ); + +/* NIST test vector "abc...stu" */ +DIGEST_TEST ( sha512_224_nist_abc_stu, &sha512_224_algorithm, + DIGEST_NIST_ABC_STU, + DIGEST ( 0x23, 0xfe, 0xc5, 0xbb, 0x94, 0xd6, 0x0b, 0x23, 0x30, + 0x81, 0x92, 0x64, 0x0b, 0x0c, 0x45, 0x33, 0x35, 0xd6, + 0x64, 0x73, 0x4f, 0xe4, 0x0e, 0x72, 0x68, 0x67, 0x4a, + 0xf9 ) ); + +/** + * Perform SHA-512 family self-test + * + */ +static void sha512_test_exec ( void ) { + + /* Correctness tests */ + digest_ok ( &sha512_empty ); + digest_ok ( &sha512_nist_abc ); + digest_ok ( &sha512_nist_abc_stu ); + digest_ok ( &sha384_empty ); + digest_ok ( &sha384_nist_abc ); + digest_ok ( &sha384_nist_abc_stu ); + digest_ok ( &sha512_256_empty ); + digest_ok ( &sha512_256_nist_abc ); + digest_ok ( &sha512_256_nist_abc_stu ); + digest_ok ( &sha512_224_empty ); + digest_ok ( &sha512_224_nist_abc ); + digest_ok ( &sha512_224_nist_abc_stu ); + + /* Speed tests */ + DBG ( "SHA512 required %ld cycles per byte\n", + digest_cost ( &sha512_algorithm ) ); + DBG ( "SHA384 required %ld cycles per byte\n", + digest_cost ( &sha384_algorithm ) ); + DBG ( "SHA512/256 required %ld cycles per byte\n", + digest_cost ( &sha512_256_algorithm ) ); + DBG ( "SHA512/224 required %ld cycles per byte\n", + digest_cost ( &sha512_224_algorithm ) ); +} + +/** SHA-512 family self-test */ +struct self_test sha512_test __self_test = { + .name = "sha512", + .exec = sha512_test_exec, +}; diff --git a/roms/ipxe/src/tests/string_test.c b/roms/ipxe/src/tests/string_test.c index 3b48d9f3d..4693b5f65 100644 --- a/roms/ipxe/src/tests/string_test.c +++ b/roms/ipxe/src/tests/string_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -31,7 +35,10 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <stdint.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> +#include <strings.h> +#include <ipxe/string.h> #include <ipxe/test.h> /** @@ -63,6 +70,18 @@ static void string_test_exec ( void ) { ok ( *(strchr ( "Testing", 'g' )) == 'g' ); ok ( strchr ( "Testing", 'x' ) == NULL ); + /* Test strrchr() */ + ok ( strrchr ( "", 'a' ) == NULL ); + ok ( *(strrchr ( "Haystack", 'a' )) == 'a' ); + ok ( *(strrchr ( "Haystack", 'k' )) == 'k' ); + ok ( strrchr ( "Haystack", 'x' ) == NULL ); + + /* Test memchr() */ + ok ( memchr ( "", '\0', 0 ) == NULL ); + ok ( *((uint8_t *)memchr ( "post\0null", 'l', 9 )) == 'l' ); + ok ( *((uint8_t *)memchr ( "post\0null", '\0', 9 )) == '\0' ); + ok ( memchr ( "thingy", 'z', 6 ) == NULL ); + /* Test strcmp() */ ok ( strcmp ( "", "" ) == 0 ); ok ( strcmp ( "Hello", "Hello" ) == 0 ); @@ -78,11 +97,31 @@ static void string_test_exec ( void ) { ok ( strncmp ( "Goodbye", "Goodbye world", 32 ) != 0 ); ok ( strncmp ( "Goodbye", "Goodbye world", 7 ) == 0 ); + /* Test strcasecmp() */ + ok ( strcasecmp ( "", "" ) == 0 ); + ok ( strcasecmp ( "Uncle Jack", "Uncle jack" ) == 0 ); + ok ( strcasecmp ( "Uncle Jack", "Uncle" ) != 0 ); + ok ( strcasecmp ( "Uncle", "Uncle Jack" ) != 0 ); + ok ( strcasecmp ( "not", "equal" ) != 0 ); + /* Test memcmp() */ ok ( memcmp ( "", "", 0 ) == 0 ); ok ( memcmp ( "Foo", "Foo", 3 ) == 0 ); ok ( memcmp ( "Foo", "Bar", 3 ) != 0 ); + /* Test strstr() */ + { + const char haystack[] = "find me!"; + char *found; + + found = strstr ( haystack, "find" ); + ok ( found == &haystack[0] ); + found = strstr ( haystack, "me" ); + ok ( found == &haystack[5] ); + found = strstr ( haystack, "me." ); + ok ( found == NULL ); + } + /* Test memset() */ { static uint8_t test[7] = { '>', 1, 1, 1, 1, 1, '<' }; @@ -154,6 +193,107 @@ static void string_test_exec ( void ) { ok ( strcmp ( dup, "hello" ) == 0 ); free ( dup ); } + + /* Test strcpy() */ + { + const char longer[7] = "copyme"; + const char shorter[3] = "hi"; + char dest[7]; + char *copy; + + copy = strcpy ( dest, longer ); + ok ( copy == dest ); + ok ( memcmp ( dest, longer, 7 ) == 0 ); + copy = strcpy ( dest, shorter ); + ok ( copy == dest ); + ok ( memcmp ( dest, shorter, 3 ) == 0 ); + ok ( memcmp ( ( dest + 3 ), ( longer + 3 ), 4 ) == 0 ); + } + + /* Test strncpy() */ + { + const char src[5] = "copy"; + const char orig[8] = { 'x', 'x', 'x', 'x', 'x', 'x', 'x', 'x' }; + const char zero[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; + char dest[8]; + char *copy; + + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 5 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 5 ) == 0 ); + ok ( memcmp ( dest + 5, orig + 5, 3 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 4 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 4 ) == 0 ); + ok ( memcmp ( dest + 4, orig + 4, 4 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, src, 8 ); + ok ( copy == dest ); + ok ( memcmp ( dest, src, 5 ) == 0 ); + ok ( memcmp ( dest + 5, zero + 5, 3 ) == 0 ); + memcpy ( dest, orig, sizeof ( dest ) ); + copy = strncpy ( dest, "", 8 ); + ok ( copy == dest ); + ok ( memcmp ( dest, zero, 8 ) == 0 ); + } + + /* Test strcat() */ + { + char buf[16] = "append"; + char *dest; + + dest = strcat ( buf, " this" ); + ok ( dest == buf ); + ok ( strcmp ( buf, "append this" ) == 0 ); + } + + /* Test digit_value() */ + { + unsigned int i; + char buf[2]; + for ( i = 0 ; i < 16 ; i++ ) { + snprintf ( buf, sizeof ( buf ), "%x", i ); + ok ( digit_value ( buf[0] ) == i ); + snprintf ( buf, sizeof ( buf ), "%X", i ); + ok ( digit_value ( buf[0] ) == i ); + } + ok ( digit_value ( 0 ) >= 16 ); + ok ( digit_value ( 9 ) >= 16 ); + ok ( digit_value ( '0' - 1 ) >= 16 ); + ok ( digit_value ( '9' + 1 ) >= 16 ); + ok ( digit_value ( 'A' - 1 ) >= 16 ); + ok ( digit_value ( 'F' + 1 ) >= 16 ); + ok ( digit_value ( 'a' - 1 ) >= 16 ); + ok ( digit_value ( 'f' + 1 ) >= 16 ); + } + + /* Test strtoul() */ + ok ( strtoul ( "12345", NULL, 0 ) == 12345UL ); + ok ( strtoul ( " 741", NULL, 10 ) == 741UL ); + ok ( strtoul ( " 555a", NULL, 0 ) == 555UL ); + ok ( strtoul ( " 555a", NULL, 16 ) == 0x555aUL ); + ok ( strtoul ( "-12", NULL, 0 ) == -12UL ); + ok ( strtoul ( "+3", NULL, 0 ) == 3UL ); + ok ( strtoul ( "721", NULL, 0 ) == 721UL ); + ok ( strtoul ( "721", NULL, 8 ) == 0721UL ); + ok ( strtoul ( "0721", NULL, 0 ) == 0721UL ); + ok ( strtoul ( "", NULL, 0 ) == 0UL ); + ok ( strtoul ( "\t0xcAfe", NULL, 0 ) == 0xcafeUL ); + ok ( strtoul ( "0xffffffff", NULL, 0 ) == 0xffffffffUL ); + { + static const char string[] = "123aHa.world"; + char *endp; + ok ( strtoul ( string, &endp, 0 ) == 123UL ); + ok ( endp == &string[3] ); + ok ( strtoul ( string, &endp, 16 ) == 0x123aUL ); + ok ( endp == &string[4] ); + ok ( strtoul ( string, &endp, 26 ) == + ( ( ( ( ( 1 * 26 + 2 ) * 26 + 3 ) * 26 + 10 ) * 26 + + 17 ) * 26 + 10 ) ); + ok ( endp == &string[6] ); + } } /** String self-test */ diff --git a/roms/ipxe/src/tests/tcpip_test.c b/roms/ipxe/src/tests/tcpip_test.c index 00c88ae32..759f886bc 100644 --- a/roms/ipxe/src/tests/tcpip_test.c +++ b/roms/ipxe/src/tests/tcpip_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/test.c b/roms/ipxe/src/tests/test.c index c05e72a76..67bd4cf89 100644 --- a/roms/ipxe/src/tests/test.c +++ b/roms/ipxe/src/tests/test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/tests.c b/roms/ipxe/src/tests/tests.c index 2b4b78c7c..54ce86677 100644 --- a/roms/ipxe/src/tests/tests.c +++ b/roms/ipxe/src/tests/tests.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -26,6 +30,8 @@ FILE_LICENCE ( GPL2_OR_LATER ); */ /* Drag in all applicable self-tests */ +PROVIDE_REQUIRING_SYMBOL(); +REQUIRE_OBJECT ( memset_test ); REQUIRE_OBJECT ( memcpy_test ); REQUIRE_OBJECT ( string_test ); REQUIRE_OBJECT ( math_test ); @@ -37,12 +43,14 @@ REQUIRE_OBJECT ( base16_test ); REQUIRE_OBJECT ( settings_test ); REQUIRE_OBJECT ( time_test ); REQUIRE_OBJECT ( tcpip_test ); +REQUIRE_OBJECT ( ipv4_test ); REQUIRE_OBJECT ( ipv6_test ); REQUIRE_OBJECT ( crc32_test ); REQUIRE_OBJECT ( md5_test ); REQUIRE_OBJECT ( sha1_test ); REQUIRE_OBJECT ( sha256_test ); -REQUIRE_OBJECT ( aes_cbc_test ); +REQUIRE_OBJECT ( sha512_test ); +REQUIRE_OBJECT ( aes_test ); REQUIRE_OBJECT ( hmac_drbg_test ); REQUIRE_OBJECT ( hash_df_test ); REQUIRE_OBJECT ( bigint_test ); @@ -56,3 +64,6 @@ REQUIRE_OBJECT ( png_test ); REQUIRE_OBJECT ( dns_test ); REQUIRE_OBJECT ( uri_test ); REQUIRE_OBJECT ( profile_test ); +REQUIRE_OBJECT ( setjmp_test ); +REQUIRE_OBJECT ( pccrc_test ); +REQUIRE_OBJECT ( linebuf_test ); diff --git a/roms/ipxe/src/tests/time_test.c b/roms/ipxe/src/tests/time_test.c index 28acebee6..3bf01dd1d 100644 --- a/roms/ipxe/src/tests/time_test.c +++ b/roms/ipxe/src/tests/time_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/uri_test.c b/roms/ipxe/src/tests/uri_test.c index 14f1b4ad0..da7fb8abe 100644 --- a/roms/ipxe/src/tests/uri_test.c +++ b/roms/ipxe/src/tests/uri_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -66,6 +70,8 @@ struct uri_resolve_test { struct uri_tftp_test { /** Next-server address */ struct in_addr next_server; + /** Port number */ + unsigned int port; /** Filename */ const char *filename; /** URI */ @@ -330,7 +336,7 @@ static void uri_tftp_okx ( struct uri_tftp_test *test, const char *file, size_t len; /* Construct URI */ - uri = tftp_uri ( test->next_server, test->filename ); + uri = tftp_uri ( test->next_server, test->port, test->filename ); okx ( uri != NULL, file, line ); if ( uri ) { uri_okx ( uri, &test->uri, file, line ); @@ -674,7 +680,7 @@ static struct uri_resolve_test uri_fragment = { /** TFTP URI with absolute path */ static struct uri_tftp_test uri_tftp_absolute = { - { .s_addr = htonl ( 0xc0a80002 ) /* 192.168.0.2 */ }, + { .s_addr = htonl ( 0xc0a80002 ) /* 192.168.0.2 */ }, 0, "/absolute/path", { .scheme = "tftp", @@ -686,7 +692,7 @@ static struct uri_tftp_test uri_tftp_absolute = { /** TFTP URI with relative path */ static struct uri_tftp_test uri_tftp_relative = { - { .s_addr = htonl ( 0xc0a80003 ) /* 192.168.0.3 */ }, + { .s_addr = htonl ( 0xc0a80003 ) /* 192.168.0.3 */ }, 0, "relative/path", { .scheme = "tftp", @@ -698,7 +704,7 @@ static struct uri_tftp_test uri_tftp_relative = { /** TFTP URI with path containing special characters */ static struct uri_tftp_test uri_tftp_icky = { - { .s_addr = htonl ( 0x0a000006 ) /* 10.0.0.6 */ }, + { .s_addr = htonl ( 0x0a000006 ) /* 10.0.0.6 */ }, 0, "C:\\tftpboot\\icky#path", { .scheme = "tftp", @@ -708,6 +714,19 @@ static struct uri_tftp_test uri_tftp_icky = { "tftp://10.0.0.6/C%3A\\tftpboot\\icky%23path", }; +/** TFTP URI with custom port */ +static struct uri_tftp_test uri_tftp_port = { + { .s_addr = htonl ( 0xc0a80001 ) /* 192.168.0.1 */ }, 4069, + "/another/path", + { + .scheme = "tftp", + .host = "192.168.0.1", + .port = "4069", + .path = "/another/path", + }, + "tftp://192.168.0.1:4069/another/path", +}; + /** Current working URI test */ static struct uri_churi_test uri_churi[] = { { @@ -842,6 +861,7 @@ static void uri_test_exec ( void ) { uri_tftp_ok ( &uri_tftp_absolute ); uri_tftp_ok ( &uri_tftp_relative ); uri_tftp_ok ( &uri_tftp_icky ); + uri_tftp_ok ( &uri_tftp_port ); /* Current working URI tests */ uri_churi_ok ( uri_churi ); diff --git a/roms/ipxe/src/tests/vsprintf_test.c b/roms/ipxe/src/tests/vsprintf_test.c index 11512ec8e..0ad4f1c56 100644 --- a/roms/ipxe/src/tests/vsprintf_test.c +++ b/roms/ipxe/src/tests/vsprintf_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/tests/x509_test.c b/roms/ipxe/src/tests/x509_test.c index fd39e12d2..658d5247c 100644 --- a/roms/ipxe/src/tests/x509_test.c +++ b/roms/ipxe/src/tests/x509_test.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * @@ -1105,6 +1109,7 @@ struct self_test x509_test __self_test = { }; /* Drag in algorithms required for tests */ +REQUIRING_SYMBOL ( x509_test ); REQUIRE_OBJECT ( rsa ); REQUIRE_OBJECT ( sha1 ); REQUIRE_OBJECT ( sha256 ); diff --git a/roms/ipxe/src/usr/autoboot.c b/roms/ipxe/src/usr/autoboot.c index 47476ae40..912543828 100644 --- a/roms/ipxe/src/usr/autoboot.c +++ b/roms/ipxe/src/usr/autoboot.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> @@ -42,6 +46,7 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <usr/prompt.h> #include <usr/autoboot.h> #include <config/general.h> +#include <config/branding.h> /** @file * @@ -101,7 +106,7 @@ static struct uri * parse_next_server_and_filename ( struct in_addr next_server, /* Construct a TFTP URI for the filename, if applicable */ if ( next_server.s_addr && filename[0] && ! uri_is_absolute ( uri ) ) { uri_put ( uri ); - uri = tftp_uri ( next_server, filename ); + uri = tftp_uri ( next_server, 0, filename ); if ( ! uri ) return NULL; } @@ -173,6 +178,7 @@ int uriboot ( struct uri *filename, struct uri *root_path, int drive, if ( filename ) { if ( ( rc = imgdownload ( filename, 0, &image ) ) != 0 ) goto err_download; + imgstat ( image ); image->flags |= IMAGE_AUTO_UNREGISTER; if ( ( rc = image_exec ( image ) ) != 0 ) { printf ( "Could not boot image: %s\n", @@ -434,9 +440,14 @@ int netboot ( struct net_device *netdev ) { * @ret is_autoboot Network device matches the autoboot device */ static int is_autoboot_busloc ( struct net_device *netdev ) { + struct device *dev; - return ( ( netdev->dev->desc.bus_type == autoboot_desc.bus_type ) && - ( netdev->dev->desc.location == autoboot_desc.location ) ); + for ( dev = netdev->dev ; dev ; dev = dev->parent ) { + if ( ( dev->desc.bus_type == autoboot_desc.bus_type ) && + ( dev->desc.location == autoboot_desc.location ) ) + return 1; + } + return 0; } /** @@ -522,7 +533,8 @@ static int shell_banner ( void ) { /* Prompt user */ printf ( "\n" ); - return ( prompt ( "Press Ctrl-B for the iPXE command line...", + return ( prompt ( "Press Ctrl-B for the " PRODUCT_SHORT_NAME + " command line...", ( ( BANNER_TIMEOUT * TICKS_PER_SEC ) / 10 ), CTRL_B ) == 0 ); } @@ -531,28 +543,29 @@ static int shell_banner ( void ) { * Main iPXE flow of execution * * @v netdev Network device, or NULL + * @ret rc Return status code */ -void ipxe ( struct net_device *netdev ) { +int ipxe ( struct net_device *netdev ) { struct feature *feature; struct image *image; char *scriptlet; + int rc; /* * Print welcome banner * * * If you wish to brand this build of iPXE, please do so by - * defining the string PRODUCT_NAME in config/general.h. + * defining the string PRODUCT_NAME in config/branding.h. * * While nothing in the GPL prevents you from removing all * references to iPXE or http://ipxe.org, we prefer you not to * do so. * */ - printf ( NORMAL "\n\n%s\n" BOLD "iPXE %s" - NORMAL " -- Open Source Network Boot Firmware -- " - CYAN "http://ipxe.org" NORMAL "\n" - "Features:", product_name, product_version ); + printf ( NORMAL "\n\n" PRODUCT_NAME "\n" BOLD PRODUCT_SHORT_NAME " %s" + NORMAL " -- " PRODUCT_TAG_LINE " -- " + CYAN PRODUCT_URI NORMAL "\nFeatures:", product_version ); for_each_table_entry ( feature, FEATURES ) printf ( " %s", feature->name ); printf ( "\n" ); @@ -560,28 +573,30 @@ void ipxe ( struct net_device *netdev ) { /* Boot system */ if ( ( image = first_image() ) != NULL ) { /* We have an embedded image; execute it */ - image_exec ( image ); + return image_exec ( image ); } else if ( shell_banner() ) { /* User wants shell; just give them a shell */ - shell(); + return shell(); } else { fetch_string_setting_copy ( NULL, &scriptlet_setting, &scriptlet ); if ( scriptlet ) { /* User has defined a scriptlet; execute it */ - system ( scriptlet ); + rc = system ( scriptlet ); free ( scriptlet ); + return rc; } else { /* Try booting. If booting fails, offer the * user another chance to enter the shell. */ if ( netdev ) { - netboot ( netdev ); + rc = netboot ( netdev ); } else { - autoboot(); + rc = autoboot(); } if ( shell_banner() ) - shell(); + rc = shell(); + return rc; } } } diff --git a/roms/ipxe/src/usr/dhcpmgmt.c b/roms/ipxe/src/usr/dhcpmgmt.c index 23982b19c..dcb360b23 100644 --- a/roms/ipxe/src/usr/dhcpmgmt.c +++ b/roms/ipxe/src/usr/dhcpmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> diff --git a/roms/ipxe/src/usr/fcmgmt.c b/roms/ipxe/src/usr/fcmgmt.c index a30f37a71..6f626143f 100644 --- a/roms/ipxe/src/usr/fcmgmt.c +++ b/roms/ipxe/src/usr/fcmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> diff --git a/roms/ipxe/src/usr/ifmgmt.c b/roms/ipxe/src/usr/ifmgmt.c index 3d05895c2..aefdaa45d 100644 --- a/roms/ipxe/src/usr/ifmgmt.c +++ b/roms/ipxe/src/usr/ifmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <string.h> #include <stdio.h> @@ -99,11 +103,12 @@ static void ifstat_errors ( struct net_device_stats *stats, */ void ifstat ( struct net_device *netdev ) { printf ( "%s: %s using %s on %s (%s)\n" - " [Link:%s, TX:%d TXE:%d RX:%d RXE:%d]\n", + " [Link:%s%s, TX:%d TXE:%d RX:%d RXE:%d]\n", netdev->name, netdev_addr ( netdev ), netdev->dev->driver_name, netdev->dev->name, ( netdev_is_open ( netdev ) ? "open" : "closed" ), ( netdev_link_ok ( netdev ) ? "up" : "down" ), + ( netdev_link_blocked ( netdev ) ? " (blocked)" : "" ), netdev->tx_stats.good, netdev->tx_stats.bad, netdev->rx_stats.good, netdev->rx_stats.bad ); if ( ! netdev_link_ok ( netdev ) ) { diff --git a/roms/ipxe/src/usr/imgmgmt.c b/roms/ipxe/src/usr/imgmgmt.c index c9c571640..352dd0242 100644 --- a/roms/ipxe/src/usr/imgmgmt.c +++ b/roms/ipxe/src/usr/imgmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/usr/imgtrust.c b/roms/ipxe/src/usr/imgtrust.c index da7ff2ef0..a269833a6 100644 --- a/roms/ipxe/src/usr/imgtrust.c +++ b/roms/ipxe/src/usr/imgtrust.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdlib.h> #include <errno.h> diff --git a/roms/ipxe/src/usr/ipstat.c b/roms/ipxe/src/usr/ipstat.c index 95ad799dc..0f09cc2ff 100644 --- a/roms/ipxe/src/usr/ipstat.c +++ b/roms/ipxe/src/usr/ipstat.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <ipxe/ipstat.h> diff --git a/roms/ipxe/src/usr/lotest.c b/roms/ipxe/src/usr/lotest.c index ad7a2fad7..6b328713c 100644 --- a/roms/ipxe/src/usr/lotest.c +++ b/roms/ipxe/src/usr/lotest.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/usr/neighmgmt.c b/roms/ipxe/src/usr/neighmgmt.c index e4d21a208..9fd88f82b 100644 --- a/roms/ipxe/src/usr/neighmgmt.c +++ b/roms/ipxe/src/usr/neighmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <ipxe/neighbour.h> diff --git a/roms/ipxe/src/usr/pingmgmt.c b/roms/ipxe/src/usr/pingmgmt.c index 16b3ec994..bb33c5d47 100644 --- a/roms/ipxe/src/usr/pingmgmt.c +++ b/roms/ipxe/src/usr/pingmgmt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdio.h> diff --git a/roms/ipxe/src/usr/profstat.c b/roms/ipxe/src/usr/profstat.c index 991427473..d80fa26b2 100644 --- a/roms/ipxe/src/usr/profstat.c +++ b/roms/ipxe/src/usr/profstat.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <ipxe/profile.h> diff --git a/roms/ipxe/src/usr/prompt.c b/roms/ipxe/src/usr/prompt.c index 957b4ab3d..fca0a157c 100644 --- a/roms/ipxe/src/usr/prompt.c +++ b/roms/ipxe/src/usr/prompt.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); /** @file * diff --git a/roms/ipxe/src/usr/pxemenu.c b/roms/ipxe/src/usr/pxemenu.c index b69905df1..2d05d3f51 100644 --- a/roms/ipxe/src/usr/pxemenu.c +++ b/roms/ipxe/src/usr/pxemenu.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdint.h> #include <stdlib.h> diff --git a/roms/ipxe/src/usr/route.c b/roms/ipxe/src/usr/route.c index ba4cc3221..690ba3b6b 100644 --- a/roms/ipxe/src/usr/route.c +++ b/roms/ipxe/src/usr/route.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <ipxe/netdevice.h> #include <usr/route.h> @@ -42,3 +46,7 @@ void route ( void ) { } } } + +/* Drag in routing management configuration */ +REQUIRING_SYMBOL ( route ); +REQUIRE_OBJECT ( config_route ); diff --git a/roms/ipxe/src/usr/route_ipv4.c b/roms/ipxe/src/usr/route_ipv4.c index b4d1b7bf3..6260335ac 100644 --- a/roms/ipxe/src/usr/route_ipv4.c +++ b/roms/ipxe/src/usr/route_ipv4.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <ipxe/netdevice.h> diff --git a/roms/ipxe/src/usr/route_ipv6.c b/roms/ipxe/src/usr/route_ipv6.c index 6045f85bb..9e94b4a15 100644 --- a/roms/ipxe/src/usr/route_ipv6.c +++ b/roms/ipxe/src/usr/route_ipv6.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stdio.h> #include <ipxe/netdevice.h> diff --git a/roms/ipxe/src/usr/sync.c b/roms/ipxe/src/usr/sync.c index f7a04c44c..f599588ae 100644 --- a/roms/ipxe/src/usr/sync.c +++ b/roms/ipxe/src/usr/sync.c @@ -15,9 +15,13 @@ * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA * 02110-1301, USA. + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements. */ -FILE_LICENCE ( GPL2_OR_LATER ); +FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL ); #include <stddef.h> #include <ipxe/job.h> diff --git a/roms/ipxe/src/util/Option/ROM.pm b/roms/ipxe/src/util/Option/ROM.pm index 6c396730e..232cf16b8 100644 --- a/roms/ipxe/src/util/Option/ROM.pm +++ b/roms/ipxe/src/util/Option/ROM.pm @@ -529,6 +529,26 @@ sub new { return $hash; } +sub device_list { + my $hash = shift; + my $self = tied(%$hash); + + my $device_list = $hash->{device_list}; + return undef unless $device_list; + + my @ids; + my $offset = ( $self->{offset} + $device_list ); + while ( 1 ) { + my $raw = substr ( ${$self->{data}}, $offset, 2 ); + my $id = unpack ( "S", $raw ); + last unless $id; + push @ids, $id; + $offset += 2; + } + + return @ids; +} + ############################################################################## # # Option::ROM::PnP diff --git a/roms/ipxe/src/util/disrom.pl b/roms/ipxe/src/util/disrom.pl index 574957acd..920a86b24 100755 --- a/roms/ipxe/src/util/disrom.pl +++ b/roms/ipxe/src/util/disrom.pl @@ -55,6 +55,10 @@ do { printf " %-16s %s\n", "Signature:", $pci->{signature}; printf " %-16s 0x%04x\n", "Vendor ID:", $pci->{vendor_id}; printf " %-16s 0x%04x\n", "Device ID:", $pci->{device_id}; + if ( $pci->{device_list} ) { + printf " %-16s %s\n", "Device list:", + ( join ( ", ", map { sprintf "0x%04x", $_ } $pci->device_list ) ); + } printf " %-16s 0x%02x%02x%02x\n", "Device class:", $pci->{base_class}, $pci->{sub_class}, $pci->{prog_intf}; printf " %-16s 0x%04x (%d)\n", "Image length:", diff --git a/roms/ipxe/src/util/elf2efi.c b/roms/ipxe/src/util/elf2efi.c index 45d539574..e68fa5d14 100644 --- a/roms/ipxe/src/util/elf2efi.c +++ b/roms/ipxe/src/util/elf2efi.c @@ -478,11 +478,13 @@ static void process_reloc ( bfd *bfd __attribute__ (( unused )), /* Skip absolute symbols; the symbol value won't * change when the object is loaded. */ + } else if ( ( strcmp ( howto->name, "R_386_NONE" ) == 0 ) || + ( strcmp ( howto->name, "R_X86_64_NONE" ) == 0 ) ) { + /* Ignore dummy relocations used by REQUIRE_SYMBOL() */ } else if ( strcmp ( howto->name, "R_X86_64_64" ) == 0 ) { /* Generate an 8-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 8 ); - } else if ( ( strcmp ( howto->name, "R_386_32" ) == 0 ) || - ( strcmp ( howto->name, "R_X86_64_32" ) == 0 ) ) { + } else if ( strcmp ( howto->name, "R_386_32" ) == 0 ) { /* Generate a 4-byte PE relocation */ generate_pe_reloc ( pe_reltab, offset, 4 ); } else if ( strcmp ( howto->name, "R_386_16" ) == 0 ) { diff --git a/roms/ipxe/src/util/licence.pl b/roms/ipxe/src/util/licence.pl index 0e43c7b4c..79e70fd65 100755 --- a/roms/ipxe/src/util/licence.pl +++ b/roms/ipxe/src/util/licence.pl @@ -37,6 +37,7 @@ my $known_licences = { desc => "GPL version 2 (or, at your option, any later version)", can_subsume => { gpl_any => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -49,6 +50,7 @@ my $known_licences = { can_subsume => { gpl_any => 1, gpl2_or_later => 1, + gpl2_or_later_or_ubdl => 1, public_domain => 1, bsd3 => 1, bsd2 => 1, @@ -56,6 +58,17 @@ my $known_licences = { isc => 1, }, }, + gpl2_or_later_or_ubdl => { + desc => ( "GPL version 2 (or, at your option, any later version) or ". + "Unmodified Binary Distribution Licence" ), + can_subsume => { + public_domain => 1, + bsd3 => 1, + bsd2 => 1, + mit => 1, + isc => 1, + }, + }, public_domain => { desc => "Public Domain", can_subsume => {}, diff --git a/roms/ipxe/src/util/parserom.pl b/roms/ipxe/src/util/parserom.pl index e278e6336..28df60652 100755 --- a/roms/ipxe/src/util/parserom.pl +++ b/roms/ipxe/src/util/parserom.pl @@ -1,66 +1,260 @@ #!/usr/bin/env perl # -# Parse PCI_ROM and ISA_ROM entries from a source file on stdin and -# output the relevant Makefile variable definitions to stdout +# Parse PCI_ROM and ISA_ROM entries from source file(s) specified as +# arguments and output the relevant Makefile rules to STDOUT. # -# Based upon portions of Ken Yap's genrules.pl +# Originally based on portions of Ken Yap's genrules.pl. Completely +# rewritten by Robin Smidsrød to be more maintainable. use strict; use warnings; +use Getopt::Long; -die "Syntax: $0 driver_source.c" unless @ARGV == 1; -my $source = shift; -open DRV, "<$source" or die "Could not open $source: $!\n"; +# Parse command-line options +my @exclude_driver_classes = (); +my @exclude_drivers = (); +my $debug = 0; +my $help = 0; +GetOptions( + "exclude-driver-class=s" => \@exclude_driver_classes, + "exclude-driver=s" => \@exclude_drivers, + "debug" => \$debug, + "help" => \$help, +); -( my $family, my $driver_name ) = ( $source =~ /^(.*?([^\/]+))\..$/ ) - or die "Could not parse source file name \"$source\"\n"; +# Convert exclution arrays to lookup tables +my $exclude_driver_class_map = { map { $_ => 1 } @exclude_driver_classes }; +my $exclude_driver_map = { map { $_ => 1 } @exclude_drivers }; -my $printed_family; +# Ensure STDOUT and STDERR are synchronized if debugging +if ( $debug ) { + STDOUT->autoflush(1); + STDERR->autoflush(1); +} + +# Compile regular expressions here for slight performance boost +my %RE = ( + 'parse_driver_class' => qr{ drivers/ (\w+?) / }x, + 'parse_family' => qr{^ (?:\./)? (.*) \..+? $}x, + 'find_rom_line' => qr/^ \s* ( (PCI|ISA)_ROM \s* \( \s* (.*?) ) $/x, + 'extract_pci_id' => qr/^ \s* 0x([0-9A-Fa-f]{4}) \s* ,? \s* (.*) $/x, + 'extract_quoted_string' => qr/^ \s* \" ([^\"]*?) \" \s* ,? \s* (.*) $/x, +); + +# Show help if required arguments are missing or help was requested +show_usage_and_exit() if $help or @ARGV < 1; + +# Process each source file specified +process_source_file($_) for @ARGV; + +exit; + +sub show_usage_and_exit { + print STDERR <<"EOM"; +Syntax: $0 [<options>] <source-file> [<source-file>] +Options: + --exclude-driver-class Exclude specified driver classes + --exclude-driver Exclude specified drivers + --debug Output debug information on STDERR + --help This help information +EOM + exit 1; +} + +# Figure out if source file is a driver and look for ROM declarations +sub process_source_file { + my ($source_file) = @_; + return unless defined $source_file; + return unless length $source_file; + my $state = { 'source_file' => $source_file }; + log_debug("SOURCE_FILE", $state->{source_file}); + # Skip source files that aren't drivers + parse_driver_class( $state ); + unless ( $state->{'driver_class'} ) { + log_debug("SKIP_NOT_DRIVER", $state->{source_file} ); + return; + } + # Skip source files with driver classes that are explicitly excluded + if ( $exclude_driver_class_map->{ $state->{'driver_class'} } ) { + log_debug("SKIP_EXCL_CLASS", $state->{'driver_class'} ); + return; + } + # Skip source files without driver information + parse_family( $state ); + parse_driver_name( $state ); + unless ( $state->{'family'} and $state->{'driver_name'} ) { + log_debug("SKIP_NO_DRV_INFO", $state->{source_file} ); + return; + } + # Skip source files with drivers that are explicitly excluded + if ( $exclude_driver_map->{ $state->{'driver_name'} } ) { + log_debug("SKIP_EXCL_DRV", $state->{'driver_name'} ); + return; + } + # Iterate through lines in source files looking for ROM declarations + # and # output Makefile rules + open( my $fh, "<", $state->{'source_file'} ) + or die "Couldn't open $state->{source_file}: $!\n"; + while (<$fh>) { + process_rom_decl($state, $1, $2, $3) if m/$RE{find_rom_line}/; + } + close($fh) or die "Couldn't close $source_file: $!\n"; + return 1; +} + +# Verify that the found ROM declaration is sane and dispatch to the right +# handler depending on type +sub process_rom_decl { + my ($state, $rom_line, $rom_type, $rom_decl) = @_; + return unless defined $rom_line; + return unless length $rom_line; + log_debug("ROM_LINE", $rom_line); + return unless defined $rom_type; + return unless length $rom_type; + log_debug("ROM_TYPE", $rom_type); + $state->{'type'} = lc $rom_type; + return process_pci_rom($state, $rom_decl) if $rom_type eq "PCI"; + return process_isa_rom($state, $rom_decl) if $rom_type eq "ISA"; + return; +} + +# Extract values from PCI_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_pci_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $vendor, $decl) = extract_pci_id($decl, 'PCI_VENDOR'); + (my $device, $decl) = extract_pci_id($decl, 'PCI_DEVICE'); + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $vendor and $device and $image and $desc ) { + print_make_rules( $state, "${vendor}${device}", $desc, $vendor, $device ); + print_make_rules( $state, $image, $desc, $vendor, $device, 1 ); + } + else { + log_debug("WARNING", "Malformed PCI_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} + +# Extract values from ISA_ROM declaration lines and dispatch to +# Makefile rule generator +sub process_isa_rom { + my ($state, $decl) = @_; + return unless defined $decl; + return unless length $decl; + (my $image, $decl) = extract_quoted_string($decl, 'IMAGE'); + (my $desc, $decl) = extract_quoted_string($decl, 'DESCRIPTION'); + if ( $image and $desc ) { + print_make_rules( $state, $image, $desc ); + } + else { + log_debug("WARNING", "Malformed ISA_ROM macro on line $. of $state->{source_file}"); + } + return 1; +} -sub rom { - ( my $type, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; - my $ids = $vendor ? "$vendor,$device" : "-"; - unless ( $printed_family ) { +# Output Makefile rules for the specified ROM declarations +sub print_make_rules { + my ( $state, my $image, my $desc, my $vendor, my $device, my $dup ) = @_; + unless ( $state->{'is_header_printed'} ) { + print "# NIC\t\n"; + print "# NIC\tfamily\t$state->{family}\n"; + print "DRIVERS_$state->{driver_class} += $state->{driver_name}\n"; + print "DRIVERS += $state->{driver_name}\n"; + print "\n"; + $state->{'is_header_printed'} = 1; + } + return if $vendor and ( $vendor eq "ffff" or $device eq "ffff" ); + my $ids = $vendor ? "$vendor,$device" : "-"; + print "# NIC\t$image\t$ids\t$desc\n"; + print "DRIVER_$image = $state->{driver_name}\n"; + print "ROM_TYPE_$image = $state->{type}\n"; + print "ROM_DESCRIPTION_$image = \"$desc\"\n"; + print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; + print "PCI_DEVICE_$image = 0x$device\n" if $device; + print "ROMS += $image\n" unless $dup; + print "ROMS_$state->{driver_name} += $image\n" unless $dup; print "\n"; - print "# NIC\t\n"; - print "# NIC\tfamily\t$family\n"; - print "DRIVERS += $driver_name\n"; - $printed_family = 1; - } - print "\n"; - return if ( $vendor && ( ( $vendor eq "ffff" ) || ( $device eq "ffff" ) ) ); - print "# NIC\t$image\t$ids\t$desc\n"; - print "DRIVER_$image = $driver_name\n"; - print "ROM_TYPE_$image = $type\n"; - print "ROM_DESCRIPTION_$image = \"$desc\"\n"; - print "PCI_VENDOR_$image = 0x$vendor\n" if $vendor; - print "PCI_DEVICE_$image = 0x$device\n" if $device; - print "ROMS += $image\n" unless $dup; - print "ROMS_$driver_name += $image\n" unless $dup; + return 1; +} + +# Driver class is whatever comes after the "drivers" part of the filename (relative path) +sub parse_driver_class { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_driver_class}/ ) { + log_debug("DRIVER_CLASS", $1); + $state->{'driver_class'} = $1; + } + return; +} + +# Family name is filename (relative path) without extension +sub parse_family { + my ($state) = @_; + my $filename = $state->{'source_file'}; + return unless defined $filename; + return unless length $filename; + if ( $filename =~ m/$RE{parse_family}/ ) { + log_debug("FAMILY", $1); + $state->{'family'} = $1; + } + return; +} + +# Driver name is last part of family name +sub parse_driver_name { + my ($state) = @_; + my $family = $state->{'family'}; + return unless defined $family; + return unless length $family; + my @parts = split "/", $family; + $state->{'driver_name'} = $parts[-1]; + log_debug("DRIVER", $state->{'driver_name'}); + return; } -while ( <DRV> ) { - next unless /(PCI|ISA)_ROM\s*\(/; - - if ( /^\s*PCI_ROM\s*\( - \s*0x([0-9A-Fa-f]{4})\s*, # PCI vendor - \s*0x([0-9A-Fa-f]{4})\s*, # PCI device - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s*, # Description - \s*.*\s* # Driver data - \)/x ) { - ( my $vendor, my $device, my $image, my $desc ) = ( lc $1, lc $2, $3, $4 ); - rom ( "pci", lc "${vendor}${device}", $desc, $vendor, $device ); - rom ( "pci", $image, $desc, $vendor, $device, 1 ); - } elsif ( /^\s*ISA_ROM\s*\( - \s*\"([^\"]*)\"\s*, # Image - \s*\"([^\"]*)\"\s* # Description - \)/x ) { - ( my $image, my $desc ) = ( $1, $2 ); - rom ( "isa", $image, $desc ); - } else { - warn "Malformed PCI_ROM or ISA_ROM macro on line $. of $source\n"; - } +# Extract a PCI vendor/device ID e.g. 0x8086, possibly followed by a comma +# Should always be 4-digit lower-case hex number +sub extract_pci_id { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_pci_id}/ ) { + my $id = lc $1; + log_debug($label, $id); + return $id, $2; + } + return "", $str; } -close DRV; +# Extract a double-quoted string, possibly followed by a comma +sub extract_quoted_string { + my ($str, $label) = @_; + return "", $str unless defined $str; + return "", $str unless length $str; + if ( $str =~ m/$RE{extract_quoted_string}/ ) { + log_debug($label, $1); + return $1, $2; + } + return "", $str; +} + +# Output debug info to STDERR (off by default) +sub log_debug { + my ($label, $str) = @_; + return unless $debug; + return unless defined $str; + print STDERR "\n" if $label eq 'SOURCE_FILE'; + print STDERR "="; + if ( defined $label ) { + my $pad_count = 16 - length $label; + print STDERR $label . ":" . ( " " x $pad_count ); + } + print STDERR $str . "\n"; + return; +} diff --git a/roms/ipxe/src/util/relicense.pl b/roms/ipxe/src/util/relicense.pl new file mode 100755 index 000000000..41954c1b3 --- /dev/null +++ b/roms/ipxe/src/util/relicense.pl @@ -0,0 +1,169 @@ +#!/usr/bin/perl -w + +=head1 NAME + +relicense.pl + +=head1 SYNOPSIS + +relicense.pl [options] -p <permissions file> <file> [<file>...] + +Option: + + -p,--permitted=FILE Specify file of emails with relicensing permission + -f,--force Manually force relicensing + -h,--help Display brief help message + -v,--verbose Increase verbosity + -q,--quiet Decrease verbosity + +=cut + +use File::Slurp; +use IPC::Run qw ( run ); +use Getopt::Long; +use Pod::Usage; +use strict; +use warnings; + +# Parse command-line options +my $verbosity = 0; +my $permfile; +my $force; +Getopt::Long::Configure ( "bundling", "auto_abbrev" ); +GetOptions ( + 'permitted|p=s' => \$permfile, + 'force|f' => \$force, + 'verbose|v+' => sub { $verbosity++; }, + 'quiet|q+' => sub { $verbosity--; }, + 'help|h' => sub { pod2usage ( 1 ); }, +) or die "Could not parse command-line options"; +pod2usage ( 1 ) unless @ARGV; + +# Read permitted emails file +my @emails = ( $permfile ? read_file ( $permfile ) : () ); +chomp @emails; +my $permitted = { map { /^.*<(\S+)>$/; ( $1 || $_ ) => 1 } @emails }; + +# Define list of relicensable licences +my $relicensable = { + GPL2_OR_LATER => 1, +}; + +# Define blurb to be added to copyright notice +my $blurb = ' + * + * You can also choose to distribute this program under the terms of + * the Unmodified Binary Distribution Licence (as given in the file + * COPYING.UBDL), provided that you have satisfied its requirements.'; + +# Process files +my @succeeded; +my @failed; +while ( my $filename = shift @ARGV ) { + + # Read file to determine existing licence + my $file = read_file ( $filename ); + my @licences = ( $file =~ /^\s*FILE_LICENCE\s*\(\s*(\S+)\s*\)\s*;?$/mg ); + die "No licence declaration in $filename\n" unless @licences; + die "Multiple licence declarations in $filename\n" if @licences > 1; + my $licence = $licences[0]; + + # Skip if file is already UBDL-licensed + next if $licence =~ /_OR_UBDL$/; + + # Fail immediately if file is not a candidate for relicensing + if ( ! exists $relicensable->{$licence} ) { + print "Non-relicensable licence $licence in $filename\n"; + push @failed, $filename; + next; + } + + # Run git-blame + my $stdout; + my $stderr; + run [ "git", "blame", "-M", "-C", "-p", "-w", $filename ], + \undef, \$stdout, \$stderr + or die "git-blame $filename: $?"; + die $stderr if $stderr; + + # Process output + my @stdout = split ( /\n/, $stdout ); + chomp @stdout; + my $details = {}; + my $failures = 0; + while ( @stdout ) { + + # Parse output + my $commit_line = shift @stdout; + ( my $commit, undef, my $lineno, undef, my $count ) = + ( $commit_line =~ + /^([0-9a-f]{40})\s+([0-9]+)\s+([0-9]+)(\s+([0-9]+))?$/ ) + or die "Malformed commit line \"$commit_line\"\n"; + if ( $count ) { + $details->{$commit} ||= {}; + while ( ! ( $stdout[0] =~ /^\t/ ) ) { + my $detail_line = shift @stdout; + ( my $key, undef, my $value ) = + ( $detail_line =~ /^([a-z-]+)(\s+(.+))?$/ ) + or die "Malformed detail line \"$detail_line\" for $commit_line\n"; + $details->{$commit}->{$key} = $value; + } + } + die "Missing commit details for $commit_line\n" + unless %{$details->{$commit}}; + my $code_line = shift @stdout; + ( my $line ) = ( $code_line =~ /^\t(.*)$/ ) + or die "Malformed code line \"$code_line\" for $commit_line\n"; + + # Skip trivial lines and lines so common that they are likely to + # be misattributed by git-blame + next if $line =~ /^\s*$/; # Empty lines + next if $line =~ /^\s*\/\*/; # Start of comments + next if $line =~ /^\s*\*/; # Middle (or end) of comments + next if $line =~ /^\s*\{\s*$/; # Standalone opening braces + next if $line =~ /^\s*\};?\s*$/; # Standalone closing braces + next if $line =~ /^\#include/; # Header inclusions + next if $line =~ /^\s*return\s+0;/; # return 0; + next if $line =~ /^\s*return\s+rc;/; # return rc; + next if $line =~ /^\s*PCI_ROM\s*\(.*\)\s*,\s*$/; # PCI IDs + next if $line =~ /^\s*FILE_LICENCE\s*\(.*\)\s*;$/; # Licence declarations + + # Identify author + my $author_mail = $details->{$commit}->{"author-mail"} + or die "Missing author email for $commit_line\n"; + ( my $email ) = ( $author_mail =~ /^<(\S+)>$/ ) + or die "Malformed author email \"$author_mail\" for $commit_line\n"; + undef $email if exists $details->{$commit}->{boundary}; + + # Check for relicensing permission + next if defined $email && exists $permitted->{$email}; + + # Print out lines lacking permission + printf $filename."\n" unless $failures; + printf "%4d %-30s %s\n", $lineno, ( $email || "<root>" ), $line; + $failures++; + } + + # Fail if there are any non-trivial lines lacking relicensing permission + if ( $failures && ! $force ) { + push @failed, $filename; + next; + } + + # Modify FILE_LICENCE() line + $file =~ s/(^\s*FILE_LICENCE\s*\(\s*${licence})(\s*\)\s*;?$)/$1_OR_UBDL$2/m + or die "Could not modify FILE_LICENCE() in $filename\n"; + + # Modify copyright notice, if present + if ( $file =~ /GNU General Public License/i ) { + $file =~ s/(02110-1301, USA.$)/$1${blurb}/m + or die "Could not modify copyright notice in $filename\n"; + } + + # Write out modified file + write_file ( $filename, { atomic => 1 }, $file ); + push @succeeded, $filename; +} + +print "Relicensed: ".join ( " ", @succeeded )."\n" if @succeeded; +die "Cannot relicense: ".join ( " ", @failed )."\n" if @failed; diff --git a/roms/ipxe/src/util/zbin.c b/roms/ipxe/src/util/zbin.c index 3b7cf95b3..1862a3827 100644 --- a/roms/ipxe/src/util/zbin.c +++ b/roms/ipxe/src/util/zbin.c @@ -1,13 +1,21 @@ +#include <stdint.h> #include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> #include <sys/stat.h> - -#define ENCODE -#define VERBOSE -#include "nrv2b.c" -FILE *infile, *outfile; +#include <lzma.h> #define DEBUG 0 +/* LZMA filter choices. Must match those used by unlzma.S */ +#define LZMA_LC 2 +#define LZMA_LP 0 +#define LZMA_PB 0 + +/* LZMA preset choice. This is a policy decision */ +#define LZMA_PRESET ( LZMA_PRESET_DEFAULT | LZMA_PRESET_EXTREME ) + struct input_file { void *buf; size_t len; @@ -177,13 +185,75 @@ static int process_zinfo_copy ( struct input_file *input, return 0; } +#define OPCODE_CALL 0xe8 +#define OPCODE_JMP 0xe9 + +static void bcj_filter ( void *data, size_t len ) { + struct { + uint8_t opcode; + int32_t target; + } __attribute__ (( packed )) *jump; + ssize_t limit = ( len - sizeof ( *jump ) ); + ssize_t offset; + + /* liblzma does include an x86 BCJ filter, but it's hideously + * convoluted and undocumented. This BCJ filter is + * substantially simpler and achieves the same compression (at + * the cost of requiring the decompressor to know the size of + * the decompressed data, which we already have in iPXE). + */ + for ( offset = 0 ; offset <= limit ; offset++ ) { + jump = ( data + offset ); + + /* Skip instructions that are not followed by a rel32 address */ + if ( ( jump->opcode != OPCODE_CALL ) && + ( jump->opcode != OPCODE_JMP ) ) + continue; + + /* Convert rel32 address to an absolute address. To + * avoid false positives (which damage the compression + * ratio), we should check that the jump target is + * within the range [0,limit). + * + * Some output values would then end up being mapped + * from two distinct input values, making the + * transformation irreversible. To solve this, we + * transform such values back into the part of the + * range which would otherwise correspond to no input + * values. + */ + if ( ( jump->target >= -offset ) && + ( jump->target < ( limit - offset ) ) ) { + /* Convert relative addresses in the range + * [-offset,limit-offset) to absolute + * addresses in the range [0,limit). + */ + jump->target += offset; + } else if ( ( jump->target >= ( limit - offset ) ) && + ( jump->target < limit ) ) { + /* Convert positive numbers in the range + * [limit-offset,limit) to negative numbers in + * the range [-offset,0). + */ + jump->target -= limit; + } + offset += sizeof ( jump->target ); + }; +} + static int process_zinfo_pack ( struct input_file *input, struct output_file *output, union zinfo_record *zinfo ) { struct zinfo_pack *pack = &zinfo->pack; size_t offset = pack->offset; size_t len = pack->len; - unsigned long packed_len; + size_t packed_len = 0; + size_t remaining = ( output->max_len - output->len ); + lzma_options_lzma options; + const lzma_filter filters[] = { + { .id = LZMA_FILTER_LZMA1, .options = &options }, + { .id = LZMA_VLI_UNKNOWN } + }; if ( ( offset + len ) > input->len ) { fprintf ( stderr, "Input buffer overrun on pack\n" ); @@ -196,9 +266,15 @@ static int process_zinfo_pack ( struct input_file *input, return -1; } - if ( ucl_nrv2b_99_compress ( ( input->buf + offset ), len, - ( output->buf + output->len ), - &packed_len, 0 ) != UCL_E_OK ) { + bcj_filter ( ( input->buf + offset ), len ); + + lzma_lzma_preset ( &options, LZMA_PRESET ); + options.lc = LZMA_LC; + options.lp = LZMA_LP; + options.pb = LZMA_PB; + if ( lzma_raw_buffer_encode ( filters, NULL, ( input->buf + offset ), + len, ( output->buf + output->len ), + &packed_len, remaining ) != LZMA_OK ) { fprintf ( stderr, "Compression failure\n" ); return -1; } @@ -206,7 +282,7 @@ static int process_zinfo_pack ( struct input_file *input, if ( DEBUG ) { fprintf ( stderr, "PACK [%#zx,%#zx) to [%#zx,%#zx)\n", offset, ( offset + len ), output->len, - ( size_t )( output->len + packed_len ) ); + ( output->len + packed_len ) ); } output->len += packed_len; diff --git a/roms/openbios/arch/ppc/qemu/init.c b/roms/openbios/arch/ppc/qemu/init.c index 4fe8b7220..2b5b8e1a9 100644 --- a/roms/openbios/arch/ppc/qemu/init.c +++ b/roms/openbios/arch/ppc/qemu/init.c @@ -302,6 +302,11 @@ cpu_generic_init(const struct cpudef *cpu) fword("encode-string"); push_str("state"); fword("property"); + + PUSH(0x20); + fword("encode-int"); + push_str("reservation-granule-size"); + fword("property"); } static void @@ -680,6 +685,60 @@ static void ffilll(void) } } +/* + * adler32 ( adler buf len -- checksum ) + * + * Adapted from Mark Adler's original implementation (zlib license) + * + * Both OS 9 and BootX require this word for payload validation. + */ + +#define DO1(buf,i) {s1 += buf[i]; s2 += s1;} +#define DO2(buf,i) DO1(buf,i); DO1(buf,i+1); +#define DO4(buf,i) DO2(buf,i); DO2(buf,i+2); +#define DO8(buf,i) DO4(buf,i); DO4(buf,i+4); +#define DO16(buf) DO8(buf,0); DO8(buf,8); + +static void adler32(void) +{ + uint32_t len = (uint32_t)POP(); + char *buf = (char *)POP(); + uint32_t adler = (uint32_t)POP(); + + if (buf == NULL) { + RET(-1); + } + + uint32_t base = 65521; + uint32_t nmax = 5552; + + uint32_t s1 = adler & 0xffff; + uint32_t s2 = (adler >> 16) & 0xffff; + + uint32_t k; + while (len > 0) { + k = (len < nmax ? len : nmax); + len -= k; + + while (k >= 16) { + DO16(buf); + buf += 16; + k -= 16; + } + if (k != 0) { + do { + s1 += *buf++; + s2 += s1; + } while (--k); + } + + s1 %= base; + s2 %= base; + } + + RET(s2 << 16 | s1); +} + void arch_of_init(void) { @@ -945,6 +1004,9 @@ arch_of_init(void) /* Implementation of filll word (required by BootX) */ bind_func("filll", ffilll); + + /* Implementation of adler32 word (required by OS 9, BootX) */ + bind_func("(adler32)", adler32); bind_func("platform-boot", boot); bind_func("(go)", go); diff --git a/roms/openbios/arch/ppc/qemu/methods.c b/roms/openbios/arch/ppc/qemu/methods.c index fd993daa9..930b47c4e 100644 --- a/roms/openbios/arch/ppc/qemu/methods.c +++ b/roms/openbios/arch/ppc/qemu/methods.c @@ -114,6 +114,8 @@ static void ciface_quiesce( unsigned long args[], unsigned long ret[] ) { usb_exit(); + + ob_ide_quiesce(); #if 0 unsigned long msr; /* This seems to be the correct thing to do - but I'm not sure */ @@ -164,21 +166,21 @@ DECLARE_UNNAMED_NODE( mmu, INSTALL_OPEN, 0 ); DECLARE_NODE( mmu_ciface, 0, 0, "+/openprom/client-services" ); -/* ( phys size align --- base ) */ +/* ( [phys] size align --- base ) */ static void mem_claim( void ) { ucell align = POP(); ucell size = POP(); - ucell phys = POP(); - ucell ret = ofmem_claim_phys( phys, size, align ); + phys_addr_t phys = -1; - if( ret == -1 ) { - printk("MEM: claim failure\n"); - throw( -13 ); - return; + if (!align) { + phys = POP(); } - PUSH( ret ); + + phys = ofmem_claim_phys(phys, size, align); + + PUSH(phys); } /* ( phys size --- ) */ @@ -188,24 +190,24 @@ mem_release( void ) POP(); POP(); } -/* ( phys size align --- base ) */ +/* ( [virt] size align --- base ) */ static void mmu_claim( void ) { ucell align = POP(); ucell size = POP(); - ucell phys = POP(); - ucell ret = ofmem_claim_virt( phys, size, align ); + ucell virt = -1; - if( ret == -1 ) { - printk("MMU: CLAIM failure\n"); - throw( -13 ); - return; + if (!align) { + virt = POP(); } - PUSH( ret ); + + virt = ofmem_claim_virt(virt, size, align); + + PUSH(virt); } -/* ( phys size --- ) */ +/* ( virt size --- ) */ static void mmu_release( void ) { diff --git a/roms/openbios/arch/ppc/qemu/qemu.fs b/roms/openbios/arch/ppc/qemu/qemu.fs index 458af1bc7..11e344a59 100644 --- a/roms/openbios/arch/ppc/qemu/qemu.fs +++ b/roms/openbios/arch/ppc/qemu/qemu.fs @@ -93,3 +93,31 @@ variable keyboard-phandle 0 keyboard-phandle ! :noname set-defaults ; PREPOST-initializer + +\ ------------------------------------------------------------------------- +\ Adler-32 wrapper +\ ------------------------------------------------------------------------- + +: adler32 ( adler buf len -- checksum ) + \ Since Mac OS 9 is the only system using this word, we take this + \ opportunity to inject a copyright message that is necessary for the + \ system to boot. + " Copyright 1983-2001 Apple Computer, Inc. THIS MESSAGE FOR COMPATIBILITY ONLY" + encode-string " copyright" + " /" find-package if + " set-property" $find if + execute + else + 3drop drop + then + then + + ( adler buf len ) + + " (adler32)" $find if + execute + else + ." Can't find " ( adler32-name ) type cr + 3drop 0 + then +; diff --git a/roms/openbios/arch/ppc/qemu/tree.fs b/roms/openbios/arch/ppc/qemu/tree.fs index 1ed838397..5b6bbc6f7 100644 --- a/roms/openbios/arch/ppc/qemu/tree.fs +++ b/roms/openbios/arch/ppc/qemu/tree.fs @@ -42,6 +42,14 @@ new-device : close ; finish-device +new-device + " rom" device-name + h# ff800000 encode-int 0 encode-int encode+ " reg" property + 1 encode-int " #address-cells" property + h# ff800000 encode-int h# 800000 encode-int encode+ + h# ff800000 encode-int encode+ " ranges" property +finish-device + \ ------------------------------------------------------------- \ /packages \ ------------------------------------------------------------- diff --git a/roms/openbios/config/scripts/switch-arch b/roms/openbios/config/scripts/switch-arch index d5e2f7710..7b8b457f6 100755 --- a/roms/openbios/config/scripts/switch-arch +++ b/roms/openbios/config/scripts/switch-arch @@ -99,23 +99,25 @@ archname() select_prefix() { - TARGETS="${1}-unknown-linux-gnu- ${1}-linux-gnu- ${1}-linux- ${1}-elf- ${1}-eabi-" + for target_arch ; do + TARGETS="${target_arch}-unknown-linux-gnu- ${target_arch}-linux-gnu- ${target_arch}-linux- ${target_arch}-elf- ${target_arch}-eabi-" - if [ x"$CROSS_COMPILE" != "x" ]; then - TARGETS=$CROSS_COMPILE - fi + if [ x"$CROSS_COMPILE" != "x" ]; then + TARGETS=$CROSS_COMPILE + fi - for TARGET in $TARGETS - do - if type ${TARGET}gcc > /dev/null 2>&1 - then + for TARGET in $TARGETS + do + if type ${TARGET}gcc > /dev/null 2>&1 + then + return + fi + done + if [ "$ARCH" = "$HOSTARCH" ]; then return fi done - if [ "$ARCH" = "$HOSTARCH" ]; then - return - fi - echo "ERROR: no ${1} cross-compiler found !" 1>&2 + echo "ERROR: no $* cross-compiler found !" 1>&2 exit 1 } @@ -251,7 +253,7 @@ for ARCH in $arch_list; do ;; ppc) - select_prefix powerpc + select_prefix powerpc powerpc64 if [ "$unix" = "no" ]; then CFLAGS="-m32 -msoft-float -fno-builtin-bcopy -fno-builtin-log2" AS_FLAGS="-m32" diff --git a/roms/openbios/drivers/cuda.c b/roms/openbios/drivers/cuda.c index 9555dea49..ff5d22de2 100644 --- a/roms/openbios/drivers/cuda.c +++ b/roms/openbios/drivers/cuda.c @@ -144,8 +144,22 @@ static int cuda_adb_req (void *host, const uint8_t *snd_buf, int len, // CUDA_DPRINTF("len: %d %02x\n", len, snd_buf[0]); len = cuda_request(host, ADB_PACKET, snd_buf, len, buffer); if (len > 1 && buffer[0] == ADB_PACKET) { - pos = buffer + 2; - len -= 2; + /* We handle 2 types of ADB packet here: + Normal: <type> <status> <data> ... + Error : <type> <status> <cmd> (<data> ...) + Ideally we should use buffer[1] (status) to determine whether this + is a normal or error packet but this requires a corresponding fix + in QEMU <= 2.4. Hence we temporarily handle it this way to ease + the transition. */ + if (len > 2 && buffer[2] == snd_buf[0]) { + /* Error */ + pos = buffer + 3; + len -= 3; + } else { + /* Normal */ + pos = buffer + 2; + len -= 2; + } } else { pos = buffer + 1; len = -1; @@ -380,7 +394,8 @@ powermgt_init(char *path) ph = find_dev(buf); set_property(ph, "device_type", "power-mgt", 10); - set_property(ph, "compatible", "power-mgt", 10); + set_property(ph, "mgt-kind", "min-consumption-pwm-led", strlen("min-consumption-pwm-led") + 1); + set_property(ph, "compatible", "cuda", strlen("cuda") + 1); } cuda_t *cuda_init (const char *path, phys_addr_t base) diff --git a/roms/openbios/drivers/escc.c b/roms/openbios/drivers/escc.c index 240043be3..afb97fa8a 100644 --- a/roms/openbios/drivers/escc.c +++ b/roms/openbios/drivers/escc.c @@ -380,12 +380,22 @@ ob_zs_init(phys_addr_t base, uint64_t offset, int intr, int slave, int keyboard) static void escc_add_channel(const char *path, const char *node, phys_addr_t addr, - uint32_t offset) + int esnum) { char buf[64], tty[32]; phandle_t dnode, aliases; - int len; - cell props[2]; + + cell props[10]; + int offset; + int legacy; + + switch (esnum) { + case 2: offset = 1; legacy = 0; break; + case 3: offset = 0; legacy = 0; break; + case 4: offset = 1; legacy = 1; break; + case 5: offset = 0; legacy = 1; break; + default: return; + } /* add device */ @@ -411,16 +421,28 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr, set_property(dnode, "device_type", "serial", strlen("serial") + 1); - snprintf(buf, sizeof(buf), "ch-%s", node); - len = strlen(buf) + 1; - snprintf(buf + len, sizeof(buf) - len, "CHRP,es2"); - set_property(dnode, "compatible", buf, len + 9); - - props[0] = IO_ESCC_OFFSET + offset * 0x20; - props[1] = 0x00000020; - set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell)); + snprintf(buf, sizeof(buf), "chrp,es%d", esnum); + set_property(dnode, "compatible", buf, 9); + + if (legacy) { + props[0] = IO_ESCC_LEGACY_OFFSET + offset * 0x4; + props[1] = 0x00000001; + props[2] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 2; + props[3] = 0x00000001; + props[4] = IO_ESCC_LEGACY_OFFSET + offset * 0x4 + 6; + props[5] = 0x00000001; + set_property(dnode, "reg", (char *)&props, 6 * sizeof(cell)); + } else { + props[0] = IO_ESCC_OFFSET + offset * 0x20; + props[1] = 0x00000020; + set_property(dnode, "reg", (char *)&props, 2 * sizeof(cell)); + } - props[0] = addr + IO_ESCC_OFFSET + offset * 0x20; + if (legacy) { + props[0] = addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4; + } else { + props[0] = addr + IO_ESCC_OFFSET + offset * 0x20; + } OLDWORLD(set_property(dnode, "AAPL,address", (char *)&props, 1 * sizeof(cell))); @@ -430,13 +452,21 @@ escc_add_channel(const char *path, const char *node, phys_addr_t addr, props[0] = (0x24) + offset; props[1] = 0; + props[2] = 0; NEWWORLD(set_property(dnode, "interrupts", - (char *)&props, 2 * sizeof(cell))); + (char *)&props, 3 * sizeof(cell))); device_end(); - uart_init_line((unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20, - CONFIG_SERIAL_SPEED); + if (legacy) { + uart_init_line( + (unsigned char*)addr + IO_ESCC_LEGACY_OFFSET + offset * 0x4, + CONFIG_SERIAL_SPEED); + } else { + uart_init_line( + (unsigned char*)addr + IO_ESCC_OFFSET + offset * 0x20, + CONFIG_SERIAL_SPEED); + } } void @@ -467,10 +497,34 @@ escc_init(const char *path, phys_addr_t addr) fword("finish-device"); - escc_add_channel(buf, "a", addr, 1); - escc_add_channel(buf, "b", addr, 0); + escc_add_channel(buf, "a", addr, 2); + escc_add_channel(buf, "b", addr, 3); escc_serial_dev = (unsigned char *)addr + IO_ESCC_OFFSET + (CONFIG_SERIAL_PORT ? 0 : 0x20); + + push_str(path); + fword("find-device"); + fword("new-device"); + + push_str("escc-legacy"); + fword("device-name"); + + snprintf(buf, sizeof(buf), "%s/escc-legacy", path); + + dnode = find_dev(buf); + + set_int_property(dnode, "#address-cells", 1); + props[0] = __cpu_to_be32(IO_ESCC_LEGACY_OFFSET); + props[1] = __cpu_to_be32(IO_ESCC_LEGACY_SIZE); + set_property(dnode, "reg", (char *)&props, sizeof(props)); + set_property(dnode, "device_type", "escc-legacy", + strlen("escc-legacy") + 1); + set_property(dnode, "compatible", "chrp,es1", 9); + + fword("finish-device"); + + escc_add_channel(buf, "a", addr, 4); + escc_add_channel(buf, "b", addr, 5); } #endif diff --git a/roms/openbios/drivers/escc.h b/roms/openbios/drivers/escc.h index caaf00d40..e73f267b2 100644 --- a/roms/openbios/drivers/escc.h +++ b/roms/openbios/drivers/escc.h @@ -1,6 +1,8 @@ #define IO_ESCC_SIZE 0x00001000 #define IO_ESCC_OFFSET 0x00013000 +#define IO_ESCC_LEGACY_SIZE 0x00001000 +#define IO_ESCC_LEGACY_OFFSET 0x00012000 #define ZS_REGS 8 diff --git a/roms/openbios/drivers/ide.c b/roms/openbios/drivers/ide.c index 327c64a40..5125b78b9 100644 --- a/roms/openbios/drivers/ide.c +++ b/roms/openbios/drivers/ide.c @@ -1488,6 +1488,28 @@ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, return 0; } +void ob_ide_quiesce(void) +{ + struct ide_channel *channel; + int i; + + channel = channels; + while (channel) { + for (i = 0; i < 2; i++) { + struct ide_drive *drive = &channel->drives[i]; + + if (!drive->present) + continue; + + ob_ide_select_drive(drive); + ob_ide_software_reset(drive); + ob_ide_device_type_check(drive); + } + + channel = channel->next; + } +} + #if defined(CONFIG_DRIVER_MACIO) static unsigned char macio_ide_inb(struct ide_channel *chan, unsigned int port) @@ -1581,6 +1603,12 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) set_property(dnode, "compatible", (is_oldworld() ? "heathrow-ata" : "keylargo-ata"), 13); + set_property(dnode, "model", ((current_channel == 3) ? + "ata-3" : "ata-4"), strlen("ata-*") + 1); + + set_property(dnode, "AAPL,connector", "ata", + strlen("ata") + 1); + props[0] = 0x00000526; props[1] = 0x00000085; props[2] = 0x00000025; @@ -1589,8 +1617,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) props[5] = 0x00000000; props[6] = 0x00000000; props[7] = 0x00000000; - OLDWORLD(set_property(dnode, "AAPL,pio-timing", - (char *)&props, 8*sizeof(props[0]))); + set_property(dnode, "AAPL,pio-timing", + (char *)&props, 8*sizeof(props[0])); /* The first interrupt entry is the ide interrupt, the second the dbdma interrupt */ @@ -1634,8 +1662,8 @@ int macio_ide_init(const char *path, uint32_t addr, int nb_channels) (char *)&props, 2*sizeof(props[0]))); props[0] = 0; - OLDWORLD(set_property(dnode, "AAPL,bus-id", (char*)props, - 1 * sizeof(props[0]))); + set_property(dnode, "AAPL,bus-id", (char*)props, + 1 * sizeof(props[0])); IDE_DPRINTF(DEV_NAME": [io ports 0x%lx]\n", current_channel, chan->mmio); diff --git a/roms/openbios/drivers/pci.c b/roms/openbios/drivers/pci.c index 366f4a17f..935ecb8f8 100644 --- a/roms/openbios/drivers/pci.c +++ b/roms/openbios/drivers/pci.c @@ -824,7 +824,7 @@ int ebus_config_cb(const pci_config_t *config) ncells += pci_encode_phys_addr(props + ncells, flags, space_code, config->dev, PCI_BASE_ADDR_0 + (i * sizeof(uint32_t)), - 0); + config->assigned[i] & ~mask); props[ncells++] = config->sizes[i]; } @@ -1420,6 +1420,12 @@ static void ob_pci_host_set_interrupt_map(phandle_t host) target_node = find_dev("/pci/mac-io/escc/ch-b"); set_int_property(target_node, "interrupt-parent", dnode); + target_node = find_dev("/pci/mac-io/escc-legacy/ch-a"); + set_int_property(target_node, "interrupt-parent", dnode); + + target_node = find_dev("/pci/mac-io/escc-legacy/ch-b"); + set_int_property(target_node, "interrupt-parent", dnode); + /* QEMU only emulates 2 of the 3 ata buses currently */ /* On a new world Mac these are not numbered but named by the * ATA version they support. Thus we have: ata-3, ata-3, ata-4 diff --git a/roms/openbios/include/drivers/drivers.h b/roms/openbios/include/drivers/drivers.h index 3b83b12d1..48f81a870 100644 --- a/roms/openbios/include/drivers/drivers.h +++ b/roms/openbios/include/drivers/drivers.h @@ -52,6 +52,7 @@ void kbd_init(uint64_t base); /* drivers/ide.c */ int ob_ide_init(const char *path, uint32_t io_port0, uint32_t ctl_port0, uint32_t io_port1, uint32_t ctl_port1); +void ob_ide_quiesce(void); int macio_ide_init(const char *path, uint32_t addr, int nb_channels); #endif #ifdef CONFIG_DRIVER_ESP diff --git a/roms/openbios/libopenbios/bootinfo_load.c b/roms/openbios/libopenbios/bootinfo_load.c index fa9e36bd4..f33678185 100644 --- a/roms/openbios/libopenbios/bootinfo_load.c +++ b/roms/openbios/libopenbios/bootinfo_load.c @@ -161,6 +161,12 @@ bootinfo_init_program(void) feval("load-size"); size = POP(); + /* Some bootinfo scripts contain a binary payload after the + NULL-terminated Forth string such as OS 9. Restrict our + size to just the Forth section, otherwise we end up trying + to allocate memory for the entire binary which might fail. */ + size = strnlen(base, size); + bootscript = malloc(size); if (bootscript == NULL) { DPRINTF("Can't malloc %d bytes\n", size); |