1 /* 2 * Copyright (C) 2007 Michael Brown <mbrown (at) fensystems.co.uk>. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of the 7 * License, or any later version. 8 * 9 * This program is distributed in the hope that it will be useful, but 10 * WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 12 * General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 17 */ 18 19 FILE_LICENCE ( GPL2_OR_LATER ); 20 21 #include <stdint.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <pxe.h> 25 #include <realmode.h> 26 #include <bios.h> 27 #include <pnpbios.h> 28 #include <basemem.h> 29 #include <gpxe/pci.h> 30 #include <undi.h> 31 #include <undirom.h> 32 #include <undiload.h> 33 34 /** @file 35 * 36 * UNDI load/unload 37 * 38 */ 39 40 /** Parameter block for calling UNDI loader */ 41 static struct s_UNDI_LOADER __bss16 ( undi_loader ); 42 #define undi_loader __use_data16 ( undi_loader ) 43 44 /** UNDI loader entry point */ 45 static SEGOFF16_t __bss16 ( undi_loader_entry ); 46 #define undi_loader_entry __use_data16 ( undi_loader_entry ) 47 48 /** 49 * Call UNDI loader to create a pixie 50 * 51 * @v undi UNDI device 52 * @v undirom UNDI ROM 53 * @ret rc Return status code 54 */ 55 int undi_load ( struct undi_device *undi, struct undi_rom *undirom ) { 56 struct s_PXE ppxe; 57 unsigned int fbms_seg; 58 uint16_t exit; 59 int rc; 60 61 /* Set up START_UNDI parameters */ 62 memset ( &undi_loader, 0, sizeof ( undi_loader ) ); 63 undi_loader.AX = undi->pci_busdevfn; 64 undi_loader.BX = undi->isapnp_csn; 65 undi_loader.DX = undi->isapnp_read_port; 66 undi_loader.ES = BIOS_SEG; 67 undi_loader.DI = find_pnp_bios(); 68 69 /* Allocate base memory for PXE stack */ 70 undi->restore_fbms = get_fbms(); 71 fbms_seg = ( undi->restore_fbms << 6 ); 72 fbms_seg -= ( ( undirom->code_size + 0x0f ) >> 4 ); 73 undi_loader.UNDI_CS = fbms_seg; 74 fbms_seg -= ( ( undirom->data_size + 0x0f ) >> 4 ); 75 undi_loader.UNDI_DS = fbms_seg; 76 77 /* Debug info */ 78 DBGC ( undi, "UNDI %p loading UNDI ROM %p to CS %04x DS %04x for ", 79 undi, undirom, undi_loader.UNDI_CS, undi_loader.UNDI_DS ); 80 if ( undi->pci_busdevfn != UNDI_NO_PCI_BUSDEVFN ) { 81 unsigned int bus = ( undi->pci_busdevfn >> 8 ); 82 unsigned int devfn = ( undi->pci_busdevfn & 0xff ); 83 DBGC ( undi, "PCI %02x:%02x.%x\n", 84 bus, PCI_SLOT ( devfn ), PCI_FUNC ( devfn ) ); 85 } 86 if ( undi->isapnp_csn != UNDI_NO_ISAPNP_CSN ) { 87 DBGC ( undi, "ISAPnP(%04x) CSN %04x\n", 88 undi->isapnp_read_port, undi->isapnp_csn ); 89 } 90 91 /* Call loader */ 92 undi_loader_entry = undirom->loader_entry; 93 __asm__ __volatile__ ( REAL_CODE ( "pushw %%ds\n\t" 94 "pushw %%ax\n\t" 95 "lcall *undi_loader_entry\n\t" 96 "addw $4, %%sp\n\t" ) 97 : "=a" ( exit ) 98 : "a" ( __from_data16 ( &undi_loader ) ) 99 : "ebx", "ecx", "edx", "esi", "edi", "ebp" ); 100 101 /* UNDI API calls may rudely change the status of A20 and not 102 * bother to restore it afterwards. Intel is known to be 103 * guilty of this. 104 * 105 * Note that we will return to this point even if A20 gets 106 * screwed up by the UNDI driver, because Etherboot always 107 * resides in an even megabyte of RAM. 108 */ 109 gateA20_set(); 110 111 if ( exit != PXENV_EXIT_SUCCESS ) { 112 rc = -undi_loader.Status; 113 if ( rc == 0 ) /* Paranoia */ 114 rc = -EIO; 115 DBGC ( undi, "UNDI %p loader failed: %s\n", 116 undi, strerror ( rc ) ); 117 return rc; 118 } 119 120 /* Populate PXE device structure */ 121 undi->pxenv = undi_loader.PXENVptr; 122 undi->ppxe = undi_loader.PXEptr; 123 copy_from_real ( &ppxe, undi->ppxe.segment, undi->ppxe.offset, 124 sizeof ( ppxe ) ); 125 undi->entry = ppxe.EntryPointSP; 126 DBGC ( undi, "UNDI %p loaded PXENV+ %04x:%04x !PXE %04x:%04x " 127 "entry %04x:%04x\n", undi, undi->pxenv.segment, 128 undi->pxenv.offset, undi->ppxe.segment, undi->ppxe.offset, 129 undi->entry.segment, undi->entry.offset ); 130 131 /* Update free base memory counter */ 132 undi->fbms = ( fbms_seg >> 6 ); 133 set_fbms ( undi->fbms ); 134 DBGC ( undi, "UNDI %p using [%d,%d) kB of base memory\n", 135 undi, undi->fbms, undi->restore_fbms ); 136 137 return 0; 138 } 139 140 /** 141 * Unload a pixie 142 * 143 * @v undi UNDI device 144 * @ret rc Return status code 145 * 146 * Erases the PXENV+ and !PXE signatures, and frees the used base 147 * memory (if possible). 148 */ 149 int undi_unload ( struct undi_device *undi ) { 150 static uint32_t dead = 0xdeaddead; 151 152 DBGC ( undi, "UNDI %p unloading\n", undi ); 153 154 /* Erase signatures */ 155 if ( undi->pxenv.segment ) 156 put_real ( dead, undi->pxenv.segment, undi->pxenv.offset ); 157 if ( undi->ppxe.segment ) 158 put_real ( dead, undi->ppxe.segment, undi->ppxe.offset ); 159 160 /* Free base memory, if possible */ 161 if ( undi->fbms == get_fbms() ) { 162 DBGC ( undi, "UNDI %p freeing [%d,%d) kB of base memory\n", 163 undi, undi->fbms, undi->restore_fbms ); 164 set_fbms ( undi->restore_fbms ); 165 return 0; 166 } else { 167 DBGC ( undi, "UNDI %p leaking [%d,%d) kB of base memory\n", 168 undi, undi->fbms, undi->restore_fbms ); 169 return -EBUSY; 170 } 171 } 172