This is an old revision of the document!


Alan Shieh, Linux UNDI Driver

UNDI proposal

OldUNDIProposal

= Goals = * Support both 16:16 and 16:32 protected mode UNDI stacks

= Phase 1: 16:16 UNDI stack =

Most work should be reusable in the 16:32 mode. The main difference will be the page table and LDT/GDT setup, which will be driven by the PXE extensions to provide the necessary information to the kernel.

Linux UNDI execution process

The process will interact with a network driver to gain access to the kernel send and receive queues, and to perform interrupt processing.

The 16:16 version will make assumptions about page table / memory layout. This restriction will be removed in the 16:32 version.

High level requirements 1. Page table will be initialized for two “regions”:

 a) PXE execution environment: the physical/virtual address range that

Etherboot is known to reside in.

 b) Area for Linux process

2. Implement 16:32⇒16:16 thunks between Linux process and PXE code. 16:16 parameter passing area for all the parameter structures 3. Poll for interrupts using PXENV_UNDI_ISR_IN_START. 4. Implement bottom-half processing using PXEENV_UNDI_ISR.PXEENV_UNDI_ISR_IN_PROCESS, PXEENV_UNDI_ISR.PXEENV_UNDI_ISR_GET_NEXT The processing flowchart is provided in the PXE 2.1 specification. == Implement stub driver for NIC == Prototype will support only PCI devices. 1. Perform PCI probe using PCI_IDs specified as module parameters. 2. Initialize GDT using !PXE information. 3. Pump packets between UNDI execution process and kernel transmit/receive queues. 4. Rmmod should clean up properly so that a full driver can be loaded later. == Communications between UNDI execution process and driver == The driver will export two pipes under /proc with the following interfaces: kernel_to_process: * TxPacket(char data[len], int len); Kernel requests to send a packet Maybe mii/ethtool/ifconfig? process_to_kernel: * RxPacket(char data[len], int len); Process received packet, tell kernel to queue it for network stack == Etherboot & boot process modifications == 1. Modify Etherboot to report 16:16 segment descriptors via !PXE. 2. Add a configuration flag to Etherboot to prevent it from unloading UNDI 3. Pass PCI_IDs to kernel 4. Linux: Before switching to protected mode, reserve !PXE.SegDescCnt descriptors in GDT, and set !PXE.FirstSelector to the appropriate location. The descriptors will not be copied from !PXE structure until the stub driver is loaded. Hopefully, the PXE stack will not gain control in the interim and crash because the segment descriptors have not been initialized. 5. Use PCI_IDs to initialize NIC stub driver == Initialization and testing == I. Internal testing from within the driver process. Implement an interactive debugging console? 1. UNDI_OPEN, UNDI_CLOSE, UNDI_GET_STATE UNDI_GET_INFORMATION, UNDI_GET_STATISTICS, UNDI_CLEAR_STATISTICS (stats are useful for debugging later functionality) UNDI_INITIATE_DIAGS (sanity check the driver and execution environment) 2. UNDI_TRANSMIT 3. UNDI_RECEIVE Optional: UNDI_SET_STATION_ADDRESS, UNDI_GET_NIC_TYPE, UNDI_GET_IFACE_INFO Ignored: UNDI_SET_PACKET_FILTER, UNDI_SET_MULTICAST_ADDRESS, UNDI_FORCE_INTERRUPT, UNDI_GET_MULTICAST_ADDRESS 4. IPC pipes 5. Interrupt polling, Rx/Tx handling II. Linux milestones & tests 1. Transmit & receive 2. DHCP 3. ARP 4. TCP & NFS 5. Simulated boot process: a) fetch network module from remote host, e.g. via HTTP or NFS b) unload UNDI module, kill UNDI execution process c) load network module d) network tests (as before) = Phase 2: 16:32 mode = == Extend PXE interface == This mode will require additional information from the PXE stack, such as the precise page table / memory layout and LDT/GDT entries for the PXE stack. This information will be returned via a new UNDI op-code PXENV_GET_UNDI_ENV32. This entry point will be specially coded so that it can execute in 16:16 mode (e.g. with KEEP_IT_REAL compile/link options) via !PXE.EntryPointSP. This will be the only op-code supported via that entry point, so the vast majority of Etherboot will use 16:32. typedef struct s_PXENV_GET_UNDI_ENV32 { /* Outputs */ PXENV_STATUS Status; ADDR32 PageDirectoryBase; SEGOFF16 EntryPoint16_32; /* Inputs */ UINT32 DescriptorBufferSize; ADDR32 DescriptorBuffer; } t_PXENV_GET_UNDI_ENV32; All UNDI op-codes will be accessible only through EntryPoint16_32. PageDirectoryBase will be used to pass the memory map to the kernel. The format is the native IA-32 2-level page table. The AVAIL fields of the PTE and PDEs will be used to convey additional information about each page: 000 = Normal page, can be relocated in physical memory 001 = PCI DMA page, can only be relocated if the relocation is transparent to the PCI device. 010 = PCI MMIO page. Must use these exact physical addresses. min(!PXE.SegDescCnt, DescriptorBufferSize / sizeof(descriptor)) entries will be set in DescriptorBuffer. Each descriptor entry is 64 bits, in the native IA-32 segment descriptor format. This way, any arbitrary set of descriptors can be specified. == Delta from 16:16 prototype == 1. Page table, LDT/GDT initialization 2. Thunks will call into 16:32 if available, otherwise 16:16 ========================== End of base functionality goals ========================== = Compatibility improvements = 1. Interrupt-driven operation for quirky cards Polling is much easier to deal with, however according to comments in undi.c, this doesn't work for all cards. So interrupt-driven operation will increase the compatibility of the driver. This will be quite tricky, since incorrect top-half handling can easily jam the system. After PCI probe, the kernel module will install a small interrupt handler. The interrupt handler will need to use PXEENV_UNDI_ISR.PXEENV_UNDI_ISR_IN_START. However, this is not readily accessible, as it resides in the UNDI execution process. If the NIC IRQ line is not shared, then this is trivial. The IRQ line can be disabled while invoking the PXEENV_UNDI_ISR_IN_START in the UNDI execution process, and the re-enabled when the process returns. If the IRQ line is shared, we'll still need to mask the IRQ line while dispatching to the ISR. However, this will also mask the other devices, which might be needed to execute the process (e.g. disk IRQs for demand paging). To decrease the likelihood of problems, the entire process should be pinned in memory, linked statically, etc. Alternatively, the kernel could switch to a polling strategy on all IRQs while waiting for the user application to return. Another solution would be to pull an UNDI execution environment into the kernel context, then dispatching directly to the UNDI ISR, however this would require changes to the kernel memory map and probably end up being messy. == New kernel⇔user pipe commands == kernel_to_process: * UNDI_Int(t_PXEENV_UNDI_ISR isr); UNDI interrupt received by top-half process_to_kernel: * UNDI_Int_Ack(); Acknowledge interrupt, which will reenable the IRQ line = Support for other PXE stacks = Experience & experiment with other PXE stack * how they use memory (DMA, code locations) * tricks to support generic PXE stack

  (paging, IOMMU, Linux kernel layout modifications)

*** Attempt to support unmodified PXE stacks


QR Code
QR Code soc:alanshieh (generated for current page)